From 5ef82ddd2f363cc137b322f3f10dd03007733dc7 Mon Sep 17 00:00:00 2001 From: Barrett Clark Date: Wed, 3 Nov 2021 15:47:14 -0500 Subject: [PATCH 1/3] Cloud: fix e2e tests - Fix tests and remove commented code - Remove parallel for some flaky tests - Add README --- go.mod | 2 +- go.sum | 3 +- internal/cloud/e2e/README.md | 25 + internal/cloud/e2e/apply_auto_approve_test.go | 117 +- .../e2e/backend_apply_before_init_test.go | 109 +- internal/cloud/e2e/helper_test.go | 30 +- .../cloud/e2e/init_with_empty_tags_test.go | 103 +- internal/cloud/e2e/main_test.go | 6 + .../e2e/migrate_state_multi_to_tfc_test.go | 316 ++- ...igrate_state_remote_backend_to_tfc_test.go | 1747 +++++++++-------- .../e2e/migrate_state_single_to_tfc_test.go | 129 +- .../e2e/migrate_state_tfc_to_other_test.go | 104 +- .../e2e/migrate_state_tfc_to_tfc_test.go | 250 ++- internal/cloud/e2e/run_variables_test.go | 119 +- 14 files changed, 1609 insertions(+), 1451 deletions(-) create mode 100644 internal/cloud/e2e/README.md diff --git a/go.mod b/go.mod index 4700526c9..a830edcc3 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.0 github.com/hashicorp/go-tfe v0.20.1-0.20211110172530-c43c6b574caa github.com/hashicorp/go-uuid v1.0.2 - github.com/hashicorp/go-version v1.2.1 + github.com/hashicorp/go-version v1.3.0 github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f github.com/hashicorp/hcl/v2 v2.10.1 github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 diff --git a/go.sum b/go.sum index 2298eacdd..f7d7f23db 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,9 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= diff --git a/internal/cloud/e2e/README.md b/internal/cloud/e2e/README.md new file mode 100644 index 000000000..02a50bbfe --- /dev/null +++ b/internal/cloud/e2e/README.md @@ -0,0 +1,25 @@ +# How to run tests + +To run them, use: +``` +TF_ACC=1 go test -tags=e2e ./internal/cloud/e2e/... -ldflags "-X \"github.com/hashicorp/terraform/version.Prerelease=\"" +``` + +Required flags +* `-tags=e2e` for running e2e tests. +* `TF_ACC=1`. This variable is used as part of terraform for tests that make + external network calls. This is needed to run these tests. Without it, the + tests do not run. +* `TFE_TOKEN=` and `TFE_HOSTNAME=`. The helpers +for these tests require admin access to a TFC/TFE instance. +* `-timeout=30m`. Some of these tests take longer than the default 10m timeout for `go test`. + +### Flags + +* Use the `-v` flag for normal verbose mode. +* Use the `-tfoutput` flag to print the terraform output to standard out. +* Use `-ldflags` to change the version Prerelease to match a version +available remotely. Some behaviors rely on the exact local version Terraform +being available in TFC/TFE, and manipulating the Prerelease during build is +often the only way to ensure this. +[(More on `-ldflags`.)](https://www.digitalocean.com/community/tutorials/using-ldflags-to-set-version-information-for-go-applications) diff --git a/internal/cloud/e2e/apply_auto_approve_test.go b/internal/cloud/e2e/apply_auto_approve_test.go index cf27a422d..1689caedb 100644 --- a/internal/cloud/e2e/apply_auto_approve_test.go +++ b/internal/cloud/e2e/apply_auto_approve_test.go @@ -6,7 +6,6 @@ package main import ( "context" "io/ioutil" - "log" "os" "testing" @@ -17,7 +16,6 @@ import ( ) func Test_terraform_apply_autoApprove(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -184,74 +182,75 @@ func Test_terraform_apply_autoApprove(t *testing.T) { }, } for name, tc := range cases { - log.Println("Test: ", name) + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=info") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() if err != nil { t.Fatal(err) } } - - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } - - err = cmd.Wait() - if err != nil { - t.Fatal(err) - } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) - } + if tc.validations != nil { + tc.validations(t, organization.Name) + } + }) } } diff --git a/internal/cloud/e2e/backend_apply_before_init_test.go b/internal/cloud/e2e/backend_apply_before_init_test.go index c3b0becb7..48d3f3d40 100644 --- a/internal/cloud/e2e/backend_apply_before_init_test.go +++ b/internal/cloud/e2e/backend_apply_before_init_test.go @@ -4,7 +4,6 @@ package main import ( - "fmt" "io/ioutil" "os" "testing" @@ -51,10 +50,8 @@ func Test_backend_apply_before_init(t *testing.T) { expectedCmdOutput: `Successfully configured the backend "local"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, }, @@ -77,68 +74,70 @@ func Test_backend_apply_before_init(t *testing.T) { } for name, tc := range cases { - fmt.Println("Test: ", name) - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=info") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + err = cmd.Start() if err != nil { t.Fatal(err) } - } - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } } } } - } - err = cmd.Wait() - if err != nil && !tfCmd.expectError { - t.Fatal(err) + err = cmd.Wait() + if err != nil && !tfCmd.expectError { + t.Fatal(err) + } } } - } + }) } } diff --git a/internal/cloud/e2e/helper_test.go b/internal/cloud/e2e/helper_test.go index 0f4480ddf..87ed67654 100644 --- a/internal/cloud/e2e/helper_test.go +++ b/internal/cloud/e2e/helper_test.go @@ -10,8 +10,10 @@ import ( "testing" "time" + expect "github.com/Netflix/go-expect" tfe "github.com/hashicorp/go-tfe" "github.com/hashicorp/go-uuid" + goversion "github.com/hashicorp/go-version" tfversion "github.com/hashicorp/terraform/version" ) @@ -38,6 +40,16 @@ type testCases map[string]struct { validations func(t *testing.T, orgName string) } +func defaultOpts() []expect.ConsoleOpt { + opts := []expect.ConsoleOpt{ + expect.WithDefaultTimeout(expectConsoleTimeout), + } + if verboseMode { + opts = append(opts, expect.WithStdout(os.Stdout)) + } + return opts +} + func createOrganization(t *testing.T) (*tfe.Organization, func()) { ctx := context.Background() org, err := tfeClient.Organizations.Create(ctx, tfe.OrganizationCreateOptions{ @@ -193,9 +205,16 @@ func writeMainTF(t *testing.T, block string, dir string) { f.Close() } -// Ensure that TFC/E has a particular terraform version. +// The e2e tests rely on the fact that the terraform version in TFC/E is able to +// run the `cloud` configuration block, which is available in 1.1 and will +// continue to be available in later versions. So this function checks that +// there is a version that is >= 1.1. func skipWithoutRemoteTerraformVersion(t *testing.T) { - version := tfversion.String() + version := tfversion.Version + baseVersion, err := goversion.NewVersion(version) + if err != nil { + t.Fatalf(fmt.Sprintf("Error instantiating go-version for %s", version)) + } opts := tfe.AdminTerraformVersionsListOptions{ ListOptions: tfe.ListOptions{ PageNumber: 1, @@ -213,7 +232,12 @@ findTfVersion: t.Fatalf("Could not retrieve list of terraform versions: %v", err) } for _, item := range tfVersionList.Items { - if item.Version == version { + availableVersion, err := goversion.NewVersion(item.Version) + if err != nil { + t.Logf("Error instantiating go-version for %s", item.Version) + continue + } + if availableVersion.Core().GreaterThanOrEqual(baseVersion.Core()) { hasVersion = true break findTfVersion } diff --git a/internal/cloud/e2e/init_with_empty_tags_test.go b/internal/cloud/e2e/init_with_empty_tags_test.go index 8b8f4807c..15bc3ba18 100644 --- a/internal/cloud/e2e/init_with_empty_tags_test.go +++ b/internal/cloud/e2e/init_with_empty_tags_test.go @@ -4,7 +4,6 @@ package main import ( - "fmt" "io/ioutil" "os" "testing" @@ -42,68 +41,70 @@ func Test_init_with_empty_tags(t *testing.T) { } for name, tc := range cases { - fmt.Println("Test: ", name) - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=info") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + err = cmd.Start() if err != nil { t.Fatal(err) } - } - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } } } } - } - err = cmd.Wait() - if err != nil && !tfCmd.expectError { - t.Fatal(err) + err = cmd.Wait() + if err != nil && !tfCmd.expectError { + t.Fatal(err) + } } } - } + }) } } diff --git a/internal/cloud/e2e/main_test.go b/internal/cloud/e2e/main_test.go index 48e40b81f..783dc4d20 100644 --- a/internal/cloud/e2e/main_test.go +++ b/internal/cloud/e2e/main_test.go @@ -4,6 +4,7 @@ package main import ( + "flag" "fmt" "io/ioutil" "log" @@ -22,6 +23,7 @@ var cliConfigFileEnv string var tfeClient *tfe.Client var tfeHostname string var tfeToken string +var verboseMode bool func TestMain(m *testing.M) { log.SetFlags(log.LstdFlags | log.Lshortfile) @@ -43,6 +45,10 @@ func accTest() bool { } func setup() func() { + tfOutput := flag.Bool("tfoutput", false, "This flag produces the terraform output from tests.") + flag.Parse() + verboseMode = *tfOutput + setTfeClient() teardown := setupBinary() diff --git a/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go b/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go index 6e1bd40d9..07c4b04f8 100644 --- a/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go @@ -16,7 +16,6 @@ import ( ) func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -38,20 +37,16 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { expectedCmdOutput: `Successfully configured the backend "local"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, { command: []string{"workspace", "new", "prod"}, expectedCmdOutput: `Created and switched to workspace "prod"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, { command: []string{"workspace", "select", "default"}, @@ -113,20 +108,16 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { expectedCmdOutput: `Successfully configured the backend "local"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, { command: []string{"workspace", "new", "prod"}, expectedCmdOutput: `Created and switched to workspace "prod"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, }, @@ -171,82 +162,81 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { } for name, tc := range cases { - t.Log("Test: ", name) - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - defer tf.Close() - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) + tf := e2e.NewBinary(terraformBin, tmpDir) + defer tf.Close() + tf.AddEnv(cliConfigFileEnv) - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - t.Log("Running commands: ", tfCmd.command) - tfCmd.command = append(tfCmd.command) - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + tfCmd.command = append(tfCmd.command) + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() if err != nil { t.Fatal(err) } } - - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } - - err = cmd.Wait() - if err != nil { - t.Fatal(err) - } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) - } + if tc.validations != nil { + tc.validations(t, organization.Name) + } + }) } - } func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -268,20 +258,16 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { expectedCmdOutput: `Successfully configured the backend "local"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, { command: []string{"workspace", "new", "prod"}, expectedCmdOutput: `Created and switched to workspace "prod"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, { command: []string{"workspace", "select", "default"}, @@ -311,21 +297,12 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { { command: []string{"init", "-migrate-state"}, expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`, - userInput: []string{"dev", "1", "app-*", "1"}, + userInput: []string{"dev", "1", "app-*"}, postInputOutput: []string{ `Would you like to rename your workspaces?`, - "What pattern would you like to add to all your workspaces?", - "The currently selected workspace (prod) does not exist.", + "How would you like to rename your workspaces?", "Terraform Cloud has been successfully initialized!"}, }, - { - command: []string{"workspace", "select", "app-prod"}, - expectedCmdOutput: `Switched to workspace "app-prod".`, - }, - { - command: []string{"output"}, - expectedCmdOutput: `val = "prod"`, - }, { command: []string{"workspace", "select", "app-dev"}, expectedCmdOutput: `Switched to workspace "app-dev".`, @@ -334,6 +311,14 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { command: []string{"output"}, expectedCmdOutput: `val = "default"`, }, + { + command: []string{"workspace", "select", "app-prod"}, + expectedCmdOutput: `Switched to workspace "app-prod".`, + }, + { + command: []string{"output"}, + expectedCmdOutput: `val = "prod"`, + }, }, }, }, @@ -417,17 +402,12 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { { command: []string{"init", "-migrate-state"}, expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`, - userInput: []string{"dev", "1", "app-*", "1"}, + userInput: []string{"dev", "1", "app-*"}, postInputOutput: []string{ `Would you like to rename your workspaces?`, - "What pattern would you like to add to all your workspaces?", - "The currently selected workspace (default) does not exist.", + "How would you like to rename your workspaces?", "Terraform Cloud has been successfully initialized!"}, }, - { - command: []string{"workspace", "select", "app-dev"}, - expectedCmdOutput: `Switched to workspace "app-dev".`, - }, { command: []string{"workspace", "select", "app-billing"}, expectedCmdOutput: `Switched to workspace "app-billing".`, @@ -436,6 +416,10 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { command: []string{"workspace", "select", "app-identity"}, expectedCmdOutput: `Switched to workspace "app-identity".`, }, + { + command: []string{"workspace", "select", "app-dev"}, + expectedCmdOutput: `Switched to workspace "app-dev".`, + }, }, }, }, @@ -466,78 +450,78 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { } for name, tc := range cases { - t.Log("Test: ", name) - organization, cleanup := createOrganization(t) - t.Log(organization.Name) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - defer tf.Close() - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) + tf := e2e.NewBinary(terraformBin, tmpDir) + defer tf.Close() + tf.AddEnv(cliConfigFileEnv) - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - t.Log("running commands: ", tfCmd.command) - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + if output == "" { + continue + } + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() if err != nil { t.Fatal(err) } } - - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - if output == "" { - continue - } - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } - - err = cmd.Wait() - if err != nil { - t.Fatal(err) - } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) - } + if tc.validations != nil { + tc.validations(t, organization.Name) + } + }) } } diff --git a/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go b/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go index 7fa2d6401..819875e3f 100644 --- a/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go @@ -15,916 +15,1053 @@ import ( ) func Test_migrate_remote_backend_name_to_tfc_name(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() - cases := map[string]struct { - operations []operationSets - validations func(t *testing.T, orgName string) - }{ - "backend name strategy, to cloud with name strategy": { - operations: []operationSets{ - { - prep: func(t *testing.T, orgName, dir string) { - remoteWorkspace := "remote-workspace" - tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `Successfully configured the backend "remote"!`, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "remote-workspace"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - }, - }, - { - prep: func(t *testing.T, orgName, dir string) { - wsName := "cloud-workspace" - tfBlock := terraformConfigCloudBackendName(orgName, wsName) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Do you want to copy existing state to Terraform Cloud?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Terraform Cloud has been successfully initialized!`}, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: `cloud-workspace`, - }, - }, - }, + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + remoteWorkspace := "remote-workspace" + tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) + writeMainTF(t, tfBlock, dir) }, - validations: func(t *testing.T, orgName string) { - expectedName := "cloud-workspace" - ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) - if err != nil { - t.Fatal(err) - } - if ws == nil { - t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) - } + commands: []tfCommand{ + { + command: []string{"init"}, + expectedCmdOutput: `Successfully configured the backend "remote"!`, + }, + { + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, + }, }, }, - "backend name strategy, to cloud name strategy, using the same name": { - operations: []operationSets{ - { - prep: func(t *testing.T, orgName, dir string) { - remoteWorkspace := "remote-workspace" - tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `Successfully configured the backend "remote"!`, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "remote-workspace"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - }, - }, - { - prep: func(t *testing.T, orgName, dir string) { - wsName := "remote-workspace" - tfBlock := terraformConfigCloudBackendName(orgName, wsName) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Terraform Cloud has been successfully initialized!`, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: `remote-workspace`, - }, - }, - }, + { + prep: func(t *testing.T, orgName, dir string) { + wsName := "cloud-workspace" + tfBlock := terraformConfigCloudBackendName(orgName, wsName) + writeMainTF(t, tfBlock, dir) }, - validations: func(t *testing.T, orgName string) { - expectedName := "remote-workspace" - ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) - if err != nil { - t.Fatal(err) - } - if ws == nil { - t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) - } + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Do you want to copy existing state to Terraform Cloud?`, + userInput: []string{"yes"}, + postInputOutput: []string{`Terraform Cloud has been successfully initialized!`}, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: `cloud-workspace`, + }, }, }, } - - for name, tc := range cases { - t.Log("Test: ", name) - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) + validations := func(t *testing.T, orgName string) { + expectedName := "cloud-workspace" + ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) if err != nil { t.Fatal(err) } - defer exp.Close() - - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) + if ws == nil { + t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) } - defer os.RemoveAll(tmpDir) + } - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - organization, cleanup := createOrganization(t) - defer cleanup() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) - if err != nil { - t.Fatal(err) - } - } + organization, cleanup := createOrganization(t) + defer cleanup() + for _, op := range operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - err = cmd.Wait() + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) if err != nil { t.Fatal(err) } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } } } + + if validations != nil { + validations(t, organization.Name) + } +} + +func Test_migrate_remote_backend_name_to_tfc_same_name(t *testing.T) { + skipWithoutRemoteTerraformVersion(t) + ctx := context.Background() + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + remoteWorkspace := "remote-workspace" + tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init"}, + expectedCmdOutput: `Successfully configured the backend "remote"!`, + }, + { + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, + }, + }, + }, + { + prep: func(t *testing.T, orgName, dir string) { + wsName := "remote-workspace" + tfBlock := terraformConfigCloudBackendName(orgName, wsName) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Terraform Cloud has been successfully initialized!`, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: `remote-workspace`, + }, + }, + }, + } + validations := func(t *testing.T, orgName string) { + expectedName := "remote-workspace" + ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) + if err != nil { + t.Fatal(err) + } + if ws == nil { + t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) + } + } + + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() + + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() + + organization, cleanup := createOrganization(t) + defer cleanup() + for _, op := range operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() + + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } + } + } + + if validations != nil { + validations(t, organization.Name) + } } func Test_migrate_remote_backend_name_to_tfc_name_different_org(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() - cases := map[string]struct { - operations []operationSets - validations func(t *testing.T, orgName string) - }{ - "backend name strategy, to cloud name strategy, using the same name, different organization": { - operations: []operationSets{ + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + remoteWorkspace := "remote-workspace" + tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ { - prep: func(t *testing.T, orgName, dir string) { - remoteWorkspace := "remote-workspace" - tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `Successfully configured the backend "remote"!`, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "remote-workspace"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - }, + command: []string{"init"}, + expectedCmdOutput: `Successfully configured the backend "remote"!`, }, { - prep: func(t *testing.T, orgName, dir string) { - wsName := "remote-workspace" - tfBlock := terraformConfigCloudBackendName(orgName, wsName) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Do you want to copy existing state to Terraform Cloud?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Terraform Cloud has been successfully initialized!`}, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: `remote-workspace`, - }, - }, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, - validations: func(t *testing.T, orgName string) { - expectedName := "remote-workspace" - ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) - if err != nil { - t.Fatal(err) - } - if ws == nil { - t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) - } + }, + { + prep: func(t *testing.T, orgName, dir string) { + wsName := "remote-workspace" + tfBlock := terraformConfigCloudBackendName(orgName, wsName) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Do you want to copy existing state to Terraform Cloud?`, + userInput: []string{"yes"}, + postInputOutput: []string{`Terraform Cloud has been successfully initialized!`}, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: `remote-workspace`, + }, }, }, } - - for name, tc := range cases { - t.Log("Test: ", name) - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) + validations := func(t *testing.T, orgName string) { + expectedName := "remote-workspace" + ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) if err != nil { t.Fatal(err) } - defer exp.Close() - - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) + if ws == nil { + t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) } - defer os.RemoveAll(tmpDir) + } - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - orgOne, cleanupOne := createOrganization(t) - orgTwo, cleanupTwo := createOrganization(t) - defer cleanupOne() - defer cleanupTwo() - orgs := []string{orgOne.Name, orgTwo.Name} - var orgName string - for index, op := range tc.operations { - orgName = orgs[index] - op.prep(t, orgName, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) - if err != nil { - t.Fatal(err) - } - } + orgOne, cleanupOne := createOrganization(t) + orgTwo, cleanupTwo := createOrganization(t) + defer cleanupOne() + defer cleanupTwo() + orgs := []string{orgOne.Name, orgTwo.Name} + var orgName string + for index, op := range operations { + orgName = orgs[index] + op.prep(t, orgName, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - err = cmd.Wait() + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) if err != nil { t.Fatal(err) } } - } - if tc.validations != nil { - tc.validations(t, orgName) + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } } } + + if validations != nil { + validations(t, orgName) + } } func Test_migrate_remote_backend_name_to_tfc_tags(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() - cases := map[string]struct { - operations []operationSets - validations func(t *testing.T, orgName string) - }{ - "single workspace with backend name strategy, to cloud with tags strategy": { - operations: []operationSets{ + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + remoteWorkspace := "remote-workspace" + tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ { - prep: func(t *testing.T, orgName, dir string) { - remoteWorkspace := "remote-workspace" - tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `Successfully configured the backend "remote"!`, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "remote-workspace"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: `default`, - }, - }, + command: []string{"init"}, + expectedCmdOutput: `Successfully configured the backend "remote"!`, }, { - prep: func(t *testing.T, orgName, dir string) { - tag := "app" - tfBlock := terraformConfigCloudBackendTags(orgName, tag) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`, - userInput: []string{"cloud-workspace", "yes"}, - postInputOutput: []string{ - `Do you want to copy existing state to Terraform Cloud?`, - `Terraform Cloud has been successfully initialized!`}, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: `cloud-workspace`, - }, - }, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: `default`, }, }, - validations: func(t *testing.T, orgName string) { - wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{ - Tags: tfe.String("app"), - }) - if err != nil { - t.Fatal(err) - } - if len(wsList.Items) != 1 { - t.Fatalf("Expected number of workspaces to be 1, but got %d", len(wsList.Items)) - } - ws := wsList.Items[0] - if ws.Name != "cloud-workspace" { - t.Fatalf("Expected workspace to be `cloud-workspace`, but is %s", ws.Name) - } + }, + { + prep: func(t *testing.T, orgName, dir string) { + tag := "app" + tfBlock := terraformConfigCloudBackendTags(orgName, tag) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`, + userInput: []string{"cloud-workspace", "yes"}, + postInputOutput: []string{ + `Do you want to copy existing state to Terraform Cloud?`, + `Terraform Cloud has been successfully initialized!`}, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: `cloud-workspace`, + }, }, }, } - - for name, tc := range cases { - t.Log("Test: ", name) - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) + validations := func(t *testing.T, orgName string) { + wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{ + Tags: tfe.String("app"), + }) if err != nil { t.Fatal(err) } - defer exp.Close() - - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) + if len(wsList.Items) != 1 { + t.Fatalf("Expected number of workspaces to be 1, but got %d", len(wsList.Items)) } - defer os.RemoveAll(tmpDir) + ws := wsList.Items[0] + if ws.Name != "cloud-workspace" { + t.Fatalf("Expected workspace to be `cloud-workspace`, but is %s", ws.Name) + } + } - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - organization, cleanup := createOrganization(t) - defer cleanup() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) - if err != nil { - t.Fatal(err) - } - } + organization, cleanup := createOrganization(t) + defer cleanup() + for _, op := range operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - err = cmd.Wait() + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) if err != nil { t.Fatal(err) } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) - } - } -} - -func Test_migrate_remote_backend_prefix_to_tfc_name(t *testing.T) { - t.Parallel() - skipWithoutRemoteTerraformVersion(t) - - ctx := context.Background() - cases := map[string]struct { - operations []operationSets - validations func(t *testing.T, orgName string) - }{ - "single workspace with backend prefix strategy, to cloud with name strategy": { - operations: []operationSets{ - { - prep: func(t *testing.T, orgName, dir string) { - _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) - prefix := "app-" - tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `Terraform has been successfully initialized!`, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "app-one"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - }, - }, - { - prep: func(t *testing.T, orgName, dir string) { - wsName := "cloud-workspace" - tfBlock := terraformConfigCloudBackendName(orgName, wsName) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Do you want to copy existing state to Terraform Cloud?`, - userInput: []string{"yes"}, - postInputOutput: []string{ - `Terraform Cloud has been successfully initialized!`}, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: `cloud-workspace`, - }, - }, - }, - }, - validations: func(t *testing.T, orgName string) { - expectedName := "cloud-workspace" - ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) - if err != nil { - t.Fatal(err) - } - if ws == nil { - t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) - } - }, - }, - "multiple workspaces with backend prefix strategy, to cloud with name strategy": { - operations: []operationSets{ - { - prep: func(t *testing.T, orgName, dir string) { - _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) - _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")}) - prefix := "app-" - tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `The currently selected workspace (default) does not exist.`, - userInput: []string{"1"}, - postInputOutput: []string{`Terraform has been successfully initialized!`}, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "app-one"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - { - command: []string{"workspace", "list"}, - expectedCmdOutput: "* one", // app name retrieved via prefix - }, - { - command: []string{"workspace", "select", "two"}, - expectedCmdOutput: `Switched to workspace "two".`, // app name retrieved via prefix - }, - }, - }, - { - prep: func(t *testing.T, orgName, dir string) { - wsName := "cloud-workspace" - tfBlock := terraformConfigCloudBackendName(orgName, wsName) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Do you want to copy only your current workspace?`, - userInput: []string{"yes"}, - postInputOutput: []string{ - `Terraform Cloud has been successfully initialized!`}, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: `cloud-workspace`, - }, - }, - }, - }, - validations: func(t *testing.T, orgName string) { - expectedName := "cloud-workspace" - ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) - if err != nil { - t.Fatal(err) - } - if ws == nil { - t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) - } - wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{}) - if err != nil { - t.Fatal(err) - } - if len(wsList.Items) != 3 { - t.Fatalf("expected number of workspaces in this org to be 3, but got %d", len(wsList.Items)) - } - ws, empty := getWorkspace(wsList.Items, "cloud-workspace") - if empty { - t.Fatalf("expected workspaces to include 'cloud-workspace' but didn't.") - } - ws, empty = getWorkspace(wsList.Items, "app-one") - if empty { - t.Fatalf("expected workspaces to include 'app-one' but didn't.") - } - ws, empty = getWorkspace(wsList.Items, "app-two") - if empty { - t.Fatalf("expected workspaces to include 'app-two' but didn't.") - } - }, - }, - } - - for name, tc := range cases { - t.Log("Test: ", name) - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() - - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) - - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() - - organization, cleanup := createOrganization(t) - defer cleanup() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() - - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) - if err != nil { - t.Fatal(err) - } - } - - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) } } } + } - err = cmd.Wait() + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } + } + } + + if validations != nil { + validations(t, organization.Name) + } +} + +func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_single_workspace(t *testing.T) { + skipWithoutRemoteTerraformVersion(t) + + ctx := context.Background() + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) + prefix := "app-" + tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init"}, + expectedCmdOutput: `Terraform has been successfully initialized!`, + }, + { + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, + }, + }, + }, + { + prep: func(t *testing.T, orgName, dir string) { + wsName := "cloud-workspace" + tfBlock := terraformConfigCloudBackendName(orgName, wsName) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Do you want to copy existing state to Terraform Cloud?`, + userInput: []string{"yes"}, + postInputOutput: []string{ + `Terraform Cloud has been successfully initialized!`}, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: `cloud-workspace`, + }, + }, + }, + } + validations := func(t *testing.T, orgName string) { + expectedName := "cloud-workspace" + ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) + if err != nil { + t.Fatal(err) + } + if ws == nil { + t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) + } + } + + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() + + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() + + organization, cleanup := createOrganization(t) + defer cleanup() + for _, op := range operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() + + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) if err != nil { t.Fatal(err) } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) - } - } -} - -func Test_migrate_remote_backend_prefix_to_tfc_tags(t *testing.T) { - t.Parallel() - skipWithoutRemoteTerraformVersion(t) - - ctx := context.Background() - cases := map[string]struct { - operations []operationSets - validations func(t *testing.T, orgName string) - }{ - "single workspace with backend prefix strategy, to cloud with tags strategy": { - operations: []operationSets{ - { - prep: func(t *testing.T, orgName, dir string) { - _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) - prefix := "app-" - tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `Terraform has been successfully initialized!`, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "app-one"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - }, - }, - { - prep: func(t *testing.T, orgName, dir string) { - tag := "app" - tfBlock := terraformConfigCloudBackendTags(orgName, tag) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`, - userInput: []string{"cloud-workspace", "yes"}, - postInputOutput: []string{ - `Do you want to copy existing state to Terraform Cloud?`, - `Terraform Cloud has been successfully initialized!`}, - }, - { - command: []string{"workspace", "list"}, - expectedCmdOutput: `cloud-workspace`, - }, - }, - }, - }, - validations: func(t *testing.T, orgName string) { - expectedName := "cloud-workspace" - ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) - if err != nil { - t.Fatal(err) - } - if ws == nil { - t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) - } - }, - }, - "multiple workspaces with backend prefix strategy, to cloud with tags strategy": { - operations: []operationSets{ - { - prep: func(t *testing.T, orgName, dir string) { - _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) - _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")}) - prefix := "app-" - tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init"}, - expectedCmdOutput: `The currently selected workspace (default) does not exist.`, - userInput: []string{"1"}, - postInputOutput: []string{`Terraform has been successfully initialized!`}, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "app-one"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - { - command: []string{"workspace", "select", "two"}, - }, - { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "app-two"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, - }, - }, - }, - { - prep: func(t *testing.T, orgName, dir string) { - tag := "app" - tfBlock := terraformConfigCloudBackendTags(orgName, tag) - writeMainTF(t, tfBlock, dir) - }, - commands: []tfCommand{ - { - command: []string{"init", "-migrate-state", "-ignore-remote-version"}, - expectedCmdOutput: `Would you like to rename your workspaces?`, - userInput: []string{"1", "*"}, - postInputOutput: []string{`What pattern would you like to add to all your workspaces?`, - `Terraform Cloud has been successfully initialized!`}, - }, - { - command: []string{"workspace", "show"}, - expectedCmdOutput: "two", // this comes from the original workspace name from the previous backend. - }, - { - command: []string{"workspace", "select", "one"}, - expectedCmdOutput: `Switched to workspace "one".`, // this comes from the original workspace name from the previous backend. - }, - }, - }, - }, - validations: func(t *testing.T, orgName string) { - wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{ - Tags: tfe.String("app"), - }) - if err != nil { - t.Fatal(err) - } - if len(wsList.Items) != 2 { - t.Logf("Expected the number of workspaces to be 2, but got %d", len(wsList.Items)) - } - ws, empty := getWorkspace(wsList.Items, "one") - if empty { - t.Fatalf("expected workspaces to include 'one' but didn't.") - } - if len(ws.TagNames) == 0 { - t.Fatalf("expected workspaces 'one' to have tags.") - } - ws, empty = getWorkspace(wsList.Items, "two") - if empty { - t.Fatalf("expected workspaces to include 'two' but didn't.") - } - if len(ws.TagNames) == 0 { - t.Fatalf("expected workspaces 'two' to have tags.") - } - }, - }, - } - - for name, tc := range cases { - t.Log("Test: ", name) - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() - - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) - - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() - - organization, cleanup := createOrganization(t) - defer cleanup() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() - - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) - if err != nil { - t.Fatal(err) - } - } - - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) } } } + } - err = cmd.Wait() + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } + } + } + + if validations != nil { + validations(t, organization.Name) + } +} + +func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_multi_workspace(t *testing.T) { + skipWithoutRemoteTerraformVersion(t) + + ctx := context.Background() + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) + _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")}) + prefix := "app-" + tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init"}, + expectedCmdOutput: `The currently selected workspace (default) does not exist.`, + userInput: []string{"1"}, + postInputOutput: []string{`Terraform has been successfully initialized!`}, + }, + { + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, + }, + { + command: []string{"workspace", "list"}, + expectedCmdOutput: "* one", // app name retrieved via prefix + }, + { + command: []string{"workspace", "select", "two"}, + expectedCmdOutput: `Switched to workspace "two".`, // app name retrieved via prefix + }, + }, + }, + { + prep: func(t *testing.T, orgName, dir string) { + wsName := "cloud-workspace" + tfBlock := terraformConfigCloudBackendName(orgName, wsName) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Do you want to copy only your current workspace?`, + userInput: []string{"yes"}, + postInputOutput: []string{ + `Terraform Cloud has been successfully initialized!`}, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: `cloud-workspace`, + }, + }, + }, + } + validations := func(t *testing.T, orgName string) { + expectedName := "cloud-workspace" + ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) + if err != nil { + t.Fatal(err) + } + if ws == nil { + t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) + } + wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{}) + if err != nil { + t.Fatal(err) + } + if len(wsList.Items) != 3 { + t.Fatalf("expected number of workspaces in this org to be 3, but got %d", len(wsList.Items)) + } + ws, empty := getWorkspace(wsList.Items, "cloud-workspace") + if empty { + t.Fatalf("expected workspaces to include 'cloud-workspace' but didn't.") + } + ws, empty = getWorkspace(wsList.Items, "app-one") + if empty { + t.Fatalf("expected workspaces to include 'app-one' but didn't.") + } + ws, empty = getWorkspace(wsList.Items, "app-two") + if empty { + t.Fatalf("expected workspaces to include 'app-two' but didn't.") + } + } + + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() + + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() + + organization, cleanup := createOrganization(t) + defer cleanup() + for _, op := range operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() + + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) if err != nil { t.Fatal(err) } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } } } + + if validations != nil { + validations(t, organization.Name) + } +} + +func Test_migrate_remote_backend_prefix_to_tfc_tags_strategy_single_workspace(t *testing.T) { + skipWithoutRemoteTerraformVersion(t) + + ctx := context.Background() + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) + prefix := "app-" + tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init"}, + expectedCmdOutput: `Terraform has been successfully initialized!`, + }, + { + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, + }, + }, + }, + { + prep: func(t *testing.T, orgName, dir string) { + tag := "app" + tfBlock := terraformConfigCloudBackendTags(orgName, tag) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`, + userInput: []string{"cloud-workspace", "yes"}, + postInputOutput: []string{ + `Do you want to copy existing state to Terraform Cloud?`, + `Terraform Cloud has been successfully initialized!`}, + }, + { + command: []string{"workspace", "list"}, + expectedCmdOutput: `cloud-workspace`, + }, + }, + }, + } + validations := func(t *testing.T, orgName string) { + expectedName := "cloud-workspace" + ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName) + if err != nil { + t.Fatal(err) + } + if ws == nil { + t.Fatalf("Expected workspace %s to be present, but is not.", expectedName) + } + } + + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() + + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() + + organization, cleanup := createOrganization(t) + defer cleanup() + for _, op := range operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() + + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } + } + } + + if validations != nil { + validations(t, organization.Name) + } +} + +func Test_migrate_remote_backend_prefix_to_tfc_tags_strategy_multi_workspace(t *testing.T) { + skipWithoutRemoteTerraformVersion(t) + + ctx := context.Background() + operations := []operationSets{ + { + prep: func(t *testing.T, orgName, dir string) { + _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")}) + _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")}) + prefix := "app-" + tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init"}, + expectedCmdOutput: `The currently selected workspace (default) does not exist.`, + userInput: []string{"1"}, + postInputOutput: []string{`Terraform has been successfully initialized!`}, + }, + { + command: []string{"apply"}, + expectedCmdOutput: `Do you want to perform these actions in workspace "app-one"?`, + userInput: []string{"yes"}, + postInputOutput: []string{`Apply complete!`}, + }, + { + command: []string{"workspace", "select", "two"}, + }, + { + command: []string{"apply"}, + expectedCmdOutput: `Do you want to perform these actions in workspace "app-two"?`, + userInput: []string{"yes"}, + postInputOutput: []string{`Apply complete!`}, + }, + }, + }, + { + prep: func(t *testing.T, orgName, dir string) { + tag := "app" + tfBlock := terraformConfigCloudBackendTags(orgName, tag) + writeMainTF(t, tfBlock, dir) + }, + commands: []tfCommand{ + { + command: []string{"init", "-migrate-state", "-ignore-remote-version"}, + expectedCmdOutput: `Do you wish to proceed?`, + userInput: []string{"yes"}, + postInputOutput: []string{`Terraform Cloud has been successfully initialized!`}, + }, + { + command: []string{"workspace", "show"}, + expectedCmdOutput: "app-two", + }, + { + command: []string{"workspace", "select", "app-one"}, + expectedCmdOutput: `Switched to workspace "app-one".`, + }, + }, + }, + } + validations := func(t *testing.T, orgName string) { + wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{ + Tags: tfe.String("app"), + }) + if err != nil { + t.Fatal(err) + } + if len(wsList.Items) != 2 { + t.Logf("Expected the number of workspaces to be 2, but got %d", len(wsList.Items)) + } + ws, empty := getWorkspace(wsList.Items, "app-one") + if empty { + t.Fatalf("expected workspaces to include 'app-one' but didn't.") + } + if len(ws.TagNames) == 0 { + t.Fatalf("expected workspaces 'one' to have tags.") + } + ws, empty = getWorkspace(wsList.Items, "app-two") + if empty { + t.Fatalf("expected workspaces to include 'app-two' but didn't.") + } + if len(ws.TagNames) == 0 { + t.Fatalf("expected workspaces 'app-two' to have tags.") + } + } + + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() + + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() + + organization, cleanup := createOrganization(t) + defer cleanup() + for _, op := range operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() + + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } + } + } + + if validations != nil { + validations(t, organization.Name) + } } diff --git a/internal/cloud/e2e/migrate_state_single_to_tfc_test.go b/internal/cloud/e2e/migrate_state_single_to_tfc_test.go index d8317ca69..14c84eef9 100644 --- a/internal/cloud/e2e/migrate_state_single_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_single_to_tfc_test.go @@ -15,7 +15,6 @@ import ( ) func Test_migrate_single_to_tfc(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -37,10 +36,8 @@ func Test_migrate_single_to_tfc(t *testing.T) { expectedCmdOutput: `Successfully configured the backend "local"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, }, @@ -88,10 +85,8 @@ func Test_migrate_single_to_tfc(t *testing.T) { expectedCmdOutput: `Successfully configured the backend "local"!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, }, @@ -133,73 +128,75 @@ func Test_migrate_single_to_tfc(t *testing.T) { } for name, tc := range cases { - t.Log("Test: ", name) - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=info") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() if err != nil { t.Fatal(err) } } - - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } - - err = cmd.Wait() - if err != nil { - t.Fatal(err) - } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) - } + if tc.validations != nil { + tc.validations(t, organization.Name) + } + }) } } diff --git a/internal/cloud/e2e/migrate_state_tfc_to_other_test.go b/internal/cloud/e2e/migrate_state_tfc_to_other_test.go index 3de277843..6e8b3a901 100644 --- a/internal/cloud/e2e/migrate_state_tfc_to_other_test.go +++ b/internal/cloud/e2e/migrate_state_tfc_to_other_test.go @@ -4,7 +4,6 @@ package main import ( - "fmt" "io/ioutil" "os" "testing" @@ -14,7 +13,6 @@ import ( ) func Test_migrate_tfc_to_other(t *testing.T) { - t.Parallel() cases := map[string]struct { operations []operationSets }{ @@ -51,68 +49,70 @@ func Test_migrate_tfc_to_other(t *testing.T) { } for name, tc := range cases { - fmt.Println("Test: ", name) - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=info") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + err = cmd.Start() if err != nil { t.Fatal(err) } - } - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } } } } - } - err = cmd.Wait() - if err != nil && !tfCmd.expectError { - t.Fatal(err) + err = cmd.Wait() + if err != nil && !tfCmd.expectError { + t.Fatal(err) + } } } - } + }) } } diff --git a/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go b/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go index 6ef83fcfb..496bf9881 100644 --- a/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go @@ -16,7 +16,6 @@ import ( ) func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -55,10 +54,8 @@ func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { expectedCmdOutput: `prod`, // this comes from the `prep` function }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "prod"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, }, @@ -119,10 +116,8 @@ func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { expectedCmdOutput: `Terraform Cloud has been successfully initialized!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "prod"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, }, @@ -183,10 +178,8 @@ func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { expectedCmdOutput: `Terraform Cloud has been successfully initialized!`, }, { - command: []string{"apply"}, - expectedCmdOutput: `Do you want to perform these actions in workspace "prod"?`, - userInput: []string{"yes"}, - postInputOutput: []string{`Apply complete!`}, + command: []string{"apply", "-auto-approve"}, + postInputOutput: []string{`Apply complete!`}, }, }, }, @@ -214,95 +207,92 @@ func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { }, }, validations: func(t *testing.T, orgName string) { - wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{ - Tags: tfe.String("app"), - }) + // We created the workspace, so it will be there. We could not complete the state migration, + // though, so the workspace should be empty. + ws, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, "new-workspace", &tfe.WorkspaceReadOptions{Include: "current_run"}) if err != nil { t.Fatal(err) } - // The migration never occured, so we have no workspaces with this tag. - if len(wsList.Items) != 0 { - t.Fatalf("Expected number of workspaces to be 0, but got %d", len(wsList.Items)) + if ws.CurrentRun != nil { + t.Fatal("Expected to workspace be empty") } }, }, } for name, tc := range cases { - t.Log("Test: ", name) - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + t.Run(name, func(t *testing.T) { + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - defer tf.Close() - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) + tf := e2e.NewBinary(terraformBin, tmpDir) + defer tf.Close() + tf.AddEnv(cliConfigFileEnv) - orgName, cleanup := tc.setup(t) - defer cleanup() - for _, op := range tc.operations { - op.prep(t, orgName, tf.WorkDir()) - for _, tfCmd := range op.commands { - t.Log("Running commands: ", tfCmd.command) - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + orgName, cleanup := tc.setup(t) + defer cleanup() + for _, op := range tc.operations { + op.prep(t, orgName, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + err = cmd.Start() if err != nil { t.Fatal(err) } - } - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } } } } - } - err = cmd.Wait() - if err != nil && !tfCmd.expectError { - t.Fatal(err.Error()) + err = cmd.Wait() + if err != nil && !tfCmd.expectError { + t.Fatal(err.Error()) + } } } - } - if tc.validations != nil { - tc.validations(t, orgName) - } + if tc.validations != nil { + tc.validations(t, orgName) + } + }) } } func Test_migrate_tfc_to_tfc_multiple_workspace(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -454,7 +444,6 @@ func Test_migrate_tfc_to_tfc_multiple_workspace(t *testing.T) { tag := "billing" tfBlock := terraformConfigCloudBackendTags(orgName, tag) writeMainTF(t, tfBlock, dir) - t.Log(orgName) }, commands: []tfCommand{ { @@ -462,8 +451,7 @@ func Test_migrate_tfc_to_tfc_multiple_workspace(t *testing.T) { expectedCmdOutput: `Would you like to rename your workspaces?`, userInput: []string{"1", "new-*", "1"}, postInputOutput: []string{ - `What pattern would you like to add to all your workspaces?`, - `The currently selected workspace (app-staging) does not exist.`, + `How would you like to rename your workspaces?`, `Terraform Cloud has been successfully initialized!`}, }, }, @@ -492,75 +480,73 @@ func Test_migrate_tfc_to_tfc_multiple_workspace(t *testing.T) { } for name, tc := range cases { - t.Log("Test: ", name) - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + t.Run(name, func(t *testing.T) { + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - defer tf.Close() - tf.AddEnv("TF_LOG=INFO") - tf.AddEnv(cliConfigFileEnv) + tf := e2e.NewBinary(terraformBin, tmpDir) + defer tf.Close() + tf.AddEnv(cliConfigFileEnv) - orgName, cleanup := tc.setup(t) - defer cleanup() - for _, op := range tc.operations { - op.prep(t, orgName, tf.WorkDir()) - for _, tfCmd := range op.commands { - t.Log("Running commands: ", tfCmd.command) - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + orgName, cleanup := tc.setup(t) + defer cleanup() + for _, op := range tc.operations { + op.prep(t, orgName, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + err = cmd.Start() if err != nil { t.Fatal(err) } - } - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatal(err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } } } } - } - t.Log(cmd.Stderr) - err = cmd.Wait() - if err != nil { - t.Fatal(err.Error()) + err = cmd.Wait() + if err != nil { + t.Fatal(err.Error()) + } } } - } - if tc.validations != nil { - tc.validations(t, orgName) - } + if tc.validations != nil { + tc.validations(t, orgName) + } + }) } } diff --git a/internal/cloud/e2e/run_variables_test.go b/internal/cloud/e2e/run_variables_test.go index e13381ea1..5f4f40cec 100644 --- a/internal/cloud/e2e/run_variables_test.go +++ b/internal/cloud/e2e/run_variables_test.go @@ -46,7 +46,6 @@ output "test_env" { } func Test_cloud_run_variables(t *testing.T) { - t.Parallel() skipWithoutRemoteTerraformVersion(t) cases := testCases{ @@ -78,75 +77,75 @@ func Test_cloud_run_variables(t *testing.T) { } for name, tc := range cases { - fmt.Println("Test: ", name) - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + t.Run(name, func(t *testing.T) { + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=info") - tf.AddEnv("TF_CLI_ARGS=-no-color") - tf.AddEnv("TF_VAR_baz=qux") - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv("TF_CLI_ARGS=-no-color") + tf.AddEnv("TF_VAR_baz=qux") + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + err = cmd.Start() if err != nil { t.Fatal(err) } + + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) + if err != nil { + t.Fatalf(`Expected command output "%s", but got %v `, tfCmd.expectedCmdOutput, err) + } + } + + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatalf(`Expected command output "%s", but got %v `, tfCmd.expectedCmdOutput, err) + } + } + } + } + + err = cmd.Wait() + if err != nil && !tfCmd.expectError { + t.Fatal(err) + } } - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } - - err = cmd.Wait() - if err != nil && !tfCmd.expectError { - t.Fatal(err) + if tc.validations != nil { + tc.validations(t, organization.Name) } } - - if tc.validations != nil { - tc.validations(t, organization.Name) - } - } + }) } } From 57a4b51e87686f70f967c28661c23d03f24af12c Mon Sep 17 00:00:00 2001 From: Omar Ismail Date: Thu, 11 Nov 2021 15:21:21 -0500 Subject: [PATCH 2/3] remove build tags for cloud e2e --- internal/cloud/e2e/README.md | 3 +-- internal/cloud/e2e/apply_auto_approve_test.go | 3 --- .../cloud/e2e/backend_apply_before_init_test.go | 3 --- internal/cloud/e2e/helper_test.go | 8 ++------ internal/cloud/e2e/init_with_empty_tags_test.go | 3 --- internal/cloud/e2e/main_test.go | 16 +++++++++++----- .../cloud/e2e/migrate_state_multi_to_tfc_test.go | 4 ---- .../migrate_state_remote_backend_to_tfc_test.go | 9 +++------ .../e2e/migrate_state_single_to_tfc_test.go | 3 --- .../cloud/e2e/migrate_state_tfc_to_other_test.go | 3 --- .../cloud/e2e/migrate_state_tfc_to_tfc_test.go | 3 --- internal/cloud/e2e/run_variables_test.go | 9 ++++++--- 12 files changed, 23 insertions(+), 44 deletions(-) diff --git a/internal/cloud/e2e/README.md b/internal/cloud/e2e/README.md index 02a50bbfe..7928fc3f7 100644 --- a/internal/cloud/e2e/README.md +++ b/internal/cloud/e2e/README.md @@ -2,11 +2,10 @@ To run them, use: ``` -TF_ACC=1 go test -tags=e2e ./internal/cloud/e2e/... -ldflags "-X \"github.com/hashicorp/terraform/version.Prerelease=\"" +TFE_TOKEN= TFE_HOSTNAME= TF_ACC=1 go test ./internal/cloud/e2e/... -ldflags "-X \"github.com/hashicorp/terraform/version.Prerelease=\"" ``` Required flags -* `-tags=e2e` for running e2e tests. * `TF_ACC=1`. This variable is used as part of terraform for tests that make external network calls. This is needed to run these tests. Without it, the tests do not run. diff --git a/internal/cloud/e2e/apply_auto_approve_test.go b/internal/cloud/e2e/apply_auto_approve_test.go index 1689caedb..41bbb1178 100644 --- a/internal/cloud/e2e/apply_auto_approve_test.go +++ b/internal/cloud/e2e/apply_auto_approve_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( diff --git a/internal/cloud/e2e/backend_apply_before_init_test.go b/internal/cloud/e2e/backend_apply_before_init_test.go index 48d3f3d40..4115ecfab 100644 --- a/internal/cloud/e2e/backend_apply_before_init_test.go +++ b/internal/cloud/e2e/backend_apply_before_init_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( diff --git a/internal/cloud/e2e/helper_test.go b/internal/cloud/e2e/helper_test.go index 87ed67654..6c65ba983 100644 --- a/internal/cloud/e2e/helper_test.go +++ b/internal/cloud/e2e/helper_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( @@ -24,7 +21,6 @@ const ( type tfCommand struct { command []string expectedCmdOutput string - expectedErr string expectError bool userInput []string postInputOutput []string @@ -105,7 +101,7 @@ func randomString(t *testing.T) string { } func terraformConfigLocalBackend() string { - return fmt.Sprintf(` + return ` terraform { backend "local" { } @@ -114,7 +110,7 @@ terraform { output "val" { value = "${terraform.workspace}" } -`) +` } func terraformConfigRemoteBackendName(org, name string) string { diff --git a/internal/cloud/e2e/init_with_empty_tags_test.go b/internal/cloud/e2e/init_with_empty_tags_test.go index 15bc3ba18..ab74ef9d0 100644 --- a/internal/cloud/e2e/init_with_empty_tags_test.go +++ b/internal/cloud/e2e/init_with_empty_tags_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( diff --git a/internal/cloud/e2e/main_test.go b/internal/cloud/e2e/main_test.go index 783dc4d20..656c093fa 100644 --- a/internal/cloud/e2e/main_test.go +++ b/internal/cloud/e2e/main_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( @@ -27,8 +24,9 @@ var verboseMode bool func TestMain(m *testing.M) { log.SetFlags(log.LstdFlags | log.Lshortfile) - if !accTest() { - // if TF_ACC is not set, we want to skip all these tests. + hasRequiredEnvVars := accTest() && hasHostname() && hasToken() + if !hasRequiredEnvVars { + // if the above three required variables are not set, then skip all tests. return } teardown := setup() @@ -44,6 +42,14 @@ func accTest() bool { return os.Getenv("TF_ACC") != "" } +func hasHostname() bool { + return os.Getenv("TFE_HOSTNAME") != "" +} + +func hasToken() bool { + return os.Getenv("TFE_TOKEN") != "" +} + func setup() func() { tfOutput := flag.Bool("tfoutput", false, "This flag produces the terraform output from tests.") flag.Parse() diff --git a/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go b/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go index 07c4b04f8..7e1ff542b 100644 --- a/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( @@ -186,7 +183,6 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { for _, op := range tc.operations { op.prep(t, organization.Name, tf.WorkDir()) for _, tfCmd := range op.commands { - tfCmd.command = append(tfCmd.command) cmd := tf.Cmd(tfCmd.command...) cmd.Stdin = exp.Tty() cmd.Stdout = exp.Tty() diff --git a/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go b/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go index 819875e3f..b50228192 100644 --- a/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( @@ -705,15 +702,15 @@ func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_multi_workspace(t * if len(wsList.Items) != 3 { t.Fatalf("expected number of workspaces in this org to be 3, but got %d", len(wsList.Items)) } - ws, empty := getWorkspace(wsList.Items, "cloud-workspace") + _, empty := getWorkspace(wsList.Items, "cloud-workspace") if empty { t.Fatalf("expected workspaces to include 'cloud-workspace' but didn't.") } - ws, empty = getWorkspace(wsList.Items, "app-one") + _, empty = getWorkspace(wsList.Items, "app-one") if empty { t.Fatalf("expected workspaces to include 'app-one' but didn't.") } - ws, empty = getWorkspace(wsList.Items, "app-two") + _, empty = getWorkspace(wsList.Items, "app-two") if empty { t.Fatalf("expected workspaces to include 'app-two' but didn't.") } diff --git a/internal/cloud/e2e/migrate_state_single_to_tfc_test.go b/internal/cloud/e2e/migrate_state_single_to_tfc_test.go index 14c84eef9..e171f048a 100644 --- a/internal/cloud/e2e/migrate_state_single_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_single_to_tfc_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( diff --git a/internal/cloud/e2e/migrate_state_tfc_to_other_test.go b/internal/cloud/e2e/migrate_state_tfc_to_other_test.go index 6e8b3a901..3da776c3f 100644 --- a/internal/cloud/e2e/migrate_state_tfc_to_other_test.go +++ b/internal/cloud/e2e/migrate_state_tfc_to_other_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( diff --git a/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go b/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go index 496bf9881..64204565d 100644 --- a/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( diff --git a/internal/cloud/e2e/run_variables_test.go b/internal/cloud/e2e/run_variables_test.go index 5f4f40cec..b4d3569a2 100644 --- a/internal/cloud/e2e/run_variables_test.go +++ b/internal/cloud/e2e/run_variables_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( @@ -10,7 +7,9 @@ import ( "testing" expect "github.com/Netflix/go-expect" + tfe "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/internal/e2e" + tfversion "github.com/hashicorp/terraform/version" ) func terraformConfigRequiredVariable(org, name string) string { @@ -54,6 +53,10 @@ func Test_cloud_run_variables(t *testing.T) { { prep: func(t *testing.T, orgName, dir string) { wsName := "new-workspace" + _ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{ + Name: tfe.String(wsName), + TerraformVersion: tfe.String(tfversion.String()), + }) tfBlock := terraformConfigRequiredVariable(orgName, wsName) writeMainTF(t, tfBlock, dir) }, From 9b675f8b70b93dcce67ee536617dae1b49162b17 Mon Sep 17 00:00:00 2001 From: Omar Ismail Date: Fri, 12 Nov 2021 12:00:24 -0500 Subject: [PATCH 3/3] Add skip for cloud e2e tests when env vars missing --- internal/cloud/e2e/apply_auto_approve_test.go | 117 +++++++++--------- .../e2e/backend_apply_before_init_test.go | 1 + .../cloud/e2e/init_with_empty_tags_test.go | 1 + internal/cloud/e2e/main_test.go | 63 +++++----- .../e2e/migrate_state_multi_to_tfc_test.go | 2 + ...igrate_state_remote_backend_to_tfc_test.go | 8 ++ .../e2e/migrate_state_single_to_tfc_test.go | 1 + .../e2e/migrate_state_tfc_to_other_test.go | 1 + .../e2e/migrate_state_tfc_to_tfc_test.go | 2 + internal/cloud/e2e/run_variables_test.go | 1 + 10 files changed, 108 insertions(+), 89 deletions(-) diff --git a/internal/cloud/e2e/apply_auto_approve_test.go b/internal/cloud/e2e/apply_auto_approve_test.go index 41bbb1178..9f086c88e 100644 --- a/internal/cloud/e2e/apply_auto_approve_test.go +++ b/internal/cloud/e2e/apply_auto_approve_test.go @@ -13,6 +13,7 @@ import ( ) func Test_terraform_apply_autoApprove(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -178,76 +179,72 @@ func Test_terraform_apply_autoApprove(t *testing.T) { }, }, } - for name, tc := range cases { - tc := tc - t.Run(name, func(t *testing.T) { - t.Parallel() - organization, cleanup := createOrganization(t) - defer cleanup() - exp, err := expect.NewConsole(defaultOpts()...) - if err != nil { - t.Fatal(err) - } - defer exp.Close() + for _, tc := range cases { + organization, cleanup := createOrganization(t) + defer cleanup() + exp, err := expect.NewConsole(defaultOpts()...) + if err != nil { + t.Fatal(err) + } + defer exp.Close() - tmpDir, err := ioutil.TempDir("", "terraform-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir, err := ioutil.TempDir("", "terraform-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) - tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv(cliConfigFileEnv) - defer tf.Close() + tf := e2e.NewBinary(terraformBin, tmpDir) + tf.AddEnv(cliConfigFileEnv) + defer tf.Close() - for _, op := range tc.operations { - op.prep(t, organization.Name, tf.WorkDir()) - for _, tfCmd := range op.commands { - cmd := tf.Cmd(tfCmd.command...) - cmd.Stdin = exp.Tty() - cmd.Stdout = exp.Tty() - cmd.Stderr = exp.Tty() + for _, op := range tc.operations { + op.prep(t, organization.Name, tf.WorkDir()) + for _, tfCmd := range op.commands { + cmd := tf.Cmd(tfCmd.command...) + cmd.Stdin = exp.Tty() + cmd.Stdout = exp.Tty() + cmd.Stderr = exp.Tty() - err = cmd.Start() - if err != nil { - t.Fatal(err) - } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } - if tfCmd.expectedCmdOutput != "" { - _, err := exp.ExpectString(tfCmd.expectedCmdOutput) - if err != nil { - t.Fatal(err) - } - } - - lenInput := len(tfCmd.userInput) - lenInputOutput := len(tfCmd.postInputOutput) - if lenInput > 0 { - for i := 0; i < lenInput; i++ { - input := tfCmd.userInput[i] - exp.SendLine(input) - // use the index to find the corresponding - // output that matches the input. - if lenInputOutput-1 >= i { - output := tfCmd.postInputOutput[i] - _, err := exp.ExpectString(output) - if err != nil { - t.Fatal(err) - } - } - } - } - - err = cmd.Wait() + if tfCmd.expectedCmdOutput != "" { + _, err := exp.ExpectString(tfCmd.expectedCmdOutput) if err != nil { t.Fatal(err) } } - } - if tc.validations != nil { - tc.validations(t, organization.Name) + lenInput := len(tfCmd.userInput) + lenInputOutput := len(tfCmd.postInputOutput) + if lenInput > 0 { + for i := 0; i < lenInput; i++ { + input := tfCmd.userInput[i] + exp.SendLine(input) + // use the index to find the corresponding + // output that matches the input. + if lenInputOutput-1 >= i { + output := tfCmd.postInputOutput[i] + _, err := exp.ExpectString(output) + if err != nil { + t.Fatal(err) + } + } + } + } + + err = cmd.Wait() + if err != nil { + t.Fatal(err) + } } - }) + } + + if tc.validations != nil { + tc.validations(t, organization.Name) + } } } diff --git a/internal/cloud/e2e/backend_apply_before_init_test.go b/internal/cloud/e2e/backend_apply_before_init_test.go index 4115ecfab..a5c9fee5a 100644 --- a/internal/cloud/e2e/backend_apply_before_init_test.go +++ b/internal/cloud/e2e/backend_apply_before_init_test.go @@ -10,6 +10,7 @@ import ( ) func Test_backend_apply_before_init(t *testing.T) { + skipIfMissingEnvVar(t) t.Parallel() skipWithoutRemoteTerraformVersion(t) diff --git a/internal/cloud/e2e/init_with_empty_tags_test.go b/internal/cloud/e2e/init_with_empty_tags_test.go index ab74ef9d0..ca9743d5e 100644 --- a/internal/cloud/e2e/init_with_empty_tags_test.go +++ b/internal/cloud/e2e/init_with_empty_tags_test.go @@ -10,6 +10,7 @@ import ( ) func Test_init_with_empty_tags(t *testing.T) { + skipIfMissingEnvVar(t) t.Parallel() skipWithoutRemoteTerraformVersion(t) diff --git a/internal/cloud/e2e/main_test.go b/internal/cloud/e2e/main_test.go index 656c093fa..d758eb65d 100644 --- a/internal/cloud/e2e/main_test.go +++ b/internal/cloud/e2e/main_test.go @@ -23,12 +23,6 @@ var tfeToken string var verboseMode bool func TestMain(m *testing.M) { - log.SetFlags(log.LstdFlags | log.Lshortfile) - hasRequiredEnvVars := accTest() && hasHostname() && hasToken() - if !hasRequiredEnvVars { - // if the above three required variables are not set, then skip all tests. - return - } teardown := setup() code := m.Run() teardown() @@ -50,6 +44,16 @@ func hasToken() bool { return os.Getenv("TFE_TOKEN") != "" } +func hasRequiredEnvVars() bool { + return accTest() && hasHostname() && hasToken() +} + +func skipIfMissingEnvVar(t *testing.T) { + if !hasRequiredEnvVars() { + t.Skip("Skipping test, required environment variables missing. Use `TF_ACC`, `TFE_HOSTNAME`, `TFE_TOKEN`") + } +} + func setup() func() { tfOutput := flag.Bool("tfoutput", false, "This flag produces the terraform output from tests.") flag.Parse() @@ -64,41 +68,38 @@ func setup() func() { } func setTfeClient() { - hostname := os.Getenv("TFE_HOSTNAME") - token := os.Getenv("TFE_TOKEN") - if hostname == "" { - log.Fatal("hostname cannot be empty") - } - if token == "" { - log.Fatal("token cannot be empty") - } - tfeHostname = hostname - tfeToken = token + tfeHostname = os.Getenv("TFE_HOSTNAME") + tfeToken = os.Getenv("TFE_TOKEN") cfg := &tfe.Config{ - Address: fmt.Sprintf("https://%s", hostname), - Token: token, + Address: fmt.Sprintf("https://%s", tfeHostname), + Token: tfeToken, } - // Create a new TFE client. - client, err := tfe.NewClient(cfg) - if err != nil { - log.Fatal(err) + if tfeHostname != "" && tfeToken != "" { + // Create a new TFE client. + client, err := tfe.NewClient(cfg) + if err != nil { + fmt.Printf("Could not create new tfe client: %v\n", err) + os.Exit(1) + } + tfeClient = client } - tfeClient = client } func setupBinary() func() { log.Println("Setting up terraform binary") tmpTerraformBinaryDir, err := ioutil.TempDir("", "terraform-test") if err != nil { - log.Fatal(err) + fmt.Printf("Could not create temp directory: %v\n", err) + os.Exit(1) } log.Println(tmpTerraformBinaryDir) currentDir, err := os.Getwd() defer os.Chdir(currentDir) if err != nil { - log.Fatal(err) + fmt.Printf("Could not change directories: %v\n", err) + os.Exit(1) } // Getting top level dir dirPaths := strings.Split(currentDir, "/") @@ -107,7 +108,8 @@ func setupBinary() func() { topDir := strings.Join(dirPaths[0:topLevel], "/") if err := os.Chdir(topDir); err != nil { - log.Fatal(err) + fmt.Printf("Could not change directories: %v\n", err) + os.Exit(1) } cmd := exec.Command( @@ -118,7 +120,8 @@ func setupBinary() func() { ) err = cmd.Run() if err != nil { - log.Fatal(err) + fmt.Printf("Could not run exec command: %v\n", err) + os.Exit(1) } credFile := fmt.Sprintf("%s/dev.tfrc", tmpTerraformBinaryDir) @@ -136,11 +139,13 @@ func writeCredRC(file string) { creds := credentialBlock() f, err := os.Create(file) if err != nil { - log.Fatal(err) + fmt.Printf("Could not create file: %v\n", err) + os.Exit(1) } _, err = f.WriteString(creds) if err != nil { - log.Fatal(err) + fmt.Printf("Could not write credentials: %v\n", err) + os.Exit(1) } f.Close() } diff --git a/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go b/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go index 7e1ff542b..950f3859e 100644 --- a/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_multi_to_tfc_test.go @@ -13,6 +13,7 @@ import ( ) func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -233,6 +234,7 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { } func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() diff --git a/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go b/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go index b50228192..76ef0a34c 100644 --- a/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go @@ -12,6 +12,7 @@ import ( ) func Test_migrate_remote_backend_name_to_tfc_name(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -133,6 +134,7 @@ func Test_migrate_remote_backend_name_to_tfc_name(t *testing.T) { } func Test_migrate_remote_backend_name_to_tfc_same_name(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() operations := []operationSets{ @@ -251,6 +253,7 @@ func Test_migrate_remote_backend_name_to_tfc_same_name(t *testing.T) { } func Test_migrate_remote_backend_name_to_tfc_name_different_org(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -377,6 +380,7 @@ func Test_migrate_remote_backend_name_to_tfc_name_different_org(t *testing.T) { } func Test_migrate_remote_backend_name_to_tfc_tags(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -509,6 +513,7 @@ func Test_migrate_remote_backend_name_to_tfc_tags(t *testing.T) { } func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_single_workspace(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -632,6 +637,7 @@ func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_single_workspace(t } func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_multi_workspace(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -785,6 +791,7 @@ func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_multi_workspace(t * } func Test_migrate_remote_backend_prefix_to_tfc_tags_strategy_single_workspace(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -909,6 +916,7 @@ func Test_migrate_remote_backend_prefix_to_tfc_tags_strategy_single_workspace(t } func Test_migrate_remote_backend_prefix_to_tfc_tags_strategy_multi_workspace(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() diff --git a/internal/cloud/e2e/migrate_state_single_to_tfc_test.go b/internal/cloud/e2e/migrate_state_single_to_tfc_test.go index e171f048a..becd882b9 100644 --- a/internal/cloud/e2e/migrate_state_single_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_single_to_tfc_test.go @@ -12,6 +12,7 @@ import ( ) func Test_migrate_single_to_tfc(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() diff --git a/internal/cloud/e2e/migrate_state_tfc_to_other_test.go b/internal/cloud/e2e/migrate_state_tfc_to_other_test.go index 3da776c3f..d99b5504e 100644 --- a/internal/cloud/e2e/migrate_state_tfc_to_other_test.go +++ b/internal/cloud/e2e/migrate_state_tfc_to_other_test.go @@ -10,6 +10,7 @@ import ( ) func Test_migrate_tfc_to_other(t *testing.T) { + skipIfMissingEnvVar(t) cases := map[string]struct { operations []operationSets }{ diff --git a/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go b/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go index 64204565d..9419745e0 100644 --- a/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go +++ b/internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go @@ -13,6 +13,7 @@ import ( ) func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -290,6 +291,7 @@ func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { } func Test_migrate_tfc_to_tfc_multiple_workspace(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() diff --git a/internal/cloud/e2e/run_variables_test.go b/internal/cloud/e2e/run_variables_test.go index b4d3569a2..07c6d869c 100644 --- a/internal/cloud/e2e/run_variables_test.go +++ b/internal/cloud/e2e/run_variables_test.go @@ -45,6 +45,7 @@ output "test_env" { } func Test_cloud_run_variables(t *testing.T) { + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) cases := testCases{