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