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
This commit is contained in:
Lukas W 2021-12-09 15:09:28 +01:00 committed by GitHub
parent 92eccda949
commit 0e5958d671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 15 deletions

3
.changelog/11550.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
cli: Return non-zero exit code from monitor if deployment fails
```

View File

@ -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

View File

@ -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.

View File

@ -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.