backport of commit 7ab7edf9cdec6ceab92e47c1472a43d802de0486 (#19240)
Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com>
This commit is contained in:
parent
b62e524bbb
commit
655b6fa97f
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
ui: show plan output warnings alongside placement failures and dry-run info when running a job through the web ui
|
||||||
|
```
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: BUSL-1.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Component from '@glimmer/component';
|
||||||
|
import { htmlSafe } from '@ember/template';
|
||||||
|
|
||||||
|
export default class JobEditorReviewComponent extends Component {
|
||||||
|
// Slightly formats the warning string to be more readable
|
||||||
|
get warnings() {
|
||||||
|
return htmlSafe(
|
||||||
|
(this.args.data.planOutput.warnings || '')
|
||||||
|
.replace(/\n/g, '<br>')
|
||||||
|
.replace(/\t/g, ' ')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,5 +12,8 @@ export default class JobPlan extends Model {
|
||||||
@attr() diff;
|
@attr() diff;
|
||||||
@fragmentArray('placement-failure', { defaultValue: () => [] })
|
@fragmentArray('placement-failure', { defaultValue: () => [] })
|
||||||
failedTGAllocs;
|
failedTGAllocs;
|
||||||
|
|
||||||
@hasMany('allocation') preemptions;
|
@hasMany('allocation') preemptions;
|
||||||
|
|
||||||
|
@attr('string') warnings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,22 +13,32 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="boxed-section
|
<Hds::Alert @type="inline" @color={{if @data.planOutput.failedTGAllocs "critical" "success"}} data-test-dry-run-message as |A|>
|
||||||
{{if @data.planOutput.failedTGAllocs 'is-warning' 'is-primary'}}"
|
<A.Title data-test-dry-run-title>Scheduler dry-run</A.Title>
|
||||||
data-test-dry-run-message
|
<A.Description data-test-dry-run-body>
|
||||||
>
|
|
||||||
<div class="boxed-section-head" data-test-dry-run-title>Scheduler dry-run</div>
|
|
||||||
<div class="boxed-section-body" data-test-dry-run-body>
|
|
||||||
{{#if @data.planOutput.failedTGAllocs}}
|
{{#if @data.planOutput.failedTGAllocs}}
|
||||||
{{#each @data.planOutput.failedTGAllocs as |placementFailure|}}
|
{{#each @data.planOutput.failedTGAllocs as |placementFailure|}}
|
||||||
<PlacementFailure @failedTGAlloc={{placementFailure}} />
|
<PlacementFailure @failedTGAlloc={{placementFailure}} />
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
{{else}}
|
||||||
All tasks successfully allocated.
|
All tasks successfully allocated.
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</A.Description>
|
||||||
</div>
|
</Hds::Alert>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
{{#if this.warnings}}
|
||||||
|
<Hds::Alert @type="inline" @color="warning" data-test-dry-run-warnings as |A|>
|
||||||
|
<A.Description data-test-dry-run-warning-body>
|
||||||
|
<p>
|
||||||
|
{{this.warnings}}
|
||||||
|
</p>
|
||||||
|
</A.Description>
|
||||||
|
</Hds::Alert>
|
||||||
|
<br>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if
|
{{#if
|
||||||
(and
|
(and
|
||||||
@data.planOutput.preemptions.isFulfilled @data.planOutput.preemptions.length
|
@data.planOutput.preemptions.isFulfilled @data.planOutput.preemptions.length
|
||||||
|
|
|
@ -139,10 +139,16 @@ export default function () {
|
||||||
const FailedTGAllocs =
|
const FailedTGAllocs =
|
||||||
body.Job.Unschedulable && generateFailedTGAllocs(body.Job);
|
body.Job.Unschedulable && generateFailedTGAllocs(body.Job);
|
||||||
|
|
||||||
|
const jobPlanWarnings = body.Job.WithWarnings && generateWarnings();
|
||||||
|
|
||||||
return new Response(
|
return new Response(
|
||||||
200,
|
200,
|
||||||
{},
|
{},
|
||||||
JSON.stringify({ FailedTGAllocs, Diff: generateDiff(req.params.id) })
|
JSON.stringify({
|
||||||
|
FailedTGAllocs,
|
||||||
|
Warnings: jobPlanWarnings,
|
||||||
|
Diff: generateDiff(req.params.id),
|
||||||
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1224,3 +1230,7 @@ function generateFailedTGAllocs(job, taskGroups) {
|
||||||
return hash;
|
return hash;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateWarnings() {
|
||||||
|
return '2 warnings:\n\n* Group "yourtask" has warnings: 1 error occurred:\n\t* Task "yourtask" has warnings: 1 error occurred:\n\t* 2 errors occurred:\n\t* Identity[vault_default] identities without an audience are insecure\n\t* Identity[vault_default] identities without an expiration are insecure\n* Task yourtask has an identity called vault_default but no vault block';
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import jobEditor from 'nomad-ui/tests/pages/components/job-editor';
|
||||||
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
|
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
|
||||||
import setupCodeMirror from 'nomad-ui/tests/helpers/codemirror';
|
import setupCodeMirror from 'nomad-ui/tests/helpers/codemirror';
|
||||||
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
|
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
|
||||||
|
import percySnapshot from '@percy/ember';
|
||||||
|
|
||||||
const Editor = create(jobEditor());
|
const Editor = create(jobEditor());
|
||||||
|
|
||||||
|
@ -290,8 +291,8 @@ module('Integration | Component | job-editor', function (hooks) {
|
||||||
await componentA11yAudit(this.element, assert);
|
await componentA11yAudit(this.element, assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the scheduler dry-run has warnings, the warnings are shown to the user', async function (assert) {
|
test('when the scheduler dry-run has errors, the errors are shown to the user', async function (assert) {
|
||||||
assert.expect(4);
|
assert.expect(5);
|
||||||
|
|
||||||
const spec = jsonJob({ Unschedulable: true });
|
const spec = jsonJob({ Unschedulable: true });
|
||||||
const job = await this.store.createRecord('job');
|
const job = await this.store.createRecord('job');
|
||||||
|
@ -312,7 +313,27 @@ module('Integration | Component | job-editor', function (hooks) {
|
||||||
'The scheduler dry-run message includes the warning from send back by the API'
|
'The scheduler dry-run message includes the warning from send back by the API'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert.notOk(
|
||||||
|
Editor.warningMessage.isPresent,
|
||||||
|
'The scheduler dry-run warning block is not present when there is an error but no warnings'
|
||||||
|
);
|
||||||
|
|
||||||
await componentA11yAudit(this.element, assert);
|
await componentA11yAudit(this.element, assert);
|
||||||
|
|
||||||
|
await percySnapshot(assert);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('When the scheduler dry-run has warnings, the warnings are shown to the user', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
const spec = jsonJob({ WithWarnings: true });
|
||||||
|
const job = await this.store.createRecord('job');
|
||||||
|
await renderNewJob(this, job);
|
||||||
|
await planJob(spec);
|
||||||
|
assert.ok(
|
||||||
|
Editor.warningMessage.isPresent,
|
||||||
|
'The scheduler dry-run warning block is shown to the user'
|
||||||
|
);
|
||||||
|
await percySnapshot(assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the scheduler dry-run has no warnings, a success message is shown to the user', async function (assert) {
|
test('when the scheduler dry-run has no warnings, a success message is shown to the user', async function (assert) {
|
||||||
|
|
|
@ -41,7 +41,12 @@ export default () => ({
|
||||||
scope: '[data-test-dry-run-message]',
|
scope: '[data-test-dry-run-message]',
|
||||||
title: text('[data-test-dry-run-title]'),
|
title: text('[data-test-dry-run-title]'),
|
||||||
body: text('[data-test-dry-run-body]'),
|
body: text('[data-test-dry-run-body]'),
|
||||||
errored: hasClass('is-warning'),
|
errored: hasClass('hds-alert--color-critical'),
|
||||||
succeeded: hasClass('is-primary'),
|
succeeded: hasClass('hds-alert--color-success'),
|
||||||
|
},
|
||||||
|
|
||||||
|
warningMessage: {
|
||||||
|
scope: '[data-test-dry-run-warnings]',
|
||||||
|
body: text('[data-test-dry-run-warning-body]'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue