github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  
    10  	tfe "github.com/hashicorp/go-tfe"
    11  )
    12  
    13  func Test_migrate_remote_backend_single_org(t *testing.T) {
    14  	t.Parallel()
    15  	skipIfMissingEnvVar(t)
    16  	skipWithoutRemoteTerraformVersion(t)
    17  
    18  	ctx := context.Background()
    19  	cases := testCases{
    20  		"migrate remote backend name to tfc name": {
    21  			operations: []operationSets{
    22  				{
    23  					prep: func(t *testing.T, orgName, dir string) {
    24  						remoteWorkspace := "remote-workspace"
    25  						tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
    26  						writeMainTF(t, tfBlock, dir)
    27  					},
    28  					commands: []tfCommand{
    29  						{
    30  							command:           []string{"init"},
    31  							expectedCmdOutput: `Successfully configured the backend "remote"!`,
    32  						},
    33  						{
    34  							command:           []string{"apply", "-auto-approve"},
    35  							expectedCmdOutput: `Apply complete!`,
    36  						},
    37  					},
    38  				},
    39  				{
    40  					prep: func(t *testing.T, orgName, dir string) {
    41  						wsName := "cloud-workspace"
    42  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
    43  						writeMainTF(t, tfBlock, dir)
    44  					},
    45  					commands: []tfCommand{
    46  						{
    47  							command:           []string{"init", "-ignore-remote-version"},
    48  							expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
    49  							userInput:         []string{"yes", "yes"},
    50  							postInputOutput: []string{
    51  								`Should Terraform migrate your existing state?`,
    52  								`Terraform Cloud has been successfully initialized!`},
    53  						},
    54  						{
    55  							command:           []string{"workspace", "show"},
    56  							expectedCmdOutput: `cloud-workspace`,
    57  						},
    58  					},
    59  				},
    60  			},
    61  			validations: func(t *testing.T, orgName string) {
    62  				expectedName := "cloud-workspace"
    63  				ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
    64  				if err != nil {
    65  					t.Fatal(err)
    66  				}
    67  				if ws == nil {
    68  					t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
    69  				}
    70  			},
    71  		},
    72  		"migrate remote backend name to tfc same name": {
    73  			operations: []operationSets{
    74  				{
    75  					prep: func(t *testing.T, orgName, dir string) {
    76  						remoteWorkspace := "remote-workspace"
    77  						tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
    78  						writeMainTF(t, tfBlock, dir)
    79  					},
    80  					commands: []tfCommand{
    81  						{
    82  							command:           []string{"init"},
    83  							expectedCmdOutput: `Successfully configured the backend "remote"!`,
    84  						},
    85  						{
    86  							command:         []string{"apply", "-auto-approve"},
    87  							postInputOutput: []string{`Apply complete!`},
    88  						},
    89  					},
    90  				},
    91  				{
    92  					prep: func(t *testing.T, orgName, dir string) {
    93  						wsName := "remote-workspace"
    94  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
    95  						writeMainTF(t, tfBlock, dir)
    96  					},
    97  					commands: []tfCommand{
    98  						{
    99  							command:           []string{"init", "-ignore-remote-version"},
   100  							expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   101  							userInput:         []string{"yes", "yes"},
   102  							postInputOutput: []string{
   103  								`Should Terraform migrate your existing state?`,
   104  								`Terraform Cloud has been successfully initialized!`},
   105  						},
   106  						{
   107  							command:           []string{"workspace", "show"},
   108  							expectedCmdOutput: `remote-workspace`,
   109  						},
   110  					},
   111  				},
   112  			},
   113  			validations: func(t *testing.T, orgName string) {
   114  				expectedName := "remote-workspace"
   115  				ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   116  				if err != nil {
   117  					t.Fatal(err)
   118  				}
   119  				if ws == nil {
   120  					t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   121  				}
   122  			},
   123  		},
   124  		"migrate remote backend name to tfc tags": {
   125  			operations: []operationSets{
   126  				{
   127  					prep: func(t *testing.T, orgName, dir string) {
   128  						remoteWorkspace := "remote-workspace"
   129  						tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
   130  						writeMainTF(t, tfBlock, dir)
   131  					},
   132  					commands: []tfCommand{
   133  						{
   134  							command:           []string{"init"},
   135  							expectedCmdOutput: `Successfully configured the backend "remote"!`,
   136  						},
   137  						{
   138  							command:         []string{"apply", "-auto-approve"},
   139  							postInputOutput: []string{`Apply complete!`},
   140  						},
   141  						{
   142  							command:           []string{"workspace", "show"},
   143  							expectedCmdOutput: `default`,
   144  						},
   145  					},
   146  				},
   147  				{
   148  					prep: func(t *testing.T, orgName, dir string) {
   149  						tag := "app"
   150  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   151  						writeMainTF(t, tfBlock, dir)
   152  					},
   153  					commands: []tfCommand{
   154  						{
   155  							command:           []string{"init", "-ignore-remote-version"},
   156  							expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   157  							userInput:         []string{"yes", "cloud-workspace", "yes"},
   158  							postInputOutput: []string{
   159  								`Should Terraform migrate your existing state?`,
   160  								`Terraform Cloud requires all workspaces to be given an explicit name.`,
   161  								`Terraform Cloud has been successfully initialized!`},
   162  						},
   163  						{
   164  							command:           []string{"workspace", "show"},
   165  							expectedCmdOutput: `cloud-workspace`,
   166  						},
   167  					},
   168  				},
   169  			},
   170  			validations: func(t *testing.T, orgName string) {
   171  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
   172  					Tags: "app",
   173  				})
   174  				if err != nil {
   175  					t.Fatal(err)
   176  				}
   177  				if len(wsList.Items) != 1 {
   178  					t.Fatalf("Expected number of workspaces to be 1, but got %d", len(wsList.Items))
   179  				}
   180  				ws := wsList.Items[0]
   181  				if ws.Name != "cloud-workspace" {
   182  					t.Fatalf("Expected workspace to be `cloud-workspace`, but is %s", ws.Name)
   183  				}
   184  			},
   185  		},
   186  		"migrate remote backend prefix to tfc name strategy single workspace": {
   187  			operations: []operationSets{
   188  				{
   189  					prep: func(t *testing.T, orgName, dir string) {
   190  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   191  						prefix := "app-"
   192  						tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   193  						writeMainTF(t, tfBlock, dir)
   194  					},
   195  					commands: []tfCommand{
   196  						{
   197  							command:           []string{"init"},
   198  							expectedCmdOutput: `Terraform has been successfully initialized!`,
   199  						},
   200  						{
   201  							command:         []string{"apply", "-auto-approve"},
   202  							postInputOutput: []string{`Apply complete!`},
   203  						},
   204  					},
   205  				},
   206  				{
   207  					prep: func(t *testing.T, orgName, dir string) {
   208  						wsName := "cloud-workspace"
   209  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   210  						writeMainTF(t, tfBlock, dir)
   211  					},
   212  					commands: []tfCommand{
   213  						{
   214  							command:           []string{"init", "-ignore-remote-version"},
   215  							expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   216  							userInput:         []string{"yes", "yes"},
   217  							postInputOutput: []string{
   218  								`Should Terraform migrate your existing state?`,
   219  								`Terraform Cloud has been successfully initialized!`},
   220  						},
   221  						{
   222  							command:           []string{"workspace", "show"},
   223  							expectedCmdOutput: `cloud-workspace`,
   224  						},
   225  					},
   226  				},
   227  			},
   228  			validations: func(t *testing.T, orgName string) {
   229  				expectedName := "cloud-workspace"
   230  				ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   231  				if err != nil {
   232  					t.Fatal(err)
   233  				}
   234  				if ws == nil {
   235  					t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   236  				}
   237  			},
   238  		},
   239  		"migrate remote backend prefix to tfc name strategy multi workspace": {
   240  			operations: []operationSets{
   241  				{
   242  					prep: func(t *testing.T, orgName, dir string) {
   243  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   244  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")})
   245  						prefix := "app-"
   246  						tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   247  						writeMainTF(t, tfBlock, dir)
   248  					},
   249  					commands: []tfCommand{
   250  						{
   251  							command:           []string{"init"},
   252  							expectedCmdOutput: `The currently selected workspace (default) does not exist.`,
   253  							userInput:         []string{"1"},
   254  							postInputOutput:   []string{`Terraform has been successfully initialized!`},
   255  						},
   256  						{
   257  							command:         []string{"apply", "-auto-approve"},
   258  							postInputOutput: []string{`Apply complete!`},
   259  						},
   260  						{
   261  							command:           []string{"workspace", "list"},
   262  							expectedCmdOutput: "* one", // app name retrieved via prefix
   263  						},
   264  						{
   265  							command:           []string{"workspace", "select", "two"},
   266  							expectedCmdOutput: `Switched to workspace "two".`, // app name retrieved via prefix
   267  						},
   268  					},
   269  				},
   270  				{
   271  					prep: func(t *testing.T, orgName, dir string) {
   272  						wsName := "cloud-workspace"
   273  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   274  						writeMainTF(t, tfBlock, dir)
   275  					},
   276  					commands: []tfCommand{
   277  						{
   278  							command:           []string{"init", "-ignore-remote-version"},
   279  							expectedCmdOutput: `Do you want to copy only your current workspace?`,
   280  							userInput:         []string{"yes"},
   281  							postInputOutput: []string{
   282  								`Terraform Cloud has been successfully initialized!`},
   283  						},
   284  						{
   285  							command:           []string{"workspace", "show"},
   286  							expectedCmdOutput: `cloud-workspace`,
   287  						},
   288  					},
   289  				},
   290  			},
   291  			validations: func(t *testing.T, orgName string) {
   292  				expectedName := "cloud-workspace"
   293  				ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   294  				if err != nil {
   295  					t.Fatal(err)
   296  				}
   297  				if ws == nil {
   298  					t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   299  				}
   300  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
   301  				if err != nil {
   302  					t.Fatal(err)
   303  				}
   304  				if len(wsList.Items) != 3 {
   305  					t.Fatalf("expected number of workspaces in this org to be 3, but got %d", len(wsList.Items))
   306  				}
   307  				_, empty := getWorkspace(wsList.Items, "cloud-workspace")
   308  				if empty {
   309  					t.Fatalf("expected workspaces to include 'cloud-workspace' but didn't.")
   310  				}
   311  				_, empty = getWorkspace(wsList.Items, "app-one")
   312  				if empty {
   313  					t.Fatalf("expected workspaces to include 'app-one' but didn't.")
   314  				}
   315  				_, empty = getWorkspace(wsList.Items, "app-two")
   316  				if empty {
   317  					t.Fatalf("expected workspaces to include 'app-two' but didn't.")
   318  				}
   319  			},
   320  		},
   321  		"migrate remote backend prefix to tfc tags strategy single workspace": {
   322  			operations: []operationSets{
   323  				{
   324  					prep: func(t *testing.T, orgName, dir string) {
   325  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   326  						prefix := "app-"
   327  						tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   328  						writeMainTF(t, tfBlock, dir)
   329  					},
   330  					commands: []tfCommand{
   331  						{
   332  							command:           []string{"init"},
   333  							expectedCmdOutput: `Terraform has been successfully initialized!`,
   334  						},
   335  						{
   336  							command:         []string{"apply", "-auto-approve"},
   337  							postInputOutput: []string{`Apply complete!`},
   338  						},
   339  					},
   340  				},
   341  				{
   342  					prep: func(t *testing.T, orgName, dir string) {
   343  						tag := "app"
   344  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   345  						writeMainTF(t, tfBlock, dir)
   346  					},
   347  					commands: []tfCommand{
   348  						{
   349  							command:           []string{"init", "-ignore-remote-version"},
   350  							expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   351  							userInput:         []string{"yes", "cloud-workspace", "yes"},
   352  							postInputOutput: []string{
   353  								`Should Terraform migrate your existing state?`,
   354  								`Terraform Cloud requires all workspaces to be given an explicit name.`,
   355  								`Terraform Cloud has been successfully initialized!`},
   356  						},
   357  						{
   358  							command:           []string{"workspace", "list"},
   359  							expectedCmdOutput: `cloud-workspace`,
   360  						},
   361  					},
   362  				},
   363  			},
   364  			validations: func(t *testing.T, orgName string) {
   365  				expectedName := "cloud-workspace"
   366  				ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   367  				if err != nil {
   368  					t.Fatal(err)
   369  				}
   370  				if ws == nil {
   371  					t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   372  				}
   373  			},
   374  		},
   375  		"migrate remote backend prefix to tfc tags strategy multi workspace": {
   376  			operations: []operationSets{
   377  				{
   378  					prep: func(t *testing.T, orgName, dir string) {
   379  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   380  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")})
   381  						prefix := "app-"
   382  						tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   383  						writeMainTF(t, tfBlock, dir)
   384  					},
   385  					commands: []tfCommand{
   386  						{
   387  							command:           []string{"init"},
   388  							expectedCmdOutput: `The currently selected workspace (default) does not exist.`,
   389  							userInput:         []string{"1"},
   390  							postInputOutput:   []string{`Terraform has been successfully initialized!`},
   391  						},
   392  						{
   393  							command:           []string{"apply"},
   394  							expectedCmdOutput: `Do you want to perform these actions in workspace "app-one"?`,
   395  							userInput:         []string{"yes"},
   396  							postInputOutput:   []string{`Apply complete!`},
   397  						},
   398  						{
   399  							command: []string{"workspace", "select", "two"},
   400  						},
   401  						{
   402  							command:           []string{"apply"},
   403  							expectedCmdOutput: `Do you want to perform these actions in workspace "app-two"?`,
   404  							userInput:         []string{"yes"},
   405  							postInputOutput:   []string{`Apply complete!`},
   406  						},
   407  					},
   408  				},
   409  				{
   410  					prep: func(t *testing.T, orgName, dir string) {
   411  						tag := "app"
   412  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   413  						writeMainTF(t, tfBlock, dir)
   414  					},
   415  					commands: []tfCommand{
   416  						{
   417  							command:           []string{"init", "-ignore-remote-version"},
   418  							expectedCmdOutput: `Do you wish to proceed?`,
   419  							userInput:         []string{"yes"},
   420  							postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
   421  						},
   422  						{
   423  							command:           []string{"workspace", "show"},
   424  							expectedCmdOutput: "app-two",
   425  						},
   426  						{
   427  							command:           []string{"workspace", "select", "app-one"},
   428  							expectedCmdOutput: `Switched to workspace "app-one".`,
   429  						},
   430  					},
   431  				},
   432  			},
   433  			validations: func(t *testing.T, orgName string) {
   434  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
   435  					Tags: "app",
   436  				})
   437  				if err != nil {
   438  					t.Fatal(err)
   439  				}
   440  				if len(wsList.Items) != 2 {
   441  					t.Logf("Expected the number of workspaces to be 2, but got %d", len(wsList.Items))
   442  				}
   443  				ws, empty := getWorkspace(wsList.Items, "app-one")
   444  				if empty {
   445  					t.Fatalf("expected workspaces to include 'app-one' but didn't.")
   446  				}
   447  				if len(ws.TagNames) == 0 {
   448  					t.Fatalf("expected workspaces 'one' to have tags.")
   449  				}
   450  				ws, empty = getWorkspace(wsList.Items, "app-two")
   451  				if empty {
   452  					t.Fatalf("expected workspaces to include 'app-two' but didn't.")
   453  				}
   454  				if len(ws.TagNames) == 0 {
   455  					t.Fatalf("expected workspaces 'app-two' to have tags.")
   456  				}
   457  			},
   458  		},
   459  	}
   460  
   461  	testRunner(t, cases, 1)
   462  }
   463  
   464  func Test_migrate_remote_backend_multi_org(t *testing.T) {
   465  	t.Parallel()
   466  	skipIfMissingEnvVar(t)
   467  	skipWithoutRemoteTerraformVersion(t)
   468  
   469  	ctx := context.Background()
   470  	cases := testCases{
   471  		"migrate remote backend name to tfc name": {
   472  			operations: []operationSets{
   473  				{
   474  					prep: func(t *testing.T, orgName, dir string) {
   475  						remoteWorkspace := "remote-workspace"
   476  						tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
   477  						writeMainTF(t, tfBlock, dir)
   478  					},
   479  					commands: []tfCommand{
   480  						{
   481  							command:           []string{"init"},
   482  							expectedCmdOutput: `Successfully configured the backend "remote"!`,
   483  						},
   484  						{
   485  							command:         []string{"apply", "-auto-approve"},
   486  							postInputOutput: []string{`Apply complete!`},
   487  						},
   488  					},
   489  				},
   490  				{
   491  					prep: func(t *testing.T, orgName, dir string) {
   492  						wsName := "remote-workspace"
   493  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   494  						writeMainTF(t, tfBlock, dir)
   495  					},
   496  					commands: []tfCommand{
   497  						{
   498  							command:           []string{"init", "-ignore-remote-version"},
   499  							expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   500  							userInput:         []string{"yes", "yes"},
   501  							postInputOutput: []string{
   502  								`Should Terraform migrate your existing state?`,
   503  								`Terraform Cloud has been successfully initialized!`},
   504  						},
   505  						{
   506  							command:           []string{"workspace", "show"},
   507  							expectedCmdOutput: `remote-workspace`,
   508  						},
   509  					},
   510  				},
   511  			},
   512  			validations: func(t *testing.T, orgName string) {
   513  				expectedName := "remote-workspace"
   514  				ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   515  				if err != nil {
   516  					t.Fatal(err)
   517  				}
   518  				if ws == nil {
   519  					t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   520  				}
   521  			},
   522  		},
   523  	}
   524  
   525  	testRunner(t, cases, 2)
   526  }