Backend State Migration: Add remote backend test
This commit is contained in:
parent
3fedd6898c
commit
dc76bbee73
|
@ -29,7 +29,7 @@ func Test_terraform_apply_autoApprove(t *testing.T) {
|
|||
TerraformVersion: tfe.String(terraformVersion),
|
||||
AutoApply: tfe.Bool(false),
|
||||
}
|
||||
workspace := createWorkspace(t, org, wOpts)
|
||||
workspace := createWorkspace(t, org.Name, wOpts)
|
||||
cleanup := func() {
|
||||
defer orgCleanup()
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ func Test_terraform_apply_autoApprove(t *testing.T) {
|
|||
TerraformVersion: tfe.String(terraformVersion),
|
||||
AutoApply: tfe.Bool(true),
|
||||
}
|
||||
workspace := createWorkspace(t, org, wOpts)
|
||||
workspace := createWorkspace(t, org.Name, wOpts)
|
||||
cleanup := func() {
|
||||
defer orgCleanup()
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func Test_terraform_apply_autoApprove(t *testing.T) {
|
|||
TerraformVersion: tfe.String(terraformVersion),
|
||||
AutoApply: tfe.Bool(false),
|
||||
}
|
||||
workspace := createWorkspace(t, org, wOpts)
|
||||
workspace := createWorkspace(t, org.Name, wOpts)
|
||||
cleanup := func() {
|
||||
defer orgCleanup()
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ func Test_terraform_apply_autoApprove(t *testing.T) {
|
|||
TerraformVersion: tfe.String(terraformVersion),
|
||||
AutoApply: tfe.Bool(true),
|
||||
}
|
||||
workspace := createWorkspace(t, org, wOpts)
|
||||
workspace := createWorkspace(t, org.Name, wOpts)
|
||||
cleanup := func() {
|
||||
defer orgCleanup()
|
||||
}
|
||||
|
|
|
@ -56,9 +56,9 @@ func createOrganization(t *testing.T) (*tfe.Organization, func()) {
|
|||
}
|
||||
}
|
||||
|
||||
func createWorkspace(t *testing.T, org *tfe.Organization, wOpts tfe.WorkspaceCreateOptions) *tfe.Workspace {
|
||||
func createWorkspace(t *testing.T, orgName string, wOpts tfe.WorkspaceCreateOptions) *tfe.Workspace {
|
||||
ctx := context.Background()
|
||||
w, err := tfeClient.Workspaces.Create(ctx, org.Name, wOpts)
|
||||
w, err := tfeClient.Workspaces.Create(ctx, orgName, wOpts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -66,6 +66,15 @@ func createWorkspace(t *testing.T, org *tfe.Organization, wOpts tfe.WorkspaceCre
|
|||
return w
|
||||
}
|
||||
|
||||
func getWorkspace(workspaces []*tfe.Workspace, workspace string) (*tfe.Workspace, bool) {
|
||||
for _, ws := range workspaces {
|
||||
if ws.Name == workspace {
|
||||
return ws, false
|
||||
}
|
||||
}
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func randomString(t *testing.T) string {
|
||||
v, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
|
@ -87,6 +96,44 @@ output "val" {
|
|||
`)
|
||||
}
|
||||
|
||||
func terraformConfigRemoteBackendName(org, name string) string {
|
||||
return fmt.Sprintf(`
|
||||
terraform {
|
||||
backend "remote" {
|
||||
hostname = "%s"
|
||||
organization = "%s"
|
||||
|
||||
workspaces {
|
||||
name = "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output "val" {
|
||||
value = "${terraform.workspace}"
|
||||
}
|
||||
`, tfeHostname, org, name)
|
||||
}
|
||||
|
||||
func terraformConfigRemoteBackendPrefix(org, prefix string) string {
|
||||
return fmt.Sprintf(`
|
||||
terraform {
|
||||
backend "remote" {
|
||||
hostname = "%s"
|
||||
organization = "%s"
|
||||
|
||||
workspaces {
|
||||
prefix = "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output "val" {
|
||||
value = "${terraform.workspace}"
|
||||
}
|
||||
`, tfeHostname, org, prefix)
|
||||
}
|
||||
|
||||
func terraformConfigCloudBackendTags(org, tag string) string {
|
||||
return fmt.Sprintf(`
|
||||
terraform {
|
||||
|
|
|
@ -5,7 +5,6 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
@ -168,7 +167,7 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
|
|||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
fmt.Println("Test: ", name)
|
||||
t.Log("Test: ", name)
|
||||
organization, cleanup := createOrganization(t)
|
||||
defer cleanup()
|
||||
exp, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithDefaultTimeout(expectConsoleTimeout))
|
||||
|
@ -192,6 +191,7 @@ func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
|
|||
op.prep(t, organization.Name, tf.WorkDir())
|
||||
for _, tfCmd := range op.commands {
|
||||
t.Log("Running commands: ", tfCmd.command)
|
||||
tfCmd.command = append(tfCmd.command, "-ignore-remote-version")
|
||||
cmd := tf.Cmd(tfCmd.command...)
|
||||
cmd.Stdin = exp.Tty()
|
||||
cmd.Stdout = exp.Tty()
|
||||
|
@ -357,7 +357,7 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) {
|
|||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
fmt.Println("Test: ", name)
|
||||
t.Log("Test: ", name)
|
||||
organization, cleanup := createOrganization(t)
|
||||
t.Log(organization.Name)
|
||||
defer cleanup()
|
||||
|
|
|
@ -4,56 +4,912 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
expect "github.com/Netflix/go-expect"
|
||||
tfe "github.com/hashicorp/go-tfe"
|
||||
"github.com/hashicorp/terraform/internal/e2e"
|
||||
)
|
||||
|
||||
// REMOTE BACKEND
|
||||
/*
|
||||
- RB name -> TFC name
|
||||
-- straight copy if only if different name, or same WS name in diff org
|
||||
-- other
|
||||
-- ensure that the local workspace, after migration, is the new name (in the tfc config block)
|
||||
- RB name -> TFC tags
|
||||
-- just add tag, if in same org
|
||||
-- If new org, if WS exists, just add tag
|
||||
-- If new org, if WS not exists, create and add tag
|
||||
- RB prefix -> TFC name
|
||||
-- create if not exists
|
||||
-- migrate the current worksapce state to ws name
|
||||
- RB prefix -> TFC tags
|
||||
-- update previous workspaces (prefix + local) with cloud config tag
|
||||
-- Rename the local workspaces to match the TFC workspaces (prefix + former local, ie app-prod). inform user
|
||||
|
||||
*/
|
||||
func Test_migrate_remote_backend_name_to_tfc(t *testing.T) {
|
||||
t.Skip("TODO: see comments")
|
||||
_ = map[string]struct {
|
||||
func Test_migrate_remote_backend_name_to_tfc_name(t *testing.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 name strategy": {},
|
||||
"single workspace with backend name strategy, to cloud with tags strategy": {},
|
||||
"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 the new backend?`,
|
||||
userInput: []string{"yes"},
|
||||
postInputOutput: []string{`Successfully configured the backend "cloud"!`},
|
||||
},
|
||||
{
|
||||
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)
|
||||
}
|
||||
},
|
||||
},
|
||||
"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 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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tc.validations != nil {
|
||||
tc.validations(t, organization.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_migrate_remote_backend_name_to_tfc_name_different_org(t *testing.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{
|
||||
{
|
||||
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: `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: `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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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 tc.validations != nil {
|
||||
tc.validations(t, orgName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_migrate_remote_backend_name_to_tfc_tags(t *testing.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{
|
||||
{
|
||||
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`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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{"cloud-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: `cloud-workspace`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
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.Skip("TODO: see comments")
|
||||
_ = map[string]struct {
|
||||
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": {},
|
||||
"multiple workspaces with backend prefix strategy, to cloud with name strategy": {},
|
||||
"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 the new backend?`,
|
||||
userInput: []string{"yes"},
|
||||
postInputOutput: []string{
|
||||
`Successfully configured the backend "cloud"!`},
|
||||
},
|
||||
{
|
||||
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{
|
||||
`Successfully configured the backend "cloud"!`},
|
||||
},
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
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.Skip("TODO: see comments")
|
||||
_ = map[string]struct {
|
||||
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": {},
|
||||
"multiple workspaces with backend prefix strategy, to cloud with tags strategy": {},
|
||||
"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: `The "cloud" backend configuration only allows named workspaces!`,
|
||||
userInput: []string{"cloud-workspace", "yes"},
|
||||
postInputOutput: []string{
|
||||
`Do you want to copy existing state to the new backend?`,
|
||||
`Successfully configured the backend "cloud"!`},
|
||||
},
|
||||
{
|
||||
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?`,
|
||||
`Successfully configured the backend "cloud"!`},
|
||||
},
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tc.validations != nil {
|
||||
tc.validations(t, organization.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,16 +57,16 @@ func Test_cloud_run_variables(t *testing.T) {
|
|||
},
|
||||
commands: []tfCommand{
|
||||
{
|
||||
command: []string{"init"},
|
||||
expectedOutput: `Successfully configured the backend "cloud"!`,
|
||||
command: []string{"init"},
|
||||
expectedCmdOutput: `Successfully configured the backend "cloud"!`,
|
||||
},
|
||||
{
|
||||
command: []string{"plan", "-var", "foo=bar"},
|
||||
expectedOutput: ` + test_cli = "bar"`,
|
||||
command: []string{"plan", "-var", "foo=bar"},
|
||||
expectedCmdOutput: ` + test_cli = "bar"`,
|
||||
},
|
||||
{
|
||||
command: []string{"plan", "-var", "foo=bar"},
|
||||
expectedOutput: ` + test_env = "qux"`,
|
||||
command: []string{"plan", "-var", "foo=bar"},
|
||||
expectedCmdOutput: ` + test_env = "qux"`,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -110,23 +110,28 @@ func Test_cloud_run_variables(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if tfCmd.expectedOutput != "" {
|
||||
_, err := exp.ExpectString(tfCmd.expectedOutput)
|
||||
if tfCmd.expectedCmdOutput != "" {
|
||||
_, err := exp.ExpectString(tfCmd.expectedCmdOutput)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(tfCmd.userInput) > 0 {
|
||||
for _, input := range tfCmd.userInput {
|
||||
lenInput := len(tfCmd.userInput)
|
||||
lenInputOutput := len(tfCmd.postInputOutput)
|
||||
if lenInput > 0 {
|
||||
for i := 0; i < lenInput; i++ {
|
||||
input := tfCmd.userInput[i]
|
||||
exp.SendLine(input)
|
||||
}
|
||||
}
|
||||
|
||||
if tfCmd.postInputOutput != "" {
|
||||
_, err := exp.ExpectString(tfCmd.postInputOutput)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ type backendMigrateOpts struct {
|
|||
//
|
||||
// This will attempt to lock both states for the migration.
|
||||
func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
|
||||
log.Printf("[TRACE] backendMigrateState: need to migrate from %q to %q backend config", opts.SourceType, opts.DestinationType)
|
||||
log.Printf("[INFO] backendMigrateState: need to migrate from %q to %q backend config", opts.SourceType, opts.DestinationType)
|
||||
// We need to check what the named state status is. If we're converting
|
||||
// from multi-state to single-state for example, we need to handle that.
|
||||
var sourceSingleState, destinationSingleState, sourceTFC, destinationTFC bool
|
||||
|
@ -154,7 +154,7 @@ func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
|
|||
|
||||
// Multi-state to multi-state.
|
||||
func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
|
||||
log.Print("[TRACE] backendMigrateState: migrating all named workspaces")
|
||||
log.Print("[INFO] backendMigrateState: migrating all named workspaces")
|
||||
|
||||
migrate := opts.force
|
||||
if !migrate {
|
||||
|
@ -209,9 +209,9 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
|
|||
|
||||
// Multi-state to single state.
|
||||
func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
|
||||
log.Printf("[TRACE] backendMigrateState: destination backend type %q does not support named workspaces", opts.DestinationType)
|
||||
log.Printf("[INFO] backendMigrateState: destination backend type %q does not support named workspaces", opts.DestinationType)
|
||||
|
||||
currentEnv, err := m.Workspace()
|
||||
currentWorkspace, err := m.Workspace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
|
|||
opts.DestinationType),
|
||||
Description: fmt.Sprintf(
|
||||
strings.TrimSpace(inputBackendMigrateMultiToSingle),
|
||||
opts.SourceType, opts.DestinationType, currentEnv),
|
||||
opts.SourceType, opts.DestinationType, currentWorkspace),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
|
@ -241,7 +241,7 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
|
|||
}
|
||||
|
||||
// Copy the default state
|
||||
opts.sourceWorkspace = currentEnv
|
||||
opts.sourceWorkspace = currentWorkspace
|
||||
|
||||
// now switch back to the default env so we can acccess the new backend
|
||||
m.SetWorkspace(backend.DefaultStateName)
|
||||
|
@ -251,7 +251,7 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
|
|||
|
||||
// Single state to single state, assumed default state name.
|
||||
func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
|
||||
log.Printf("[TRACE] backendMigrateState: migrating %q workspace to %q workspace", opts.sourceWorkspace, opts.destinationWorkspace)
|
||||
log.Printf("[INFO] backendMigrateState: single-to-single migrating %q workspace to %q workspace", opts.sourceWorkspace, opts.destinationWorkspace)
|
||||
|
||||
sourceState, err := opts.Source.StateMgr(opts.sourceWorkspace)
|
||||
if err != nil {
|
||||
|
@ -534,7 +534,7 @@ func (m *Meta) backendMigrateTFC(opts *backendMigrateOpts) error {
|
|||
// Everything below, by the above two conditionals, now assumes that the
|
||||
// destination is always Terraform Cloud (TFC).
|
||||
|
||||
sourceSingle := sourceSingleState || (len(sourceWorkspaces) == 1 && sourceWorkspaces[0] == backend.DefaultStateName)
|
||||
sourceSingle := sourceSingleState || (len(sourceWorkspaces) == 1)
|
||||
if sourceSingle {
|
||||
if cloudBackendDestination.WorkspaceMapping.Strategy() == cloud.WorkspaceNameStrategy {
|
||||
// If we know the name via WorkspaceNameStrategy, then set the
|
||||
|
@ -543,6 +543,14 @@ func (m *Meta) backendMigrateTFC(opts *backendMigrateOpts) error {
|
|||
// in TFC if it does not exist.
|
||||
opts.destinationWorkspace = cloudBackendDestination.WorkspaceMapping.Name
|
||||
}
|
||||
|
||||
currentWorkspace, err := m.Workspace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.sourceWorkspace = currentWorkspace
|
||||
|
||||
log.Printf("[INFO] backendMigrateTFC: single-to-single migration from source %s to destination %q", opts.sourceWorkspace, opts.destinationWorkspace)
|
||||
// Run normal single-to-single state migration
|
||||
// This will handle both situations where the new cloud backend
|
||||
// configuration is using a workspace.name strategy or workspace.tags
|
||||
|
@ -559,13 +567,14 @@ func (m *Meta) backendMigrateTFC(opts *backendMigrateOpts) error {
|
|||
return err
|
||||
}
|
||||
|
||||
currentEnv, err := m.Workspace()
|
||||
currentWorkspace, err := m.Workspace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts.sourceWorkspace = currentEnv
|
||||
opts.sourceWorkspace = currentWorkspace
|
||||
opts.destinationWorkspace = cloudBackendDestination.WorkspaceMapping.Name
|
||||
log.Printf("[INFO] backendMigrateTFC: multi-to-single migration from source %s to destination %q", opts.sourceWorkspace, opts.destinationWorkspace)
|
||||
|
||||
return m.backendMigrateState_s_s(opts)
|
||||
}
|
||||
|
@ -574,9 +583,13 @@ func (m *Meta) backendMigrateTFC(opts *backendMigrateOpts) error {
|
|||
// workspace over to new one, prompt for workspace name pattern (*),
|
||||
// and start migrating, and create tags for each workspace.
|
||||
if multiSource && destinationTagsStrategy {
|
||||
log.Printf("[INFO] backendMigrateTFC: multi-to-multi migration from source workspaces %q", sourceWorkspaces)
|
||||
return m.backendMigrateState_S_TFC(opts, sourceWorkspaces)
|
||||
}
|
||||
|
||||
// TODO(omar): after the check for sourceSingle is done, everything following
|
||||
// it has to be multi. So rework the code to not need to check for multi, adn
|
||||
// return m.backendMigrateState_S_TFC here.
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -619,6 +632,7 @@ func (m *Meta) backendMigrateState_S_TFC(opts *backendMigrateOpts, sourceWorkspa
|
|||
opts.force = true
|
||||
|
||||
// Perform the migration
|
||||
log.Printf("[INFO] backendMigrateTFC: multi-to-multi migration, source workspace %q to destination workspace %q", opts.sourceWorkspace, opts.destinationWorkspace)
|
||||
if err := m.backendMigrateState_s_s(opts); err != nil {
|
||||
return fmt.Errorf(strings.TrimSpace(
|
||||
errMigrateMulti), name, opts.SourceType, opts.DestinationType, err)
|
||||
|
|
Loading…
Reference in New Issue