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..7928fc3f7 --- /dev/null +++ b/internal/cloud/e2e/README.md @@ -0,0 +1,24 @@ +# How to run tests + +To run them, use: +``` +TFE_TOKEN= TFE_HOSTNAME= TF_ACC=1 go test ./internal/cloud/e2e/... -ldflags "-X \"github.com/hashicorp/terraform/version.Prerelease=\"" +``` + +Required flags +* `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..9f086c88e 100644 --- a/internal/cloud/e2e/apply_auto_approve_test.go +++ b/internal/cloud/e2e/apply_auto_approve_test.go @@ -1,12 +1,8 @@ -//go:build e2e -// +build e2e - package main import ( "context" "io/ioutil" - "log" "os" "testing" @@ -17,7 +13,7 @@ import ( ) func Test_terraform_apply_autoApprove(t *testing.T) { - t.Parallel() + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -183,12 +179,10 @@ func Test_terraform_apply_autoApprove(t *testing.T) { }, }, } - for name, tc := range cases { - log.Println("Test: ", name) - + for _, tc := range cases { organization, cleanup := createOrganization(t) defer cleanup() - exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout)) + exp, err := expect.NewConsole(defaultOpts()...) if err != nil { t.Fatal(err) } @@ -201,7 +195,6 @@ func Test_terraform_apply_autoApprove(t *testing.T) { defer os.RemoveAll(tmpDir) tf := e2e.NewBinary(terraformBin, tmpDir) - tf.AddEnv("TF_LOG=info") tf.AddEnv(cliConfigFileEnv) defer tf.Close() diff --git a/internal/cloud/e2e/backend_apply_before_init_test.go b/internal/cloud/e2e/backend_apply_before_init_test.go index c3b0becb7..a5c9fee5a 100644 --- a/internal/cloud/e2e/backend_apply_before_init_test.go +++ b/internal/cloud/e2e/backend_apply_before_init_test.go @@ -1,10 +1,6 @@ -//go:build e2e -// +build e2e - package main import ( - "fmt" "io/ioutil" "os" "testing" @@ -14,6 +10,7 @@ import ( ) func Test_backend_apply_before_init(t *testing.T) { + skipIfMissingEnvVar(t) t.Parallel() skipWithoutRemoteTerraformVersion(t) @@ -51,10 +48,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 +72,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..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 ( @@ -10,8 +7,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" ) @@ -22,7 +21,6 @@ const ( type tfCommand struct { command []string expectedCmdOutput string - expectedErr string expectError bool userInput []string postInputOutput []string @@ -38,6 +36,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{ @@ -93,7 +101,7 @@ func randomString(t *testing.T) string { } func terraformConfigLocalBackend() string { - return fmt.Sprintf(` + return ` terraform { backend "local" { } @@ -102,7 +110,7 @@ terraform { output "val" { value = "${terraform.workspace}" } -`) +` } func terraformConfigRemoteBackendName(org, name string) string { @@ -193,9 +201,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 +228,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..ca9743d5e 100644 --- a/internal/cloud/e2e/init_with_empty_tags_test.go +++ b/internal/cloud/e2e/init_with_empty_tags_test.go @@ -1,10 +1,6 @@ -//go:build e2e -// +build e2e - package main import ( - "fmt" "io/ioutil" "os" "testing" @@ -14,6 +10,7 @@ import ( ) func Test_init_with_empty_tags(t *testing.T) { + skipIfMissingEnvVar(t) t.Parallel() skipWithoutRemoteTerraformVersion(t) @@ -42,68 +39,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..d758eb65d 100644 --- a/internal/cloud/e2e/main_test.go +++ b/internal/cloud/e2e/main_test.go @@ -1,9 +1,7 @@ -//go:build e2e -// +build e2e - package main import ( + "flag" "fmt" "io/ioutil" "log" @@ -22,13 +20,9 @@ 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) - if !accTest() { - // if TF_ACC is not set, we want to skip all these tests. - return - } teardown := setup() code := m.Run() teardown() @@ -42,7 +36,29 @@ 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 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() + verboseMode = *tfOutput + setTfeClient() teardown := setupBinary() @@ -52,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, "/") @@ -95,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( @@ -106,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) @@ -124,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 6e1bd40d9..950f3859e 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 ( @@ -16,7 +13,7 @@ import ( ) func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) { - t.Parallel() + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -38,20 +35,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 +106,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 +160,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 { + 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() + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -268,20 +256,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 +295,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 +309,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 +400,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 +414,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 +448,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..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 @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package main import ( @@ -15,916 +12,1061 @@ import ( ) func Test_migrate_remote_backend_name_to_tfc_name(t *testing.T) { - t.Parallel() + skipIfMissingEnvVar(t) 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) { + skipIfMissingEnvVar(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() + skipIfMissingEnvVar(t) 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() + skipIfMissingEnvVar(t) 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) { + skipIfMissingEnvVar(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) { + skipIfMissingEnvVar(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)) + } + _, empty := getWorkspace(wsList.Items, "cloud-workspace") + if empty { + t.Fatalf("expected workspaces to include 'cloud-workspace' but didn't.") + } + _, empty = getWorkspace(wsList.Items, "app-one") + if empty { + t.Fatalf("expected workspaces to include 'app-one' but didn't.") + } + _, 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) { + skipIfMissingEnvVar(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) { + skipIfMissingEnvVar(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..becd882b9 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 ( @@ -15,7 +12,7 @@ import ( ) func Test_migrate_single_to_tfc(t *testing.T) { - t.Parallel() + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -37,10 +34,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 +83,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 +126,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..d99b5504e 100644 --- a/internal/cloud/e2e/migrate_state_tfc_to_other_test.go +++ b/internal/cloud/e2e/migrate_state_tfc_to_other_test.go @@ -1,10 +1,6 @@ -//go:build e2e -// +build e2e - package main import ( - "fmt" "io/ioutil" "os" "testing" @@ -14,7 +10,7 @@ import ( ) func Test_migrate_tfc_to_other(t *testing.T) { - t.Parallel() + skipIfMissingEnvVar(t) cases := map[string]struct { operations []operationSets }{ @@ -51,68 +47,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..9419745e0 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 ( @@ -16,7 +13,7 @@ import ( ) func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) { - t.Parallel() + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -55,10 +52,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 +114,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 +176,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 +205,93 @@ 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() + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) ctx := context.Background() @@ -454,7 +443,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 +450,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 +479,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..07c6d869c 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 { @@ -46,7 +45,7 @@ output "test_env" { } func Test_cloud_run_variables(t *testing.T) { - t.Parallel() + skipIfMissingEnvVar(t) skipWithoutRemoteTerraformVersion(t) cases := testCases{ @@ -55,6 +54,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) }, @@ -78,75 +81,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) - } - } + }) } }