Backend State Migration from cloud to cloud.
* Add test for tfc to tfc mgiration * Fix old tests, and remove unused code.
This commit is contained in:
parent
93bfcff61a
commit
dfb4609be2
2
go.mod
2
go.mod
|
@ -41,7 +41,7 @@ require (
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/hashicorp/go-plugin v1.4.3
|
github.com/hashicorp/go-plugin v1.4.3
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.0
|
github.com/hashicorp/go-retryablehttp v0.7.0
|
||||||
github.com/hashicorp/go-tfe v0.19.1-0.20211012181137-3666eed9e8e9
|
github.com/hashicorp/go-tfe v0.19.1-0.20211015143223-e7e0a0182bbd
|
||||||
github.com/hashicorp/go-uuid v1.0.2
|
github.com/hashicorp/go-uuid v1.0.2
|
||||||
github.com/hashicorp/go-version v1.2.1
|
github.com/hashicorp/go-version v1.2.1
|
||||||
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
|
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -379,8 +379,8 @@ github.com/hashicorp/go-slug v0.7.0/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
|
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
github.com/hashicorp/go-tfe v0.19.1-0.20211012181137-3666eed9e8e9 h1:97QOrhJha4EmU+mUC1ubf15B40CjdcObULUop49+u8c=
|
github.com/hashicorp/go-tfe v0.19.1-0.20211015143223-e7e0a0182bbd h1:mn11v5DDNXkZq32QM8JSLNoUSbW2Ud4jMxm8IMpfS2w=
|
||||||
github.com/hashicorp/go-tfe v0.19.1-0.20211012181137-3666eed9e8e9/go.mod h1:gyXLXbpBVxA2F/6opah8XBsOkZJxHYQmghl0OWi8keI=
|
github.com/hashicorp/go-tfe v0.19.1-0.20211015143223-e7e0a0182bbd/go.mod h1:gyXLXbpBVxA2F/6opah8XBsOkZJxHYQmghl0OWi8keI=
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||||
|
|
|
@ -7,191 +7,175 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
expect "github.com/Netflix/go-expect"
|
||||||
tfe "github.com/hashicorp/go-tfe"
|
tfe "github.com/hashicorp/go-tfe"
|
||||||
"github.com/hashicorp/terraform/internal/e2e"
|
"github.com/hashicorp/terraform/internal/e2e"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_terraform_apply_autoApprove(t *testing.T) {
|
func Test_terraform_apply_autoApprove(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
cases := map[string]struct {
|
tfVersion := "1.1.0-tfc-integration"
|
||||||
setup func(t *testing.T) (map[string]string, func())
|
if !hasTerraformVersion(version) {
|
||||||
commands []tfCommand
|
t.Skip("Skipping test because TFC does not have current terraform version.")
|
||||||
validations func(t *testing.T, orgName, wsName string)
|
}
|
||||||
}{
|
|
||||||
"workspace manual apply, terraform apply without auto-approve": {
|
|
||||||
setup: func(t *testing.T) (map[string]string, func()) {
|
|
||||||
org, orgCleanup := createOrganization(t)
|
|
||||||
wOpts := tfe.WorkspaceCreateOptions{
|
|
||||||
Name: tfe.String(randomString(t)),
|
|
||||||
TerraformVersion: tfe.String(terraformVersion),
|
|
||||||
AutoApply: tfe.Bool(false),
|
|
||||||
}
|
|
||||||
workspace := createWorkspace(t, org.Name, wOpts)
|
|
||||||
cleanup := func() {
|
|
||||||
defer orgCleanup()
|
|
||||||
}
|
|
||||||
names := map[string]string{
|
|
||||||
"organization": org.Name,
|
|
||||||
"workspace": workspace.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
return names, cleanup
|
cases := map[string]struct {
|
||||||
},
|
operations []operationSets
|
||||||
commands: []tfCommand{
|
validations func(t *testing.T, orgName string)
|
||||||
|
}{
|
||||||
|
"workspace manual apply, terraform apply without auto-approve, expect prompt": {
|
||||||
|
operations: []operationSets{
|
||||||
{
|
{
|
||||||
command: []string{"init"},
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
expectedCmdOutput: "Terraform has been successfully initialized",
|
wsName := "app"
|
||||||
expectedErr: "",
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
},
|
Name: tfe.String(wsName),
|
||||||
{
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
command: []string{"apply"},
|
AutoApply: tfe.Bool(false),
|
||||||
expectedCmdOutput: "Do you want to perform these actions in workspace",
|
})
|
||||||
expectedErr: "Error asking approve",
|
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init"},
|
||||||
|
expectedCmdOutput: `Successfully configured the backend "cloud"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply"},
|
||||||
|
expectedCmdOutput: `Do you want to perform these actions in workspace "app"?`,
|
||||||
|
userInput: []string{"yes"},
|
||||||
|
postInputOutput: []string{`Apply complete!`},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
validations: func(t *testing.T, orgName, wsName string) {
|
validations: func(t *testing.T, orgName string) {
|
||||||
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, wsName, &tfe.WorkspaceReadOptions{Include: "current_run"})
|
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, "app", &tfe.WorkspaceReadOptions{Include: "current_run"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if workspace.CurrentRun == nil {
|
if workspace.CurrentRun == nil {
|
||||||
t.Fatal("Expected workspace to have run, but got nil")
|
t.Fatal("Expected workspace to have run, but got nil")
|
||||||
}
|
}
|
||||||
if workspace.CurrentRun.Status != tfe.RunPlanned {
|
if workspace.CurrentRun.Status != tfe.RunApplied {
|
||||||
t.Fatalf("Expected run status to be `planned`, but is %s", workspace.CurrentRun.Status)
|
t.Fatalf("Expected run status to be `applied`, but is %s", workspace.CurrentRun.Status)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"workspace auto apply, terraform apply without auto-approve": {
|
"workspace auto apply, terraform apply without auto-approve, expect prompt": {
|
||||||
setup: func(t *testing.T) (map[string]string, func()) {
|
operations: []operationSets{
|
||||||
org, orgCleanup := createOrganization(t)
|
|
||||||
wOpts := tfe.WorkspaceCreateOptions{
|
|
||||||
Name: tfe.String(randomString(t)),
|
|
||||||
TerraformVersion: tfe.String(terraformVersion),
|
|
||||||
AutoApply: tfe.Bool(true),
|
|
||||||
}
|
|
||||||
workspace := createWorkspace(t, org.Name, wOpts)
|
|
||||||
cleanup := func() {
|
|
||||||
defer orgCleanup()
|
|
||||||
}
|
|
||||||
names := map[string]string{
|
|
||||||
"organization": org.Name,
|
|
||||||
"workspace": workspace.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
return names, cleanup
|
|
||||||
},
|
|
||||||
commands: []tfCommand{
|
|
||||||
{
|
{
|
||||||
command: []string{"init"},
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
expectedCmdOutput: "Terraform has been successfully initialized",
|
wsName := "app"
|
||||||
expectedErr: "",
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
},
|
Name: tfe.String(wsName),
|
||||||
{
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
command: []string{"apply"},
|
AutoApply: tfe.Bool(true),
|
||||||
expectedCmdOutput: "Do you want to perform these actions in workspace",
|
})
|
||||||
expectedErr: "Error asking approve",
|
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init"},
|
||||||
|
expectedCmdOutput: `Successfully configured the backend "cloud"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply"},
|
||||||
|
expectedCmdOutput: `Do you want to perform these actions in workspace "app"?`,
|
||||||
|
userInput: []string{"yes"},
|
||||||
|
postInputOutput: []string{`Apply complete!`},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
validations: func(t *testing.T, orgName, wsName string) {
|
validations: func(t *testing.T, orgName string) {
|
||||||
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, wsName, &tfe.WorkspaceReadOptions{Include: "current_run"})
|
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, "app", &tfe.WorkspaceReadOptions{Include: "current_run"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if workspace.CurrentRun == nil {
|
if workspace.CurrentRun == nil {
|
||||||
t.Fatalf("Expected workspace to have run, but got nil")
|
t.Fatal("Expected workspace to have run, but got nil")
|
||||||
}
|
|
||||||
if workspace.CurrentRun.Status != tfe.RunPlanned {
|
|
||||||
t.Fatalf("Expected run status to be `planned`, but is %s", workspace.CurrentRun.Status)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"workspace manual apply, terraform apply auto-approve": {
|
|
||||||
setup: func(t *testing.T) (map[string]string, func()) {
|
|
||||||
org, orgCleanup := createOrganization(t)
|
|
||||||
wOpts := tfe.WorkspaceCreateOptions{
|
|
||||||
Name: tfe.String(randomString(t)),
|
|
||||||
TerraformVersion: tfe.String(terraformVersion),
|
|
||||||
AutoApply: tfe.Bool(false),
|
|
||||||
}
|
|
||||||
workspace := createWorkspace(t, org.Name, wOpts)
|
|
||||||
cleanup := func() {
|
|
||||||
defer orgCleanup()
|
|
||||||
}
|
|
||||||
names := map[string]string{
|
|
||||||
"organization": org.Name,
|
|
||||||
"workspace": workspace.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
return names, cleanup
|
|
||||||
},
|
|
||||||
commands: []tfCommand{
|
|
||||||
{
|
|
||||||
command: []string{"init"},
|
|
||||||
expectedCmdOutput: "Terraform has been successfully initialized",
|
|
||||||
expectedErr: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: []string{"apply", "-auto-approve"},
|
|
||||||
expectedCmdOutput: "Apply complete! Resources: 1 added, 0 changed, 0 destroyed.",
|
|
||||||
expectedErr: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
validations: func(t *testing.T, orgName, wsName string) {
|
|
||||||
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, wsName, &tfe.WorkspaceReadOptions{Include: "current_run"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if workspace.CurrentRun == nil {
|
|
||||||
t.Fatalf("Expected workspace to have run, but got nil")
|
|
||||||
}
|
}
|
||||||
if workspace.CurrentRun.Status != tfe.RunApplied {
|
if workspace.CurrentRun.Status != tfe.RunApplied {
|
||||||
t.Fatalf("Expected run status to be `applied`, but is %s", workspace.CurrentRun.Status)
|
t.Fatalf("Expected run status to be `applied`, but is %s", workspace.CurrentRun.Status)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"workspace auto apply, terraform apply auto-approve": {
|
"workspace manual apply, terraform apply with auto-approve, no prompt": {
|
||||||
setup: func(t *testing.T) (map[string]string, func()) {
|
operations: []operationSets{
|
||||||
org, orgCleanup := createOrganization(t)
|
|
||||||
|
|
||||||
wOpts := tfe.WorkspaceCreateOptions{
|
|
||||||
Name: tfe.String(randomString(t)),
|
|
||||||
TerraformVersion: tfe.String(terraformVersion),
|
|
||||||
AutoApply: tfe.Bool(true),
|
|
||||||
}
|
|
||||||
workspace := createWorkspace(t, org.Name, wOpts)
|
|
||||||
cleanup := func() {
|
|
||||||
defer orgCleanup()
|
|
||||||
}
|
|
||||||
names := map[string]string{
|
|
||||||
"organization": org.Name,
|
|
||||||
"workspace": workspace.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
return names, cleanup
|
|
||||||
},
|
|
||||||
commands: []tfCommand{
|
|
||||||
{
|
{
|
||||||
command: []string{"init"},
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
expectedCmdOutput: "Terraform has been successfully initialized",
|
wsName := "app"
|
||||||
expectedErr: "",
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
},
|
Name: tfe.String(wsName),
|
||||||
{
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
command: []string{"apply", "-auto-approve"},
|
AutoApply: tfe.Bool(false),
|
||||||
expectedCmdOutput: "Apply complete! Resources: 1 added, 0 changed, 0 destroyed.",
|
})
|
||||||
expectedErr: "",
|
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init"},
|
||||||
|
expectedCmdOutput: `Successfully configured the backend "cloud"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply", "-auto-approve"},
|
||||||
|
expectedCmdOutput: `Apply complete!`,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
validations: func(t *testing.T, orgName, wsName string) {
|
validations: func(t *testing.T, orgName string) {
|
||||||
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, wsName, &tfe.WorkspaceReadOptions{Include: "current_run"})
|
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, "app", &tfe.WorkspaceReadOptions{Include: "current_run"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if workspace.CurrentRun == nil {
|
if workspace.CurrentRun == nil {
|
||||||
t.Fatalf("Expected workspace to have run, but got nil")
|
t.Fatal("Expected workspace to have run, but got nil")
|
||||||
|
}
|
||||||
|
if workspace.CurrentRun.Status != tfe.RunApplied {
|
||||||
|
t.Fatalf("Expected run status to be `applied`, but is %s", workspace.CurrentRun.Status)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"workspace auto apply, terraform apply with auto-approve, no prompt": {
|
||||||
|
operations: []operationSets{
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
wsName := "app"
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String(wsName),
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
AutoApply: tfe.Bool(true),
|
||||||
|
})
|
||||||
|
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init"},
|
||||||
|
expectedCmdOutput: `Successfully configured the backend "cloud"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply", "-auto-approve"},
|
||||||
|
expectedCmdOutput: `Apply complete!`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validations: func(t *testing.T, orgName string) {
|
||||||
|
workspace, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, "app", &tfe.WorkspaceReadOptions{Include: "current_run"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if workspace.CurrentRun == nil {
|
||||||
|
t.Fatal("Expected workspace to have run, but got nil")
|
||||||
}
|
}
|
||||||
if workspace.CurrentRun.Status != tfe.RunApplied {
|
if workspace.CurrentRun.Status != tfe.RunApplied {
|
||||||
t.Fatalf("Expected run status to be `applied`, but is %s", workspace.CurrentRun.Status)
|
t.Fatalf("Expected run status to be `applied`, but is %s", workspace.CurrentRun.Status)
|
||||||
|
@ -201,38 +185,73 @@ func Test_terraform_apply_autoApprove(t *testing.T) {
|
||||||
}
|
}
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
log.Println("Test: ", name)
|
log.Println("Test: ", name)
|
||||||
resourceData, cleanup := tc.setup(t)
|
|
||||||
|
organization, cleanup := createOrganization(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer exp.Close()
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", "terraform-test")
|
tmpDir, err := ioutil.TempDir("", "terraform-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
orgName := resourceData["organization"]
|
defer os.RemoveAll(tmpDir)
|
||||||
wsName := resourceData["workspace"]
|
|
||||||
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
|
|
||||||
writeMainTF(t, tfBlock, tmpDir)
|
|
||||||
tf := e2e.NewBinary(terraformBin, tmpDir)
|
tf := e2e.NewBinary(terraformBin, tmpDir)
|
||||||
defer tf.Close()
|
tf.AddEnv("TF_LOG=info")
|
||||||
tf.AddEnv("TF_LOG=debug")
|
|
||||||
tf.AddEnv(cliConfigFileEnv)
|
tf.AddEnv(cliConfigFileEnv)
|
||||||
|
defer tf.Close()
|
||||||
|
|
||||||
for _, cmd := range tc.commands {
|
for _, op := range tc.operations {
|
||||||
stdout, stderr, err := tf.Run(cmd.command...)
|
op.prep(t, organization.Name, tf.WorkDir())
|
||||||
if cmd.expectedErr == "" && err != nil {
|
for _, tfCmd := range op.commands {
|
||||||
t.Fatalf("Expected no error, but got %v. stderr\n: %s", err, stderr)
|
cmd := tf.Cmd(tfCmd.command...)
|
||||||
}
|
cmd.Stdin = exp.Tty()
|
||||||
if cmd.expectedErr != "" {
|
cmd.Stdout = exp.Tty()
|
||||||
if !strings.Contains(stderr, cmd.expectedErr) {
|
cmd.Stderr = exp.Tty()
|
||||||
t.Fatalf("Expected to find error %s, but got %s", cmd.expectedErr, stderr)
|
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if cmd.expectedCmdOutput != "" && !strings.Contains(stdout, cmd.expectedCmdOutput) {
|
if tfCmd.expectedCmdOutput != "" {
|
||||||
t.Fatalf("Expected to find output %s, but did not find in\n%s", cmd.expectedCmdOutput, stdout)
|
_, 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tc.validations(t, orgName, wsName)
|
if tc.validations != nil {
|
||||||
|
tc.validations(t, organization.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,18 @@ func createOrganization(t *testing.T) (*tfe.Organization, func()) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove this when we are ready to release. This should not need beta
|
||||||
|
// or internal access as the release will be. Also, we won't be able to access
|
||||||
|
// admin in production.
|
||||||
|
opts := tfe.AdminOrganizationUpdateOptions{
|
||||||
|
AccessBetaTools: tfe.Bool(true),
|
||||||
|
AccessInternalTools: tfe.Bool(true),
|
||||||
|
}
|
||||||
|
_, err = tfeClient.Admin.Organizations.Update(ctx, org.Name, opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
return org, func() {
|
return org, func() {
|
||||||
if err := tfeClient.Organizations.Delete(ctx, org.Name); err != nil {
|
if err := tfeClient.Organizations.Delete(ctx, org.Name); err != nil {
|
||||||
t.Errorf("Error destroying organization! WARNING: Dangling resources\n"+
|
t.Errorf("Error destroying organization! WARNING: Dangling resources\n"+
|
||||||
|
@ -147,14 +159,10 @@ terraform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "random_pet" "server" {
|
output "tag_val" {
|
||||||
keepers = {
|
value = "%s"
|
||||||
uuid = uuid()
|
|
||||||
}
|
|
||||||
|
|
||||||
length = 3
|
|
||||||
}
|
}
|
||||||
`, tfeHostname, org, tag)
|
`, tfeHostname, org, tag, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func terraformConfigCloudBackendName(org, name string) string {
|
func terraformConfigCloudBackendName(org, name string) string {
|
||||||
|
@ -170,12 +178,8 @@ terraform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "random_pet" "server" {
|
output "val" {
|
||||||
keepers = {
|
value = "${terraform.workspace}"
|
||||||
uuid = uuid()
|
|
||||||
}
|
|
||||||
|
|
||||||
length = 3
|
|
||||||
}
|
}
|
||||||
`, tfeHostname, org, name)
|
`, tfeHostname, org, name)
|
||||||
}
|
}
|
||||||
|
@ -191,3 +195,40 @@ func writeMainTF(t *testing.T, block string, dir string) {
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that TFC/E has a particular terraform version.
|
||||||
|
func hasTerraformVersion(version string) bool {
|
||||||
|
opts := tfe.AdminTerraformVersionsListOptions{
|
||||||
|
ListOptions: tfe.ListOptions{
|
||||||
|
PageNumber: 1,
|
||||||
|
PageSize: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hasVersion := false
|
||||||
|
|
||||||
|
findTfVersion:
|
||||||
|
for {
|
||||||
|
// TODO: update go-tfe Read() to retrieve a terraform version by name.
|
||||||
|
// Currently you can only retrieve by ID.
|
||||||
|
tfVersionList, err := tfeClient.Admin.TerraformVersions.List(context.Background(), opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not retrieve list of terraform versions: %v", err)
|
||||||
|
}
|
||||||
|
for _, item := range tfVersionList.Items {
|
||||||
|
if item.Version == version {
|
||||||
|
hasVersion = true
|
||||||
|
break findTfVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit the loop when we've seen all pages.
|
||||||
|
if tfVersionList.CurrentPage >= tfVersionList.TotalPages {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the page number to get the next page.
|
||||||
|
opts.PageNumber = tfVersionList.NextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasVersion
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -16,7 +15,6 @@ import (
|
||||||
tfe "github.com/hashicorp/go-tfe"
|
tfe "github.com/hashicorp/go-tfe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var terraformVersion string
|
|
||||||
var terraformBin string
|
var terraformBin string
|
||||||
var cliConfigFileEnv string
|
var cliConfigFileEnv string
|
||||||
|
|
||||||
|
@ -46,7 +44,6 @@ func accTest() bool {
|
||||||
func setup() func() {
|
func setup() func() {
|
||||||
setTfeClient()
|
setTfeClient()
|
||||||
teardown := setupBinary()
|
teardown := setupBinary()
|
||||||
setVersion()
|
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
teardown()
|
teardown()
|
||||||
|
@ -117,30 +114,6 @@ func setupBinary() func() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setVersion() {
|
|
||||||
log.Println("Retrieving version")
|
|
||||||
cmd := exec.Command(terraformBin, "version", "-json")
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Sprintf("Could not output terraform version: %v", err))
|
|
||||||
}
|
|
||||||
var data map[string]interface{}
|
|
||||||
if err := json.Unmarshal(out, &data); err != nil {
|
|
||||||
log.Fatal(fmt.Sprintf("Could not unmarshal version output: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err = exec.Command("git", "rev-parse", "HEAD").Output()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Sprintf("Could not execute go build command: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := string(out)[0:8]
|
|
||||||
|
|
||||||
fullVersion := data["terraform_version"].(string)
|
|
||||||
version := strings.Split(fullVersion, "-")[0]
|
|
||||||
terraformVersion = fmt.Sprintf("%s-%s", version, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeCredRC(file string) {
|
func writeCredRC(file string) {
|
||||||
creds := credentialBlock()
|
creds := credentialBlock()
|
||||||
f, err := os.Create(file)
|
f, err := os.Create(file)
|
||||||
|
|
|
@ -67,7 +67,7 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
|
||||||
expectedCmdOutput: `Do you want to copy only your current workspace?`,
|
expectedCmdOutput: `Do you want to copy only your current workspace?`,
|
||||||
userInput: []string{"yes", "yes"},
|
userInput: []string{"yes", "yes"},
|
||||||
postInputOutput: []string{
|
postInputOutput: []string{
|
||||||
`Do you want to copy existing state to the new backend?`,
|
`Do you want to copy existing state to Terraform Cloud?`,
|
||||||
`Successfully configured Terraform Cloud!`},
|
`Successfully configured Terraform Cloud!`},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -138,8 +138,8 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
|
||||||
expectedCmdOutput: `Do you want to copy only your current workspace?`,
|
expectedCmdOutput: `Do you want to copy only your current workspace?`,
|
||||||
userInput: []string{"yes", "yes"},
|
userInput: []string{"yes", "yes"},
|
||||||
postInputOutput: []string{
|
postInputOutput: []string{
|
||||||
`Do you want to copy existing state to the new backend?`,
|
`Do you want to copy existing state to Terraform Cloud?`,
|
||||||
`Successfully configured Terraform Cloud!`},
|
`Terraform Cloud has been successfully initialized!`},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: []string{"workspace", "list"},
|
command: []string{"workspace", "list"},
|
||||||
|
|
|
@ -5,7 +5,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -104,7 +103,9 @@ func Test_migrate_single_to_tfc(t *testing.T) {
|
||||||
command: []string{"init", "-migrate-state"},
|
command: []string{"init", "-migrate-state"},
|
||||||
expectedCmdOutput: `Terraform Cloud configuration only allows named workspaces!`,
|
expectedCmdOutput: `Terraform Cloud configuration only allows named workspaces!`,
|
||||||
userInput: []string{"new-workspace", "yes"},
|
userInput: []string{"new-workspace", "yes"},
|
||||||
postInputOutput: []string{`Successfully configured Terraform Cloud!`},
|
postInputOutput: []string{
|
||||||
|
`Do you want to copy existing state to the new backend?`,
|
||||||
|
`Successfully configured Terraform Cloud!`},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: []string{"workspace", "list"},
|
command: []string{"workspace", "list"},
|
||||||
|
@ -129,7 +130,7 @@ func Test_migrate_single_to_tfc(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
fmt.Println("Test: ", name)
|
t.Log("Test: ", name)
|
||||||
organization, cleanup := createOrganization(t)
|
organization, cleanup := createOrganization(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout))
|
exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout))
|
||||||
|
|
|
@ -1,21 +1,503 @@
|
||||||
|
//go:build e2e
|
||||||
|
// +build e2e
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
expect "github.com/Netflix/go-expect"
|
||||||
|
tfe "github.com/hashicorp/go-tfe"
|
||||||
|
"github.com/hashicorp/terraform/internal/e2e"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
tfVersion := "1.1.0-tfc-integration"
|
||||||
|
if !hasTerraformVersion(version) {
|
||||||
|
t.Skip("Skipping test because TFC does not have current terraform version.")
|
||||||
|
}
|
||||||
|
|
||||||
If org to org, treat it like a new backend. Then go through the multi/single logic
|
cases := map[string]struct {
|
||||||
|
setup func(t *testing.T) (string, func())
|
||||||
|
operations []operationSets
|
||||||
|
validations func(t *testing.T, orgName string)
|
||||||
|
}{
|
||||||
|
"migrating from name to name": {
|
||||||
|
setup: func(t *testing.T) (string, func()) {
|
||||||
|
organization, cleanup := createOrganization(t)
|
||||||
|
return organization.Name, cleanup
|
||||||
|
},
|
||||||
|
operations: []operationSets{
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
wsName := "prod"
|
||||||
|
// Creating the workspace here instead of it being created
|
||||||
|
// dynamically in the Cloud StateMgr because we want to ensure that
|
||||||
|
// the terraform version selected for the workspace matches the
|
||||||
|
// terraform version of this current branch.
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("prod"),
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init"},
|
||||||
|
expectedCmdOutput: `Successfully configured the backend "cloud"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "show"},
|
||||||
|
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!`},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
wsName := "dev"
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String(wsName),
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
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 the new backend?`,
|
||||||
|
userInput: []string{"yes"},
|
||||||
|
postInputOutput: []string{`Successfully configured the backend "cloud"!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "show"},
|
||||||
|
expectedCmdOutput: `dev`, // this comes from the `prep` function
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validations: func(t *testing.T, orgName string) {
|
||||||
|
wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// this workspace name is what exists in the cloud backend configuration block
|
||||||
|
if len(wsList.Items) != 2 {
|
||||||
|
t.Fatal("Expected number of workspaces to be 2")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"migrating from name to tags": {
|
||||||
|
setup: func(t *testing.T) (string, func()) {
|
||||||
|
organization, cleanup := createOrganization(t)
|
||||||
|
return organization.Name, cleanup
|
||||||
|
},
|
||||||
|
operations: []operationSets{
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
wsName := "prod"
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("prod"),
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init"},
|
||||||
|
expectedCmdOutput: `Successfully configured the backend "cloud"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply"},
|
||||||
|
expectedCmdOutput: `Do you want to perform these actions in workspace "prod"?`,
|
||||||
|
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: `The "cloud" backend configuration only allows named workspaces!`,
|
||||||
|
userInput: []string{"new-workspace", "yes"},
|
||||||
|
postInputOutput: []string{
|
||||||
|
`Do you want to copy existing state to the new backend?`,
|
||||||
|
`Successfully configured the backend "cloud"!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "show"},
|
||||||
|
expectedCmdOutput: `new-workspace`, // this comes from the `prep` function
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// this workspace name is what exists in the cloud backend configuration block
|
||||||
|
if len(wsList.Items) != 1 {
|
||||||
|
t.Fatal("Expected number of workspaces to be 1")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
If same org, but name/tag changes
|
for name, tc := range cases {
|
||||||
config name -> config name
|
t.Log("Test: ", name)
|
||||||
-- straight copy
|
exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout))
|
||||||
config name -> config tags
|
if err != nil {
|
||||||
-- jsut add tag to workspace.
|
t.Fatal(err)
|
||||||
config tags -> config name
|
}
|
||||||
-- straight copy
|
defer exp.Close()
|
||||||
*/
|
|
||||||
func Test_migrate_tfc_to_tfc(t *testing.T) {
|
tmpDir, err := ioutil.TempDir("", "terraform-test")
|
||||||
t.Skip("todo: see comments")
|
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)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.validations != nil {
|
||||||
|
tc.validations(t, orgName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_migrate_tfc_to_tfc_multiple_workspace(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
tfVersion := "1.1.0-tfc-integration"
|
||||||
|
if !hasTerraformVersion(version) {
|
||||||
|
t.Skip("Skipping test because TFC does not have current terraform version.")
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]struct {
|
||||||
|
setup func(t *testing.T) (string, func())
|
||||||
|
operations []operationSets
|
||||||
|
validations func(t *testing.T, orgName string)
|
||||||
|
}{
|
||||||
|
"migrating from multiple workspaces via tags to name": {
|
||||||
|
setup: func(t *testing.T) (string, func()) {
|
||||||
|
organization, cleanup := createOrganization(t)
|
||||||
|
return organization.Name, cleanup
|
||||||
|
},
|
||||||
|
operations: []operationSets{
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
tag := "app"
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("app-prod"),
|
||||||
|
Tags: []*tfe.Tag{{Name: tag}},
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("app-staging"),
|
||||||
|
Tags: []*tfe.Tag{{Name: tag}},
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
tfBlock := terraformConfigCloudBackendTags(orgName, tag)
|
||||||
|
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-prod"?`,
|
||||||
|
userInput: []string{"yes"},
|
||||||
|
postInputOutput: []string{`Apply complete!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "select", "app-staging"},
|
||||||
|
expectedCmdOutput: `Switched to workspace "app-staging".`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply"},
|
||||||
|
expectedCmdOutput: `Do you want to perform these actions in workspace "app-staging"?`,
|
||||||
|
userInput: []string{"yes"},
|
||||||
|
postInputOutput: []string{`Apply complete!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"output"},
|
||||||
|
expectedCmdOutput: `tag_val = "app"`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
name := "service"
|
||||||
|
// Doing this here instead of relying on dynamic workspace creation
|
||||||
|
// because we want to set the terraform version here so that it is
|
||||||
|
// using the right version for post init operations.
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String(name),
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
tfBlock := terraformConfigCloudBackendName(orgName, name)
|
||||||
|
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", "yes"},
|
||||||
|
postInputOutput: []string{
|
||||||
|
`Do you want to copy existing state to the new backend?`,
|
||||||
|
`Terraform has been successfully initialized!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "show"},
|
||||||
|
expectedCmdOutput: `service`, // this comes from the `prep` function
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"output"},
|
||||||
|
expectedCmdOutput: `tag_val = "app"`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validations: func(t *testing.T, orgName string) {
|
||||||
|
ws, err := tfeClient.Workspaces.Read(ctx, orgName, "service")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if ws == nil {
|
||||||
|
t.Fatal("Expected to workspace not be empty")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"migrating from multiple workspaces via tags to other tags": {
|
||||||
|
setup: func(t *testing.T) (string, func()) {
|
||||||
|
organization, cleanup := createOrganization(t)
|
||||||
|
return organization.Name, cleanup
|
||||||
|
},
|
||||||
|
operations: []operationSets{
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
tag := "app"
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("app-prod"),
|
||||||
|
Tags: []*tfe.Tag{{Name: tag}},
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("app-staging"),
|
||||||
|
Tags: []*tfe.Tag{{Name: tag}},
|
||||||
|
TerraformVersion: tfe.String(tfVersion),
|
||||||
|
})
|
||||||
|
tfBlock := terraformConfigCloudBackendTags(orgName, tag)
|
||||||
|
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"},
|
||||||
|
expectedCmdOutput: `Apply complete!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "select", "app-staging"},
|
||||||
|
expectedCmdOutput: `Switched to workspace "app-staging".`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply", "-auto-approve"},
|
||||||
|
expectedCmdOutput: `Apply complete!`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
tag := "billing"
|
||||||
|
tfBlock := terraformConfigCloudBackendTags(orgName, tag)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
t.Log(orgName)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init", "-migrate-state", "-ignore-remote-version"},
|
||||||
|
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.`,
|
||||||
|
`Successfully configured the backend "cloud"!`},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validations: func(t *testing.T, orgName string) {
|
||||||
|
wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{
|
||||||
|
Tags: tfe.String("billing"),
|
||||||
|
})
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
_, empty := getWorkspace(wsList.Items, "new-app-prod")
|
||||||
|
if empty {
|
||||||
|
t.Fatalf("expected workspaces to include 'new-app-prod' but didn't.")
|
||||||
|
}
|
||||||
|
_, empty = getWorkspace(wsList.Items, "new-app-staging")
|
||||||
|
if empty {
|
||||||
|
t.Fatalf("expected workspaces to include 'new-app-staging' 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)
|
||||||
|
defer tf.Close()
|
||||||
|
tf.AddEnv("TF_LOG=INFO")
|
||||||
|
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()
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(cmd.Stderr)
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.validations != nil {
|
||||||
|
tc.validations(t, orgName)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -545,12 +545,6 @@ func (m *Meta) backendMigrateTFC(opts *backendMigrateOpts) error {
|
||||||
return fmt.Errorf(strings.TrimSpace(errTFCMigrateNotYetImplemented))
|
return fmt.Errorf(strings.TrimSpace(errTFCMigrateNotYetImplemented))
|
||||||
}
|
}
|
||||||
|
|
||||||
// from TFC to TFC
|
|
||||||
if sourceTFC && destinationTFC {
|
|
||||||
// TODO: see internal/cloud/e2e/migrate_state_tfc_to_tfc_test.go for notes
|
|
||||||
panic("not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything below, by the above two conditionals, now assumes that the
|
// Everything below, by the above two conditionals, now assumes that the
|
||||||
// destination is always Terraform Cloud (TFC).
|
// destination is always Terraform Cloud (TFC).
|
||||||
|
|
||||||
|
@ -659,6 +653,13 @@ func (m *Meta) backendMigrateState_S_TFC(opts *backendMigrateOpts, sourceWorkspa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After migrating multiple workspaces, we want to ensure that a workspace is
|
||||||
|
// set or we prompt the user to set a workspace.
|
||||||
|
err = m.selectWorkspace(opts.Destination)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue