1package command
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/hashicorp/nomad/api"
8	"github.com/hashicorp/nomad/api/contexts"
9	flaghelper "github.com/hashicorp/nomad/helper/flag-helpers"
10	"github.com/posener/complete"
11)
12
13type DeploymentPromoteCommand struct {
14	Meta
15}
16
17func (c *DeploymentPromoteCommand) Help() string {
18	helpText := `
19Usage: nomad deployment promote [options] <deployment id>
20
21  Promote is used to promote task groups in a deployment. Promotion should occur
22  when the deployment has placed canaries for a task group and those canaries have
23  been deemed healthy. When a task group is promoted, the rolling upgrade of the
24  remaining allocations is unblocked. If the canaries are found to be unhealthy,
25  the deployment may either be failed using the "nomad deployment fail" command,
26  the job can be failed forward by submitting a new version or failed backwards by
27  reverting to an older version using the "nomad job revert" command.
28
29General Options:
30
31  ` + generalOptionsUsage() + `
32
33Promote Options:
34
35  -group
36    Group may be specified many times and is used to promote that particular
37    group. If no specific groups are specified, all groups are promoted.
38
39  -detach
40    Return immediately instead of entering monitor mode. After deployment
41    resume, the evaluation ID will be printed to the screen, which can be used
42    to examine the evaluation using the eval-status command.
43
44  -verbose
45    Display full information.
46`
47	return strings.TrimSpace(helpText)
48}
49
50func (c *DeploymentPromoteCommand) Synopsis() string {
51	return "Promote canaries in a deployment"
52}
53
54func (c *DeploymentPromoteCommand) AutocompleteFlags() complete.Flags {
55	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
56		complete.Flags{
57			"-group":   complete.PredictAnything,
58			"-detach":  complete.PredictNothing,
59			"-verbose": complete.PredictNothing,
60		})
61}
62
63func (c *DeploymentPromoteCommand) AutocompleteArgs() complete.Predictor {
64	return complete.PredictFunc(func(a complete.Args) []string {
65		client, err := c.Meta.Client()
66		if err != nil {
67			return nil
68		}
69
70		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Deployments, nil)
71		if err != nil {
72			return []string{}
73		}
74		return resp.Matches[contexts.Deployments]
75	})
76}
77
78func (c *DeploymentPromoteCommand) Name() string { return "deployment promote" }
79
80func (c *DeploymentPromoteCommand) Run(args []string) int {
81	var detach, verbose bool
82	var groups []string
83
84	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
85	flags.Usage = func() { c.Ui.Output(c.Help()) }
86	flags.BoolVar(&detach, "detach", false, "")
87	flags.BoolVar(&verbose, "verbose", false, "")
88	flags.Var((*flaghelper.StringFlag)(&groups), "group", "")
89
90	if err := flags.Parse(args); err != nil {
91		return 1
92	}
93
94	// Check that we got exactly one argument
95	args = flags.Args()
96	if l := len(args); l != 1 {
97		c.Ui.Error("This command takes one argument: <deployment id>")
98		c.Ui.Error(commandErrorText(c))
99		return 1
100	}
101	dID := args[0]
102
103	// Truncate the id unless full length is requested
104	length := shortId
105	if verbose {
106		length = fullId
107	}
108
109	// Get the HTTP client
110	client, err := c.Meta.Client()
111	if err != nil {
112		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
113		return 1
114	}
115
116	// Do a prefix lookup
117	deploy, possible, err := getDeployment(client.Deployments(), dID)
118	if err != nil {
119		c.Ui.Error(fmt.Sprintf("Error retrieving deployment: %s", err))
120		return 1
121	}
122
123	if len(possible) != 0 {
124		c.Ui.Error(fmt.Sprintf("Prefix matched multiple deployments\n\n%s", formatDeployments(possible, length)))
125		return 1
126	}
127
128	var u *api.DeploymentUpdateResponse
129	if len(groups) == 0 {
130		u, _, err = client.Deployments().PromoteAll(deploy.ID, nil)
131	} else {
132		u, _, err = client.Deployments().PromoteGroups(deploy.ID, groups, nil)
133	}
134
135	if err != nil {
136		c.Ui.Error(fmt.Sprintf("Error promoting deployment: %s", err))
137		return 1
138	}
139
140	// Nothing to do
141	evalCreated := u.EvalID != ""
142	if detach || !evalCreated {
143		return 0
144	}
145
146	mon := newMonitor(c.Ui, client, length)
147	return mon.monitor(u.EvalID, false)
148}
149