diff --git a/ui/app/components/allocation-row.js b/ui/app/components/allocation-row.js index ef310d7edfaf..b102f79d96ef 100644 --- a/ui/app/components/allocation-row.js +++ b/ui/app/components/allocation-row.js @@ -73,6 +73,9 @@ export default Component.extend({ function qualifyAllocation() { const allocation = this.allocation; + + // Make sure the allocation is a complete record and not a partial so we + // can show information such as preemptions and rescheduled allocation. return allocation.reload().then(() => { this.fetchStats.perform(); diff --git a/ui/app/models/allocation.js b/ui/app/models/allocation.js index 6452a2bcb215..3d6b95562476 100644 --- a/ui/app/models/allocation.js +++ b/ui/app/models/allocation.js @@ -66,11 +66,22 @@ export default Model.extend({ return classMap[this.clientStatus] || 'is-dark'; }), - taskGroup: computed('taskGroupName', 'job.taskGroups.[]', function() { + isOld: computed('jobVersion', 'job.version', function() { + return this.jobVersion !== this.get('job.version'); + }), + + taskGroup: computed('isOld', 'jobTaskGroup', 'allocationTaskGroup', function() { + if (!this.isOld) return this.jobTaskGroup; + return this.allocationTaskGroup; + }), + + jobTaskGroup: computed('taskGroupName', 'job.taskGroups.[]', function() { const taskGroups = this.get('job.taskGroups'); return taskGroups && taskGroups.findBy('name', this.taskGroupName); }), + allocationTaskGroup: fragment('task-group', { defaultValue: null }), + unhealthyDrivers: computed('taskGroup.drivers.[]', 'node.unhealthyDriverNames.[]', function() { const taskGroupUnhealthyDrivers = this.get('taskGroup.drivers'); const nodeUnhealthyDrivers = this.get('node.unhealthyDriverNames'); diff --git a/ui/app/serializers/allocation.js b/ui/app/serializers/allocation.js index 89e321a62e4e..43ace830f627 100644 --- a/ui/app/serializers/allocation.js +++ b/ui/app/serializers/allocation.js @@ -2,6 +2,12 @@ import { inject as service } from '@ember/service'; import { get } from '@ember/object'; import ApplicationSerializer from './application'; +const taskGroupFromJob = (job, taskGroupName) => { + const taskGroups = job && job.TaskGroups; + const taskGroup = taskGroups && taskGroups.find(group => group.Name === taskGroupName); + return taskGroup ? taskGroup : null; +}; + export default ApplicationSerializer.extend({ system: service(), @@ -54,6 +60,9 @@ export default ApplicationSerializer.extend({ // When present, the resources are nested under AllocatedResources.Shared hash.AllocatedResources = hash.AllocatedResources && hash.AllocatedResources.Shared; + // The Job definition for an allocation is only included in findRecord responses. + hash.AllocationTaskGroup = !hash.Job ? null : taskGroupFromJob(hash.Job, hash.TaskGroup); + return this._super(typeHash, hash); }, }); diff --git a/ui/mirage/factories/allocation.js b/ui/mirage/factories/allocation.js index 6316dc0abbc4..c91ba9c95ae4 100644 --- a/ui/mirage/factories/allocation.js +++ b/ui/mirage/factories/allocation.js @@ -13,7 +13,7 @@ const REF_TIME = new Date(); export default Factory.extend({ id: i => (i >= 100 ? `${UUIDS[i % 100]}-${i}` : UUIDS[i]), - jobVersion: () => faker.random.number(10), + jobVersion: 1, modifyIndex: () => faker.random.number({ min: 10, max: 2000 }), modifyTime: () => faker.date.past(2 / 365, REF_TIME) * 1000000, diff --git a/ui/mirage/factories/job.js b/ui/mirage/factories/job.js index 617d9239a7ae..90e2f27b3949 100644 --- a/ui/mirage/factories/job.js +++ b/ui/mirage/factories/job.js @@ -18,6 +18,8 @@ export default Factory.extend({ return this.id; }, + version: 1, + groupsCount: () => faker.random.number({ min: 1, max: 2 }), region: () => 'global', diff --git a/ui/tests/unit/models/allocation-test.js b/ui/tests/unit/models/allocation-test.js new file mode 100644 index 000000000000..dce5ed10d7fc --- /dev/null +++ b/ui/tests/unit/models/allocation-test.js @@ -0,0 +1,98 @@ +import { run } from '@ember/runloop'; +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | allocation', function(hooks) { + setupTest(hooks); + hooks.beforeEach(function() { + this.store = this.owner.lookup('service:store'); + }); + + test("When the allocation's job version matches the job's version, the task group comes from the job.", function(assert) { + const job = run(() => + this.store.createRecord('job', { + name: 'this-job', + version: 1, + taskGroups: [ + { + name: 'from-job', + count: 1, + task: [], + }, + ], + }) + ); + + const allocation = run(() => + this.store.createRecord('allocation', { + job, + jobVersion: 1, + taskGroupName: 'from-job', + allocationTaskGroup: { + name: 'from-allocation', + count: 1, + task: [], + }, + }) + ); + + assert.equal(allocation.get('taskGroup.name'), 'from-job'); + }); + + test("When the allocation's job version does not match the job's version, the task group comes from the alloc.", function(assert) { + const job = run(() => + this.store.createRecord('job', { + name: 'this-job', + version: 1, + taskGroups: [ + { + name: 'from-job', + count: 1, + task: [], + }, + ], + }) + ); + + const allocation = run(() => + this.store.createRecord('allocation', { + job, + jobVersion: 2, + taskGroupName: 'from-job', + allocationTaskGroup: { + name: 'from-allocation', + count: 1, + task: [], + }, + }) + ); + + assert.equal(allocation.get('taskGroup.name'), 'from-allocation'); + }); + + test("When the allocation's job version does not match the job's version and the allocation has no task group, then task group is null", async function(assert) { + const job = run(() => + this.store.createRecord('job', { + name: 'this-job', + version: 1, + taskGroups: [ + { + name: 'from-job', + count: 1, + task: [], + }, + ], + }) + ); + + const allocation = run(() => + this.store.createRecord('allocation', { + job, + jobVersion: 2, + taskGroupName: 'from-job', + }) + ); + + assert.equal(allocation.get('taskGroup.name'), null); + }); +}); diff --git a/ui/tests/unit/serializers/allocation-test.js b/ui/tests/unit/serializers/allocation-test.js index 9036f136df64..7a76c208c5b2 100644 --- a/ui/tests/unit/serializers/allocation-test.js +++ b/ui/tests/unit/serializers/allocation-test.js @@ -45,6 +45,7 @@ module('Unit | Serializer | Allocation', function(hooks) { }, ], wasPreempted: false, + allocationTaskGroup: null, }, relationships: { followUpEvaluation: { @@ -116,6 +117,7 @@ module('Unit | Serializer | Allocation', function(hooks) { }, ], wasPreempted: false, + allocationTaskGroup: null, }, relationships: { followUpEvaluation: { @@ -180,6 +182,7 @@ module('Unit | Serializer | Allocation', function(hooks) { }, ], wasPreempted: true, + allocationTaskGroup: null, }, relationships: { followUpEvaluation: { @@ -213,6 +216,93 @@ module('Unit | Serializer | Allocation', function(hooks) { }, }, }, + + { + name: 'Derives task group from embedded job when available', + in: { + ID: 'test-allocation', + JobID: 'test-summary', + Name: 'test-summary[1]', + Namespace: 'test-namespace', + TaskGroup: 'test-group', + CreateTime: +sampleDate * 1000000, + ModifyTime: +sampleDate * 1000000, + TaskStates: { + task: { + State: 'running', + Failed: false, + }, + }, + Job: { + ID: 'test-summary', + Name: 'test-summary', + TaskGroups: [ + { + Name: 'fake-group', + Count: 2, + Tasks: [], + EphemeralDisk: {}, + }, + { + Name: 'test-group', + Count: 3, + Tasks: [], + EphemeralDisk: {}, + }, + ], + }, + }, + out: { + data: { + id: 'test-allocation', + type: 'allocation', + attributes: { + taskGroupName: 'test-group', + name: 'test-summary[1]', + modifyTime: sampleDate, + createTime: sampleDate, + states: [ + { + name: 'task', + state: 'running', + failed: false, + }, + ], + wasPreempted: false, + allocationTaskGroup: { + name: 'test-group', + count: 3, + tasks: [], + services: [], + volumes: [], + }, + }, + relationships: { + followUpEvaluation: { + data: null, + }, + nextAllocation: { + data: null, + }, + previousAllocation: { + data: null, + }, + preemptedAllocations: { + data: [], + }, + preemptedByAllocation: { + data: null, + }, + job: { + data: { + id: '["test-summary","test-namespace"]', + type: 'job', + }, + }, + }, + }, + }, + }, ]; normalizationTestCases.forEach(testCase => { diff --git a/ui/tests/unit/serializers/volume-test.js b/ui/tests/unit/serializers/volume-test.js index 5168c07f40a3..c98999932343 100644 --- a/ui/tests/unit/serializers/volume-test.js +++ b/ui/tests/unit/serializers/volume-test.js @@ -255,6 +255,7 @@ module('Unit | Serializer | Volume', function(hooks) { taskGroupName: 'foobar', wasPreempted: false, states: [], + allocationTaskGroup: null, }, relationships: { followUpEvaluation: { @@ -286,6 +287,7 @@ module('Unit | Serializer | Volume', function(hooks) { taskGroupName: 'write-here', wasPreempted: false, states: [], + allocationTaskGroup: null, }, relationships: { followUpEvaluation: { @@ -317,6 +319,7 @@ module('Unit | Serializer | Volume', function(hooks) { taskGroupName: 'look-if-you-must', wasPreempted: false, states: [], + allocationTaskGroup: null, }, relationships: { followUpEvaluation: {