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  }