1package command
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/hashicorp/nomad/api/contexts"
8	"github.com/posener/complete"
9)
10
11type AllocSignalCommand struct {
12	Meta
13}
14
15func (a *AllocSignalCommand) Help() string {
16	helpText := `
17Usage: nomad alloc signal [options] <signal> <allocation> <task>
18
19  signal an existing allocation. This command is used to signal a specific alloc
20  and its subtasks. If no task is provided then all of the allocations subtasks
21  will receive the signal.
22
23General Options:
24
25  ` + generalOptionsUsage() + `
26
27Signal Specific Options:
28
29  -s
30    Specify the signal that the selected tasks should receive.
31
32  -verbose
33    Show full information.
34`
35	return strings.TrimSpace(helpText)
36}
37
38func (c *AllocSignalCommand) Name() string { return "alloc signal" }
39
40func (c *AllocSignalCommand) Run(args []string) int {
41	var verbose bool
42	var signal string
43
44	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
45	flags.Usage = func() { c.Ui.Output(c.Help()) }
46	flags.BoolVar(&verbose, "verbose", false, "")
47	flags.StringVar(&signal, "s", "SIGKILL", "")
48
49	if err := flags.Parse(args); err != nil {
50		return 1
51	}
52
53	// Check that we got exactly one alloc
54	args = flags.Args()
55	if len(args) < 1 || len(args) > 2 {
56		c.Ui.Error("This command takes up to two arguments: <alloc-id> <task>")
57		c.Ui.Error(commandErrorText(c))
58		return 1
59	}
60
61	allocID := args[0]
62
63	// Truncate the id unless full length is requested
64	length := shortId
65	if verbose {
66		length = fullId
67	}
68
69	// Query the allocation info
70	if len(allocID) == 1 {
71		c.Ui.Error(fmt.Sprintf("Alloc ID must contain at least two characters."))
72		return 1
73	}
74
75	allocID = sanitizeUUIDPrefix(allocID)
76
77	// Get the HTTP client
78	client, err := c.Meta.Client()
79	if err != nil {
80		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
81		return 1
82	}
83
84	allocs, _, err := client.Allocations().PrefixList(allocID)
85	if err != nil {
86		c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err))
87		return 1
88	}
89
90	if len(allocs) == 0 {
91		c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID))
92		return 1
93	}
94
95	if len(allocs) > 1 {
96		// Format the allocs
97		out := formatAllocListStubs(allocs, verbose, length)
98		c.Ui.Error(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", out))
99		return 1
100	}
101
102	// Prefix lookup matched a single allocation
103	alloc, _, err := client.Allocations().Info(allocs[0].ID, nil)
104	if err != nil {
105		c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
106		return 1
107	}
108
109	var taskName string
110	if len(args) == 2 {
111		// Validate Task
112		taskName = args[1]
113		err := validateTaskExistsInAllocation(taskName, alloc)
114		if err != nil {
115			c.Ui.Error(err.Error())
116			return 1
117		}
118	}
119
120	err = client.Allocations().Signal(alloc, nil, taskName, signal)
121	if err != nil {
122		c.Ui.Error(fmt.Sprintf("Error signalling allocation: %s", err))
123		return 1
124	}
125
126	return 0
127}
128
129func (a *AllocSignalCommand) Synopsis() string {
130	return "Signal a running allocation"
131}
132
133func (c *AllocSignalCommand) AutocompleteFlags() complete.Flags {
134	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
135		complete.Flags{
136			"-s":       complete.PredictNothing,
137			"-verbose": complete.PredictNothing,
138		})
139}
140func (c *AllocSignalCommand) AutocompleteArgs() complete.Predictor {
141	// Here we only autocomplete allocation names. Eventually we may consider
142	// expanding this to also autocomplete task names. To do so, we'll need to
143	// either change the autocompletion api, or implement parsing such that we can
144	// easily compute the current arg position.
145	return complete.PredictFunc(func(a complete.Args) []string {
146		client, err := c.Meta.Client()
147		if err != nil {
148			return nil
149		}
150
151		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Allocs, nil)
152		if err != nil {
153			return []string{}
154		}
155		return resp.Matches[contexts.Allocs]
156	})
157}
158