Files
Packer-Cn/hcl2template/types.variables_test.go
Wilken Rivera 57cbe4e203 core: Update validation options for undeclared variables (#12104)
* Update validation options for undeclared variables

In an effort to help users move from JSON to HCL2 templates the support for
variable definitions files are being updated to ignore undeclared
variable warnings on build execution. For legacy JSON templates builds no
warnings are displayed when var-files contain undeclared variables.

Since preferred mode HCL2 templates is to be explicit with variable
declarations - they must be declared to be used - validation for
undeclared variables still warns when running `packer validate`. A new
flag has been added to the validate command that can be used to disable
undeclared variable warnings.

* Update validation test for unused variables

Example Run
```
~>  go run . validate -no-warn-undeclared-var -var-file
command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl
command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
The configuration is valid.

~>  go run . validate -var-file
command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl
command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
Warning: Undefined variable

The variable "unused" was set but was not declared as an input variable.
To declare variable "unused" place this block in one of your .pkr.hcl
files,
such as variables.pkr.hcl

variable "unused" {
  type    = string
  default = null

}

The configuration is valid.

~>  go run . build -var-file
command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl
command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
file.chocolate: output will be in this color.

Build 'file.chocolate' finished after 744 microseconds.

==> Wait completed after 798 microseconds

==> Builds finished. The artifacts of successful builds are:
--> file.chocolate: Stored file: chocolate.txt
```

* Rename Strict field to WarnOnUndeclaredVar

The field name Strict is a bit vague since it is only used for
checking against undeclared variables within a var-file definition.
To mitigate against potential overloading of this field it is
being renamed to be more explicit on its usage.

* command/build: Add warn-on-undeclared-var flag

Now that the default behaviour is to not display warnings for undeclared variables
an optional flag has been added to toggle the old behaviour.

```
~>  go run . build -warn-on-undeclared-var -var-file command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
Warning: Undefined variable

The variable "unused" was set but was not declared as an input variable.
To declare variable "unused" place this block in one of your .pkr.hcl files,
such as variables.pkr.hcl

variable "unused" {
  type    = string
  default = null

}

file.chocolate: output will be in this color.

Build 'file.chocolate' finished after 762 microseconds.

==> Wait completed after 799 microseconds

==> Builds finished. The artifacts of successful builds are:
--> file.chocolate: Stored file: chocolate.txt
```
2022-11-14 17:06:45 -05:00

1040 lines
26 KiB
Go

package hcl2template
import (
"fmt"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl/v2"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer/builder/null"
. "github.com/hashicorp/packer/hcl2template/internal"
"github.com/hashicorp/packer/packer"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
)
func TestParse_variables(t *testing.T) {
defaultParser := getBasicParser()
tests := []parseTest{
{"basic variables",
defaultParser,
parseTestArgs{"testdata/variables/basic.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Builds: Builds{
&BuildBlock{
Sources: []SourceUseBlock{
{
SourceRef: SourceRef{
Type: "null",
Name: "test",
},
},
},
},
},
Sources: map[SourceRef]SourceBlock{
{
Type: "null",
Name: "test",
}: {
Type: "null",
Name: "test",
},
},
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"image_name": &Variable{
Name: "image_name",
Type: cty.String,
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo-image-{{user `my_secret`}}")}},
},
"key": &Variable{
Name: "key",
Type: cty.String,
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("value")}},
},
"my_secret": &Variable{
Name: "my_secret",
Type: cty.String,
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
},
"image_id": &Variable{
Name: "image_id",
Type: cty.String,
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("image-id-default")}},
},
"port": &Variable{
Name: "port",
Type: cty.Number,
Values: []VariableAssignment{{From: "default", Value: cty.NumberIntVal(42)}},
},
"availability_zone_names": &Variable{
Name: "availability_zone_names",
Values: []VariableAssignment{{
From: "default",
Value: cty.ListVal([]cty.Value{
cty.StringVal("us-west-1a"),
}),
}},
Type: cty.List(cty.String),
Description: fmt.Sprintln("Describing is awesome ;D"),
},
"super_secret_password": &Variable{
Name: "super_secret_password",
Sensitive: true,
Values: []VariableAssignment{{
From: "default",
Value: cty.NullVal(cty.String),
}},
Type: cty.String,
Description: fmt.Sprintln("Handle with care plz"),
},
},
LocalVariables: Variables{
"owner": &Variable{
Name: "owner",
Values: []VariableAssignment{{
From: "default",
Value: cty.StringVal("Community Team"),
}},
Type: cty.String,
},
"service_name": &Variable{
Name: "service_name",
Values: []VariableAssignment{{
From: "default",
Value: cty.StringVal("forum"),
}},
Type: cty.String,
},
"supersecret": &Variable{
Name: "supersecret",
Values: []VariableAssignment{{
From: "default",
Value: cty.StringVal("secretvar"),
}},
Type: cty.String,
Sensitive: true,
},
},
},
false, false,
[]packersdk.Build{
&packer.CoreBuild{
Type: "null.test",
Builder: &null.Builder{},
Provisioners: []packer.CoreBuildProvisioner{},
PostProcessors: [][]packer.CoreBuildPostProcessor{},
Prepared: true,
},
},
false,
},
{"duplicate variable",
defaultParser,
parseTestArgs{"testdata/variables/duplicate_variable.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"boolean_value": &Variable{
Name: "boolean_value",
Values: []VariableAssignment{{
From: "default",
Value: cty.BoolVal(false),
}},
Type: cty.Bool,
},
},
},
true, true,
[]packersdk.Build{},
false,
},
{"duplicate variable in variables",
defaultParser,
parseTestArgs{"testdata/variables/duplicate_variables.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"boolean_value": &Variable{
Name: "boolean_value",
Values: []VariableAssignment{{
From: "default",
Value: cty.BoolVal(false),
}},
Type: cty.Bool,
},
},
},
true, true,
[]packersdk.Build{},
false,
},
{"duplicate local block",
defaultParser,
parseTestArgs{"testdata/variables/duplicate_locals", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: "testdata/variables/duplicate_locals",
LocalVariables: Variables{
"sensible": &Variable{
Values: []VariableAssignment{
{
From: "default",
Value: cty.StringVal("something"),
},
},
Type: cty.String,
Name: "sensible",
},
},
},
true, true,
[]packersdk.Build{},
false,
},
{"invalid default type",
defaultParser,
parseTestArgs{"testdata/variables/invalid_default.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"broken_type": &Variable{
Name: "broken_type",
Values: []VariableAssignment{{
From: "default",
Value: cty.UnknownVal(cty.DynamicPseudoType),
}},
Type: cty.List(cty.String),
},
},
},
true, true,
[]packersdk.Build{},
false,
},
{"unknown key",
defaultParser,
parseTestArgs{"testdata/variables/unknown_key.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"broken_variable": &Variable{
Name: "broken_variable",
Values: []VariableAssignment{{From: "default", Value: cty.BoolVal(true)}},
Type: cty.Bool,
},
},
},
true, true,
[]packersdk.Build{},
false,
},
{"unset used variable",
defaultParser,
parseTestArgs{"testdata/variables/unset_used_string_variable.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"foo": &Variable{
Name: "foo",
Type: cty.String,
},
},
},
true, true,
[]packersdk.Build{},
true,
},
{"unset unused variable",
defaultParser,
parseTestArgs{"testdata/variables/unset_unused_string_variable.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"foo": &Variable{
Name: "foo",
Type: cty.String,
},
},
Sources: map[SourceRef]SourceBlock{
SourceRef{Type: "null", Name: "null-builder"}: SourceBlock{
Name: "null-builder",
Type: "null",
},
},
Builds: Builds{
&BuildBlock{
Sources: []SourceUseBlock{
{
SourceRef: SourceRef{Type: "null", Name: "null-builder"},
},
},
},
},
},
true, true,
[]packersdk.Build{
&packer.CoreBuild{
Type: "null",
Builder: &null.Builder{},
Provisioners: []packer.CoreBuildProvisioner{},
PostProcessors: [][]packer.CoreBuildPostProcessor{},
Prepared: true,
},
},
false,
},
{"locals within another locals usage in different files",
defaultParser,
parseTestArgs{"testdata/variables/complicated", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Builds: Builds{
&BuildBlock{
Sources: []SourceUseBlock{
{
SourceRef: SourceRef{
Type: "null",
Name: "test",
},
},
},
},
},
Sources: map[SourceRef]SourceBlock{
{
Type: "null",
Name: "test",
}: {
Type: "null",
Name: "test",
},
},
Basedir: "testdata/variables/complicated",
InputVariables: Variables{
"name_prefix": &Variable{
Name: "name_prefix",
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
Type: cty.String,
},
},
LocalVariables: Variables{
"name_prefix": &Variable{
Name: "name_prefix",
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
Type: cty.String,
},
"foo": &Variable{
Name: "foo",
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
Type: cty.String,
},
"bar": &Variable{
Name: "bar",
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
Type: cty.String,
},
"for_var": &Variable{
Name: "for_var",
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("foo")}},
Type: cty.String,
},
"bar_var": &Variable{
Name: "bar_var",
Values: []VariableAssignment{{
From: "default",
Value: cty.TupleVal([]cty.Value{
cty.StringVal("foo"),
cty.StringVal("foo"),
cty.StringVal("foo"),
}),
}},
Type: cty.Tuple([]cty.Type{
cty.String,
cty.String,
cty.String,
}),
},
},
},
false, false,
[]packersdk.Build{
&packer.CoreBuild{
Type: "null.test",
Builder: &null.Builder{},
Provisioners: []packer.CoreBuildProvisioner{},
PostProcessors: [][]packer.CoreBuildPostProcessor{},
Prepared: true,
},
},
false,
},
{"recursive locals",
defaultParser,
parseTestArgs{"testdata/variables/recursive_locals.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
LocalVariables: Variables{},
},
true, true,
[]packersdk.Build{},
false,
},
{"set variable from var-file",
defaultParser,
parseTestArgs{"testdata/variables/foo-string.variable.pkr.hcl", nil, []string{"testdata/variables/set-foo-too-wee.hcl"}},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
Builds: Builds{
&BuildBlock{
Sources: []SourceUseBlock{
{
SourceRef: SourceRef{
Type: "null",
Name: "test",
},
},
},
},
},
Sources: map[SourceRef]SourceBlock{
{
Type: "null",
Name: "test",
}: {
Type: "null",
Name: "test",
},
},
InputVariables: Variables{
"foo": &Variable{
Name: "foo",
Values: []VariableAssignment{
VariableAssignment{"default", cty.StringVal("bar"), nil},
VariableAssignment{"varfile", cty.StringVal("wee"), nil},
},
Type: cty.String,
},
},
},
false, false,
[]packersdk.Build{
&packer.CoreBuild{
Type: "null.test",
Builder: &null.Builder{},
Provisioners: []packer.CoreBuildProvisioner{},
PostProcessors: [][]packer.CoreBuildPostProcessor{},
Prepared: true,
},
},
false,
},
{"unknown variable from var-file",
defaultParser,
parseTestArgs{"testdata/variables/empty.pkr.hcl", nil, []string{"testdata/variables/set-foo-too-wee.hcl"}},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Builds: Builds{
&BuildBlock{
Sources: []SourceUseBlock{
{
SourceRef: SourceRef{
Type: "null",
Name: "test",
},
},
},
},
},
Sources: map[SourceRef]SourceBlock{
{
Type: "null",
Name: "test",
}: {
Type: "null",
Name: "test",
},
},
Basedir: filepath.Join("testdata", "variables"),
},
false, false,
[]packersdk.Build{
&packer.CoreBuild{
Type: "null.test",
Builder: &null.Builder{},
Provisioners: []packer.CoreBuildProvisioner{},
PostProcessors: [][]packer.CoreBuildPostProcessor{},
Prepared: true,
},
},
false,
},
{"provisioner variable decoding",
defaultParser,
parseTestArgs{"testdata/variables/provisioner_variable_decoding.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables"),
InputVariables: Variables{
"max_retries": &Variable{
Name: "max_retries",
Values: []VariableAssignment{{"default", cty.StringVal("1"), nil}},
Type: cty.String,
},
"max_retries_int": &Variable{
Name: "max_retries_int",
Values: []VariableAssignment{{"default", cty.NumberIntVal(1), nil}},
Type: cty.Number,
},
},
Sources: map[SourceRef]SourceBlock{
SourceRef{Type: "null", Name: "null-builder"}: SourceBlock{
Name: "null-builder",
Type: "null",
},
},
Builds: Builds{
&BuildBlock{
Sources: []SourceUseBlock{
{
SourceRef: SourceRef{Type: "null", Name: "null-builder"},
},
},
ProvisionerBlocks: []*ProvisionerBlock{
{
PType: "shell",
MaxRetries: 1,
},
{
PType: "shell",
MaxRetries: 1,
},
},
},
},
},
false, false,
[]packersdk.Build{&packer.CoreBuild{
Type: "null.null-builder",
Prepared: true,
Builder: &null.Builder{},
Provisioners: []packer.CoreBuildProvisioner{
{
PType: "shell",
Provisioner: &packer.RetriedProvisioner{
MaxRetries: 1,
Provisioner: &HCL2Provisioner{
Provisioner: &MockProvisioner{
Config: MockConfig{
NestedMockConfig: NestedMockConfig{
Tags: []MockTag{},
},
NestedSlice: []NestedMockConfig{},
},
},
},
},
},
{
PType: "shell",
Provisioner: &packer.RetriedProvisioner{
MaxRetries: 1,
Provisioner: &HCL2Provisioner{
Provisioner: &MockProvisioner{
Config: MockConfig{
NestedMockConfig: NestedMockConfig{
Tags: []MockTag{},
},
NestedSlice: []NestedMockConfig{},
},
},
},
},
},
},
PostProcessors: [][]packer.CoreBuildPostProcessor{},
},
},
false,
},
{"valid validation block",
defaultParser,
parseTestArgs{"testdata/variables/validation/valid.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables", "validation"),
Builds: Builds{
&BuildBlock{
Sources: []SourceUseBlock{
{
SourceRef: SourceRef{
Type: "null",
Name: "test",
},
},
},
},
},
Sources: map[SourceRef]SourceBlock{
{
Type: "null",
Name: "test",
}: {
Type: "null",
Name: "test",
},
},
InputVariables: Variables{
"image_id": &Variable{
Values: []VariableAssignment{
{"default", cty.StringVal("ami-something-something"), nil},
},
Name: "image_id",
Type: cty.String,
Validations: []*VariableValidation{
&VariableValidation{
ErrorMessage: `The image_id value must be a valid AMI id, starting with "ami-".`,
},
},
},
},
},
false, false,
[]packersdk.Build{
&packer.CoreBuild{
Type: "null.test",
Builder: &null.Builder{},
Provisioners: []packer.CoreBuildProvisioner{},
PostProcessors: [][]packer.CoreBuildPostProcessor{},
Prepared: true,
},
},
false,
},
{"valid validation block - invalid default",
defaultParser,
parseTestArgs{"testdata/variables/validation/invalid_default.pkr.hcl", nil, nil},
&PackerConfig{
CorePackerVersionString: lockedVersion,
Basedir: filepath.Join("testdata", "variables", "validation"),
InputVariables: Variables{
"image_id": &Variable{
Values: []VariableAssignment{{"default", cty.StringVal("potato"), nil}},
Name: "image_id",
Type: cty.String,
Validations: []*VariableValidation{
&VariableValidation{
ErrorMessage: `The image_id value must be a valid AMI id, starting with "ami-".`,
},
},
},
},
},
true, true,
nil,
false,
},
}
testParse(t, tests)
}
func TestVariables_collectVariableValues(t *testing.T) {
type args struct {
env []string
hclFiles []string
argv map[string]string
}
tests := []struct {
name string
variables Variables
validationOptions ValidationOptions
args args
wantDiags bool
wantDiagsHasError bool
wantVariables Variables
wantValues map[string]cty.Value
}{
{name: "string",
variables: Variables{"used_string": &Variable{
Values: []VariableAssignment{
{"default", cty.StringVal("default_value"), nil},
},
Type: cty.String,
}},
args: args{
env: []string{`PKR_VAR_used_string=env_value`},
hclFiles: []string{
`used_string="xy"`,
`used_string="varfile_value"`,
},
argv: map[string]string{
"used_string": `cmd_value`,
},
},
// output
wantDiags: false,
wantVariables: Variables{
"used_string": &Variable{
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{
"used_string": cty.StringVal("cmd_value"),
},
},
{name: "quoted string",
variables: Variables{"quoted_string": &Variable{
Values: []VariableAssignment{
{"default", cty.StringVal(`"default_value"`), nil},
},
Type: cty.String,
}},
args: args{
env: []string{`PKR_VAR_quoted_string="env_value"`},
hclFiles: []string{
`quoted_string="\"xy\""`,
`quoted_string="\"varfile_value\""`,
},
argv: map[string]string{
"quoted_string": `"cmd_value"`,
},
},
// output
wantDiags: false,
wantVariables: Variables{
"quoted_string": &Variable{
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{
"quoted_string": cty.StringVal(`"cmd_value"`),
},
},
{name: "array of strings",
variables: Variables{"used_strings": &Variable{
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"]`},
hclFiles: []string{
`used_strings=["xy"]`,
`used_strings=["varfile_value_1"]`,
},
argv: map[string]string{
"used_strings": `["cmd_value_1"]`,
},
},
// output
wantDiags: false,
wantVariables: Variables{
"used_strings": &Variable{
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{
"used_strings": stringListVal("cmd_value_1"),
},
},
{name: "bool",
variables: Variables{"enabled": &Variable{
Values: []VariableAssignment{{"default", cty.False, nil}},
Type: cty.Bool,
}},
args: args{
env: []string{`PKR_VAR_enabled=true`},
hclFiles: []string{
`enabled="false"`,
},
argv: map[string]string{
"enabled": `true`,
},
},
// output
wantDiags: false,
wantVariables: Variables{
"enabled": &Variable{
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{
"enabled": cty.True,
},
},
{name: "invalid env var",
variables: Variables{"used_string": &Variable{
Values: []VariableAssignment{{"default", cty.StringVal("default_value"), nil}},
Type: cty.String,
}},
args: args{
env: []string{`PKR_VAR_used_string`},
},
// output
wantDiags: false,
wantVariables: Variables{
"used_string": &Variable{
Type: cty.String,
Values: []VariableAssignment{{"default", cty.StringVal("default_value"), nil}},
},
},
wantValues: map[string]cty.Value{
"used_string": cty.StringVal("default_value"),
},
},
{name: "undefined but set value - pkrvar file - normal mode",
variables: Variables{},
args: args{
hclFiles: []string{`undefined_string="value"`},
},
// output
wantDiags: false,
wantDiagsHasError: false,
wantVariables: Variables{},
wantValues: map[string]cty.Value{},
},
{name: "undefined but set value - pkrvar file - strict mode",
variables: Variables{},
validationOptions: ValidationOptions{
WarnOnUndeclaredVar: true,
},
args: args{
hclFiles: []string{`undefined_string="value"`},
},
// output
wantDiags: true,
wantDiagsHasError: false,
wantVariables: Variables{},
wantValues: map[string]cty.Value{},
},
{name: "undefined but set value - env",
variables: Variables{},
args: args{
env: []string{`PKR_VAR_undefined_string=value`},
},
// output
wantDiags: false,
wantVariables: Variables{},
wantValues: map[string]cty.Value{},
},
{name: "undefined but set value - argv",
variables: Variables{},
args: args{
argv: map[string]string{
"undefined_string": "value",
},
},
// output
wantDiags: true,
wantDiagsHasError: true,
wantVariables: Variables{},
wantValues: map[string]cty.Value{},
},
{name: "value not corresponding to type - env",
variables: Variables{
"used_string": &Variable{
Type: cty.List(cty.String),
},
},
args: args{
env: []string{`PKR_VAR_used_string="string"`},
},
// output
wantDiags: true,
wantDiagsHasError: true,
wantVariables: Variables{
"used_string": &Variable{
Type: cty.List(cty.String),
Values: []VariableAssignment{{"env", cty.DynamicVal, nil}},
},
},
wantValues: map[string]cty.Value{
"used_string": cty.DynamicVal,
},
},
{name: "value not corresponding to type - cfg file",
variables: Variables{
"used_string": &Variable{
Type: cty.Bool,
},
},
args: args{
hclFiles: []string{`used_string=["string"]`},
},
// output
wantDiags: true,
wantDiagsHasError: true,
wantVariables: Variables{
"used_string": &Variable{
Type: cty.Bool,
Values: []VariableAssignment{{"varfile", cty.DynamicVal, nil}},
},
},
wantValues: map[string]cty.Value{
"used_string": cty.DynamicVal,
},
},
{name: "value not corresponding to type - argv",
variables: Variables{
"used_string": &Variable{
Type: cty.Bool,
},
},
args: args{
argv: map[string]string{
"used_string": `["true"]`,
},
},
// output
wantDiags: true,
wantDiagsHasError: true,
wantVariables: Variables{
"used_string": &Variable{
Type: cty.Bool,
Values: []VariableAssignment{{"cmd", cty.DynamicVal, nil}},
},
},
wantValues: map[string]cty.Value{
"used_string": cty.DynamicVal,
},
},
{name: "defining a variable block in a variables file is invalid ",
variables: Variables{},
args: args{
hclFiles: []string{`variable "something" {}`},
},
// output
wantDiags: true,
wantDiagsHasError: true,
wantVariables: Variables{},
wantValues: map[string]cty.Value{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var files []*hcl.File
parser := getBasicParser()
for i, hclContent := range tt.args.hclFiles {
file, diags := parser.ParseHCL([]byte(hclContent), fmt.Sprintf("test_file_%d_*"+hcl2AutoVarFileExt, i))
if diags != nil {
t.Fatalf("ParseHCLFile %d: %v", i, diags)
}
files = append(files, file)
}
cfg := &PackerConfig{
InputVariables: tt.variables,
ValidationOptions: tt.validationOptions,
}
gotDiags := cfg.collectInputVariableValues(tt.args.env, files, tt.args.argv)
if (gotDiags == nil) == tt.wantDiags {
t.Fatalf("Variables.collectVariableValues() = %v, want %v", gotDiags, tt.wantDiags)
}
if tt.wantDiagsHasError != gotDiags.HasErrors() {
t.Fatalf("Variables.collectVariableValues() unexpected diagnostics HasErrors. %s", gotDiags)
}
if diff := cmp.Diff(tt.wantVariables, tt.variables, cmpOpts...); diff != "" {
t.Fatalf("didn't get expected variables: %s", diff)
}
values := map[string]cty.Value{}
for k, v := range tt.variables {
value, diag := v.Value(), v.ValidateValue()
if diag != nil {
t.Fatalf("Value %s: %v", k, diag)
}
values[k] = value
}
if diff := cmp.Diff(fmt.Sprintf("%#v", values), fmt.Sprintf("%#v", tt.wantValues)); diff != "" {
t.Fatalf("didn't get expected values: %s", diff)
}
})
}
}
func stringListVal(strings ...string) cty.Value {
values := []cty.Value{}
for _, str := range strings {
values = append(values, cty.StringVal(str))
}
list, err := convert.Convert(cty.ListVal(values), cty.List(cty.String))
if err != nil {
panic(err)
}
return list
}