From 8533008684b72d60b68f761f2ff2127f1363a927 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Mon, 6 Mar 2023 16:35:52 +0000 Subject: [PATCH 1/4] fix: add additional flags to init command ensure existing properties specifically called out are "protected" addressess https://github.com/gruntwork-io/terratest/issues/318 --- modules/terraform/init.go | 15 ++++ modules/terraform/init_test.go | 145 +++++++++++++++++++++++++++++++++ modules/terraform/options.go | 2 + 3 files changed, 162 insertions(+) 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. From e8b129ffde3e698960f69eafcea92e20c61c2b5b Mon Sep 17 00:00:00 2001 From: dnitsch Date: Mon, 6 Mar 2023 16:48:08 +0000 Subject: [PATCH 2/4] fix: remove statefile check Not doing an InitApply so never writes it out tested previously --- modules/terraform/init_test.go | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/modules/terraform/init_test.go b/modules/terraform/init_test.go index 9f0b2d2e4..8b92143d4 100644 --- a/modules/terraform/init_test.go +++ b/modules/terraform/init_test.go @@ -258,22 +258,9 @@ func TestInitAdditionalFlags(t *testing.T) { b, options, expect, cleanUp := tt.setup(t) defer cleanUp() - _, _ = InitE(t, options) + _, err := 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) - } - } - } - } + assert.NoError(t, err) }) } } From 3ab513312de7536039a65b5b86581a0714166c46 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Mon, 6 Mar 2023 16:52:57 +0000 Subject: [PATCH 3/4] fix: code comments --- modules/terraform/init.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/terraform/init.go b/modules/terraform/init.go index 198bb383d..69b0b243b 100644 --- a/modules/terraform/init.go +++ b/modules/terraform/init.go @@ -31,9 +31,11 @@ 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 + // AdditionalInitFlags should not overwrite previously set + // flags via Options public properties if len(options.AdditionalInitFlags) > 0 { - for _, v := range options.AdditionalInitFlags { if strings.HasPrefix(v, "-upgrade") || strings.HasPrefix(v, "-reconfigure") || From b2247ca0bf85e0c2e2001590821f12dbf23678b7 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Tue, 7 Mar 2023 09:49:32 +0000 Subject: [PATCH 4/4] fix: randomize temp folders in test should avoid untested errors when run in Parallel --- modules/terraform/init_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/terraform/init_test.go b/modules/terraform/init_test.go index 8b92143d4..b73fb7358 100644 --- a/modules/terraform/init_test.go +++ b/modules/terraform/init_test.go @@ -158,7 +158,7 @@ func TestInitAdditionalFlags(t *testing.T) { b := &bytes.Buffer{} l := testLog{b} stateDirectory := t.TempDir() - testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "be-set-to-false") require.NoError(t, err) backendPath := filepath.Join(stateDirectory, "backend.tfstate") @@ -183,7 +183,7 @@ func TestInitAdditionalFlags(t *testing.T) { b := &bytes.Buffer{} l := testLog{b} stateDirectory := t.TempDir() - testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "be-set-to-true") require.NoError(t, err) backendPath := filepath.Join(stateDirectory, "backend.tfstate") @@ -207,7 +207,7 @@ func TestInitAdditionalFlags(t *testing.T) { b := &bytes.Buffer{} l := testLog{b} stateDirectory := t.TempDir() - testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "not-set-via-args") require.NoError(t, err) backendPath := filepath.Join(stateDirectory, "backend.tfstate") @@ -232,7 +232,7 @@ func TestInitAdditionalFlags(t *testing.T) { b := &bytes.Buffer{} l := testLog{b} stateDirectory := t.TempDir() - testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "") + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-backend", "be-no-specified") require.NoError(t, err) backendPath := filepath.Join(stateDirectory, "backend.tfstate") return b,