github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/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  	flaghelper "github.com/hashicorp/nomad/helper/flags"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/posener/complete"
    13  )
    14  
    15  type JobValidateCommand struct {
    16  	Meta
    17  	JobGetter
    18  }
    19  
    20  func (c *JobValidateCommand) Help() string {
    21  	helpText := `
    22  Usage: nomad job validate [options] <path>
    23  Alias: nomad validate
    24  
    25    Checks if a given HCL job file has a valid specification. This can be used to
    26    check for any syntax errors or validation problems with a job.
    27  
    28    If the supplied path is "-", the jobfile is read from stdin. Otherwise
    29    it is read from the file at the supplied path or downloaded and
    30    read from URL specified.
    31  
    32    When ACLs are enabled, this command requires a token with the 'read-job'
    33    capability for the job's namespace.
    34  
    35  Validate Options:
    36  
    37    -hcl1
    38      Parses the job file as HCLv1.
    39  
    40    -var 'key=value'
    41      Variable for template, can be used multiple times.
    42  
    43    -var-file=path
    44      Path to HCL2 file containing user variables.
    45  
    46  `
    47  	return strings.TrimSpace(helpText)
    48  }
    49  
    50  func (c *JobValidateCommand) Synopsis() string {
    51  	return "Checks if a given job specification is valid"
    52  }
    53  
    54  func (c *JobValidateCommand) AutocompleteFlags() complete.Flags {
    55  	return complete.Flags{
    56  		"-hcl1":     complete.PredictNothing,
    57  		"-var":      complete.PredictAnything,
    58  		"-var-file": complete.PredictFiles("*.var"),
    59  	}
    60  }
    61  
    62  func (c *JobValidateCommand) AutocompleteArgs() complete.Predictor {
    63  	return complete.PredictOr(complete.PredictFiles("*.nomad"), complete.PredictFiles("*.hcl"))
    64  }
    65  
    66  func (c *JobValidateCommand) Name() string { return "job validate" }
    67  
    68  func (c *JobValidateCommand) Run(args []string) int {
    69  	var varArgs, varFiles flaghelper.StringFlag
    70  
    71  	flagSet := c.Meta.FlagSet(c.Name(), FlagSetNone)
    72  	flagSet.Usage = func() { c.Ui.Output(c.Help()) }
    73  	flagSet.BoolVar(&c.JobGetter.hcl1, "hcl1", false, "")
    74  	flagSet.Var(&varArgs, "var", "")
    75  	flagSet.Var(&varFiles, "var-file", "")
    76  
    77  	if err := flagSet.Parse(args); err != nil {
    78  		return 1
    79  	}
    80  
    81  	// Check that we got exactly one node
    82  	args = flagSet.Args()
    83  	if len(args) != 1 {
    84  		c.Ui.Error("This command takes one argument: <path>")
    85  		c.Ui.Error(commandErrorText(c))
    86  		return 1
    87  	}
    88  
    89  	// Get Job struct from Jobfile
    90  	job, err := c.JobGetter.ApiJobWithArgs(args[0], varArgs, varFiles)
    91  	if err != nil {
    92  		c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err))
    93  		return 1
    94  	}
    95  
    96  	// Get the HTTP client
    97  	client, err := c.Meta.Client()
    98  	if err != nil {
    99  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   100  		return 255
   101  	}
   102  
   103  	// Force the region to be that of the job.
   104  	if r := job.Region; r != nil {
   105  		client.SetRegion(*r)
   106  	}
   107  
   108  	// Check that the job is valid
   109  	jr, _, err := client.Jobs().Validate(job, nil)
   110  	if err != nil {
   111  		jr, err = c.validateLocal(job)
   112  	}
   113  	if err != nil {
   114  		c.Ui.Error(fmt.Sprintf("Error validating job: %s", err))
   115  		return 1
   116  	}
   117  
   118  	if jr != nil && !jr.DriverConfigValidated {
   119  		c.Ui.Output(
   120  			c.Colorize().Color("[bold][yellow]Driver configuration not validated since connection to Nomad agent couldn't be established.[reset]\n"))
   121  	}
   122  
   123  	if jr != nil && jr.Error != "" {
   124  		c.Ui.Error(
   125  			c.Colorize().Color("[bold][red]Job validation errors:[reset]"))
   126  		c.Ui.Error(jr.Error)
   127  		return 1
   128  	}
   129  
   130  	// Print any warnings if there are any
   131  	if jr.Warnings != "" {
   132  		c.Ui.Output(
   133  			c.Colorize().Color(fmt.Sprintf("[bold][yellow]Job Warnings:\n%s[reset]\n", jr.Warnings)))
   134  	}
   135  
   136  	// Done!
   137  	c.Ui.Output(
   138  		c.Colorize().Color("[bold][green]Job validation successful[reset]"))
   139  	return 0
   140  }
   141  
   142  // validateLocal validates without talking to a Nomad agent
   143  func (c *JobValidateCommand) validateLocal(aj *api.Job) (*api.JobValidateResponse, error) {
   144  	var out api.JobValidateResponse
   145  
   146  	job := agent.ApiJobToStructJob(aj)
   147  	job.Canonicalize()
   148  
   149  	if vErr := job.Validate(); vErr != nil {
   150  		if merr, ok := vErr.(*multierror.Error); ok {
   151  			for _, err := range merr.Errors {
   152  				out.ValidationErrors = append(out.ValidationErrors, err.Error())
   153  			}
   154  			out.Error = merr.Error()
   155  		} else {
   156  			out.ValidationErrors = append(out.ValidationErrors, vErr.Error())
   157  			out.Error = vErr.Error()
   158  		}
   159  	}
   160  
   161  	out.Warnings = structs.MergeMultierrorWarnings(job.Warnings())
   162  	return &out, nil
   163  }