Skip to content

Commit

Permalink
Add support for setting terragrunt-source-map using env vars (#1676)
Browse files Browse the repository at this point in the history
  • Loading branch information
yorinasub17 authored May 24, 2021
1 parent daabff2 commit a716f68
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 22 deletions.
37 changes: 16 additions & 21 deletions cli/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -407,25 +411,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
Expand Down Expand Up @@ -472,9 +473,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))
}
2 changes: 1 addition & 1 deletion cli/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,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 {
Expand Down
1 change: 1 addition & 0 deletions docs/_docs/04_reference/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ append the path of `source` parameter in each module to the `--terragrunt-source
### terragrunt-source-map

**CLI Arg**: `--terragrunt-source-map`<br/>
**Environment Variable**: `TERRAGRUNT_SOURCE_MAP` (encoded as comma separated value, e.g., `source1=dest1,source2=dest2`)<br/>
**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`
Expand Down
21 changes: 21 additions & 0 deletions test/integration_serial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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://[email protected]/gruntwork-io/i-dont-exist.git=%s", tmpEnvPath),
fmt.Sprintf("git::ssh://[email protected]/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)
}
28 changes: 28 additions & 0 deletions util/collections.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"regexp"
"strings"

"github.com/gruntwork-io/terragrunt/errors"
)

func MatchesAny(regExps []string, s string) bool {
Expand Down Expand Up @@ -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))
}
41 changes: 41 additions & 0 deletions util/collections_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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://[email protected]=/path/to/local"},
map[string]string{"ssh://[email protected]": "/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,
)
})
}
}

0 comments on commit a716f68

Please sign in to comment.