1package command
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/hashicorp/nomad/api"
8)
9
10type AllocStopCommand struct {
11	Meta
12}
13
14func (a *AllocStopCommand) Help() string {
15	helpText := `
16Usage: nomad alloc stop [options] <allocation>
17Alias: nomad stop
18
19  Stop an existing allocation. This command is used to signal a specific alloc
20  to shut down. When the allocation has been shut down, it will then be
21  rescheduled. An interactive monitoring session will display log lines as the
22  allocation completes shutting down. It is safe to exit the monitor early with
23  ctrl-c.
24
25  When ACLs are enabled, this command requires a token with the
26  'alloc-lifecycle', 'read-job', and 'list-jobs' capabilities for the
27  allocation's namespace.
28
29General Options:
30
31  ` + generalOptionsUsage(usageOptsDefault) + `
32
33Stop Specific Options:
34
35  -detach
36    Return immediately instead of entering monitor mode. After the
37    stop command is submitted, a new evaluation ID is printed to the
38    screen, which can be used to examine the rescheduling evaluation using the
39    eval-status command.
40
41  -verbose
42    Show full information.
43`
44	return strings.TrimSpace(helpText)
45}
46
47func (c *AllocStopCommand) Name() string { return "alloc stop" }
48
49func (c *AllocStopCommand) Run(args []string) int {
50	var detach, verbose bool
51
52	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
53	flags.Usage = func() { c.Ui.Output(c.Help()) }
54	flags.BoolVar(&detach, "detach", false, "")
55	flags.BoolVar(&verbose, "verbose", false, "")
56
57	if err := flags.Parse(args); err != nil {
58		return 1
59	}
60
61	// Check that we got exactly one alloc
62	args = flags.Args()
63	if len(args) != 1 {
64		c.Ui.Error("This command takes one argument: <alloc-id>")
65		c.Ui.Error(commandErrorText(c))
66		return 1
67	}
68
69	allocID := args[0]
70
71	// Truncate the id unless full length is requested
72	length := shortId
73	if verbose {
74		length = fullId
75	}
76
77	// Query the allocation info
78	if len(allocID) == 1 {
79		c.Ui.Error("Alloc ID must contain at least two characters.")
80		return 1
81	}
82
83	allocID = sanitizeUUIDPrefix(allocID)
84
85	// Get the HTTP client
86	client, err := c.Meta.Client()
87	if err != nil {
88		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
89		return 1
90	}
91
92	allocs, _, err := client.Allocations().PrefixList(allocID)
93	if err != nil {
94		c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err))
95		return 1
96	}
97
98	if len(allocs) == 0 {
99		c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID))
100		return 1
101	}
102
103	if len(allocs) > 1 {
104		// Format the allocs
105		out := formatAllocListStubs(allocs, verbose, length)
106		c.Ui.Error(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", out))
107		return 1
108	}
109
110	// Prefix lookup matched a single allocation
111	q := &api.QueryOptions{Namespace: allocs[0].Namespace}
112	alloc, _, err := client.Allocations().Info(allocs[0].ID, q)
113	if err != nil {
114		c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
115		return 1
116	}
117
118	resp, err := client.Allocations().Stop(alloc, nil)
119	if err != nil {
120		c.Ui.Error(fmt.Sprintf("Error stopping allocation: %s", err))
121		return 1
122	}
123
124	if detach {
125		c.Ui.Output(resp.EvalID)
126		return 0
127	}
128
129	mon := newMonitor(c.Ui, client, length)
130	return mon.monitor(resp.EvalID)
131}
132
133func (a *AllocStopCommand) Synopsis() string {
134	return "Stop and reschedule a running allocation"
135}
136