github.com/thomasobenaus/nomad@v0.11.1/command/job_validate.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 multierror "github.com/hashicorp/go-multierror" 8 "github.com/hashicorp/nomad/api" 9 "github.com/hashicorp/nomad/command/agent" 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/posener/complete" 12 ) 13 14 type JobValidateCommand struct { 15 Meta 16 JobGetter 17 } 18 19 func (c *JobValidateCommand) Help() string { 20 helpText := ` 21 Usage: nomad job validate [options] <path> 22 Alias: nomad validate 23 24 Checks if a given HCL job file has a valid specification. This can be used to 25 check for any syntax errors or validation problems with a job. 26 27 If the supplied path is "-", the jobfile is read from stdin. Otherwise 28 it is read from the file at the supplied path or downloaded and 29 read from URL specified. 30 ` 31 return strings.TrimSpace(helpText) 32 } 33 34 func (c *JobValidateCommand) Synopsis() string { 35 return "Checks if a given job specification is valid" 36 } 37 38 func (c *JobValidateCommand) AutocompleteFlags() complete.Flags { 39 return nil 40 } 41 42 func (c *JobValidateCommand) AutocompleteArgs() complete.Predictor { 43 return complete.PredictOr(complete.PredictFiles("*.nomad"), complete.PredictFiles("*.hcl")) 44 } 45 46 func (c *JobValidateCommand) Name() string { return "job validate" } 47 48 func (c *JobValidateCommand) Run(args []string) int { 49 flags := c.Meta.FlagSet(c.Name(), FlagSetNone) 50 flags.Usage = func() { c.Ui.Output(c.Help()) } 51 if err := flags.Parse(args); err != nil { 52 return 1 53 } 54 55 // Check that we got exactly one node 56 args = flags.Args() 57 if len(args) != 1 { 58 c.Ui.Error("This command takes one argument: <path>") 59 c.Ui.Error(commandErrorText(c)) 60 return 1 61 } 62 63 // Get Job struct from Jobfile 64 job, err := c.JobGetter.ApiJob(args[0]) 65 if err != nil { 66 c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err)) 67 return 1 68 } 69 70 // Get the HTTP client 71 client, err := c.Meta.Client() 72 if err != nil { 73 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 74 return 255 75 } 76 77 // Force the region to be that of the job. 78 if r := job.Region; r != nil { 79 client.SetRegion(*r) 80 } 81 82 // Check that the job is valid 83 jr, _, err := client.Jobs().Validate(job, nil) 84 if err != nil { 85 jr, err = c.validateLocal(job) 86 } 87 if err != nil { 88 c.Ui.Error(fmt.Sprintf("Error validating job: %s", err)) 89 return 1 90 } 91 92 if jr != nil && !jr.DriverConfigValidated { 93 c.Ui.Output( 94 c.Colorize().Color("[bold][yellow]Driver configuration not validated since connection to Nomad agent couldn't be established.[reset]\n")) 95 } 96 97 if jr != nil && jr.Error != "" { 98 c.Ui.Error( 99 c.Colorize().Color("[bold][red]Job validation errors:[reset]")) 100 c.Ui.Error(jr.Error) 101 return 1 102 } 103 104 // Print any warnings if there are any 105 if jr.Warnings != "" { 106 c.Ui.Output( 107 c.Colorize().Color(fmt.Sprintf("[bold][yellow]Job Warnings:\n%s[reset]\n", jr.Warnings))) 108 } 109 110 // Done! 111 c.Ui.Output( 112 c.Colorize().Color("[bold][green]Job validation successful[reset]")) 113 return 0 114 } 115 116 // validateLocal validates without talking to a Nomad agent 117 func (c *JobValidateCommand) validateLocal(aj *api.Job) (*api.JobValidateResponse, error) { 118 var out api.JobValidateResponse 119 120 job := agent.ApiJobToStructJob(aj) 121 canonicalizeWarnings := job.Canonicalize() 122 123 if vErr := job.Validate(); vErr != nil { 124 if merr, ok := vErr.(*multierror.Error); ok { 125 for _, err := range merr.Errors { 126 out.ValidationErrors = append(out.ValidationErrors, err.Error()) 127 } 128 out.Error = merr.Error() 129 } else { 130 out.ValidationErrors = append(out.ValidationErrors, vErr.Error()) 131 out.Error = vErr.Error() 132 } 133 } 134 135 warnings := job.Warnings() 136 out.Warnings = structs.MergeMultierrorWarnings(warnings, canonicalizeWarnings) 137 return &out, nil 138 }