mirror of
https://github.com/kemko/nomad.git
synced 2026-01-05 18:05:42 +03:00
Test coverage for scheduler dry-run addition to the plan page
This commit is contained in:
@@ -210,7 +210,7 @@ export default Model.extend({
|
||||
try {
|
||||
// If the definition is already JSON then it doesn't need to be parsed.
|
||||
const json = JSON.parse(definition);
|
||||
this.set('_newDefinitionJSON', definition);
|
||||
this.set('_newDefinitionJSON', json);
|
||||
this.setIDByPayload(json);
|
||||
promise = RSVP.resolve(definition);
|
||||
} catch (err) {
|
||||
|
||||
@@ -74,9 +74,9 @@
|
||||
{{job-diff data-test-plan-output diff=planOutput.diff verbose=false}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section {{if planOutput.failedTGAllocs "is-warning" "is-primary"}}">
|
||||
<div class="boxed-section-head">Scheduler dry-run</div>
|
||||
<div class="boxed-section-body">
|
||||
<div class="boxed-section {{if planOutput.failedTGAllocs "is-warning" "is-primary"}}" data-test-dry-run-message>
|
||||
<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 planOutput.failedTGAllocs}}
|
||||
{{#each planOutput.failedTGAllocs as |placementFailure|}}
|
||||
{{placement-failure failedTGAlloc=placementFailure}}
|
||||
|
||||
@@ -3,6 +3,7 @@ import Response from 'ember-cli-mirage/response';
|
||||
import { HOSTS } from './common';
|
||||
import { logFrames, logEncode } from './data/logs';
|
||||
import { generateDiff } from './factories/job-version';
|
||||
import { generateTaskGroupFailures } from './factories/evaluation';
|
||||
|
||||
const { copy } = Ember;
|
||||
|
||||
@@ -56,7 +57,7 @@ export default function() {
|
||||
})
|
||||
);
|
||||
|
||||
this.post('/jobs', function({ jobs }, req) {
|
||||
this.post('/jobs', function(schema, req) {
|
||||
const body = JSON.parse(req.requestBody);
|
||||
|
||||
if (!body.Job) return new Response(400, {}, 'Job is a required field on the request payload');
|
||||
@@ -64,7 +65,7 @@ export default function() {
|
||||
return okEmpty();
|
||||
});
|
||||
|
||||
this.post('/jobs/parse', function({ jobs }, req) {
|
||||
this.post('/jobs/parse', function(schema, req) {
|
||||
const body = JSON.parse(req.requestBody);
|
||||
|
||||
if (!body.JobHCL)
|
||||
@@ -84,13 +85,19 @@ export default function() {
|
||||
return new Response(200, {}, this.serialize(job));
|
||||
});
|
||||
|
||||
this.post('/job/:id/plan', function({ jobs }, req) {
|
||||
this.post('/job/:id/plan', function(schema, req) {
|
||||
const body = JSON.parse(req.requestBody);
|
||||
|
||||
if (!body.Job) return new Response(400, {}, 'Job is a required field on the request payload');
|
||||
if (!body.Diff) return new Response(400, {}, 'Expected Diff to be true');
|
||||
|
||||
return new Response(200, {}, JSON.stringify({ Diff: generateDiff(req.params.id) }));
|
||||
const FailedTGAllocs = body.Job.Unschedulable && generateFailedTGAllocs(body.Job);
|
||||
|
||||
return new Response(
|
||||
200,
|
||||
{},
|
||||
JSON.stringify({ FailedTGAllocs, Diff: generateDiff(req.params.id) })
|
||||
);
|
||||
});
|
||||
|
||||
this.get(
|
||||
@@ -319,3 +326,16 @@ function filterKeys(object, ...keys) {
|
||||
function okEmpty() {
|
||||
return new Response(200, {}, '{}');
|
||||
}
|
||||
|
||||
function generateFailedTGAllocs(job, taskGroups) {
|
||||
const taskGroupsFromSpec = job.TaskGroups && job.TaskGroups.mapBy('Name');
|
||||
|
||||
let tgNames = ['tg-one', 'tg-two'];
|
||||
if (taskGroupsFromSpec && taskGroupsFromSpec.length) tgNames = taskGroupsFromSpec;
|
||||
if (taskGroups && taskGroups.length) tgNames = taskGroups;
|
||||
|
||||
return tgNames.reduce((hash, tgName) => {
|
||||
hash[tgName] = generateTaskGroupFailures();
|
||||
return hash;
|
||||
}, {});
|
||||
}
|
||||
|
||||
@@ -72,19 +72,7 @@ export default Factory.extend({
|
||||
}
|
||||
|
||||
const placementFailures = failedTaskGroupNames.reduce((hash, name) => {
|
||||
hash[name] = {
|
||||
CoalescedFailures: faker.random.number({ min: 1, max: 20 }),
|
||||
NodesEvaluated: faker.random.number({ min: 1, max: 100 }),
|
||||
NodesExhausted: faker.random.number({ min: 1, max: 100 }),
|
||||
|
||||
NodesAvailable: Math.random() > 0.7 ? generateNodesAvailable() : null,
|
||||
ClassFiltered: Math.random() > 0.7 ? generateClassFiltered() : null,
|
||||
ConstraintFiltered: Math.random() > 0.7 ? generateConstraintFiltered() : null,
|
||||
ClassExhausted: Math.random() > 0.7 ? generateClassExhausted() : null,
|
||||
DimensionExhausted: Math.random() > 0.7 ? generateDimensionExhausted() : null,
|
||||
QuotaExhausted: Math.random() > 0.7 ? generateQuotaExhausted() : null,
|
||||
Scores: Math.random() > 0.7 ? generateScores() : null,
|
||||
};
|
||||
hash[name] = generateTaskGroupFailures();
|
||||
return hash;
|
||||
}, {});
|
||||
|
||||
@@ -111,3 +99,19 @@ function assignJob(evaluation, server) {
|
||||
job_id: job.id,
|
||||
});
|
||||
}
|
||||
|
||||
export function generateTaskGroupFailures() {
|
||||
return {
|
||||
CoalescedFailures: faker.random.number({ min: 1, max: 20 }),
|
||||
NodesEvaluated: faker.random.number({ min: 1, max: 100 }),
|
||||
NodesExhausted: faker.random.number({ min: 1, max: 100 }),
|
||||
|
||||
NodesAvailable: Math.random() > 0.7 ? generateNodesAvailable() : null,
|
||||
ClassFiltered: Math.random() > 0.7 ? generateClassFiltered() : null,
|
||||
ConstraintFiltered: Math.random() > 0.7 ? generateConstraintFiltered() : null,
|
||||
ClassExhausted: Math.random() > 0.7 ? generateClassExhausted() : null,
|
||||
DimensionExhausted: Math.random() > 0.7 ? generateDimensionExhausted() : null,
|
||||
QuotaExhausted: Math.random() > 0.7 ? generateQuotaExhausted() : null,
|
||||
Scores: Math.random() > 0.7 ? generateScores() : null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance';
|
||||
import JobRun from 'nomad-ui/tests/pages/jobs/run';
|
||||
|
||||
const newJobName = 'new-job';
|
||||
const newJobTaskGroupName = 'redis';
|
||||
|
||||
const jsonJob = overrides => {
|
||||
return JSON.stringify(
|
||||
@@ -15,15 +16,17 @@ const jsonJob = overrides => {
|
||||
Namespace: 'default',
|
||||
Datacenters: ['dc1'],
|
||||
Priority: 50,
|
||||
TaskGroups: {
|
||||
redis: {
|
||||
Tasks: {
|
||||
redis: {
|
||||
TaskGroups: [
|
||||
{
|
||||
Name: newJobTaskGroupName,
|
||||
Tasks: [
|
||||
{
|
||||
Name: 'redis',
|
||||
Driver: 'docker',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides
|
||||
),
|
||||
@@ -37,7 +40,7 @@ job "${newJobName}" {
|
||||
namespace = "default"
|
||||
datacenters = ["dc1"]
|
||||
|
||||
task "redis" {
|
||||
task "${newJobTaskGroupName}" {
|
||||
driver = "docker"
|
||||
}
|
||||
}
|
||||
@@ -313,3 +316,52 @@ test('when submitting a job to a different namespace, the redirect to the job ov
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('when the scheduler dry-run has warnings, the warnings are shown to the user', function(assert) {
|
||||
// Unschedulable is a hint to Mirage to respond with warnings from the plan endpoint
|
||||
const spec = jsonJob({ Unschedulable: true });
|
||||
|
||||
JobRun.visit();
|
||||
|
||||
andThen(() => {
|
||||
JobRun.editor.fillIn(spec);
|
||||
JobRun.plan();
|
||||
});
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(
|
||||
JobRun.dryRunMessage.errored,
|
||||
'The scheduler dry-run message is in the warning state'
|
||||
);
|
||||
assert.notOk(
|
||||
JobRun.dryRunMessage.succeeded,
|
||||
'The success message is not shown in addition to the warning message'
|
||||
);
|
||||
assert.ok(
|
||||
JobRun.dryRunMessage.body.includes(newJobTaskGroupName),
|
||||
'The scheduler dry-run message includes the warning from send back by the API'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('when the scheduler dry-run has no warnings, a success message is shown to the user', function(assert) {
|
||||
const spec = hclJob();
|
||||
|
||||
JobRun.visit();
|
||||
|
||||
andThen(() => {
|
||||
JobRun.editor.fillIn(spec);
|
||||
JobRun.plan();
|
||||
});
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(
|
||||
JobRun.dryRunMessage.succeeded,
|
||||
'The scheduler dry-run message is in the success state'
|
||||
);
|
||||
assert.notOk(
|
||||
JobRun.dryRunMessage.errored,
|
||||
'The warning message is not shown in addition to the success message'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { clickable, create, isPresent, text, visitable } from 'ember-cli-page-object';
|
||||
import { clickable, create, hasClass, isPresent, text, visitable } from 'ember-cli-page-object';
|
||||
import { codeFillable, code } from 'nomad-ui/tests/pages/helpers/codemirror';
|
||||
|
||||
import error from 'nomad-ui/tests/pages/components/error';
|
||||
@@ -35,4 +35,12 @@ export default create({
|
||||
contents: code('[data-test-editor]'),
|
||||
fillIn: codeFillable('[data-test-editor]'),
|
||||
},
|
||||
|
||||
dryRunMessage: {
|
||||
scope: '[data-test-dry-run-message]',
|
||||
title: text('[data-test-dry-run-title]'),
|
||||
body: text('[data-test-dry-run-body]'),
|
||||
errored: hasClass('is-warning'),
|
||||
succeeded: hasClass('is-primary'),
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user