From c09f8dee391d3a6965700098363107d7281bfd39 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Tue, 31 Aug 2021 16:32:47 -0400 Subject: [PATCH 1/2] ui: set namespace when looking for and displaying children jobs --- ui/app/components/job-page/parts/children.js | 1 + ui/app/routes/jobs/job/index.js | 10 +++-- .../components/job-page/parts/children.hbs | 7 +++- ui/tests/acceptance/job-detail-test.js | 40 +++++++++++++++++++ ui/tests/helpers/module-for-job.js | 39 +++++++++++++++--- ui/tests/pages/jobs/detail.js | 7 ++++ 6 files changed, 92 insertions(+), 12 deletions(-) diff --git a/ui/app/components/job-page/parts/children.js b/ui/app/components/job-page/parts/children.js index 1f2ba13c7944..20f72910dc9e 100644 --- a/ui/app/components/job-page/parts/children.js +++ b/ui/app/components/job-page/parts/children.js @@ -9,6 +9,7 @@ import classic from 'ember-classic-decorator'; @classic @classNames('boxed-section') export default class Children extends Component.extend(Sortable) { + @service system; @service userSettings; job = null; diff --git a/ui/app/routes/jobs/job/index.js b/ui/app/routes/jobs/job/index.js index 4bea45f1f2d7..8f1cf1827fe9 100644 --- a/ui/app/routes/jobs/job/index.js +++ b/ui/app/routes/jobs/job/index.js @@ -1,6 +1,6 @@ import Route from '@ember/routing/route'; import { collect } from '@ember/object/computed'; -import { watchRecord, watchRelationship, watchAll } from 'nomad-ui/utils/properties/watch'; +import { watchRecord, watchRelationship, watchQuery } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; export default class IndexRoute extends Route.extend(WithWatchers) { @@ -15,7 +15,9 @@ export default class IndexRoute extends Route.extend(WithWatchers) { evaluations: this.watchEvaluations.perform(model), latestDeployment: model.get('supportsDeployments') && this.watchLatestDeployment.perform(model), - list: model.get('hasChildren') && this.watchAll.perform(), + list: + model.get('hasChildren') && + this.watchAllJobs.perform({ namespace: model.namespace.get('name') }), }); } @@ -32,7 +34,7 @@ export default class IndexRoute extends Route.extend(WithWatchers) { } @watchRecord('job') watch; - @watchAll('job') watchAll; + @watchQuery('job') watchAllJobs; @watchRecord('job-summary') watchSummary; @watchRelationship('allocations') watchAllocations; @watchRelationship('evaluations') watchEvaluations; @@ -40,7 +42,7 @@ export default class IndexRoute extends Route.extend(WithWatchers) { @collect( 'watch', - 'watchAll', + 'watchAllJobs', 'watchSummary', 'watchAllocations', 'watchEvaluations', diff --git a/ui/app/templates/components/job-page/parts/children.hbs b/ui/app/templates/components/job-page/parts/children.hbs index e9d81d3be48e..4070bfa06b39 100644 --- a/ui/app/templates/components/job-page/parts/children.hbs +++ b/ui/app/templates/components/job-page/parts/children.hbs @@ -31,9 +31,12 @@ @sortProperty={{this.sortProperty}} @sortDescending={{this.sortDescending}} @class="with-foot" as |t|> - + Name - Submitted At + {{#if this.system.shouldShowNamespaces}} + Namespace + {{/if}} + Submitted At Status Type Priority diff --git a/ui/tests/acceptance/job-detail-test.js b/ui/tests/acceptance/job-detail-test.js index c9a0821507ad..74e02db95997 100644 --- a/ui/tests/acceptance/job-detail-test.js +++ b/ui/tests/acceptance/job-detail-test.js @@ -25,6 +25,7 @@ moduleForJob( .sortBy('submitTime') .reverse()[0]; + assert.ok(JobDetail.jobsHeader.hasSubmitTime); assert.equal( JobDetail.jobs[0].submitTime, moment(mostRecentLaunch.submitTime / 1000000).format('MMM DD HH:mm:ss ZZ') @@ -33,6 +34,25 @@ moduleForJob( } ); +moduleForJob( + 'Acceptance | job detail (periodic in namespace)', + 'children', + () => { + const namespace = server.create('namespace', { id: 'test' }); + const parent = server.create('job', 'periodic', { + shallow: true, + namespaceId: namespace.name, + }); + return parent; + }, + { + 'display namespace in children table': async function(job, assert) { + assert.ok(JobDetail.jobsHeader.hasNamespace); + assert.equal(JobDetail.jobs[0].namespace, job.namespace); + }, + } +); + moduleForJob( 'Acceptance | job detail (parameterized)', 'children', @@ -44,6 +64,7 @@ moduleForJob( .sortBy('submitTime') .reverse()[0]; + assert.ok(JobDetail.jobsHeader.hasSubmitTime); assert.equal( JobDetail.jobs[0].submitTime, moment(mostRecentLaunch.submitTime / 1000000).format('MMM DD HH:mm:ss ZZ') @@ -52,6 +73,25 @@ moduleForJob( } ); +moduleForJob( + 'Acceptance | job detail (parameterized in namespace)', + 'children', + () => { + const namespace = server.create('namespace', { id: 'test' }); + const parent = server.create('job', 'parameterized', { + shallow: true, + namespaceId: namespace.name, + }); + return parent; + }, + { + 'display namespace in children table': async function(job, assert) { + assert.ok(JobDetail.jobsHeader.hasNamespace); + assert.equal(JobDetail.jobs[0].namespace, job.namespace); + }, + } +); + moduleForJob('Acceptance | job detail (periodic child)', 'allocations', () => { const parent = server.create('job', 'periodic', { childrenCount: 1, shallow: true }); return server.db.jobs.where({ parentId: parent.id })[0]; diff --git a/ui/tests/helpers/module-for-job.js b/ui/tests/helpers/module-for-job.js index 3a6c27b60eab..0bf9f70e38fa 100644 --- a/ui/tests/helpers/module-for-job.js +++ b/ui/tests/helpers/module-for-job.js @@ -22,32 +22,51 @@ export default function moduleForJob(title, context, jobFactory, additionalTests hooks.beforeEach(async function() { server.create('node'); job = jobFactory(); - await JobDetail.visit({ id: job.id }); + if (!job.namespace || job.namespace === 'default') { + await JobDetail.visit({ id: job.id }); + } else { + await JobDetail.visit({ id: job.id, namespace: job.namespace }); + } }); test('visiting /jobs/:job_id', async function(assert) { - assert.equal(currentURL(), `/jobs/${encodeURIComponent(job.id)}`); + assert.equal( + currentURL(), + urlWithNamespace(`/jobs/${encodeURIComponent(job.id)}`, job.namespace) + ); assert.equal(document.title, `Job ${job.name} - Nomad`); }); test('the subnav links to overview', async function(assert) { await JobDetail.tabFor('overview').visit(); - assert.equal(currentURL(), `/jobs/${encodeURIComponent(job.id)}`); + assert.equal( + currentURL(), + urlWithNamespace(`/jobs/${encodeURIComponent(job.id)}`, job.namespace) + ); }); test('the subnav links to definition', async function(assert) { await JobDetail.tabFor('definition').visit(); - assert.equal(currentURL(), `/jobs/${encodeURIComponent(job.id)}/definition`); + assert.equal( + currentURL(), + urlWithNamespace(`/jobs/${encodeURIComponent(job.id)}/definition`, job.namespace) + ); }); test('the subnav links to versions', async function(assert) { await JobDetail.tabFor('versions').visit(); - assert.equal(currentURL(), `/jobs/${encodeURIComponent(job.id)}/versions`); + assert.equal( + currentURL(), + urlWithNamespace(`/jobs/${encodeURIComponent(job.id)}/versions`, job.namespace) + ); }); test('the subnav links to evaluations', async function(assert) { await JobDetail.tabFor('evaluations').visit(); - assert.equal(currentURL(), `/jobs/${encodeURIComponent(job.id)}/evaluations`); + assert.equal( + currentURL(), + urlWithNamespace(`/jobs/${encodeURIComponent(job.id)}/evaluations`, job.namespace) + ); }); test('the title buttons are dependent on job status', async function(assert) { @@ -99,3 +118,11 @@ export default function moduleForJob(title, context, jobFactory, additionalTests } }); } + +function urlWithNamespace(url, namespace) { + if (!namespace || namespace === 'default') { + return url; + } + + return `${url}?namespace=${namespace}`; +} diff --git a/ui/tests/pages/jobs/detail.js b/ui/tests/pages/jobs/detail.js index 81f18f849ca9..9d2b679c584c 100644 --- a/ui/tests/pages/jobs/detail.js +++ b/ui/tests/pages/jobs/detail.js @@ -64,9 +64,16 @@ export default create({ viewAllAllocations: text('[data-test-view-all-allocations]'), + jobsHeader: { + scope: '[data-test-jobs-header]', + hasSubmitTime: isPresent('[data-test-jobs-submit-time-header]'), + hasNamespace: isPresent('[data-test-jobs-namespace-header]'), + }, + jobs: collection('[data-test-job-row]', { id: attribute('data-test-job-row'), name: text('[data-test-job-name]'), + namespace: text('[data-test-job-namespace]'), link: attribute('href', '[data-test-job-name] a'), submitTime: text('[data-test-job-submit-time]'), status: text('[data-test-job-status]'), From 87ef2709c4ea5e4c2444f35aa2794a746881dab9 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Wed, 1 Sep 2021 12:34:30 -0400 Subject: [PATCH 2/2] changelog: add entry for #11110 --- .changelog/11110.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/11110.txt diff --git a/.changelog/11110.txt b/.changelog/11110.txt new file mode 100644 index 000000000000..4216ed46af2c --- /dev/null +++ b/.changelog/11110.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixed an issue that prevented periodic and dispatch jobs in a non-default namespace to be properly rendered +```