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 }