diff --git a/modules/terraform/init.go b/modules/terraform/init.go index e1cb17730..198bb383d 100644 --- a/modules/terraform/init.go +++ b/modules/terraform/init.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "strings" "github.com/gruntwork-io/terratest/modules/testing" ) @@ -30,5 +31,19 @@ func InitE(t testing.TestingT, options *Options) (string, error) { args = append(args, FormatTerraformBackendConfigAsArgs(options.BackendConfig)...) args = append(args, FormatTerraformPluginDirAsArgs(options.PluginDir)...) + // Down to the user to supply to correct flags + if len(options.AdditionalInitFlags) > 0 { + + for _, v := range options.AdditionalInitFlags { + if strings.HasPrefix(v, "-upgrade") || + strings.HasPrefix(v, "-reconfigure") || + strings.HasPrefix(v, "-migrate-state") || + strings.HasPrefix(v, "-force-copy") { + continue + } + args = append(args, v) + } + } + return RunTerraformCommandE(t, options, args...) } diff --git a/modules/terraform/init_test.go b/modules/terraform/init_test.go index d98e94f48..9f0b2d2e4 100644 --- a/modules/terraform/init_test.go +++ b/modules/terraform/init_test.go @@ -1,11 +1,16 @@ package terraform import ( + "bytes" + "fmt" + "io" "os" "path/filepath" "testing" "github.com/gruntwork-io/terratest/modules/files" + "github.com/gruntwork-io/terratest/modules/logger" + ttest "github.com/gruntwork-io/terratest/modules/testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -132,3 +137,143 @@ func TestInitBackendMigration(t *testing.T) { _, err = InitE(t, options) assert.NoError(t, err, "Backend initialization with changed configuration should success with -migrate-state option") } + +type testLog struct { + w io.Writer +} + +func (l testLog) Logf(t ttest.TestingT, format string, args ...interface{}) { + fmt.Fprintf(l.w, format, args...) +} + +func TestInitAdditionalFlags(t *testing.T) { + t.Parallel() + + ttests := map[string]struct { + // returns logged out buffer, opts, expect string, cleanup + setup func(t *testing.T) (*bytes.Buffer, *Options, string, func()) + }{ + "backend set to false": { + func(t *testing.T) (*bytes.Buffer, *Options, string, func()) { + b := &bytes.Buffer{} + l := testLog{b} + stateDirectory := t.TempDir() + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + require.NoError(t, err) + backendPath := filepath.Join(stateDirectory, "backend.tfstate") + + return b, + &Options{ + Logger: logger.New(l), + TerraformDir: testFolder, + Reconfigure: true, + BackendConfig: map[string]any{ + "path": backendPath, + }, + AdditionalInitFlags: []string{"-backend=false"}, + }, + fmt.Sprintf("[init -upgrade=false -reconfigure -backend-config=path=%s -backend=false]", backendPath), + func() { + os.RemoveAll(testFolder) + } + }, + }, + "backend set to true": { + func(t *testing.T) (*bytes.Buffer, *Options, string, func()) { + b := &bytes.Buffer{} + l := testLog{b} + stateDirectory := t.TempDir() + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + require.NoError(t, err) + backendPath := filepath.Join(stateDirectory, "backend.tfstate") + + return b, &Options{ + Logger: logger.New(l), + TerraformDir: testFolder, + Reconfigure: true, + BackendConfig: map[string]any{ + "path": backendPath, + }, + AdditionalInitFlags: []string{"-backend=true"}, + }, + fmt.Sprintf("[init -upgrade=false -reconfigure -backend-config=path=%s -backend=true]", backendPath), + func() { + os.RemoveAll(testFolder) + } + }, + }, + "backend not set via additional args": { + func(t *testing.T) (*bytes.Buffer, *Options, string, func()) { + b := &bytes.Buffer{} + l := testLog{b} + stateDirectory := t.TempDir() + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + require.NoError(t, err) + backendPath := filepath.Join(stateDirectory, "backend.tfstate") + + return b, &Options{ + Logger: logger.New(l), + TerraformDir: testFolder, + Reconfigure: true, + BackendConfig: map[string]any{ + "path": backendPath, + }, + AdditionalInitFlags: []string{}, + }, + fmt.Sprintf("[init -upgrade=false -reconfigure -backend-config=path=%s]", backendPath), + func() { + os.RemoveAll(testFolder) + } + }, + }, + // should ignore the + "backend set to false and protected flags re-specified": { + setup: func(t *testing.T) (*bytes.Buffer, *Options, string, func()) { + b := &bytes.Buffer{} + l := testLog{b} + stateDirectory := t.TempDir() + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + require.NoError(t, err) + backendPath := filepath.Join(stateDirectory, "backend.tfstate") + return b, + &Options{ + Logger: logger.New(l), + TerraformDir: testFolder, + Reconfigure: false, + MigrateState: false, + BackendConfig: map[string]any{ + "path": backendPath, + }, + AdditionalInitFlags: []string{"-backend=false", "-reconfigure", "-migrate-state"}, + }, + fmt.Sprintf("[init -upgrade=false -backend-config=path=%s -backend=false]", backendPath), + func() { + os.RemoveAll(testFolder) + } + }, + }, + } + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + + b, options, expect, cleanUp := tt.setup(t) + defer cleanUp() + _, _ = InitE(t, options) + assert.Contains(t, b.String(), expect) + + if expect == "-backend=true" { + if statePath, ok := options.BackendConfig["path"]; ok { + ls, _ := os.ReadDir(fmt.Sprintf("%s", statePath)) + for idx, v := range ls { + if v.Name() == "backend.tfstate" { + return + } + if idx == len(ls) { + t.Errorf("failed to find backend state file when it should have been created under: %s", statePath) + } + } + } + } + }) + } +} diff --git a/modules/terraform/options.go b/modules/terraform/options.go index 9495a6c09..e598176e5 100644 --- a/modules/terraform/options.go +++ b/modules/terraform/options.go @@ -71,6 +71,8 @@ type Options struct { PlanFilePath string // The path to output a plan file to (for the plan command) or read one from (for the apply command) PluginDir string // The path of downloaded plugins to pass to the terraform init command (-plugin-dir) SetVarsAfterVarFiles bool // Pass -var options after -var-file options to Terraform commands + AdditionalInitFlags []string // additional flags to pass to init command - e.g. `-backend=false`. complete list of options [here](https://developer.hashicorp.com/terraform/cli/commands/init) + // AdditionalApplyDestroylags []string // additional flags to pass to apply/destroy command } // Clone makes a deep copy of most fields on the Options object and returns it.