2017-09-19 14:47:10 +00:00
|
|
|
import Ember from 'ember';
|
2018-05-04 20:08:30 +00:00
|
|
|
import moment from 'moment';
|
2019-09-26 18:47:07 +00:00
|
|
|
import { Factory, trait } from 'ember-cli-mirage';
|
|
|
|
import faker from 'faker';
|
2017-09-19 14:47:10 +00:00
|
|
|
import { provide, pickOne } from '../utils';
|
2019-09-04 14:39:56 +00:00
|
|
|
import { generateResources } from '../common';
|
2017-09-19 14:47:10 +00:00
|
|
|
|
|
|
|
const UUIDS = provide(100, faker.random.uuid.bind(faker.random));
|
|
|
|
const CLIENT_STATUSES = ['pending', 'running', 'complete', 'failed', 'lost'];
|
|
|
|
const DESIRED_STATUSES = ['run', 'stop', 'evict'];
|
2017-11-30 23:08:31 +00:00
|
|
|
const REF_TIME = new Date();
|
2017-09-19 14:47:10 +00:00
|
|
|
|
|
|
|
export default Factory.extend({
|
|
|
|
id: i => (i >= 100 ? `${UUIDS[i % 100]}-${i}` : UUIDS[i]),
|
|
|
|
|
2017-10-18 19:29:33 +00:00
|
|
|
jobVersion: () => faker.random.number(10),
|
2017-10-18 00:51:00 +00:00
|
|
|
|
2018-07-19 20:31:39 +00:00
|
|
|
modifyIndex: () => faker.random.number({ min: 10, max: 2000 }),
|
2017-11-30 23:08:31 +00:00
|
|
|
modifyTime: () => faker.date.past(2 / 365, REF_TIME) * 1000000,
|
|
|
|
|
2018-07-19 20:31:39 +00:00
|
|
|
createIndex: () => faker.random.number({ min: 10, max: 2000 }),
|
|
|
|
createTime() {
|
|
|
|
return faker.date.past(2 / 365, new Date(this.modifyTime / 1000000)) * 1000000;
|
|
|
|
},
|
|
|
|
|
2018-02-16 02:55:59 +00:00
|
|
|
namespace: null,
|
|
|
|
|
2019-09-26 18:47:07 +00:00
|
|
|
clientStatus: faker.helpers.randomize(CLIENT_STATUSES),
|
|
|
|
desiredStatus: faker.helpers.randomize(DESIRED_STATUSES),
|
2017-09-19 14:47:10 +00:00
|
|
|
|
2019-04-12 03:08:09 +00:00
|
|
|
// When true, doesn't create any resources, state, or events
|
|
|
|
shallow: false,
|
|
|
|
|
2017-09-19 14:47:10 +00:00
|
|
|
withTaskWithPorts: trait({
|
|
|
|
afterCreate(allocation, server) {
|
|
|
|
const taskGroup = server.db.taskGroups.findBy({ name: allocation.taskGroup });
|
|
|
|
const resources = taskGroup.taskIds.map(id =>
|
|
|
|
server.create(
|
2019-04-02 01:30:44 +00:00
|
|
|
'task-resource',
|
2017-09-19 14:47:10 +00:00
|
|
|
{
|
|
|
|
allocation,
|
|
|
|
name: server.db.tasks.find(id).name,
|
|
|
|
},
|
|
|
|
'withReservedPorts'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2019-04-02 01:30:44 +00:00
|
|
|
allocation.update({ taskResourceIds: resources.mapBy('id') });
|
2017-09-19 14:47:10 +00:00
|
|
|
},
|
|
|
|
}),
|
|
|
|
|
2017-10-31 04:02:40 +00:00
|
|
|
withoutTaskWithPorts: trait({
|
|
|
|
afterCreate(allocation, server) {
|
|
|
|
const taskGroup = server.db.taskGroups.findBy({ name: allocation.taskGroup });
|
|
|
|
const resources = taskGroup.taskIds.map(id =>
|
|
|
|
server.create(
|
2019-04-02 01:30:44 +00:00
|
|
|
'task-resource',
|
2017-10-31 04:02:40 +00:00
|
|
|
{
|
|
|
|
allocation,
|
|
|
|
name: server.db.tasks.find(id).name,
|
|
|
|
},
|
|
|
|
'withoutReservedPorts'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2019-04-02 01:30:44 +00:00
|
|
|
allocation.update({ taskResourceIds: resources.mapBy('id') });
|
2017-10-31 04:02:40 +00:00
|
|
|
},
|
|
|
|
}),
|
|
|
|
|
2019-09-04 14:39:56 +00:00
|
|
|
withAllocatedResources: trait({
|
|
|
|
allocatedResources: () => {
|
|
|
|
return {
|
|
|
|
Shared: generateResources({ networks: { minPorts: 2 } }),
|
|
|
|
};
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
|
2018-05-04 20:08:30 +00:00
|
|
|
rescheduleAttempts: 0,
|
|
|
|
rescheduleSuccess: false,
|
|
|
|
|
|
|
|
rescheduled: trait({
|
|
|
|
// Create another allocation carrying the events of this as well as the reschduleSuccess state.
|
|
|
|
// Pass along rescheduleAttempts after decrementing.
|
|
|
|
// After rescheduleAttempts hits zero, a final allocation is made with no nextAllocation and
|
|
|
|
// a clientStatus of failed or running, depending on rescheduleSuccess
|
|
|
|
afterCreate(allocation, server) {
|
2018-05-05 02:04:36 +00:00
|
|
|
const attempts = allocation.rescheduleAttempts - 1;
|
2018-05-04 20:08:30 +00:00
|
|
|
const previousEvents =
|
|
|
|
(allocation.rescheduleTracker && allocation.rescheduleTracker.Events) || [];
|
|
|
|
|
|
|
|
let rescheduleTime;
|
|
|
|
if (previousEvents.length) {
|
|
|
|
const lastEvent = previousEvents[previousEvents.length - 1];
|
|
|
|
rescheduleTime = moment(lastEvent.RescheduleTime / 1000000).add(5, 'minutes');
|
|
|
|
} else {
|
|
|
|
rescheduleTime = faker.date.past(2 / 365, REF_TIME);
|
|
|
|
}
|
|
|
|
|
|
|
|
rescheduleTime *= 1000000;
|
|
|
|
|
|
|
|
const rescheduleTracker = {
|
|
|
|
Events: previousEvents.concat([
|
|
|
|
{
|
|
|
|
PrevAllocID: allocation.id,
|
|
|
|
PrevNodeID: null, //allocation.node.id,
|
|
|
|
RescheduleTime: rescheduleTime,
|
|
|
|
},
|
|
|
|
]),
|
|
|
|
};
|
|
|
|
|
|
|
|
let nextAllocation;
|
2018-05-05 02:04:36 +00:00
|
|
|
if (attempts > 0) {
|
2018-05-04 20:08:30 +00:00
|
|
|
nextAllocation = server.create('allocation', 'rescheduled', {
|
2018-05-05 02:04:36 +00:00
|
|
|
rescheduleAttempts: Math.max(attempts, 0),
|
2018-05-04 20:08:30 +00:00
|
|
|
rescheduleSuccess: allocation.rescheduleSuccess,
|
|
|
|
previousAllocation: allocation.id,
|
2019-04-12 03:08:09 +00:00
|
|
|
shallow: allocation.shallow,
|
2018-05-04 20:08:30 +00:00
|
|
|
clientStatus: 'failed',
|
|
|
|
rescheduleTracker,
|
|
|
|
followupEvalId: server.create('evaluation', {
|
|
|
|
waitUntil: rescheduleTime,
|
|
|
|
}).id,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
nextAllocation = server.create('allocation', {
|
|
|
|
previousAllocation: allocation.id,
|
|
|
|
clientStatus: allocation.rescheduleSuccess ? 'running' : 'failed',
|
2019-04-12 03:08:09 +00:00
|
|
|
shallow: allocation.shallow,
|
2018-05-04 20:08:30 +00:00
|
|
|
rescheduleTracker,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
allocation.update({ nextAllocation: nextAllocation.id, clientStatus: 'failed' });
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
|
2019-04-20 00:51:32 +00:00
|
|
|
preempted: trait({
|
|
|
|
afterCreate(allocation, server) {
|
|
|
|
const preempter = server.create('allocation', { preemptedAllocations: [allocation.id] });
|
|
|
|
allocation.update({ preemptedByAllocation: preempter.id });
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
|
|
|
|
preempter: trait({
|
|
|
|
afterCreate(allocation, server) {
|
|
|
|
const preempted = server.create('allocation', { preemptedByAllocation: allocation.id });
|
|
|
|
allocation.update({ preemptedAllocations: [preempted.id] });
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
|
2017-09-19 14:47:10 +00:00
|
|
|
afterCreate(allocation, server) {
|
|
|
|
Ember.assert(
|
|
|
|
'[Mirage] No jobs! make sure jobs are created before allocations',
|
|
|
|
server.db.jobs.length
|
|
|
|
);
|
|
|
|
Ember.assert(
|
|
|
|
'[Mirage] No nodes! make sure nodes are created before allocations',
|
|
|
|
server.db.nodes.length
|
|
|
|
);
|
|
|
|
|
|
|
|
const job = allocation.jobId ? server.db.jobs.find(allocation.jobId) : pickOne(server.db.jobs);
|
2018-07-06 17:50:22 +00:00
|
|
|
const namespace = allocation.namespace || job.namespace;
|
2017-09-19 14:47:10 +00:00
|
|
|
const node = allocation.nodeId
|
|
|
|
? server.db.nodes.find(allocation.nodeId)
|
|
|
|
: pickOne(server.db.nodes);
|
|
|
|
const taskGroup = allocation.taskGroup
|
|
|
|
? server.db.taskGroups.findBy({ name: allocation.taskGroup })
|
|
|
|
: pickOne(server.db.taskGroups.where({ jobId: job.id }));
|
|
|
|
|
|
|
|
allocation.update({
|
2018-07-06 17:50:22 +00:00
|
|
|
namespace,
|
2017-09-19 14:47:10 +00:00
|
|
|
jobId: job.id,
|
|
|
|
nodeId: node.id,
|
2019-04-12 03:08:09 +00:00
|
|
|
taskStateIds: [],
|
|
|
|
taskResourceIds: [],
|
2017-09-19 14:47:10 +00:00
|
|
|
taskGroup: taskGroup.name,
|
|
|
|
name: allocation.name || `${taskGroup.name}.[${faker.random.number(10)}]`,
|
|
|
|
});
|
|
|
|
|
2019-04-12 03:08:09 +00:00
|
|
|
if (!allocation.shallow) {
|
|
|
|
const states = taskGroup.taskIds.map(id =>
|
|
|
|
server.create('task-state', {
|
|
|
|
allocation,
|
|
|
|
name: server.db.tasks.find(id).name,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
const resources = taskGroup.taskIds.map(id =>
|
|
|
|
server.create('task-resource', {
|
|
|
|
allocation,
|
|
|
|
name: server.db.tasks.find(id).name,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
allocation.update({
|
|
|
|
taskStateIds: allocation.clientStatus === 'pending' ? [] : states.mapBy('id'),
|
|
|
|
taskResourceIds: allocation.clientStatus === 'pending' ? [] : resources.mapBy('id'),
|
|
|
|
});
|
|
|
|
|
|
|
|
// Each allocation has a corresponding allocation stats running on some client.
|
|
|
|
// Create that record, even though it's not a relationship.
|
|
|
|
server.create('client-allocation-stat', {
|
|
|
|
id: allocation.id,
|
|
|
|
_taskNames: states.mapBy('name'),
|
|
|
|
});
|
|
|
|
}
|
2017-09-19 14:47:10 +00:00
|
|
|
},
|
|
|
|
});
|