open-nomad/command/job_revert.go

180 lines
4.5 KiB
Go
Raw Normal View History

2017-06-30 03:28:52 +00:00
package command
import (
"fmt"
"os"
2017-06-30 03:28:52 +00:00
"strings"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/api/contexts"
"github.com/posener/complete"
2017-06-30 03:28:52 +00:00
)
type JobRevertCommand struct {
Meta
}
func (c *JobRevertCommand) Help() string {
helpText := `
2017-06-30 03:42:53 +00:00
Usage: nomad job revert [options] <job> <version>
2017-06-30 03:28:52 +00:00
2017-10-13 21:36:02 +00:00
Revert is used to revert a job to a prior version of the job. The available
versions to revert to can be found using "nomad job history" command.
2017-06-30 03:28:52 +00:00
When ACLs are enabled, this command requires a token with the 'submit-job'
and 'list-jobs' capabilities for the job's namespace.
2017-06-30 03:28:52 +00:00
General Options:
` + generalOptionsUsage(usageOptsDefault) + `
2017-06-30 03:28:52 +00:00
2017-06-30 03:42:53 +00:00
Revert Options:
2017-06-30 03:28:52 +00:00
-detach
2017-06-30 21:27:13 +00:00
Return immediately instead of entering monitor mode. After job revert,
2017-06-30 03:28:52 +00:00
the evaluation ID will be printed to the screen, which can be used to
examine the evaluation using the eval-status command.
-consul-token
The Consul token used to verify that the caller has access to the Service
Identity policies associated in the targeted version of the job.
-vault-token
The Vault token used to verify that the caller has access to the Vault
policies in the targeted version of the job.
-verbose
Display full information.
2017-06-30 03:28:52 +00:00
`
return strings.TrimSpace(helpText)
}
func (c *JobRevertCommand) Synopsis() string {
return "Revert to a prior version of the job"
}
func (c *JobRevertCommand) AutocompleteFlags() complete.Flags {
2017-08-23 21:56:21 +00:00
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
complete.Flags{
"-detach": complete.PredictNothing,
"-verbose": complete.PredictNothing,
})
}
func (c *JobRevertCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictFunc(func(a complete.Args) []string {
client, err := c.Meta.Client()
if err != nil {
return nil
}
resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Jobs, nil)
if err != nil {
return []string{}
}
return resp.Matches[contexts.Jobs]
})
}
func (c *JobRevertCommand) Name() string { return "job revert" }
2017-06-30 03:28:52 +00:00
func (c *JobRevertCommand) Run(args []string) int {
var detach, verbose bool
var consulToken, vaultToken string
2017-06-30 03:28:52 +00:00
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
2017-06-30 03:28:52 +00:00
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&detach, "detach", false, "")
flags.BoolVar(&verbose, "verbose", false, "")
flags.StringVar(&consulToken, "consul-token", "", "")
flags.StringVar(&vaultToken, "vault-token", "", "")
2017-06-30 03:28:52 +00:00
if err := flags.Parse(args); err != nil {
return 1
}
// Truncate the id unless full length is requested
length := shortId
if verbose {
length = fullId
}
2017-06-30 03:42:53 +00:00
// Check that we got two args
2017-06-30 03:28:52 +00:00
args = flags.Args()
2017-06-30 03:42:53 +00:00
if l := len(args); l != 2 {
c.Ui.Error("This command takes two arguments: <job> <version>")
c.Ui.Error(commandErrorText(c))
2017-06-30 03:28:52 +00:00
return 1
}
// Get the HTTP client
client, err := c.Meta.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
return 1
}
// Parse the Consul token
if consulToken == "" {
// Check the environment variable
consulToken = os.Getenv("CONSUL_HTTP_TOKEN")
}
// Parse the Vault token
if vaultToken == "" {
// Check the environment variable
vaultToken = os.Getenv("VAULT_TOKEN")
}
jobID := strings.TrimSpace(args[0])
2017-06-30 03:42:53 +00:00
revertVersion, ok, err := parseVersion(args[1])
if !ok {
c.Ui.Error("The job version to revert to must be specified using the -job-version flag")
return 1
}
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to parse job-version flag: %v", err))
return 1
}
2017-06-30 03:28:52 +00:00
// Check if the job exists
jobs, _, err := client.Jobs().PrefixList(jobID)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error listing jobs: %s", err))
return 1
}
if len(jobs) == 0 {
c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID))
return 1
}
if len(jobs) > 1 {
if (jobID != jobs[0].ID) || (c.allNamespaces() && jobs[0].ID == jobs[1].ID) {
c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs, c.allNamespaces())))
return 1
}
2017-06-30 03:28:52 +00:00
}
// Prefix lookup matched a single job
q := &api.WriteOptions{Namespace: jobs[0].JobSummary.Namespace}
resp, _, err := client.Jobs().Revert(jobs[0].ID, revertVersion, nil, q, consulToken, vaultToken)
2017-06-30 03:28:52 +00:00
if err != nil {
c.Ui.Error(fmt.Sprintf("Error retrieving job versions: %s", err))
return 1
}
// Nothing to do
evalCreated := resp.EvalID != ""
if !evalCreated {
return 0
}
if detach {
c.Ui.Output("Evaluation ID: " + resp.EvalID)
2017-06-30 03:28:52 +00:00
return 0
}
mon := newMonitor(c.Ui, client, length)
return mon.monitor(resp.EvalID)
2017-06-30 03:28:52 +00:00
}