Revert "UI Placement failures""
This reverts commits 141ecd8d9170f56c8302b5c776532e6d287a40e3 d2d838c2de08aac716a9431d9720b5c12bb19ba1 8099db433641429816e8479c6d23116269f744c0 86262b3ceff607627a9c9e0e25eb81b9b5ee739c eb4104ec528982e0ee6ae9a05fb0460e53139f3f 0e0e23e238017815bdb6dcfbc056275b3deaacca 6d45658d77fb4c40760a63cced71b74757356e48 b52a8176e85b9c6f13ec012f7fce2ec3df1c8751
This commit is contained in:
parent
961926880a
commit
f747cc79e4
17
ansi.nomad
17
ansi.nomad
|
@ -1,17 +0,0 @@
|
|||
job "ansi-test" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
|
||||
group "ansi-test" {
|
||||
count = 1
|
||||
|
||||
task "ansi-test" {
|
||||
driver = "raw_exec"
|
||||
|
||||
config {
|
||||
command = "node"
|
||||
args = [ "/Users/michael/work/ansi-test/index.js" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,10 +30,6 @@ export default Controller.extend(Sortable, WithNamespaceResetting, {
|
|||
listToSort: computed.alias('taskGroups'),
|
||||
sortedTaskGroups: computed.alias('listSorted'),
|
||||
|
||||
sortedEvaluations: computed('model.evaluations.@each.modifyIndex', function() {
|
||||
return (this.get('model.evaluations') || []).sortBy('modifyIndex').reverse();
|
||||
}),
|
||||
|
||||
actions: {
|
||||
gotoTaskGroup(taskGroup) {
|
||||
this.transitionToRoute('jobs.job.task-group', taskGroup.get('job'), taskGroup);
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
import { belongsTo } from 'ember-data/relationships';
|
||||
import { fragmentArray } from 'ember-data-model-fragments/attributes';
|
||||
import shortUUIDProperty from '../utils/properties/short-uuid';
|
||||
|
||||
const { computed } = Ember;
|
||||
|
||||
export default Model.extend({
|
||||
shortId: shortUUIDProperty('id'),
|
||||
priority: attr('number'),
|
||||
type: attr('string'),
|
||||
triggeredBy: attr('string'),
|
||||
status: attr('string'),
|
||||
statusDescription: attr('string'),
|
||||
failedTGAllocs: fragmentArray('placement-failure', { defaultValue: () => [] }),
|
||||
|
||||
hasPlacementFailures: computed.bool('failedTGAllocs.length'),
|
||||
|
||||
// TEMPORARY: https://github.com/emberjs/data/issues/5209
|
||||
originalJobId: attr('string'),
|
||||
|
||||
job: belongsTo('job'),
|
||||
|
||||
modifyIndex: attr('number'),
|
||||
});
|
|
@ -53,35 +53,8 @@ export default Model.extend({
|
|||
versions: hasMany('job-versions'),
|
||||
allocations: hasMany('allocations'),
|
||||
deployments: hasMany('deployments'),
|
||||
evaluations: hasMany('evaluations'),
|
||||
namespace: belongsTo('namespace'),
|
||||
|
||||
hasPlacementFailures: computed.bool('latestFailureEvaluation'),
|
||||
|
||||
latestEvaluation: computed('evaluations.@each.modifyIndex', 'evaluations.isPending', function() {
|
||||
const evaluations = this.get('evaluations');
|
||||
if (!evaluations || evaluations.get('isPending')) {
|
||||
return null;
|
||||
}
|
||||
return evaluations.sortBy('modifyIndex').get('lastObject');
|
||||
}),
|
||||
|
||||
latestFailureEvaluation: computed(
|
||||
'evaluations.@each.modifyIndex',
|
||||
'evaluations.isPending',
|
||||
function() {
|
||||
const evaluations = this.get('evaluations');
|
||||
if (!evaluations || evaluations.get('isPending')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const failureEvaluations = evaluations.filterBy('hasPlacementFailures');
|
||||
if (failureEvaluations) {
|
||||
return failureEvaluations.sortBy('modifyIndex').get('lastObject');
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
supportsDeployments: computed.equal('type', 'service'),
|
||||
|
||||
runningDeployment: computed('deployments.@each.status', function() {
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import attr from 'ember-data/attr';
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
|
||||
export default Fragment.extend({
|
||||
name: attr('string'),
|
||||
|
||||
coalescedFailures: attr('number'),
|
||||
|
||||
nodesEvaluated: attr('number'),
|
||||
nodesExhausted: attr('number'),
|
||||
|
||||
// Maps keyed by relevant dimension (dc, class, constraint, etc) with count values
|
||||
nodesAvailable: attr(),
|
||||
classFiltered: attr(),
|
||||
constraintFiltered: attr(),
|
||||
classExhausted: attr(),
|
||||
dimensionExhausted: attr(),
|
||||
quotaExhausted: attr(),
|
||||
scores: attr(),
|
||||
});
|
|
@ -24,11 +24,6 @@ export default Fragment.extend({
|
|||
|
||||
reservedEphemeralDisk: attr('number'),
|
||||
|
||||
placementFailures: computed('job.latestFailureEvaluation.failedTGAllocs.[]', function() {
|
||||
const placementFailures = this.get('job.latestFailureEvaluation.failedTGAllocs');
|
||||
return placementFailures && placementFailures.findBy('name', this.get('name'));
|
||||
}),
|
||||
|
||||
queuedOrStartingAllocs: computed('summary.{queuedAllocs,startingAllocs}', function() {
|
||||
return this.get('summary.queuedAllocs') + this.get('summary.startingAllocs');
|
||||
}),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
import notifyError from 'nomad-ui/utils/notify-error';
|
||||
|
||||
const { Route, RSVP, inject } = Ember;
|
||||
const { Route, inject } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
store: inject.service(),
|
||||
|
@ -17,7 +17,7 @@ export default Route.extend({
|
|||
return this.get('store')
|
||||
.findRecord('job', fullId, { reload: true })
|
||||
.then(job => {
|
||||
return RSVP.all([job.get('allocations'), job.get('evaluations')]).then(() => job);
|
||||
return job.get('allocations').then(() => job);
|
||||
})
|
||||
.catch(notifyError(this));
|
||||
},
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
const { inject, get, assign } = Ember;
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
system: inject.service(),
|
||||
|
||||
normalize(typeHash, hash) {
|
||||
hash.FailedTGAllocs = Object.keys(hash.FailedTGAllocs || {}).map(key => {
|
||||
return assign({ Name: key }, get(hash, `FailedTGAllocs.${key}`) || {});
|
||||
});
|
||||
|
||||
hash.PlainJobId = hash.JobID;
|
||||
hash.Namespace =
|
||||
hash.Namespace ||
|
||||
get(hash, 'Job.Namespace') ||
|
||||
this.get('system.activeNamespace.id') ||
|
||||
'default';
|
||||
hash.JobID = JSON.stringify([hash.JobID, hash.Namespace]);
|
||||
|
||||
// TEMPORARY: https://github.com/emberjs/data/issues/5209
|
||||
hash.OriginalJobId = hash.JobID;
|
||||
|
||||
return this._super(typeHash, hash);
|
||||
},
|
||||
});
|
|
@ -19,7 +19,7 @@ export default ApplicationSerializer.extend({
|
|||
// Transform the map-based JobSummary object into an array-based
|
||||
// JobSummary fragment list
|
||||
hash.TaskGroupSummaries = Object.keys(get(hash, 'JobSummary.Summary') || {}).map(key => {
|
||||
const allocStats = get(hash, `JobSummary.Summary.${key}`) || {};
|
||||
const allocStats = get(hash, `JobSummary.Summary.${key}`);
|
||||
const summary = { Name: key };
|
||||
|
||||
Object.keys(allocStats).forEach(
|
||||
|
@ -65,11 +65,6 @@ export default ApplicationSerializer.extend({
|
|||
related: buildURL(`${jobURL}/deployments`, { namespace: namespace }),
|
||||
},
|
||||
},
|
||||
evaluations: {
|
||||
links: {
|
||||
related: buildURL(`${jobURL}/evaluations`, { namespace: namespace }),
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
@import "./components/loading-spinner";
|
||||
@import "./components/metrics";
|
||||
@import "./components/node-status-light";
|
||||
@import "./components/simple-list";
|
||||
@import "./components/status-text";
|
||||
@import "./components/timeline";
|
||||
@import "./components/tooltip";
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
.simple-list {
|
||||
list-style: disc;
|
||||
list-style-position: inside;
|
||||
margin-left: 1.5rem;
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
|
@ -47,57 +47,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{#if model.hasPlacementFailures}}
|
||||
<div class="boxed-section is-danger placement-failures">
|
||||
<div class="boxed-section-head">
|
||||
Placement Failures
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{#each model.taskGroups as |taskGroup|}}
|
||||
{{#if taskGroup.placementFailures}}
|
||||
{{#with taskGroup.placementFailures as |failures|}}
|
||||
<h3 class="title is-5">
|
||||
{{taskGroup.name}}
|
||||
<span class="badge is-light">{{inc failures.coalescedFailures}} unplaced</span>
|
||||
</h3>
|
||||
<ul class="simple-list">
|
||||
{{#if (eq failures.nodesEvaluated 0)}}
|
||||
<li>No nodes were eligible for evaluation</li>
|
||||
{{/if}}
|
||||
{{#each-in failures.nodesAvailable as |datacenter available|}}
|
||||
{{#if (eq available 0)}}
|
||||
<li>No nodes are available in datacenter {{datacenter}}</li>
|
||||
{{/if}}
|
||||
{{/each-in}}
|
||||
{{#each-in failures.classFiltered as |class count|}}
|
||||
<li>Class {{class}} filtered {{count}} {{pluralize "node" count}}</li>
|
||||
{{/each-in}}
|
||||
{{#each-in failures.constraintFiltered as |constraint count|}}
|
||||
<li>Constraint <code>{{constraint}}</code> filtered {{count}} {{pluralize "node" count}}</li>
|
||||
{{/each-in}}
|
||||
{{#if failures.nodesExhausted}}
|
||||
<li>Resources exhausted on {{failures.nodesExhausted}} {{pluralize "node" failures.nodesExhausted}}</li>
|
||||
{{/if}}
|
||||
{{#each-in failures.classExhausted as |class count|}}
|
||||
<li>Class {{class}} exhausted on {{count}} {{pluralize "node" count}}</li>
|
||||
{{/each-in}}
|
||||
{{#each-in failures.dimensionExhausted as |dimension count|}}
|
||||
<li>Dimension {{dimension}} exhausted on {{count}} {{pluralize "node" count}}</li>
|
||||
{{/each-in}}
|
||||
{{#each-in failures.quotaExhausted as |quota dimension|}}
|
||||
<li>Quota limit hit {{dimension}}</li>
|
||||
{{/each-in}}
|
||||
{{#each-in failures.scores as |name score|}}
|
||||
<li>Score {{name}} = {{score}}</li>
|
||||
{{/each-in}}
|
||||
</ul>
|
||||
{{/with}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if model.runningDeployment}}
|
||||
<div class="boxed-section is-info active-deployment">
|
||||
<div class="boxed-section-head">
|
||||
|
@ -160,39 +109,5 @@
|
|||
{{/list-pagination}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Evaluations
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed evaluations">
|
||||
{{#list-table source=sortedEvaluations as |t|}}
|
||||
{{#t.head}}
|
||||
<th>ID</th>
|
||||
<th>Priority</th>
|
||||
<th>Triggered By</th>
|
||||
<th>Status</th>
|
||||
<th>Placement Failures</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr>
|
||||
<td>{{row.model.shortId}}</td>
|
||||
<td>{{row.model.priority}}</td>
|
||||
<td>{{row.model.triggeredBy}}</td>
|
||||
<td>{{row.model.status}}</td>
|
||||
<td>
|
||||
{{#if (eq row.model.status "blocked")}}
|
||||
N/A - In Progress
|
||||
{{else if row.model.hasPlacementFailures}}
|
||||
True
|
||||
{{else}}
|
||||
False
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
|
|
|
@ -71,7 +71,7 @@ function ipv6() {
|
|||
for (var i = 0; i < 8; i++) {
|
||||
var subnet = [];
|
||||
for (var char = 0; char < 4; char++) {
|
||||
subnet.push(faker.random.number(15).toString(16));
|
||||
subnet.push(faker.random.number(16).toString(16));
|
||||
}
|
||||
subnets.push(subnet.join(''));
|
||||
}
|
||||
|
|
|
@ -59,12 +59,6 @@ export default function() {
|
|||
|
||||
this.get('/deployment/:id');
|
||||
|
||||
this.get('/job/:id/evaluations', function({ evaluations }, { params }) {
|
||||
return this.serialize(evaluations.where({ jobId: params.id }));
|
||||
});
|
||||
|
||||
this.get('/evaluation/:id');
|
||||
|
||||
this.get('/deployment/allocations/:id', function(schema, { params }) {
|
||||
const job = schema.jobs.find(schema.deployments.find(params.id).jobId);
|
||||
const allocations = schema.allocations.where({ jobId: job.id });
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import { Factory, faker, trait } from 'ember-cli-mirage';
|
||||
import { provide, pickOne } from '../utils';
|
||||
import { DATACENTERS } from '../common';
|
||||
|
||||
const EVAL_TYPES = ['system', 'service', 'batch'];
|
||||
const EVAL_STATUSES = ['blocked', 'pending', 'complete', 'failed', 'canceled'];
|
||||
const EVAL_TRIGGERED_BY = [
|
||||
'job-register',
|
||||
'job-deregister',
|
||||
'periodic-job',
|
||||
'node-update',
|
||||
'scheduled',
|
||||
'roling-update',
|
||||
'deployment-watcher',
|
||||
'failed-follow-up',
|
||||
'max-plan-attempts',
|
||||
];
|
||||
|
||||
const generateCountMap = (keysCount, list) => () => {
|
||||
const sample = Array(keysCount)
|
||||
.fill(null)
|
||||
.map(() => pickOne(list))
|
||||
.uniq();
|
||||
return sample.reduce((hash, key) => {
|
||||
hash[key] = faker.random.number({ min: 1, max: 5 });
|
||||
return hash;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const generateNodesAvailable = generateCountMap(5, DATACENTERS);
|
||||
const generateClassFiltered = generateCountMap(3, provide(10, faker.hacker.abbreviation));
|
||||
const generateClassExhausted = generateClassFiltered;
|
||||
const generateDimensionExhausted = generateCountMap(1, ['cpu', 'mem', 'disk', 'iops']);
|
||||
const generateQuotaExhausted = generateDimensionExhausted;
|
||||
const generateScores = generateCountMap(1, ['binpack', 'job-anti-affinity']);
|
||||
const generateConstraintFiltered = generateCountMap(2, [
|
||||
'prop = val',
|
||||
'driver = docker',
|
||||
'arch = x64',
|
||||
]);
|
||||
|
||||
export default Factory.extend({
|
||||
id: () => faker.random.uuid(),
|
||||
|
||||
priority: () => faker.random.number(100),
|
||||
|
||||
type: faker.list.random(...EVAL_TYPES),
|
||||
triggeredBy: faker.list.random(...EVAL_TRIGGERED_BY),
|
||||
status: faker.list.random(...EVAL_STATUSES),
|
||||
statusDescription: () => faker.lorem.sentence(),
|
||||
|
||||
failedTGAllocs: null,
|
||||
|
||||
modifyIndex: () => faker.random.number({ min: 10, max: 2000 }),
|
||||
|
||||
withPlacementFailures: trait({
|
||||
status: faker.list.random(...EVAL_STATUSES.without('blocked')),
|
||||
afterCreate(evaluation, server) {
|
||||
assignJob(evaluation, server);
|
||||
const taskGroups = server.db.taskGroups.where({ jobId: evaluation.jobId });
|
||||
|
||||
const taskGroupNames = taskGroups.mapBy('name');
|
||||
const failedTaskGroupsCount = faker.random.number({ min: 1, max: taskGroupNames.length });
|
||||
const failedTaskGroupNames = [];
|
||||
for (let i = 0; i < failedTaskGroupsCount; i++) {
|
||||
failedTaskGroupNames.push(
|
||||
...taskGroupNames.splice(faker.random.number(taskGroupNames.length), 1)
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
return hash;
|
||||
}, {});
|
||||
|
||||
evaluation.update({
|
||||
failedTGAllocs: placementFailures,
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
afterCreate(evaluation, server) {
|
||||
assignJob(evaluation, server);
|
||||
},
|
||||
});
|
||||
|
||||
function assignJob(evaluation, server) {
|
||||
Ember.assert(
|
||||
'[Mirage] No jobs! make sure jobs are created before evaluations',
|
||||
server.db.jobs.length
|
||||
);
|
||||
|
||||
const job = evaluation.jobId ? server.db.jobs.find(evaluation.jobId) : pickOne(server.db.jobs);
|
||||
evaluation.update({
|
||||
jobId: job.id,
|
||||
});
|
||||
}
|
|
@ -14,7 +14,7 @@ export default Factory.extend({
|
|||
|
||||
region: () => 'global',
|
||||
type: faker.list.random(...JOB_TYPES),
|
||||
priority: () => faker.random.number(100),
|
||||
priority: () => faker.random.number(200),
|
||||
all_at_once: faker.random.boolean,
|
||||
status: faker.list.random(...JOB_STATUSES),
|
||||
datacenters: provider(
|
||||
|
@ -41,12 +41,6 @@ export default Factory.extend({
|
|||
// When true, deployments for the job will always have a 'running' status
|
||||
activeDeployment: false,
|
||||
|
||||
// When true, an evaluation with a high modify index and placement failures is created
|
||||
failedPlacements: false,
|
||||
|
||||
// When true, no evaluations have failed placements
|
||||
noFailedPlacements: false,
|
||||
|
||||
afterCreate(job, server) {
|
||||
const groups = server.createList('task-group', job.groupsCount, {
|
||||
job,
|
||||
|
@ -90,17 +84,5 @@ export default Factory.extend({
|
|||
activeDeployment: job.activeDeployment,
|
||||
});
|
||||
});
|
||||
|
||||
server.createList('evaluation', faker.random.number({ min: 1, max: 5 }), { job });
|
||||
if (!job.noFailedPlacements) {
|
||||
server.createList('evaluation', faker.random.number(3), 'withPlacementFailures', { job });
|
||||
}
|
||||
|
||||
if (job.failedPlacements) {
|
||||
server.create('evaluation', 'withPlacementFailures', {
|
||||
job,
|
||||
modifyIndex: 4000,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,8 +4,7 @@ export default function(server) {
|
|||
|
||||
server.createList('namespace', 3);
|
||||
|
||||
server.createList('job', 10);
|
||||
server.createList('job', 5, { failedPlacements: true });
|
||||
server.createList('job', 15);
|
||||
|
||||
server.createList('token', 3);
|
||||
logTokens(server);
|
||||
|
|
|
@ -296,83 +296,6 @@ test('the active deployment section can be expanded to show task groups and allo
|
|||
});
|
||||
});
|
||||
|
||||
test('the evaluations table lists evaluations sorted by modify index', function(assert) {
|
||||
job = server.create('job');
|
||||
const evaluations = server.db.evaluations
|
||||
.where({ jobId: job.id })
|
||||
.sortBy('modifyIndex')
|
||||
.reverse();
|
||||
|
||||
visit(`/jobs/${job.id}`);
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(
|
||||
findAll('.evaluations tbody tr').length,
|
||||
evaluations.length,
|
||||
'A row for each evaluation'
|
||||
);
|
||||
|
||||
evaluations.forEach((evaluation, index) => {
|
||||
const row = $(findAll('.evaluations tbody tr')[index]);
|
||||
assert.equal(
|
||||
row.find('td:eq(0)').text(),
|
||||
evaluation.id.split('-')[0],
|
||||
`Short ID, row ${index}`
|
||||
);
|
||||
});
|
||||
|
||||
const firstEvaluation = evaluations[0];
|
||||
const row = $(findAll('.evaluations tbody tr')[0]);
|
||||
assert.equal(row.find('td:eq(1)').text(), '' + firstEvaluation.priority, 'Priority');
|
||||
assert.equal(row.find('td:eq(2)').text(), firstEvaluation.triggeredBy, 'Triggered By');
|
||||
assert.equal(row.find('td:eq(3)').text(), firstEvaluation.status, 'Status');
|
||||
});
|
||||
});
|
||||
|
||||
test('when the job has placement failures, they are called out', function(assert) {
|
||||
job = server.create('job', { failedPlacements: true });
|
||||
const failedEvaluation = server.db.evaluations
|
||||
.where({ jobId: job.id })
|
||||
.filter(evaluation => evaluation.failedTGAllocs)
|
||||
.sortBy('modifyIndex')
|
||||
.reverse()[0];
|
||||
|
||||
const failedTaskGroupNames = Object.keys(failedEvaluation.failedTGAllocs);
|
||||
|
||||
visit(`/jobs/${job.id}`);
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(find('.placement-failures'), 'Placement failures section found');
|
||||
|
||||
const taskGroupLabels = findAll('.placement-failures h3.title').map(title =>
|
||||
title.textContent.trim()
|
||||
);
|
||||
failedTaskGroupNames.forEach(name => {
|
||||
assert.ok(
|
||||
taskGroupLabels.find(label => label.includes(name)),
|
||||
`${name} included in placement failures list`
|
||||
);
|
||||
assert.ok(
|
||||
taskGroupLabels.find(label =>
|
||||
label.includes(failedEvaluation.failedTGAllocs[name].CoalescedFailures + 1)
|
||||
),
|
||||
'The number of unplaced allocs = CoalescedFailures + 1'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('when the job has no placement failures, the placement failures section is gone', function(
|
||||
assert
|
||||
) {
|
||||
job = server.create('job', { noFailedPlacements: true });
|
||||
visit(`/jobs/${job.id}`);
|
||||
|
||||
andThen(() => {
|
||||
assert.notOk(find('.placement-failures'), 'Placement failures section not found');
|
||||
});
|
||||
});
|
||||
|
||||
test('when the job is not found, an error message is shown, but the URL persists', function(
|
||||
assert
|
||||
) {
|
||||
|
|
Loading…
Reference in a new issue