diff --git a/pkg/cli/args.go b/pkg/cli/args.go index ffd995128..94ddf85a5 100644 --- a/pkg/cli/args.go +++ b/pkg/cli/args.go @@ -5,6 +5,8 @@ import ( "strings" ) +const tailMinArgsLen = 2 + const ( SingleDashFlag NormalizeActsType = iota DoubleDashFlag @@ -42,9 +44,7 @@ func (args Args) Last() string { // Tail returns the rest of the arguments (not the first one) // or else an empty string slice func (args Args) Tail() Args { - const minArgsLen = 2 - - if args.Len() < minArgsLen { + if args.Len() < tailMinArgsLen { return []string{} } diff --git a/shell/run_shell_cmd.go b/shell/run_shell_cmd.go index 1a368a1b5..1c25a913e 100644 --- a/shell/run_shell_cmd.go +++ b/shell/run_shell_cmd.go @@ -153,7 +153,7 @@ func RunShellCommandWithOutput( logger := opts.Logger.WithField(format.TFBinaryKeyName, filepath.Base(opts.TerraformPath)) outWriter = writer.New( - writer.WithLogger(logger.WithOptions(log.WithOutput(outWriter))), + writer.WithLogger(logger.WithOptions(log.WithOutput(errWriter))), writer.WithDefaultLevel(log.StdoutLevel), writer.WithMsgSeparator(logMsgSeparator), ) @@ -162,7 +162,7 @@ func RunShellCommandWithOutput( writer.WithLogger(logger.WithOptions(log.WithOutput(errWriter))), writer.WithDefaultLevel(log.StderrLevel), writer.WithMsgSeparator(logMsgSeparator), - writer.WithParseFunc(terraform.ParseLogFunc(tfLogMsgPrefix)), + writer.WithParseFunc(terraform.ParseLogFunc(tfLogMsgPrefix, false)), ) } } @@ -453,5 +453,18 @@ func shouldForceForwardTFStdout(args cli.Args) bool { terraform.CommandNameConsole, } - return collections.ListContainsElement(tfCommands, args.CommandName()) || args.Tail().Contains(terraform.FlagNameJSON) + tfFlags := []string{ + terraform.FlagNameJSON, + terraform.FlagNameVersion, + terraform.FlagNameHelpLong, + terraform.FlagNameHelpShort, + } + + for _, flag := range tfFlags { + if args.Normalize(cli.SingleDashFlag).Contains(flag) { + return true + } + } + + return collections.ListContainsElement(tfCommands, args.CommandName()) } diff --git a/terraform/log.go b/terraform/log.go index 944f8245f..2a7e6cb45 100644 --- a/terraform/log.go +++ b/terraform/log.go @@ -5,10 +5,13 @@ import ( "strings" "time" + "github.com/gruntwork-io/go-commons/errors" "github.com/gruntwork-io/terragrunt/pkg/log" "github.com/gruntwork-io/terragrunt/pkg/log/writer" ) +const parseLogNumberOfValues = 4 + var ( // logTimestampFormat is TF_LOG timestamp formats. logTimestampFormat = "2006-01-02T15:04:05.000Z0700" @@ -22,41 +25,51 @@ var ( tfLogTimeLevelMsgReg = regexp.MustCompile(`(?i)(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\S*)\s*\[(trace|debug|warn|info|error)\]\s*(.+\S)$`) ) -func ParseLogFunc(msgPrefix string) writer.WriterParseFunc { +// ParseLogFunc wraps `ParseLog` to add msg prefix and bypasses the the parse erorr if `returnError` is false, +// since returning the error for `log/writer` will cause TG to fall with a `broken pipe` error. +func ParseLogFunc(msgPrefix string, returnError bool) writer.WriterParseFunc { return func(str string) (msg string, ptrTime *time.Time, ptrLevel *log.Level, err error) { - return ParseLog(msgPrefix, str) + if msg, ptrTime, ptrLevel, err = ParseLog(str); err != nil { + if returnError { + return str, nil, nil, err + } + + return str, nil, nil, nil + } + + return msgPrefix + msg, ptrTime, ptrLevel, nil } } -func ParseLog(msgPrefix, str string) (msg string, ptrTime *time.Time, ptrLevel *log.Level, err error) { - const numberOfValues = 4 - +func ParseLog(str string) (msg string, ptrTime *time.Time, ptrLevel *log.Level, err error) { if !tfLogTimeLevelMsgReg.MatchString(str) { - return str, nil, nil, nil + return str, nil, nil, errors.Errorf("could not parse string %q: does not match a known format", str) } match := tfLogTimeLevelMsgReg.FindStringSubmatch(str) - if len(match) != numberOfValues { - return str, nil, nil, nil + if len(match) != parseLogNumberOfValues { + return str, nil, nil, errors.Errorf("could not parse string %q: does not match a known format", str) } timeStr, levelStr, msg := match[1], match[2], match[3] if levelStr != "" { - if level, err := log.ParseLevel(strings.ToLower(levelStr)); err == nil { - ptrLevel = &level - } else { - msg = "[" + levelStr + "] " + msg + level, err := log.ParseLevel(strings.ToLower(levelStr)) + if err != nil { + return str, nil, nil, errors.Errorf("could not parse level %q: %w", levelStr, err) } + + ptrLevel = &level } if timeStr != "" { - if time, err := time.Parse(logTimestampFormat, timeStr); err == nil { - ptrTime = &time - } else { - msg = timeStr + " " + msg + time, err := time.Parse(logTimestampFormat, timeStr) + if err != nil { + return str, nil, nil, errors.Errorf("could not parse time %q: %w", timeStr, err) } + + ptrTime = &time } - return msgPrefix + msg, ptrTime, ptrLevel, nil + return msg, ptrTime, ptrLevel, nil } diff --git a/terraform/terraform.go b/terraform/terraform.go index f22c1686b..14bc742a8 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -25,8 +25,11 @@ const ( CommandNameShow = "show" CommandNameVersion = "version" - FlagNameJSON = "-json" - FlagNameNoColor = "-no-color" + FlagNameHelpLong = "-help" + FlagNameHelpShort = "-h" + FlagNameVersion = "-version" + FlagNameJSON = "-json" + FlagNameNoColor = "-no-color" // `apply -destroy` is alias for `destroy` FlagNameDestroy = "-destroy" diff --git a/test/integration_auto_retry_test.go b/test/integration_auto_retry_test.go index d5d7a64b3..ff459b026 100644 --- a/test/integration_auto_retry_test.go +++ b/test/integration_auto_retry_test.go @@ -31,7 +31,7 @@ func TestAutoRetryBasicRerun(t *testing.T) { out := new(bytes.Buffer) rootPath := copyEnvironment(t, testFixtureAutoRetryRerun) modulePath := util.JoinPath(rootPath, testFixtureAutoRetryRerun) - err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, out, os.Stderr) + err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, out, os.Stderr) require.NoError(t, err) assert.Contains(t, out.String(), "Apply complete!") @@ -55,7 +55,7 @@ func TestAutoRetryExhaustRetries(t *testing.T) { out := new(bytes.Buffer) rootPath := copyEnvironment(t, testFixtureAutoRetryExhaust) modulePath := util.JoinPath(rootPath, testFixtureAutoRetryExhaust) - err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, out, os.Stderr) + err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, out, os.Stderr) require.Error(t, err) assert.Contains(t, out.String(), "Failed to load backend") @@ -68,7 +68,7 @@ func TestAutoRetryCustomRetryableErrors(t *testing.T) { out := new(bytes.Buffer) rootPath := copyEnvironment(t, testFixtureAutoRetryCustomErrors) modulePath := util.JoinPath(rootPath, testFixtureAutoRetryCustomErrors) - err := runTerragruntCommand(t, "terragrunt apply --auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, out, os.Stderr) + err := runTerragruntCommand(t, "terragrunt apply --auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, out, os.Stderr) require.NoError(t, err) assert.Contains(t, out.String(), "My own little error") @@ -101,7 +101,7 @@ func TestAutoRetryCustomRetryableErrorsFailsWhenRetryableErrorsNotSet(t *testing out := new(bytes.Buffer) rootPath := copyEnvironment(t, testFixtureAutoRetryCustomErrorsNotSet) modulePath := util.JoinPath(rootPath, testFixtureAutoRetryCustomErrorsNotSet) - err := runTerragruntCommand(t, "terragrunt apply --auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, out, os.Stderr) + err := runTerragruntCommand(t, "terragrunt apply --auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, out, os.Stderr) require.Error(t, err) assert.Contains(t, out.String(), "My own little error") @@ -137,7 +137,7 @@ func TestAutoRetryApplyAllDependentModuleRetries(t *testing.T) { out := new(bytes.Buffer) rootPath := copyEnvironment(t, testFixtureAutoRetryApplyAllRetries) modulePath := util.JoinPath(rootPath, testFixtureAutoRetryApplyAllRetries) - err := runTerragruntCommand(t, "terragrunt apply-all -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, out, os.Stderr) + err := runTerragruntCommand(t, "terragrunt apply-all -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, out, os.Stderr) require.NoError(t, err) s := out.String() @@ -154,7 +154,7 @@ func TestAutoRetryConfigurableRetries(t *testing.T) { stderr := new(bytes.Buffer) rootPath := copyEnvironment(t, testFixtureAutoRetryConfigurableRetries) modulePath := util.JoinPath(rootPath, testFixtureAutoRetryConfigurableRetries) - err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, stdout, stderr) + err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, stdout, stderr) sleeps := regexp.MustCompile("Sleeping 0s before retrying.").FindAllStringIndex(stderr.String(), -1) require.NoError(t, err) diff --git a/test/integration_destroy_test.go b/test/integration_destroy_test.go index 6956ece08..4992c2848 100644 --- a/test/integration_destroy_test.go +++ b/test/integration_destroy_test.go @@ -40,7 +40,7 @@ func TestTerragruntDestroyOrder(t *testing.T) { runTerragrunt(t, "terragrunt run-all apply --terragrunt-non-interactive --terragrunt-working-dir "+rootPath) - stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt run-all destroy --terragrunt-non-interactive --terragrunt-working-dir "+rootPath) + stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt run-all destroy --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath) require.NoError(t, err) assert.Regexp(t, regexp.MustCompile(`(?smi)(?:(Module E|Module D|Module B).*){3}(?:(Module A|Module C).*){2}`), stdout) } @@ -54,7 +54,7 @@ func TestTerragruntApplyDestroyOrder(t *testing.T) { runTerragrunt(t, "terragrunt run-all apply --terragrunt-non-interactive --terragrunt-working-dir "+rootPath) - stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt run-all apply -destroy --terragrunt-non-interactive --terragrunt-working-dir "+rootPath) + stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt run-all apply -destroy --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath) require.NoError(t, err) assert.Regexp(t, regexp.MustCompile(`(?smi)(?:(Module E|Module D|Module B).*){3}(?:(Module A|Module C).*){2}`), stdout) } @@ -211,7 +211,7 @@ func TestPreventDestroyDependenciesIncludedConfig(t *testing.T) { showStderr bytes.Buffer ) - err = runTerragruntCommand(t, "terragrunt show --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, &showStdout, &showStderr) + err = runTerragruntCommand(t, "terragrunt show --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, &showStdout, &showStderr) logBufferContentsLineByLine(t, showStdout, "show stdout for "+modulePath) logBufferContentsLineByLine(t, showStderr, "show stderr for "+modulePath) diff --git a/test/integration_download_test.go b/test/integration_download_test.go index 0523260ca..36b01638e 100644 --- a/test/integration_download_test.go +++ b/test/integration_download_test.go @@ -415,7 +415,7 @@ func TestTerragruntExternalDependencies(t *testing.T) { rootPath := copyEnvironment(t, testFixtureExternalDependence) modulePath := util.JoinPath(rootPath, testFixtureExternalDependence, "module-b") - err := runTerragruntCommand(t, "terragrunt apply-all --terragrunt-non-interactive --terragrunt-include-external-dependencies --terragrunt-working-dir "+modulePath, &applyAllStdout, &applyAllStderr) + err := runTerragruntCommand(t, "terragrunt apply-all --terragrunt-non-interactive --terragrunt-include-external-dependencies --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, &applyAllStdout, &applyAllStderr) logBufferContentsLineByLine(t, applyAllStdout, "apply-all stdout") logBufferContentsLineByLine(t, applyAllStderr, "apply-all stderr") applyAllStdoutString := applyAllStdout.String() @@ -518,7 +518,7 @@ func TestPreventDestroyDependencies(t *testing.T) { showStderr bytes.Buffer ) - err = runTerragruntCommand(t, "terragrunt show --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, &showStdout, &showStderr) + err = runTerragruntCommand(t, "terragrunt show --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, &showStdout, &showStderr) logBufferContentsLineByLine(t, showStdout, "show stdout for "+modulePath) logBufferContentsLineByLine(t, showStderr, "show stderr for "+modulePath) diff --git a/test/integration_engine_test.go b/test/integration_engine_test.go index 19e7084b9..e6a2a4b39 100644 --- a/test/integration_engine_test.go +++ b/test/integration_engine_test.go @@ -33,7 +33,7 @@ var LocalEngineBinaryPath = "terragrunt-iac-engine-opentofu_rpc_" + testEngineVe func TestEngineLocalPlan(t *testing.T) { rootPath := setupLocalEngine(t) - stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir %s --terragrunt-log-level debug", rootPath)) + stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s --terragrunt-log-level debug", rootPath)) require.NoError(t, err) assert.Contains(t, stderr, LocalEngineBinaryPath+": plugin address") @@ -45,7 +45,7 @@ func TestEngineLocalPlan(t *testing.T) { func TestEngineLocalApply(t *testing.T) { rootPath := setupLocalEngine(t) - stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s", rootPath)) + stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s", rootPath)) require.NoError(t, err) assert.Contains(t, stderr, LocalEngineBinaryPath+": plugin address") @@ -61,7 +61,7 @@ func TestEngineOpentofu(t *testing.T) { tmpEnvPath := copyEnvironment(t, testFixtureOpenTofuEngine) rootPath := util.JoinPath(tmpEnvPath, testFixtureOpenTofuEngine) - stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s", rootPath)) + stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s", rootPath)) require.NoError(t, err) assert.Contains(t, stderr, "starting plugin:") @@ -77,7 +77,7 @@ func TestEngineRunAllOpentofu(t *testing.T) { tmpEnvPath := copyEnvironment(t, testFixtureOpenTofuRunAll) rootPath := util.JoinPath(tmpEnvPath, testFixtureOpenTofuRunAll) - stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt run-all apply -no-color -auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s", rootPath)) + stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt run-all apply -no-color -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s", rootPath)) require.NoError(t, err) assert.Contains(t, stderr, "starting plugin:") @@ -94,7 +94,7 @@ func TestEngineRunAllOpentofuCustomPath(t *testing.T) { cacheDir, rootPath := setupEngineCache(t) - stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt run-all apply -no-color -auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s", rootPath)) + stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt run-all apply -no-color -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s", rootPath)) require.NoError(t, err) assert.Contains(t, stderr, "starting plugin:") @@ -123,7 +123,7 @@ func TestEngineDownloadOverHttp(t *testing.T) { "__hardcoded_url__": fmt.Sprintf("https://github.com/gruntwork-io/terragrunt-engine-opentofu/releases/download/v0.0.4/terragrunt-iac-engine-opentofu_rpc_v0.0.4_%s_%s.zip", platform, arch), }) - stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s", rootPath)) + stdout, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s", rootPath)) require.NoError(t, err) assert.Contains(t, stderr, "starting plugin:") diff --git a/test/integration_include_test.go b/test/integration_include_test.go index 7e95b0020..29e8b2948 100644 --- a/test/integration_include_test.go +++ b/test/integration_include_test.go @@ -105,7 +105,7 @@ func TestTerragruntRunAllModulesThatIncludeRestrictsSet(t *testing.T) { err := runTerragruntCommand( t, fmt.Sprintf( - "terragrunt run-all plan --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-working-dir %s --terragrunt-modules-that-include alpha.hcl", + "terragrunt run-all plan --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-forward-tf-stdout --terragrunt-working-dir %s --terragrunt-modules-that-include alpha.hcl", modulePath, ), &stdout, @@ -132,7 +132,7 @@ func TestTerragruntRunAllModulesWithPrefix(t *testing.T) { stderr := bytes.Buffer{} err := runTerragruntCommand( t, - "terragrunt run-all plan --terragrunt-non-interactive --terragrunt-working-dir "+modulePath, + "terragrunt run-all plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, &stdout, &stderr, ) @@ -145,7 +145,7 @@ func TestTerragruntRunAllModulesWithPrefix(t *testing.T) { assert.Contains(t, planOutput, "beta") assert.Contains(t, planOutput, "charlie") - stdoutLines := strings.Split(planOutput, "\n") + stdoutLines := strings.Split(stderr.String(), "\n") for _, line := range stdoutLines { if strings.Contains(line, "alpha") { assert.Contains(t, line, "prefix=a") diff --git a/test/integration_serial_test.go b/test/integration_serial_test.go index 5d59934e5..a67ecf758 100644 --- a/test/integration_serial_test.go +++ b/test/integration_serial_test.go @@ -340,7 +340,7 @@ func TestTerragruntDownloadDir(t *testing.T) { func TestExtraArguments(t *testing.T) { out := new(bytes.Buffer) - runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+testFixtureExtraArgsPath, out, os.Stderr) + runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testFixtureExtraArgsPath, out, os.Stderr) t.Log(out.String()) assert.Contains(t, out.String(), "Hello, World from dev!") } @@ -348,14 +348,14 @@ func TestExtraArguments(t *testing.T) { func TestExtraArgumentsWithEnv(t *testing.T) { out := new(bytes.Buffer) t.Setenv("TF_VAR_env", "prod") - runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+testFixtureExtraArgsPath, out, os.Stderr) + runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testFixtureExtraArgsPath, out, os.Stderr) t.Log(out.String()) assert.Contains(t, out.String(), "Hello, World!") } func TestExtraArgumentsWithEnvVarBlock(t *testing.T) { out := new(bytes.Buffer) - runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+testFixtureEnvVarsBlockPath, out, os.Stderr) + runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testFixtureEnvVarsBlockPath, out, os.Stderr) t.Log(out.String()) assert.Contains(t, out.String(), "I'm set in extra_arguments env_vars") } @@ -363,7 +363,7 @@ func TestExtraArgumentsWithEnvVarBlock(t *testing.T) { func TestExtraArgumentsWithRegion(t *testing.T) { out := new(bytes.Buffer) t.Setenv("TF_VAR_region", "us-west-2") - runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+testFixtureExtraArgsPath, out, os.Stderr) + runTerragruntRedirectOutput(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testFixtureExtraArgsPath, out, os.Stderr) t.Log(out.String()) assert.Contains(t, out.String(), "Hello, World from Oregon!") } @@ -393,7 +393,7 @@ func TestPreserveEnvVarApplyAll(t *testing.T) { func TestPriorityOrderOfArgument(t *testing.T) { out := new(bytes.Buffer) injectedValue := "Injected-directly-by-argument" - runTerragruntRedirectOutput(t, fmt.Sprintf("terragrunt apply -auto-approve -var extra_var=%s --terragrunt-non-interactive --terragrunt-working-dir %s", injectedValue, testFixtureExtraArgsPath), out, os.Stderr) + runTerragruntRedirectOutput(t, fmt.Sprintf("terragrunt apply -auto-approve -var extra_var=%s --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s", injectedValue, testFixtureExtraArgsPath), out, os.Stderr) t.Log(out.String()) // And the result value for test should be the injected variable since the injected arguments are injected before the suplied parameters, // so our override of extra_var should be the last argument. diff --git a/test/integration_test.go b/test/integration_test.go index b634cad7f..ee5e9eef6 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -28,6 +28,7 @@ import ( "github.com/gruntwork-io/terragrunt/config" "github.com/gruntwork-io/terragrunt/internal/view/diagnostic" "github.com/gruntwork-io/terragrunt/options" + "github.com/gruntwork-io/terragrunt/pkg/log" "github.com/gruntwork-io/terragrunt/remote" "github.com/gruntwork-io/terragrunt/shell" "github.com/gruntwork-io/terragrunt/util" @@ -139,12 +140,12 @@ func TestLogWithAbsPath(t *testing.T) { tmpEnvPath := copyEnvironment(t, testFixtureLogFormatter) rootPath := util.JoinPath(tmpEnvPath, testFixtureLogFormatter) - stdout, stderr, err := runTerragruntCommandWithOutput(t, "terragrunt run-all init --terragrunt-log-level debug --terragrunt-log-show-abs-paths --terragrunt-non-interactive --terragrunt-disable-log-formatting=false -no-color --terragrunt-no-color --terragrunt-working-dir "+rootPath) + _, stderr, err := runTerragruntCommandWithOutput(t, "terragrunt run-all init --terragrunt-log-level debug --terragrunt-log-show-abs-paths --terragrunt-non-interactive --terragrunt-disable-log-formatting=false -no-color --terragrunt-no-color --terragrunt-working-dir "+rootPath) require.NoError(t, err) for _, prefixName := range []string{"app", "dep"} { prefixName = filepath.Join(rootPath, prefixName) - assert.Contains(t, stdout, "STDOUT ["+prefixName+"] "+wrappedBinary()+": Initializing provider plugins...") + assert.Contains(t, stderr, "STDOUT ["+prefixName+"] "+wrappedBinary()+": Initializing provider plugins...") assert.Contains(t, stderr, "DEBUG ["+prefixName+"] Reading Terragrunt config file at "+prefixName+"/terragrunt.hcl") } } @@ -160,9 +161,11 @@ func TestLogFormatterPrettyOutput(t *testing.T) { require.NoError(t, err) for _, prefixName := range []string{"app", "dep"} { - assert.Contains(t, stdout, "STDOUT ["+prefixName+"] "+wrappedBinary()+": Initializing provider plugins...") + assert.Contains(t, stderr, "STDOUT ["+prefixName+"] "+wrappedBinary()+": Initializing provider plugins...") assert.Contains(t, stderr, "DEBUG ["+prefixName+"] Reading Terragrunt config file at ./"+prefixName+"/terragrunt.hcl") } + + assert.Empty(t, stdout) assert.Contains(t, stderr, "DEBUG Terragrunt Version:") } @@ -173,11 +176,11 @@ func TestLogFormatterKeyValueOutput(t *testing.T) { tmpEnvPath := copyEnvironment(t, testFixtureLogFormatter) rootPath := util.JoinPath(tmpEnvPath, testFixtureLogFormatter) - stdout, stderr, err := runTerragruntCommandWithOutput(t, "terragrunt run-all init -no-color --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-disable-log-formatting --terragrunt-working-dir "+rootPath) + _, stderr, err := runTerragruntCommandWithOutput(t, "terragrunt run-all init -no-color --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-disable-log-formatting --terragrunt-working-dir "+rootPath) require.NoError(t, err) for _, prefixName := range []string{"app", "dep"} { - assert.Contains(t, stdout, "level=stdout prefix="+prefixName+" binary="+wrappedBinary()+" msg=Initializing provider plugins...\n") + assert.Contains(t, stderr, "level=stdout prefix="+prefixName+" binary="+wrappedBinary()+" msg=Initializing provider plugins...\n") assert.Contains(t, stderr, "level=debug prefix="+prefixName+" msg=Reading Terragrunt config file at ./"+prefixName+"/terragrunt.hcl\n") } assert.Contains(t, stderr, "level=debug prefix=. msg=Terragrunt Version:") @@ -193,7 +196,9 @@ func TestLogRawModuleOutput(t *testing.T) { stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt run-all init -no-color --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath) require.NoError(t, err) - assert.Contains(t, strings.ReplaceAll(stdout, "\n", ""), "Initializing the backend...Initializing provider plugins...") + stdoutInline := strings.ReplaceAll(stdout, "\n", "") + assert.Contains(t, stdoutInline, "Initializing the backend...Initializing provider plugins...") + assert.NotRegexp(t, regexp.MustCompile(`(?i)(`+strings.Join(log.AllLevels.Names(), "|")+`)+`), stdoutInline) } func TestTerragruntExcludesFile(t *testing.T) { @@ -430,7 +435,7 @@ func TestTerragruntInitOnce(t *testing.T) { tmpEnvPath := copyEnvironment(t, testFixtureInitOnce) rootPath := util.JoinPath(tmpEnvPath, testFixtureInitOnce) - stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+rootPath) + stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath) require.NoError(t, err) assert.Contains(t, stdout, "Initializing modules") @@ -441,7 +446,7 @@ func TestTerragruntInitOnce(t *testing.T) { err = os.WriteFile(cfgPath, bytes, 0644) require.NoError(t, err) - stdout, _, err = runTerragruntCommandWithOutput(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+rootPath) + stdout, _, err = runTerragruntCommandWithOutput(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath) require.NoError(t, err) assert.NotContains(t, stdout, "Initializing modules", "init command executed more than once") } @@ -814,7 +819,7 @@ func TestTerragruntExcludeExternalDependencies(t *testing.T) { rootPath := copyEnvironment(t, testFixtureExternalDependence) modulePath := util.JoinPath(rootPath, testFixtureExternalDependence, includedModule) - err := runTerragruntCommand(t, "terragrunt apply-all --terragrunt-non-interactive --terragrunt-ignore-external-dependencies --terragrunt-working-dir "+modulePath, &applyAllStdout, &applyAllStderr) + err := runTerragruntCommand(t, "terragrunt apply-all --terragrunt-non-interactive --terragrunt-ignore-external-dependencies --terragrunt-forward-tf-stdout --terragrunt-working-dir "+modulePath, &applyAllStdout, &applyAllStderr) logBufferContentsLineByLine(t, applyAllStdout, "apply-all stdout") logBufferContentsLineByLine(t, applyAllStderr, "apply-all stderr") applyAllStdoutString := applyAllStdout.String() @@ -857,7 +862,7 @@ func TestApplySkipFalse(t *testing.T) { showStdout := bytes.Buffer{} showStderr := bytes.Buffer{} - err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &showStdout, &showStderr) + err := runTerragruntCommand(t, "terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath, &showStdout, &showStderr) logBufferContentsLineByLine(t, showStdout, "show stdout") logBufferContentsLineByLine(t, showStderr, "show stderr") @@ -878,7 +883,7 @@ func TestApplyAllSkipTrue(t *testing.T) { showStdout := bytes.Buffer{} showStderr := bytes.Buffer{} - err := runTerragruntCommand(t, fmt.Sprintf("terragrunt apply-all --terragrunt-non-interactive --terragrunt-working-dir %s --terragrunt-log-level info", rootPath), &showStdout, &showStderr) + err := runTerragruntCommand(t, fmt.Sprintf("terragrunt apply-all --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s --terragrunt-log-level info", rootPath), &showStdout, &showStderr) logBufferContentsLineByLine(t, showStdout, "show stdout") logBufferContentsLineByLine(t, showStderr, "show stderr") @@ -900,7 +905,7 @@ func TestApplyAllSkipFalse(t *testing.T) { showStdout := bytes.Buffer{} showStderr := bytes.Buffer{} - err := runTerragruntCommand(t, "terragrunt apply-all --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &showStdout, &showStderr) + err := runTerragruntCommand(t, "terragrunt apply-all --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath, &showStdout, &showStderr) logBufferContentsLineByLine(t, showStdout, "show stdout") logBufferContentsLineByLine(t, showStderr, "show stderr") @@ -1750,14 +1755,14 @@ func TestDataDir(t *testing.T) { stderr bytes.Buffer ) - err := runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &stdout, &stderr) + err := runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath, &stdout, &stderr) require.NoError(t, err) assert.Contains(t, stdout.String(), "Initializing provider plugins") stdout = bytes.Buffer{} stderr = bytes.Buffer{} - err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &stdout, &stderr) + err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath, &stdout, &stderr) require.NoError(t, err) assert.NotContains(t, stdout.String(), "Initializing provider plugins") } @@ -2166,7 +2171,7 @@ func TestTerragruntGenerateAttr(t *testing.T) { text := "test-terragrunt-generate-attr-hello-world" - stdout, _, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s -var text=\"%s\"", generateTestCase, text)) + stdout, _, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt apply -auto-approve --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir %s -var text=\"%s\"", generateTestCase, text)) require.NoError(t, err) assert.Contains(t, stdout, text) } @@ -3007,7 +3012,7 @@ func TestNoMultipleInitsWithoutSourceChange(t *testing.T) { stdout := bytes.Buffer{} stderr := bytes.Buffer{} - err := runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+testPath, &stdout, &stderr) + err := runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testPath, &stdout, &stderr) require.NoError(t, err) // providers initialization during first plan assert.Equal(t, 1, strings.Count(stdout.String(), "has been successfully initialized!")) @@ -3015,7 +3020,7 @@ func TestNoMultipleInitsWithoutSourceChange(t *testing.T) { stdout = bytes.Buffer{} stderr = bytes.Buffer{} - err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+testPath, &stdout, &stderr) + err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testPath, &stdout, &stderr) require.NoError(t, err) // no initialization expected for second plan run // https://github.com/gruntwork-io/terragrunt/issues/1921 @@ -3040,7 +3045,7 @@ func TestAutoInitWhenSourceIsChanged(t *testing.T) { stdout := bytes.Buffer{} stderr := bytes.Buffer{} - err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+testPath, &stdout, &stderr) + err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testPath, &stdout, &stderr) require.NoError(t, err) // providers initialization during first plan assert.Equal(t, 1, strings.Count(stdout.String(), "has been successfully initialized!")) @@ -3051,7 +3056,7 @@ func TestAutoInitWhenSourceIsChanged(t *testing.T) { stdout = bytes.Buffer{} stderr = bytes.Buffer{} - err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-working-dir "+testPath, &stdout, &stderr) + err = runTerragruntCommand(t, "terragrunt plan --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testPath, &stdout, &stderr) require.NoError(t, err) // auto initialization when source is changed assert.Equal(t, 1, strings.Count(stdout.String(), "has been successfully initialized!")) @@ -3067,7 +3072,7 @@ func TestNoColor(t *testing.T) { stdout := bytes.Buffer{} stderr := bytes.Buffer{} - err := runTerragruntCommand(t, "terragrunt plan -no-color --terragrunt-working-dir "+testPath, &stdout, &stderr) + err := runTerragruntCommand(t, "terragrunt plan -no-color --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testPath, &stdout, &stderr) require.NoError(t, err) // providers initialization during first plan assert.Equal(t, 1, strings.Count(stdout.String(), "has been successfully initialized!")) @@ -3340,7 +3345,7 @@ func TestInitSkipCache(t *testing.T) { require.NoError( t, - runTerragruntCommand(t, "terragrunt plan --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &stdout, &stderr), + runTerragruntCommand(t, "terragrunt plan --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath, &stdout, &stderr), ) // verify that init was invoked @@ -3352,7 +3357,7 @@ func TestInitSkipCache(t *testing.T) { require.NoError( t, - runTerragruntCommand(t, "terragrunt plan --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &stdout, &stderr), + runTerragruntCommand(t, "terragrunt plan --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath, &stdout, &stderr), ) // verify that init wasn't invoked second time since cache directories are ignored @@ -3370,7 +3375,7 @@ func TestInitSkipCache(t *testing.T) { require.NoError( t, - runTerragruntCommand(t, "terragrunt plan --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &stdout, &stderr), + runTerragruntCommand(t, "terragrunt plan --terragrunt-log-level debug --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+rootPath, &stdout, &stderr), ) // verify that init was invoked @@ -3548,12 +3553,9 @@ func TestTerragruntInvokeTerraformTests(t *testing.T) { cleanupTerraformFolder(t, tmpEnvPath) testPath := util.JoinPath(tmpEnvPath, testFixtureTfTest) - stdout := bytes.Buffer{} - stderr := bytes.Buffer{} - - err := runTerragruntCommand(t, "terragrunt test --terragrunt-non-interactive --terragrunt-working-dir "+testPath, &stdout, &stderr) + stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt test --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testPath) require.NoError(t, err) - assert.Contains(t, stdout.String(), "1 passed, 0 failed") + assert.Contains(t, stdout, "1 passed, 0 failed") } func TestTerragruntCommandsThatNeedInput(t *testing.T) { @@ -3563,7 +3565,7 @@ func TestTerragruntCommandsThatNeedInput(t *testing.T) { cleanupTerraformFolder(t, tmpEnvPath) testPath := util.JoinPath(tmpEnvPath, testCommandsThatNeedInput) - stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt apply --terragrunt-non-interactive --terragrunt-working-dir "+testPath) + stdout, _, err := runTerragruntCommandWithOutput(t, "terragrunt apply --terragrunt-non-interactive --terragrunt-forward-tf-stdout --terragrunt-working-dir "+testPath) require.NoError(t, err) assert.Contains(t, stdout, "Apply complete") } @@ -3725,7 +3727,7 @@ func TestTerragruntRunAllPlanAndShow(t *testing.T) { _, _, err = runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt run-all plan --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-working-dir %s --terragrunt-out-dir %s", testPath, tmpDir)) require.NoError(t, err) - stdout, _, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt run-all show --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-working-dir %s --terragrunt-out-dir %s -no-color", testPath, tmpDir)) + stdout, _, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt run-all show --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-forward-tf-stdout --terragrunt-working-dir %s --terragrunt-out-dir %s -no-color", testPath, tmpDir)) require.NoError(t, err) // Verify that output contains the plan and not just the actual state output