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