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