github.com/djenriquez/nomad-1@v0.8.1/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) Run(args []string) int { 81 var detach, purge, verbose, autoYes bool 82 83 flags := c.Meta.FlagSet("job stop", FlagSetClient) 84 flags.Usage = func() { c.Ui.Output(c.Help()) } 85 flags.BoolVar(&detach, "detach", false, "") 86 flags.BoolVar(&verbose, "verbose", false, "") 87 flags.BoolVar(&autoYes, "yes", false, "") 88 flags.BoolVar(&purge, "purge", false, "") 89 90 if err := flags.Parse(args); err != nil { 91 return 1 92 } 93 94 // Truncate the id unless full length is requested 95 length := shortId 96 if verbose { 97 length = fullId 98 } 99 100 // Check that we got exactly one job 101 args = flags.Args() 102 if len(args) != 1 { 103 c.Ui.Error(c.Help()) 104 return 1 105 } 106 jobID := args[0] 107 108 // Get the HTTP client 109 client, err := c.Meta.Client() 110 if err != nil { 111 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 112 return 1 113 } 114 115 // Check if the job exists 116 jobs, _, err := client.Jobs().PrefixList(jobID) 117 if err != nil { 118 c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err)) 119 return 1 120 } 121 if len(jobs) == 0 { 122 c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID)) 123 return 1 124 } 125 if len(jobs) > 1 && strings.TrimSpace(jobID) != jobs[0].ID { 126 c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs))) 127 return 1 128 } 129 // Prefix lookup matched a single job 130 job, _, err := client.Jobs().Info(jobs[0].ID, nil) 131 if err != nil { 132 c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err)) 133 return 1 134 } 135 136 // Confirm the stop if the job was a prefix match. 137 if jobID != *job.ID && !autoYes { 138 question := fmt.Sprintf("Are you sure you want to stop job %q? [y/N]", *job.ID) 139 answer, err := c.Ui.Ask(question) 140 if err != nil { 141 c.Ui.Error(fmt.Sprintf("Failed to parse answer: %v", err)) 142 return 1 143 } 144 145 if answer == "" || strings.ToLower(answer)[0] == 'n' { 146 // No case 147 c.Ui.Output("Cancelling job stop") 148 return 0 149 } else if strings.ToLower(answer)[0] == 'y' && len(answer) > 1 { 150 // Non exact match yes 151 c.Ui.Output("For confirmation, an exact ‘y’ is required.") 152 return 0 153 } else if answer != "y" { 154 c.Ui.Output("No confirmation detected. For confirmation, an exact 'y' is required.") 155 return 1 156 } 157 } 158 159 // Invoke the stop 160 evalID, _, err := client.Jobs().Deregister(*job.ID, purge, nil) 161 if err != nil { 162 c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err)) 163 return 1 164 } 165 166 // If we are stopping a periodic job there won't be an evalID. 167 if evalID == "" { 168 return 0 169 } 170 171 if detach { 172 c.Ui.Output(evalID) 173 return 0 174 } 175 176 // Start monitoring the stop eval 177 mon := newMonitor(c.Ui, client, length) 178 return mon.monitor(evalID, false) 179 }