backport of commit 7ab7edf9cdec6ceab92e47c1472a43d802de0486 (#19240)

Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com>
This commit is contained in:
hc-github-team-nomad-core 2023-11-30 09:58:50 -06:00 committed by GitHub
parent b62e524bbb
commit 655b6fa97f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 87 additions and 17 deletions

3
.changelog/19225.txt Normal file
View File

@ -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
```

View File

@ -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, '&nbsp;&nbsp;&nbsp;&nbsp;')
);
}
}

View File

@ -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;
} }

View File

@ -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

View File

@ -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';
}

View File

@ -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) {

View File

@ -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]'),
}, },
}); });