github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/modules/terraform/options.go (about) 1 package terraform 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/gruntwork-io/terratest/modules/logger" 8 "github.com/gruntwork-io/terratest/modules/ssh" 9 "github.com/jinzhu/copier" 10 "github.com/stretchr/testify/require" 11 ) 12 13 var ( 14 DefaultRetryableTerraformErrors = map[string]string{ 15 // Helm related terraform calls may fail when too many tests run in parallel. While the exact cause is unknown, 16 // this is presumably due to all the network contention involved. Usually a retry resolves the issue. 17 ".*read: connection reset by peer.*": "Failed to reach helm charts repository.", 18 ".*transport is closing.*": "Failed to reach Kubernetes API.", 19 20 // `terraform init` frequently fails in CI due to network issues accessing plugins. The reason is unknown, but 21 // eventually these succeed after a few retries. 22 ".*unable to verify signature.*": "Failed to retrieve plugin due to transient network error.", 23 ".*unable to verify checksum.*": "Failed to retrieve plugin due to transient network error.", 24 ".*no provider exists with the given name.*": "Failed to retrieve plugin due to transient network error.", 25 ".*registry service is unreachable.*": "Failed to retrieve plugin due to transient network error.", 26 ".*Error installing provider.*": "Failed to retrieve plugin due to transient network error.", 27 ".*Failed to query available provider packages.*": "Failed to retrieve plugin due to transient network error.", 28 ".*timeout while waiting for plugin to start.*": "Failed to retrieve plugin due to transient network error.", 29 ".*timed out waiting for server handshake.*": "Failed to retrieve plugin due to transient network error.", 30 "could not query provider registry for": "Failed to retrieve plugin due to transient network error.", 31 32 // Provider bugs where the data after apply is not propagated. This is usually an eventual consistency issue, so 33 // retrying should self resolve it. 34 // See https://github.com/terraform-providers/terraform-provider-aws/issues/12449 for an example. 35 ".*Provider produced inconsistent result after apply.*": "Provider eventual consistency error.", 36 } 37 ) 38 39 // Options for running Terraform commands 40 type Options struct { 41 TerraformBinary string // Name of the binary that will be used 42 TerraformDir string // The path to the folder where the Terraform code is defined. 43 44 // The vars to pass to Terraform commands using the -var option. Note that terraform does not support passing `null` 45 // as a variable value through the command line. That is, if you use `map[string]interface{}{"foo": nil}` as `Vars`, 46 // this will translate to the string literal `"null"` being assigned to the variable `foo`. However, nulls in 47 // lists and maps/objects are supported. E.g., the following var will be set as expected (`{ bar = null }`: 48 // map[string]interface{}{ 49 // "foo": map[string]interface{}{"bar": nil}, 50 // } 51 Vars map[string]interface{} 52 53 VarFiles []string // The var file paths to pass to Terraform commands using -var-file option. 54 Targets []string // The target resources to pass to the terraform command with -target 55 Lock bool // The lock option to pass to the terraform command with -lock 56 LockTimeout string // The lock timeout option to pass to the terraform command with -lock-timeout 57 EnvVars map[string]string // Environment variables to set when running Terraform 58 BackendConfig map[string]interface{} // The vars to pass to the terraform init command for extra configuration for the backend 59 RetryableTerraformErrors map[string]string // If Terraform apply fails with one of these (transient) errors, retry. The keys are a regexp to match against the error and the message is what to display to a user if that error is matched. 60 MaxRetries int // Maximum number of times to retry errors matching RetryableTerraformErrors 61 TimeBetweenRetries time.Duration // The amount of time to wait between retries 62 Upgrade bool // Whether the -upgrade flag of the terraform init command should be set to true or not 63 NoColor bool // Whether the -no-color flag will be set for any Terraform command or not 64 SshAgent *ssh.SshAgent // Overrides local SSH agent with the given in-process agent 65 NoStderr bool // Disable stderr redirection 66 OutputMaxLineSize int // The max size of one line in stdout and stderr (in bytes) 67 Logger *logger.Logger // Set a non-default logger that should be used. See the logger package for more info. 68 Parallelism int // Set the parallelism setting for Terraform 69 PlanFilePath string // The path to output a plan file to (for the plan command) or read one from (for the apply command) 70 PluginDir string // The path of downloaded plugins to pass to the terraform init command (-plugin-dir) 71 } 72 73 // Clone makes a deep copy of most fields on the Options object and returns it. 74 // 75 // NOTE: options.SshAgent and options.Logger CANNOT be deep copied (e.g., the SshAgent struct contains channels and 76 // listeners that can't be meaningfully copied), so the original values are retained. 77 func (options *Options) Clone() (*Options, error) { 78 newOptions := &Options{} 79 if err := copier.Copy(newOptions, options); err != nil { 80 return nil, err 81 } 82 return newOptions, nil 83 } 84 85 // WithDefaultRetryableErrors makes a copy of the Options object and returns an updated object with sensible defaults 86 // for retryable errors. The included retryable errors are typical errors that most terraform modules encounter during 87 // testing, and are known to self resolve upon retrying. 88 // This will fail the test if there are any errors in the cloning process. 89 func WithDefaultRetryableErrors(t *testing.T, originalOptions *Options) *Options { 90 newOptions, err := originalOptions.Clone() 91 require.NoError(t, err) 92 93 if newOptions.RetryableTerraformErrors == nil { 94 newOptions.RetryableTerraformErrors = map[string]string{} 95 } 96 for k, v := range DefaultRetryableTerraformErrors { 97 newOptions.RetryableTerraformErrors[k] = v 98 } 99 100 // These defaults for retry configuration are arbitrary, but have worked well in practice across Gruntwork 101 // modules. 102 newOptions.MaxRetries = 3 103 newOptions.TimeBetweenRetries = 5 * time.Second 104 105 return newOptions 106 }