Skip to content
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

Suppressing 423 error in Terragrunt Provider Cache #3453

Merged
merged 28 commits into from
Oct 8, 2024

Conversation

levkohimins
Copy link
Contributor

Description

Fixes #3430.
Fixes #3420.

TODOs

Read the Gruntwork contribution guidelines.

  • Update the docs.
  • Run the relevant tests successfully, including pre-commit checks.
  • Ensure any 3rd party code adheres with our license policy or delete this line if its not applicable.
  • Include release notes. If this PR is backward incompatible, include a migration guide.

Release Notes (draft)

Added / Removed / Updated [X].

Migration Guide

@levkohimins levkohimins changed the title Fixing 423 error suppression in Terragrunt Provider Cache Fixing 423 error in Terragrunt Provider Cache Oct 3, 2024
@levkohimins levkohimins changed the title Fixing 423 error in Terragrunt Provider Cache Suppressing 423 error in Terragrunt Provider Cache Oct 3, 2024
@levkohimins levkohimins marked this pull request as ready for review October 3, 2024 23:39

// WithPanicHandling wraps every command you add to *cli.App to handle panics by logging them with a stack trace and returning
// an error up the chain.
func WithPanicHandling(action func(c *cli.Context) error) func(c *cli.Context) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WithPanicHandling seems to be unused, is it intended?

$ grep -irn "WithPanicHandling" .
./internal/errors/errors.go:106:// WithPanicHandling wraps every command you add to *cli.App to handle panics by logging them with a stack trace and returning
./internal/errors/errors.go:108:func WithPanicHandling(action func(c *cli.Context) error) func(c *cli.Context) error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Removed.

}

// ErrorWithExitCode is a custom error that is used to specify the app exit code.
type ErrorWithExitCode struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the name conform to the XxxError format?
Like ExitCodeError

Copy link
Contributor Author

@levkohimins levkohimins Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed.

@@ -951,11 +951,11 @@ func TestAwsMockOutputsFromRemoteState(t *testing.T) { //nolint: paralleltest
require.NoError(t, os.Remove(filepath.Join(environmentPath, "/app1/.terraform/terraform.tfstate")))
require.NoError(t, os.RemoveAll(filepath.Join(environmentPath, "/app1/.terraform")))

_, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt init --terragrunt-fetch-dependency-output-from-state --terragrunt-non-interactive --terragrunt-working-dir %s/app2", environmentPath))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to fail test TestAwsMockOutputsFromRemoteState after multiple CI reruns

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@denis256
Copy link
Member

denis256 commented Oct 7, 2024

I noticed strict linter complaining about some moved code, most probably we need another issue to address the findings

internal/os/exec/ptty_unix.go:24: Function 'runCommandWithPTY' is too long (75 > 60) (funlen)
func runCommandWithPTY(logger log.Logger, cmd *exec.Cmd) (err error) {
config/include.go:102: the line is 228 characters long, which exceeds the maximum of 140 characters. (lll)
                return nil, errors.New("you reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message.Code: HANDLE_INCLUDE_NIL_INCLUDE_CONFIG")
config/include.go:168: the line is 240 characters long, which exceeds the maximum of 140 characters. (lll)
                return nil, errors.New("you reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: HANDLE_INCLUDE_DEPENDENCY_NIL_INCLUDE_CONFIG")
cli/commands/render-json/action.go:35: the line is 269 characters long, which exceeds the maximum of 140 characters. (lll)
                return errors.New("terragrunt was not able to render the config as json because it received no config. This is almost certainly a bug in Terragrunt. Please open an issue on github.com/gruntwork-io/terragrunt with this message and the contents of your terragrunt.hcl")
internal/os/signal/signal_unix.go:15:22: var-declaration: should omit type []os.Signal from declaration of var InterruptSignals; it will be inferred from the right-hand side (revive)
var InterruptSignals []os.Signal = []os.Signal{syscall.SIGTERM, syscall.SIGINT}
                     ^
internal/os/exec/ptty_unix.go:102:1: exported: exported function PrepareConsole should have comment or be unexported (revive)
func PrepareConsole(logger log.Logger) {
^
internal/errors/errors.go:13:9: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(message, args...)" (err113)
        err := fmt.Errorf(message, args...)
               ^
internal/errors/errors.go:99:10: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"%v\", rec)" (err113)
                        err = fmt.Errorf("%v", rec)
                              ^
internal/errors/export.go:18:9: do not define dynamic errors, use wrapped static errors instead: "errors.New(text)" (err113)
        return errors.New(text)
               ^
internal/os/signal/signal_unix.go:12:5: InterruptSignal is a global variable (gochecknoglobals)
var InterruptSignal = syscall.SIGINT

@denis256
Copy link
Member

denis256 commented Oct 7, 2024

Something is not right with error handling I suspect

terragrunt version v0.67.16

14:02:51.934 ERROR  5 errors occurred:
	* [./app2] command failed with exit code 1
	* [./app3] command failed with exit code 1
	* [./app4] command failed with exit code 1
	* [./app5] command failed with exit code 1
	* [./app1] command failed with exit code 1


14:02:51.934 ERROR  Unable to determine underlying exit code, so Terragrunt will exit with error code 1
14:02:51.935 ERROR  Suggested fixes: 
The executables 'terraform' and 'tofu' are missing from your $PATH. Please add at least one of these to your $PATH.

terragrunt version v0.67.16-18-g55a5d7bea90e

14:04:48.674 ERROR  5 errors occurred:
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)


14:04:48.677 ERROR  Unable to determine underlying exit code, so Terragrunt will exit with error code 1
14:04:48.677 ERROR  runtime error: invalid memory address or nil pointer dereference
14:04:48.678 ERROR  Unable to determine underlying exit code, so Terragrunt will exit with error code 1

Copy link
Collaborator

@yhakbar yhakbar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major in my review. I'll avoid submitting approval, as it seems Denis has found some significant issues with error handling.

@@ -165,7 +165,7 @@ func (module *RunningModule) moduleFinished(moduleErr error) {
if moduleErr == nil {
module.Module.TerragruntOptions.Logger.Debugf("Module %s has finished successfully!", module.Module.Path)
} else {
module.Module.TerragruntOptions.Logger.Errorf("Module %s has finished with an error: %v", module.Module.Path, moduleErr)
module.Module.TerragruntOptions.Logger.Errorf("Module %s has finished with an error", module.Module.Path)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't we wrapping moduleErr here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We display this error in the last steps. Since this error contains multiple lines, it is better to avoid duplicating it in the log.

errors.As(actualError, &multiError)
require.NotNil(t, multiError, "Expected a MutliError, but got: %v", actualError)

assert.Equal(t, len(expectedErrors), len(multiError.Errors))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would assert.Len have been more useful here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It definitely fits better. Thanks.

@@ -0,0 +1,31 @@
package errors
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this better than using the standard library directly? It seems to me that we should just use the standard library running Is, As, etc.

Copy link
Contributor Author

@levkohimins levkohimins Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just for convenience, in order not to have extra imports that require manual assignment of aliases.

import goerrors "errors"

}

// RegisterGracefullyShutdown registers a graceful shutdown for the command in two ways:
// 1. If the context cacnel contains a cause with a signal, this means that Terragrunt received the signal from the OS,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 1. If the context cacnel contains a cause with a signal, this means that Terragrunt received the signal from the OS,
// 1. If the context cancel contains a cause with a signal, this means that Terragrunt received the signal from the OS,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

}, sig)

if cmd.forwardSignalDelay > 0 {
cmd.logger.Infof("%s signal will be forwarded to %s with delay %s",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just double-checking here: Are you sure this should be in info?


// SendSignal sends the given `sig` to the executed command.
func (cmd *Cmd) SendSignal(sig os.Signal) {
cmd.logger.Infof("%s signal is forwarded to %s", cases.Title(language.English).String(sig.String()), cmd.filename)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just double-checking here: Are you sure this should this be in info?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you are most likely right, changed to Debug

cmd.logger.Infof("%s signal is forwarded to %s", cases.Title(language.English).String(sig.String()), cmd.filename)

if err := cmd.Process.Signal(sig); err != nil {
cmd.logger.Errorf("Failed to forwarding signal %s to %s: %v", sig, cmd.filename, err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this error being wrapped via %w?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a log message, not returned error. I actually do not really know if there is a sense to wrap it when we display it as a text.

runChannel <- cmd.Run()
}()

time.Sleep(1000 * time.Millisecond)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a dumb question, but why aren't we waiting 1 * time.Second?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't pay attention, and you're right, I fixed it.

runChannel <- cmd.Run()
}()

time.Sleep(1000 * time.Millisecond)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same potentially dumb question here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't pay attention, and you're right, I fixed it.

if opts.JSONLogFormat && opts.TerraformLogsToJSON {
logger := opts.Logger.WithField("workingDir", opts.WorkingDir).WithField("executedCommandArgs", args)
outWriter = logger.WithOptions(log.WithOutput(errWriter)).Writer()
errWriter = logger.WithOptions(log.WithOutput(errWriter)).WriterLevel(log.ErrorLevel)
} else if command == opts.TerraformPath {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@levkohimins
Copy link
Contributor Author

Something is not right with error handling I suspect

terragrunt version v0.67.16

14:02:51.934 ERROR  5 errors occurred:
	* [./app2] command failed with exit code 1
	* [./app3] command failed with exit code 1
	* [./app4] command failed with exit code 1
	* [./app5] command failed with exit code 1
	* [./app1] command failed with exit code 1


14:02:51.934 ERROR  Unable to determine underlying exit code, so Terragrunt will exit with error code 1
14:02:51.935 ERROR  Suggested fixes: 
The executables 'terraform' and 'tofu' are missing from your $PATH. Please add at least one of these to your $PATH.
terragrunt version v0.67.16-18-g55a5d7bea90e

14:04:48.674 ERROR  5 errors occurred:
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
	* %!s(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)


14:04:48.677 ERROR  Unable to determine underlying exit code, so Terragrunt will exit with error code 1
14:04:48.677 ERROR  runtime error: invalid memory address or nil pointer dereference
14:04:48.678 ERROR  Unable to determine underlying exit code, so Terragrunt will exit with error code 1

Fixed.

@levkohimins
Copy link
Contributor Author

I noticed strict linter complaining about some moved code, most probably we need another issue to address the findings

internal/os/exec/ptty_unix.go:24: Function 'runCommandWithPTY' is too long (75 > 60) (funlen)
func runCommandWithPTY(logger log.Logger, cmd *exec.Cmd) (err error) {
config/include.go:102: the line is 228 characters long, which exceeds the maximum of 140 characters. (lll)
                return nil, errors.New("you reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message.Code: HANDLE_INCLUDE_NIL_INCLUDE_CONFIG")
config/include.go:168: the line is 240 characters long, which exceeds the maximum of 140 characters. (lll)
                return nil, errors.New("you reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: HANDLE_INCLUDE_DEPENDENCY_NIL_INCLUDE_CONFIG")
cli/commands/render-json/action.go:35: the line is 269 characters long, which exceeds the maximum of 140 characters. (lll)
                return errors.New("terragrunt was not able to render the config as json because it received no config. This is almost certainly a bug in Terragrunt. Please open an issue on github.com/gruntwork-io/terragrunt with this message and the contents of your terragrunt.hcl")
internal/os/signal/signal_unix.go:15:22: var-declaration: should omit type []os.Signal from declaration of var InterruptSignals; it will be inferred from the right-hand side (revive)
var InterruptSignals []os.Signal = []os.Signal{syscall.SIGTERM, syscall.SIGINT}
                     ^
internal/os/exec/ptty_unix.go:102:1: exported: exported function PrepareConsole should have comment or be unexported (revive)
func PrepareConsole(logger log.Logger) {
^
internal/errors/errors.go:13:9: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(message, args...)" (err113)
        err := fmt.Errorf(message, args...)
               ^
internal/errors/errors.go:99:10: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"%v\", rec)" (err113)
                        err = fmt.Errorf("%v", rec)
                              ^
internal/errors/export.go:18:9: do not define dynamic errors, use wrapped static errors instead: "errors.New(text)" (err113)
        return errors.New(text)
               ^
internal/os/signal/signal_unix.go:12:5: InterruptSignal is a global variable (gochecknoglobals)
var InterruptSignal = syscall.SIGINT

I fixed almost everything except the functions that are too long.

Copy link
Member

@denis256 denis256 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


// Errorf creates a new error with the given format and values.
// It can be used as a drop-in replacement for fmt.Errorf() to provide descriptive errors in return values.
// If none of the given values contains an stack trace, it will be created.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"a stack trace" (in all places)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks, I will fix it in the next PR.

@levkohimins levkohimins merged commit afd5d7f into main Oct 8, 2024
4 of 5 checks passed
@levkohimins levkohimins deleted the bug/provider-cache-suppress-423-error branch October 8, 2024 21:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

423 Locked error since version 0.67.5 New log shows STDERR with Terragrunt Provider Cache
3 participants