github.com/kevinklinger/open_terraform@v1.3.6/noninternal/cloud/e2e/migrate_state_tfc_to_tfc_test.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	tfe "github.com/hashicorp/go-tfe"
     8  	tfversion "github.com/kevinklinger/open_terraform/version"
     9  )
    10  
    11  func Test_migrate_tfc_to_tfc_single_workspace(t *testing.T) {
    12  	t.Parallel()
    13  	skipIfMissingEnvVar(t)
    14  	skipWithoutRemoteTerraformVersion(t)
    15  
    16  	ctx := context.Background()
    17  
    18  	cases := testCases{
    19  		"migrating from name to name": {
    20  			operations: []operationSets{
    21  				{
    22  					prep: func(t *testing.T, orgName, dir string) {
    23  						wsName := "prod"
    24  						// Creating the workspace here instead of it being created
    25  						// dynamically in the Cloud StateMgr because we want to ensure that
    26  						// the terraform version selected for the workspace matches the
    27  						// terraform version of this current branch.
    28  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
    29  							Name:             tfe.String("prod"),
    30  							TerraformVersion: tfe.String(tfversion.String()),
    31  						})
    32  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
    33  						writeMainTF(t, tfBlock, dir)
    34  					},
    35  					commands: []tfCommand{
    36  						{
    37  							command:           []string{"init"},
    38  							expectedCmdOutput: `Terraform Cloud has been successfully initialized!`,
    39  						},
    40  						{
    41  							command:           []string{"workspace", "show"},
    42  							expectedCmdOutput: `prod`, // this comes from the `prep` function
    43  						},
    44  						{
    45  							command:         []string{"apply", "-auto-approve"},
    46  							postInputOutput: []string{`Apply complete!`},
    47  						},
    48  					},
    49  				},
    50  				{
    51  					prep: func(t *testing.T, orgName, dir string) {
    52  						wsName := "dev"
    53  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
    54  							Name:             tfe.String(wsName),
    55  							TerraformVersion: tfe.String(tfversion.String()),
    56  						})
    57  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
    58  						writeMainTF(t, tfBlock, dir)
    59  					},
    60  					commands: []tfCommand{
    61  						{
    62  							command:         []string{"init", "-ignore-remote-version"},
    63  							postInputOutput: []string{`Terraform Cloud has been successfully initialized!`},
    64  						},
    65  						{
    66  							command:           []string{"workspace", "show"},
    67  							expectedCmdOutput: `dev`, // this comes from the `prep` function
    68  						},
    69  					},
    70  				},
    71  			},
    72  			validations: func(t *testing.T, orgName string) {
    73  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
    74  				if err != nil {
    75  					t.Fatal(err)
    76  				}
    77  				// this workspace name is what exists in the cloud backend configuration block
    78  				if len(wsList.Items) != 2 {
    79  					t.Fatal("Expected number of workspaces to be 2")
    80  				}
    81  			},
    82  		},
    83  		"migrating from name to tags": {
    84  			operations: []operationSets{
    85  				{
    86  					prep: func(t *testing.T, orgName, dir string) {
    87  						wsName := "prod"
    88  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
    89  							Name:             tfe.String("prod"),
    90  							TerraformVersion: tfe.String(tfversion.String()),
    91  						})
    92  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
    93  						writeMainTF(t, tfBlock, dir)
    94  					},
    95  					commands: []tfCommand{
    96  						{
    97  							command:           []string{"init"},
    98  							expectedCmdOutput: `Terraform Cloud has been successfully initialized!`,
    99  						},
   100  						{
   101  							command:         []string{"apply", "-auto-approve"},
   102  							postInputOutput: []string{`Apply complete!`},
   103  						},
   104  					},
   105  				},
   106  				{
   107  					prep: func(t *testing.T, orgName, dir string) {
   108  						tag := "app"
   109  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   110  						writeMainTF(t, tfBlock, dir)
   111  					},
   112  					commands: []tfCommand{
   113  						{
   114  							command:           []string{"init", "-ignore-remote-version"},
   115  							expectedCmdOutput: `There are no workspaces with the configured tags (app)`,
   116  							userInput:         []string{"new-workspace"},
   117  							postInputOutput: []string{
   118  								`Terraform can create a properly tagged workspace for you now.`,
   119  								`Terraform Cloud has been successfully initialized!`},
   120  						},
   121  						{
   122  							command:           []string{"workspace", "show"},
   123  							expectedCmdOutput: `new-workspace`, // this comes from the `prep` function
   124  						},
   125  					},
   126  				},
   127  			},
   128  			validations: func(t *testing.T, orgName string) {
   129  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
   130  					Tags: "app",
   131  				})
   132  				if err != nil {
   133  					t.Fatal(err)
   134  				}
   135  				// this workspace name is what exists in the cloud backend configuration block
   136  				if len(wsList.Items) != 1 {
   137  					t.Fatal("Expected number of workspaces to be 1")
   138  				}
   139  			},
   140  		},
   141  		"migrating from name to tags without ignore-version flag": {
   142  			operations: []operationSets{
   143  				{
   144  					prep: func(t *testing.T, orgName, dir string) {
   145  						wsName := "prod"
   146  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   147  							Name:             tfe.String("prod"),
   148  							TerraformVersion: tfe.String(tfversion.String()),
   149  						})
   150  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   151  						writeMainTF(t, tfBlock, dir)
   152  					},
   153  					commands: []tfCommand{
   154  						{
   155  							command:           []string{"init"},
   156  							expectedCmdOutput: `Terraform Cloud has been successfully initialized!`,
   157  						},
   158  						{
   159  							command:         []string{"apply", "-auto-approve"},
   160  							postInputOutput: []string{`Apply complete!`},
   161  						},
   162  					},
   163  				},
   164  				{
   165  					prep: func(t *testing.T, orgName, dir string) {
   166  						tag := "app"
   167  						// This is only here to ensure that the updated terraform version is
   168  						// present in the workspace, and it does not default to a lower
   169  						// version that does not support `cloud`.
   170  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   171  							Name:             tfe.String("new-workspace"),
   172  							TerraformVersion: tfe.String(tfversion.String()),
   173  						})
   174  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   175  						writeMainTF(t, tfBlock, dir)
   176  					},
   177  					commands: []tfCommand{
   178  						{
   179  							command:           []string{"init"},
   180  							expectedCmdOutput: `There are no workspaces with the configured tags (app)`,
   181  							userInput:         []string{"new-workspace"},
   182  							postInputOutput: []string{
   183  								`Terraform can create a properly tagged workspace for you now.`,
   184  								`Terraform Cloud has been successfully initialized!`},
   185  						},
   186  					},
   187  				},
   188  			},
   189  			validations: func(t *testing.T, orgName string) {
   190  				// We created the workspace, so it will be there. We could not complete the state migration,
   191  				// though, so the workspace should be empty.
   192  				ws, err := tfeClient.Workspaces.ReadWithOptions(ctx, orgName, "new-workspace", &tfe.WorkspaceReadOptions{Include: []tfe.WSIncludeOpt{tfe.WSCurrentRun}})
   193  				if err != nil {
   194  					t.Fatal(err)
   195  				}
   196  				if ws.CurrentRun != nil {
   197  					t.Fatal("Expected to workspace be empty")
   198  				}
   199  			},
   200  		},
   201  	}
   202  
   203  	testRunner(t, cases, 1)
   204  }
   205  
   206  func Test_migrate_tfc_to_tfc_multiple_workspace(t *testing.T) {
   207  	t.Parallel()
   208  	skipIfMissingEnvVar(t)
   209  	skipWithoutRemoteTerraformVersion(t)
   210  
   211  	ctx := context.Background()
   212  
   213  	cases := testCases{
   214  		"migrating from multiple workspaces via tags to name": {
   215  			operations: []operationSets{
   216  				{
   217  					prep: func(t *testing.T, orgName, dir string) {
   218  						tag := "app"
   219  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   220  							Name:             tfe.String("app-prod"),
   221  							Tags:             []*tfe.Tag{{Name: tag}},
   222  							TerraformVersion: tfe.String(tfversion.String()),
   223  						})
   224  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   225  							Name:             tfe.String("app-staging"),
   226  							Tags:             []*tfe.Tag{{Name: tag}},
   227  							TerraformVersion: tfe.String(tfversion.String()),
   228  						})
   229  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   230  						writeMainTF(t, tfBlock, dir)
   231  					},
   232  					commands: []tfCommand{
   233  						{
   234  							command:           []string{"init"},
   235  							expectedCmdOutput: `The currently selected workspace (default) does not exist.`,
   236  							userInput:         []string{"1"},
   237  							postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
   238  						},
   239  						{
   240  							command:           []string{"apply"},
   241  							expectedCmdOutput: `Do you want to perform these actions in workspace "app-prod"?`,
   242  							userInput:         []string{"yes"},
   243  							postInputOutput:   []string{`Apply complete!`},
   244  						},
   245  						{
   246  							command:           []string{"workspace", "select", "app-staging"},
   247  							expectedCmdOutput: `Switched to workspace "app-staging".`,
   248  						},
   249  						{
   250  							command:         []string{"apply", "-auto-approve"},
   251  							postInputOutput: []string{`Apply complete!`},
   252  						},
   253  						{
   254  							command:           []string{"output"},
   255  							expectedCmdOutput: `tag_val = "app"`,
   256  						},
   257  					},
   258  				},
   259  				{
   260  					prep: func(t *testing.T, orgName, dir string) {
   261  						name := "service"
   262  						// Doing this here instead of relying on dynamic workspace creation
   263  						// because we want to set the terraform version here so that it is
   264  						// using the right version for post init operations.
   265  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   266  							Name:             tfe.String(name),
   267  							TerraformVersion: tfe.String(tfversion.String()),
   268  						})
   269  						tfBlock := terraformConfigCloudBackendName(orgName, name)
   270  						writeMainTF(t, tfBlock, dir)
   271  					},
   272  					commands: []tfCommand{
   273  						{
   274  							command:           []string{"init", "-ignore-remote-version"},
   275  							expectedCmdOutput: `Terraform Cloud has been successfully initialized!`,
   276  							postInputOutput:   []string{`tag_val = "service"`},
   277  						},
   278  						{
   279  							command:           []string{"workspace", "show"},
   280  							expectedCmdOutput: `service`, // this comes from the `prep` function
   281  						},
   282  					},
   283  				},
   284  			},
   285  			validations: func(t *testing.T, orgName string) {
   286  				ws, err := tfeClient.Workspaces.Read(ctx, orgName, "service")
   287  				if err != nil {
   288  					t.Fatal(err)
   289  				}
   290  				if ws == nil {
   291  					t.Fatal("Expected to workspace not be empty")
   292  				}
   293  			},
   294  		},
   295  		"migrating from multiple workspaces via tags to other tags": {
   296  			operations: []operationSets{
   297  				{
   298  					prep: func(t *testing.T, orgName, dir string) {
   299  						tag := "app"
   300  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   301  							Name:             tfe.String("app-prod"),
   302  							Tags:             []*tfe.Tag{{Name: tag}},
   303  							TerraformVersion: tfe.String(tfversion.String()),
   304  						})
   305  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   306  							Name:             tfe.String("app-staging"),
   307  							Tags:             []*tfe.Tag{{Name: tag}},
   308  							TerraformVersion: tfe.String(tfversion.String()),
   309  						})
   310  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   311  						writeMainTF(t, tfBlock, dir)
   312  					},
   313  					commands: []tfCommand{
   314  						{
   315  							command:           []string{"init"},
   316  							expectedCmdOutput: `The currently selected workspace (default) does not exist.`,
   317  							userInput:         []string{"1"},
   318  							postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
   319  						},
   320  						{
   321  							command:           []string{"apply", "-auto-approve"},
   322  							expectedCmdOutput: `Apply complete!`,
   323  						},
   324  						{
   325  							command:           []string{"workspace", "select", "app-staging"},
   326  							expectedCmdOutput: `Switched to workspace "app-staging".`,
   327  						},
   328  						{
   329  							command:           []string{"apply", "-auto-approve"},
   330  							expectedCmdOutput: `Apply complete!`,
   331  						},
   332  					},
   333  				},
   334  				{
   335  					prep: func(t *testing.T, orgName, dir string) {
   336  						tag := "billing"
   337  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   338  						writeMainTF(t, tfBlock, dir)
   339  					},
   340  					commands: []tfCommand{
   341  						{
   342  							command:           []string{"init", "-ignore-remote-version"},
   343  							expectedCmdOutput: `There are no workspaces with the configured tags (billing)`,
   344  							userInput:         []string{"new-app-prod"},
   345  							postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
   346  						},
   347  					},
   348  				},
   349  			},
   350  			validations: func(t *testing.T, orgName string) {
   351  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
   352  					Tags: "billing",
   353  				})
   354  				if err != nil {
   355  					t.Fatal(err)
   356  				}
   357  				if len(wsList.Items) != 1 {
   358  					t.Logf("Expected the number of workspaces to be 2, but got %d", len(wsList.Items))
   359  				}
   360  				_, empty := getWorkspace(wsList.Items, "new-app-prod")
   361  				if empty {
   362  					t.Fatalf("expected workspaces to include 'new-app-prod' but didn't.")
   363  				}
   364  			},
   365  		},
   366  	}
   367  
   368  	testRunner(t, cases, 1)
   369  }