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  }