Skip to content

Commit d3da531

Browse files
authored
Merge pull request #1 from autero1/auto_retry_implementation
Implement configuration init.
2 parents 83d9b14 + e0888b4 commit d3da531

File tree

7 files changed

+68
-1
lines changed

7 files changed

+68
-1
lines changed

cli/args.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func parseTerragruntOptionsFromArgs(args []string, writer, errWriter io.Writer)
9999

100100
opts.TerraformPath = filepath.ToSlash(terraformPath)
101101
opts.AutoInit = !parseBooleanArg(args, OPT_TERRAGRUNT_NO_AUTO_INIT, os.Getenv("TERRAGRUNT_AUTO_INIT") == "false")
102+
opts.AutoRetry = !parseBooleanArg(args, OPT_TERRAGRUNT_NO_AUTO_RETRY, os.Getenv("TERRAGRUNT_AUTO_RETRY") == "false")
102103
opts.NonInteractive = parseBooleanArg(args, OPT_NON_INTERACTIVE, os.Getenv("TF_INPUT") == "false" || os.Getenv("TF_INPUT") == "0")
103104
opts.TerraformCliArgs = filterTerragruntArgs(args)
104105
opts.TerraformCommand = util.FirstArg(opts.TerraformCliArgs)

cli/cli_app.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
const OPT_TERRAGRUNT_CONFIG = "terragrunt-config"
2626
const OPT_TERRAGRUNT_TFPATH = "terragrunt-tfpath"
2727
const OPT_TERRAGRUNT_NO_AUTO_INIT = "terragrunt-no-auto-init"
28+
const OPT_TERRAGRUNT_NO_AUTO_RETRY = "terragrunt-no-auto-retry"
2829
const OPT_NON_INTERACTIVE = "terragrunt-non-interactive"
2930
const OPT_WORKING_DIR = "terragrunt-working-dir"
3031
const OPT_DOWNLOAD_DIR = "terragrunt-download-dir"
@@ -34,7 +35,7 @@ const OPT_TERRAGRUNT_IAM_ROLE = "terragrunt-iam-role"
3435
const OPT_TERRAGRUNT_IGNORE_DEPENDENCY_ERRORS = "terragrunt-ignore-dependency-errors"
3536
const OPT_TERRAGRUNT_EXCLUDE_DIR = "terragrunt-exclude-dir"
3637

37-
var ALL_TERRAGRUNT_BOOLEAN_OPTS = []string{OPT_NON_INTERACTIVE, OPT_TERRAGRUNT_SOURCE_UPDATE, OPT_TERRAGRUNT_IGNORE_DEPENDENCY_ERRORS, OPT_TERRAGRUNT_NO_AUTO_INIT}
38+
var ALL_TERRAGRUNT_BOOLEAN_OPTS = []string{OPT_NON_INTERACTIVE, OPT_TERRAGRUNT_SOURCE_UPDATE, OPT_TERRAGRUNT_IGNORE_DEPENDENCY_ERRORS, OPT_TERRAGRUNT_NO_AUTO_INIT, OPT_TERRAGRUNT_NO_AUTO_RETRY}
3839
var ALL_TERRAGRUNT_STRING_OPTS = []string{OPT_TERRAGRUNT_CONFIG, OPT_TERRAGRUNT_TFPATH, OPT_WORKING_DIR, OPT_DOWNLOAD_DIR, OPT_TERRAGRUNT_SOURCE, OPT_TERRAGRUNT_IAM_ROLE, OPT_TERRAGRUNT_EXCLUDE_DIR}
3940

4041
const CMD_PLAN_ALL = "plan-all"
@@ -108,6 +109,7 @@ GLOBAL OPTIONS:
108109
terragrunt-config Path to the Terragrunt config file. Default is terraform.tfvars.
109110
terragrunt-tfpath Path to the Terraform binary. Default is terraform (on PATH).
110111
terragrunt-no-auto-init Don't automatically run 'terraform init' during other terragrunt commands. You must run 'terragrunt init' manually.
112+
terragrunt-no-auto-retry Don't automatically re-run command or in case of transient errors.
111113
terragrunt-non-interactive Assume "yes" for all prompts.
112114
terragrunt-working-dir The path to the Terraform templates. Default is current directory.
113115
terragrunt-download-dir The path where to download Terraform code. Default is .terragrunt-cache in the working directory.

options/auto_retry_options.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package options
2+
3+
const DEFAULT_MAX_RETRY_ATTEMPTS = 3
4+
const DEFAULT_SLEEP = 5
5+
6+
var RETRYABLE_ERRORS = []string{
7+
".*Failed to load state.*tcp.*timeout.*",
8+
".*Failed to load backend.*TLS handshake timeout.*",
9+
".*Creating metric alarm failed.*request to update this alarm is in progress.*",
10+
".*Error installing provider.*TLS handshake timeout.*",
11+
}
12+
13+
var ERRORS_REQUIRING_INIT = []string{
14+
".*Failed to load state.*tcp.*timeout.*",
15+
".*Failed to load backend.*TLS handshake timeout.*",
16+
".*Creating metric alarm failed.*request to update this alarm is in progress.*",
17+
".*Error installing provider.*TLS handshake timeout.*",
18+
}

options/options.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ type TerragruntOptions struct {
8080
// exposed here primarily so we can set it to a low value at test time.
8181
MaxFoldersToCheck int
8282

83+
// Whether we should automatically run terraform init if necessary when executing other commands
84+
AutoRetry bool
85+
86+
// Maximum number of times to retry errors matching RetryableErrors
87+
MaxRetryAttempts int
88+
89+
// Sleep is the duration in seconds to wait before retrying
90+
Sleep int
91+
92+
// RetryableErrors is an array of regular expressions with RE2 syntax (https://github.com/google/re2/wiki/Syntax) that qualify for retrying
93+
RetryableErrors []string
94+
95+
// ErrorsRequiringInit is an array of regular expressions with RE2 syntax (https://github.com/google/re2/wiki/Syntax) that qualify for re-running init
96+
ErrorsRequiringInit []string
97+
8398
// Unix-style glob of directories to exclude when running *-all commands
8499
ExcludeDirs []string
85100

@@ -117,6 +132,11 @@ func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, erro
117132
Writer: os.Stdout,
118133
ErrWriter: os.Stderr,
119134
MaxFoldersToCheck: DEFAULT_MAX_FOLDERS_TO_CHECK,
135+
AutoRetry: true,
136+
MaxRetryAttempts: DEFAULT_MAX_RETRY_ATTEMPTS,
137+
Sleep: DEFAULT_SLEEP,
138+
RetryableErrors: util.CloneStringList(RETRYABLE_ERRORS),
139+
ErrorsRequiringInit: util.CloneStringList(ERRORS_REQUIRING_INIT),
120140
ExcludeDirs: []string{},
121141
RunTerragrunt: func(terragruntOptions *TerragruntOptions) error {
122142
return errors.WithStackTrace(RunTerragruntCommandNotSet)
@@ -178,6 +198,11 @@ func (terragruntOptions *TerragruntOptions) Clone(terragruntConfigPath string) *
178198
Writer: terragruntOptions.Writer,
179199
ErrWriter: terragruntOptions.ErrWriter,
180200
MaxFoldersToCheck: terragruntOptions.MaxFoldersToCheck,
201+
AutoRetry: terragruntOptions.AutoRetry,
202+
MaxRetryAttempts: terragruntOptions.MaxRetryAttempts,
203+
Sleep: terragruntOptions.Sleep,
204+
RetryableErrors: util.CloneStringList(terragruntOptions.RetryableErrors),
205+
ErrorsRequiringInit: util.CloneStringList(terragruntOptions.ErrorsRequiringInit),
181206
ExcludeDirs: terragruntOptions.ExcludeDirs,
182207
RunTerragrunt: terragruntOptions.RunTerragrunt,
183208
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
resource "null_resource" "tf_retryable_error" {
2+
provisioner "local-exec" {
3+
command = "if [[ -f terraform.tfstate.backup ]]; then exit 0; else echo 'Error: Error loading modules: module foo: not found, mayx need to run 'terraform init'' && exit 1; fi"
4+
interpreter = ["/bin/bash", "-c"]
5+
}
6+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
terragrunt = {}

test/integration_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const (
7373
TEST_FIXTURE_HOOKS_INIT_ONCE_WITH_SOURCE_WITH_BACKEND = "fixture-hooks/init-once/with-source-with-backend"
7474
TEST_FIXTURE_FAILED_TERRAFORM = "fixture-failure"
7575
TEST_FIXTURE_EXIT_CODE = "fixture-exit-code"
76+
TEST_FIXTURE_AUTO_RETRY_RERUN = "fixture-auto-retry/re-run"
7677
TERRAFORM_FOLDER = ".terraform"
7778
TERRAFORM_STATE = "terraform.tfstate"
7879
TERRAFORM_STATE_BACKUP = "terraform.tfstate.backup"
@@ -839,6 +840,19 @@ func TestPriorityOrderOfArgument(t *testing.T) {
839840
assert.Contains(t, out.String(), fmt.Sprintf("test = %s", injectedValue))
840841
}
841842

843+
//func TestAutoRetryRerun(t *testing.T) {
844+
// t.Parallel()
845+
//
846+
// out := new(bytes.Buffer)
847+
// rootPath := copyEnvironment(t, TEST_FIXTURE_AUTO_RETRY_RERUN)
848+
// modulePath := util.JoinPath(rootPath, TEST_FIXTURE_AUTO_RETRY_RERUN)
849+
// err := runTerragruntCommand(t, fmt.Sprintf("terragrunt apply --auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s", modulePath), out, os.Stderr)
850+
//
851+
// assert.Nil(t, err)
852+
// // t.Log(out)
853+
// assert.NotContains(t, out.String(), "may need to run 'terraform init'")
854+
//}
855+
842856
// This tests terragrunt properly passes through terraform commands and any number of specified args
843857
func TestTerraformCommandCliArgs(t *testing.T) {
844858
t.Parallel()

0 commit comments

Comments
 (0)