From 98aa88c739a18a0cf2a42ce1fb075a4fd8798384 Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Thu, 25 May 2023 14:20:48 -0400 Subject: [PATCH] [ui, deployments] Show a "Latest Deployment Status" cell within the Job Status panel on steady service jobs (#17246) * Failed or lost cell condensed * Latest Deployment cell * Stylistic changes and deploying state fixup * Rewritten tooltip message and updated lost/failed tests * failed-or-lost cell updates to job status panel acceptance tests --- .../components/job-status/failed-or-lost.hbs | 74 +++++++++-------- .../components/job-status/failed-or-lost.js | 7 -- .../job-status/latest-deployment.hbs | 10 +++ .../job-status/latest-deployment.js | 31 +++++++ .../components/job-status/panel/deploying.hbs | 13 +-- ui/app/components/job-status/panel/steady.hbs | 20 ++--- .../styles/components/job-status-panel.scss | 28 ++++--- ui/tests/acceptance/job-status-panel-test.js | 45 ++++++----- .../job-status/failed-or-lost-test.js | 81 ++++++++++++++++--- 9 files changed, 205 insertions(+), 104 deletions(-) delete mode 100644 ui/app/components/job-status/failed-or-lost.js create mode 100644 ui/app/components/job-status/latest-deployment.hbs create mode 100644 ui/app/components/job-status/latest-deployment.js diff --git a/ui/app/components/job-status/failed-or-lost.hbs b/ui/app/components/job-status/failed-or-lost.hbs index 0f0116dc4..9cfbcd702 100644 --- a/ui/app/components/job-status/failed-or-lost.hbs +++ b/ui/app/components/job-status/failed-or-lost.hbs @@ -1,36 +1,44 @@
-

- {{@title}} - - +

Replaced Allocations

+

- {{#if (eq @title "Rescheduled")}} - - {{@allocs.length}} - - {{/if}} - {{#if (eq @title "Restarted")}} - - {{@allocs.length}} - - {{/if}} +
\ No newline at end of file diff --git a/ui/app/components/job-status/failed-or-lost.js b/ui/app/components/job-status/failed-or-lost.js deleted file mode 100644 index 9e1f5ad8d..000000000 --- a/ui/app/components/job-status/failed-or-lost.js +++ /dev/null @@ -1,7 +0,0 @@ -import Component from '@glimmer/component'; - -export default class JobStatusFailedOrLostComponent extends Component { - get shouldLinkToAllocations() { - return this.args.allocs.length; - } -} diff --git a/ui/app/components/job-status/latest-deployment.hbs b/ui/app/components/job-status/latest-deployment.hbs new file mode 100644 index 000000000..49fa142d9 --- /dev/null +++ b/ui/app/components/job-status/latest-deployment.hbs @@ -0,0 +1,10 @@ +
+ +

+ Latest Deployment + +

+
+ +

{{this.healthyAllocs}}/{{this.desiredTotal}} Healthy

+
\ No newline at end of file diff --git a/ui/app/components/job-status/latest-deployment.js b/ui/app/components/job-status/latest-deployment.js new file mode 100644 index 000000000..2387365d3 --- /dev/null +++ b/ui/app/components/job-status/latest-deployment.js @@ -0,0 +1,31 @@ +import Component from '@glimmer/component'; +import { alias } from '@ember/object/computed'; + +export default class JobStatusLatestDeploymentComponent extends Component { + @alias('args.job.latestDeployment') deployment; + @alias('deployment.status') status; + + get healthyAllocs() { + return this.deployment + .get('taskGroupSummaries') + .mapBy('healthyAllocs') + .reduce((sum, count) => sum + count, 0); + } + get desiredTotal() { + return this.deployment + .get('taskGroupSummaries') + .mapBy('desiredTotal') + .reduce((sum, count) => sum + count, 0); + } + + get statusColor() { + switch (this.status) { + case 'successful': + return 'success'; + case 'failed': + return 'critical'; + default: + return 'neutral'; + } + } +} diff --git a/ui/app/components/job-status/panel/deploying.hbs b/ui/app/components/job-status/panel/deploying.hbs index ade49931f..3a0f0f10a 100644 --- a/ui/app/components/job-status/panel/deploying.hbs +++ b/ui/app/components/job-status/panel/deploying.hbs @@ -104,17 +104,10 @@ - - diff --git a/ui/app/components/job-status/panel/steady.hbs b/ui/app/components/job-status/panel/steady.hbs index 67b3b9e39..5d9a238eb 100644 --- a/ui/app/components/job-status/panel/steady.hbs +++ b/ui/app/components/job-status/panel/steady.hbs @@ -41,7 +41,7 @@ -
+
{{#each this.allocTypes as |type|}} - {{#if this.supportsRescheduling}} - - {{/if}}
@@ -92,6 +84,10 @@
+ {{#if @job.latestDeployment}} + + {{/if}} +
diff --git a/ui/app/styles/components/job-status-panel.scss b/ui/app/styles/components/job-status-panel.scss index 13825d1a5..5c73a1b0f 100644 --- a/ui/app/styles/components/job-status-panel.scss +++ b/ui/app/styles/components/job-status-panel.scss @@ -48,11 +48,15 @@ // grid-area: legend-and-summary; // TODO: may revisit this grid-area later, but is currently used in 2 competing ways display: grid; - gap: 0.5rem; - grid-template-columns: 55% 15% 15% 15%; + gap: 1rem; + grid-template-columns: 3fr 1fr 1fr; + &.has-latest-deployment { + grid-template-columns: 3fr 1fr 1fr 1fr; + } & > section > h4, - & > legend > h4 { + & > legend > h4, + & > section > a > h4 { margin-bottom: 0.5rem; } @@ -62,8 +66,6 @@ gap: 0.5rem; } .versions { - display: grid; - gap: 0.5rem; & > ul { display: grid; grid-template-columns: repeat(auto-fit, 100px); @@ -76,12 +78,18 @@ } } } + .latest-deployment { + h4 svg { + position: relative; + top: 3px; + } + } - .failed-or-lost { - .failed-or-lost-link { - display: block; - font-size: 1.5rem; - font-weight: bold; + .failed-or-lost > div { + display: grid; + gap: 3px; + .tooltip { + top: 3px; } } } diff --git a/ui/tests/acceptance/job-status-panel-test.js b/ui/tests/acceptance/job-status-panel-test.js index f8322b667..25a4e5f43 100644 --- a/ui/tests/acceptance/job-status-panel-test.js +++ b/ui/tests/acceptance/job-status-panel-test.js @@ -648,29 +648,29 @@ module('Acceptance | job status panel', function (hooks) { await visit(`/jobs/${job.id}`); assert.dom('.job-status-panel').exists(); assert - .dom('.failed-or-lost') + .dom('.failed-or-lost-links > span') .exists({ count: 2 }, 'Restarted and Rescheduled cells are both present'); - - let rescheduledCell = [...findAll('.failed-or-lost')][0]; - let restartedCell = [...findAll('.failed-or-lost')][1]; + // await this.pauseTest(); + let rescheduledCell = [...findAll('.failed-or-lost-links > span')][0]; + let restartedCell = [...findAll('.failed-or-lost-links > span')][1]; // Check that the title in each cell has the right text - assert.dom(rescheduledCell.querySelector('h4')).hasText('Rescheduled'); - assert.dom(restartedCell.querySelector('h4')).hasText('Restarted'); + assert.dom(rescheduledCell).hasText('0 Rescheduled'); + assert.dom(restartedCell).hasText('0 Restarted'); // Check that both values are zero and non-links assert .dom(rescheduledCell.querySelector('a')) .doesNotExist('Rescheduled cell is not a link'); assert - .dom(rescheduledCell.querySelector('.failed-or-lost-link')) - .hasText('0', 'Rescheduled cell has zero value'); + .dom(rescheduledCell) + .hasText('0 Rescheduled', 'Rescheduled cell has zero value'); assert .dom(restartedCell.querySelector('a')) .doesNotExist('Restarted cell is not a link'); assert - .dom(restartedCell.querySelector('.failed-or-lost-link')) - .hasText('0', 'Restarted cell has zero value'); + .dom(restartedCell) + .hasText('0 Restarted', 'Restarted cell has zero value'); // A wild event appears! Change a recent task event to type "Restarting" in a task state: this.store @@ -687,9 +687,9 @@ module('Acceptance | job status panel', function (hooks) { await settled(); assert - .dom(restartedCell.querySelector('.failed-or-lost-link')) + .dom(restartedCell) .hasText( - '1', + '1 Restarted', 'Restarted cell updates when a task event with type "Restarting" is added' ); @@ -708,9 +708,9 @@ module('Acceptance | job status panel', function (hooks) { // Trigger a reschedule! Set up a desiredTransition object with a Reschedule property on one of the allocations. assert - .dom(restartedCell.querySelector('.failed-or-lost-link')) + .dom(restartedCell) .hasText( - '2', + '2 Restarted', 'Restarted cell updates when a second task event with type "Restarting" is added' ); @@ -725,8 +725,11 @@ module('Acceptance | job status panel', function (hooks) { await settled(); assert - .dom(rescheduledCell.querySelector('.failed-or-lost-link')) - .hasText('1', 'Rescheduled cell updates when desiredTransition is set'); + .dom(rescheduledCell) + .hasText( + '1 Rescheduled', + 'Rescheduled cell updates when desiredTransition is set' + ); assert .dom(rescheduledCell.querySelector('a')) .exists('Rescheduled cell with a non-zero number is now a link'); @@ -900,10 +903,10 @@ module('Acceptance | job status panel', function (hooks) { await visit(`/jobs/${job.id}`); assert.dom('.job-status-panel').exists(); assert.dom('.failed-or-lost').exists({ count: 1 }); - assert.dom('.failed-or-lost h4').hasText('Restarted'); + assert.dom('.failed-or-lost h4').hasText('Replaced Allocations'); assert - .dom('.failed-or-lost-link') - .hasText('0', 'Restarted cell at zero by default'); + .dom('.failed-or-lost-links > span') + .hasText('0 Restarted', 'Restarted cell at zero by default'); // A wild event appears! Change a recent task event to type "Restarting" in a task state: this.store @@ -920,9 +923,9 @@ module('Acceptance | job status panel', function (hooks) { await settled(); assert - .dom('.failed-or-lost-link') + .dom('.failed-or-lost-links > span') .hasText( - '1', + '1 Restarted', 'Restarted cell updates when a task event with type "Restarting" is added' ); }); diff --git a/ui/tests/integration/components/job-status/failed-or-lost-test.js b/ui/tests/integration/components/job-status/failed-or-lost-test.js index 722302243..64ecc98c0 100644 --- a/ui/tests/integration/components/job-status/failed-or-lost-test.js +++ b/ui/tests/integration/components/job-status/failed-or-lost-test.js @@ -9,6 +9,11 @@ module('Integration | Component | job-status/failed-or-lost', function (hooks) { test('it renders', async function (assert) { assert.expect(3); + + let job = { + id: 'job1', + }; + let allocs = [ { id: 1, @@ -21,20 +26,23 @@ module('Integration | Component | job-status/failed-or-lost', function (hooks) { ]; this.set('allocs', allocs); + this.set('job', job); await render(hbs``); - assert.dom('h4').hasText('Rescheduled'); - assert.dom('.failed-or-lost-link').hasText('2'); - + assert.dom('h4').hasText('Replaced Allocations'); + assert.dom('.failed-or-lost-links').hasText('2 Restarted'); await componentA11yAudit(this.element, assert); }); test('it links or does not link appropriately', async function (assert) { + let job = { + id: 'job1', + }; + let allocs = [ { id: 1, @@ -47,16 +55,67 @@ module('Integration | Component | job-status/failed-or-lost', function (hooks) { ]; this.set('allocs', allocs); + this.set('job', job); await render(hbs``); // Ensure it's of type a - assert.dom('.failed-or-lost-link').hasTagName('a'); + assert.dom('.failed-or-lost-links > span > *:last-child').hasTagName('a'); this.set('allocs', []); - assert.dom('.failed-or-lost-link').doesNotHaveTagName('a'); + assert + .dom('.failed-or-lost-links > span > *:last-child') + .doesNotHaveTagName('a'); + }); + + test('it shows rescheduling as well', async function (assert) { + let job = { + id: 'job1', + }; + + let restartedAllocs = [ + { + id: 1, + name: 'alloc1', + }, + { + id: 2, + name: 'alloc2', + }, + ]; + + let rescheduledAllocs = [ + { + id: 1, + name: 'alloc1', + }, + { + id: 2, + name: 'alloc2', + }, + { + id: 3, + name: 'alloc3', + }, + ]; + + this.set('restartedAllocs', restartedAllocs); + this.set('rescheduledAllocs', rescheduledAllocs); + this.set('job', job); + this.set('supportsRescheduling', true); + + await render(hbs``); + + assert.dom('.failed-or-lost-links').containsText('2 Restarted'); + assert.dom('.failed-or-lost-links').containsText('3 Rescheduled'); + this.set('supportsRescheduling', false); + assert.dom('.failed-or-lost-links').doesNotContainText('Rescheduled'); }); });