github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/cloud/e2e/migrate_state_multi_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  	tfversion "github.com/terramate-io/tf/version"
    12  )
    13  
    14  func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
    15  	t.Parallel()
    16  	skipIfMissingEnvVar(t)
    17  	skipWithoutRemoteTerraformVersion(t)
    18  
    19  	ctx := context.Background()
    20  
    21  	cases := testCases{
    22  		"migrating multiple workspaces to cloud using name strategy; current workspace is 'default'": {
    23  			operations: []operationSets{
    24  				{
    25  					prep: func(t *testing.T, orgName, dir string) {
    26  						tfBlock := terraformConfigLocalBackend()
    27  						writeMainTF(t, tfBlock, dir)
    28  					},
    29  					commands: []tfCommand{
    30  						{
    31  							command:           []string{"init"},
    32  							expectedCmdOutput: `Successfully configured the backend "local"!`,
    33  						},
    34  						{
    35  							command:         []string{"apply", "-auto-approve"},
    36  							postInputOutput: []string{`Apply complete!`},
    37  						},
    38  						{
    39  							command:           []string{"workspace", "new", "prod"},
    40  							expectedCmdOutput: `Created and switched to workspace "prod"!`,
    41  						},
    42  						{
    43  							command:         []string{"apply", "-auto-approve"},
    44  							postInputOutput: []string{`Apply complete!`},
    45  						},
    46  						{
    47  							command:           []string{"workspace", "select", "default"},
    48  							expectedCmdOutput: `Switched to workspace "default".`,
    49  						},
    50  					},
    51  				},
    52  				{
    53  					prep: func(t *testing.T, orgName, dir string) {
    54  						wsName := "new-workspace"
    55  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
    56  						writeMainTF(t, tfBlock, dir)
    57  					},
    58  					commands: []tfCommand{
    59  						{
    60  							command:           []string{"init"},
    61  							expectedCmdOutput: `Do you want to copy only your current workspace?`,
    62  							userInput:         []string{"yes"},
    63  							postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
    64  						},
    65  						{
    66  							command:           []string{"workspace", "show"},
    67  							expectedCmdOutput: `new-workspace`, // this comes from the `prep` function
    68  						},
    69  						{
    70  							command:           []string{"output"},
    71  							expectedCmdOutput: `val = "default"`, // this was the output of the current workspace selected before migration
    72  						},
    73  					},
    74  				},
    75  			},
    76  			validations: func(t *testing.T, orgName string) {
    77  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
    78  				if err != nil {
    79  					t.Fatal(err)
    80  				}
    81  				if len(wsList.Items) != 1 {
    82  					t.Fatalf("Expected the number of workspaces to be 1, but got %d", len(wsList.Items))
    83  				}
    84  				ws := wsList.Items[0]
    85  				// this workspace name is what exists in the cloud backend configuration block
    86  				if ws.Name != "new-workspace" {
    87  					t.Fatalf("Expected workspace to be `new-workspace`, but is %s", ws.Name)
    88  				}
    89  			},
    90  		},
    91  		"migrating multiple workspaces to cloud using name strategy; current workspace is 'prod'": {
    92  			operations: []operationSets{
    93  				{
    94  					prep: func(t *testing.T, orgName, dir string) {
    95  						tfBlock := terraformConfigLocalBackend()
    96  						writeMainTF(t, tfBlock, dir)
    97  					},
    98  					commands: []tfCommand{
    99  						{
   100  							command:           []string{"init"},
   101  							expectedCmdOutput: `Successfully configured the backend "local"!`,
   102  						},
   103  						{
   104  							command:         []string{"apply", "-auto-approve"},
   105  							postInputOutput: []string{`Apply complete!`},
   106  						},
   107  						{
   108  							command:           []string{"workspace", "new", "prod"},
   109  							expectedCmdOutput: `Created and switched to workspace "prod"!`,
   110  						},
   111  						{
   112  							command:         []string{"apply", "-auto-approve"},
   113  							postInputOutput: []string{`Apply complete!`},
   114  						},
   115  					},
   116  				},
   117  				{
   118  					prep: func(t *testing.T, orgName, dir string) {
   119  						wsName := "new-workspace"
   120  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   121  						writeMainTF(t, tfBlock, dir)
   122  					},
   123  					commands: []tfCommand{
   124  						{
   125  							command:           []string{"init"},
   126  							expectedCmdOutput: `Do you want to copy only your current workspace?`,
   127  							userInput:         []string{"yes"},
   128  							postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
   129  						},
   130  						{
   131  							command:           []string{"workspace", "list"},
   132  							expectedCmdOutput: `new-workspace`, // this comes from the `prep` function
   133  						},
   134  						{
   135  							command:           []string{"output"},
   136  							expectedCmdOutput: `val = "prod"`,
   137  						},
   138  					},
   139  				},
   140  			},
   141  			validations: func(t *testing.T, orgName string) {
   142  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
   143  				if err != nil {
   144  					t.Fatal(err)
   145  				}
   146  				ws := wsList.Items[0]
   147  				// this workspace name is what exists in the cloud backend configuration block
   148  				if ws.Name != "new-workspace" {
   149  					t.Fatalf("Expected workspace to be `new-workspace`, but is %s", ws.Name)
   150  				}
   151  			},
   152  		},
   153  		"migrating multiple workspaces to cloud using name strategy; 'default' workspace is empty": {
   154  			operations: []operationSets{
   155  				{
   156  					prep: func(t *testing.T, orgName, dir string) {
   157  						tfBlock := terraformConfigLocalBackend()
   158  						writeMainTF(t, tfBlock, dir)
   159  					},
   160  					commands: []tfCommand{
   161  						{
   162  							command:           []string{"init"},
   163  							expectedCmdOutput: `Successfully configured the backend "local"!`,
   164  						},
   165  						{
   166  							command:           []string{"workspace", "new", "workspace1"},
   167  							expectedCmdOutput: `Created and switched to workspace "workspace1"!`,
   168  						},
   169  						{
   170  							command:         []string{"apply", "-auto-approve"},
   171  							postInputOutput: []string{`Apply complete!`},
   172  						},
   173  						{
   174  							command:           []string{"workspace", "new", "workspace2"},
   175  							expectedCmdOutput: `Created and switched to workspace "workspace2"!`,
   176  						},
   177  						{
   178  							command:         []string{"apply", "-auto-approve"},
   179  							postInputOutput: []string{`Apply complete!`},
   180  						},
   181  					},
   182  				},
   183  				{
   184  					prep: func(t *testing.T, orgName, dir string) {
   185  						wsName := "new-workspace"
   186  						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   187  						writeMainTF(t, tfBlock, dir)
   188  					},
   189  					commands: []tfCommand{
   190  						{
   191  							command:           []string{"init"},
   192  							expectedCmdOutput: `Do you want to copy only your current workspace?`,
   193  							userInput:         []string{"yes"},
   194  							postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
   195  						},
   196  						{
   197  							command:     []string{"workspace", "select", "default"},
   198  							expectError: true,
   199  						},
   200  						{
   201  							command:           []string{"output"},
   202  							expectedCmdOutput: `val = "workspace2"`, // this was the output of the current workspace selected before migration
   203  						},
   204  					},
   205  				},
   206  			},
   207  			validations: func(t *testing.T, orgName string) {
   208  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
   209  				if err != nil {
   210  					t.Fatal(err)
   211  				}
   212  				if len(wsList.Items) != 1 {
   213  					t.Fatalf("Expected the number of workspaces to be 1, but got %d", len(wsList.Items))
   214  				}
   215  				ws := wsList.Items[0]
   216  				// this workspace name is what exists in the cloud backend configuration block
   217  				if ws.Name != "new-workspace" {
   218  					t.Fatalf("Expected workspace to be `new-workspace`, but is %s", ws.Name)
   219  				}
   220  			},
   221  		},
   222  	}
   223  
   224  	testRunner(t, cases, 1)
   225  }
   226  
   227  func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) {
   228  	t.Parallel()
   229  	skipIfMissingEnvVar(t)
   230  	skipWithoutRemoteTerraformVersion(t)
   231  
   232  	ctx := context.Background()
   233  
   234  	cases := map[string]struct {
   235  		operations  []operationSets
   236  		validations func(t *testing.T, orgName string)
   237  	}{
   238  		"migrating multiple workspaces to cloud using tags strategy; pattern is using prefix `app-*`": {
   239  			operations: []operationSets{
   240  				{
   241  					prep: func(t *testing.T, orgName, dir string) {
   242  						tfBlock := terraformConfigLocalBackend()
   243  						writeMainTF(t, tfBlock, dir)
   244  					},
   245  					commands: []tfCommand{
   246  						{
   247  							command:           []string{"init"},
   248  							expectedCmdOutput: `Successfully configured the backend "local"!`,
   249  						},
   250  						{
   251  							command:         []string{"apply", "-auto-approve"},
   252  							postInputOutput: []string{`Apply complete!`},
   253  						},
   254  						{
   255  							command:           []string{"workspace", "new", "prod"},
   256  							expectedCmdOutput: `Created and switched to workspace "prod"!`,
   257  						},
   258  						{
   259  							command:         []string{"apply", "-auto-approve"},
   260  							postInputOutput: []string{`Apply complete!`},
   261  						},
   262  						{
   263  							command:           []string{"workspace", "select", "default"},
   264  							expectedCmdOutput: `Switched to workspace "default".`,
   265  						},
   266  						{
   267  							command:           []string{"output"},
   268  							expectedCmdOutput: `val = "default"`,
   269  						},
   270  						{
   271  							command:           []string{"workspace", "select", "prod"},
   272  							expectedCmdOutput: `Switched to workspace "prod".`,
   273  						},
   274  						{
   275  							command:           []string{"output"},
   276  							expectedCmdOutput: `val = "prod"`,
   277  						},
   278  					},
   279  				},
   280  				{
   281  					prep: func(t *testing.T, orgName, dir string) {
   282  						tag := "app"
   283  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   284  						writeMainTF(t, tfBlock, dir)
   285  					},
   286  					commands: []tfCommand{
   287  						{
   288  							command:           []string{"init"},
   289  							expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`,
   290  							userInput:         []string{"dev", "1", "app-*"},
   291  							postInputOutput: []string{
   292  								`Would you like to rename your workspaces?`,
   293  								"How would you like to rename your workspaces?",
   294  								"Terraform Cloud has been successfully initialized!"},
   295  						},
   296  						{
   297  							command:           []string{"workspace", "select", "app-dev"},
   298  							expectedCmdOutput: `Switched to workspace "app-dev".`,
   299  						},
   300  						{
   301  							command:           []string{"output"},
   302  							expectedCmdOutput: `val = "default"`,
   303  						},
   304  						{
   305  							command:           []string{"workspace", "select", "app-prod"},
   306  							expectedCmdOutput: `Switched to workspace "app-prod".`,
   307  						},
   308  						{
   309  							command:           []string{"output"},
   310  							expectedCmdOutput: `val = "prod"`,
   311  						},
   312  					},
   313  				},
   314  			},
   315  			validations: func(t *testing.T, orgName string) {
   316  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
   317  					Tags: "app",
   318  				})
   319  				if err != nil {
   320  					t.Fatal(err)
   321  				}
   322  				if len(wsList.Items) != 2 {
   323  					t.Fatalf("Expected the number of workspaecs to be 2, but got %d", len(wsList.Items))
   324  				}
   325  				expectedWorkspaceNames := []string{"app-prod", "app-dev"}
   326  				for _, ws := range wsList.Items {
   327  					hasName := false
   328  					for _, expectedNames := range expectedWorkspaceNames {
   329  						if expectedNames == ws.Name {
   330  							hasName = true
   331  						}
   332  					}
   333  					if !hasName {
   334  						t.Fatalf("Worksapce %s is not in the expected list of workspaces", ws.Name)
   335  					}
   336  				}
   337  			},
   338  		},
   339  		"migrating multiple workspaces to cloud using tags strategy; existing workspaces": {
   340  			operations: []operationSets{
   341  				{
   342  					prep: func(t *testing.T, orgName, dir string) {
   343  						tfBlock := terraformConfigLocalBackend()
   344  						writeMainTF(t, tfBlock, dir)
   345  					},
   346  					commands: []tfCommand{
   347  						{
   348  							command:           []string{"init"},
   349  							expectedCmdOutput: `Successfully configured the backend "local"!`,
   350  						},
   351  						{
   352  							command:         []string{"apply", "-auto-approve"},
   353  							postInputOutput: []string{`Apply complete!`},
   354  						},
   355  						{
   356  							command:           []string{"workspace", "new", "identity"},
   357  							expectedCmdOutput: `Created and switched to workspace "identity"!`,
   358  						},
   359  						{
   360  							command:         []string{"apply", "-auto-approve"},
   361  							postInputOutput: []string{`Apply complete!`},
   362  						},
   363  						{
   364  							command:           []string{"workspace", "new", "billing"},
   365  							expectedCmdOutput: `Created and switched to workspace "billing"!`,
   366  						},
   367  						{
   368  							command:         []string{"apply", "-auto-approve"},
   369  							postInputOutput: []string{`Apply complete!`},
   370  						},
   371  						{
   372  							command:           []string{"workspace", "select", "default"},
   373  							expectedCmdOutput: `Switched to workspace "default".`,
   374  						},
   375  					},
   376  				},
   377  				{
   378  					prep: func(t *testing.T, orgName, dir string) {
   379  						tag := "app"
   380  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   381  							Name:             tfe.String("identity"),
   382  							TerraformVersion: tfe.String(tfversion.String()),
   383  						})
   384  						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
   385  							Name:             tfe.String("billing"),
   386  							TerraformVersion: tfe.String(tfversion.String()),
   387  						})
   388  						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   389  						writeMainTF(t, tfBlock, dir)
   390  					},
   391  					commands: []tfCommand{
   392  						{
   393  							command:           []string{"init"},
   394  							expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`,
   395  							userInput:         []string{"dev", "1", "app-*"},
   396  							postInputOutput: []string{
   397  								`Would you like to rename your workspaces?`,
   398  								"How would you like to rename your workspaces?",
   399  								"Terraform Cloud has been successfully initialized!"},
   400  						},
   401  						{
   402  							command:           []string{"workspace", "select", "app-billing"},
   403  							expectedCmdOutput: `Switched to workspace "app-billing".`,
   404  						},
   405  						{
   406  							command:           []string{"workspace", "select", "app-identity"},
   407  							expectedCmdOutput: `Switched to workspace "app-identity".`,
   408  						},
   409  						{
   410  							command:           []string{"workspace", "select", "app-dev"},
   411  							expectedCmdOutput: `Switched to workspace "app-dev".`,
   412  						},
   413  					},
   414  				},
   415  			},
   416  			validations: func(t *testing.T, orgName string) {
   417  				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
   418  					Tags: "app",
   419  				})
   420  				if err != nil {
   421  					t.Fatal(err)
   422  				}
   423  				if len(wsList.Items) != 3 {
   424  					t.Fatalf("Expected the number of workspaecs to be 3, but got %d", len(wsList.Items))
   425  				}
   426  				expectedWorkspaceNames := []string{"app-billing", "app-dev", "app-identity"}
   427  				for _, ws := range wsList.Items {
   428  					hasName := false
   429  					for _, expectedNames := range expectedWorkspaceNames {
   430  						if expectedNames == ws.Name {
   431  							hasName = true
   432  						}
   433  					}
   434  					if !hasName {
   435  						t.Fatalf("Worksapce %s is not in the expected list of workspaces", ws.Name)
   436  					}
   437  				}
   438  			},
   439  		},
   440  	}
   441  
   442  	testRunner(t, cases, 1)
   443  }