Skip to content

Commit c45e788

Browse files
authored
Merge pull request #1622 from gruntwork-io/feat/add-missing-terragrunt-commands
Add missing terragrunt commands: ValidateAll, RunAll, FormatAll, OutputListAll
2 parents 2ff7ab2 + efe547b commit c45e788

16 files changed

+588
-10
lines changed

modules/terragrunt/README.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,13 @@ options := &terragrunt.Options{
8585
### Non-Stack Commands
8686
Work with standard terragrunt configurations (dependencies via `dependency` blocks):
8787

88+
- `Init(t, options)` - Initialize configuration
8889
- `ApplyAll(t, options)` - Apply all modules with dependencies
8990
- `DestroyAll(t, options)` - Destroy all modules with dependencies
9091
- `PlanAllExitCode(t, options)` - Plan all and return exit code (0=no changes, 2=changes)
91-
- `Init(t, options)` - Initialize configuration
92+
- `ValidateAll(t, options)` - Validate all modules
93+
- `RunAll(t, options, command)` - Run any terraform command with --all flag
94+
- `FormatAll(t, options)` - Format all terragrunt.hcl files
9295

9396
### Stack Commands
9497
Work with `terragrunt.stack.hcl` configurations:
@@ -99,6 +102,7 @@ Work with `terragrunt.stack.hcl` configurations:
99102
- `Output(t, options, key)` - Get stack output value
100103
- `OutputJson(t, options, key)` - Get stack output as JSON
101104
- `OutputAll(t, options)` - Get all stack outputs as map
105+
- `OutputListAll(t, options)` - Get list of all output variable names
102106

103107
## Examples
104108

@@ -180,6 +184,47 @@ func TestInfrastructureUpToDate(t *testing.T) {
180184
}
181185
```
182186

187+
### Using RunAll for Flexibility
188+
189+
```go
190+
func TestCustomCommand(t *testing.T) {
191+
t.Parallel()
192+
193+
options := &terragrunt.Options{
194+
TerragruntDir: "../modules",
195+
}
196+
197+
// Run any terraform command with --all
198+
terragrunt.RunAll(t, options, "refresh")
199+
200+
// Verify state is current
201+
output := terragrunt.RunAll(t, options, "show")
202+
assert.Contains(t, output, "expected-resource")
203+
}
204+
```
205+
206+
### Validating Output Keys
207+
208+
```go
209+
func TestOutputKeys(t *testing.T) {
210+
t.Parallel()
211+
212+
options := &terragrunt.Options{
213+
TerragruntDir: "../stack",
214+
}
215+
216+
terragrunt.ApplyAll(t, options)
217+
defer terragrunt.DestroyAll(t, options)
218+
219+
// Get list of all output keys
220+
keys := terragrunt.OutputListAll(t, options)
221+
222+
// Verify required outputs exist
223+
assert.Contains(t, keys, "vpc_id")
224+
assert.Contains(t, keys, "subnet_ids")
225+
}
226+
```
227+
183228
## Not Supported
184229

185230
This module does **NOT** support:

modules/terragrunt/apply_test.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,21 @@ import (
77
"github.com/stretchr/testify/require"
88
)
99

10-
func TestApplyAllNoError(t *testing.T) {
10+
func TestApplyAll(t *testing.T) {
11+
t.Parallel()
12+
13+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-no-error", t.Name())
14+
require.NoError(t, err)
15+
16+
out := ApplyAll(t, &Options{
17+
TerragruntDir: testFolder,
18+
TerragruntBinary: "terragrunt",
19+
})
20+
21+
require.Contains(t, out, "Hello, World")
22+
}
23+
24+
func TestApplyAllE(t *testing.T) {
1125
t.Parallel()
1226

1327
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-no-error", t.Name())
@@ -18,7 +32,7 @@ func TestApplyAllNoError(t *testing.T) {
1832
TerragruntBinary: "terragrunt",
1933
}
2034

21-
out := ApplyAll(t, options)
22-
35+
out, err := ApplyAllE(t, options)
36+
require.NoError(t, err)
2337
require.Contains(t, out, "Hello, World")
2438
}

modules/terragrunt/destroy_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"github.com/stretchr/testify/require"
88
)
99

10-
func TestDestroyAllNoError(t *testing.T) {
10+
func TestDestroyAll(t *testing.T) {
1111
t.Parallel()
1212

1313
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-no-error", t.Name())
@@ -18,11 +18,29 @@ func TestDestroyAllNoError(t *testing.T) {
1818
TerragruntBinary: "terragrunt",
1919
}
2020

21-
out := ApplyAll(t, options)
21+
ApplyAll(t, options)
22+
destroyOut := DestroyAll(t, options)
23+
require.NotEmpty(t, destroyOut)
24+
}
25+
26+
func TestDestroyAllE(t *testing.T) {
27+
t.Parallel()
28+
29+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-no-error", t.Name())
30+
require.NoError(t, err)
31+
32+
options := &Options{
33+
TerragruntDir: testFolder,
34+
TerragruntBinary: "terragrunt",
35+
}
36+
37+
out, err := ApplyAllE(t, options)
38+
require.NoError(t, err)
2239
require.Contains(t, out, "Hello, World")
2340

2441
// Test that destroy completes successfully
25-
destroyOut := DestroyAll(t, options)
42+
destroyOut, err := DestroyAllE(t, options)
43+
require.NoError(t, err)
2644
require.NotEmpty(t, destroyOut, "Destroy output should not be empty")
2745
}
2846

modules/terragrunt/format.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package terragrunt
2+
3+
import (
4+
"github.com/gruntwork-io/terratest/modules/testing"
5+
"github.com/stretchr/testify/require"
6+
)
7+
8+
// FormatAll runs terragrunt hclfmt to format all terragrunt.hcl files and returns stdout/stderr
9+
func FormatAll(t testing.TestingT, options *Options) string {
10+
out, err := FormatAllE(t, options)
11+
require.NoError(t, err)
12+
return out
13+
}
14+
15+
// FormatAllE runs terragrunt hclfmt to format all terragrunt.hcl files and returns stdout/stderr
16+
func FormatAllE(t testing.TestingT, options *Options) (string, error) {
17+
return runTerragruntCommandE(t, options, "hclfmt")
18+
}

modules/terragrunt/format_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package terragrunt
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/gruntwork-io/terratest/modules/files"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestFormatAll(t *testing.T) {
13+
t.Parallel()
14+
15+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-multi-plan", t.Name())
16+
require.NoError(t, err)
17+
18+
// Create an unformatted terragrunt.hcl file in foo directory
19+
unformattedContent := `terraform {
20+
source = "git::git@github.com:foo/modules.git//app"
21+
}
22+
inputs={
23+
foo="bar"
24+
}`
25+
tgFile := filepath.Join(testFolder, "foo", "terragrunt.hcl")
26+
err = os.WriteFile(tgFile, []byte(unformattedContent), 0644)
27+
require.NoError(t, err)
28+
29+
options := &Options{
30+
TerragruntDir: testFolder,
31+
TerragruntBinary: "terragrunt",
32+
}
33+
34+
out := FormatAll(t, options)
35+
require.NotEmpty(t, out)
36+
}
37+
38+
func TestFormatAllE(t *testing.T) {
39+
t.Parallel()
40+
41+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-multi-plan", t.Name())
42+
require.NoError(t, err)
43+
44+
options := &Options{
45+
TerragruntDir: testFolder,
46+
TerragruntBinary: "terragrunt",
47+
}
48+
49+
out, err := FormatAllE(t, options)
50+
require.NoError(t, err)
51+
require.NotEmpty(t, out)
52+
}

modules/terragrunt/init_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ func TestInit(t *testing.T) {
1515
"../../test/fixtures/terragrunt/terragrunt-no-error", t.Name())
1616
require.NoError(t, err)
1717

18+
out := Init(t, &Options{
19+
TerragruntDir: testFolder,
20+
TerragruntBinary: "terragrunt",
21+
TerraformArgs: []string{"-upgrade=true"},
22+
})
23+
require.Contains(t, out, "Terraform has been successfully initialized!")
24+
}
25+
26+
func TestInitE(t *testing.T) {
27+
t.Parallel()
28+
29+
testFolder, err := files.CopyTerraformFolderToTemp(
30+
"../../test/fixtures/terragrunt/terragrunt-no-error", t.Name())
31+
require.NoError(t, err)
32+
1833
out, err := InitE(t, &Options{
1934
TerragruntDir: testFolder,
2035
TerragruntBinary: "terragrunt",

modules/terragrunt/plan_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,23 @@ import (
88
"github.com/stretchr/testify/require"
99
)
1010

11-
func TestTgPlanAllNoError(t *testing.T) {
11+
func TestPlanAllExitCode(t *testing.T) {
12+
t.Parallel()
13+
14+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-multi-plan", t.Name())
15+
require.NoError(t, err)
16+
17+
options := &Options{
18+
TerragruntDir: testFolder,
19+
TerragruntBinary: "terragrunt",
20+
}
21+
22+
ApplyAll(t, options)
23+
exitCode := PlanAllExitCode(t, options)
24+
require.Equal(t, 0, exitCode)
25+
}
26+
27+
func TestPlanAllExitCodeE(t *testing.T) {
1228
t.Parallel()
1329

1430
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-multi-plan", t.Name())
@@ -34,7 +50,7 @@ func TestTgPlanAllNoError(t *testing.T) {
3450
require.Equal(t, 0, getExitCode)
3551
}
3652

37-
func TestTgPlanAllWithError(t *testing.T) {
53+
func TestPlanAllWithError(t *testing.T) {
3854
t.Parallel()
3955

4056
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-with-plan-error", t.Name())

modules/terragrunt/run_all.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package terragrunt
2+
3+
import (
4+
"github.com/gruntwork-io/terratest/modules/testing"
5+
"github.com/stretchr/testify/require"
6+
)
7+
8+
// RunAll runs terragrunt run-all <command> with the given options and returns stdout/stderr.
9+
// This is a generic wrapper that allows running any terraform command with --all flag.
10+
func RunAll(t testing.TestingT, options *Options, command string) string {
11+
out, err := RunAllE(t, options, command)
12+
require.NoError(t, err)
13+
return out
14+
}
15+
16+
// RunAllE runs terragrunt run-all <command> with the given options and returns stdout/stderr.
17+
// This is a generic wrapper that allows running any terraform command with --all flag.
18+
func RunAllE(t testing.TestingT, options *Options, command string) (string, error) {
19+
return runTerragruntCommandE(t, options, command, "--all")
20+
}

modules/terragrunt/run_all_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package terragrunt
2+
3+
import (
4+
"testing"
5+
6+
"github.com/gruntwork-io/terratest/modules/files"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestRunAll(t *testing.T) {
11+
t.Parallel()
12+
13+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-multi-plan", t.Name())
14+
require.NoError(t, err)
15+
16+
options := &Options{
17+
TerragruntDir: testFolder,
18+
TerragruntBinary: "terragrunt",
19+
}
20+
21+
// Test with validate command
22+
out := RunAll(t, options, "validate")
23+
require.NotEmpty(t, out)
24+
}
25+
26+
func TestRunAllE(t *testing.T) {
27+
t.Parallel()
28+
29+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-multi-plan", t.Name())
30+
require.NoError(t, err)
31+
32+
options := &Options{
33+
TerragruntDir: testFolder,
34+
TerragruntBinary: "terragrunt",
35+
}
36+
37+
// Test with validate command
38+
out, err := RunAllE(t, options, "validate")
39+
require.NoError(t, err)
40+
require.NotEmpty(t, out)
41+
}
42+
43+
func TestRunAllWithPlan(t *testing.T) {
44+
t.Parallel()
45+
46+
testFolder, err := files.CopyTerragruntFolderToTemp("../../test/fixtures/terragrunt/terragrunt-multi-plan", t.Name())
47+
require.NoError(t, err)
48+
49+
options := &Options{
50+
TerragruntDir: testFolder,
51+
TerragruntBinary: "terragrunt",
52+
}
53+
54+
// Test with plan command - verify output contains expected terraform plan text
55+
out, err := RunAllE(t, options, "plan")
56+
require.NoError(t, err)
57+
require.Contains(t, out, "Changes to Outputs")
58+
}

modules/terragrunt/stack_clean_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@ func TestStackClean(t *testing.T) {
1717

1818
stackDir := path.Join(testFolder, "live", ".terragrunt-stack")
1919

20+
StackGenerate(t, &Options{
21+
TerragruntDir: path.Join(testFolder, "live"),
22+
TerragruntBinary: "terragrunt",
23+
})
24+
25+
require.DirExists(t, stackDir)
26+
27+
out := StackClean(t, &Options{
28+
TerragruntDir: path.Join(testFolder, "live"),
29+
TerragruntBinary: "terragrunt",
30+
})
31+
32+
require.Contains(t, out, "Deleting stack directory")
33+
require.NoDirExists(t, stackDir)
34+
}
35+
36+
func TestStackCleanE(t *testing.T) {
37+
t.Parallel()
38+
39+
testFolder, err := files.CopyTerraformFolderToTemp(
40+
"../../test/fixtures/terragrunt/terragrunt-stack-init", t.Name())
41+
require.NoError(t, err)
42+
43+
stackDir := path.Join(testFolder, "live", ".terragrunt-stack")
44+
2045
// First generate the stack to create .terragrunt-stack directory
2146
_, err = StackGenerateE(t, &Options{
2247
TerragruntDir: path.Join(testFolder, "live"),

0 commit comments

Comments
 (0)