1package command
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/hashicorp/nomad/api/contexts"
8	"github.com/posener/complete"
9)
10
11type DeploymentFailCommand struct {
12	Meta
13}
14
15func (c *DeploymentFailCommand) Help() string {
16	helpText := `
17Usage: nomad deployment fail [options] <deployment id>
18
19  Fail is used to mark a deployment as failed. Failing a deployment will
20  stop the placement of new allocations as part of rolling deployment and
21  if the job is configured to auto revert, the job will attempt to roll back to a
22  stable version.
23
24General Options:
25
26  ` + generalOptionsUsage() + `
27
28Fail Options:
29
30  -detach
31    Return immediately instead of entering monitor mode. After deployment
32    resume, the evaluation ID will be printed to the screen, which can be used
33    to examine the evaluation using the eval-status command.
34
35  -verbose
36    Display full information.
37`
38	return strings.TrimSpace(helpText)
39}
40
41func (c *DeploymentFailCommand) Synopsis() string {
42	return "Manually fail a deployment"
43}
44
45func (c *DeploymentFailCommand) AutocompleteFlags() complete.Flags {
46	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
47		complete.Flags{
48			"-detach":  complete.PredictNothing,
49			"-verbose": complete.PredictNothing,
50		})
51}
52
53func (c *DeploymentFailCommand) AutocompleteArgs() complete.Predictor {
54	return complete.PredictFunc(func(a complete.Args) []string {
55		client, err := c.Meta.Client()
56		if err != nil {
57			return nil
58		}
59
60		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Deployments, nil)
61		if err != nil {
62			return []string{}
63		}
64		return resp.Matches[contexts.Deployments]
65	})
66}
67
68func (c *DeploymentFailCommand) Name() string { return "deployment fail" }
69
70func (c *DeploymentFailCommand) Run(args []string) int {
71	var detach, verbose bool
72
73	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
74	flags.Usage = func() { c.Ui.Output(c.Help()) }
75	flags.BoolVar(&detach, "detach", false, "")
76	flags.BoolVar(&verbose, "verbose", false, "")
77
78	if err := flags.Parse(args); err != nil {
79		return 1
80	}
81
82	// Check that we got one argument
83	args = flags.Args()
84	if l := len(args); l != 1 {
85		c.Ui.Error("This command takes one argument: <deployment id>")
86		c.Ui.Error(commandErrorText(c))
87		return 1
88	}
89
90	dID := args[0]
91
92	// Truncate the id unless full length is requested
93	length := shortId
94	if verbose {
95		length = fullId
96	}
97
98	// Get the HTTP client
99	client, err := c.Meta.Client()
100	if err != nil {
101		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
102		return 1
103	}
104
105	// Do a prefix lookup
106	deploy, possible, err := getDeployment(client.Deployments(), dID)
107	if err != nil {
108		c.Ui.Error(fmt.Sprintf("Error retrieving deployment: %s", err))
109		return 1
110	}
111
112	if len(possible) != 0 {
113		c.Ui.Error(fmt.Sprintf("Prefix matched multiple deployments\n\n%s", formatDeployments(possible, length)))
114		return 1
115	}
116
117	u, _, err := client.Deployments().Fail(deploy.ID, nil)
118	if err != nil {
119		c.Ui.Error(fmt.Sprintf("Error failing deployment: %s", err))
120		return 1
121	}
122
123	if u.RevertedJobVersion == nil {
124		c.Ui.Output(fmt.Sprintf("Deployment %q failed", deploy.ID))
125	} else {
126		c.Ui.Output(fmt.Sprintf("Deployment %q failed. Auto-reverted to job version %d.", deploy.ID, *u.RevertedJobVersion))
127	}
128
129	evalCreated := u.EvalID != ""
130
131	// Nothing to do
132	if detach || !evalCreated {
133		return 0
134	}
135
136	c.Ui.Output("")
137	mon := newMonitor(c.Ui, client, length)
138	return mon.monitor(u.EvalID, false)
139}
140