9c0f85d7b6
This partially addresses #7799. Task state filesystems are contained within a subdirectory of their parent allocation, so almost everything that existed for browsing task state filesystems was applicable to browsing allocations, just without the task name prepended to the path. I aimed to push this differential handling into as few contained places as possible. The tests also have significant overlap, so this includes an extracted behavior to run the same tests for allocations and task states.
137 lines
4.1 KiB
JavaScript
137 lines
4.1 KiB
JavaScript
import { inject as service } from '@ember/service';
|
|
import { computed } from '@ember/object';
|
|
import { equal, none } from '@ember/object/computed';
|
|
import Model from 'ember-data/model';
|
|
import attr from 'ember-data/attr';
|
|
import { belongsTo, hasMany } from 'ember-data/relationships';
|
|
import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes';
|
|
import intersection from 'lodash.intersection';
|
|
import shortUUIDProperty from '../utils/properties/short-uuid';
|
|
|
|
const STATUS_ORDER = {
|
|
pending: 1,
|
|
running: 2,
|
|
complete: 3,
|
|
failed: 4,
|
|
lost: 5,
|
|
};
|
|
|
|
export default Model.extend({
|
|
token: service(),
|
|
|
|
shortId: shortUUIDProperty('id'),
|
|
job: belongsTo('job'),
|
|
node: belongsTo('node'),
|
|
name: attr('string'),
|
|
taskGroupName: attr('string'),
|
|
resources: fragment('resources'),
|
|
allocatedResources: fragment('resources'),
|
|
jobVersion: attr('number'),
|
|
|
|
modifyIndex: attr('number'),
|
|
modifyTime: attr('date'),
|
|
|
|
createIndex: attr('number'),
|
|
createTime: attr('date'),
|
|
|
|
clientStatus: attr('string'),
|
|
desiredStatus: attr('string'),
|
|
statusIndex: computed('clientStatus', function() {
|
|
return STATUS_ORDER[this.clientStatus] || 100;
|
|
}),
|
|
|
|
isRunning: equal('clientStatus', 'running'),
|
|
isMigrating: attr('boolean'),
|
|
|
|
// An allocation model created from any allocation list response will be lacking
|
|
// many properties (some of which can always be null). This is an indicator that
|
|
// the allocation needs to be reloaded to get the complete allocation state.
|
|
isPartial: none('allocationTaskGroup'),
|
|
|
|
// When allocations are server-side rescheduled, a paper trail
|
|
// is left linking all reschedule attempts.
|
|
previousAllocation: belongsTo('allocation', { inverse: 'nextAllocation' }),
|
|
nextAllocation: belongsTo('allocation', { inverse: 'previousAllocation' }),
|
|
|
|
preemptedAllocations: hasMany('allocation', { inverse: 'preemptedByAllocation' }),
|
|
preemptedByAllocation: belongsTo('allocation', { inverse: 'preemptedAllocations' }),
|
|
wasPreempted: attr('boolean'),
|
|
|
|
followUpEvaluation: belongsTo('evaluation'),
|
|
|
|
statusClass: computed('clientStatus', function() {
|
|
const classMap = {
|
|
pending: 'is-pending',
|
|
running: 'is-primary',
|
|
complete: 'is-complete',
|
|
failed: 'is-error',
|
|
lost: 'is-light',
|
|
};
|
|
|
|
return classMap[this.clientStatus] || 'is-dark';
|
|
}),
|
|
|
|
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');
|
|
|
|
if (taskGroupUnhealthyDrivers && nodeUnhealthyDrivers) {
|
|
return intersection(taskGroupUnhealthyDrivers, nodeUnhealthyDrivers);
|
|
}
|
|
|
|
return [];
|
|
}),
|
|
|
|
states: fragmentArray('task-state'),
|
|
rescheduleEvents: fragmentArray('reschedule-event'),
|
|
|
|
hasRescheduleEvents: computed('rescheduleEvents.length', 'nextAllocation', function() {
|
|
return this.get('rescheduleEvents.length') > 0 || this.nextAllocation;
|
|
}),
|
|
|
|
hasStoppedRescheduling: computed(
|
|
'nextAllocation',
|
|
'clientStatus',
|
|
'followUpEvaluation.content',
|
|
function() {
|
|
return (
|
|
!this.get('nextAllocation.content') &&
|
|
!this.get('followUpEvaluation.content') &&
|
|
this.clientStatus === 'failed'
|
|
);
|
|
}
|
|
),
|
|
|
|
stop() {
|
|
return this.store.adapterFor('allocation').stop(this);
|
|
},
|
|
|
|
restart(taskName) {
|
|
return this.store.adapterFor('allocation').restart(this, taskName);
|
|
},
|
|
|
|
ls(path) {
|
|
return this.store.adapterFor('allocation').ls(this, path);
|
|
},
|
|
|
|
stat(path) {
|
|
return this.store.adapterFor('allocation').stat(this, path);
|
|
},
|
|
});
|