github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/command/job_stop.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/nomad/api/contexts" 8 "github.com/posener/complete" 9 ) 10 11 type JobStopCommand struct { 12 Meta 13 } 14 15 func (c *JobStopCommand) Help() string { 16 helpText := ` 17 Usage: nomad job stop [options] <job> 18 Alias: nomad stop 19 20 Stop an existing job. This command is used to signal allocations 21 to shut down for the given job ID. Upon successful deregistration, 22 an interactive monitor session will start to display log lines as 23 the job unwinds its allocations and completes shutting down. It 24 is safe to exit the monitor early using ctrl+c. 25 26 General Options: 27 28 ` + generalOptionsUsage() + ` 29 30 Stop Options: 31 32 -detach 33 Return immediately instead of entering monitor mode. After the 34 deregister command is submitted, a new evaluation ID is printed to the 35 screen, which can be used to examine the evaluation using the eval-status 36 command. 37 38 -purge 39 Purge is used to stop the job and purge it from the system. If not set, the 40 job will still be queryable and will be purged by the garbage collector. 41 42 -yes 43 Automatic yes to prompts. 44 45 -verbose 46 Display full information. 47 ` 48 return strings.TrimSpace(helpText) 49 } 50 51 func (c *JobStopCommand) Synopsis() string { 52 return "Stop a running job" 53 } 54 55 func (c *JobStopCommand) AutocompleteFlags() complete.Flags { 56 return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), 57 complete.Flags{ 58 "-detach": complete.PredictNothing, 59 "-purge": complete.PredictNothing, 60 "-yes": complete.PredictNothing, 61 "-verbose": complete.PredictNothing, 62 }) 63 } 64 65 func (c *JobStopCommand) AutocompleteArgs() complete.Predictor { 66 return complete.PredictFunc(func(a complete.Args) []string { 67 client, err := c.Meta.Client() 68 if err != nil { 69 return nil 70 } 71 72 resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Jobs, nil) 73 if err != nil { 74 return []string{} 75 } 76 return resp.Matches[contexts.Jobs] 77 }) 78 } 79 80 func (c *JobStopCommand) Name() string { return "job stop" } 81 82 func (c *JobStopCommand) Run(args []string) int { 83 var detach, purge, verbose, autoYes bool 84 85 flags := c.Meta.FlagSet(c.Name(), FlagSetClient) 86 flags.Usage = func() { c.Ui.Output(c.Help()) } 87 flags.BoolVar(&detach, "detach", false, "") 88 flags.BoolVar(&verbose, "verbose", false, "") 89 flags.BoolVar(&autoYes, "yes", false, "") 90 flags.BoolVar(&purge, "purge", false, "") 91 92 if err := flags.Parse(args); err != nil { 93 return 1 94 } 95 96 // Truncate the id unless full length is requested 97 length := shortId 98 if verbose { 99 length = fullId 100 } 101 102 // Check that we got exactly one job 103 args = flags.Args() 104 if len(args) != 1 { 105 c.Ui.Error("This command takes one argument: <job>") 106 c.Ui.Error(commandErrorText(c)) 107 return 1 108 } 109 jobID := args[0] 110 111 // Get the HTTP client 112 client, err := c.Meta.Client() 113 if err != nil { 114 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 115 return 1 116 } 117 118 // Check if the job exists 119 jobs, _, err := client.Jobs().PrefixList(jobID) 120 if err != nil { 121 c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err)) 122 return 1 123 } 124 if len(jobs) == 0 { 125 c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID)) 126 return 1 127 } 128 if len(jobs) > 1 && strings.TrimSpace(jobID) != jobs[0].ID { 129 c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs))) 130 return 1 131 } 132 // Prefix lookup matched a single job 133 job, _, err := client.Jobs().Info(jobs[0].ID, nil) 134 if err != nil { 135 c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err)) 136 return 1 137 } 138 139 // Confirm the stop if the job was a prefix match. 140 if jobID != *job.ID && !autoYes { 141 question := fmt.Sprintf("Are you sure you want to stop job %q? [y/N]", *job.ID) 142 answer, err := c.Ui.Ask(question) 143 if err != nil { 144 c.Ui.Error(fmt.Sprintf("Failed to parse answer: %v", err)) 145 return 1 146 } 147 148 if answer == "" || strings.ToLower(answer)[0] == 'n' { 149 // No case 150 c.Ui.Output("Cancelling job stop") 151 return 0 152 } else if strings.ToLower(answer)[0] == 'y' && len(answer) > 1 { 153 // Non exact match yes 154 c.Ui.Output("For confirmation, an exact ‘y’ is required.") 155 return 0 156 } else if answer != "y" { 157 c.Ui.Output("No confirmation detected. For confirmation, an exact 'y' is required.") 158 return 1 159 } 160 } 161 162 // Invoke the stop 163 evalID, _, err := client.Jobs().Deregister(*job.ID, purge, nil) 164 if err != nil { 165 c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err)) 166 return 1 167 } 168 169 // If we are stopping a periodic job there won't be an evalID. 170 if evalID == "" { 171 return 0 172 } 173 174 if detach { 175 c.Ui.Output(evalID) 176 return 0 177 } 178 179 // Start monitoring the stop eval 180 mon := newMonitor(c.Ui, client, length) 181 return mon.monitor(evalID, false) 182 }