kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/command/taint_test.go (about) 1 package command 2 3 import ( 4 "os" 5 "strings" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/mitchellh/cli" 10 11 "kubeform.dev/terraform-backend-sdk/addrs" 12 "kubeform.dev/terraform-backend-sdk/states" 13 ) 14 15 func TestTaint(t *testing.T) { 16 state := states.BuildState(func(s *states.SyncState) { 17 s.SetResourceInstanceCurrent( 18 addrs.Resource{ 19 Mode: addrs.ManagedResourceMode, 20 Type: "test_instance", 21 Name: "foo", 22 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 23 &states.ResourceInstanceObjectSrc{ 24 AttrsJSON: []byte(`{"id":"bar"}`), 25 Status: states.ObjectReady, 26 }, 27 addrs.AbsProviderConfig{ 28 Provider: addrs.NewDefaultProvider("test"), 29 Module: addrs.RootModule, 30 }, 31 ) 32 }) 33 statePath := testStateFile(t, state) 34 35 ui := new(cli.MockUi) 36 view, _ := testView(t) 37 c := &TaintCommand{ 38 Meta: Meta{ 39 Ui: ui, 40 View: view, 41 }, 42 } 43 44 args := []string{ 45 "-state", statePath, 46 "test_instance.foo", 47 } 48 if code := c.Run(args); code != 0 { 49 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 50 } 51 52 testStateOutput(t, statePath, testTaintStr) 53 } 54 55 func TestTaint_lockedState(t *testing.T) { 56 state := states.BuildState(func(s *states.SyncState) { 57 s.SetResourceInstanceCurrent( 58 addrs.Resource{ 59 Mode: addrs.ManagedResourceMode, 60 Type: "test_instance", 61 Name: "foo", 62 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 63 &states.ResourceInstanceObjectSrc{ 64 AttrsJSON: []byte(`{"id":"bar"}`), 65 Status: states.ObjectReady, 66 }, 67 addrs.AbsProviderConfig{ 68 Provider: addrs.NewDefaultProvider("test"), 69 Module: addrs.RootModule, 70 }, 71 ) 72 }) 73 statePath := testStateFile(t, state) 74 75 unlock, err := testLockState(testDataDir, statePath) 76 if err != nil { 77 t.Fatal(err) 78 } 79 defer unlock() 80 ui := new(cli.MockUi) 81 view, _ := testView(t) 82 c := &TaintCommand{ 83 Meta: Meta{ 84 Ui: ui, 85 View: view, 86 }, 87 } 88 89 args := []string{ 90 "-state", statePath, 91 "test_instance.foo", 92 } 93 if code := c.Run(args); code == 0 { 94 t.Fatal("expected error") 95 } 96 97 output := ui.ErrorWriter.String() 98 if !strings.Contains(output, "lock") { 99 t.Fatal("command output does not look like a lock error:", output) 100 } 101 } 102 103 func TestTaint_backup(t *testing.T) { 104 // Get a temp cwd 105 tmp, cwd := testCwd(t) 106 defer testFixCwd(t, tmp, cwd) 107 108 // Write the temp state 109 state := states.BuildState(func(s *states.SyncState) { 110 s.SetResourceInstanceCurrent( 111 addrs.Resource{ 112 Mode: addrs.ManagedResourceMode, 113 Type: "test_instance", 114 Name: "foo", 115 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 116 &states.ResourceInstanceObjectSrc{ 117 AttrsJSON: []byte(`{"id":"bar"}`), 118 Status: states.ObjectReady, 119 }, 120 addrs.AbsProviderConfig{ 121 Provider: addrs.NewDefaultProvider("test"), 122 Module: addrs.RootModule, 123 }, 124 ) 125 }) 126 testStateFileDefault(t, state) 127 128 ui := new(cli.MockUi) 129 view, _ := testView(t) 130 c := &TaintCommand{ 131 Meta: Meta{ 132 Ui: ui, 133 View: view, 134 }, 135 } 136 137 args := []string{ 138 "test_instance.foo", 139 } 140 if code := c.Run(args); code != 0 { 141 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 142 } 143 144 testStateOutput(t, DefaultStateFilename+".backup", testTaintDefaultStr) 145 testStateOutput(t, DefaultStateFilename, testTaintStr) 146 } 147 148 func TestTaint_backupDisable(t *testing.T) { 149 // Get a temp cwd 150 tmp, cwd := testCwd(t) 151 defer testFixCwd(t, tmp, cwd) 152 153 // Write the temp state 154 state := states.BuildState(func(s *states.SyncState) { 155 s.SetResourceInstanceCurrent( 156 addrs.Resource{ 157 Mode: addrs.ManagedResourceMode, 158 Type: "test_instance", 159 Name: "foo", 160 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 161 &states.ResourceInstanceObjectSrc{ 162 AttrsJSON: []byte(`{"id":"bar"}`), 163 Status: states.ObjectReady, 164 }, 165 addrs.AbsProviderConfig{ 166 Provider: addrs.NewDefaultProvider("test"), 167 Module: addrs.RootModule, 168 }, 169 ) 170 }) 171 testStateFileDefault(t, state) 172 173 ui := new(cli.MockUi) 174 view, _ := testView(t) 175 c := &TaintCommand{ 176 Meta: Meta{ 177 Ui: ui, 178 View: view, 179 }, 180 } 181 182 args := []string{ 183 "-backup", "-", 184 "test_instance.foo", 185 } 186 if code := c.Run(args); code != 0 { 187 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 188 } 189 190 if _, err := os.Stat(DefaultStateFilename + ".backup"); err == nil { 191 t.Fatal("backup path should not exist") 192 } 193 194 testStateOutput(t, DefaultStateFilename, testTaintStr) 195 } 196 197 func TestTaint_badState(t *testing.T) { 198 ui := new(cli.MockUi) 199 view, _ := testView(t) 200 c := &TaintCommand{ 201 Meta: Meta{ 202 Ui: ui, 203 View: view, 204 }, 205 } 206 207 args := []string{ 208 "-state", "i-should-not-exist-ever", 209 "foo", 210 } 211 if code := c.Run(args); code != 1 { 212 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 213 } 214 } 215 216 func TestTaint_defaultState(t *testing.T) { 217 // Get a temp cwd 218 tmp, cwd := testCwd(t) 219 defer testFixCwd(t, tmp, cwd) 220 221 // Write the temp state 222 state := states.BuildState(func(s *states.SyncState) { 223 s.SetResourceInstanceCurrent( 224 addrs.Resource{ 225 Mode: addrs.ManagedResourceMode, 226 Type: "test_instance", 227 Name: "foo", 228 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 229 &states.ResourceInstanceObjectSrc{ 230 AttrsJSON: []byte(`{"id":"bar"}`), 231 Status: states.ObjectReady, 232 }, 233 addrs.AbsProviderConfig{ 234 Provider: addrs.NewDefaultProvider("test"), 235 Module: addrs.RootModule, 236 }, 237 ) 238 }) 239 testStateFileDefault(t, state) 240 241 ui := new(cli.MockUi) 242 view, _ := testView(t) 243 c := &TaintCommand{ 244 Meta: Meta{ 245 Ui: ui, 246 View: view, 247 }, 248 } 249 250 args := []string{ 251 "test_instance.foo", 252 } 253 if code := c.Run(args); code != 0 { 254 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 255 } 256 257 testStateOutput(t, DefaultStateFilename, testTaintStr) 258 } 259 260 func TestTaint_defaultWorkspaceState(t *testing.T) { 261 // Get a temp cwd 262 tmp, cwd := testCwd(t) 263 defer testFixCwd(t, tmp, cwd) 264 265 state := states.BuildState(func(s *states.SyncState) { 266 s.SetResourceInstanceCurrent( 267 addrs.Resource{ 268 Mode: addrs.ManagedResourceMode, 269 Type: "test_instance", 270 Name: "foo", 271 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 272 &states.ResourceInstanceObjectSrc{ 273 AttrsJSON: []byte(`{"id":"bar"}`), 274 Status: states.ObjectReady, 275 }, 276 addrs.AbsProviderConfig{ 277 Provider: addrs.NewDefaultProvider("test"), 278 Module: addrs.RootModule, 279 }, 280 ) 281 }) 282 testWorkspace := "development" 283 path := testStateFileWorkspaceDefault(t, testWorkspace, state) 284 285 ui := new(cli.MockUi) 286 view, _ := testView(t) 287 meta := Meta{Ui: ui, View: view} 288 meta.SetWorkspace(testWorkspace) 289 c := &TaintCommand{ 290 Meta: meta, 291 } 292 293 args := []string{ 294 "test_instance.foo", 295 } 296 if code := c.Run(args); code != 0 { 297 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 298 } 299 300 testStateOutput(t, path, testTaintStr) 301 } 302 303 func TestTaint_missing(t *testing.T) { 304 state := states.BuildState(func(s *states.SyncState) { 305 s.SetResourceInstanceCurrent( 306 addrs.Resource{ 307 Mode: addrs.ManagedResourceMode, 308 Type: "test_instance", 309 Name: "foo", 310 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 311 &states.ResourceInstanceObjectSrc{ 312 AttrsJSON: []byte(`{"id":"bar"}`), 313 Status: states.ObjectReady, 314 }, 315 addrs.AbsProviderConfig{ 316 Provider: addrs.NewDefaultProvider("test"), 317 Module: addrs.RootModule, 318 }, 319 ) 320 }) 321 statePath := testStateFile(t, state) 322 323 ui := new(cli.MockUi) 324 view, _ := testView(t) 325 c := &TaintCommand{ 326 Meta: Meta{ 327 Ui: ui, 328 View: view, 329 }, 330 } 331 332 args := []string{ 333 "-state", statePath, 334 "test_instance.bar", 335 } 336 if code := c.Run(args); code == 0 { 337 t.Fatalf("bad: %d\n\n%s", code, ui.OutputWriter.String()) 338 } 339 } 340 341 func TestTaint_missingAllow(t *testing.T) { 342 state := states.BuildState(func(s *states.SyncState) { 343 s.SetResourceInstanceCurrent( 344 addrs.Resource{ 345 Mode: addrs.ManagedResourceMode, 346 Type: "test_instance", 347 Name: "foo", 348 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 349 &states.ResourceInstanceObjectSrc{ 350 AttrsJSON: []byte(`{"id":"bar"}`), 351 Status: states.ObjectReady, 352 }, 353 addrs.AbsProviderConfig{ 354 Provider: addrs.NewDefaultProvider("test"), 355 Module: addrs.RootModule, 356 }, 357 ) 358 }) 359 statePath := testStateFile(t, state) 360 361 ui := new(cli.MockUi) 362 view, _ := testView(t) 363 c := &TaintCommand{ 364 Meta: Meta{ 365 Ui: ui, 366 View: view, 367 }, 368 } 369 370 args := []string{ 371 "-allow-missing", 372 "-state", statePath, 373 "test_instance.bar", 374 } 375 if code := c.Run(args); code != 0 { 376 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 377 } 378 379 // Check for the warning 380 actual := strings.TrimSpace(ui.ErrorWriter.String()) 381 expected := strings.TrimSpace(` 382 Warning: No such resource instance 383 384 Resource instance test_instance.bar was not found, but this is not an error 385 because -allow-missing was set. 386 387 `) 388 if diff := cmp.Diff(expected, actual); diff != "" { 389 t.Fatalf("wrong output\n%s", diff) 390 } 391 } 392 393 func TestTaint_stateOut(t *testing.T) { 394 // Get a temp cwd 395 tmp, cwd := testCwd(t) 396 defer testFixCwd(t, tmp, cwd) 397 398 // Write the temp state 399 state := states.BuildState(func(s *states.SyncState) { 400 s.SetResourceInstanceCurrent( 401 addrs.Resource{ 402 Mode: addrs.ManagedResourceMode, 403 Type: "test_instance", 404 Name: "foo", 405 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 406 &states.ResourceInstanceObjectSrc{ 407 AttrsJSON: []byte(`{"id":"bar"}`), 408 Status: states.ObjectReady, 409 }, 410 addrs.AbsProviderConfig{ 411 Provider: addrs.NewDefaultProvider("test"), 412 Module: addrs.RootModule, 413 }, 414 ) 415 }) 416 testStateFileDefault(t, state) 417 418 ui := new(cli.MockUi) 419 view, _ := testView(t) 420 c := &TaintCommand{ 421 Meta: Meta{ 422 Ui: ui, 423 View: view, 424 }, 425 } 426 427 args := []string{ 428 "-state-out", "foo", 429 "test_instance.foo", 430 } 431 if code := c.Run(args); code != 0 { 432 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 433 } 434 435 testStateOutput(t, DefaultStateFilename, testTaintDefaultStr) 436 testStateOutput(t, "foo", testTaintStr) 437 } 438 439 func TestTaint_module(t *testing.T) { 440 state := states.BuildState(func(s *states.SyncState) { 441 s.SetResourceInstanceCurrent( 442 addrs.Resource{ 443 Mode: addrs.ManagedResourceMode, 444 Type: "test_instance", 445 Name: "foo", 446 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 447 &states.ResourceInstanceObjectSrc{ 448 AttrsJSON: []byte(`{"id":"bar"}`), 449 Status: states.ObjectReady, 450 }, 451 addrs.AbsProviderConfig{ 452 Provider: addrs.NewDefaultProvider("test"), 453 Module: addrs.RootModule, 454 }, 455 ) 456 s.SetResourceInstanceCurrent( 457 addrs.Resource{ 458 Mode: addrs.ManagedResourceMode, 459 Type: "test_instance", 460 Name: "blah", 461 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey)), 462 &states.ResourceInstanceObjectSrc{ 463 AttrsJSON: []byte(`{"id":"blah"}`), 464 Status: states.ObjectReady, 465 }, 466 addrs.AbsProviderConfig{ 467 Provider: addrs.NewDefaultProvider("test"), 468 Module: addrs.RootModule, 469 }, 470 ) 471 }) 472 statePath := testStateFile(t, state) 473 474 ui := new(cli.MockUi) 475 view, _ := testView(t) 476 c := &TaintCommand{ 477 Meta: Meta{ 478 Ui: ui, 479 View: view, 480 }, 481 } 482 483 args := []string{ 484 "-state", statePath, 485 "module.child.test_instance.blah", 486 } 487 if code := c.Run(args); code != 0 { 488 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 489 } 490 491 testStateOutput(t, statePath, testTaintModuleStr) 492 } 493 494 func TestTaint_checkRequiredVersion(t *testing.T) { 495 // Create a temporary working directory that is empty 496 td := tempDir(t) 497 testCopyDir(t, testFixturePath("taint-check-required-version"), td) 498 defer os.RemoveAll(td) 499 defer testChdir(t, td)() 500 501 // Write the temp state 502 state := states.BuildState(func(s *states.SyncState) { 503 s.SetResourceInstanceCurrent( 504 addrs.Resource{ 505 Mode: addrs.ManagedResourceMode, 506 Type: "test_instance", 507 Name: "foo", 508 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 509 &states.ResourceInstanceObjectSrc{ 510 AttrsJSON: []byte(`{"id":"bar"}`), 511 Status: states.ObjectReady, 512 }, 513 addrs.AbsProviderConfig{ 514 Provider: addrs.NewDefaultProvider("test"), 515 Module: addrs.RootModule, 516 }, 517 ) 518 }) 519 path := testStateFile(t, state) 520 521 ui := cli.NewMockUi() 522 view, _ := testView(t) 523 c := &TaintCommand{ 524 Meta: Meta{ 525 testingOverrides: metaOverridesForProvider(testProvider()), 526 Ui: ui, 527 View: view, 528 }, 529 } 530 531 args := []string{"test_instance.foo"} 532 if code := c.Run(args); code != 1 { 533 t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String()) 534 } 535 536 // State is unchanged 537 testStateOutput(t, path, testTaintDefaultStr) 538 539 // Required version diags are correct 540 errStr := ui.ErrorWriter.String() 541 if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) { 542 t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr) 543 } 544 if strings.Contains(errStr, `required_version = ">= 0.13.0"`) { 545 t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr) 546 } 547 } 548 549 const testTaintStr = ` 550 test_instance.foo: (tainted) 551 ID = bar 552 provider = provider["registry.terraform.io/hashicorp/test"] 553 ` 554 555 const testTaintDefaultStr = ` 556 test_instance.foo: 557 ID = bar 558 provider = provider["registry.terraform.io/hashicorp/test"] 559 ` 560 561 const testTaintModuleStr = ` 562 test_instance.foo: 563 ID = bar 564 provider = provider["registry.terraform.io/hashicorp/test"] 565 566 module.child: 567 test_instance.blah: (tainted) 568 ID = blah 569 provider = provider["registry.terraform.io/hashicorp/test"] 570 `