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