Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a -hcl2-strict flag to allow for lenient hcl variable parsing. #11284

Merged
merged 5 commits into from
Nov 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/11284.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
cli: added `hcl2-strict` flag to control HCL2 parsing errors where variable passed without root
```
5 changes: 3 additions & 2 deletions command/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,10 @@ type JobGetter struct {

// ApiJob returns the Job struct from jobfile.
func (j *JobGetter) ApiJob(jpath string) (*api.Job, error) {
return j.ApiJobWithArgs(jpath, nil, nil)
return j.ApiJobWithArgs(jpath, nil, nil, true)
}

func (j *JobGetter) ApiJobWithArgs(jpath string, vars []string, varfiles []string) (*api.Job, error) {
func (j *JobGetter) ApiJobWithArgs(jpath string, vars []string, varfiles []string, strict bool) (*api.Job, error) {
var jobfile io.Reader
pathName := filepath.Base(jpath)
switch jpath {
Expand Down Expand Up @@ -459,6 +459,7 @@ func (j *JobGetter) ApiJobWithArgs(jpath string, vars []string, varfiles []strin
AllowFS: true,
VarFiles: varfiles,
Envs: os.Environ(),
Strict: strict,
})

if err != nil {
Expand Down
53 changes: 52 additions & 1 deletion command/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,58 @@ job "example" {
_, err = vf.WriteString(fileVars + "\n")
require.NoError(t, err)

j, err := (&JobGetter{}).ApiJobWithArgs(hclf.Name(), cliArgs, []string{vf.Name()})
j, err := (&JobGetter{}).ApiJobWithArgs(hclf.Name(), cliArgs, []string{vf.Name()}, true)
require.NoError(t, err)

require.NotNil(t, j)
require.Equal(t, expected, j.Datacenters)
}

func TestJobGetter_HCL2_Variables_StrictFalse(t *testing.T) {
t.Parallel()

hcl := `
variables {
var1 = "default-val"
var2 = "default-val"
var3 = "default-val"
var4 = "default-val"
}

job "example" {
datacenters = ["${var.var1}", "${var.var2}", "${var.var3}", "${var.var4}"]
}
`

os.Setenv("NOMAD_VAR_var4", "from-envvar")
defer os.Unsetenv("NOMAD_VAR_var4")

// Both the CLI and var file contain variables that are not used with the
// template and therefore would error, if hcl2-strict was true.
cliArgs := []string{`var2=from-cli`,`unsedVar1=from-cli`}
fileVars := `
var3 = "from-varfile"
unsedVar2 = "from-varfile"
`
expected := []string{"default-val", "from-cli", "from-varfile", "from-envvar"}

hclf, err := ioutil.TempFile("", "hcl")
require.NoError(t, err)
defer os.Remove(hclf.Name())
defer hclf.Close()

_, err = hclf.WriteString(hcl)
require.NoError(t, err)

vf, err := ioutil.TempFile("", "var.hcl")
require.NoError(t, err)
defer os.Remove(vf.Name())
defer vf.Close()

_, err = vf.WriteString(fileVars + "\n")
require.NoError(t, err)

j, err := (&JobGetter{}).ApiJobWithArgs(hclf.Name(), cliArgs, []string{vf.Name()}, false)
require.NoError(t, err)

require.NotNil(t, j)
Expand Down
11 changes: 9 additions & 2 deletions command/job_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ Plan Options:
-hcl1
Parses the job file as HCLv1.

-hcl2-strict
Whether an error should be produced from the HCL2 parser where a variable
has been supplied which is not defined within the root variables. Defaults
to true.

-policy-override
Sets the flag to force override any soft mandatory Sentinel policies.

Expand All @@ -105,6 +110,7 @@ func (c *JobPlanCommand) AutocompleteFlags() complete.Flags {
"-policy-override": complete.PredictNothing,
"-verbose": complete.PredictNothing,
"-hcl1": complete.PredictNothing,
"-hcl2-strict": complete.PredictNothing,
"-var": complete.PredictAnything,
"-var-file": complete.PredictFiles("*.var"),
})
Expand All @@ -116,7 +122,7 @@ func (c *JobPlanCommand) AutocompleteArgs() complete.Predictor {

func (c *JobPlanCommand) Name() string { return "job plan" }
func (c *JobPlanCommand) Run(args []string) int {
var diff, policyOverride, verbose bool
var diff, policyOverride, verbose, hcl2Strict bool
var varArgs, varFiles flaghelper.StringFlag

flagSet := c.Meta.FlagSet(c.Name(), FlagSetClient)
Expand All @@ -125,6 +131,7 @@ func (c *JobPlanCommand) Run(args []string) int {
flagSet.BoolVar(&policyOverride, "policy-override", false, "")
flagSet.BoolVar(&verbose, "verbose", false, "")
flagSet.BoolVar(&c.JobGetter.hcl1, "hcl1", false, "")
flagSet.BoolVar(&hcl2Strict, "hcl2-strict", true, "")
flagSet.Var(&varArgs, "var", "")
flagSet.Var(&varFiles, "var-file", "")

Expand All @@ -142,7 +149,7 @@ func (c *JobPlanCommand) Run(args []string) int {

path := args[0]
// Get Job struct from Jobfile
job, err := c.JobGetter.ApiJobWithArgs(args[0], varArgs, varFiles)
job, err := c.JobGetter.ApiJobWithArgs(args[0], varArgs, varFiles, hcl2Strict)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err))
return 255
Expand Down
11 changes: 9 additions & 2 deletions command/job_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ Run Options:
-hcl1
Parses the job file as HCLv1.

-hcl2-strict
Whether an error should be produced from the HCL2 parser where a variable
has been supplied which is not defined within the root variables. Defaults
to true.

-output
Output the JSON that would be submitted to the HTTP API without submitting
the job.
Expand Down Expand Up @@ -144,6 +149,7 @@ func (c *JobRunCommand) AutocompleteFlags() complete.Flags {
"-policy-override": complete.PredictNothing,
"-preserve-counts": complete.PredictNothing,
"-hcl1": complete.PredictNothing,
"-hcl2-strict": complete.PredictNothing,
"-var": complete.PredictAnything,
"-var-file": complete.PredictFiles("*.var"),
})
Expand All @@ -156,7 +162,7 @@ func (c *JobRunCommand) AutocompleteArgs() complete.Predictor {
func (c *JobRunCommand) Name() string { return "job run" }

func (c *JobRunCommand) Run(args []string) int {
var detach, verbose, output, override, preserveCounts bool
var detach, verbose, output, override, preserveCounts, hcl2Strict bool
var checkIndexStr, consulToken, consulNamespace, vaultToken, vaultNamespace string
var varArgs, varFiles flaghelper.StringFlag

Expand All @@ -168,6 +174,7 @@ func (c *JobRunCommand) Run(args []string) int {
flagSet.BoolVar(&override, "policy-override", false, "")
flagSet.BoolVar(&preserveCounts, "preserve-counts", false, "")
flagSet.BoolVar(&c.JobGetter.hcl1, "hcl1", false, "")
flagSet.BoolVar(&hcl2Strict, "hcl2-strict", true, "")
flagSet.StringVar(&checkIndexStr, "check-index", "", "")
flagSet.StringVar(&consulToken, "consul-token", "", "")
flagSet.StringVar(&consulNamespace, "consul-namespace", "", "")
Expand Down Expand Up @@ -195,7 +202,7 @@ func (c *JobRunCommand) Run(args []string) int {
}

// Get Job struct from Jobfile
job, err := c.JobGetter.ApiJobWithArgs(args[0], varArgs, varFiles)
job, err := c.JobGetter.ApiJobWithArgs(args[0], varArgs, varFiles, hcl2Strict)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err))
return 1
Expand Down
17 changes: 12 additions & 5 deletions command/job_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,16 @@ Validate Options:
-hcl1
Parses the job file as HCLv1.

-hcl2-strict
Whether an error should be produced from the HCL2 parser where a variable
has been supplied which is not defined within the root variables. Defaults
to true.

-var 'key=value'
Variable for template, can be used multiple times.

-var-file=path
Path to HCL2 file containing user variables.

`
return strings.TrimSpace(helpText)
}
Expand All @@ -53,9 +57,10 @@ func (c *JobValidateCommand) Synopsis() string {

func (c *JobValidateCommand) AutocompleteFlags() complete.Flags {
return complete.Flags{
"-hcl1": complete.PredictNothing,
"-var": complete.PredictAnything,
"-var-file": complete.PredictFiles("*.var"),
"-hcl1": complete.PredictNothing,
"-hcl2-strict": complete.PredictNothing,
"-var": complete.PredictAnything,
"-var-file": complete.PredictFiles("*.var"),
}
}

Expand All @@ -67,10 +72,12 @@ func (c *JobValidateCommand) Name() string { return "job validate" }

func (c *JobValidateCommand) Run(args []string) int {
var varArgs, varFiles flaghelper.StringFlag
var hcl2Strict bool

flagSet := c.Meta.FlagSet(c.Name(), FlagSetNone)
flagSet.Usage = func() { c.Ui.Output(c.Help()) }
flagSet.BoolVar(&c.JobGetter.hcl1, "hcl1", false, "")
flagSet.BoolVar(&hcl2Strict, "hcl2-strict", true, "")
flagSet.Var(&varArgs, "var", "")
flagSet.Var(&varFiles, "var-file", "")

Expand All @@ -87,7 +94,7 @@ func (c *JobValidateCommand) Run(args []string) int {
}

// Get Job struct from Jobfile
job, err := c.JobGetter.ApiJobWithArgs(args[0], varArgs, varFiles)
job, err := c.JobGetter.ApiJobWithArgs(args[0], varArgs, varFiles, hcl2Strict)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err))
return 1
Expand Down
14 changes: 8 additions & 6 deletions jobspec2/types.variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,12 @@ func (c *jobConfig) collectInputVariableValues(env []string, files []*hcl.File,
})
}

// Define the severity of variable passed that are undefined.
undefSev := hcl.DiagWarning
if c.ParseConfig.Strict {
undefSev = hcl.DiagError
}

// files will contain files found in the folder then files passed as
// arguments.
for _, file := range files {
Expand Down Expand Up @@ -583,12 +589,8 @@ func (c *jobConfig) collectInputVariableValues(env []string, files []*hcl.File,
for name, attr := range attrs {
variable, found := variables[name]
if !found {
sev := hcl.DiagWarning
if c.ParseConfig.Strict {
sev = hcl.DiagError
}
diags = append(diags, &hcl.Diagnostic{
Severity: sev,
Severity: undefSev,
Summary: "Undefined variable",
Detail: fmt.Sprintf("A %q variable was set but was "+
"not found in known variables. To declare "+
Expand Down Expand Up @@ -630,7 +632,7 @@ func (c *jobConfig) collectInputVariableValues(env []string, files []*hcl.File,
variable, found := variables[name]
if !found {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Severity: undefSev,
Summary: "Undefined -var variable",
Detail: fmt.Sprintf("A %q variable was passed in the command "+
"line but was not found in known variables. "+
Expand Down
8 changes: 8 additions & 0 deletions website/content/docs/commands/job/plan.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ capability for the job's namespace.

- `-hcl1`: If set, HCL1 parser is used for parsing the job spec.

- `-hcl2-strict`: Whether an error should be produced from the HCL2 parser where
a variable has been supplied which is not defined within the root variables.
Defaults to true.

- `-var=<key=value>`: Variable for template, can be used multiple times.

- `-var-file=<path>`: Path to HCL2 file containing user variables.

- `-verbose`: Increase diff verbosity.

## Examples
Expand Down
14 changes: 9 additions & 5 deletions website/content/docs/commands/job/run.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ downloaded and read from URL specified. Nomad downloads the job file using

By default, on successful job submission the run command will enter an
interactive monitor and display log information detailing the scheduling
decisions, placement information, and [deployment status] for the provided job
if applicable ([`batch`] and [`system`] jobs don't create deployments). The monitor will
decisions, placement information, and [deployment status] for the provided job
if applicable ([`batch`] and [`system`] jobs don't create deployments). The monitor will
exit after scheduling and deployment have finished or failed.

On successful job submission and scheduling, exit code 0 will be returned. If
Expand Down Expand Up @@ -72,6 +72,10 @@ that volume.

- `-hcl1`: If set, HCL1 parser is used for parsing the job spec.

- `-hcl2-strict`: Whether an error should be produced from the HCL2 parser where
a variable has been supplied which is not defined within the root variables.
Defaults to true.

- `-output`: Output the JSON that would be submitted to the HTTP API without
submitting the job.

Expand Down Expand Up @@ -133,7 +137,7 @@ $ nomad job run job1.nomad

Deployed
Task Group Desired Placed Healthy Unhealthy Progress Deadline
cache 2 2 1 0 2021-06-09T15:32:58-07:00
cache 2 2 1 0 2021-06-09T15:32:58-07:00
web 1 1 1 0 2021-06-09T15:32:58-07:00
```

Expand All @@ -154,7 +158,7 @@ $ nomad job run -check-index 6 example.nomad
==> 2021-06-09T16:57:30-07:00: Evaluation "5ef16dff" finished with status "complete"
==> 2021-06-09T16:57:30-07:00: Monitoring deployment "62eb607c"
✓ Deployment "62eb607c" successful

2021-06-09T16:57:30-07:00
ID = 62eb607c
Job ID = example
Expand Down Expand Up @@ -218,7 +222,7 @@ $ nomad job run example.nomad
```

[`go-getter`]: https://github.com/hashicorp/go-getter
[deployment status]: /docs/commands/deployment#status
[deployment status]: /docs/commands/deployment#status
[`batch`]: /docs/schedulers#batch
[`system`]: /docs/schedulers#system
[`job plan` command]: /docs/commands/job/plan
Expand Down
12 changes: 12 additions & 0 deletions website/content/docs/commands/job/validate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ of 1 indicates an error.
When ACLs are enabled, this command requires a token with the `read-job`
capability for the job's namespace.

## Validate Options

- `-hcl1`: If set, HCL1 parser is used for parsing the job spec.

- `-hcl2-strict`: Whether an error should be produced from the HCL2 parser where
a variable has been supplied which is not defined within the root variables.
Defaults to true.

- `-var=<key=value>`: Variable for template, can be used multiple times.

- `-var-file=<path>`: Path to HCL2 file containing user variables.

## Examples

Validate a job with invalid syntax:
Expand Down