[ui] General status for steady-state jobs (#17599)
* Degraded vs Healthy etc. status * Standardize the look of a deploying status panel * badge styles * remove job.status from title component in favour of in-panel status * Remove a redundant check * re-attrd fail-deployment button considered
This commit is contained in:
parent
67efb19e94
commit
94507cc7b7
|
@ -3,34 +3,28 @@
|
|||
SPDX-License-Identifier: MPL-2.0
|
||||
~}}
|
||||
|
||||
<div class="job-status-panel boxed-section active-deployment is-info" data-test-job-status-panel>
|
||||
<div class="boxed-section-head">
|
||||
<div class="job-status-panel boxed-section active-deployment" data-test-job-status-panel>
|
||||
<div class="boxed-section-head hds-foreground-primary">
|
||||
<div class="boxed-section-row"
|
||||
{{did-insert (action this.establishOldAllocBlockIDs)}}
|
||||
>
|
||||
Deployment Status
|
||||
<span class="badge is-white bumper-left" data-test-active-deployment-stat="id">{{@job.latestDeployment.shortId}}</span>
|
||||
<h2>Status:
|
||||
<Hds::Badge @text="Deploying {{@job.latestDeployment.shortId}}" @color="highlight" @type="filled" />
|
||||
</h2>
|
||||
<div class="pull-right">
|
||||
{{#if @job.latestDeployment.isRunning}}
|
||||
<TwoStepButton
|
||||
<Hds::Button
|
||||
data-test-fail
|
||||
{{on "click" (perform this.fail)}}
|
||||
disabled={{this.fail.isRunning}}
|
||||
@color="critical"
|
||||
@text="Fail Deployment"
|
||||
{{keyboard-shortcut
|
||||
label="Fail Deployment"
|
||||
pattern=(array "f" "a" "i" "l")
|
||||
action=(perform this.fail)
|
||||
}}
|
||||
data-test-fail
|
||||
@classes={{hash
|
||||
idleButton="is-danger"
|
||||
confirmationMessage="inherit-color"
|
||||
confirmButton="is-danger"}}
|
||||
@idleText="Fail Deployment"
|
||||
@cancelText="Cancel"
|
||||
@confirmText="Yes, Fail Deployment"
|
||||
@confirmationMessage="Are you sure?"
|
||||
@inlineText={{true}}
|
||||
@awaitingConfirmation={{this.fail.isRunning}}
|
||||
@disabled={{this.fail.isRunning}}
|
||||
@onConfirm={{perform this.fail}} />
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,7 +50,7 @@
|
|||
<A.Description>Your canary allocations have failed their health checks. Please have a look at the error logs and task events for the allocations in question.</A.Description>
|
||||
</Hds::Alert>
|
||||
{{else}}
|
||||
<Hds::Alert @type="inline" @color="highlight" as |A|>
|
||||
<Hds::Alert @type="inline" @color="neutral" as |A|>
|
||||
<A.Title>Checking Canary health</A.Title>
|
||||
{{#if this.deploymentIsAutoPromoted}}
|
||||
<A.Description>Your canary allocations are being placed and health-checked. If they pass, they will be automatically promoted and your deployment will continue.</A.Description>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
<div class="job-status-panel boxed-section steady-state {{if (eq @statusMode "historical") "historical-state" "current-state"}}" data-test-job-status-panel data-test-status-mode={{@statusMode}}>
|
||||
<div class="boxed-section-head">
|
||||
<h2>Status</h2>
|
||||
<h2>Status: <Hds::Badge @text={{this.currentStatus.label}} @color={{this.currentStatus.state}} @type="filled" /></h2>
|
||||
|
||||
<div class="select-mode">
|
||||
<button type="button"
|
||||
data-test-status-mode-current
|
||||
|
|
|
@ -34,11 +34,12 @@ export default class JobStatusPanelSteadyComponent extends Component {
|
|||
|
||||
/**
|
||||
* @typedef {Object} AllocationBlock
|
||||
* @property {AllocationStatus} [RUNNING]
|
||||
* @property {AllocationStatus} [PENDING]
|
||||
* @property {AllocationStatus} [FAILED]
|
||||
* @property {AllocationStatus} [LOST]
|
||||
* @property {AllocationStatus} [UNPLACED]
|
||||
* @property {AllocationStatus} [running]
|
||||
* @property {AllocationStatus} [pending]
|
||||
* @property {AllocationStatus} [failed]
|
||||
* @property {AllocationStatus} [lost]
|
||||
* @property {AllocationStatus} [unplaced]
|
||||
* @property {AllocationStatus} [complete]
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -200,4 +201,57 @@ export default class JobStatusPanelSteadyComponent extends Component {
|
|||
get latestVersionAllocations() {
|
||||
return this.job.allocations.filter((a) => !a.isOld);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CurrentStatus
|
||||
* @property {"Healthy"|"Failed"|"Degraded"|"Recovering"|"Complete"|"Running"} label - The current status of the job
|
||||
* @property {"highlight"|"success"|"warning"|"critical"} state -
|
||||
*/
|
||||
|
||||
/**
|
||||
* A general assessment for how a job is going, in a non-deployment state
|
||||
* @returns {CurrentStatus}
|
||||
*/
|
||||
get currentStatus() {
|
||||
// If all allocs are running, the job is Healthy
|
||||
const totalAllocs = this.totalAllocs;
|
||||
|
||||
if (this.job.type === 'batch' || this.job.type === 'sysbatch') {
|
||||
// If all the allocs are complete, the job is Complete
|
||||
const completeAllocs = this.allocBlocks.complete?.healthy?.nonCanary;
|
||||
if (completeAllocs?.length === totalAllocs) {
|
||||
return { label: 'Complete', state: 'success' };
|
||||
}
|
||||
|
||||
// If any allocations are running the job is "Running"
|
||||
const healthyAllocs = this.allocBlocks.running?.healthy?.nonCanary;
|
||||
if (healthyAllocs?.length + completeAllocs?.length === totalAllocs) {
|
||||
return { label: 'Running', state: 'success' };
|
||||
}
|
||||
}
|
||||
|
||||
const healthyAllocs = this.allocBlocks.running?.healthy?.nonCanary;
|
||||
if (healthyAllocs?.length === totalAllocs) {
|
||||
return { label: 'Healthy', state: 'success' };
|
||||
}
|
||||
|
||||
// If any allocations are pending the job is "Recovering"
|
||||
const pendingAllocs = this.allocBlocks.pending?.healthy?.nonCanary;
|
||||
if (pendingAllocs?.length > 0) {
|
||||
return { label: 'Recovering', state: 'highlight' };
|
||||
}
|
||||
|
||||
// If any allocations are failed, lost, or unplaced in a steady state, the job is "Degraded"
|
||||
const failedOrLostAllocs = [
|
||||
...this.allocBlocks.failed?.healthy?.nonCanary,
|
||||
...this.allocBlocks.lost?.healthy?.nonCanary,
|
||||
...this.allocBlocks.unplaced?.healthy?.nonCanary,
|
||||
];
|
||||
|
||||
if (failedOrLostAllocs.length === totalAllocs) {
|
||||
return { label: 'Failed', state: 'critical' };
|
||||
} else {
|
||||
return { label: 'Degraded', state: 'warning' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
@import '~@hashicorp/design-system-tokens/dist/products/css/helpers/colors.css';
|
||||
|
||||
.job-status-panel {
|
||||
// #region layout
|
||||
|
@ -24,6 +25,28 @@
|
|||
}
|
||||
}
|
||||
|
||||
.boxed-section-head h2 .hds-badge {
|
||||
margin-left: 5px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
&.active-deployment {
|
||||
& > .boxed-section-head {
|
||||
background: var(--token-color-surface-highlight);
|
||||
|
||||
h2 .hds-badge {
|
||||
background-color: var(--token-color-border-highlight);
|
||||
border-color: var(--token-color-border-highlight);
|
||||
color: var(--token-color-foreground-highlight-high-contrast);
|
||||
}
|
||||
}
|
||||
& > .boxed-section-head,
|
||||
& > .boxed-section-body,
|
||||
& > .boxed-section-foot {
|
||||
border-color: var(--token-color-border-highlight);
|
||||
}
|
||||
}
|
||||
|
||||
&.active-deployment .boxed-section-body {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
|
@ -132,7 +155,7 @@
|
|||
gap: 0.5rem;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin-left: 1rem;
|
||||
margin-left: auto;
|
||||
|
||||
button {
|
||||
height: auto;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<span>Pack</span>
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="bumper-left tag {{this.job.statusClass}}" data-test-job-status>{{this.job.status}}</span>
|
||||
{{yield}}
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -303,8 +303,7 @@ module('Integration | Component | job-page/service', function (hooks) {
|
|||
this.setProperties(commonProperties(job));
|
||||
await render(commonTemplate);
|
||||
|
||||
await click('.active-deployment [data-test-idle-button]');
|
||||
await click('.active-deployment [data-test-confirm-button]');
|
||||
await click('.active-deployment [data-test-fail]');
|
||||
|
||||
const requests = this.server.pretender.handledRequests;
|
||||
|
||||
|
@ -331,8 +330,7 @@ module('Integration | Component | job-page/service', function (hooks) {
|
|||
this.setProperties(commonProperties(job));
|
||||
await render(commonTemplate);
|
||||
|
||||
await click('.active-deployment [data-test-idle-button]');
|
||||
await click('.active-deployment [data-test-confirm-button]');
|
||||
await click('.active-deployment [data-test-fail]');
|
||||
|
||||
assert.equal(
|
||||
find('[data-test-job-error-title]').textContent,
|
||||
|
|
|
@ -48,7 +48,7 @@ module(
|
|||
});
|
||||
|
||||
test('the latest deployment section shows up for the currently running deployment: Ungrouped Allocations (small cluster)', async function (assert) {
|
||||
assert.expect(25);
|
||||
assert.expect(24);
|
||||
|
||||
this.server.create('node');
|
||||
|
||||
|
@ -116,11 +116,6 @@ module(
|
|||
'Shows an active deployment if latest status is Running'
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
find('.active-deployment').classList.contains('is-info'),
|
||||
'Running deployment gets the is-info class'
|
||||
);
|
||||
|
||||
// Half the shown allocations are running, 1 is pending, 1 is failed; none are canaries or healthy.
|
||||
// The rest (lost, unknown, etc.) all show up as "Unplaced"
|
||||
assert
|
||||
|
|
Loading…
Reference in New Issue