From bb1fc6ec5f5ef5a293d8f3cf9a4f1905c5d40a46 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Thu, 9 Dec 2021 15:09:28 +0100 Subject: [PATCH] CLI: Return non-zero exit code when deployment fails in `nomad run` (#11550) * Exit non-zero from run command if deployment fails * Fix typo in deployment monitor introduced in 0edda11 --- .changelog/11550.txt | 3 +++ command/deployment_status.go | 33 ++++++++++++++--------- command/monitor.go | 5 +++- website/content/docs/commands/job/run.mdx | 3 ++- 4 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 .changelog/11550.txt diff --git a/.changelog/11550.txt b/.changelog/11550.txt new file mode 100644 index 00000000000..fab2cca169d --- /dev/null +++ b/.changelog/11550.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: Return non-zero exit code from monitor if deployment fails +``` diff --git a/command/deployment_status.go b/command/deployment_status.go index 18de5e218ce..47a0c835ad9 100644 --- a/command/deployment_status.go +++ b/command/deployment_status.go @@ -181,11 +181,11 @@ func (c *DeploymentStatusCommand) Run(args []string) int { return 0 } -func (c *DeploymentStatusCommand) monitor(client *api.Client, deployID string, index uint64, verbose bool) { +func (c *DeploymentStatusCommand) monitor(client *api.Client, deployID string, index uint64, verbose bool) (status string, err error) { if isStdoutTerminal() { - c.ttyMonitor(client, deployID, index, verbose) + return c.ttyMonitor(client, deployID, index, verbose) } else { - c.defaultMonitor(client, deployID, index, verbose) + return c.defaultMonitor(client, deployID, index, verbose) } } @@ -208,7 +208,7 @@ func isStdoutTerminal() bool { // but only used for tty and non-Windows machines since glint doesn't work with // cmd/PowerShell and non-interactive interfaces // Margins are used to match the text alignment from job run -func (c *DeploymentStatusCommand) ttyMonitor(client *api.Client, deployID string, index uint64, verbose bool) { +func (c *DeploymentStatusCommand) ttyMonitor(client *api.Client, deployID string, index uint64, verbose bool) (status string, err error) { var length int if verbose { length = fullId @@ -242,7 +242,9 @@ func (c *DeploymentStatusCommand) ttyMonitor(client *api.Client, deployID string UPDATE: for { - deploy, meta, err := client.Deployments().Info(deployID, &q) + var deploy *api.Deployment + var meta *api.QueryMeta + deploy, meta, err = client.Deployments().Info(deployID, &q) if err != nil { d.Append(glint.Layout(glint.Style( glint.Text(fmt.Sprintf("%s: Error fetching deployment", formatTime(time.Now()))), @@ -252,7 +254,7 @@ UPDATE: return } - status := deploy.Status + status = deploy.Status statusComponent = glint.Layout( glint.Text(""), glint.Text(formatTime(time.Now())), @@ -309,7 +311,8 @@ UPDATE: // Wait for rollback to launch time.Sleep(1 * time.Second) - rollback, _, err := client.Jobs().LatestDeployment(deploy.JobID, nil) + var rollback *api.Deployment + rollback, _, err = client.Jobs().LatestDeployment(deploy.JobID, nil) if err != nil { d.Append(glint.Layout(glint.Style( @@ -342,7 +345,7 @@ UPDATE: glint.Text(fmt.Sprintf("✓ Deployment %q %s", limit(deployID, length), status)), ).Row().MarginLeft(2) break UPDATE - case structs.DeploymentStatusCancelled, structs.DeploymentStatusDescriptionBlocked: + case structs.DeploymentStatusCancelled, structs.DeploymentStatusBlocked: endSpinner = glint.Layout( glint.Text(fmt.Sprintf("! Deployment %q %s", limit(deployID, length), status)), ).Row().MarginLeft(2) @@ -355,10 +358,11 @@ UPDATE: // Render one final time with completion message d.Set(endSpinner, statusComponent, glint.Text("")) d.RenderFrame() + return } // Used for Windows and non-tty -func (c *DeploymentStatusCommand) defaultMonitor(client *api.Client, deployID string, index uint64, verbose bool) { +func (c *DeploymentStatusCommand) defaultMonitor(client *api.Client, deployID string, index uint64, verbose bool) (status string, err error) { writer := uilive.New() writer.Start() defer writer.Stop() @@ -377,13 +381,15 @@ func (c *DeploymentStatusCommand) defaultMonitor(client *api.Client, deployID st } for { - deploy, meta, err := client.Deployments().Info(deployID, &q) + var deploy *api.Deployment + var meta *api.QueryMeta + deploy, meta, err = client.Deployments().Info(deployID, &q) if err != nil { c.Ui.Error(c.Colorize().Color(fmt.Sprintf("%s: Error fetching deployment", formatTime(time.Now())))) return } - status := deploy.Status + status = deploy.Status info := formatTime(time.Now()) info += fmt.Sprintf("\n%s", formatDeployment(client, deploy, length)) @@ -413,7 +419,8 @@ func (c *DeploymentStatusCommand) defaultMonitor(client *api.Client, deployID st if hasAutoRevert(deploy) { // Wait for rollback to launch time.Sleep(1 * time.Second) - rollback, _, err := client.Jobs().LatestDeployment(deploy.JobID, nil) + var rollback *api.Deployment + rollback, _, err = client.Jobs().LatestDeployment(deploy.JobID, nil) // Separate rollback monitoring from failed deployment // Needs to be after time.Sleep or it messes up the formatting @@ -436,7 +443,7 @@ func (c *DeploymentStatusCommand) defaultMonitor(client *api.Client, deployID st } return - case structs.DeploymentStatusSuccessful, structs.DeploymentStatusCancelled, structs.DeploymentStatusDescriptionBlocked: + case structs.DeploymentStatusSuccessful, structs.DeploymentStatusCancelled, structs.DeploymentStatusBlocked: return default: q.WaitIndex = meta.LastIndex diff --git a/command/monitor.go b/command/monitor.go index 459734f058f..631e2671755 100644 --- a/command/monitor.go +++ b/command/monitor.go @@ -300,7 +300,10 @@ func (m *monitor) monitor(evalID string) int { meta := new(Meta) meta.Ui = m.ui cmd := &DeploymentStatusCommand{Meta: *meta} - cmd.monitor(m.client, dID, 0, verbose) + status, err := cmd.monitor(m.client, dID, 0, verbose) + if err != nil || status != structs.DeploymentStatusSuccessful { + return 1 + } } // Treat scheduling failures specially using a dedicated exit code. diff --git a/website/content/docs/commands/job/run.mdx b/website/content/docs/commands/job/run.mdx index 73f50f15ccd..d450e8a2c10 100644 --- a/website/content/docs/commands/job/run.mdx +++ b/website/content/docs/commands/job/run.mdx @@ -34,7 +34,8 @@ exit after scheduling and deployment have finished or failed. On successful job submission and scheduling, exit code 0 will be returned. If there are job placement issues encountered (unsatisfiable constraints, resource exhaustion, etc), then the exit code will be 2. Any other errors, including -client connection issues or internal errors, are indicated by exit code 1. +deployment failures, client connection issues, or internal errors, are indicated +by exit code 1. If the job has specified the region, the `-region` flag and `$NOMAD_REGION` environment variable are overridden and the job's region is used.