From f747cc79e47266916ff3c0664f09056ead478317 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Wed, 29 Nov 2017 20:03:39 -0800 Subject: [PATCH] Revert "UI Placement failures"" This reverts commits 141ecd8d9170f56c8302b5c776532e6d287a40e3 d2d838c2de08aac716a9431d9720b5c12bb19ba1 8099db433641429816e8479c6d23116269f744c0 86262b3ceff607627a9c9e0e25eb81b9b5ee739c eb4104ec528982e0ee6ae9a05fb0460e53139f3f 0e0e23e238017815bdb6dcfbc056275b3deaacca 6d45658d77fb4c40760a63cced71b74757356e48 b52a8176e85b9c6f13ec012f7fce2ec3df1c8751 --- ansi.nomad | 17 ---- ui/app/controllers/jobs/job/index.js | 4 - ui/app/models/evaluation.js | 27 ------ ui/app/models/job.js | 27 ------ ui/app/models/placement-failure.js | 20 ---- ui/app/models/task-group.js | 5 - ui/app/routes/jobs/job.js | 4 +- ui/app/serializers/evaluation.js | 27 ------ ui/app/serializers/job.js | 7 +- ui/app/styles/components.scss | 1 - ui/app/styles/components/simple-list.scss | 9 -- ui/app/templates/jobs/job/index.hbs | 85 ----------------- ui/mirage/common.js | 2 +- ui/mirage/config.js | 6 -- ui/mirage/factories/evaluation.js | 110 ---------------------- ui/mirage/factories/job.js | 20 +--- ui/mirage/scenarios/default.js | 3 +- ui/tests/acceptance/job-detail-test.js | 77 --------------- 18 files changed, 6 insertions(+), 445 deletions(-) delete mode 100644 ansi.nomad delete mode 100644 ui/app/models/evaluation.js delete mode 100644 ui/app/models/placement-failure.js delete mode 100644 ui/app/serializers/evaluation.js delete mode 100644 ui/app/styles/components/simple-list.scss delete mode 100644 ui/mirage/factories/evaluation.js diff --git a/ansi.nomad b/ansi.nomad deleted file mode 100644 index 1a70ace62..000000000 --- a/ansi.nomad +++ /dev/null @@ -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" ] - } - } - } -} diff --git a/ui/app/controllers/jobs/job/index.js b/ui/app/controllers/jobs/job/index.js index 2738cccf1..6e840df3e 100644 --- a/ui/app/controllers/jobs/job/index.js +++ b/ui/app/controllers/jobs/job/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); diff --git a/ui/app/models/evaluation.js b/ui/app/models/evaluation.js deleted file mode 100644 index 8afdf15ae..000000000 --- a/ui/app/models/evaluation.js +++ /dev/null @@ -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'), -}); diff --git a/ui/app/models/job.js b/ui/app/models/job.js index 4f33983ac..18d74b8ff 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -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() { diff --git a/ui/app/models/placement-failure.js b/ui/app/models/placement-failure.js deleted file mode 100644 index d711166b1..000000000 --- a/ui/app/models/placement-failure.js +++ /dev/null @@ -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(), -}); diff --git a/ui/app/models/task-group.js b/ui/app/models/task-group.js index b3998a4cd..56e83d59a 100644 --- a/ui/app/models/task-group.js +++ b/ui/app/models/task-group.js @@ -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'); }), diff --git a/ui/app/routes/jobs/job.js b/ui/app/routes/jobs/job.js index 4317a1e35..ae4c66ca1 100644 --- a/ui/app/routes/jobs/job.js +++ b/ui/app/routes/jobs/job.js @@ -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)); }, diff --git a/ui/app/serializers/evaluation.js b/ui/app/serializers/evaluation.js deleted file mode 100644 index 76a2b9b3c..000000000 --- a/ui/app/serializers/evaluation.js +++ /dev/null @@ -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); - }, -}); diff --git a/ui/app/serializers/job.js b/ui/app/serializers/job.js index 74eeb78b0..0f36f7524 100644 --- a/ui/app/serializers/job.js +++ b/ui/app/serializers/job.js @@ -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 }), - }, - }, }); }, }); diff --git a/ui/app/styles/components.scss b/ui/app/styles/components.scss index cb2162c2f..0b4545c21 100644 --- a/ui/app/styles/components.scss +++ b/ui/app/styles/components.scss @@ -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"; diff --git a/ui/app/styles/components/simple-list.scss b/ui/app/styles/components/simple-list.scss deleted file mode 100644 index b3c962fd5..000000000 --- a/ui/app/styles/components/simple-list.scss +++ /dev/null @@ -1,9 +0,0 @@ -.simple-list { - list-style: disc; - list-style-position: inside; - margin-left: 1.5rem; - - li { - margin-bottom: 0.5em; - } -} diff --git a/ui/app/templates/jobs/job/index.hbs b/ui/app/templates/jobs/job/index.hbs index 07a83d98f..f740bca74 100644 --- a/ui/app/templates/jobs/job/index.hbs +++ b/ui/app/templates/jobs/job/index.hbs @@ -47,57 +47,6 @@ - {{#if model.hasPlacementFailures}} -
-
- Placement Failures -
-
- {{#each model.taskGroups as |taskGroup|}} - {{#if taskGroup.placementFailures}} - {{#with taskGroup.placementFailures as |failures|}} -

- {{taskGroup.name}} - {{inc failures.coalescedFailures}} unplaced -

-
    - {{#if (eq failures.nodesEvaluated 0)}} -
  • No nodes were eligible for evaluation
  • - {{/if}} - {{#each-in failures.nodesAvailable as |datacenter available|}} - {{#if (eq available 0)}} -
  • No nodes are available in datacenter {{datacenter}}
  • - {{/if}} - {{/each-in}} - {{#each-in failures.classFiltered as |class count|}} -
  • Class {{class}} filtered {{count}} {{pluralize "node" count}}
  • - {{/each-in}} - {{#each-in failures.constraintFiltered as |constraint count|}} -
  • Constraint {{constraint}} filtered {{count}} {{pluralize "node" count}}
  • - {{/each-in}} - {{#if failures.nodesExhausted}} -
  • Resources exhausted on {{failures.nodesExhausted}} {{pluralize "node" failures.nodesExhausted}}
  • - {{/if}} - {{#each-in failures.classExhausted as |class count|}} -
  • Class {{class}} exhausted on {{count}} {{pluralize "node" count}}
  • - {{/each-in}} - {{#each-in failures.dimensionExhausted as |dimension count|}} -
  • Dimension {{dimension}} exhausted on {{count}} {{pluralize "node" count}}
  • - {{/each-in}} - {{#each-in failures.quotaExhausted as |quota dimension|}} -
  • Quota limit hit {{dimension}}
  • - {{/each-in}} - {{#each-in failures.scores as |name score|}} -
  • Score {{name}} = {{score}}
  • - {{/each-in}} -
- {{/with}} - {{/if}} - {{/each}} -
-
- {{/if}} - {{#if model.runningDeployment}}
@@ -160,39 +109,5 @@ {{/list-pagination}}
- -
-
- Evaluations -
-
- {{#list-table source=sortedEvaluations as |t|}} - {{#t.head}} - ID - Priority - Triggered By - Status - Placement Failures - {{/t.head}} - {{#t.body as |row|}} - - {{row.model.shortId}} - {{row.model.priority}} - {{row.model.triggeredBy}} - {{row.model.status}} - - {{#if (eq row.model.status "blocked")}} - N/A - In Progress - {{else if row.model.hasPlacementFailures}} - True - {{else}} - False - {{/if}} - - - {{/t.body}} - {{/list-table}} -
-
{{/gutter-menu}} diff --git a/ui/mirage/common.js b/ui/mirage/common.js index 75416b593..c9c5de517 100644 --- a/ui/mirage/common.js +++ b/ui/mirage/common.js @@ -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('')); } diff --git a/ui/mirage/config.js b/ui/mirage/config.js index dd23a35e7..64bbcea41 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -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 }); diff --git a/ui/mirage/factories/evaluation.js b/ui/mirage/factories/evaluation.js deleted file mode 100644 index 98cb7c03e..000000000 --- a/ui/mirage/factories/evaluation.js +++ /dev/null @@ -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, - }); -} diff --git a/ui/mirage/factories/job.js b/ui/mirage/factories/job.js index b18d16d71..eee780e47 100644 --- a/ui/mirage/factories/job.js +++ b/ui/mirage/factories/job.js @@ -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, - }); - } }, }); diff --git a/ui/mirage/scenarios/default.js b/ui/mirage/scenarios/default.js index 7ab761099..700d96b90 100644 --- a/ui/mirage/scenarios/default.js +++ b/ui/mirage/scenarios/default.js @@ -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); diff --git a/ui/tests/acceptance/job-detail-test.js b/ui/tests/acceptance/job-detail-test.js index cf3b1bcc5..512c572e4 100644 --- a/ui/tests/acceptance/job-detail-test.js +++ b/ui/tests/acceptance/job-detail-test.js @@ -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 ) {