github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/alloc_restart.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/nomad/api" 8 "github.com/hashicorp/nomad/api/contexts" 9 "github.com/posener/complete" 10 ) 11 12 type AllocRestartCommand struct { 13 Meta 14 } 15 16 func (c *AllocRestartCommand) Help() string { 17 helpText := ` 18 Usage: nomad alloc restart [options] <allocation> <task> 19 20 Restart an existing allocation. This command is used to restart a specific alloc 21 and its tasks. If no task is provided then all of the allocation's tasks that 22 are currently running will be restarted. 23 24 Use the option '-all-tasks' to restart tasks that have already run, such as 25 non-sidecar prestart and poststart tasks. 26 27 When ACLs are enabled, this command requires a token with the 28 'alloc-lifecycle', 'read-job', and 'list-jobs' capabilities for the 29 allocation's namespace. 30 31 General Options: 32 33 ` + generalOptionsUsage(usageOptsDefault) + ` 34 35 Restart Specific Options: 36 37 -all-tasks 38 If set, all tasks in the allocation will be restarted, even the ones that 39 already ran. This option cannot be used with '-task' or the '<task>' 40 argument. 41 42 -task <task-name> 43 Specify the individual task to restart. If task name is given with both an 44 argument and the '-task' option, preference is given to the '-task' option. 45 This option cannot be used with '-all-tasks'. 46 47 -verbose 48 Show full information. 49 ` 50 return strings.TrimSpace(helpText) 51 } 52 53 func (c *AllocRestartCommand) Name() string { return "alloc restart" } 54 55 func (c *AllocRestartCommand) Run(args []string) int { 56 var allTasks, verbose bool 57 var task string 58 59 flags := c.Meta.FlagSet(c.Name(), FlagSetClient) 60 flags.Usage = func() { c.Ui.Output(c.Help()) } 61 flags.BoolVar(&allTasks, "all-tasks", false, "") 62 flags.BoolVar(&verbose, "verbose", false, "") 63 flags.StringVar(&task, "task", "", "") 64 65 if err := flags.Parse(args); err != nil { 66 return 1 67 } 68 69 // Check that we got exactly one alloc 70 args = flags.Args() 71 if len(args) < 1 || len(args) > 2 { 72 c.Ui.Error("This command takes one or two arguments: <alloc-id> <task-name>") 73 c.Ui.Error(commandErrorText(c)) 74 return 1 75 } 76 77 allocID := args[0] 78 79 // If -task isn't provided fallback to reading the task name 80 // from args. 81 if task == "" && len(args) >= 2 { 82 task = args[1] 83 } 84 85 if allTasks && task != "" { 86 c.Ui.Error("The -all-tasks option is not allowed when restarting a specific task.") 87 return 1 88 } 89 90 // Truncate the id unless full length is requested 91 length := shortId 92 if verbose { 93 length = fullId 94 } 95 96 // Query the allocation info 97 if len(allocID) == 1 { 98 c.Ui.Error("Alloc ID must contain at least two characters.") 99 return 1 100 } 101 102 allocID = sanitizeUUIDPrefix(allocID) 103 104 // Get the HTTP client 105 client, err := c.Meta.Client() 106 if err != nil { 107 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 108 return 1 109 } 110 111 allocs, _, err := client.Allocations().PrefixList(allocID) 112 if err != nil { 113 c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err)) 114 return 1 115 } 116 117 if len(allocs) == 0 { 118 c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID)) 119 return 1 120 } 121 122 if len(allocs) > 1 { 123 // Format the allocs 124 out := formatAllocListStubs(allocs, verbose, length) 125 c.Ui.Error(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", out)) 126 return 1 127 } 128 129 // Prefix lookup matched a single allocation 130 q := &api.QueryOptions{Namespace: allocs[0].Namespace} 131 alloc, _, err := client.Allocations().Info(allocs[0].ID, q) 132 if err != nil { 133 c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err)) 134 return 1 135 } 136 137 if task != "" { 138 err := validateTaskExistsInAllocation(task, alloc) 139 if err != nil { 140 c.Ui.Error(err.Error()) 141 return 1 142 } 143 } 144 145 if allTasks { 146 err = client.Allocations().RestartAllTasks(alloc, nil) 147 } else { 148 err = client.Allocations().Restart(alloc, task, nil) 149 } 150 if err != nil { 151 target := "allocation" 152 if task != "" { 153 target = "task" 154 } 155 c.Ui.Error(fmt.Sprintf("Failed to restart %s:\n\n%s", target, err.Error())) 156 return 1 157 } 158 159 return 0 160 } 161 162 func validateTaskExistsInAllocation(taskName string, alloc *api.Allocation) error { 163 tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup) 164 if tg == nil { 165 return fmt.Errorf("Could not find allocation task group: %s", alloc.TaskGroup) 166 } 167 168 foundTaskNames := make([]string, len(tg.Tasks)) 169 for i, task := range tg.Tasks { 170 foundTaskNames[i] = task.Name 171 if task.Name == taskName { 172 return nil 173 } 174 } 175 176 return fmt.Errorf("Could not find task named: %s, found:\n%s", taskName, formatList(foundTaskNames)) 177 } 178 179 func (c *AllocRestartCommand) Synopsis() string { 180 return "Restart a running allocation" 181 } 182 183 func (c *AllocRestartCommand) AutocompleteArgs() complete.Predictor { 184 // Here we attempt to autocomplete allocations for any position of arg. 185 // We should eventually try to auto complete the task name if the arg is 186 // at position 2. 187 return complete.PredictFunc(func(a complete.Args) []string { 188 client, err := c.Meta.Client() 189 if err != nil { 190 return nil 191 } 192 193 resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Allocs, nil) 194 if err != nil { 195 return []string{} 196 } 197 return resp.Matches[contexts.Allocs] 198 }) 199 }