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