diff --git a/nomad/job_endpoint_statuses.go b/nomad/job_endpoint_statuses.go index 32b051271a3..c62fb37c193 100644 --- a/nomad/job_endpoint_statuses.go +++ b/nomad/job_endpoint_statuses.go @@ -219,6 +219,7 @@ func jobStatusesJobFromJob(ws memdb.WatchSet, store *state.StateStore, job *stru GroupCountSum: 0, ChildStatuses: nil, LatestDeployment: nil, + Stop: job.Stop, } // the GroupCountSum will map to how many allocations we expect to run diff --git a/nomad/structs/job.go b/nomad/structs/job.go index 981992fbea0..547e913eae6 100644 --- a/nomad/structs/job.go +++ b/nomad/structs/job.go @@ -97,6 +97,7 @@ type JobStatusesJob struct { // ParentID is set on child (batch) jobs, specifying the parent job ID ParentID string LatestDeployment *JobStatusesLatestDeployment + Stop bool // has the job been manually stopped? } // JobStatusesAlloc contains a subset of Allocation info. diff --git a/ui/app/components/job-status/panel/steady.js b/ui/app/components/job-status/panel/steady.js index a4aabdd0c79..c37f998564c 100644 --- a/ui/app/components/job-status/panel/steady.js +++ b/ui/app/components/job-status/panel/steady.js @@ -205,8 +205,8 @@ export default class JobStatusPanelSteadyComponent extends Component { /** * @typedef {Object} CurrentStatus - * @property {"Healthy"|"Failed"|"Degraded"|"Recovering"|"Complete"|"Running"} label - The current status of the job - * @property {"highlight"|"success"|"warning"|"critical"} state - + * @property {"Healthy"|"Failed"|"Degraded"|"Recovering"|"Complete"|"Running"|"Stopped"} label - The current status of the job + * @property {"highlight"|"success"|"warning"|"critical"|"neutral"} state - */ /** @@ -217,6 +217,13 @@ export default class JobStatusPanelSteadyComponent extends Component { // If all allocs are running, the job is Healthy const totalAllocs = this.totalAllocs; + if (this.job.status === 'dead' && this.job.stopped) { + return { + label: 'Stopped', + state: 'neutral', + }; + } + 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; diff --git a/ui/app/models/job.js b/ui/app/models/job.js index 629ab638f6c..71285f64be2 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -32,6 +32,7 @@ export default class Job extends Model { @attr('number') modifyIndex; @attr('date') submitTime; @attr('string') nodePool; // Jobs are related to Node Pools either directly or via its Namespace, but no relationship. + @attr('boolean') stopped; @attr() ui; @attr('number') groupCountSum; @@ -89,7 +90,7 @@ export default class Job extends Model { /** * @typedef {Object} CurrentStatus - * @property {"Healthy"|"Failed"|"Deploying"|"Degraded"|"Recovering"|"Complete"|"Running"|"Removed"} label - The current status of the job + * @property {"Healthy"|"Failed"|"Deploying"|"Degraded"|"Recovering"|"Complete"|"Running"|"Removed"|"Stopped"} label - The current status of the job * @property {"highlight"|"success"|"warning"|"critical"|"neutral"} state - */ @@ -224,6 +225,7 @@ export default class Job extends Model { * - Degraded: A deployment is not taking place, and some allocations are failed, lost, or unplaced * - Failed: All allocations are failed, lost, or unplaced * - Removed: The job appeared in our initial query, but has since been garbage collected + * - Stopped: The job has been manually stopped (and not purged or yet garbage collected) by a user * @returns {CurrentStatus} */ /** @@ -238,6 +240,14 @@ export default class Job extends Model { return { label: 'Deploying', state: 'highlight' }; } + // if manually stopped by a user: + if (this.stopped) { + return { + label: 'Stopped', + state: 'neutral', + }; + } + // If the job was requested initially, but a subsequent request for it was // not found, we can remove links to it but maintain its presence in the list // until the user specifies they want a refresh diff --git a/ui/app/serializers/job.js b/ui/app/serializers/job.js index a7876c435bb..940e558b82b 100644 --- a/ui/app/serializers/job.js +++ b/ui/app/serializers/job.js @@ -62,6 +62,12 @@ export default class JobSerializer extends ApplicationSerializer { }); } + // job.stop is reserved as a method (points to adapter method) so we rename it here + if (hash.Stop) { + hash.Stopped = hash.Stop; + delete hash.Stop; + } + return super.normalize(typeHash, hash); }