diff --git a/modules/terraform/errors.go b/modules/terraform/errors.go index ad64d33af..c446cec03 100644 --- a/modules/terraform/errors.go +++ b/modules/terraform/errors.go @@ -66,8 +66,16 @@ func (err VarFileNotFound) Error() string { return fmt.Sprintf("Could not resolve var file %s", err.Path) } +// InputFileKeyNotFound occurs when tfvar file does not contain a value for the key +// specified in the function call +type InputFileKeyNotFound string + +func (err InputFileKeyNotFound) Error() string { + return fmt.Sprintf("tfvar file doesn't contain a value for the key %q", string(err)) +} + type HclDecodeError struct { - FilePath string + FilePath string ErrorText string } diff --git a/modules/terraform/options.go b/modules/terraform/options.go index e98c67aab..20e2b3421 100644 --- a/modules/terraform/options.go +++ b/modules/terraform/options.go @@ -108,16 +108,19 @@ func WithDefaultRetryableErrors(t *testing.T, originalOptions *Options) *Options } // GetVariableAsStringFromVarFile Gets the string represention of a variable from a provided input file found in VarFile -// For list or map +// For list or map, use GetVariableAsListFromVarFile or GetVariableAsMapFromVarFile, respectively. func GetVariableAsStringFromVarFile(t *testing.T, option *Options, fileName string, key string) string { - result, err := GetVariableAsStringFromVarFileE(option, fileName, key) + result, err := GetVariableAsStringFromVarFileE(t, option, fileName, key) require.NoError(t, err) return result } -func GetVariableAsStringFromVarFileE(option *Options, fileName string, key string) (string, error) { - variables, err := GetAllVariablesFromVarFileE(option, fileName) +// GetVariableAsStringFromVarFileE Gets the string represention of a variable from a provided input file found in VarFile +// Will return an error if GetAllVariablesFromVarFileE returns an error or the key provided does not exist in the file. +// For list or map, use GetVariableAsListFromVarFile or GetVariableAsMapFromVarFile, respectively. +func GetVariableAsStringFromVarFileE(t *testing.T, option *Options, fileName string, key string) (string, error) { + variables, err := GetAllVariablesFromVarFileE(t, option, fileName) if err != nil { return "", err @@ -126,27 +129,36 @@ func GetVariableAsStringFromVarFileE(option *Options, fileName string, key strin variable, exists := variables[key] if !exists { - return "", OutputKeyNotFound(key) + return "", InputFileKeyNotFound(key) } return fmt.Sprintf("%v", variable), nil } +// GetVariableAsMapFromVarFile Gets the map represention of a variable from a provided input file found in VarFile func GetVariableAsMapFromVarFile(t *testing.T, option *Options, fileName string, key string) map[string]string { - result, err := GetVariableAsMapFromVarFileE(option, fileName, key) + result, err := GetVariableAsMapFromVarFileE(t, option, fileName, key) require.NoError(t, err) return result } -func GetVariableAsMapFromVarFileE(option *Options, fileName string, key string) (map[string]string, error) { +// GetVariableAsMapFromVarFileE Gets the map represention of a variable from a provided input file found in VarFile +// Returns an error if GetAllVariablesFromVarFileE returns an error, the key provided does not exist, or the value associated with the key is not a map +func GetVariableAsMapFromVarFileE(t *testing.T, option *Options, fileName string, key string) (map[string]string, error) { resultMap := make(map[string]string) - variables, err := GetAllVariablesFromVarFileE(option, fileName) + variables, err := GetAllVariablesFromVarFileE(t, option, fileName) if err != nil { return nil, err } + _, exists := variables[key] + + if !exists { + return nil, InputFileKeyNotFound(key) + } + if reflect.TypeOf(variables[key]).String() != "[]map[string]interface {}" { return nil, UnexpectedOutputType{Key: key, ExpectedType: "map", ActualType: reflect.TypeOf(variables[key]).String()} } @@ -160,21 +172,28 @@ func GetVariableAsMapFromVarFileE(option *Options, fileName string, key string) return resultMap, nil } +// GetVariableAsListFromVarFile Gets the string list represention of a variable from a provided input file found in VarFile func GetVariableAsListFromVarFile(t *testing.T, option *Options, fileName string, key string) []string { - result, err := GetVariableAsListFromVarFileE(option, fileName, key) + result, err := GetVariableAsListFromVarFileE(t, option, fileName, key) require.NoError(t, err) return result } -func GetVariableAsListFromVarFileE(option *Options, fileName string, key string) ([]string, error) { +// GetVariableAsListFromVarFileE Gets the string list represention of a variable from a provided input file found in VarFile +// Will return error if GetAllVariablesFromVarFileE returns an error, the key provided does not exist, or the value associated with the key is not a list +func GetVariableAsListFromVarFileE(t *testing.T, option *Options, fileName string, key string) ([]string, error) { resultArray := []string{} - variables, err := GetAllVariablesFromVarFileE(option, fileName) + variables, err := GetAllVariablesFromVarFileE(t, option, fileName) if err != nil { return nil, err } + if _, exists := variables[key]; !exists { + return nil, InputFileKeyNotFound(key) + } + if reflect.TypeOf(variables[key]).String() != "[]interface {}" { return nil, UnexpectedOutputType{Key: key, ExpectedType: "list", ActualType: reflect.TypeOf(variables[key]).String()} } @@ -186,18 +205,17 @@ func GetVariableAsListFromVarFileE(option *Options, fileName string, key string) return resultArray, nil } -// GetVariablesFromVarFiles Parses all data from a provided input file found in VarFile and returns them as a key-value map -// Note that the values in the map are interface{} type so you will need to convert them to the expected data type +// GetAllVariablesFromVarFile Parses all data from a provided input file found in VarFile and returns them as a key-value map func GetAllVariablesFromVarFile(t *testing.T, option *Options, fileName string) map[string]interface{} { - variableMaps, err := GetAllVariablesFromVarFileE(option, fileName) + variableMaps, err := GetAllVariablesFromVarFileE(t, option, fileName) require.NoError(t, err) return variableMaps } -// GetVariablesFromVarFilesE Parses all data from a provided input file found ind in VarFile and returns them as a key-value map -// Note that the values in the map are interface{} type so you will need to convert them to the expected data type -func GetAllVariablesFromVarFileE(option *Options, fileName string) (map[string]interface{}, error) { +// GetAllVariablesFromVarFileE Parses all data from a provided input file found ind in VarFile and returns them as a key-value map +// Retursn an error if the specified file does not exist, the specified file is not readable, or the specified file cannot be decoded from HCL +func GetAllVariablesFromVarFileE(t *testing.T, option *Options, fileName string) (map[string]interface{}, error) { variableMap := map[string]interface{}{} fileIndex := -1 if len(option.VarFiles) == 0 { @@ -226,7 +244,6 @@ func GetAllVariablesFromVarFileE(option *Options, fileName string) (map[string]i if err != nil { return nil, HclDecodeError{FilePath: option.VarFiles[fileIndex], ErrorText: err.Error()} } - return variableMap, nil } diff --git a/modules/terraform/options_test.go b/modules/terraform/options_test.go index d45f1694c..8483192ee 100644 --- a/modules/terraform/options_test.go +++ b/modules/terraform/options_test.go @@ -10,57 +10,212 @@ import ( "github.com/stretchr/testify/require" ) -func TestGetVariablesFromVarFiles(t *testing.T) { +func TestGetVariablesFromVarFilesAsString(t *testing.T) { randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) - randomFileName2 := fmt.Sprintf("./%s.tfvars", random.UniqueId()) testHcl := []byte(` aws_region = "us-east-2" aws_account_id = "111111111111" + number_type = 2 + boolean_type = true tags = { foo = "bar" } list = ["item1"]`) - testHcl2 := []byte(` - aws_region = "us-west-2"`) - WriteFile(t, randomFileName, testHcl) defer os.Remove(randomFileName) - WriteFile(t, randomFileName2, testHcl2) - defer os.Remove(randomFileName2) + stringVal := GetVariableAsStringFromVarFile(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "aws_region") - varMap, err := GetVariablesFromVarFiles(&Options{ - VarFiles: []string{randomFileName, randomFileName2}, - }) + boolString := GetVariableAsStringFromVarFile(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "boolean_type") - if err != nil { - fmt.Println(err.Error()) - t.FailNow() - } + numString := GetVariableAsStringFromVarFile(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "number_type") + + require.Equal(t, "us-east-2", stringVal) + require.Equal(t, "true", boolString) + require.Equal(t, "2", numString) + +} + +func TestGetVariablesFromVarFilesAsStringKeyDoesNotExist(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) + + testHcl := []byte(` + aws_region = "us-east-2" + aws_account_id = "111111111111" + tags = { + foo = "bar" + } + list = ["item1"]`) + + WriteFile(t, randomFileName, testHcl) + defer os.Remove(randomFileName) + + _, err := GetVariableAsStringFromVarFileE(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "badkey") + + require.Error(t, err) +} + +func TestGetVariableAsMapFromVarFile(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) + expected := make(map[string]string) + expected["foo"] = "bar" + + testHcl := []byte(` + aws_region = "us-east-2" + aws_account_id = "111111111111" + tags = { + foo = "bar" + } + list = ["item1"]`) + + WriteFile(t, randomFileName, testHcl) + defer os.Remove(randomFileName) + + val := GetVariableAsMapFromVarFile(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "tags") + + require.Equal(t, expected, val) +} + +func TestGetVariableAsMapFromVarFileNotMap(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) + + testHcl := []byte(` + aws_region = "us-east-2" + aws_account_id = "111111111111" + tags = { + foo = "bar" + } + list = ["item1"]`) + + WriteFile(t, randomFileName, testHcl) + defer os.Remove(randomFileName) + + _, err := GetVariableAsMapFromVarFileE(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "aws_region") + + require.Error(t, err) +} + +func TestGetVariableAsMapFromVarFileKeyDoesNotExist(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) + + testHcl := []byte(` + aws_region = "us-east-2" + aws_account_id = "111111111111" + tags = { + foo = "bar" + } + list = ["item1"]`) + + WriteFile(t, randomFileName, testHcl) + defer os.Remove(randomFileName) + + _, err := GetVariableAsMapFromVarFileE(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "badkey") + + require.Error(t, err) +} + +func TestGetVariableAsListFromVarFile(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) + expected := []string{"item1"} + + testHcl := []byte(` + aws_region = "us-east-2" + aws_account_id = "111111111111" + tags = { + foo = "bar" + } + list = ["item1"]`) + + WriteFile(t, randomFileName, testHcl) + defer os.Remove(randomFileName) + + val := GetVariableAsListFromVarFile(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "list") - require.Equal(t, 2, len(varMap)) - require.Equal(t, "us-east-2", varMap[0]["aws_region"]) - require.Equal(t, "111111111111", varMap[0]["aws_account_id"]) - require.Equal(t, map[string]interface{}{ "foo": "bar", }, varMap[0]["tags"].([]map[string]interface{})[0]) - require.Equal(t, []interface{}{ "item1" }, varMap[0]["list"].([]interface{})) - require.Equal(t, "us-west-2", varMap[1]["aws_region"]) + require.Equal(t, expected, val) } -func TestGetVariablesFromVarFilesNoFileError(t *testing.T) { - _, err := GetVariablesFromVarFiles(&Options{ - VarFiles: []string{"thisdoesntexist"}, - }) +func TestGetVariableAsListNotList(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) - require.Equal(t, "open thisdoesntexist: no such file or directory", err.Error()) + testHcl := []byte(` + aws_region = "us-east-2" + aws_account_id = "111111111111" + tags = { + foo = "bar" + } + list = ["item1"]`) + + WriteFile(t, randomFileName, testHcl) + defer os.Remove(randomFileName) + + _, err := GetVariableAsListFromVarFileE(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "tags") + + require.Error(t, err) } -func TestGetVariablesFromVarFilesBadFile(t *testing.T) { +func TestGetVariableAsListKeyDoesNotExist(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) + + testHcl := []byte(` + aws_region = "us-east-2" + aws_account_id = "111111111111" + tags = { + foo = "bar" + } + list = ["item1"]`) + + WriteFile(t, randomFileName, testHcl) + defer os.Remove(randomFileName) + + _, err := GetVariableAsListFromVarFileE(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName, "badkey") + + require.Error(t, err) +} + +func TestGetAllVariablesFromVarFileENotInVarFiles(t *testing.T) { + _, err := GetAllVariablesFromVarFileE(t, &Options{ + VarFiles: []string{"filea"}, + }, "fileb") + + //require.Equal(t, "open thisdoesntexist: no such file or directory", err.Error()) + require.Error(t, err) +} + +func TestGetAllVariablesFromVarFileEFileDoesNotExist(t *testing.T) { + _, err := GetAllVariablesFromVarFileE(t, &Options{ + VarFiles: []string{"filea"}, + }, "filea") + + require.Equal(t, "open filea: no such file or directory", err.Error()) +} + +func TestGetAllVariablesFromVarFileBadFile(t *testing.T) { randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) testHcl := []byte(` thiswillnotwork`) - + err := ioutil.WriteFile(randomFileName, testHcl, 0644) if err != nil { @@ -70,9 +225,9 @@ func TestGetVariablesFromVarFilesBadFile(t *testing.T) { defer os.Remove(randomFileName) - _, err = GetVariablesFromVarFiles(&Options{ + _, err = GetAllVariablesFromVarFileE(t, &Options{ VarFiles: []string{randomFileName}, - }) + }, randomFileName) if err == nil { t.FailNow() @@ -83,6 +238,36 @@ func TestGetVariablesFromVarFilesBadFile(t *testing.T) { } +func TestGetAllVariablesFromVarFile(t *testing.T) { + randomFileName := fmt.Sprintf("./%s.tfvars", random.UniqueId()) + testHcl := []byte(` + aws_region = "us-east-2" + `) + + err := ioutil.WriteFile(randomFileName, testHcl, 0644) + + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + defer os.Remove(randomFileName) + + val, err := GetAllVariablesFromVarFileE(t, &Options{ + VarFiles: []string{randomFileName}, + }, randomFileName) + + if err != nil { + t.FailNow() + } + + expected := make(map[string]interface{}) + expected["aws_region"] = "us-east-2" + + require.Equal(t, expected, val) + +} + // Helper function to write a file to the filesystem // Will immediately fail the test if it could not write the file func WriteFile(t *testing.T, fileName string, bytes []byte) { @@ -92,4 +277,4 @@ func WriteFile(t *testing.T, fileName string, bytes []byte) { fmt.Println(err.Error()) t.FailNow() } -} \ No newline at end of file +}