From c4ec506009f42805d43c3acde8f122c088a2c2c4 Mon Sep 17 00:00:00 2001 From: Mike Nomitch Date: Tue, 11 Oct 2022 15:20:51 -0700 Subject: [PATCH] Adds purge job button to UI when job stopped --- ui/app/adapters/job.js | 5 ++++ ui/app/components/job-page/parts/title.js | 24 +++++++++++++++++++ ui/app/models/job.js | 4 ++++ .../components/job-page/parts/title.hbs | 9 +++++++ ui/tests/helpers/module-for-job.js | 2 ++ .../components/job-page/helpers.js | 16 +++++++++++++ .../components/job-page/periodic-test.js | 21 ++++++++++++++++ .../components/job-page/service-test.js | 17 +++++++++++++ ui/tests/pages/jobs/detail.js | 1 + ui/tests/unit/adapters/job-test.js | 14 +++++++++++ 10 files changed, 113 insertions(+) diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index 08cc9b98e..d56ce732e 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -29,6 +29,11 @@ export default class JobAdapter extends WatchableNamespaceIDs { return this.ajax(url, 'DELETE'); } + purge(job) { + const url = this.urlForFindRecord(job.get('id'), 'job') + '?purge=true'; + return this.ajax(url, 'DELETE'); + } + parse(spec) { const url = addToPath(this.urlForFindAll('job'), '/parse?namespace=*'); return this.ajax(url, 'POST', { diff --git a/ui/app/components/job-page/parts/title.js b/ui/app/components/job-page/parts/title.js index 24d8fc073..07e3dc7c3 100644 --- a/ui/app/components/job-page/parts/title.js +++ b/ui/app/components/job-page/parts/title.js @@ -1,5 +1,6 @@ import Component from '@ember/component'; import { task } from 'ember-concurrency'; +import { inject as service } from '@ember/service'; import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error'; import { tagName } from '@ember-decorators/component'; import classic from 'ember-classic-decorator'; @@ -7,6 +8,8 @@ import classic from 'ember-classic-decorator'; @classic @tagName('') export default class Title extends Component { + @service router; + job = null; title = null; @@ -27,6 +30,27 @@ export default class Title extends Component { }) stopJob; + @task(function* () { + try { + const job = this.job; + yield job.purge(); + this.flashMessages.add({ + title: 'Job Purged', + message: `You have purged ${this.job.name}`, + type: 'success', + destroyOnClick: false, + timeout: 5000, + }); + this.router.transitionTo('jobs'); + } catch (err) { + this.handleError({ + title: 'Error purging job', + description: messageFromAdapterError(err, 'purge jobs'), + }); + } + }) + purgeJob; + @task(function* () { const job = this.job; const definition = yield job.fetchRawDefinition(); diff --git a/ui/app/models/job.js b/ui/app/models/job.js index eb8558f07..2c3a2d20c 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -227,6 +227,10 @@ export default class Job extends Model { return this.store.adapterFor('job').stop(this); } + purge() { + return this.store.adapterFor('job').purge(this); + } + plan() { assert('A job must be parsed before planned', this._newDefinitionJSON); return this.store.adapterFor('job').plan(this); diff --git a/ui/app/templates/components/job-page/parts/title.hbs b/ui/app/templates/components/job-page/parts/title.hbs index a4fc8ed27..bfc8543b3 100644 --- a/ui/app/templates/components/job-page/parts/title.hbs +++ b/ui/app/templates/components/job-page/parts/title.hbs @@ -25,6 +25,15 @@ @awaitingConfirmation={{this.stopJob.isRunning}} @onConfirm={{perform this.stopJob}} /> {{else}} + req.url === expectedURL), + 'DELETE URL was made correctly' + ); +} diff --git a/ui/tests/integration/components/job-page/periodic-test.js b/ui/tests/integration/components/job-page/periodic-test.js index aa55a8881..685677490 100644 --- a/ui/tests/integration/components/job-page/periodic-test.js +++ b/ui/tests/integration/components/job-page/periodic-test.js @@ -11,9 +11,11 @@ import { jobURL, stopJob, startJob, + purgeJob, expectError, expectDeleteRequest, expectStartRequest, + expectPurgeRequest, } from './helpers'; import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; @@ -226,6 +228,25 @@ module('Integration | Component | job-page/periodic', function (hooks) { await expectError(assert, 'Could Not Start Job'); }); + test('Purging a job sends a purge request for the job', async function (assert) { + assert.expect(1); + + const mirageJob = this.server.create('job', 'periodic', { + childrenCount: 0, + createAllocations: false, + status: 'dead', + }); + await this.store.findAll('job'); + + const job = this.store.peekAll('job').findBy('plainId', mirageJob.id); + + this.setProperties(commonProperties(job)); + await render(commonTemplate); + + await purgeJob(); + expectPurgeRequest(assert, this.server, job); + }); + test('Each job row includes the submitted time', async function (assert) { this.server.create('job', 'periodic', { id: 'parent', diff --git a/ui/tests/integration/components/job-page/service-test.js b/ui/tests/integration/components/job-page/service-test.js index 745c90f55..84026ccc7 100644 --- a/ui/tests/integration/components/job-page/service-test.js +++ b/ui/tests/integration/components/job-page/service-test.js @@ -7,9 +7,11 @@ import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage'; import { startJob, stopJob, + purgeJob, expectError, expectDeleteRequest, expectStartRequest, + expectPurgeRequest, } from './helpers'; import Job from 'nomad-ui/tests/pages/jobs/detail'; import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer'; @@ -128,6 +130,21 @@ module('Integration | Component | job-page/service', function (hooks) { await expectError(assert, 'Could Not Start Job'); }); + test('Purging a job sends a purge request for the job', async function (assert) { + assert.expect(1); + + const mirageJob = makeMirageJob(this.server, { status: 'dead' }); + await this.store.findAll('job'); + + const job = this.store.peekAll('job').findBy('plainId', mirageJob.id); + + this.setProperties(commonProperties(job)); + await render(commonTemplate); + + await purgeJob(); + expectPurgeRequest(assert, this.server, job); + }); + test('Recent allocations shows allocations in the job context', async function (assert) { assert.expect(3); diff --git a/ui/tests/pages/jobs/detail.js b/ui/tests/pages/jobs/detail.js index 55f8507dc..860e4515b 100644 --- a/ui/tests/pages/jobs/detail.js +++ b/ui/tests/pages/jobs/detail.js @@ -37,6 +37,7 @@ export default create({ stop: twoStepButton('[data-test-stop]'), start: twoStepButton('[data-test-start]'), + purge: twoStepButton('[data-test-purge]'), packTag: isPresent('[data-test-pack-tag]'), metaTable: isPresent('[data-test-meta]'), diff --git a/ui/tests/unit/adapters/job-test.js b/ui/tests/unit/adapters/job-test.js index 534c22376..8872b4d4b 100644 --- a/ui/tests/unit/adapters/job-test.js +++ b/ui/tests/unit/adapters/job-test.js @@ -515,6 +515,20 @@ module('Unit | Adapter | Job', function (hooks) { assert.equal(request.method, 'DELETE'); }); + test('purge requests include the activeRegion', async function (assert) { + const region = 'region-2'; + const job = await this.initializeWithJob({ region }); + + await this.subject().purge(job); + + const request = this.server.pretender.handledRequests[0]; + assert.equal( + request.url, + `/v1/job/${job.plainId}?purge=true®ion=${region}` + ); + assert.equal(request.method, 'DELETE'); + }); + test('parse requests include the activeRegion', async function (assert) { const region = 'region-2'; await this.initializeUI({ region });