diff --git a/config/config_partial.go b/config/config_partial.go index c3d477a35..6fc5527f4 100644 --- a/config/config_partial.go +++ b/config/config_partial.go @@ -5,6 +5,8 @@ import ( "os" "path/filepath" + "github.com/gruntwork-io/terragrunt/internal/strict" + clone "github.com/huandu/go-clone" "github.com/gruntwork-io/terragrunt/internal/cache" @@ -333,6 +335,16 @@ func PartialParseConfig(ctx *ParsingContext, file *hclparse.File, includeFromChi output.IamWebIdentityToken = *decoded.IamWebIdentityToken } case TerragruntInputs: + control, ok := strict.GetStrictControl(strict.SkipDependenciesInputs) + if ok { + _, skipInputs := control.Evaluate(ctx.TerragruntOptions) + if skipInputs != nil { + ctx.TerragruntOptions.Logger.Warnf("Skipping inputs reading from %v inputs for better performance", file.ConfigPath) + + break + } + } + decoded := terragruntInputs{} if _, ok := evalParsingContext.Variables[MetadataDependency]; !ok { diff --git a/docs/_docs/04_reference/strict-mode.md b/docs/_docs/04_reference/strict-mode.md index b949f3e8d..330036a1d 100644 --- a/docs/_docs/04_reference/strict-mode.md +++ b/docs/_docs/04_reference/strict-mode.md @@ -72,6 +72,7 @@ The following strict mode controls are available: - [destroy-all](#destroy-all) - [output-all](#output-all) - [validate-all](#validate-all) +- [skip-dependencies-inputs](#skip-dependencies-inputs) ### spin-up @@ -114,3 +115,9 @@ Throw an error when using the `output-all` command. Throw an error when using the `validate-all` command. **Reason**: The `validate-all` command is deprecated and will be removed in a future version. Use `terragrunt run-all validate` instead. + +### skip-dependencies-inputs + +Disable reading of dependency inputs to enhance dependency resolution performance by preventing recursively parsing Terragrunt inputs from dependencies. + +**Reason**: Enabling the `skip-dependencies-inputs` option prevents the recursive parsing of Terragrunt inputs, leading to optimized performance during dependency resolution. diff --git a/internal/strict/strict.go b/internal/strict/strict.go index 8038a8ae9..3121589d6 100644 --- a/internal/strict/strict.go +++ b/internal/strict/strict.go @@ -8,10 +8,11 @@ package strict import ( - "errors" "fmt" "strings" + "github.com/gruntwork-io/terragrunt/internal/errors" + "github.com/gruntwork-io/terragrunt/options" "github.com/gruntwork-io/terragrunt/util" ) @@ -40,6 +41,9 @@ const ( OutputAll = "output-all" // ValidateAll is the control that prevents the deprecated `validate-all` command from being used. ValidateAll = "validate-all" + + // SkipDependenciesInputs is the control that prevents reading dependencies inputs and get performance boost. + SkipDependenciesInputs = "skip-dependencies-inputs" ) // GetStrictControl returns the strict control with the given name. @@ -76,32 +80,36 @@ type Controls map[string]Control //nolint:lll,gochecknoglobals,stylecheck var StrictControls = Controls{ SpinUp: { - Error: errors.New("The `spin-up` command is no longer supported. Use `terragrunt run-all apply` instead."), - Warning: "The `spin-up` command is deprecated and will be removed in a future version. Use `terragrunt run-all apply` instead.", + Error: errors.Errorf("The `%s` command is no longer supported. Use `terragrunt run-all apply` instead.", SpinUp), + Warning: fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `terragrunt run-all apply` instead.", SpinUp), }, TearDown: { - Error: errors.New("The `tear-down` command is no longer supported. Use `terragrunt run-all destroy` instead."), - Warning: "The `tear-down` command is deprecated and will be removed in a future version. Use `terragrunt run-all destroy` instead.", + Error: errors.Errorf("The `%s` command is no longer supported. Use `terragrunt run-all destroy` instead.", TearDown), + Warning: fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `terragrunt run-all destroy` instead.", TearDown), }, PlanAll: { - Error: errors.New("The `plan-all` command is no longer supported. Use `terragrunt run-all plan` instead."), - Warning: "The `plan-all` command is deprecated and will be removed in a future version. Use `terragrunt run-all plan` instead.", + Error: errors.Errorf("The `%s` command is no longer supported. Use `terragrunt run-all plan` instead.", PlanAll), + Warning: fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `terragrunt run-all plan` instead.", PlanAll), }, ApplyAll: { - Error: errors.New("The `apply-all` command is no longer supported. Use `terragrunt run-all apply` instead."), - Warning: "The `apply-all` command is deprecated and will be removed in a future version. Use `terragrunt run-all apply` instead.", + Error: errors.Errorf("The `%s` command is no longer supported. Use `terragrunt run-all apply` instead.", ApplyAll), + Warning: fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `terragrunt run-all apply` instead.", ApplyAll), }, DestroyAll: { - Error: errors.New("The `destroy-all` command is no longer supported. Use `terragrunt run-all destroy` instead."), - Warning: "The `destroy-all` command is deprecated and will be removed in a future version. Use `terragrunt run-all destroy` instead.", + Error: errors.Errorf("The `%s` command is no longer supported. Use `terragrunt run-all destroy` instead.", DestroyAll), + Warning: fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `terragrunt run-all destroy` instead.", DestroyAll), }, OutputAll: { - Error: errors.New("The `output-all` command is no longer supported. Use `terragrunt run-all output` instead."), - Warning: "The `output-all` command is deprecated and will be removed in a future version. Use `terragrunt run-all output` instead.", + Error: errors.Errorf("The `%s` command is no longer supported. Use `terragrunt run-all output` instead.", OutputAll), + Warning: fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `terragrunt run-all output` instead.", OutputAll), }, ValidateAll: { - Error: errors.New("The `validate-all` command is no longer supported. Use `terragrunt run-all validate` instead."), - Warning: "The `validate-all` command is deprecated and will be removed in a future version. Use `terragrunt run-all validate` instead.", + Error: errors.Errorf("The `%s` command is no longer supported. Use `terragrunt run-all validate` instead.", ValidateAll), + Warning: fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `terragrunt run-all validate` instead.", ValidateAll), + }, + SkipDependenciesInputs: { + Error: errors.Errorf("The `%s` option is deprecated. Reading inputs from dependencies has been deprecated and will be removed in a future version of Terragrunt. To continue using inputs from dependencies, forward them as outputs.", SkipDependenciesInputs), + Warning: fmt.Sprintf("The `%s` option is deprecated and will be removed in a future version of Terragrunt. Reading inputs from dependencies has been deprecated. To continue using inputs from dependencies, forward them as outputs.", SkipDependenciesInputs), }, } diff --git a/options/options.go b/options/options.go index e0f9967d4..42f45c09e 100644 --- a/options/options.go +++ b/options/options.go @@ -612,6 +612,8 @@ func (opts *TerragruntOptions) Clone(terragruntConfigPath string) (*TerragruntOp EngineLogLevel: opts.EngineLogLevel, EngineSkipChecksumCheck: opts.EngineSkipChecksumCheck, Engine: cloneEngineOptions(opts.Engine), + // copy array + StrictControls: util.CloneStringList(opts.StrictControls), }, nil } diff --git a/test/fixtures/dependency-optimisation/module-a/main.tf b/test/fixtures/dependency-optimisation/module-a/main.tf new file mode 100644 index 000000000..b8d8902f7 --- /dev/null +++ b/test/fixtures/dependency-optimisation/module-a/main.tf @@ -0,0 +1,3 @@ +output "test_output" { + value = "hello from module-a" +} diff --git a/test/fixtures/dependency-optimisation/module-a/terragrunt.hcl b/test/fixtures/dependency-optimisation/module-a/terragrunt.hcl new file mode 100644 index 000000000..814e7c2a2 --- /dev/null +++ b/test/fixtures/dependency-optimisation/module-a/terragrunt.hcl @@ -0,0 +1,7 @@ +terraform { + source = ".//" +} + +include "root" { + path = find_in_parent_folders() +} diff --git a/test/fixtures/dependency-optimisation/module-b/main.tf b/test/fixtures/dependency-optimisation/module-b/main.tf new file mode 100644 index 000000000..bc9212a8b --- /dev/null +++ b/test/fixtures/dependency-optimisation/module-b/main.tf @@ -0,0 +1,7 @@ +variable "test_var" { + type = string +} + +output "test_output" { + value = var.test_var +} diff --git a/test/fixtures/dependency-optimisation/module-b/terragrunt.hcl b/test/fixtures/dependency-optimisation/module-b/terragrunt.hcl new file mode 100644 index 000000000..2f45d03ed --- /dev/null +++ b/test/fixtures/dependency-optimisation/module-b/terragrunt.hcl @@ -0,0 +1,15 @@ +terraform { + source = ".//" +} + +include "root" { + path = find_in_parent_folders() +} + +dependency "module_a" { + config_path = "../module-a" +} + +inputs = { + test_var = dependency.module_a.outputs.test_output +} diff --git a/test/fixtures/dependency-optimisation/module-c/main.tf b/test/fixtures/dependency-optimisation/module-c/main.tf new file mode 100644 index 000000000..24a7098af --- /dev/null +++ b/test/fixtures/dependency-optimisation/module-c/main.tf @@ -0,0 +1,7 @@ +variable "test_var" { + type = string +} + +output "result" { + value = var.test_var +} diff --git a/test/fixtures/dependency-optimisation/module-c/terragrunt.hcl b/test/fixtures/dependency-optimisation/module-c/terragrunt.hcl new file mode 100644 index 000000000..e8095907c --- /dev/null +++ b/test/fixtures/dependency-optimisation/module-c/terragrunt.hcl @@ -0,0 +1,15 @@ +terraform { + source = ".//" +} + +include "root" { + path = find_in_parent_folders() +} + +dependency "module_b" { + config_path = "../module-b" +} + +inputs = { + test_var = dependency.module_b.outputs.test_output +} diff --git a/test/fixtures/dependency-optimisation/terragrunt.hcl b/test/fixtures/dependency-optimisation/terragrunt.hcl new file mode 100644 index 000000000..1d2801d96 --- /dev/null +++ b/test/fixtures/dependency-optimisation/terragrunt.hcl @@ -0,0 +1,10 @@ +remote_state { + backend = "local" + generate = { + path = "backend.tf" + if_exists = "overwrite_terragrunt" + } + config = { + path = "terraform.tfstate" + } +} diff --git a/test/integration_test.go b/test/integration_test.go index 74a057be9..f76d730b6 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -103,6 +103,7 @@ const ( testFixtureTfTest = "fixtures/tftest/" testFixtureErrorPrint = "fixtures/error-print" testFixtureBufferModuleOutput = "fixtures/buffer-module-output" + testFixtureDependenciesOptimisation = "fixtures/dependency-optimisation" terraformFolder = ".terraform" @@ -2682,6 +2683,24 @@ func TestLogFailingDependencies(t *testing.T) { assert.Contains(t, output, fmt.Sprintf("%s invocation failed in %s", wrappedBinary(), getPathRelativeTo(t, testdataDir, path))) } +func TestDependenciesOptimisation(t *testing.T) { + tmpEnvPath := copyEnvironment(t, testFixtureDependenciesOptimisation) + rootPath := util.JoinPath(tmpEnvPath, testFixtureDependenciesOptimisation) + + _, _, err := runTerragruntCommandWithOutput(t, "terragrunt run-all apply -auto-approve --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-working-dir "+rootPath) + require.NoError(t, err) + + config.ClearOutputCache() + + moduleC := util.JoinPath(tmpEnvPath, testFixtureDependenciesOptimisation, "module-c") + t.Setenv("TERRAGRUNT_STRICT_CONTROL", "skip-dependencies-inputs") + _, stderr, err := runTerragruntCommandWithOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-working-dir "+moduleC) + require.NoError(t, err) + + // checking that dependencies optimisation is working and outputs from module-a are not retrieved + assert.NotContains(t, stderr, "Retrieved output from ../module-a/terragrunt.hcl") +} + func cleanupTerraformFolder(t *testing.T, templatesPath string) { t.Helper()