add VariableAssignment struct that help describe an input var assignment
This commit is contained in:
parent
8de2f40a07
commit
6911495fc4
|
@ -69,25 +69,7 @@ func testParse(t *testing.T, tests []parseTest) {
|
|||
if tt.parseWantDiagHasErrors != gotDiags.HasErrors() {
|
||||
t.Fatalf("Parser.parse() unexpected diagnostics HasErrors. %s", gotDiags)
|
||||
}
|
||||
if diff := cmp.Diff(tt.parseWantCfg, gotCfg,
|
||||
cmpopts.IgnoreUnexported(
|
||||
PackerConfig{},
|
||||
cty.Value{},
|
||||
cty.Type{},
|
||||
Variable{},
|
||||
SourceBlock{},
|
||||
ProvisionerBlock{},
|
||||
PostProcessorBlock{},
|
||||
),
|
||||
cmpopts.IgnoreFields(PackerConfig{},
|
||||
"Cwd", // Cwd will change for every computer
|
||||
),
|
||||
cmpopts.IgnoreTypes(HCL2Ref{}),
|
||||
cmpopts.IgnoreTypes([]*LocalBlock{}),
|
||||
cmpopts.IgnoreTypes([]hcl.Range{}),
|
||||
cmpopts.IgnoreTypes(hcl.Range{}),
|
||||
cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}),
|
||||
cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}),
|
||||
if diff := cmp.Diff(tt.parseWantCfg, gotCfg, cmpOpts...,
|
||||
); diff != "" {
|
||||
t.Fatalf("Parser.parse() wrong packer config. %s", diff)
|
||||
}
|
||||
|
@ -96,11 +78,8 @@ func testParse(t *testing.T, tests []parseTest) {
|
|||
gotInputVar := gotCfg.InputVariables
|
||||
for name, value := range tt.parseWantCfg.InputVariables {
|
||||
if variable, ok := gotInputVar[name]; ok {
|
||||
if diff := cmp.Diff(variable.DefaultValue.GoString(), value.DefaultValue.GoString()); diff != "" {
|
||||
t.Fatalf("Parser.parse(): unexpected default value for %s: %s", name, diff)
|
||||
}
|
||||
if diff := cmp.Diff(variable.VarfileValue.GoString(), value.VarfileValue.GoString()); diff != "" {
|
||||
t.Fatalf("Parser.parse(): varfile value differs for %s: %s", name, diff)
|
||||
if diff := cmp.Diff(variable, value, cmpOpts...); diff != "" {
|
||||
t.Fatalf("Parser.parse(): unexpected variable values %s: %s", name, diff)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Parser.parse() missing input variable. %s", name)
|
||||
|
@ -110,8 +89,8 @@ func testParse(t *testing.T, tests []parseTest) {
|
|||
gotLocalVar := gotCfg.LocalVariables
|
||||
for name, value := range tt.parseWantCfg.LocalVariables {
|
||||
if variable, ok := gotLocalVar[name]; ok {
|
||||
if variable.DefaultValue.GoString() != value.DefaultValue.GoString() {
|
||||
t.Fatalf("Parser.parse() local variable %s expected '%s' but was '%s'", name, value.DefaultValue.GoString(), variable.DefaultValue.GoString())
|
||||
if diff := cmp.Diff(variable, value, cmpOpts...); diff != "" {
|
||||
t.Fatalf("Parser.parse(): unexpected variable values %s: %s", name, diff)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Parser.parse() missing local variable. %s", name)
|
||||
|
@ -127,18 +106,7 @@ func testParse(t *testing.T, tests []parseTest) {
|
|||
if tt.getBuildsWantDiags == (gotDiags == nil) {
|
||||
t.Fatalf("Parser.getBuilds() unexpected diagnostics. %s", gotDiags)
|
||||
}
|
||||
if diff := cmp.Diff(tt.getBuildsWantBuilds, gotBuilds,
|
||||
cmpopts.IgnoreUnexported(
|
||||
cty.Value{},
|
||||
cty.Type{},
|
||||
packer.CoreBuild{},
|
||||
packer.CoreBuildProvisioner{},
|
||||
packer.CoreBuildPostProcessor{},
|
||||
null.Builder{},
|
||||
HCL2Provisioner{},
|
||||
HCL2PostProcessor{},
|
||||
),
|
||||
); diff != "" {
|
||||
if diff := cmp.Diff(tt.getBuildsWantBuilds, gotBuilds, cmpOpts...); diff != "" {
|
||||
t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff)
|
||||
}
|
||||
})
|
||||
|
@ -250,3 +218,34 @@ var (
|
|||
},
|
||||
}
|
||||
)
|
||||
|
||||
var cmpOpts = []cmp.Option{
|
||||
cmpopts.IgnoreUnexported(
|
||||
PackerConfig{},
|
||||
cty.Value{},
|
||||
cty.Type{},
|
||||
Variable{},
|
||||
SourceBlock{},
|
||||
ProvisionerBlock{},
|
||||
PostProcessorBlock{},
|
||||
packer.CoreBuild{},
|
||||
HCL2Provisioner{},
|
||||
HCL2PostProcessor{},
|
||||
packer.CoreBuildPostProcessor{},
|
||||
packer.CoreBuildProvisioner{},
|
||||
packer.CoreBuildPostProcessor{},
|
||||
null.Builder{},
|
||||
),
|
||||
cmpopts.IgnoreFields(PackerConfig{},
|
||||
"Cwd", // Cwd will change for every os type
|
||||
),
|
||||
cmpopts.IgnoreFields(VariableAssignment{},
|
||||
"Expr", // its an interface
|
||||
),
|
||||
cmpopts.IgnoreTypes(HCL2Ref{}),
|
||||
cmpopts.IgnoreTypes([]*LocalBlock{}),
|
||||
cmpopts.IgnoreTypes([]hcl.Range{}),
|
||||
cmpopts.IgnoreTypes(hcl.Range{}),
|
||||
cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}),
|
||||
cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
variable "image_metadata" {
|
||||
default = {
|
||||
key: "value",
|
||||
something: {
|
||||
foo: "bar",
|
||||
}
|
||||
}
|
||||
validation {
|
||||
condition = length(var.image_metadata.key) > 4
|
||||
error_message = "The image_metadata.key field must be more than 4 runes."
|
||||
}
|
||||
validation {
|
||||
condition = substr(var.image_metadata.something.foo, 0, 3) == "bar"
|
||||
error_message = "The image_metadata.something.foo field must start with \"bar\"."
|
||||
}
|
||||
}
|
7
hcl2template/testdata/variables/validation/valid_map/invalid_value.auto.pkrvars.hcl
vendored
Normal file
7
hcl2template/testdata/variables/validation/valid_map/invalid_value.auto.pkrvars.hcl
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
image_metadata = {
|
||||
key: "value",
|
||||
something: {
|
||||
foo: "woo",
|
||||
}
|
||||
}
|
|
@ -210,9 +210,13 @@ func (c *PackerConfig) evaluateLocalVariable(local *LocalBlock) hcl.Diagnostics
|
|||
return diags
|
||||
}
|
||||
c.LocalVariables[local.Name] = &Variable{
|
||||
Name: local.Name,
|
||||
DefaultValue: value,
|
||||
Type: value.Type(),
|
||||
Name: local.Name,
|
||||
Values: []VariableAssignment{{
|
||||
Value: value,
|
||||
Expr: local.Expr,
|
||||
From: "default",
|
||||
}},
|
||||
Type: value.Type(),
|
||||
}
|
||||
|
||||
return diags
|
||||
|
|
|
@ -25,51 +25,58 @@ func TestParser_complete(t *testing.T) {
|
|||
Basedir: "testdata/complete",
|
||||
InputVariables: Variables{
|
||||
"foo": &Variable{
|
||||
Name: "foo",
|
||||
DefaultValue: cty.StringVal("value"),
|
||||
Name: "foo",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("value")}},
|
||||
},
|
||||
"image_id": &Variable{
|
||||
Name: "image_id",
|
||||
DefaultValue: cty.StringVal("image-id-default"),
|
||||
Name: "image_id",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("image-id-default")}},
|
||||
},
|
||||
"port": &Variable{
|
||||
Name: "port",
|
||||
DefaultValue: cty.NumberIntVal(42),
|
||||
Name: "port",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.NumberIntVal(42)}},
|
||||
},
|
||||
"availability_zone_names": &Variable{
|
||||
Name: "availability_zone_names",
|
||||
DefaultValue: cty.ListVal([]cty.Value{
|
||||
cty.StringVal("A"),
|
||||
cty.StringVal("B"),
|
||||
cty.StringVal("C"),
|
||||
}),
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.ListVal([]cty.Value{
|
||||
cty.StringVal("A"),
|
||||
cty.StringVal("B"),
|
||||
cty.StringVal("C"),
|
||||
}),
|
||||
}},
|
||||
},
|
||||
},
|
||||
LocalVariables: Variables{
|
||||
"feefoo": &Variable{
|
||||
Name: "feefoo",
|
||||
DefaultValue: cty.StringVal("value_image-id-default"),
|
||||
Name: "feefoo",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("value_image-id-default")}},
|
||||
},
|
||||
"standard_tags": &Variable{
|
||||
Name: "standard_tags",
|
||||
DefaultValue: cty.ObjectVal(map[string]cty.Value{
|
||||
"Component": cty.StringVal("user-service"),
|
||||
"Environment": cty.StringVal("production"),
|
||||
}),
|
||||
Values: []VariableAssignment{{From: "default",
|
||||
Value: cty.ObjectVal(map[string]cty.Value{
|
||||
"Component": cty.StringVal("user-service"),
|
||||
"Environment": cty.StringVal("production"),
|
||||
}),
|
||||
}},
|
||||
},
|
||||
"abc_map": &Variable{
|
||||
Name: "abc_map",
|
||||
DefaultValue: cty.TupleVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("a"),
|
||||
Values: []VariableAssignment{{From: "default",
|
||||
Value: cty.TupleVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("a"),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("b"),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("c"),
|
||||
}),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("b"),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("c"),
|
||||
}),
|
||||
}),
|
||||
}},
|
||||
},
|
||||
},
|
||||
Sources: map[SourceRef]SourceBlock{
|
||||
|
|
|
@ -26,15 +26,26 @@ type LocalBlock struct {
|
|||
Expr hcl.Expression
|
||||
}
|
||||
|
||||
// VariableAssignment represents a way a variable was set: the expression
|
||||
// setting it and the value of that expression. It helps pinpoint were
|
||||
// something was set in diagnostics.
|
||||
type VariableAssignment struct {
|
||||
// From tells were it was taken from, command/varfile/env/default
|
||||
From string
|
||||
Value cty.Value
|
||||
Expr hcl.Expression
|
||||
}
|
||||
|
||||
type Variable struct {
|
||||
// CmdValue, VarfileValue, EnvValue, DefaultValue are possible values of
|
||||
// the variable; The first value set from these will be the one used. If
|
||||
// none is set; an error will be returned if a user tries to use the
|
||||
// Variable.
|
||||
CmdValue cty.Value
|
||||
VarfileValue cty.Value
|
||||
EnvValue cty.Value
|
||||
DefaultValue cty.Value
|
||||
// Values contains possible values for the variable; The last value set
|
||||
// from these will be the one used. If none is set; an error will be
|
||||
// returned by Value().
|
||||
Values []VariableAssignment
|
||||
|
||||
// Validations contains all variables validation rules to be applied to the
|
||||
// used value. Only the used value - the last value from Values - is
|
||||
// validated.
|
||||
Validations []*VariableValidation
|
||||
|
||||
// Cty Type of the variable. If the default value or a collected value is
|
||||
// not of this type nor can be converted to this type an error diagnostic
|
||||
|
@ -53,25 +64,23 @@ type Variable struct {
|
|||
// the variable from the output stream. By replacing the text.
|
||||
Sensitive bool
|
||||
|
||||
Validations []*VariableValidation
|
||||
|
||||
Range hcl.Range
|
||||
}
|
||||
|
||||
func (v *Variable) GoString() string {
|
||||
return fmt.Sprintf("{Type:%s,CmdValue:%s,VarfileValue:%s,EnvValue:%s,DefaultValue:%s}",
|
||||
v.Type.GoString(),
|
||||
PrintableCtyValue(v.CmdValue),
|
||||
PrintableCtyValue(v.VarfileValue),
|
||||
PrintableCtyValue(v.EnvValue),
|
||||
PrintableCtyValue(v.DefaultValue),
|
||||
)
|
||||
b := &strings.Builder{}
|
||||
fmt.Fprintf(b, "{type:%s", v.Type.GoString())
|
||||
for _, vv := range v.Values {
|
||||
fmt.Fprintf(b, ",%s:%s", vv.From, vv.Value)
|
||||
}
|
||||
fmt.Fprintf(b, "}")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// validateValue ensures that all of the configured custom validations for a
|
||||
// variable value are passing.
|
||||
//
|
||||
func (v *Variable) validateValue(val cty.Value) (diags hcl.Diagnostics) {
|
||||
func (v *Variable) validateValue(val VariableAssignment) (diags hcl.Diagnostics) {
|
||||
if len(v.Validations) == 0 {
|
||||
log.Printf("[TRACE] validateValue: not active for %s, so skipping", v.Name)
|
||||
return nil
|
||||
|
@ -80,7 +89,7 @@ func (v *Variable) validateValue(val cty.Value) (diags hcl.Diagnostics) {
|
|||
hclCtx := &hcl.EvalContext{
|
||||
Variables: map[string]cty.Value{
|
||||
"var": cty.ObjectVal(map[string]cty.Value{
|
||||
v.Name: val,
|
||||
v.Name: val.Value,
|
||||
}),
|
||||
},
|
||||
Functions: Functions(""),
|
||||
|
@ -88,7 +97,6 @@ func (v *Variable) validateValue(val cty.Value) (diags hcl.Diagnostics) {
|
|||
|
||||
for _, validation := range v.Validations {
|
||||
const errInvalidCondition = "Invalid variable validation result"
|
||||
const errInvalidValue = "Invalid value for variable"
|
||||
|
||||
result, moreDiags := validation.Condition.Value(hclCtx)
|
||||
diags = append(diags, moreDiags...)
|
||||
|
@ -125,11 +133,15 @@ func (v *Variable) validateValue(val cty.Value) (diags hcl.Diagnostics) {
|
|||
}
|
||||
|
||||
if result.False() {
|
||||
subj := validation.DeclRange.Ptr()
|
||||
if val.Expr != nil {
|
||||
subj = val.Expr.Range().Ptr()
|
||||
}
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: errInvalidValue,
|
||||
Summary: fmt.Sprintf("Invalid value for %s variable", val.From),
|
||||
Detail: fmt.Sprintf("%s\n\nThis was checked by the validation rule at %s.", validation.ErrorMessage, validation.DeclRange.String()),
|
||||
Subject: validation.DeclRange.Ptr(),
|
||||
Subject: subj,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -137,26 +149,20 @@ func (v *Variable) validateValue(val cty.Value) (diags hcl.Diagnostics) {
|
|||
return diags
|
||||
}
|
||||
|
||||
// Value returns the last found value from the list of variable settings.
|
||||
func (v *Variable) Value() (cty.Value, hcl.Diagnostics) {
|
||||
for _, value := range []cty.Value{
|
||||
v.CmdValue,
|
||||
v.VarfileValue,
|
||||
v.EnvValue,
|
||||
v.DefaultValue,
|
||||
} {
|
||||
if value != cty.NilVal {
|
||||
return value, v.validateValue(value)
|
||||
}
|
||||
if len(v.Values) == 0 {
|
||||
return cty.UnknownVal(v.Type), hcl.Diagnostics{&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Unset variable %q", v.Name),
|
||||
Detail: "A used variable must be set or have a default value; see " +
|
||||
"https://packer.io/docs/configuration/from-1.5/syntax for " +
|
||||
"details.",
|
||||
Context: v.Range.Ptr(),
|
||||
}}
|
||||
}
|
||||
|
||||
return cty.UnknownVal(v.Type), hcl.Diagnostics{&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Unset variable %q", v.Name),
|
||||
Detail: "A used variable must be set or have a default value; see " +
|
||||
"https://packer.io/docs/configuration/from-1.5/syntax for " +
|
||||
"details.",
|
||||
Context: v.Range.Ptr(),
|
||||
}}
|
||||
val := v.Values[len(v.Values)-1]
|
||||
return val.Value, v.validateValue(v.Values[len(v.Values)-1])
|
||||
}
|
||||
|
||||
type Variables map[string]*Variable
|
||||
|
@ -205,10 +211,14 @@ func (variables *Variables) decodeVariable(key string, attr *hcl.Attribute, ectx
|
|||
}
|
||||
|
||||
(*variables)[key] = &Variable{
|
||||
Name: key,
|
||||
DefaultValue: value,
|
||||
Type: value.Type(),
|
||||
Range: attr.Range,
|
||||
Name: key,
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: value,
|
||||
Expr: attr.Expr,
|
||||
}},
|
||||
Type: value.Type(),
|
||||
Range: attr.Range,
|
||||
}
|
||||
|
||||
return diags
|
||||
|
@ -310,12 +320,16 @@ func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.Eval
|
|||
}
|
||||
}
|
||||
|
||||
v.DefaultValue = defaultValue
|
||||
v.Values = append(v.Values, VariableAssignment{
|
||||
From: "default",
|
||||
Value: defaultValue,
|
||||
Expr: def.Expr,
|
||||
})
|
||||
|
||||
// It's possible no type attribute was assigned so lets make sure we
|
||||
// have a valid type otherwise there could be issues parsing the value.
|
||||
if v.Type == cty.NilType {
|
||||
v.Type = v.DefaultValue.Type()
|
||||
v.Type = defaultValue.Type()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,7 +546,11 @@ func (cfg *PackerConfig) collectInputVariableValues(env []string, files []*hcl.F
|
|||
val = cty.DynamicVal
|
||||
}
|
||||
}
|
||||
variable.EnvValue = val
|
||||
variable.Values = append(variable.Values, VariableAssignment{
|
||||
From: "env",
|
||||
Value: val,
|
||||
Expr: expr,
|
||||
})
|
||||
}
|
||||
|
||||
// files will contain files found in the folder then files passed as
|
||||
|
@ -616,7 +634,11 @@ func (cfg *PackerConfig) collectInputVariableValues(env []string, files []*hcl.F
|
|||
}
|
||||
}
|
||||
|
||||
variable.VarfileValue = val
|
||||
variable.Values = append(variable.Values, VariableAssignment{
|
||||
From: "varfile",
|
||||
Value: val,
|
||||
Expr: attr.Expr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -660,7 +682,11 @@ func (cfg *PackerConfig) collectInputVariableValues(env []string, files []*hcl.F
|
|||
}
|
||||
}
|
||||
|
||||
variable.CmdValue = val
|
||||
variable.Values = append(variable.Values, VariableAssignment{
|
||||
From: "cmd",
|
||||
Value: val,
|
||||
Expr: expr,
|
||||
})
|
||||
}
|
||||
|
||||
return diags
|
||||
|
|
|
@ -25,47 +25,59 @@ func TestParse_variables(t *testing.T) {
|
|||
Basedir: filepath.Join("testdata", "variables"),
|
||||
InputVariables: Variables{
|
||||
"image_name": &Variable{
|
||||
Name: "image_name",
|
||||
DefaultValue: cty.StringVal("foo-image-{{user `my_secret`}}"),
|
||||
Name: "image_name",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo-image-{{user `my_secret`}}")}},
|
||||
},
|
||||
"key": &Variable{
|
||||
Name: "key",
|
||||
DefaultValue: cty.StringVal("value"),
|
||||
Name: "key",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("value")}},
|
||||
},
|
||||
"my_secret": &Variable{
|
||||
Name: "my_secret",
|
||||
DefaultValue: cty.StringVal("foo"),
|
||||
Name: "my_secret",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
|
||||
},
|
||||
"image_id": &Variable{
|
||||
Name: "image_id",
|
||||
DefaultValue: cty.StringVal("image-id-default"),
|
||||
Name: "image_id",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("image-id-default")}},
|
||||
},
|
||||
"port": &Variable{
|
||||
Name: "port",
|
||||
DefaultValue: cty.NumberIntVal(42),
|
||||
Name: "port",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.NumberIntVal(42)}},
|
||||
},
|
||||
"availability_zone_names": &Variable{
|
||||
Name: "availability_zone_names",
|
||||
DefaultValue: cty.ListVal([]cty.Value{
|
||||
cty.StringVal("us-west-1a"),
|
||||
}),
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.ListVal([]cty.Value{
|
||||
cty.StringVal("us-west-1a"),
|
||||
}),
|
||||
}},
|
||||
Description: fmt.Sprintln("Describing is awesome ;D"),
|
||||
},
|
||||
"super_secret_password": &Variable{
|
||||
Name: "super_secret_password",
|
||||
Sensitive: true,
|
||||
DefaultValue: cty.NullVal(cty.String),
|
||||
Description: fmt.Sprintln("Handle with care plz"),
|
||||
Name: "super_secret_password",
|
||||
Sensitive: true,
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.NullVal(cty.String),
|
||||
}},
|
||||
Description: fmt.Sprintln("Handle with care plz"),
|
||||
},
|
||||
},
|
||||
LocalVariables: Variables{
|
||||
"owner": &Variable{
|
||||
Name: "owner",
|
||||
DefaultValue: cty.StringVal("Community Team"),
|
||||
Name: "owner",
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.StringVal("Community Team"),
|
||||
}},
|
||||
},
|
||||
"service_name": &Variable{
|
||||
Name: "service_name",
|
||||
DefaultValue: cty.StringVal("forum"),
|
||||
Name: "service_name",
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.StringVal("forum"),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -81,6 +93,10 @@ func TestParse_variables(t *testing.T) {
|
|||
InputVariables: Variables{
|
||||
"boolean_value": &Variable{
|
||||
Name: "boolean_value",
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.BoolVal(false),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -96,6 +112,10 @@ func TestParse_variables(t *testing.T) {
|
|||
InputVariables: Variables{
|
||||
"boolean_value": &Variable{
|
||||
Name: "boolean_value",
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.BoolVal(false),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -111,6 +131,10 @@ func TestParse_variables(t *testing.T) {
|
|||
InputVariables: Variables{
|
||||
"broken_type": &Variable{
|
||||
Name: "broken_type",
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.UnknownVal(cty.DynamicPseudoType),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -126,8 +150,8 @@ func TestParse_variables(t *testing.T) {
|
|||
Basedir: filepath.Join("testdata", "variables"),
|
||||
InputVariables: Variables{
|
||||
"broken_variable": &Variable{
|
||||
Name: "broken_variable",
|
||||
DefaultValue: cty.BoolVal(true),
|
||||
Name: "broken_variable",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.BoolVal(true)}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -196,34 +220,37 @@ func TestParse_variables(t *testing.T) {
|
|||
Basedir: "testdata/variables/complicated",
|
||||
InputVariables: Variables{
|
||||
"name_prefix": &Variable{
|
||||
Name: "name_prefix",
|
||||
DefaultValue: cty.StringVal("foo"),
|
||||
Name: "name_prefix",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
|
||||
},
|
||||
},
|
||||
LocalVariables: Variables{
|
||||
"name_prefix": &Variable{
|
||||
Name: "name_prefix",
|
||||
DefaultValue: cty.StringVal("foo"),
|
||||
Name: "name_prefix",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
|
||||
},
|
||||
"foo": &Variable{
|
||||
Name: "foo",
|
||||
DefaultValue: cty.StringVal("foo"),
|
||||
Name: "foo",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
|
||||
},
|
||||
"bar": &Variable{
|
||||
Name: "bar",
|
||||
DefaultValue: cty.StringVal("foo"),
|
||||
Name: "bar",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
|
||||
},
|
||||
"for_var": &Variable{
|
||||
Name: "for_var",
|
||||
DefaultValue: cty.StringVal("foo"),
|
||||
Name: "for_var",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
|
||||
},
|
||||
"bar_var": &Variable{
|
||||
Name: "bar_var",
|
||||
DefaultValue: cty.TupleVal([]cty.Value{
|
||||
cty.StringVal("foo"),
|
||||
cty.StringVal("foo"),
|
||||
cty.StringVal("foo"),
|
||||
}),
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.TupleVal([]cty.Value{
|
||||
cty.StringVal("foo"),
|
||||
cty.StringVal("foo"),
|
||||
cty.StringVal("foo"),
|
||||
}),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -250,9 +277,11 @@ func TestParse_variables(t *testing.T) {
|
|||
Basedir: filepath.Join("testdata", "variables"),
|
||||
InputVariables: Variables{
|
||||
"foo": &Variable{
|
||||
DefaultValue: cty.StringVal("bar"),
|
||||
Name: "foo",
|
||||
VarfileValue: cty.StringVal("wee"),
|
||||
Name: "foo",
|
||||
Values: []VariableAssignment{
|
||||
VariableAssignment{"default", cty.StringVal("bar"), nil},
|
||||
VariableAssignment{"varfile", cty.StringVal("wee"), nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -279,14 +308,14 @@ func TestParse_variables(t *testing.T) {
|
|||
Basedir: filepath.Join("testdata", "variables"),
|
||||
InputVariables: Variables{
|
||||
"max_retries": &Variable{
|
||||
Name: "max_retries",
|
||||
DefaultValue: cty.StringVal("1"),
|
||||
Type: cty.String,
|
||||
Name: "max_retries",
|
||||
Values: []VariableAssignment{{"default", cty.StringVal("1"), nil}},
|
||||
Type: cty.String,
|
||||
},
|
||||
"max_retries_int": &Variable{
|
||||
Name: "max_retries_int",
|
||||
DefaultValue: cty.NumberIntVal(1),
|
||||
Type: cty.Number,
|
||||
Name: "max_retries_int",
|
||||
Values: []VariableAssignment{{"default", cty.NumberIntVal(1), nil}},
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Sources: map[SourceRef]SourceBlock{
|
||||
|
@ -365,8 +394,10 @@ func TestParse_variables(t *testing.T) {
|
|||
Basedir: filepath.Join("testdata", "variables", "validation"),
|
||||
InputVariables: Variables{
|
||||
"image_id": &Variable{
|
||||
DefaultValue: cty.StringVal("ami-something-something"),
|
||||
Name: "image_id",
|
||||
Values: []VariableAssignment{
|
||||
{"default", cty.StringVal("ami-something-something"), nil},
|
||||
},
|
||||
Name: "image_id",
|
||||
Validations: []*VariableValidation{
|
||||
&VariableValidation{
|
||||
ErrorMessage: `The image_id value must be a valid AMI id, starting with "ami-".`,
|
||||
|
@ -379,6 +410,28 @@ func TestParse_variables(t *testing.T) {
|
|||
[]packer.Build{},
|
||||
false,
|
||||
},
|
||||
|
||||
{"valid validation block - invalid default",
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/variables/validation/invalid_default.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "variables", "validation"),
|
||||
InputVariables: Variables{
|
||||
"image_id": &Variable{
|
||||
Values: []VariableAssignment{{"default", cty.StringVal("ami-something-something"), nil}},
|
||||
Name: "image_id",
|
||||
Validations: []*VariableValidation{
|
||||
&VariableValidation{
|
||||
ErrorMessage: `The image_id value must be a valid AMI id, starting with "ami-".`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true, true,
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
testParse(t, tests)
|
||||
}
|
||||
|
@ -402,8 +455,10 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
|
||||
{name: "string",
|
||||
variables: Variables{"used_string": &Variable{
|
||||
DefaultValue: cty.StringVal("default_value"),
|
||||
Type: cty.String,
|
||||
Values: []VariableAssignment{
|
||||
{"default", cty.StringVal("default_value"), nil},
|
||||
},
|
||||
Type: cty.String,
|
||||
}},
|
||||
args: args{
|
||||
env: []string{`PKR_VAR_used_string=env_value`},
|
||||
|
@ -420,11 +475,14 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiags: false,
|
||||
wantVariables: Variables{
|
||||
"used_string": &Variable{
|
||||
Type: cty.String,
|
||||
CmdValue: cty.StringVal("cmd_value"),
|
||||
VarfileValue: cty.StringVal("varfile_value"),
|
||||
EnvValue: cty.StringVal("env_value"),
|
||||
DefaultValue: cty.StringVal("default_value"),
|
||||
Type: cty.String,
|
||||
Values: []VariableAssignment{
|
||||
{"default", cty.StringVal(`"default_value"`), nil},
|
||||
{"env", cty.StringVal(`"env_value"`), nil},
|
||||
{"varfile", cty.StringVal(`"xy"`), nil},
|
||||
{"varfile", cty.StringVal(`"varfile_value"`), nil},
|
||||
{"cmd", cty.StringVal(`"cmd_value"`), nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -434,8 +492,10 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
|
||||
{name: "quoted string",
|
||||
variables: Variables{"quoted_string": &Variable{
|
||||
DefaultValue: cty.StringVal(`"default_value"`),
|
||||
Type: cty.String,
|
||||
Values: []VariableAssignment{
|
||||
{"default", cty.StringVal(`"default_value"`), nil},
|
||||
},
|
||||
Type: cty.String,
|
||||
}},
|
||||
args: args{
|
||||
env: []string{`PKR_VAR_quoted_string="env_value"`},
|
||||
|
@ -452,11 +512,14 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiags: false,
|
||||
wantVariables: Variables{
|
||||
"quoted_string": &Variable{
|
||||
Type: cty.String,
|
||||
CmdValue: cty.StringVal(`"cmd_value"`),
|
||||
VarfileValue: cty.StringVal(`"varfile_value"`),
|
||||
EnvValue: cty.StringVal(`"env_value"`),
|
||||
DefaultValue: cty.StringVal(`"default_value"`),
|
||||
Type: cty.String,
|
||||
Values: []VariableAssignment{
|
||||
{"default", cty.StringVal(`"default_value"`), nil},
|
||||
{"env", cty.StringVal(`"env_value"`), nil},
|
||||
{"varfile", cty.StringVal(`"xy"`), nil},
|
||||
{"varfile", cty.StringVal(`"varfile_value"`), nil},
|
||||
{"cmd", cty.StringVal(`"cmd_value"`), nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -466,8 +529,10 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
|
||||
{name: "array of strings",
|
||||
variables: Variables{"used_strings": &Variable{
|
||||
DefaultValue: stringListVal("default_value_1"),
|
||||
Type: cty.List(cty.String),
|
||||
Values: []VariableAssignment{
|
||||
{"default", stringListVal("default_value_1"), nil},
|
||||
},
|
||||
Type: cty.List(cty.String),
|
||||
}},
|
||||
args: args{
|
||||
env: []string{`PKR_VAR_used_strings=["env_value_1", "env_value_2"]`},
|
||||
|
@ -484,11 +549,14 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiags: false,
|
||||
wantVariables: Variables{
|
||||
"used_strings": &Variable{
|
||||
Type: cty.List(cty.String),
|
||||
CmdValue: stringListVal("cmd_value_1"),
|
||||
VarfileValue: stringListVal("varfile_value_1"),
|
||||
EnvValue: stringListVal("env_value_1", "env_value_2"),
|
||||
DefaultValue: stringListVal("default_value_1"),
|
||||
Type: cty.List(cty.String),
|
||||
Values: []VariableAssignment{
|
||||
{"default", stringListVal("default_value_1"), nil},
|
||||
{"env", stringListVal("env_value_1", "env_value_2"), nil},
|
||||
{"varfile", stringListVal("xy"), nil},
|
||||
{"varfile", stringListVal("varfile_value_1"), nil},
|
||||
{"cmd", stringListVal("cmd_value_1"), nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -498,8 +566,8 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
|
||||
{name: "bool",
|
||||
variables: Variables{"enabled": &Variable{
|
||||
DefaultValue: cty.False,
|
||||
Type: cty.Bool,
|
||||
Values: []VariableAssignment{{"default", cty.False, nil}},
|
||||
Type: cty.Bool,
|
||||
}},
|
||||
args: args{
|
||||
env: []string{`PKR_VAR_enabled=true`},
|
||||
|
@ -515,11 +583,13 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiags: false,
|
||||
wantVariables: Variables{
|
||||
"enabled": &Variable{
|
||||
Type: cty.Bool,
|
||||
CmdValue: cty.True,
|
||||
VarfileValue: cty.False,
|
||||
EnvValue: cty.True,
|
||||
DefaultValue: cty.False,
|
||||
Type: cty.Bool,
|
||||
Values: []VariableAssignment{
|
||||
{"default", cty.False, nil},
|
||||
{"env", cty.True, nil},
|
||||
{"varfile", cty.False, nil},
|
||||
{"cmd", cty.True, nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -529,8 +599,8 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
|
||||
{name: "invalid env var",
|
||||
variables: Variables{"used_string": &Variable{
|
||||
DefaultValue: cty.StringVal("default_value"),
|
||||
Type: cty.String,
|
||||
Values: []VariableAssignment{{"default", cty.StringVal("default_value"), nil}},
|
||||
Type: cty.String,
|
||||
}},
|
||||
args: args{
|
||||
env: []string{`PKR_VAR_used_string`},
|
||||
|
@ -540,8 +610,8 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiags: false,
|
||||
wantVariables: Variables{
|
||||
"used_string": &Variable{
|
||||
Type: cty.String,
|
||||
DefaultValue: cty.StringVal("default_value"),
|
||||
Type: cty.String,
|
||||
Values: []VariableAssignment{{"default", cty.StringVal("default_value"), nil}},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -620,8 +690,8 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiagsHasError: true,
|
||||
wantVariables: Variables{
|
||||
"used_string": &Variable{
|
||||
Type: cty.List(cty.String),
|
||||
EnvValue: cty.DynamicVal,
|
||||
Type: cty.List(cty.String),
|
||||
Values: []VariableAssignment{{"env", cty.DynamicVal, nil}},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -644,8 +714,8 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiagsHasError: true,
|
||||
wantVariables: Variables{
|
||||
"used_string": &Variable{
|
||||
Type: cty.Bool,
|
||||
VarfileValue: cty.DynamicVal,
|
||||
Type: cty.Bool,
|
||||
Values: []VariableAssignment{{"varfile", cty.DynamicVal, nil}},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -670,8 +740,8 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
wantDiagsHasError: true,
|
||||
wantVariables: Variables{
|
||||
"used_string": &Variable{
|
||||
Type: cty.Bool,
|
||||
CmdValue: cty.DynamicVal,
|
||||
Type: cty.Bool,
|
||||
Values: []VariableAssignment{{"cmd", cty.DynamicVal, nil}},
|
||||
},
|
||||
},
|
||||
wantValues: map[string]cty.Value{
|
||||
|
@ -714,7 +784,7 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||
if tt.wantDiagsHasError != gotDiags.HasErrors() {
|
||||
t.Fatalf("Variables.collectVariableValues() unexpected diagnostics HasErrors. %s", gotDiags)
|
||||
}
|
||||
if diff := cmp.Diff(fmt.Sprintf("%#v", tt.wantVariables), fmt.Sprintf("%#v", tt.variables)); diff != "" {
|
||||
if diff := cmp.Diff(tt.wantVariables, tt.variables, cmpOpts...); diff != "" {
|
||||
t.Fatalf("didn't get expected variables: %s", diff)
|
||||
}
|
||||
values := map[string]cty.Value{}
|
||||
|
|
Loading…
Reference in New Issue