Skip to content

Commit

Permalink
Fixing interpreting empty list as null value (#3317)
Browse files Browse the repository at this point in the history
* fix: convert empty value to null value

* chore: update test

* chore: update test

* chore: unit test improvements

* chore: code improvements
  • Loading branch information
levkohimins authored Aug 2, 2024
1 parent acbc58a commit 8e8192a
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 11 deletions.
15 changes: 10 additions & 5 deletions config/cty_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,24 +333,29 @@ func updateUnknownCtyValValues(value cty.Value) (cty.Value, error) {
for key, val := range mapVals {
val, err := updateUnknownCtyValValues(val)
if err != nil {
return cty.NilVal, errors.WithStackTrace(err)
return cty.NilVal, err
}
mapVals[key] = val
}
updatedValue = mapVals
if len(mapVals) > 0 {
updatedValue = mapVals
}

case value.Type().IsTupleType(), value.Type().IsListType():
sliceVals := value.AsValueSlice()
for key, val := range sliceVals {
val, err := updateUnknownCtyValValues(val)
if err != nil {
return cty.NilVal, errors.WithStackTrace(err)
return cty.NilVal, err
}
sliceVals[key] = val
}
updatedValue = sliceVals
if len(sliceVals) > 0 {
updatedValue = sliceVals
}
}

default:
if updatedValue == nil {
return value, nil
}

Expand Down
59 changes: 59 additions & 0 deletions config/cty_helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package config

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zclconf/go-cty/cty"
)

func TestUpdateUnknownCtyValValues(t *testing.T) {
t.Parallel()

testCases := []struct {
value cty.Value
expectedValue cty.Value
}{
{
cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
"items": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
"firstname": cty.StringVal("joo"),
"lastname": cty.UnknownVal(cty.String),
})}),
})}),
cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
"items": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
"firstname": cty.StringVal("joo"),
"lastname": cty.StringVal(""),
})}),
})}),
},
{
cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{})}),
cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{})}),
},
{
cty.ObjectVal(map[string]cty.Value{}),
cty.ObjectVal(map[string]cty.Value{}),
},
{
cty.ObjectVal(map[string]cty.Value{"key": cty.UnknownVal(cty.String)}),
cty.ObjectVal(map[string]cty.Value{"key": cty.StringVal("")}),
},
}

for i, testCase := range testCases {
testCase := testCase

t.Run(fmt.Sprintf("testCase-%d", i), func(t *testing.T) {
t.Parallel()

actualValue, err := updateUnknownCtyValValues(testCase.value)
require.NoError(t, err)

assert.Equal(t, testCase.expectedValue, actualValue)
})
}
}
2 changes: 1 addition & 1 deletion configstack/stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ func TestResolveTerraformModulesReadConfigFromParentConfig(t *testing.T) {
localsConfigs[name] = map[string]interface{}{
"dependencies": interface{}(nil),
"download_dir": "",
"generate": interface{}(nil),
"generate": map[string]interface{}{},
"iam_assume_role_duration": interface{}(nil),
"iam_assume_role_session_name": "",
"iam_role": "",
Expand Down
11 changes: 6 additions & 5 deletions test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3767,16 +3767,16 @@ func TestReadTerragruntConfigFull(t *testing.T) {
require.NoError(t, json.Unmarshal([]byte(outputs["dependencies"].Value.(string)), &depsOut))
assert.Equal(
t,
depsOut,
map[string]interface{}{
"paths": []interface{}{"../../fixture"},
},
depsOut,
)

generateOut := map[string]interface{}{}
require.NoError(t, json.Unmarshal([]byte(outputs["generate"].Value.(string)), &generateOut))
assert.Equal(
t,
generateOut,
map[string]interface{}{
"provider": map[string]interface{}{
"path": "provider.tf",
Expand All @@ -3791,25 +3791,25 @@ func TestReadTerragruntConfigFull(t *testing.T) {
`,
},
},
generateOut,
)
remoteStateOut := map[string]interface{}{}
require.NoError(t, json.Unmarshal([]byte(outputs["remote_state"].Value.(string)), &remoteStateOut))
assert.Equal(
t,
remoteStateOut,
map[string]interface{}{
"backend": "local",
"disable_init": false,
"disable_dependency_optimization": false,
"generate": map[string]interface{}{"path": "backend.tf", "if_exists": "overwrite_terragrunt"},
"config": map[string]interface{}{"path": "foo.tfstate"},
},
remoteStateOut,
)
terraformOut := map[string]interface{}{}
require.NoError(t, json.Unmarshal([]byte(outputs["terraformtg"].Value.(string)), &terraformOut))
assert.Equal(
t,
terraformOut,
map[string]interface{}{
"source": "./delorean",
"include_in_copy": []interface{}{"time_machine.*"},
Expand Down Expand Up @@ -3845,8 +3845,9 @@ func TestReadTerragruntConfigFull(t *testing.T) {
"suppress_stdout": nil,
},
},
"error_hook": nil,
"error_hook": map[string]interface{}{},
},
terraformOut,
)
}

Expand Down

0 comments on commit 8e8192a

Please sign in to comment.