mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 02:15:43 +03:00
* Scaffolding actions (#18639) * Task-level actions for job submissions and retrieval * FIXME: Temporary workaround to get ember dev server to pass exec through to 4646 * Update api/tasks.go Co-authored-by: Tim Gross <tgross@hashicorp.com> * Update command/agent/job_endpoint.go Co-authored-by: Tim Gross <tgross@hashicorp.com> * Diff and copy implementations * Action structs get their own file, diff updates to behave like our other diffs * Test to observe actions changes in a version update * Tests migrated into structs/diff_test and modified with PR comments in mind * APIActionToSTructsAction now returns a new value * de-comment some plain parts, remove unused action lookup * unused param in action converter --------- Co-authored-by: Tim Gross <tgross@hashicorp.com> * New endpoint: job/:id/actions (#18690) * unused param in action converter * backing out of parse_job level and moved toward new endpoint level * Adds taskName and taskGroupName to actions at job level * Unmodified job mock actions tests * actionless job test * actionless job test * Multi group multi task actions test * HTTP method check for GET, cleaner errors in job_endpoint_test * decomment * Actions aggregated at job model level (#18733) * Removal of temporary fix to proxy to 4646 * Run Action websocket endpoint (#18760) * Working demo for review purposes * removal of cors passthru for websockets * Remove job_endpoint-specific ws handlers and aimed at existing alloc exec handlers instead * PR comments adressed, no need for taskGroup pass, better group and task lookups from alloc * early return in action validate and removed jobid from req args per PR comments * todo removal, we're checking later in the rpc * boolean style change on tty * Action CLI command (#18778) * Action command init and stuck-notes * Conditional reqpath to aim at Job action endpoint * De-logged * General CLI command cleanup, observe namespace, pass action as string, get random alloc w group adherence * tab and varname cleanup * Remove action param from Allocations().Exec calls * changelog * dont nil-check acl --------- Co-authored-by: Tim Gross <tgross@hashicorp.com>
306 lines
7.6 KiB
JavaScript
306 lines
7.6 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import { run } from '@ember/runloop';
|
|
import { module, test } from 'qunit';
|
|
import { setupTest } from 'ember-qunit';
|
|
import sinon from 'sinon';
|
|
|
|
module('Unit | Model | job', function (hooks) {
|
|
setupTest(hooks);
|
|
|
|
test('should expose aggregate allocations derived from task groups', function (assert) {
|
|
const store = this.owner.lookup('service:store');
|
|
let summary;
|
|
run(() => {
|
|
summary = store.createRecord('job-summary', {
|
|
taskGroupSummaries: [
|
|
{
|
|
name: 'one',
|
|
queuedAllocs: 1,
|
|
startingAllocs: 2,
|
|
runningAllocs: 3,
|
|
completeAllocs: 4,
|
|
failedAllocs: 5,
|
|
lostAllocs: 6,
|
|
unknownAllocs: 7,
|
|
},
|
|
{
|
|
name: 'two',
|
|
queuedAllocs: 2,
|
|
startingAllocs: 4,
|
|
runningAllocs: 6,
|
|
completeAllocs: 8,
|
|
failedAllocs: 10,
|
|
lostAllocs: 12,
|
|
unknownAllocs: 14,
|
|
},
|
|
{
|
|
name: 'three',
|
|
queuedAllocs: 3,
|
|
startingAllocs: 6,
|
|
runningAllocs: 9,
|
|
completeAllocs: 12,
|
|
failedAllocs: 15,
|
|
lostAllocs: 18,
|
|
unknownAllocs: 21,
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
const job = run(() =>
|
|
this.owner.lookup('service:store').createRecord('job', {
|
|
summary,
|
|
name: 'example',
|
|
taskGroups: [
|
|
{
|
|
name: 'one',
|
|
count: 0,
|
|
tasks: [],
|
|
},
|
|
{
|
|
name: 'two',
|
|
count: 0,
|
|
tasks: [],
|
|
},
|
|
{
|
|
name: 'three',
|
|
count: 0,
|
|
tasks: [],
|
|
},
|
|
],
|
|
})
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('totalAllocs'),
|
|
job
|
|
.get('taskGroups')
|
|
.mapBy('summary.totalAllocs')
|
|
.reduce((sum, allocs) => sum + allocs, 0),
|
|
'totalAllocs is the sum of all group totalAllocs'
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('queuedAllocs'),
|
|
job
|
|
.get('taskGroups')
|
|
.mapBy('summary.queuedAllocs')
|
|
.reduce((sum, allocs) => sum + allocs, 0),
|
|
'queuedAllocs is the sum of all group queuedAllocs'
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('startingAllocs'),
|
|
job
|
|
.get('taskGroups')
|
|
.mapBy('summary.startingAllocs')
|
|
.reduce((sum, allocs) => sum + allocs, 0),
|
|
'startingAllocs is the sum of all group startingAllocs'
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('runningAllocs'),
|
|
job
|
|
.get('taskGroups')
|
|
.mapBy('summary.runningAllocs')
|
|
.reduce((sum, allocs) => sum + allocs, 0),
|
|
'runningAllocs is the sum of all group runningAllocs'
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('completeAllocs'),
|
|
job
|
|
.get('taskGroups')
|
|
.mapBy('summary.completeAllocs')
|
|
.reduce((sum, allocs) => sum + allocs, 0),
|
|
'completeAllocs is the sum of all group completeAllocs'
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('failedAllocs'),
|
|
job
|
|
.get('taskGroups')
|
|
.mapBy('summary.failedAllocs')
|
|
.reduce((sum, allocs) => sum + allocs, 0),
|
|
'failedAllocs is the sum of all group failedAllocs'
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('lostAllocs'),
|
|
job
|
|
.get('taskGroups')
|
|
.mapBy('summary.lostAllocs')
|
|
.reduce((sum, allocs) => sum + allocs, 0),
|
|
'lostAllocs is the sum of all group lostAllocs'
|
|
);
|
|
});
|
|
|
|
test('actions are aggregated from taskgroups tasks', function (assert) {
|
|
const job = run(() =>
|
|
this.owner.lookup('service:store').createRecord('job', {
|
|
name: 'example',
|
|
taskGroups: [
|
|
{
|
|
name: 'one',
|
|
count: 0,
|
|
tasks: [
|
|
{
|
|
name: '1.1',
|
|
actions: [
|
|
{
|
|
name: 'one',
|
|
command: 'date',
|
|
args: ['+%s'],
|
|
},
|
|
{
|
|
name: 'two',
|
|
command: 'sh',
|
|
args: ['-c "echo hello"'],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'two',
|
|
count: 0,
|
|
tasks: [
|
|
{
|
|
name: '2.1',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'three',
|
|
count: 0,
|
|
tasks: [
|
|
{
|
|
name: '3.1',
|
|
actions: [
|
|
{
|
|
name: 'one',
|
|
command: 'date',
|
|
args: ['+%s'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: '3.2',
|
|
actions: [
|
|
{
|
|
name: 'one',
|
|
command: 'date',
|
|
args: ['+%s'],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
})
|
|
);
|
|
|
|
assert.equal(
|
|
job.get('actions.length'),
|
|
4,
|
|
'Job draws actions from its task groups tasks'
|
|
);
|
|
|
|
// Three actions named one, one named two
|
|
assert.equal(
|
|
job.get('actions').filterBy('name', 'one').length,
|
|
3,
|
|
'Job has three actions named one'
|
|
);
|
|
assert.equal(
|
|
job.get('actions').filterBy('name', 'two').length,
|
|
1,
|
|
'Job has one action named two'
|
|
);
|
|
|
|
// Job's actions mapped by task.name return 1.1, 1.1, 3.1, 3.2
|
|
assert.equal(
|
|
job.get('actions').mapBy('task.name').length,
|
|
4,
|
|
'Job action fragments surface their task properties'
|
|
);
|
|
assert.equal(
|
|
job
|
|
.get('actions')
|
|
.mapBy('task.name')
|
|
.filter((name) => name === '1.1').length,
|
|
2,
|
|
'Two of the job actions are from task 1.1'
|
|
);
|
|
assert.equal(
|
|
job
|
|
.get('actions')
|
|
.mapBy('task.name')
|
|
.filter((name) => name === '3.1').length,
|
|
1,
|
|
'One of the job actions is from task 3.1'
|
|
);
|
|
assert.equal(
|
|
job
|
|
.get('actions')
|
|
.mapBy('task.name')
|
|
.filter((name) => name === '3.2').length,
|
|
1,
|
|
'One of the job actions is from task 3.2'
|
|
);
|
|
});
|
|
|
|
module('#parse', function () {
|
|
test('it parses JSON', async function (assert) {
|
|
const store = this.owner.lookup('service:store');
|
|
const model = store.createRecord('job');
|
|
|
|
model.set('_newDefinition', '{"name": "Tomster"}');
|
|
|
|
const setIdByPayloadSpy = sinon.spy(model, 'setIdByPayload');
|
|
|
|
const result = await model.parse();
|
|
|
|
assert.deepEqual(
|
|
model.get('_newDefinitionJSON'),
|
|
{ name: 'Tomster' },
|
|
'Sets _newDefinitionJSON correctly'
|
|
);
|
|
assert.ok(
|
|
setIdByPayloadSpy.calledWith({ name: 'Tomster' }),
|
|
'setIdByPayload is called with the parsed JSON'
|
|
);
|
|
assert.deepEqual(result, '{"name": "Tomster"}', 'Returns the JSON input');
|
|
});
|
|
|
|
test('it dispatches a POST request to the /parse endpoint (eagerly assumes HCL specification) if JSON parse method errors', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const store = this.owner.lookup('service:store');
|
|
const model = store.createRecord('job');
|
|
|
|
model.set('_newDefinition', 'invalidJSON');
|
|
|
|
const adapter = store.adapterFor('job');
|
|
adapter.parse = sinon.stub().resolves('invalidJSON');
|
|
|
|
await model.parse();
|
|
|
|
assert.ok(
|
|
adapter.parse.calledWith('invalidJSON', undefined),
|
|
'adapter parse method should be called'
|
|
);
|
|
|
|
assert.deepEqual(
|
|
model.get('_newDefinitionJSON'),
|
|
'invalidJSON',
|
|
'_newDefinitionJSON is set'
|
|
);
|
|
});
|
|
});
|
|
});
|