-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: Cleaning up #5232 #6009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Cleaning up #5232 #6009
Changes from all commits
12f057c
e50154d
1c74da0
8fa5b09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| --- | ||
| version: "v1.0.4" | ||
| category: "bug-fixes" | ||
| --- | ||
|
|
||
| #### Fixed `exclude` block being dropped when defined only in an included parent | ||
|
|
||
| A unit that pulled in an `exclude` block from an include that did not declare its own `exclude` block saw the include's `exclude` configurations ignored. | ||
|
|
||
| Included `exclude` blocks now get properly merged into unit configurations. | ||
|
|
||
| Reported in [#5089](https://github.com/gruntwork-io/terragrunt/issues/5089). Thanks to [@HeikoNeblung](https://github.com/HeikoNeblung) for contributing this fix! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,323 @@ | ||
| package config_test | ||
|
|
||
| import ( | ||
| "os" | ||
| "path/filepath" | ||
| "testing" | ||
|
|
||
| "github.com/gruntwork-io/terragrunt/pkg/config" | ||
| "github.com/gruntwork-io/terragrunt/test/helpers/logger" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // Regression test for https://github.com/gruntwork-io/terragrunt/issues/5089: | ||
| // an exclude block defined in an included parent config must survive into the | ||
| // child's merged config when the child does not define its own exclude block. | ||
| func TestParseConfig_InheritsExcludeFromIncludedConfig(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| includeBody string | ||
| }{ | ||
| { | ||
| name: "default merge", | ||
| includeBody: ``, | ||
| }, | ||
| { | ||
| name: "shallow merge", | ||
| includeBody: `merge_strategy = "shallow"`, | ||
| }, | ||
| { | ||
| name: "deep merge", | ||
| includeBody: `merge_strategy = "deep"`, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tmpDir := t.TempDir() | ||
|
|
||
| parentPath := filepath.Join(tmpDir, "root.hcl") | ||
| require.NoError(t, os.WriteFile(parentPath, []byte(` | ||
| exclude { | ||
| if = true | ||
| actions = ["plan", "apply"] | ||
| no_run = true | ||
| } | ||
| `), 0644)) | ||
|
|
||
| childDir := filepath.Join(tmpDir, "unit") | ||
| require.NoError(t, os.MkdirAll(childDir, 0755)) | ||
|
|
||
| childPath := filepath.Join(childDir, config.DefaultTerragruntConfigPath) | ||
| require.NoError(t, os.WriteFile(childPath, []byte(` | ||
| include "root" { | ||
| path = "`+parentPath+`" | ||
| `+tt.includeBody+` | ||
|
Comment on lines
+59
to
+60
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n 'path = "`\+parentPath\+`"' pkg/config/exclude_include_test.go
rg -n 'ToSlash\(parentPath\)' pkg/config/exclude_include_test.goRepository: gruntwork-io/terragrunt Length of output: 216 🏁 Script executed: # View the actual test code sections with interpolations
sed -n '55,65p' pkg/config/exclude_include_test.go
sed -n '120,130p' pkg/config/exclude_include_test.go
sed -n '190,200p' pkg/config/exclude_include_test.go
# Check if filepath.ToSlash is used elsewhere in the codebase
rg 'filepath\.ToSlash' --type go
rg 'ToSlash.*path' --type go
# Check for backslash handling patterns in Go files
rg '\\\\' pkg/config/exclude_include_test.goRepository: gruntwork-io/terragrunt Length of output: 3450 🏁 Script executed: # Check context around the config_helpers.go comment about filepath.ToSlash
sed -n '1,50p' pkg/config/config_helpers.go | tail -30
# Look for any Windows-specific test handling or bug reports
rg -i 'windows|backslash|escape.*path' pkg/config/exclude_include_test.go
# Check if this test file has any platform-specific build tags
head -20 pkg/config/exclude_include_test.go
# Search for HCL parsing logic to understand escape handling
rg 'hcl.*parse|hclparse' --type go -A 3 | head -40
# Check if there are any existing issues or TODOs about path escaping
rg -i 'TODO|FIXME|BUG.*path|path.*escape' pkg/config/exclude_include_test.goRepository: gruntwork-io/terragrunt Length of output: 4416 🏁 Script executed: # Look at the full test context to understand what's being tested
sed -n '38,70p' pkg/config/exclude_include_test.go
# Check the hclparse package to understand how it handles paths
find . -name "hclparse*" -type f | grep -E '\.(go|mod)$'
cat pkg/config/hclparse/parser.go | head -50
# Verify how the include path is actually used in config parsing
rg -A 5 'include.*path.*parentPath' pkg/config/exclude_include_test.go | head -20
# Check if there are any Windows-specific test failures or known issues
rg -i 'windows|os\.sep' pkg/config/exclude_include_test.goRepository: gruntwork-io/terragrunt Length of output: 2360 🏁 Script executed: # Check if this test file has been run on Windows or if there are any Windows-specific issues
rg -i 'windows|skip.*windows|build.*windows' pkg/config/exclude_include_test.go
# Look at how HCL2 parser handles escape sequences in strings
# Check if there's any documentation or known issues
rg -i 'escape|backslash' pkg/config/hclparse/
# Check the actual go-cty/hcl/v2 parsing behavior by looking at similar cases in the codebase
rg -B 3 -A 3 'filepath.ToSlash.*hcl|include.*path.*ToSlash' --type go | head -30
# Verify if the include path parsing actually uses the string as-is or processes it
sed -n '500,600p' pkg/config/config.go | grep -A 10 -B 10 'include.*path'Repository: gruntwork-io/terragrunt Length of output: 456 🏁 Script executed: # Check if tests have Windows build tags or are skipped on Windows
head -30 pkg/config/exclude_include_test.go | grep -i "build\|skip\|windows"
# Check what the actual parsed result looks like by examining the ParseConfigFile function
rg -A 10 'func ParseConfigFile' pkg/config/ | head -30
# Look for any existing issues or PRs related to Windows path handling
rg -i 'issue.*path|github.*windows.*path' pkg/config/ | head -10
# Check if filepath.ToSlash is conditionally applied or always applied in similar contexts
rg -B 5 'filepath.ToSlash.*path' pkg/config/config_helpers.go | head -20Repository: gruntwork-io/terragrunt Length of output: 539 🏁 Script executed: # Check how the include path attribute is processed after HCL parsing
rg -A 20 'include.*config.*path' pkg/config/config.go | head -40
# Look for the actual attribute parsing of include paths
rg -B 5 -A 10 'func.*parseInclude|processInclude' pkg/config/ --type go | head -50
# Check if there's any string unquoting or processing of the path value
rg 'unquote|quote.*path|path.*quote' pkg/config/ --type go
# Verify if the issue is actually triggered in practice - check for any test failures or issues
find . -name "*.md" -type f -exec grep -l "Windows.*path\|path.*Windows" {} \; | head -5Repository: gruntwork-io/terragrunt Length of output: 1504 🏁 Script executed: # Check if the tests are platform-specific or have skip conditions
grep -n "skip\|build.*windows\|GOOS" pkg/config/exclude_include_test.go
# Look at how HCL2 library handles string escaping
# Check if there are comments in the code about this
rg -i 'hcl.*escape|escape.*hcl|string.*escape' pkg/config/ -A 2 | head -20
# Check if the issue has been reported in the codebase
rg 'windows.*path|path.*windows|backslash.*hcl' -i . --type md --type go | head -10
# Try to understand the actual behavior by looking at a real example
# Find if there are any error messages or tests that validate path handling
rg 'path.*escape|escape.*path' --type go | head -10
# Check if there's any documentation on how HCL2 handles paths
rg 'hashicorp/hcl' pkg/config/hclparse/ -A 5 | head -20Repository: gruntwork-io/terragrunt Length of output: 3277 🏁 Script executed: # Look at the full context of the config_helpers.go comment about filepath.ToSlash and escapes
sed -n '200,250p' pkg/config/config_helpers.go
# Check the specific issue about HCL2 parsing with backslashes
# Look for how include paths are actually resolved
sed -n '1,100p' pkg/config/include.go
# Verify if the parsed include path value is used as a filesystem path or as a string
rg -A 15 'includePath :=' pkg/config/include.go | head -25
# Check if there are any test failures or issues reported about Windows
find . -name "*.go" -type f -exec grep -l 'exclude_include\|exclude.*include' {} \; | xargs grep -l windows
# Check the actual HCL2 specification or how paths are handled
rg 'path.*=.*"' pkg/config/exclude_include_test.go | head -5Repository: gruntwork-io/terragrunt Length of output: 8131 🏁 Script executed: # Check if the test has any build constraints that skip it on Windows
head -40 pkg/config/exclude_include_test.go | grep -E "^//go:build|^// +build"
# Check if the tests actually run on Windows in CI
find . -name "*.yml" -o -name "*.yaml" | xargs grep -l "windows" | head -5
# Look at how the include path is actually parsed by the HCL parser
# The key is whether the string value is unquoted and processed
rg -B 10 -A 10 'DecodeAsString.*includePath|getStringAttributeValue.*include' pkg/config/ --type go | head -50
# Check the actual attribute decoding logic
rg 'DecodeAsString|getAttribute.*include' pkg/config/ -B 3 -A 3 | head -40Repository: gruntwork-io/terragrunt Length of output: 260 🏁 Script executed: # Check the integration test workflow to see if Windows is tested
grep -A 20 "windows" ./.github/workflows/integration-test.yml | head -30
# Check if the exclude_include_test is included in Windows tests
grep -i "exclude_include" ./.github/workflows/*.yml
# Look at the actual include attribute parsing code
rg -B 5 -A 15 'func.*decodeInclude|func.*parseInclude' pkg/config/ --type go | head -60
# Check how string attributes are decoded from HCL
rg -B 3 -A 10 'getStringAttribute|DecodeAsString' pkg/config/include.go | head -40Repository: gruntwork-io/terragrunt Length of output: 2656 🏁 Script executed: # Check if the exclude_include_test matches the Windows test pattern in CI
grep -E "TestParseConfig.*Exclude|TestParseConfig.*Include" pkg/config/exclude_include_test.go | head -5
# Check what the Windows test pattern actually matches
grep -B 5 -A 5 "TestWindows" .github/workflows/integration-test.yml | head -20
# Look for any HCL string escape handling or unquoting logic
rg 'unquote|strconv.*Unquote|cty.*String' pkg/config/hclparse/ --type go -B 2 -A 5 | head -50
# Check if there's any decoding of HCL attributes that handles path values
rg -A 20 'func.*decodeIncludeAttributes|func.*decodeIncludeBlock' pkg/config/ --type go | head -60Repository: gruntwork-io/terragrunt Length of output: 820 🏁 Script executed: # The key test functions don't start with "TestWindows", so they don't run on Windows CI
# Let's verify this by checking what pattern Windows tests actually use
grep -i "TestWindows\|windows" pkg/config/exclude_include_test.go
# Check if there's a general parsing test or config test that runs on Windows
find pkg/config -name "*test.go" -type f -exec grep -l "TestWindows" {} \;
# Now let's understand how HCL2 actually handles quoted strings with backslashes
# by looking at real usage patterns in the codebase
rg -B 3 -A 3 'include.*path.*=' pkg/config/config.go | head -30
# Check if the include path string is actually unquoted by the HCL parser
# Look for how cty values are converted to strings
rg 'asString|cty.*String|gocty.*FromCtyValue' pkg/config/ --type go | head -20Repository: gruntwork-io/terragrunt Length of output: 1920 🏁 Script executed: # Find where the include block's path attribute is actually decoded
rg -B 5 -A 15 'decodeIncludeBlock\|parseIncludeAttribute' pkg/config/ --type go | head -80
# Look for the actual function that extracts the path value from HCL attributes
rg 'func.*Include.*decode\|func.*decodeInclude' pkg/config/ --type go -A 20 | head -60
# Check how attributes are extracted from HCL in this file
rg -B 2 -A 10 'includeBlock\|include.*block' pkg/config/include.go --type go | head -50Repository: gruntwork-io/terragrunt Length of output: 49 🏁 Script executed: # Find the actual include block parsing in config.go
sed -n '1,200p' pkg/config/config.go | grep -A 30 -B 5 'include'
# Look specifically for how include attributes are decoded
rg -B 10 -A 20 'func decodeIncludeAttribute\|decodeIncludeBlock\|parseIncludeBlock' pkg/config/ --type go
# Check the actual include.go file for attribute parsing
head -200 pkg/config/include.go | tail -100
# Search for where the path attribute value is actually extracted and used
rg 'includedConfig.Path\s*=' pkg/config/ --type go -B 3 -A 3 | head -40Repository: gruntwork-io/terragrunt Length of output: 6388 🏁 Script executed: # Look for where the include attributes are actually decoded from HCL
rg -B 5 -A 20 'gohcl.*Decode\|DecodeBody.*include' pkg/config/ --type go | head -80
# Check the IncludeConfig struct definition
rg -B 3 -A 10 'type IncludeConfig struct' pkg/config/ --type go
# Look for how the path attribute is decoded
rg -B 5 -A 15 'Path.*string\|ConfigPath.*string' pkg/config/ --type go | grep -A 10 -B 5 "IncludeConfig\|Include struct"
# Check the actual attribute decoding in config.go
sed -n '600,900p' pkg/config/config.go | grep -B 10 -A 10 -i 'include\|path'Repository: gruntwork-io/terragrunt Length of output: 7710 🏁 Script executed: # Look for where IncludeConfig is actually decoded from HCL
rg -B 10 -A 10 'gohcl.*Decode.*include\|Decode.*terragruntInclude' pkg/config/ --type go | head -60
# Check if there's any code that handles the include blocks decoding
rg -B 5 -A 15 'decodeLocalsAndInclude\|parseInclude' pkg/config/ --type go | head -100
# Look at the actual HCL2 gohcl package usage
rg 'gohcl.DecodeBody' pkg/config/ --type go -B 3 -A 5Repository: gruntwork-io/terragrunt Length of output: 479 Escape interpolated include paths for Windows-safe HCL fixtures. These fixtures embed 💡 Proposed fix (apply to each test block) parentPath := filepath.Join(tmpDir, "root.hcl")
+parentPathHCL := filepath.ToSlash(parentPath)
require.NoError(t, os.WriteFile(parentPath, []byte(`
exclude {
if = true
@@
include "root" {
- path = "`+parentPath+`"
+ path = "`+parentPathHCL+`"
`+tt.includeBody+`
}
`), 0644))Applies to lines: 59-60, 125-126, 195-196, 250-251, 304-305 🤖 Prompt for AI Agents |
||
| } | ||
| `), 0644)) | ||
|
|
||
| ctx, pctx := newTestParsingContext(t, childPath) | ||
|
|
||
| l := logger.CreateLogger() | ||
|
|
||
| parsed, err := config.ParseConfigFile(ctx, pctx, l, childPath, nil) | ||
| require.NoError(t, err) | ||
| require.NotNil(t, parsed) | ||
|
|
||
| require.NotNil(t, parsed.Exclude, "expected exclude block to be inherited from included parent") | ||
| assert.True(t, parsed.Exclude.If) | ||
| assert.Equal(t, []string{"plan", "apply"}, parsed.Exclude.Actions) | ||
| require.NotNil(t, parsed.Exclude.NoRun) | ||
| assert.True(t, *parsed.Exclude.NoRun) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // Child-defined exclude blocks must still take precedence over the included | ||
| // parent's exclude block, regardless of merge strategy. | ||
| func TestParseConfig_ChildExcludeOverridesIncludedConfig(t *testing.T) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense to add a test for child with |
||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| includeBody string | ||
| }{ | ||
| { | ||
| name: "default merge", | ||
| includeBody: ``, | ||
| }, | ||
| { | ||
| name: "shallow merge", | ||
| includeBody: `merge_strategy = "shallow"`, | ||
| }, | ||
| { | ||
| name: "deep merge", | ||
| includeBody: `merge_strategy = "deep"`, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tmpDir := t.TempDir() | ||
|
|
||
| parentPath := filepath.Join(tmpDir, "root.hcl") | ||
| require.NoError(t, os.WriteFile(parentPath, []byte(` | ||
| exclude { | ||
| if = true | ||
| actions = ["plan"] | ||
| no_run = false | ||
| } | ||
| `), 0644)) | ||
|
|
||
| childDir := filepath.Join(tmpDir, "unit") | ||
| require.NoError(t, os.MkdirAll(childDir, 0755)) | ||
|
|
||
| childPath := filepath.Join(childDir, config.DefaultTerragruntConfigPath) | ||
| require.NoError(t, os.WriteFile(childPath, []byte(` | ||
| include "root" { | ||
| path = "`+parentPath+`" | ||
| `+tt.includeBody+` | ||
| } | ||
|
|
||
| exclude { | ||
| if = true | ||
| actions = ["destroy"] | ||
| no_run = true | ||
| } | ||
| `), 0644)) | ||
|
|
||
| ctx, pctx := newTestParsingContext(t, childPath) | ||
|
|
||
| l := logger.CreateLogger() | ||
|
|
||
| parsed, err := config.ParseConfigFile(ctx, pctx, l, childPath, nil) | ||
| require.NoError(t, err) | ||
| require.NotNil(t, parsed) | ||
|
|
||
| require.NotNil(t, parsed.Exclude) | ||
| assert.Equal(t, []string{"destroy"}, parsed.Exclude.Actions) | ||
| require.NotNil(t, parsed.Exclude.NoRun) | ||
| assert.True(t, *parsed.Exclude.NoRun) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // Regression coverage for sibling top-level blocks that flow through the same | ||
| // include-merge code path as exclude. These are not affected by the 5089 bug, | ||
| // but pinning the inheritance behavior keeps a future post-merge override from | ||
| // silently regressing them the way it did for exclude. | ||
| // | ||
| // See pkg/config/include.go's Merge / DeepMerge for the sites these tests | ||
| // exercise. | ||
|
|
||
| func TestParseConfig_InheritsErrorsFromIncludedConfig(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| includeBody string | ||
| }{ | ||
| {name: "default merge", includeBody: ``}, | ||
| {name: "shallow merge", includeBody: `merge_strategy = "shallow"`}, | ||
| {name: "deep merge", includeBody: `merge_strategy = "deep"`}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tmpDir := t.TempDir() | ||
|
|
||
| parentPath := filepath.Join(tmpDir, "root.hcl") | ||
| require.NoError(t, os.WriteFile(parentPath, []byte(` | ||
| errors { | ||
| retry "transient" { | ||
| retryable_errors = ["(?s).*timeout.*"] | ||
| max_attempts = 3 | ||
| sleep_interval_sec = 5 | ||
| } | ||
| } | ||
| `), 0644)) | ||
|
|
||
| childDir := filepath.Join(tmpDir, "unit") | ||
| require.NoError(t, os.MkdirAll(childDir, 0755)) | ||
|
|
||
| childPath := filepath.Join(childDir, config.DefaultTerragruntConfigPath) | ||
| require.NoError(t, os.WriteFile(childPath, []byte(` | ||
| include "root" { | ||
| path = "`+parentPath+`" | ||
| `+tt.includeBody+` | ||
| } | ||
| `), 0644)) | ||
|
|
||
| ctx, pctx := newTestParsingContext(t, childPath) | ||
|
|
||
| l := logger.CreateLogger() | ||
|
|
||
| parsed, err := config.ParseConfigFile(ctx, pctx, l, childPath, nil) | ||
| require.NoError(t, err) | ||
| require.NotNil(t, parsed) | ||
|
|
||
| require.NotNil(t, parsed.Errors, "expected errors block to be inherited from included parent") | ||
| require.Len(t, parsed.Errors.Retry, 1) | ||
| assert.Equal(t, "transient", parsed.Errors.Retry[0].Label) | ||
| assert.Equal(t, 3, parsed.Errors.Retry[0].MaxAttempts) | ||
| assert.Equal(t, 5, parsed.Errors.Retry[0].SleepIntervalSec) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestParseConfig_InheritsEngineFromIncludedConfig(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| includeBody string | ||
| }{ | ||
| {name: "default merge", includeBody: ``}, | ||
| {name: "shallow merge", includeBody: `merge_strategy = "shallow"`}, | ||
| {name: "deep merge", includeBody: `merge_strategy = "deep"`}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tmpDir := t.TempDir() | ||
|
|
||
| parentPath := filepath.Join(tmpDir, "root.hcl") | ||
| require.NoError(t, os.WriteFile(parentPath, []byte(` | ||
| engine { | ||
| source = "engine-from-parent" | ||
| version = "1.2.3" | ||
| type = "rpc" | ||
| } | ||
| `), 0644)) | ||
|
|
||
| childDir := filepath.Join(tmpDir, "unit") | ||
| require.NoError(t, os.MkdirAll(childDir, 0755)) | ||
|
|
||
| childPath := filepath.Join(childDir, config.DefaultTerragruntConfigPath) | ||
| require.NoError(t, os.WriteFile(childPath, []byte(` | ||
| include "root" { | ||
| path = "`+parentPath+`" | ||
| `+tt.includeBody+` | ||
| } | ||
| `), 0644)) | ||
|
|
||
| ctx, pctx := newTestParsingContext(t, childPath) | ||
|
|
||
| l := logger.CreateLogger() | ||
|
|
||
| parsed, err := config.ParseConfigFile(ctx, pctx, l, childPath, nil) | ||
| require.NoError(t, err) | ||
| require.NotNil(t, parsed) | ||
|
|
||
| require.NotNil(t, parsed.Engine, "expected engine block to be inherited from included parent") | ||
| assert.Equal(t, "engine-from-parent", parsed.Engine.Source) | ||
| require.NotNil(t, parsed.Engine.Version) | ||
| assert.Equal(t, "1.2.3", *parsed.Engine.Version) | ||
| require.NotNil(t, parsed.Engine.Type) | ||
| assert.Equal(t, "rpc", *parsed.Engine.Type) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestParseConfig_InheritsFeatureFlagsFromIncludedConfig(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| includeBody string | ||
| }{ | ||
| {name: "default merge", includeBody: ``}, | ||
| {name: "shallow merge", includeBody: `merge_strategy = "shallow"`}, | ||
| {name: "deep merge", includeBody: `merge_strategy = "deep"`}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tmpDir := t.TempDir() | ||
|
|
||
| parentPath := filepath.Join(tmpDir, "root.hcl") | ||
| require.NoError(t, os.WriteFile(parentPath, []byte(` | ||
| feature "from_parent" { | ||
| default = "parent_value" | ||
| } | ||
| `), 0644)) | ||
|
|
||
| childDir := filepath.Join(tmpDir, "unit") | ||
| require.NoError(t, os.MkdirAll(childDir, 0755)) | ||
|
|
||
| childPath := filepath.Join(childDir, config.DefaultTerragruntConfigPath) | ||
| require.NoError(t, os.WriteFile(childPath, []byte(` | ||
| include "root" { | ||
| path = "`+parentPath+`" | ||
| `+tt.includeBody+` | ||
| } | ||
| `), 0644)) | ||
|
|
||
| ctx, pctx := newTestParsingContext(t, childPath) | ||
|
|
||
| l := logger.CreateLogger() | ||
|
|
||
| parsed, err := config.ParseConfigFile(ctx, pctx, l, childPath, nil) | ||
| require.NoError(t, err) | ||
| require.NotNil(t, parsed) | ||
|
|
||
| require.Len(t, parsed.FeatureFlags, 1, "expected feature flag to be inherited from included parent") | ||
| assert.Equal(t, "from_parent", parsed.FeatureFlags[0].Name) | ||
| require.NotNil(t, parsed.FeatureFlags[0].Default) | ||
| assert.Equal(t, "parent_value", parsed.FeatureFlags[0].Default.AsString()) | ||
| }) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test for empty
exclude {}block on child, it may trigger different nil/zero errors