github.com/ThomasObenaus/nomad@v0.11.1/command/job_promote.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/nomad/api"
     8  	"github.com/hashicorp/nomad/api/contexts"
     9  	flaghelper "github.com/hashicorp/nomad/helper/flag-helpers"
    10  	"github.com/posener/complete"
    11  )
    12  
    13  type JobPromoteCommand struct {
    14  	Meta
    15  }
    16  
    17  func (c *JobPromoteCommand) Help() string {
    18  	helpText := `
    19  Usage: nomad job promote [options] <job id>
    20  
    21    Promote is used to promote task groups in the most recent deployment for the
    22    given job. Promotion should occur when the deployment has placed canaries for a
    23    task group and those canaries have been deemed healthy. When a task group is
    24    promoted, the rolling upgrade of the remaining allocations is unblocked. If the
    25    canaries are found to be unhealthy, the deployment may either be failed using
    26    the "nomad deployment fail" command, the job can be failed forward by submitting
    27    a new version or failed backwards by reverting to an older version using the
    28    "nomad job revert" command.
    29  
    30  General Options:
    31  
    32    ` + generalOptionsUsage() + `
    33  
    34  Promote Options:
    35  
    36    -group
    37      Group may be specified many times and is used to promote that particular
    38      group. If no specific groups are specified, all groups are promoted.
    39  
    40    -detach
    41      Return immediately instead of entering monitor mode. After deployment
    42      resume, the evaluation ID will be printed to the screen, which can be used
    43      to examine the evaluation using the eval-status command.
    44  
    45    -verbose
    46      Display full information.
    47  `
    48  	return strings.TrimSpace(helpText)
    49  }
    50  
    51  func (c *JobPromoteCommand) Synopsis() string {
    52  	return "Promote a job's canaries"
    53  }
    54  
    55  func (c *JobPromoteCommand) AutocompleteFlags() complete.Flags {
    56  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    57  		complete.Flags{
    58  			"-group":   complete.PredictAnything,
    59  			"-detach":  complete.PredictNothing,
    60  			"-verbose": complete.PredictNothing,
    61  		})
    62  }
    63  
    64  func (c *JobPromoteCommand) 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 *JobPromoteCommand) Name() string { return "job promote" }
    80  
    81  func (c *JobPromoteCommand) Run(args []string) int {
    82  	var detach, verbose bool
    83  	var groups []string
    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.Var((*flaghelper.StringFlag)(&groups), "group", "")
    90  
    91  	if err := flags.Parse(args); err != nil {
    92  		return 1
    93  	}
    94  
    95  	// Check that we got exactly one argument
    96  	args = flags.Args()
    97  	if l := len(args); l != 1 {
    98  		c.Ui.Error("This command takes one argument: <job id>")
    99  		c.Ui.Error(commandErrorText(c))
   100  		return 1
   101  	}
   102  
   103  	// Truncate the id unless full length is requested
   104  	length := shortId
   105  	if verbose {
   106  		length = fullId
   107  	}
   108  
   109  	// Get the HTTP client
   110  	client, err := c.Meta.Client()
   111  	if err != nil {
   112  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   113  		return 1
   114  	}
   115  
   116  	// Check if the job exists
   117  	jobID := args[0]
   118  	jobs, _, err := client.Jobs().PrefixList(jobID)
   119  	if err != nil {
   120  		c.Ui.Error(fmt.Sprintf("Error promoting job: %s", err))
   121  		return 1
   122  	}
   123  	if len(jobs) == 0 {
   124  		c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID))
   125  		return 1
   126  	}
   127  	if len(jobs) > 1 && strings.TrimSpace(jobID) != jobs[0].ID {
   128  		c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs)))
   129  		return 1
   130  	}
   131  	jobID = jobs[0].ID
   132  
   133  	// Do a prefix lookup
   134  	deploy, _, err := client.Jobs().LatestDeployment(jobID, nil)
   135  	if err != nil {
   136  		c.Ui.Error(fmt.Sprintf("Error retrieving deployment: %s", err))
   137  		return 1
   138  	}
   139  
   140  	if deploy == nil {
   141  		c.Ui.Error(fmt.Sprintf("Job %q has no deployment to promote", jobID))
   142  		return 1
   143  	}
   144  
   145  	var u *api.DeploymentUpdateResponse
   146  	if len(groups) == 0 {
   147  		u, _, err = client.Deployments().PromoteAll(deploy.ID, nil)
   148  	} else {
   149  		u, _, err = client.Deployments().PromoteGroups(deploy.ID, groups, nil)
   150  	}
   151  
   152  	if err != nil {
   153  		c.Ui.Error(fmt.Sprintf("Error promoting deployment %q for job %q: %s", deploy.ID, jobID, err))
   154  		return 1
   155  	}
   156  
   157  	// Nothing to do
   158  	evalCreated := u.EvalID != ""
   159  	if detach || !evalCreated {
   160  		return 0
   161  	}
   162  
   163  	mon := newMonitor(c.Ui, client, length)
   164  	return mon.monitor(u.EvalID, false)
   165  }