all variables must have a value.

A variable's default value can be set to null to force user to set it.
This commit is contained in:
Adrien Delorme 2020-03-09 17:25:56 +01:00
parent 6d8cce501e
commit 0ccff0d5b9
6 changed files with 34 additions and 18 deletions

View File

@ -126,6 +126,11 @@ func (p *Parser) parse(filename string, vars map[string]string) (*PackerConfig,
diags = append(diags, cfg.collectInputVariableValues(os.Environ(), varFiles, vars)...) diags = append(diags, cfg.collectInputVariableValues(os.Environ(), varFiles, vars)...)
} }
_, moreDiags := cfg.InputVariables.Values()
diags = append(diags, moreDiags...)
_, moreDiags = cfg.LocalVariables.Values()
diags = append(diags, moreDiags...)
// decode the actual content // decode the actual content
for _, file := range files { for _, file := range files {
diags = append(diags, p.decodeConfig(file, cfg)...) diags = append(diags, p.decodeConfig(file, cfg)...)

View File

@ -29,6 +29,7 @@ variable "super_secret_password" {
description = <<IMSENSIBLE description = <<IMSENSIBLE
Handle with care plz Handle with care plz
IMSENSIBLE IMSENSIBLE
default = null
} }
locals { locals {

View File

@ -1,4 +1,5 @@
variable "broken_type" { variable "broken_variable" {
invalid = true invalid = true
default = true
} }

View File

@ -39,11 +39,13 @@ type ValidationOptions struct {
// decoder in order to tell what is the actual value of a var or a local and // decoder in order to tell what is the actual value of a var or a local and
// the list of defined functions. // the list of defined functions.
func (cfg *PackerConfig) EvalContext() *hcl.EvalContext { func (cfg *PackerConfig) EvalContext() *hcl.EvalContext {
inputVariables, _ := cfg.InputVariables.Values()
localVariables, _ := cfg.LocalVariables.Values()
ectx := &hcl.EvalContext{ ectx := &hcl.EvalContext{
Functions: Functions(cfg.Basedir), Functions: Functions(cfg.Basedir),
Variables: map[string]cty.Value{ Variables: map[string]cty.Value{
"var": cty.ObjectVal(cfg.InputVariables.Values()), "var": cty.ObjectVal(inputVariables),
"local": cty.ObjectVal(cfg.LocalVariables.Values()), "local": cty.ObjectVal(localVariables),
}, },
} }
return ectx return ectx

View File

@ -62,7 +62,7 @@ func (v *Variable) Value() (cty.Value, *hcl.Diagnostic) {
v.EnvValue, v.EnvValue,
v.DefaultValue, v.DefaultValue,
} { } {
if !value.IsNull() { if value != cty.NilVal {
return value, nil return value, nil
} }
} }
@ -73,23 +73,26 @@ func (v *Variable) Value() (cty.Value, *hcl.Diagnostic) {
Severity: hcl.DiagError, Severity: hcl.DiagError,
Summary: fmt.Sprintf("Unset variable %q", v.Name), Summary: fmt.Sprintf("Unset variable %q", v.Name),
Detail: "A used variable must be set or have a default value; see " + Detail: "A used variable must be set or have a default value; see " +
"https://packer.io/docs/configuration/from-1.5/syntax.html for details.", "https://packer.io/docs/configuration/from-1.5/syntax.html for " +
"details.",
Context: v.Range.Ptr(), Context: v.Range.Ptr(),
} }
} }
type Variables map[string]*Variable type Variables map[string]*Variable
func (variables Variables) Values() map[string]cty.Value { func (variables Variables) Values() (map[string]cty.Value, hcl.Diagnostics) {
res := map[string]cty.Value{} res := map[string]cty.Value{}
var diags hcl.Diagnostics
for k, v := range variables { for k, v := range variables {
res[k], _ = v.Value() value, diag := v.Value()
// here the value might not be used and in that case we don't want to if diag != nil {
// force users to set it. So this error is ignored. we still set it as diags = append(diags, diag)
// it can be a `cty.NullVal(cty.DynamicPseudoType)`, which is the go continue
// cty value for 'unknown value' and should error when used. }
res[k] = value
} }
return res return res, diags
} }
// decodeVariable decodes a variable key and value into Variables // decodeVariable decodes a variable key and value into Variables

View File

@ -52,9 +52,10 @@ func TestParse_variables(t *testing.T) {
Description: fmt.Sprintln("Describing is awesome ;D"), Description: fmt.Sprintln("Describing is awesome ;D"),
}, },
"super_secret_password": &Variable{ "super_secret_password": &Variable{
Name: "super_secret_password", Name: "super_secret_password",
Sensitive: true, Sensitive: true,
Description: fmt.Sprintln("Handle with care plz"), DefaultValue: cty.NullVal(cty.String),
Description: fmt.Sprintln("Handle with care plz"),
}, },
}, },
LocalVariables: Variables{ LocalVariables: Variables{
@ -117,14 +118,16 @@ func TestParse_variables(t *testing.T) {
[]packer.Build{}, []packer.Build{},
false, false,
}, },
{"unknown key", {"unknown key",
defaultParser, defaultParser,
parseTestArgs{"testdata/variables/unknown_key.pkr.hcl", nil}, parseTestArgs{"testdata/variables/unknown_key.pkr.hcl", nil},
&PackerConfig{ &PackerConfig{
Basedir: filepath.Join("testdata", "variables"), Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{ InputVariables: Variables{
"broken_type": &Variable{ "broken_variable": &Variable{
Name: "broken_type", Name: "broken_variable",
DefaultValue: cty.BoolVal(true),
}, },
}, },
}, },
@ -132,6 +135,7 @@ func TestParse_variables(t *testing.T) {
[]packer.Build{}, []packer.Build{},
false, false,
}, },
{"unset used variable", {"unset used variable",
defaultParser, defaultParser,
parseTestArgs{"testdata/variables/unset_used_string_variable.pkr.hcl", nil}, parseTestArgs{"testdata/variables/unset_used_string_variable.pkr.hcl", nil},
@ -170,7 +174,7 @@ func TestParse_variables(t *testing.T) {
}, },
}, },
}, },
false, false, true, true,
[]packer.Build{ []packer.Build{
&packer.CoreBuild{ &packer.CoreBuild{
Type: "null", Type: "null",