github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/state_mv_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package command 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/google/go-cmp/cmp" 14 "github.com/mitchellh/cli" 15 16 "github.com/terramate-io/tf/addrs" 17 "github.com/terramate-io/tf/states" 18 ) 19 20 func TestStateMv(t *testing.T) { 21 state := states.BuildState(func(s *states.SyncState) { 22 s.SetResourceInstanceCurrent( 23 addrs.Resource{ 24 Mode: addrs.ManagedResourceMode, 25 Type: "test_instance", 26 Name: "foo", 27 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 28 &states.ResourceInstanceObjectSrc{ 29 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 30 Status: states.ObjectReady, 31 }, 32 addrs.AbsProviderConfig{ 33 Provider: addrs.NewDefaultProvider("test"), 34 Module: addrs.RootModule, 35 }, 36 ) 37 s.SetResourceInstanceCurrent( 38 addrs.Resource{ 39 Mode: addrs.ManagedResourceMode, 40 Type: "test_instance", 41 Name: "baz", 42 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 43 &states.ResourceInstanceObjectSrc{ 44 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 45 Status: states.ObjectReady, 46 Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")}, 47 }, 48 addrs.AbsProviderConfig{ 49 Provider: addrs.NewDefaultProvider("test"), 50 Module: addrs.RootModule, 51 }, 52 ) 53 }) 54 statePath := testStateFile(t, state) 55 56 p := testProvider() 57 ui := new(cli.MockUi) 58 view, _ := testView(t) 59 c := &StateMvCommand{ 60 StateMeta{ 61 Meta: Meta{ 62 testingOverrides: metaOverridesForProvider(p), 63 Ui: ui, 64 View: view, 65 }, 66 }, 67 } 68 69 args := []string{ 70 "-state", statePath, 71 "test_instance.foo", 72 "test_instance.bar", 73 } 74 if code := c.Run(args); code != 0 { 75 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 76 } 77 78 // Test it is correct 79 testStateOutput(t, statePath, testStateMvOutput) 80 81 // Test we have backups 82 backups := testStateBackups(t, filepath.Dir(statePath)) 83 if len(backups) != 1 { 84 t.Fatalf("bad: %#v", backups) 85 } 86 testStateOutput(t, backups[0], testStateMvOutputOriginal) 87 88 // Change the single instance to a counted instance 89 args = []string{ 90 "-state", statePath, 91 "test_instance.bar", 92 "test_instance.bar[0]", 93 } 94 if code := c.Run(args); code != 0 { 95 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 96 } 97 98 // extract the resource and verify the mode 99 s := testStateRead(t, statePath) 100 addr, diags := addrs.ParseAbsResourceStr("test_instance.bar") 101 if diags.HasErrors() { 102 t.Fatal(diags.Err()) 103 } 104 for key := range s.Resource(addr).Instances { 105 if _, ok := key.(addrs.IntKey); !ok { 106 t.Fatalf("expected each mode List, got key %q", key) 107 } 108 } 109 110 // change from list to map 111 args = []string{ 112 "-state", statePath, 113 "test_instance.bar[0]", 114 "test_instance.bar[\"baz\"]", 115 } 116 if code := c.Run(args); code != 0 { 117 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 118 } 119 120 // extract the resource and verify the mode 121 s = testStateRead(t, statePath) 122 addr, diags = addrs.ParseAbsResourceStr("test_instance.bar") 123 if diags.HasErrors() { 124 t.Fatal(diags.Err()) 125 } 126 for key := range s.Resource(addr).Instances { 127 if _, ok := key.(addrs.StringKey); !ok { 128 t.Fatalf("expected each mode map, found key %q", key) 129 } 130 } 131 132 // change from from map back to single 133 args = []string{ 134 "-state", statePath, 135 "test_instance.bar[\"baz\"]", 136 "test_instance.bar", 137 } 138 if code := c.Run(args); code != 0 { 139 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 140 } 141 142 // extract the resource and verify the mode 143 s = testStateRead(t, statePath) 144 addr, diags = addrs.ParseAbsResourceStr("test_instance.bar") 145 if diags.HasErrors() { 146 t.Fatal(diags.Err()) 147 } 148 for key := range s.Resource(addr).Instances { 149 if key != addrs.NoKey { 150 t.Fatalf("expected no each mode, found key %q", key) 151 } 152 } 153 154 } 155 156 func TestStateMv_backupAndBackupOutOptionsWithNonLocalBackend(t *testing.T) { 157 state := states.BuildState(func(s *states.SyncState) { 158 s.SetResourceInstanceCurrent( 159 addrs.Resource{ 160 Mode: addrs.ManagedResourceMode, 161 Type: "test_instance", 162 Name: "foo", 163 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 164 &states.ResourceInstanceObjectSrc{ 165 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 166 Status: states.ObjectReady, 167 }, 168 addrs.AbsProviderConfig{ 169 Provider: addrs.NewDefaultProvider("test"), 170 Module: addrs.RootModule, 171 }, 172 ) 173 }) 174 175 t.Run("backup option specified", func(t *testing.T) { 176 td := t.TempDir() 177 testCopyDir(t, testFixturePath("init-backend-http"), td) 178 defer testChdir(t, td)() 179 180 backupPath := filepath.Join(td, "backup") 181 182 // Set up our backend state using mock state 183 dataState, srv := testBackendState(t, state, 200) 184 defer srv.Close() 185 testStateFileRemote(t, dataState) 186 187 p := testProvider() 188 ui := new(cli.MockUi) 189 view, _ := testView(t) 190 c := &StateMvCommand{ 191 StateMeta{ 192 Meta: Meta{ 193 testingOverrides: metaOverridesForProvider(p), 194 Ui: ui, 195 View: view, 196 }, 197 }, 198 } 199 200 args := []string{ 201 "-backup", backupPath, 202 "test_instance.foo", 203 "test_instance.bar", 204 } 205 if code := c.Run(args); code == 0 { 206 t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) 207 } 208 209 gotErr := ui.ErrorWriter.String() 210 wantErr := ` 211 Error: Invalid command line options: -backup 212 213 Command line options -backup and -backup-out are legacy options that operate 214 on a local state file only. You must specify a local state file with the 215 -state option or switch to the local backend. 216 217 ` 218 if gotErr != wantErr { 219 t.Fatalf("expected error\ngot:%s\n\nwant:%s", gotErr, wantErr) 220 } 221 }) 222 223 t.Run("backup-out option specified", func(t *testing.T) { 224 td := t.TempDir() 225 testCopyDir(t, testFixturePath("init-backend-http"), td) 226 defer testChdir(t, td)() 227 228 backupOutPath := filepath.Join(td, "backup-out") 229 230 // Set up our backend state using mock state 231 dataState, srv := testBackendState(t, state, 200) 232 defer srv.Close() 233 testStateFileRemote(t, dataState) 234 235 p := testProvider() 236 ui := new(cli.MockUi) 237 view, _ := testView(t) 238 c := &StateMvCommand{ 239 StateMeta{ 240 Meta: Meta{ 241 testingOverrides: metaOverridesForProvider(p), 242 Ui: ui, 243 View: view, 244 }, 245 }, 246 } 247 248 args := []string{ 249 "-backup-out", backupOutPath, 250 "test_instance.foo", 251 "test_instance.bar", 252 } 253 if code := c.Run(args); code == 0 { 254 t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) 255 } 256 257 gotErr := ui.ErrorWriter.String() 258 wantErr := ` 259 Error: Invalid command line options: -backup-out 260 261 Command line options -backup and -backup-out are legacy options that operate 262 on a local state file only. You must specify a local state file with the 263 -state option or switch to the local backend. 264 265 ` 266 if gotErr != wantErr { 267 t.Fatalf("expected error\ngot:%s\n\nwant:%s", gotErr, wantErr) 268 } 269 }) 270 271 t.Run("backup and backup-out options specified", func(t *testing.T) { 272 td := t.TempDir() 273 testCopyDir(t, testFixturePath("init-backend-http"), td) 274 defer testChdir(t, td)() 275 276 backupPath := filepath.Join(td, "backup") 277 backupOutPath := filepath.Join(td, "backup-out") 278 279 // Set up our backend state using mock state 280 dataState, srv := testBackendState(t, state, 200) 281 defer srv.Close() 282 testStateFileRemote(t, dataState) 283 284 p := testProvider() 285 ui := new(cli.MockUi) 286 view, _ := testView(t) 287 c := &StateMvCommand{ 288 StateMeta{ 289 Meta: Meta{ 290 testingOverrides: metaOverridesForProvider(p), 291 Ui: ui, 292 View: view, 293 }, 294 }, 295 } 296 297 args := []string{ 298 "-backup", backupPath, 299 "-backup-out", backupOutPath, 300 "test_instance.foo", 301 "test_instance.bar", 302 } 303 if code := c.Run(args); code == 0 { 304 t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) 305 } 306 307 gotErr := ui.ErrorWriter.String() 308 wantErr := ` 309 Error: Invalid command line options: -backup, -backup-out 310 311 Command line options -backup and -backup-out are legacy options that operate 312 on a local state file only. You must specify a local state file with the 313 -state option or switch to the local backend. 314 315 ` 316 if gotErr != wantErr { 317 t.Fatalf("expected error\ngot:%s\n\nwant:%s", gotErr, wantErr) 318 } 319 }) 320 321 t.Run("backup option specified with state option", func(t *testing.T) { 322 td := t.TempDir() 323 testCopyDir(t, testFixturePath("init-backend-http"), td) 324 defer testChdir(t, td)() 325 326 statePath := testStateFile(t, state) 327 backupPath := filepath.Join(td, "backup") 328 329 // Set up our backend state using mock state 330 dataState, srv := testBackendState(t, state, 200) 331 defer srv.Close() 332 testStateFileRemote(t, dataState) 333 334 p := testProvider() 335 ui := new(cli.MockUi) 336 view, _ := testView(t) 337 c := &StateMvCommand{ 338 StateMeta{ 339 Meta: Meta{ 340 testingOverrides: metaOverridesForProvider(p), 341 Ui: ui, 342 View: view, 343 }, 344 }, 345 } 346 347 args := []string{ 348 "-state", statePath, 349 "-backup", backupPath, 350 "test_instance.foo", 351 "test_instance.bar", 352 } 353 if code := c.Run(args); code != 0 { 354 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 355 } 356 357 // Test it is correct 358 testStateOutput(t, statePath, testStateMvBackupAndBackupOutOptionsWithNonLocalBackendOutput) 359 }) 360 361 t.Run("backup-out option specified with state option", func(t *testing.T) { 362 td := t.TempDir() 363 testCopyDir(t, testFixturePath("init-backend-http"), td) 364 defer testChdir(t, td)() 365 366 statePath := testStateFile(t, state) 367 backupOutPath := filepath.Join(td, "backup-out") 368 369 // Set up our backend state using mock state 370 dataState, srv := testBackendState(t, state, 200) 371 defer srv.Close() 372 testStateFileRemote(t, dataState) 373 374 p := testProvider() 375 ui := new(cli.MockUi) 376 view, _ := testView(t) 377 c := &StateMvCommand{ 378 StateMeta{ 379 Meta: Meta{ 380 testingOverrides: metaOverridesForProvider(p), 381 Ui: ui, 382 View: view, 383 }, 384 }, 385 } 386 387 args := []string{ 388 "-state", statePath, 389 "-backup-out", backupOutPath, 390 "test_instance.foo", 391 "test_instance.bar", 392 } 393 if code := c.Run(args); code != 0 { 394 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 395 } 396 397 // Test it is correct 398 testStateOutput(t, statePath, testStateMvBackupAndBackupOutOptionsWithNonLocalBackendOutput) 399 }) 400 } 401 402 func TestStateMv_resourceToInstance(t *testing.T) { 403 // A single resource (no count defined) 404 state := states.BuildState(func(s *states.SyncState) { 405 s.SetResourceInstanceCurrent( 406 addrs.Resource{ 407 Mode: addrs.ManagedResourceMode, 408 Type: "test_instance", 409 Name: "foo", 410 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 411 &states.ResourceInstanceObjectSrc{ 412 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 413 Status: states.ObjectReady, 414 }, 415 addrs.AbsProviderConfig{ 416 Provider: addrs.NewDefaultProvider("test"), 417 Module: addrs.RootModule, 418 }, 419 ) 420 s.SetResourceInstanceCurrent( 421 addrs.Resource{ 422 Mode: addrs.ManagedResourceMode, 423 Type: "test_instance", 424 Name: "baz", 425 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 426 &states.ResourceInstanceObjectSrc{ 427 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 428 Status: states.ObjectReady, 429 Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")}, 430 }, 431 addrs.AbsProviderConfig{ 432 Provider: addrs.NewDefaultProvider("test"), 433 Module: addrs.RootModule, 434 }, 435 ) 436 s.SetResourceProvider( 437 addrs.Resource{ 438 Mode: addrs.ManagedResourceMode, 439 Type: "test_instance", 440 Name: "bar", 441 }.Absolute(addrs.RootModuleInstance), 442 addrs.AbsProviderConfig{ 443 Provider: addrs.NewDefaultProvider("test"), 444 Module: addrs.RootModule, 445 }, 446 ) 447 }) 448 statePath := testStateFile(t, state) 449 450 p := testProvider() 451 ui := new(cli.MockUi) 452 view, _ := testView(t) 453 c := &StateMvCommand{ 454 StateMeta{ 455 Meta: Meta{ 456 testingOverrides: metaOverridesForProvider(p), 457 Ui: ui, 458 View: view, 459 }, 460 }, 461 } 462 463 args := []string{ 464 "-state", statePath, 465 "test_instance.foo", 466 "test_instance.bar[0]", 467 } 468 if code := c.Run(args); code != 0 { 469 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 470 } 471 472 // Test it is correct 473 testStateOutput(t, statePath, ` 474 test_instance.bar.0: 475 ID = bar 476 provider = provider["registry.terraform.io/hashicorp/test"] 477 bar = value 478 foo = value 479 test_instance.baz: 480 ID = foo 481 provider = provider["registry.terraform.io/hashicorp/test"] 482 bar = value 483 foo = value 484 `) 485 486 // Test we have backups 487 backups := testStateBackups(t, filepath.Dir(statePath)) 488 if len(backups) != 1 { 489 t.Fatalf("bad: %#v", backups) 490 } 491 testStateOutput(t, backups[0], testStateMvOutputOriginal) 492 } 493 494 func TestStateMv_resourceToInstanceErr(t *testing.T) { 495 state := states.BuildState(func(s *states.SyncState) { 496 s.SetResourceInstanceCurrent( 497 addrs.Resource{ 498 Mode: addrs.ManagedResourceMode, 499 Type: "test_instance", 500 Name: "foo", 501 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 502 &states.ResourceInstanceObjectSrc{ 503 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 504 Status: states.ObjectReady, 505 }, 506 addrs.AbsProviderConfig{ 507 Provider: addrs.NewDefaultProvider("test"), 508 Module: addrs.RootModule, 509 }, 510 ) 511 s.SetResourceProvider( 512 addrs.Resource{ 513 Mode: addrs.ManagedResourceMode, 514 Type: "test_instance", 515 Name: "bar", 516 }.Absolute(addrs.RootModuleInstance), 517 addrs.AbsProviderConfig{ 518 Provider: addrs.NewDefaultProvider("test"), 519 Module: addrs.RootModule, 520 }, 521 ) 522 }) 523 statePath := testStateFile(t, state) 524 525 p := testProvider() 526 ui := cli.NewMockUi() 527 view, _ := testView(t) 528 529 c := &StateMvCommand{ 530 StateMeta{ 531 Meta: Meta{ 532 testingOverrides: metaOverridesForProvider(p), 533 Ui: ui, 534 View: view, 535 }, 536 }, 537 } 538 539 args := []string{ 540 "-state", statePath, 541 "test_instance.foo", 542 "test_instance.bar[0]", 543 } 544 545 if code := c.Run(args); code == 0 { 546 t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) 547 } 548 549 expectedErr := ` 550 Error: Invalid target address 551 552 Cannot move test_instance.foo to test_instance.bar[0]: the source is a whole 553 resource (not a resource instance) so the target must also be a whole 554 resource. 555 556 ` 557 errOutput := ui.ErrorWriter.String() 558 if errOutput != expectedErr { 559 t.Errorf("wrong output\n%s", cmp.Diff(errOutput, expectedErr)) 560 } 561 } 562 563 func TestStateMv_resourceToInstanceErrInAutomation(t *testing.T) { 564 state := states.BuildState(func(s *states.SyncState) { 565 s.SetResourceInstanceCurrent( 566 addrs.Resource{ 567 Mode: addrs.ManagedResourceMode, 568 Type: "test_instance", 569 Name: "foo", 570 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 571 &states.ResourceInstanceObjectSrc{ 572 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 573 Status: states.ObjectReady, 574 }, 575 addrs.AbsProviderConfig{ 576 Provider: addrs.NewDefaultProvider("test"), 577 Module: addrs.RootModule, 578 }, 579 ) 580 s.SetResourceProvider( 581 addrs.Resource{ 582 Mode: addrs.ManagedResourceMode, 583 Type: "test_instance", 584 Name: "bar", 585 }.Absolute(addrs.RootModuleInstance), 586 addrs.AbsProviderConfig{ 587 Provider: addrs.NewDefaultProvider("test"), 588 Module: addrs.RootModule, 589 }, 590 ) 591 }) 592 statePath := testStateFile(t, state) 593 594 p := testProvider() 595 ui := new(cli.MockUi) 596 view, _ := testView(t) 597 c := &StateMvCommand{ 598 StateMeta{ 599 Meta: Meta{ 600 testingOverrides: metaOverridesForProvider(p), 601 Ui: ui, 602 View: view, 603 RunningInAutomation: true, 604 }, 605 }, 606 } 607 608 args := []string{ 609 "-state", statePath, 610 "test_instance.foo", 611 "test_instance.bar[0]", 612 } 613 614 if code := c.Run(args); code == 0 { 615 t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) 616 } 617 618 expectedErr := ` 619 Error: Invalid target address 620 621 Cannot move test_instance.foo to test_instance.bar[0]: the source is a whole 622 resource (not a resource instance) so the target must also be a whole 623 resource. 624 625 ` 626 errOutput := ui.ErrorWriter.String() 627 if errOutput != expectedErr { 628 t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", errOutput, expectedErr) 629 t.Errorf("%s", cmp.Diff(errOutput, expectedErr)) 630 } 631 } 632 633 func TestStateMv_instanceToResource(t *testing.T) { 634 state := states.BuildState(func(s *states.SyncState) { 635 s.SetResourceInstanceCurrent( 636 addrs.Resource{ 637 Mode: addrs.ManagedResourceMode, 638 Type: "test_instance", 639 Name: "foo", 640 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 641 &states.ResourceInstanceObjectSrc{ 642 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 643 Status: states.ObjectReady, 644 }, 645 addrs.AbsProviderConfig{ 646 Provider: addrs.NewDefaultProvider("test"), 647 Module: addrs.RootModule, 648 }, 649 ) 650 s.SetResourceInstanceCurrent( 651 addrs.Resource{ 652 Mode: addrs.ManagedResourceMode, 653 Type: "test_instance", 654 Name: "baz", 655 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 656 &states.ResourceInstanceObjectSrc{ 657 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 658 Status: states.ObjectReady, 659 }, 660 addrs.AbsProviderConfig{ 661 Provider: addrs.NewDefaultProvider("test"), 662 Module: addrs.RootModule, 663 }, 664 ) 665 }) 666 statePath := testStateFile(t, state) 667 668 p := testProvider() 669 ui := new(cli.MockUi) 670 view, _ := testView(t) 671 c := &StateMvCommand{ 672 StateMeta{ 673 Meta: Meta{ 674 testingOverrides: metaOverridesForProvider(p), 675 Ui: ui, 676 View: view, 677 }, 678 }, 679 } 680 681 args := []string{ 682 "-state", statePath, 683 "test_instance.foo[0]", 684 "test_instance.bar", 685 } 686 if code := c.Run(args); code != 0 { 687 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 688 } 689 690 // Test it is correct 691 testStateOutput(t, statePath, ` 692 test_instance.bar: 693 ID = bar 694 provider = provider["registry.terraform.io/hashicorp/test"] 695 bar = value 696 foo = value 697 test_instance.baz: 698 ID = foo 699 provider = provider["registry.terraform.io/hashicorp/test"] 700 bar = value 701 foo = value 702 `) 703 704 // Test we have backups 705 backups := testStateBackups(t, filepath.Dir(statePath)) 706 if len(backups) != 1 { 707 t.Fatalf("bad: %#v", backups) 708 } 709 testStateOutput(t, backups[0], ` 710 test_instance.baz: 711 ID = foo 712 provider = provider["registry.terraform.io/hashicorp/test"] 713 bar = value 714 foo = value 715 test_instance.foo.0: 716 ID = bar 717 provider = provider["registry.terraform.io/hashicorp/test"] 718 bar = value 719 foo = value 720 `) 721 } 722 723 func TestStateMv_instanceToNewResource(t *testing.T) { 724 state := states.BuildState(func(s *states.SyncState) { 725 s.SetResourceInstanceCurrent( 726 addrs.Resource{ 727 Mode: addrs.ManagedResourceMode, 728 Type: "test_instance", 729 Name: "foo", 730 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 731 &states.ResourceInstanceObjectSrc{ 732 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 733 Status: states.ObjectReady, 734 }, 735 addrs.AbsProviderConfig{ 736 Provider: addrs.NewDefaultProvider("test"), 737 Module: addrs.RootModule, 738 }, 739 ) 740 }) 741 statePath := testStateFile(t, state) 742 743 p := testProvider() 744 ui := new(cli.MockUi) 745 view, _ := testView(t) 746 c := &StateMvCommand{ 747 StateMeta{ 748 Meta: Meta{ 749 testingOverrides: metaOverridesForProvider(p), 750 Ui: ui, 751 View: view, 752 }, 753 }, 754 } 755 756 args := []string{ 757 "-state", statePath, 758 "test_instance.foo[0]", 759 "test_instance.bar[\"new\"]", 760 } 761 if code := c.Run(args); code != 0 { 762 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 763 } 764 765 // Test it is correct 766 testStateOutput(t, statePath, ` 767 test_instance.bar["new"]: 768 ID = bar 769 provider = provider["registry.terraform.io/hashicorp/test"] 770 bar = value 771 foo = value 772 `) 773 774 // now move the instance to a new resource in a new module 775 args = []string{ 776 "-state", statePath, 777 "test_instance.bar[\"new\"]", 778 "module.test.test_instance.baz[\"new\"]", 779 } 780 if code := c.Run(args); code != 0 { 781 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 782 } 783 784 // Test it is correct 785 testStateOutput(t, statePath, ` 786 <no state> 787 module.test: 788 test_instance.baz["new"]: 789 ID = bar 790 provider = provider["registry.terraform.io/hashicorp/test"] 791 bar = value 792 foo = value 793 `) 794 } 795 796 func TestStateMv_differentResourceTypes(t *testing.T) { 797 state := states.BuildState(func(s *states.SyncState) { 798 s.SetResourceInstanceCurrent( 799 addrs.Resource{ 800 Mode: addrs.ManagedResourceMode, 801 Type: "test_instance", 802 Name: "foo", 803 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 804 &states.ResourceInstanceObjectSrc{ 805 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 806 Status: states.ObjectReady, 807 }, 808 addrs.AbsProviderConfig{ 809 Provider: addrs.NewDefaultProvider("test"), 810 Module: addrs.RootModule, 811 }, 812 ) 813 }) 814 statePath := testStateFile(t, state) 815 816 p := testProvider() 817 ui := new(cli.MockUi) 818 view, _ := testView(t) 819 c := &StateMvCommand{ 820 StateMeta{ 821 Meta: Meta{ 822 testingOverrides: metaOverridesForProvider(p), 823 Ui: ui, 824 View: view, 825 }, 826 }, 827 } 828 829 args := []string{ 830 "-state", statePath, 831 "test_instance.foo", 832 "test_network.bar", 833 } 834 if code := c.Run(args); code == 0 { 835 t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) 836 } 837 838 gotErr := ui.ErrorWriter.String() 839 wantErr := ` 840 Error: Invalid state move request 841 842 Cannot move test_instance.foo to test_network.bar: resource types don't 843 match. 844 845 ` 846 if gotErr != wantErr { 847 t.Fatalf("expected initialization error\ngot:\n%s\n\nwant:%s", gotErr, wantErr) 848 } 849 } 850 851 // don't modify backend state is we supply a -state flag 852 func TestStateMv_explicitWithBackend(t *testing.T) { 853 td := t.TempDir() 854 testCopyDir(t, testFixturePath("init-backend"), td) 855 defer testChdir(t, td)() 856 857 backupPath := filepath.Join(td, "backup") 858 859 state := states.BuildState(func(s *states.SyncState) { 860 s.SetResourceInstanceCurrent( 861 addrs.Resource{ 862 Mode: addrs.ManagedResourceMode, 863 Type: "test_instance", 864 Name: "foo", 865 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 866 &states.ResourceInstanceObjectSrc{ 867 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 868 Status: states.ObjectReady, 869 }, 870 addrs.AbsProviderConfig{ 871 Provider: addrs.NewDefaultProvider("test"), 872 Module: addrs.RootModule, 873 }, 874 ) 875 s.SetResourceInstanceCurrent( 876 addrs.Resource{ 877 Mode: addrs.ManagedResourceMode, 878 Type: "test_instance", 879 Name: "baz", 880 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 881 &states.ResourceInstanceObjectSrc{ 882 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 883 Status: states.ObjectReady, 884 }, 885 addrs.AbsProviderConfig{ 886 Provider: addrs.NewDefaultProvider("test"), 887 Module: addrs.RootModule, 888 }, 889 ) 890 }) 891 statePath := testStateFile(t, state) 892 893 // init our backend 894 ui := new(cli.MockUi) 895 view, _ := testView(t) 896 ic := &InitCommand{ 897 Meta: Meta{ 898 testingOverrides: metaOverridesForProvider(testProvider()), 899 Ui: ui, 900 View: view, 901 }, 902 } 903 904 args := []string{} 905 if code := ic.Run(args); code != 0 { 906 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 907 } 908 909 // only modify statePath 910 p := testProvider() 911 ui = new(cli.MockUi) 912 c := &StateMvCommand{ 913 StateMeta{ 914 Meta: Meta{ 915 testingOverrides: metaOverridesForProvider(p), 916 Ui: ui, 917 View: view, 918 }, 919 }, 920 } 921 922 args = []string{ 923 "-backup", backupPath, 924 "-state", statePath, 925 "test_instance.foo", 926 "test_instance.bar", 927 } 928 if code := c.Run(args); code != 0 { 929 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 930 } 931 932 // Test it is correct 933 testStateOutput(t, statePath, testStateMvOutput) 934 } 935 936 func TestStateMv_backupExplicit(t *testing.T) { 937 state := states.BuildState(func(s *states.SyncState) { 938 s.SetResourceInstanceCurrent( 939 addrs.Resource{ 940 Mode: addrs.ManagedResourceMode, 941 Type: "test_instance", 942 Name: "foo", 943 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 944 &states.ResourceInstanceObjectSrc{ 945 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 946 Status: states.ObjectReady, 947 }, 948 addrs.AbsProviderConfig{ 949 Provider: addrs.NewDefaultProvider("test"), 950 Module: addrs.RootModule, 951 }, 952 ) 953 s.SetResourceInstanceCurrent( 954 addrs.Resource{ 955 Mode: addrs.ManagedResourceMode, 956 Type: "test_instance", 957 Name: "baz", 958 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 959 &states.ResourceInstanceObjectSrc{ 960 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 961 Status: states.ObjectReady, 962 Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")}, 963 }, 964 addrs.AbsProviderConfig{ 965 Provider: addrs.NewDefaultProvider("test"), 966 Module: addrs.RootModule, 967 }, 968 ) 969 }) 970 statePath := testStateFile(t, state) 971 backupPath := statePath + ".backup.test" 972 973 p := testProvider() 974 ui := new(cli.MockUi) 975 view, _ := testView(t) 976 c := &StateMvCommand{ 977 StateMeta{ 978 Meta: Meta{ 979 testingOverrides: metaOverridesForProvider(p), 980 Ui: ui, 981 View: view, 982 }, 983 }, 984 } 985 986 args := []string{ 987 "-backup", backupPath, 988 "-state", statePath, 989 "test_instance.foo", 990 "test_instance.bar", 991 } 992 if code := c.Run(args); code != 0 { 993 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 994 } 995 996 // Test it is correct 997 testStateOutput(t, statePath, testStateMvOutput) 998 999 // Test backup 1000 testStateOutput(t, backupPath, testStateMvOutputOriginal) 1001 } 1002 1003 func TestStateMv_stateOutNew(t *testing.T) { 1004 state := states.BuildState(func(s *states.SyncState) { 1005 s.SetResourceInstanceCurrent( 1006 addrs.Resource{ 1007 Mode: addrs.ManagedResourceMode, 1008 Type: "test_instance", 1009 Name: "foo", 1010 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1011 &states.ResourceInstanceObjectSrc{ 1012 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1013 Status: states.ObjectReady, 1014 }, 1015 addrs.AbsProviderConfig{ 1016 Provider: addrs.NewDefaultProvider("test"), 1017 Module: addrs.RootModule, 1018 }, 1019 ) 1020 }) 1021 statePath := testStateFile(t, state) 1022 stateOutPath := statePath + ".out" 1023 1024 p := testProvider() 1025 ui := new(cli.MockUi) 1026 view, _ := testView(t) 1027 c := &StateMvCommand{ 1028 StateMeta{ 1029 Meta: Meta{ 1030 testingOverrides: metaOverridesForProvider(p), 1031 Ui: ui, 1032 View: view, 1033 }, 1034 }, 1035 } 1036 1037 args := []string{ 1038 "-state", statePath, 1039 "-state-out", stateOutPath, 1040 "test_instance.foo", 1041 "test_instance.bar", 1042 } 1043 if code := c.Run(args); code != 0 { 1044 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1045 } 1046 1047 // Test it is correct 1048 testStateOutput(t, stateOutPath, testStateMvOutput_stateOut) 1049 testStateOutput(t, statePath, testStateMvOutput_stateOutSrc) 1050 1051 // Test we have backups 1052 backups := testStateBackups(t, filepath.Dir(statePath)) 1053 if len(backups) != 1 { 1054 t.Fatalf("bad: %#v", backups) 1055 } 1056 testStateOutput(t, backups[0], testStateMvOutput_stateOutOriginal) 1057 } 1058 1059 func TestStateMv_stateOutExisting(t *testing.T) { 1060 stateSrc := states.BuildState(func(s *states.SyncState) { 1061 s.SetResourceInstanceCurrent( 1062 addrs.Resource{ 1063 Mode: addrs.ManagedResourceMode, 1064 Type: "test_instance", 1065 Name: "foo", 1066 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1067 &states.ResourceInstanceObjectSrc{ 1068 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1069 Status: states.ObjectReady, 1070 }, 1071 addrs.AbsProviderConfig{ 1072 Provider: addrs.NewDefaultProvider("test"), 1073 Module: addrs.RootModule, 1074 }, 1075 ) 1076 }) 1077 statePath := testStateFile(t, stateSrc) 1078 1079 stateDst := states.BuildState(func(s *states.SyncState) { 1080 s.SetResourceInstanceCurrent( 1081 addrs.Resource{ 1082 Mode: addrs.ManagedResourceMode, 1083 Type: "test_instance", 1084 Name: "qux", 1085 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1086 &states.ResourceInstanceObjectSrc{ 1087 AttrsJSON: []byte(`{"id":"bar"}`), 1088 Status: states.ObjectReady, 1089 }, 1090 addrs.AbsProviderConfig{ 1091 Provider: addrs.NewDefaultProvider("test"), 1092 Module: addrs.RootModule, 1093 }, 1094 ) 1095 }) 1096 stateOutPath := testStateFile(t, stateDst) 1097 1098 p := testProvider() 1099 ui := new(cli.MockUi) 1100 view, _ := testView(t) 1101 c := &StateMvCommand{ 1102 StateMeta{ 1103 Meta: Meta{ 1104 testingOverrides: metaOverridesForProvider(p), 1105 Ui: ui, 1106 View: view, 1107 }, 1108 }, 1109 } 1110 1111 args := []string{ 1112 "-state", statePath, 1113 "-state-out", stateOutPath, 1114 "test_instance.foo", 1115 "test_instance.bar", 1116 } 1117 if code := c.Run(args); code != 0 { 1118 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1119 } 1120 1121 // Test it is correct 1122 testStateOutput(t, stateOutPath, testStateMvExisting_stateDst) 1123 testStateOutput(t, statePath, testStateMvExisting_stateSrc) 1124 1125 // Test we have backups 1126 backups := testStateBackups(t, filepath.Dir(statePath)) 1127 if len(backups) != 1 { 1128 t.Fatalf("bad: %#v", backups) 1129 } 1130 testStateOutput(t, backups[0], testStateMvExisting_stateSrcOriginal) 1131 1132 backups = testStateBackups(t, filepath.Dir(stateOutPath)) 1133 if len(backups) != 1 { 1134 t.Fatalf("bad: %#v", backups) 1135 } 1136 testStateOutput(t, backups[0], testStateMvExisting_stateDstOriginal) 1137 } 1138 1139 func TestStateMv_noState(t *testing.T) { 1140 testCwd(t) 1141 1142 p := testProvider() 1143 ui := new(cli.MockUi) 1144 view, _ := testView(t) 1145 c := &StateMvCommand{ 1146 StateMeta{ 1147 Meta: Meta{ 1148 testingOverrides: metaOverridesForProvider(p), 1149 Ui: ui, 1150 View: view, 1151 }, 1152 }, 1153 } 1154 1155 args := []string{"from", "to"} 1156 if code := c.Run(args); code != 1 { 1157 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1158 } 1159 } 1160 1161 func TestStateMv_stateOutNew_count(t *testing.T) { 1162 state := states.BuildState(func(s *states.SyncState) { 1163 s.SetResourceInstanceCurrent( 1164 addrs.Resource{ 1165 Mode: addrs.ManagedResourceMode, 1166 Type: "test_instance", 1167 Name: "foo", 1168 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 1169 &states.ResourceInstanceObjectSrc{ 1170 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 1171 Status: states.ObjectReady, 1172 }, 1173 addrs.AbsProviderConfig{ 1174 Provider: addrs.NewDefaultProvider("test"), 1175 Module: addrs.RootModule, 1176 }, 1177 ) 1178 s.SetResourceInstanceCurrent( 1179 addrs.Resource{ 1180 Mode: addrs.ManagedResourceMode, 1181 Type: "test_instance", 1182 Name: "foo", 1183 }.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance), 1184 &states.ResourceInstanceObjectSrc{ 1185 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1186 Status: states.ObjectReady, 1187 }, 1188 addrs.AbsProviderConfig{ 1189 Provider: addrs.NewDefaultProvider("test"), 1190 Module: addrs.RootModule, 1191 }, 1192 ) 1193 s.SetResourceInstanceCurrent( 1194 addrs.Resource{ 1195 Mode: addrs.ManagedResourceMode, 1196 Type: "test_instance", 1197 Name: "bar", 1198 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1199 &states.ResourceInstanceObjectSrc{ 1200 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1201 Status: states.ObjectReady, 1202 }, 1203 addrs.AbsProviderConfig{ 1204 Provider: addrs.NewDefaultProvider("test"), 1205 Module: addrs.RootModule, 1206 }, 1207 ) 1208 }) 1209 statePath := testStateFile(t, state) 1210 stateOutPath := statePath + ".out" 1211 1212 p := testProvider() 1213 ui := new(cli.MockUi) 1214 view, _ := testView(t) 1215 c := &StateMvCommand{ 1216 StateMeta{ 1217 Meta: Meta{ 1218 testingOverrides: metaOverridesForProvider(p), 1219 Ui: ui, 1220 View: view, 1221 }, 1222 }, 1223 } 1224 1225 args := []string{ 1226 "-state", statePath, 1227 "-state-out", stateOutPath, 1228 "test_instance.foo", 1229 "test_instance.bar", 1230 } 1231 if code := c.Run(args); code != 0 { 1232 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1233 } 1234 1235 // Test it is correct 1236 testStateOutput(t, stateOutPath, testStateMvCount_stateOut) 1237 testStateOutput(t, statePath, testStateMvCount_stateOutSrc) 1238 1239 // Test we have backups 1240 backups := testStateBackups(t, filepath.Dir(statePath)) 1241 if len(backups) != 1 { 1242 t.Fatalf("bad: %#v", backups) 1243 } 1244 testStateOutput(t, backups[0], testStateMvCount_stateOutOriginal) 1245 } 1246 1247 // Modules with more than 10 resources were sorted lexically, causing the 1248 // indexes in the new location to change. 1249 func TestStateMv_stateOutNew_largeCount(t *testing.T) { 1250 state := states.BuildState(func(s *states.SyncState) { 1251 // test_instance.foo has 11 instances, all the same except for their ids 1252 for i := 0; i < 11; i++ { 1253 s.SetResourceInstanceCurrent( 1254 addrs.Resource{ 1255 Mode: addrs.ManagedResourceMode, 1256 Type: "test_instance", 1257 Name: "foo", 1258 }.Instance(addrs.IntKey(i)).Absolute(addrs.RootModuleInstance), 1259 &states.ResourceInstanceObjectSrc{ 1260 AttrsJSON: []byte(fmt.Sprintf(`{"id":"foo%d","foo":"value","bar":"value"}`, i)), 1261 Status: states.ObjectReady, 1262 }, 1263 addrs.AbsProviderConfig{ 1264 Provider: addrs.NewDefaultProvider("test"), 1265 Module: addrs.RootModule, 1266 }, 1267 ) 1268 } 1269 s.SetResourceInstanceCurrent( 1270 addrs.Resource{ 1271 Mode: addrs.ManagedResourceMode, 1272 Type: "test_instance", 1273 Name: "bar", 1274 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1275 &states.ResourceInstanceObjectSrc{ 1276 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1277 Status: states.ObjectReady, 1278 }, 1279 addrs.AbsProviderConfig{ 1280 Provider: addrs.NewDefaultProvider("test"), 1281 Module: addrs.RootModule, 1282 }, 1283 ) 1284 }) 1285 statePath := testStateFile(t, state) 1286 stateOutPath := statePath + ".out" 1287 1288 p := testProvider() 1289 ui := new(cli.MockUi) 1290 view, _ := testView(t) 1291 c := &StateMvCommand{ 1292 StateMeta{ 1293 Meta: Meta{ 1294 testingOverrides: metaOverridesForProvider(p), 1295 Ui: ui, 1296 View: view, 1297 }, 1298 }, 1299 } 1300 1301 args := []string{ 1302 "-state", statePath, 1303 "-state-out", stateOutPath, 1304 "test_instance.foo", 1305 "test_instance.bar", 1306 } 1307 if code := c.Run(args); code != 0 { 1308 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1309 } 1310 1311 // Test it is correct 1312 testStateOutput(t, stateOutPath, testStateMvLargeCount_stateOut) 1313 testStateOutput(t, statePath, testStateMvLargeCount_stateOutSrc) 1314 1315 // Test we have backups 1316 backups := testStateBackups(t, filepath.Dir(statePath)) 1317 if len(backups) != 1 { 1318 t.Fatalf("bad: %#v", backups) 1319 } 1320 testStateOutput(t, backups[0], testStateMvLargeCount_stateOutOriginal) 1321 } 1322 1323 func TestStateMv_stateOutNew_nestedModule(t *testing.T) { 1324 state := states.BuildState(func(s *states.SyncState) { 1325 s.SetResourceInstanceCurrent( 1326 addrs.Resource{ 1327 Mode: addrs.ManagedResourceMode, 1328 Type: "test_instance", 1329 Name: "foo", 1330 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("foo", addrs.NoKey).Child("child1", addrs.NoKey)), 1331 &states.ResourceInstanceObjectSrc{ 1332 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1333 Status: states.ObjectReady, 1334 }, 1335 addrs.AbsProviderConfig{ 1336 Provider: addrs.NewDefaultProvider("test"), 1337 Module: addrs.RootModule, 1338 }, 1339 ) 1340 s.SetResourceInstanceCurrent( 1341 addrs.Resource{ 1342 Mode: addrs.ManagedResourceMode, 1343 Type: "test_instance", 1344 Name: "foo", 1345 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("foo", addrs.NoKey).Child("child2", addrs.NoKey)), 1346 &states.ResourceInstanceObjectSrc{ 1347 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1348 Status: states.ObjectReady, 1349 }, 1350 addrs.AbsProviderConfig{ 1351 Provider: addrs.NewDefaultProvider("test"), 1352 Module: addrs.RootModule, 1353 }, 1354 ) 1355 }) 1356 1357 statePath := testStateFile(t, state) 1358 stateOutPath := statePath + ".out" 1359 1360 p := testProvider() 1361 ui := new(cli.MockUi) 1362 view, _ := testView(t) 1363 c := &StateMvCommand{ 1364 StateMeta{ 1365 Meta: Meta{ 1366 testingOverrides: metaOverridesForProvider(p), 1367 Ui: ui, 1368 View: view, 1369 }, 1370 }, 1371 } 1372 1373 args := []string{ 1374 "-state", statePath, 1375 "-state-out", stateOutPath, 1376 "module.foo", 1377 "module.bar", 1378 } 1379 if code := c.Run(args); code != 0 { 1380 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1381 } 1382 1383 // Test it is correct 1384 testStateOutput(t, stateOutPath, testStateMvNestedModule_stateOut) 1385 testStateOutput(t, statePath, testStateMvNestedModule_stateOutSrc) 1386 1387 // Test we have backups 1388 backups := testStateBackups(t, filepath.Dir(statePath)) 1389 if len(backups) != 1 { 1390 t.Fatalf("bad: %#v", backups) 1391 } 1392 testStateOutput(t, backups[0], testStateMvNestedModule_stateOutOriginal) 1393 } 1394 1395 func TestStateMv_toNewModule(t *testing.T) { 1396 state := states.BuildState(func(s *states.SyncState) { 1397 s.SetResourceInstanceCurrent( 1398 addrs.Resource{ 1399 Mode: addrs.ManagedResourceMode, 1400 Type: "test_instance", 1401 Name: "bar", 1402 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1403 &states.ResourceInstanceObjectSrc{ 1404 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1405 Status: states.ObjectReady, 1406 }, 1407 addrs.AbsProviderConfig{ 1408 Provider: addrs.NewDefaultProvider("test"), 1409 Module: addrs.RootModule, 1410 }, 1411 ) 1412 }) 1413 1414 statePath := testStateFile(t, state) 1415 stateOutPath1 := statePath + ".out1" 1416 stateOutPath2 := statePath + ".out2" 1417 1418 p := testProvider() 1419 ui := new(cli.MockUi) 1420 view, _ := testView(t) 1421 c := &StateMvCommand{ 1422 StateMeta{ 1423 Meta: Meta{ 1424 testingOverrides: metaOverridesForProvider(p), 1425 Ui: ui, 1426 View: view, 1427 }, 1428 }, 1429 } 1430 1431 args := []string{ 1432 "-state", statePath, 1433 "-state-out", stateOutPath1, 1434 "test_instance.bar", 1435 "module.bar.test_instance.bar", 1436 } 1437 if code := c.Run(args); code != 0 { 1438 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1439 } 1440 1441 // Test it is correct 1442 testStateOutput(t, stateOutPath1, testStateMvNewModule_stateOut) 1443 testStateOutput(t, statePath, testStateMvNestedModule_stateOutSrc) 1444 1445 // Test we have backups 1446 backups := testStateBackups(t, filepath.Dir(statePath)) 1447 if len(backups) != 1 { 1448 t.Fatalf("bad: %#v", backups) 1449 } 1450 testStateOutput(t, backups[0], testStateMvNewModule_stateOutOriginal) 1451 1452 // now verify we can move the module itself 1453 args = []string{ 1454 "-state", stateOutPath1, 1455 "-state-out", stateOutPath2, 1456 "module.bar", 1457 "module.foo", 1458 } 1459 if code := c.Run(args); code != 0 { 1460 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1461 } 1462 testStateOutput(t, stateOutPath2, testStateMvModuleNewModule_stateOut) 1463 } 1464 1465 func TestStateMv_withinBackend(t *testing.T) { 1466 td := t.TempDir() 1467 testCopyDir(t, testFixturePath("backend-unchanged"), td) 1468 defer testChdir(t, td)() 1469 1470 state := states.BuildState(func(s *states.SyncState) { 1471 s.SetResourceInstanceCurrent( 1472 addrs.Resource{ 1473 Mode: addrs.ManagedResourceMode, 1474 Type: "test_instance", 1475 Name: "foo", 1476 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1477 &states.ResourceInstanceObjectSrc{ 1478 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1479 Status: states.ObjectReady, 1480 }, 1481 addrs.AbsProviderConfig{ 1482 Provider: addrs.NewDefaultProvider("test"), 1483 Module: addrs.RootModule, 1484 }, 1485 ) 1486 s.SetResourceInstanceCurrent( 1487 addrs.Resource{ 1488 Mode: addrs.ManagedResourceMode, 1489 Type: "test_instance", 1490 Name: "baz", 1491 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1492 &states.ResourceInstanceObjectSrc{ 1493 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 1494 Status: states.ObjectReady, 1495 Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")}, 1496 }, 1497 addrs.AbsProviderConfig{ 1498 Provider: addrs.NewDefaultProvider("test"), 1499 Module: addrs.RootModule, 1500 }, 1501 ) 1502 }) 1503 1504 // the local backend state file is "foo" 1505 statePath := "local-state.tfstate" 1506 backupPath := "local-state.backup" 1507 1508 f, err := os.Create(statePath) 1509 if err != nil { 1510 t.Fatal(err) 1511 } 1512 defer f.Close() 1513 1514 if err := writeStateForTesting(state, f); err != nil { 1515 t.Fatal(err) 1516 } 1517 1518 p := testProvider() 1519 ui := new(cli.MockUi) 1520 view, _ := testView(t) 1521 c := &StateMvCommand{ 1522 StateMeta{ 1523 Meta: Meta{ 1524 testingOverrides: metaOverridesForProvider(p), 1525 Ui: ui, 1526 View: view, 1527 }, 1528 }, 1529 } 1530 1531 args := []string{ 1532 "-backup", backupPath, 1533 "test_instance.foo", 1534 "test_instance.bar", 1535 } 1536 if code := c.Run(args); code != 0 { 1537 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1538 } 1539 1540 testStateOutput(t, statePath, testStateMvOutput) 1541 testStateOutput(t, backupPath, testStateMvOutputOriginal) 1542 } 1543 1544 func TestStateMv_fromBackendToLocal(t *testing.T) { 1545 td := t.TempDir() 1546 testCopyDir(t, testFixturePath("backend-unchanged"), td) 1547 defer testChdir(t, td)() 1548 1549 state := states.NewState() 1550 state.Module(addrs.RootModuleInstance).SetResourceInstanceCurrent( 1551 mustResourceAddr("test_instance.foo").Resource.Instance(addrs.NoKey), 1552 &states.ResourceInstanceObjectSrc{ 1553 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1554 Status: states.ObjectReady, 1555 }, 1556 addrs.AbsProviderConfig{ 1557 Provider: addrs.NewDefaultProvider("test"), 1558 Module: addrs.RootModule, 1559 }, 1560 ) 1561 state.Module(addrs.RootModuleInstance).SetResourceInstanceCurrent( 1562 mustResourceAddr("test_instance.baz").Resource.Instance(addrs.NoKey), 1563 &states.ResourceInstanceObjectSrc{ 1564 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 1565 Status: states.ObjectReady, 1566 }, 1567 addrs.AbsProviderConfig{ 1568 Provider: addrs.NewDefaultProvider("test"), 1569 Module: addrs.RootModule, 1570 }, 1571 ) 1572 1573 // the local backend state file is "foo" 1574 statePath := "local-state.tfstate" 1575 1576 // real "local" state file 1577 statePathOut := "real-local.tfstate" 1578 1579 f, err := os.Create(statePath) 1580 if err != nil { 1581 t.Fatal(err) 1582 } 1583 defer f.Close() 1584 1585 if err := writeStateForTesting(state, f); err != nil { 1586 t.Fatal(err) 1587 } 1588 1589 p := testProvider() 1590 ui := new(cli.MockUi) 1591 view, _ := testView(t) 1592 c := &StateMvCommand{ 1593 StateMeta{ 1594 Meta: Meta{ 1595 testingOverrides: metaOverridesForProvider(p), 1596 Ui: ui, 1597 View: view, 1598 }, 1599 }, 1600 } 1601 1602 args := []string{ 1603 "-state-out", statePathOut, 1604 "test_instance.foo", 1605 "test_instance.bar", 1606 } 1607 if code := c.Run(args); code != 0 { 1608 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1609 } 1610 1611 testStateOutput(t, statePathOut, testStateMvCount_stateOutSrc) 1612 1613 // the backend state should be left with only baz 1614 testStateOutput(t, statePath, testStateMvOriginal_backend) 1615 } 1616 1617 // This test covers moving the only resource in a module to a new address in 1618 // that module, which triggers the maybePruneModule functionality. This caused 1619 // a panic report: https://github.com/terramate-io/tf/issues/25520 1620 func TestStateMv_onlyResourceInModule(t *testing.T) { 1621 state := states.BuildState(func(s *states.SyncState) { 1622 s.SetResourceInstanceCurrent( 1623 addrs.Resource{ 1624 Mode: addrs.ManagedResourceMode, 1625 Type: "test_instance", 1626 Name: "foo", 1627 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance.Child("foo", addrs.NoKey)), 1628 &states.ResourceInstanceObjectSrc{ 1629 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1630 Status: states.ObjectReady, 1631 }, 1632 addrs.AbsProviderConfig{ 1633 Provider: addrs.NewDefaultProvider("test"), 1634 Module: addrs.RootModule, 1635 }, 1636 ) 1637 }) 1638 1639 statePath := testStateFile(t, state) 1640 testStateOutput(t, statePath, testStateMvOnlyResourceInModule_original) 1641 1642 p := testProvider() 1643 ui := new(cli.MockUi) 1644 view, _ := testView(t) 1645 c := &StateMvCommand{ 1646 StateMeta{ 1647 Meta: Meta{ 1648 testingOverrides: metaOverridesForProvider(p), 1649 Ui: ui, 1650 View: view, 1651 }, 1652 }, 1653 } 1654 1655 args := []string{ 1656 "-state", statePath, 1657 "module.foo.test_instance.foo", 1658 "module.foo.test_instance.bar", 1659 } 1660 if code := c.Run(args); code != 0 { 1661 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 1662 } 1663 1664 // Test it is correct 1665 testStateOutput(t, statePath, testStateMvOnlyResourceInModule_output) 1666 1667 // Test we have backups 1668 backups := testStateBackups(t, filepath.Dir(statePath)) 1669 if len(backups) != 1 { 1670 t.Fatalf("bad: %#v", backups) 1671 } 1672 testStateOutput(t, backups[0], testStateMvOnlyResourceInModule_original) 1673 } 1674 1675 func TestStateMvHelp(t *testing.T) { 1676 c := &StateMvCommand{} 1677 if strings.ContainsRune(c.Help(), '\t') { 1678 t.Fatal("help text contains tab character, which will result in poor formatting") 1679 } 1680 } 1681 1682 func TestStateMvInvalidSourceAddress(t *testing.T) { 1683 state := states.BuildState(func(s *states.SyncState) {}) 1684 statePath := testStateFile(t, state) 1685 1686 p := testProvider() 1687 ui := new(cli.MockUi) 1688 view, _ := testView(t) 1689 c := &StateMvCommand{ 1690 StateMeta{ 1691 Meta: Meta{ 1692 testingOverrides: metaOverridesForProvider(p), 1693 Ui: ui, 1694 View: view, 1695 }, 1696 }, 1697 } 1698 1699 args := []string{ 1700 "-state", statePath, 1701 "foo.bar1", 1702 "foo.bar2", 1703 } 1704 code := c.Run(args) 1705 if code != 1 { 1706 t.Fatalf("expected error code 1, got:\n%d", code) 1707 } 1708 } 1709 1710 func TestStateMv_checkRequiredVersion(t *testing.T) { 1711 // Create a temporary working directory that is empty 1712 td := t.TempDir() 1713 testCopyDir(t, testFixturePath("command-check-required-version"), td) 1714 defer testChdir(t, td)() 1715 1716 state := states.BuildState(func(s *states.SyncState) { 1717 s.SetResourceInstanceCurrent( 1718 addrs.Resource{ 1719 Mode: addrs.ManagedResourceMode, 1720 Type: "test_instance", 1721 Name: "foo", 1722 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1723 &states.ResourceInstanceObjectSrc{ 1724 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 1725 Status: states.ObjectReady, 1726 }, 1727 addrs.AbsProviderConfig{ 1728 Provider: addrs.NewDefaultProvider("test"), 1729 Module: addrs.RootModule, 1730 }, 1731 ) 1732 s.SetResourceInstanceCurrent( 1733 addrs.Resource{ 1734 Mode: addrs.ManagedResourceMode, 1735 Type: "test_instance", 1736 Name: "baz", 1737 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 1738 &states.ResourceInstanceObjectSrc{ 1739 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 1740 Status: states.ObjectReady, 1741 Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")}, 1742 }, 1743 addrs.AbsProviderConfig{ 1744 Provider: addrs.NewDefaultProvider("test"), 1745 Module: addrs.RootModule, 1746 }, 1747 ) 1748 }) 1749 statePath := testStateFile(t, state) 1750 1751 p := testProvider() 1752 ui := new(cli.MockUi) 1753 view, _ := testView(t) 1754 c := &StateMvCommand{ 1755 StateMeta{ 1756 Meta: Meta{ 1757 testingOverrides: metaOverridesForProvider(p), 1758 Ui: ui, 1759 View: view, 1760 }, 1761 }, 1762 } 1763 1764 args := []string{ 1765 "-state", statePath, 1766 "test_instance.foo", 1767 "test_instance.bar", 1768 } 1769 1770 if code := c.Run(args); code != 1 { 1771 t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String()) 1772 } 1773 1774 // State is unchanged 1775 testStateOutput(t, statePath, testStateMvOutputOriginal) 1776 1777 // Required version diags are correct 1778 errStr := ui.ErrorWriter.String() 1779 if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) { 1780 t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr) 1781 } 1782 if strings.Contains(errStr, `required_version = ">= 0.13.0"`) { 1783 t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr) 1784 } 1785 } 1786 1787 const testStateMvOutputOriginal = ` 1788 test_instance.baz: 1789 ID = foo 1790 provider = provider["registry.terraform.io/hashicorp/test"] 1791 bar = value 1792 foo = value 1793 1794 Dependencies: 1795 test_instance.foo 1796 test_instance.foo: 1797 ID = bar 1798 provider = provider["registry.terraform.io/hashicorp/test"] 1799 bar = value 1800 foo = value 1801 ` 1802 1803 const testStateMvOutput = ` 1804 test_instance.bar: 1805 ID = bar 1806 provider = provider["registry.terraform.io/hashicorp/test"] 1807 bar = value 1808 foo = value 1809 test_instance.baz: 1810 ID = foo 1811 provider = provider["registry.terraform.io/hashicorp/test"] 1812 bar = value 1813 foo = value 1814 ` 1815 1816 const testStateMvBackupAndBackupOutOptionsWithNonLocalBackendOutput = ` 1817 test_instance.bar: 1818 ID = bar 1819 provider = provider["registry.terraform.io/hashicorp/test"] 1820 bar = value 1821 foo = value 1822 ` 1823 1824 const testStateMvCount_stateOut = ` 1825 test_instance.bar.0: 1826 ID = foo 1827 provider = provider["registry.terraform.io/hashicorp/test"] 1828 bar = value 1829 foo = value 1830 test_instance.bar.1: 1831 ID = bar 1832 provider = provider["registry.terraform.io/hashicorp/test"] 1833 bar = value 1834 foo = value 1835 ` 1836 1837 const testStateMvCount_stateOutSrc = ` 1838 test_instance.bar: 1839 ID = bar 1840 provider = provider["registry.terraform.io/hashicorp/test"] 1841 bar = value 1842 foo = value 1843 ` 1844 1845 const testStateMvCount_stateOutOriginal = ` 1846 test_instance.bar: 1847 ID = bar 1848 provider = provider["registry.terraform.io/hashicorp/test"] 1849 bar = value 1850 foo = value 1851 test_instance.foo.0: 1852 ID = foo 1853 provider = provider["registry.terraform.io/hashicorp/test"] 1854 bar = value 1855 foo = value 1856 test_instance.foo.1: 1857 ID = bar 1858 provider = provider["registry.terraform.io/hashicorp/test"] 1859 bar = value 1860 foo = value 1861 ` 1862 1863 const testStateMvLargeCount_stateOut = ` 1864 test_instance.bar.0: 1865 ID = foo0 1866 provider = provider["registry.terraform.io/hashicorp/test"] 1867 bar = value 1868 foo = value 1869 test_instance.bar.1: 1870 ID = foo1 1871 provider = provider["registry.terraform.io/hashicorp/test"] 1872 bar = value 1873 foo = value 1874 test_instance.bar.2: 1875 ID = foo2 1876 provider = provider["registry.terraform.io/hashicorp/test"] 1877 bar = value 1878 foo = value 1879 test_instance.bar.3: 1880 ID = foo3 1881 provider = provider["registry.terraform.io/hashicorp/test"] 1882 bar = value 1883 foo = value 1884 test_instance.bar.4: 1885 ID = foo4 1886 provider = provider["registry.terraform.io/hashicorp/test"] 1887 bar = value 1888 foo = value 1889 test_instance.bar.5: 1890 ID = foo5 1891 provider = provider["registry.terraform.io/hashicorp/test"] 1892 bar = value 1893 foo = value 1894 test_instance.bar.6: 1895 ID = foo6 1896 provider = provider["registry.terraform.io/hashicorp/test"] 1897 bar = value 1898 foo = value 1899 test_instance.bar.7: 1900 ID = foo7 1901 provider = provider["registry.terraform.io/hashicorp/test"] 1902 bar = value 1903 foo = value 1904 test_instance.bar.8: 1905 ID = foo8 1906 provider = provider["registry.terraform.io/hashicorp/test"] 1907 bar = value 1908 foo = value 1909 test_instance.bar.9: 1910 ID = foo9 1911 provider = provider["registry.terraform.io/hashicorp/test"] 1912 bar = value 1913 foo = value 1914 test_instance.bar.10: 1915 ID = foo10 1916 provider = provider["registry.terraform.io/hashicorp/test"] 1917 bar = value 1918 foo = value 1919 ` 1920 1921 const testStateMvLargeCount_stateOutSrc = ` 1922 test_instance.bar: 1923 ID = bar 1924 provider = provider["registry.terraform.io/hashicorp/test"] 1925 bar = value 1926 foo = value 1927 ` 1928 1929 const testStateMvLargeCount_stateOutOriginal = ` 1930 test_instance.bar: 1931 ID = bar 1932 provider = provider["registry.terraform.io/hashicorp/test"] 1933 bar = value 1934 foo = value 1935 test_instance.foo.0: 1936 ID = foo0 1937 provider = provider["registry.terraform.io/hashicorp/test"] 1938 bar = value 1939 foo = value 1940 test_instance.foo.1: 1941 ID = foo1 1942 provider = provider["registry.terraform.io/hashicorp/test"] 1943 bar = value 1944 foo = value 1945 test_instance.foo.2: 1946 ID = foo2 1947 provider = provider["registry.terraform.io/hashicorp/test"] 1948 bar = value 1949 foo = value 1950 test_instance.foo.3: 1951 ID = foo3 1952 provider = provider["registry.terraform.io/hashicorp/test"] 1953 bar = value 1954 foo = value 1955 test_instance.foo.4: 1956 ID = foo4 1957 provider = provider["registry.terraform.io/hashicorp/test"] 1958 bar = value 1959 foo = value 1960 test_instance.foo.5: 1961 ID = foo5 1962 provider = provider["registry.terraform.io/hashicorp/test"] 1963 bar = value 1964 foo = value 1965 test_instance.foo.6: 1966 ID = foo6 1967 provider = provider["registry.terraform.io/hashicorp/test"] 1968 bar = value 1969 foo = value 1970 test_instance.foo.7: 1971 ID = foo7 1972 provider = provider["registry.terraform.io/hashicorp/test"] 1973 bar = value 1974 foo = value 1975 test_instance.foo.8: 1976 ID = foo8 1977 provider = provider["registry.terraform.io/hashicorp/test"] 1978 bar = value 1979 foo = value 1980 test_instance.foo.9: 1981 ID = foo9 1982 provider = provider["registry.terraform.io/hashicorp/test"] 1983 bar = value 1984 foo = value 1985 test_instance.foo.10: 1986 ID = foo10 1987 provider = provider["registry.terraform.io/hashicorp/test"] 1988 bar = value 1989 foo = value 1990 ` 1991 1992 const testStateMvNestedModule_stateOut = ` 1993 <no state> 1994 module.bar.child1: 1995 test_instance.foo: 1996 ID = bar 1997 provider = provider["registry.terraform.io/hashicorp/test"] 1998 bar = value 1999 foo = value 2000 module.bar.child2: 2001 test_instance.foo: 2002 ID = bar 2003 provider = provider["registry.terraform.io/hashicorp/test"] 2004 bar = value 2005 foo = value 2006 ` 2007 2008 const testStateMvNewModule_stateOut = ` 2009 <no state> 2010 module.bar: 2011 test_instance.bar: 2012 ID = bar 2013 provider = provider["registry.terraform.io/hashicorp/test"] 2014 bar = value 2015 foo = value 2016 ` 2017 2018 const testStateMvModuleNewModule_stateOut = ` 2019 <no state> 2020 module.foo: 2021 test_instance.bar: 2022 ID = bar 2023 provider = provider["registry.terraform.io/hashicorp/test"] 2024 bar = value 2025 foo = value 2026 ` 2027 2028 const testStateMvNewModule_stateOutOriginal = ` 2029 test_instance.bar: 2030 ID = bar 2031 provider = provider["registry.terraform.io/hashicorp/test"] 2032 bar = value 2033 foo = value 2034 ` 2035 2036 const testStateMvNestedModule_stateOutSrc = ` 2037 <no state> 2038 ` 2039 2040 const testStateMvNestedModule_stateOutOriginal = ` 2041 <no state> 2042 module.foo.child1: 2043 test_instance.foo: 2044 ID = bar 2045 provider = provider["registry.terraform.io/hashicorp/test"] 2046 bar = value 2047 foo = value 2048 module.foo.child2: 2049 test_instance.foo: 2050 ID = bar 2051 provider = provider["registry.terraform.io/hashicorp/test"] 2052 bar = value 2053 foo = value 2054 ` 2055 2056 const testStateMvOutput_stateOut = ` 2057 test_instance.bar: 2058 ID = bar 2059 provider = provider["registry.terraform.io/hashicorp/test"] 2060 bar = value 2061 foo = value 2062 ` 2063 2064 const testStateMvOutput_stateOutSrc = ` 2065 <no state> 2066 ` 2067 2068 const testStateMvOutput_stateOutOriginal = ` 2069 test_instance.foo: 2070 ID = bar 2071 provider = provider["registry.terraform.io/hashicorp/test"] 2072 bar = value 2073 foo = value 2074 ` 2075 2076 const testStateMvExisting_stateSrc = ` 2077 <no state> 2078 ` 2079 2080 const testStateMvExisting_stateDst = ` 2081 test_instance.bar: 2082 ID = bar 2083 provider = provider["registry.terraform.io/hashicorp/test"] 2084 bar = value 2085 foo = value 2086 test_instance.qux: 2087 ID = bar 2088 provider = provider["registry.terraform.io/hashicorp/test"] 2089 ` 2090 2091 const testStateMvExisting_stateSrcOriginal = ` 2092 test_instance.foo: 2093 ID = bar 2094 provider = provider["registry.terraform.io/hashicorp/test"] 2095 bar = value 2096 foo = value 2097 ` 2098 2099 const testStateMvExisting_stateDstOriginal = ` 2100 test_instance.qux: 2101 ID = bar 2102 provider = provider["registry.terraform.io/hashicorp/test"] 2103 ` 2104 2105 const testStateMvOriginal_backend = ` 2106 test_instance.baz: 2107 ID = foo 2108 provider = provider["registry.terraform.io/hashicorp/test"] 2109 bar = value 2110 foo = value 2111 ` 2112 2113 const testStateMvOnlyResourceInModule_original = ` 2114 <no state> 2115 module.foo: 2116 test_instance.foo.0: 2117 ID = bar 2118 provider = provider["registry.terraform.io/hashicorp/test"] 2119 bar = value 2120 foo = value 2121 ` 2122 2123 const testStateMvOnlyResourceInModule_output = ` 2124 <no state> 2125 module.foo: 2126 test_instance.bar.0: 2127 ID = bar 2128 provider = provider["registry.terraform.io/hashicorp/test"] 2129 bar = value 2130 foo = value 2131 `