-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ui, deployments] Promote Canary and Unhealthy Allocations in the deployment status panel #17547
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ | |
<span class="alloc-health-indicator"> | ||
{{#if (eq @health "healthy")}} | ||
<FlightIcon @name="check" @color="white" /> | ||
{{else if (eq @health "unhealthy")}} | ||
<FlightIcon @name="x" @color="white" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the indicator for when an allocation is un-aggregated/summarized, in say a small enough cluster that each allocation gets its own square. |
||
{{else}} | ||
<FlightIcon @name="running" @color="white" /> | ||
{{/if}} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,18 +27,38 @@ | |
@disabled={{this.fail.isRunning}} | ||
@onConfirm={{perform this.fail}} /> | ||
{{/if}} | ||
{{#if @job.latestDeployment.requiresPromotion}} | ||
<button | ||
data-test-promote-canary | ||
type="button" | ||
class="button is-warning is-small {{if this.promote.isRunning "is-loading"}}" | ||
disabled={{this.promote.isRunning}} | ||
onclick={{perform this.promote}}>Promote Canary</button> | ||
{{/if}} | ||
</div> | ||
</div> | ||
</div> | ||
<div class="boxed-section-body"> | ||
<div class="boxed-section-body {{if @job.latestDeployment.requiresPromotion "requires-promotion"}}"> | ||
{{#if @job.latestDeployment.requiresPromotion}} | ||
<div class="canary-promotion-alert"> | ||
{{#if this.canariesHealthy}} | ||
<Hds::Alert @type="inline" @color="warning" as |A|> | ||
<A.Title>Deployment requires promotion</A.Title> | ||
<A.Description>Your deployment requires manual promotion — all canary allocations have passed their health checks.</A.Description> | ||
<A.Button data-test-promote-canary @text="Promote Canary" @color="primary" {{on "click" (perform this.promote)}} /> | ||
</Hds::Alert> | ||
{{else}} | ||
{{#if this.someCanariesHaveFailed}} | ||
<Hds::Alert @type="inline" @color="critical" as |A|> | ||
<A.Title>Some Canaries have failed</A.Title> | ||
<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|> | ||
<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> | ||
{{else}} | ||
<A.Description>Your job requires manual promotion, and your canary allocations are being placed and health-checked.</A.Description> | ||
{{/if}} | ||
</Hds::Alert> | ||
{{/if}} | ||
{{/if}} | ||
</div> | ||
{{/if}} | ||
|
||
<div class="deployment-allocations"> | ||
{{#if this.oldVersionAllocBlockIDs.length}} | ||
<h4 class="title is-5" data-test-old-allocation-tally>Previous allocations: {{#if this.oldVersionAllocBlocks.running}}{{this.oldRunningHealthyAllocBlocks.length}} running{{/if}}</h4> | ||
|
@@ -86,16 +106,18 @@ | |
|
||
{{#each-in this.newAllocsByHealth as |health count|}} | ||
<span class="legend-item {{if (eq count 0) "faded"}}"> | ||
<span class="represented-allocation legend-example"> | ||
<span class="represented-allocation legend-example {{health}}"> | ||
<span class="alloc-health-indicator"> | ||
{{#if (eq health "healthy")}} | ||
<FlightIcon @name="check" @color="#25ba81" /> | ||
{{else if (eq health "unhealthy")}} | ||
<FlightIcon @name="x" @color="#c84034" /> | ||
{{else}} | ||
<FlightIcon @name="running" @color="black" class="not-animated" /> | ||
{{/if}} | ||
</span> | ||
</span> | ||
{{count}} {{capitalize health}} | ||
{{count}} {{humanize health}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
(in case you were wondering: we don't use |
||
</span> | ||
{{/each-in}} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,32 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
); | ||
} | ||
|
||
/** | ||
* Promotion of a deployment will error if the canary allocations are not of status "Healthy"; | ||
* this function will check for that and disable the promote button if necessary. | ||
* @returns {boolean} | ||
*/ | ||
get canariesHealthy() { | ||
const relevantAllocs = this.job.allocations.filter( | ||
(a) => !a.isOld && a.isCanary && !a.hasBeenRescheduled | ||
); | ||
return relevantAllocs.every( | ||
(a) => a.clientStatus === 'running' && a.isHealthy | ||
); | ||
} | ||
|
||
get someCanariesHaveFailed() { | ||
const relevantAllocs = this.job.allocations.filter( | ||
(a) => !a.isOld && a.isCanary && !a.hasBeenRescheduled | ||
); | ||
return relevantAllocs.some( | ||
(a) => | ||
a.clientStatus === 'failed' || | ||
a.clientStatus === 'lost' || | ||
a.isUnhealthy | ||
); | ||
} | ||
|
||
@task(function* () { | ||
try { | ||
yield this.job.latestDeployment.content.promote(); | ||
|
@@ -70,6 +96,7 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
alloGroups[status] = { | ||
healthy: { nonCanary: [] }, | ||
unhealthy: { nonCanary: [] }, | ||
health_unknown: { nonCanary: [] }, | ||
}; | ||
} | ||
alloGroups[status].healthy.nonCanary.push(currentAlloc); | ||
|
@@ -88,6 +115,7 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
categories[type.label] = { | ||
healthy: { canary: [], nonCanary: [] }, | ||
unhealthy: { canary: [], nonCanary: [] }, | ||
health_unknown: { canary: [], nonCanary: [] }, | ||
}; | ||
return categories; | ||
}, {}); | ||
|
@@ -107,8 +135,10 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
status === 'running' | ||
? alloc.isHealthy | ||
? 'healthy' | ||
: 'unhealthy' | ||
: 'unhealthy'; | ||
: alloc.isUnhealthy | ||
? 'unhealthy' | ||
: 'health_unknown' | ||
: 'health_unknown'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not thrilled that I'm nesting a ternary here but it is only causing me mild discomfort |
||
|
||
if (allocationCategories[status]) { | ||
// If status is failed or lost, we only want to show it IF it's used up its restarts/rescheds. | ||
|
@@ -129,6 +159,7 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
allocationCategories['unplaced'] = { | ||
healthy: { canary: [], nonCanary: [] }, | ||
unhealthy: { canary: [], nonCanary: [] }, | ||
health_unknown: { canary: [], nonCanary: [] }, | ||
}; | ||
allocationCategories['unplaced']['healthy']['nonCanary'] = Array( | ||
availableSlotsToFill | ||
|
@@ -149,6 +180,13 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
]; | ||
} | ||
|
||
get newRunningUnhealthyAllocBlocks() { | ||
return [ | ||
...this.newVersionAllocBlocks['running']['unhealthy']['canary'], | ||
...this.newVersionAllocBlocks['running']['unhealthy']['nonCanary'], | ||
]; | ||
} | ||
|
||
get rescheduledAllocs() { | ||
return this.job.allocations.filter((a) => !a.isOld && a.hasBeenRescheduled); | ||
} | ||
|
@@ -183,8 +221,11 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
get newAllocsByHealth() { | ||
return { | ||
healthy: this.newRunningHealthyAllocBlocks.length, | ||
'health unknown': | ||
this.totalAllocs - this.newRunningHealthyAllocBlocks.length, | ||
unhealthy: this.newRunningUnhealthyAllocBlocks.length, | ||
health_unknown: | ||
this.totalAllocs - | ||
this.newRunningHealthyAllocBlocks.length - | ||
this.newRunningUnhealthyAllocBlocks.length, | ||
}; | ||
} | ||
// #endregion legend | ||
|
@@ -205,4 +246,8 @@ export default class JobStatusPanelDeployingComponent extends Component { | |
// v----- Realistic method: Tally a job's task groups' "count" property | ||
return this.args.job.taskGroups.reduce((sum, tg) => sum + tg.count, 0); | ||
} | ||
|
||
get deploymentIsAutoPromoted() { | ||
return this.job.latestDeployment?.get('isAutoPromoted'); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the little top-right-corner status indicator that shows up when enough of an alloc exist to summarize it in a "+N" block