github.com/ilhicas/nomad@v0.11.8/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  }