Skip to content

Commit e50154d

Browse files
committed
fix: Adding some testing that wasn't addressed in #5232
1 parent 12f057c commit e50154d

2 files changed

Lines changed: 152 additions & 1 deletion

File tree

pkg/config/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1448,9 +1448,10 @@ func ParseConfig(
14481448
// - Locals are deliberately not merged in so that they remain local in scope. Here, we directly set it to the
14491449
// original locals for the current config being handled, as that is the locals list that is in scope for this
14501450
// config.
1451+
// - Exclude, in contrast, is inherited from included configs. Only override the merged value when the current
1452+
// config defines its own exclude block, otherwise the parent's exclude would be clobbered with nil.
14511453
mergedConfig.Locals = config.Locals
14521454

1453-
// preserve included Exclude config when no Exclude config is present in current config
14541455
if config.Exclude != nil {
14551456
mergedConfig.Exclude = config.Exclude
14561457
}

pkg/config/exclude_include_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package config_test
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/gruntwork-io/terragrunt/pkg/config"
9+
"github.com/gruntwork-io/terragrunt/test/helpers/logger"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
// Regression test for https://github.com/gruntwork-io/terragrunt/issues/5089:
15+
// an exclude block defined in an included parent config must survive into the
16+
// child's merged config when the child does not define its own exclude block.
17+
func TestParseConfig_InheritsExcludeFromIncludedConfig(t *testing.T) {
18+
t.Parallel()
19+
20+
tests := []struct {
21+
name string
22+
includeBody string
23+
}{
24+
{
25+
name: "default merge",
26+
includeBody: ``,
27+
},
28+
{
29+
name: "shallow merge",
30+
includeBody: `merge_strategy = "shallow"`,
31+
},
32+
{
33+
name: "deep merge",
34+
includeBody: `merge_strategy = "deep"`,
35+
},
36+
}
37+
38+
for _, tt := range tests {
39+
t.Run(tt.name, func(t *testing.T) {
40+
t.Parallel()
41+
42+
tmpDir := t.TempDir()
43+
44+
parentPath := filepath.Join(tmpDir, "root.hcl")
45+
require.NoError(t, os.WriteFile(parentPath, []byte(`
46+
exclude {
47+
if = true
48+
actions = ["plan", "apply"]
49+
no_run = true
50+
}
51+
`), 0644))
52+
53+
childDir := filepath.Join(tmpDir, "unit")
54+
require.NoError(t, os.MkdirAll(childDir, 0755))
55+
56+
childPath := filepath.Join(childDir, config.DefaultTerragruntConfigPath)
57+
require.NoError(t, os.WriteFile(childPath, []byte(`
58+
include "root" {
59+
path = "`+parentPath+`"
60+
`+tt.includeBody+`
61+
}
62+
`), 0644))
63+
64+
ctx, pctx := newTestParsingContext(t, childPath)
65+
66+
l := logger.CreateLogger()
67+
68+
parsed, err := config.ParseConfigFile(ctx, pctx, l, childPath, nil)
69+
require.NoError(t, err)
70+
require.NotNil(t, parsed)
71+
72+
require.NotNil(t, parsed.Exclude, "expected exclude block to be inherited from included parent")
73+
assert.True(t, parsed.Exclude.If)
74+
assert.Equal(t, []string{"plan", "apply"}, parsed.Exclude.Actions)
75+
require.NotNil(t, parsed.Exclude.NoRun)
76+
assert.True(t, *parsed.Exclude.NoRun)
77+
})
78+
}
79+
}
80+
81+
// Child-defined exclude blocks must still take precedence over the included
82+
// parent's exclude block, regardless of merge strategy.
83+
func TestParseConfig_ChildExcludeOverridesIncludedConfig(t *testing.T) {
84+
t.Parallel()
85+
86+
tests := []struct {
87+
name string
88+
includeBody string
89+
}{
90+
{
91+
name: "default merge",
92+
includeBody: ``,
93+
},
94+
{
95+
name: "shallow merge",
96+
includeBody: `merge_strategy = "shallow"`,
97+
},
98+
{
99+
name: "deep merge",
100+
includeBody: `merge_strategy = "deep"`,
101+
},
102+
}
103+
104+
for _, tt := range tests {
105+
t.Run(tt.name, func(t *testing.T) {
106+
t.Parallel()
107+
108+
tmpDir := t.TempDir()
109+
110+
parentPath := filepath.Join(tmpDir, "root.hcl")
111+
require.NoError(t, os.WriteFile(parentPath, []byte(`
112+
exclude {
113+
if = true
114+
actions = ["plan"]
115+
no_run = false
116+
}
117+
`), 0644))
118+
119+
childDir := filepath.Join(tmpDir, "unit")
120+
require.NoError(t, os.MkdirAll(childDir, 0755))
121+
122+
childPath := filepath.Join(childDir, config.DefaultTerragruntConfigPath)
123+
require.NoError(t, os.WriteFile(childPath, []byte(`
124+
include "root" {
125+
path = "`+parentPath+`"
126+
`+tt.includeBody+`
127+
}
128+
129+
exclude {
130+
if = true
131+
actions = ["destroy"]
132+
no_run = true
133+
}
134+
`), 0644))
135+
136+
ctx, pctx := newTestParsingContext(t, childPath)
137+
138+
l := logger.CreateLogger()
139+
140+
parsed, err := config.ParseConfigFile(ctx, pctx, l, childPath, nil)
141+
require.NoError(t, err)
142+
require.NotNil(t, parsed)
143+
144+
require.NotNil(t, parsed.Exclude)
145+
assert.Equal(t, []string{"destroy"}, parsed.Exclude.Actions)
146+
require.NotNil(t, parsed.Exclude.NoRun)
147+
assert.True(t, *parsed.Exclude.NoRun)
148+
})
149+
}
150+
}

0 commit comments

Comments
 (0)