diff --git a/cli/args.go b/cli/args.go index 9e38a92ca7..8baca4df88 100644 --- a/cli/args.go +++ b/cli/args.go @@ -95,7 +95,11 @@ func parseTerragruntOptionsFromArgs(terragruntVersion string, args []string, wri return nil, err } - terraformSourceMap, err := parseMutliStringKeyValueArg(args, OPT_TERRAGRUNT_SOURCE_MAP, nil) + terraformSourceMapEnvVar, err := parseMultiStringKeyValueEnvVar("TERRAGRUNT_SOURCE_MAP") + if err != nil { + return nil, err + } + terraformSourceMap, err := parseMutliStringKeyValueArg(args, OPT_TERRAGRUNT_SOURCE_MAP, terraformSourceMapEnvVar) if err != nil { return nil, err } @@ -400,25 +404,22 @@ func parseMutliStringKeyValueArg(args []string, argName string, defaultValue map if err != nil { return nil, err } - if asList == nil { return defaultValue, nil } + return util.KeyValuePairStringListToMap(asList) +} - asMap := map[string]string{} - for _, arg := range asList { - parts := strings.Split(arg, "=") - if len(parts) != 2 { - return nil, errors.WithStackTrace(InvalidKeyValue(arg)) - } - - key := parts[0] - value := parts[1] - - asMap[key] = value +// Parses an environment variable that is encoded as a comma separated kv pair (e.g., +// `key1=value1,key2=value2,key3=value3`) and converts it to a map. Returns empty map if the environnment variable is +// not set, and error if the environment variable is not encoded as a comma separated kv pair. +func parseMultiStringKeyValueEnvVar(envVarName string) (map[string]string, error) { + rawEnvVarVal := os.Getenv(envVarName) + if rawEnvVarVal == "" { + return map[string]string{}, nil } - - return asMap, nil + mappingsAsList := strings.Split(rawEnvVarVal, ",") + return util.KeyValuePairStringListToMap(mappingsAsList) } // Convert the given variables to a map of environment variables that will expose those variables to Terraform. The @@ -465,9 +466,3 @@ type ArgMissingValue string func (err ArgMissingValue) Error() string { return fmt.Sprintf("You must specify a value for the --%s option", string(err)) } - -type InvalidKeyValue string - -func (err InvalidKeyValue) Error() string { - return fmt.Sprintf("Invalid key-value pair. Expected format KEY=VALUE, got %s.", string(err)) -} diff --git a/cli/args_test.go b/cli/args_test.go index 199bad0150..c6b51dd124 100644 --- a/cli/args_test.go +++ b/cli/args_test.go @@ -283,7 +283,7 @@ func TestParseMutliStringKeyValueArg(t *testing.T) { {[]string{"aws-provider-patch", "--other", "arg"}, "foo", map[string]string{"default": "value"}, map[string]string{"default": "value"}, nil}, {[]string{"aws-provider-patch", "--foo", "key=value"}, "foo", map[string]string{"default": "value"}, map[string]string{"key": "value"}, nil}, {[]string{"aws-provider-patch", "--foo", "key1=value1", "--foo", "key2=value2", "--foo", "key3=value3"}, "foo", map[string]string{"default": "value"}, map[string]string{"key1": "value1", "key2": "value2", "key3": "value3"}, nil}, - {[]string{"aws-provider-patch", "--foo", "invalidvalue"}, "foo", map[string]string{"default": "value"}, nil, InvalidKeyValue("invalidvalue")}, + {[]string{"aws-provider-patch", "--foo", "invalidvalue"}, "foo", map[string]string{"default": "value"}, nil, util.InvalidKeyValue("invalidvalue")}, } for _, testCase := range testCases { diff --git a/docs/_docs/04_reference/cli-options.md b/docs/_docs/04_reference/cli-options.md index 293942bc8b..2cc26bd9b7 100644 --- a/docs/_docs/04_reference/cli-options.md +++ b/docs/_docs/04_reference/cli-options.md @@ -505,6 +505,7 @@ append the path of `source` parameter in each module to the `--terragrunt-source ### terragrunt-source-map **CLI Arg**: `--terragrunt-source-map`
+**Environment Variable**: `TERRAGRUNT_SOURCE_MAP` (encoded as comma separated value, e.g., `source1=dest1,source2=dest2`)
**Requires an argument**: `--terragrunt-source-map git::ssh://github.com=/path/to/local-terraform-code` Can be supplied multiple times: `--terragrunt-source-map source1=dest1 --terragrunt-source-map source2=dest2` diff --git a/test/integration_serial_test.go b/test/integration_serial_test.go index be25cabc92..50e90a607c 100644 --- a/test/integration_serial_test.go +++ b/test/integration_serial_test.go @@ -207,3 +207,24 @@ func TestTerragruntValidateInputsWithUnusedEnvVar(t *testing.T) { moduleDir := filepath.Join("fixture-validate-inputs", "success-inputs-only") runTerragruntValidateInputs(t, moduleDir, nil, false) } + +func TestTerragruntSourceMapEnvArg(t *testing.T) { + fixtureSourceMapPath := "fixture-source-map" + cleanupTerraformFolder(t, fixtureSourceMapPath) + tmpEnvPath := copyEnvironment(t, fixtureSourceMapPath) + rootPath := filepath.Join(tmpEnvPath, fixtureSourceMapPath) + + os.Setenv( + "TERRAGRUNT_SOURCE_MAP", + strings.Join( + []string{ + fmt.Sprintf("git::ssh://git@github.com/gruntwork-io/i-dont-exist.git=%s", tmpEnvPath), + fmt.Sprintf("git::ssh://git@github.com/gruntwork-io/another-dont-exist.git=%s", tmpEnvPath), + }, + ",", + ), + ) + tgPath := filepath.Join(rootPath, "multiple-match") + tgArgs := fmt.Sprintf("terragrunt run-all apply -auto-approve --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-working-dir %s", tgPath) + runTerragrunt(t, tgArgs) +} diff --git a/util/collections.go b/util/collections.go index 6f64a40de0..b811bc588c 100644 --- a/util/collections.go +++ b/util/collections.go @@ -4,6 +4,8 @@ import ( "fmt" "regexp" "strings" + + "github.com/gruntwork-io/terragrunt/errors" ) func MatchesAny(regExps []string, s string) bool { @@ -166,3 +168,29 @@ func StringListInsert(list []string, element string, index int) []string { tail := append([]string{element}, list[index:]...) return append(list[:index], tail...) } + +// KeyValuePairListToMap converts a list of key value pair encoded as `key=value` strings into a map. +func KeyValuePairStringListToMap(asList []string) (map[string]string, error) { + asMap := map[string]string{} + for _, arg := range asList { + parts := strings.Split(arg, "=") + if len(parts) != 2 { + return nil, errors.WithStackTrace(InvalidKeyValue(arg)) + } + + key := parts[0] + value := parts[1] + + asMap[key] = value + } + + return asMap, nil +} + +// custom error types + +type InvalidKeyValue string + +func (err InvalidKeyValue) Error() string { + return fmt.Sprintf("Invalid key-value pair. Expected format KEY=VALUE, got %s.", string(err)) +} diff --git a/util/collections_test.go b/util/collections_test.go index 0d2ab83ad7..11bc1e1c0b 100644 --- a/util/collections_test.go +++ b/util/collections_test.go @@ -235,3 +235,44 @@ func TestStringListInsert(t *testing.T) { t.Logf("%v passed", testCase.list) } } + +func TestKeyValuePairStringListToMap(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + input []string + output map[string]string + }{ + { + "base", + []string{"foo=bar", "baz=carol"}, + map[string]string{ + "foo": "bar", + "baz": "carol", + }, + }, + { + "special_chars", + []string{"ssh://git@github.com=/path/to/local"}, + map[string]string{"ssh://git@github.com": "/path/to/local"}, + }, + { + "empty", + []string{}, + map[string]string{}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + actualOutput, err := KeyValuePairStringListToMap(testCase.input) + assert.NoError(t, err) + assert.Equal( + t, + testCase.output, + actualOutput, + ) + }) + } +}