From 533bc116ba59f53d71d5f3731513a9582078b6a6 Mon Sep 17 00:00:00 2001 From: Denis O Date: Tue, 19 Nov 2024 17:35:47 +0200 Subject: [PATCH] Add handling of empty feature blocks (#3580) * Add handling of empty feature blocks * partial config cleanup * Documentation update --- config/config_partial.go | 13 +++++++++++ .../config-blocks-and-attributes.md | 5 ++-- .../feature-flags/error-empty-flag/main.tf | 0 .../error-empty-flag/terragrunt.hcl | 5 ++++ test/integration_feature_flags_test.go | 23 ++++++++++++++++--- 5 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/feature-flags/error-empty-flag/main.tf create mode 100644 test/fixtures/feature-flags/error-empty-flag/terragrunt.hcl diff --git a/config/config_partial.go b/config/config_partial.go index f5fc15a92a..f46cfc4b5c 100644 --- a/config/config_partial.go +++ b/config/config_partial.go @@ -147,6 +147,19 @@ func DecodeBaseBlocks(ctx *ParsingContext, file *hclparse.File, includeFromChild if err := file.Decode(&tgFlags, evalParsingContext); err != nil { return nil, err } + // validate flags to have default value, collect errors + flagErrs := &errors.MultiError{} + + for _, flag := range tgFlags.FeatureFlags { + if flag.Default == nil { + flagErr := fmt.Errorf("feature flag %s does not have a default value in %s", flag.Name, file.ConfigPath) + flagErrs = flagErrs.Append(flagErr) + } + } + + if flagErrs.ErrorOrNil() != nil { + return nil, flagErrs + } flagsAsCtyVal, err := flagsAsCty(ctx, tgFlags.FeatureFlags) if err != nil { diff --git a/docs/_docs/04_reference/config-blocks-and-attributes.md b/docs/_docs/04_reference/config-blocks-and-attributes.md index bbc66823f2..0cba84c9f6 100644 --- a/docs/_docs/04_reference/config-blocks-and-attributes.md +++ b/docs/_docs/04_reference/config-blocks-and-attributes.md @@ -1173,8 +1173,9 @@ More details in [engine section](https://terragrunt.gruntwork.io/docs/features/e ### feature -The `feature` block is used to configure feature flags in HCL for a particular Terragrunt Unit. -Feature flags can have default values, and they can be set through a CLI flag (`--feature`) or an environment variable (`TERRAGRUNT_FEATURE`). +The `feature` block is used to configure feature flags in HCL for a specific Terragrunt Unit. +Each feature flag must include a default value; failing to specify a default value will result in an error. +Feature flags can be overridden via a CLI flag (`--feature`) or an environment variable (`TERRAGRUNT_FEATURE`). ```hcl feature "string_flag" { diff --git a/test/fixtures/feature-flags/error-empty-flag/main.tf b/test/fixtures/feature-flags/error-empty-flag/main.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/feature-flags/error-empty-flag/terragrunt.hcl b/test/fixtures/feature-flags/error-empty-flag/terragrunt.hcl new file mode 100644 index 0000000000..24bfa5e959 --- /dev/null +++ b/test/fixtures/feature-flags/error-empty-flag/terragrunt.hcl @@ -0,0 +1,5 @@ +feature "test1" {} + +feature "test2" {} + +feature "test3" {} \ No newline at end of file diff --git a/test/integration_feature_flags_test.go b/test/integration_feature_flags_test.go index f41a48760b..f78efbe410 100644 --- a/test/integration_feature_flags_test.go +++ b/test/integration_feature_flags_test.go @@ -13,9 +13,10 @@ import ( ) const ( - testSimpleFlag = "fixtures/feature-flags/simple-flag" - testIncludeFlag = "fixtures/feature-flags/include-flag" - testRunAllFlag = "fixtures/feature-flags/run-all" + testSimpleFlag = "fixtures/feature-flags/simple-flag" + testIncludeFlag = "fixtures/feature-flags/include-flag" + testRunAllFlag = "fixtures/feature-flags/run-all" + testErrorEmptyFlag = "fixtures/feature-flags/error-empty-flag" ) func TestFeatureFlagDefaults(t *testing.T) { @@ -105,6 +106,22 @@ func TestFeatureFlagRunAll(t *testing.T) { validateOutputs(t, app2) } +func TestFailOnEmptyFeatureFlag(t *testing.T) { + t.Parallel() + + cleanupTerraformFolder(t, testErrorEmptyFlag) + tmpEnvPath := helpers.CopyEnvironment(t, testErrorEmptyFlag) + rootPath := util.JoinPath(tmpEnvPath, testErrorEmptyFlag) + + _, _, err := helpers.RunTerragruntCommandWithOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+rootPath) + require.Error(t, err) + + message := err.Error() + assert.Contains(t, message, "feature flag test1 does not have a default value") + assert.Contains(t, message, "feature flag test2 does not have a default value") + assert.Contains(t, message, "feature flag test3 does not have a default value") +} + func expectedDefaults() map[string]interface{} { return map[string]interface{}{ "string_feature_flag": "test",