github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/job_periodic_force.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/nomad/api"
     8  	"github.com/posener/complete"
     9  )
    10  
    11  type JobPeriodicForceCommand struct {
    12  	Meta
    13  }
    14  
    15  func (c *JobPeriodicForceCommand) Help() string {
    16  	helpText := `
    17  Usage: nomad job periodic force <job id>
    18  
    19    This command is used to force the creation of a new instance of a periodic job.
    20    This is used to immediately run a periodic job, even if it violates the job's
    21    prohibit_overlap setting.
    22  
    23    When ACLs are enabled, this command requires a token with the 'submit-job'
    24    and 'list-jobs' capabilities for the job's namespace.
    25  
    26  General Options:
    27  
    28    ` + generalOptionsUsage(usageOptsDefault) + `
    29  
    30  Periodic Force Options:
    31  
    32    -detach
    33      Return immediately instead of entering monitor mode. After the force,
    34      the evaluation ID will be printed to the screen, which can be used to
    35      examine the evaluation using the eval-status command.
    36  
    37    -verbose
    38      Display full information.
    39  `
    40  
    41  	return strings.TrimSpace(helpText)
    42  }
    43  
    44  func (c *JobPeriodicForceCommand) Synopsis() string {
    45  	return "Force the launch of a periodic job"
    46  }
    47  
    48  func (c *JobPeriodicForceCommand) AutocompleteFlags() complete.Flags {
    49  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    50  		complete.Flags{
    51  			"-detach":  complete.PredictNothing,
    52  			"-verbose": complete.PredictNothing,
    53  		})
    54  }
    55  
    56  func (c *JobPeriodicForceCommand) AutocompleteArgs() complete.Predictor {
    57  	return complete.PredictFunc(func(a complete.Args) []string {
    58  		client, err := c.Meta.Client()
    59  		if err != nil {
    60  			return nil
    61  		}
    62  
    63  		resp, _, err := client.Jobs().PrefixList(a.Last)
    64  		if err != nil {
    65  			return []string{}
    66  		}
    67  
    68  		// filter this by periodic jobs
    69  		matches := make([]string, 0, len(resp))
    70  		for _, job := range resp {
    71  			if job.Periodic {
    72  				matches = append(matches, job.ID)
    73  			}
    74  		}
    75  		return matches
    76  	})
    77  }
    78  
    79  func (c *JobPeriodicForceCommand) Name() string { return "job periodic force" }
    80  
    81  func (c *JobPeriodicForceCommand) Run(args []string) int {
    82  	var detach, verbose bool
    83  
    84  	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
    85  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    86  	flags.BoolVar(&detach, "detach", false, "")
    87  	flags.BoolVar(&verbose, "verbose", false, "")
    88  
    89  	if err := flags.Parse(args); err != nil {
    90  		return 1
    91  	}
    92  
    93  	// Check that we got exactly one argument
    94  	args = flags.Args()
    95  	if l := len(args); l != 1 {
    96  		c.Ui.Error("This command takes one argument: <job id>")
    97  		c.Ui.Error(commandErrorText(c))
    98  		return 1
    99  	}
   100  
   101  	// Truncate the id unless full length is requested
   102  	length := shortId
   103  	if verbose {
   104  		length = fullId
   105  	}
   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  	jobID := args[0]
   116  	jobs, _, err := client.Jobs().PrefixList(jobID)
   117  	if err != nil {
   118  		c.Ui.Error(fmt.Sprintf("Error forcing periodic job: %s", err))
   119  		return 1
   120  	}
   121  	// filter non-periodic jobs
   122  	periodicJobs := make([]*api.JobListStub, 0, len(jobs))
   123  	for _, j := range jobs {
   124  		if j.Periodic {
   125  			periodicJobs = append(periodicJobs, j)
   126  		}
   127  	}
   128  	if len(periodicJobs) == 0 {
   129  		c.Ui.Error(fmt.Sprintf("No periodic job(s) with prefix or id %q found", jobID))
   130  		return 1
   131  	}
   132  	// preriodicJobs is sorted by job ID
   133  	// so if there is a job whose ID is equal to jobID then it must be the first item
   134  	if len(periodicJobs) > 1 && periodicJobs[0].ID != jobID {
   135  		c.Ui.Error(fmt.Sprintf("Prefix matched multiple periodic jobs\n\n%s", createStatusListOutput(periodicJobs, c.allNamespaces())))
   136  		return 1
   137  	}
   138  	jobID = periodicJobs[0].ID
   139  	q := &api.WriteOptions{Namespace: periodicJobs[0].JobSummary.Namespace}
   140  
   141  	// force the evaluation
   142  	evalID, _, err := client.Jobs().PeriodicForce(jobID, q)
   143  	if err != nil {
   144  		c.Ui.Error(fmt.Sprintf("Error forcing periodic job %q: %s", jobID, err))
   145  		return 1
   146  	}
   147  
   148  	if detach {
   149  		c.Ui.Output("Force periodic successful")
   150  		c.Ui.Output("Evaluation ID: " + evalID)
   151  		return 0
   152  	}
   153  
   154  	// Detach was not specified, so start monitoring
   155  	mon := newMonitor(c.Ui, client, length)
   156  	return mon.monitor(evalID)
   157  }