github.com/opentofu/opentofu@v1.7.1/internal/command/meta_backend_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 "context" 10 "os" 11 "path/filepath" 12 "reflect" 13 "sort" 14 "strings" 15 "testing" 16 17 "github.com/mitchellh/cli" 18 "github.com/zclconf/go-cty/cty" 19 20 "github.com/opentofu/opentofu/internal/addrs" 21 "github.com/opentofu/opentofu/internal/backend" 22 "github.com/opentofu/opentofu/internal/configs" 23 "github.com/opentofu/opentofu/internal/copy" 24 "github.com/opentofu/opentofu/internal/encryption" 25 "github.com/opentofu/opentofu/internal/plans" 26 "github.com/opentofu/opentofu/internal/states" 27 "github.com/opentofu/opentofu/internal/states/statefile" 28 "github.com/opentofu/opentofu/internal/states/statemgr" 29 30 backendInit "github.com/opentofu/opentofu/internal/backend/init" 31 backendLocal "github.com/opentofu/opentofu/internal/backend/local" 32 backendInmem "github.com/opentofu/opentofu/internal/backend/remote-state/inmem" 33 ) 34 35 // Test empty directory with no config/state creates a local state. 36 func TestMetaBackend_emptyDir(t *testing.T) { 37 // Create a temporary working directory that is empty 38 td := t.TempDir() 39 os.MkdirAll(td, 0755) 40 defer testChdir(t, td)() 41 42 // Get the backend 43 m := testMetaBackend(t, nil) 44 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 45 if diags.HasErrors() { 46 t.Fatal(diags.Err()) 47 } 48 49 // Write some state 50 s, err := b.StateMgr(backend.DefaultStateName) 51 if err != nil { 52 t.Fatalf("unexpected error: %s", err) 53 } 54 s.WriteState(testState()) 55 if err := s.PersistState(nil); err != nil { 56 t.Fatalf("unexpected error: %s", err) 57 } 58 59 // Verify it exists where we expect it to 60 if isEmptyState(DefaultStateFilename) { 61 t.Fatalf("no state was written") 62 } 63 64 // Verify no backup since it was empty to start 65 if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 66 t.Fatal("backup state should be empty") 67 } 68 69 // Verify no backend state was made 70 if !isEmptyState(filepath.Join(m.DataDir(), DefaultStateFilename)) { 71 t.Fatal("backend state should be empty") 72 } 73 } 74 75 // check for no state. Either the file doesn't exist, or is empty 76 func isEmptyState(path string) bool { 77 fi, err := os.Stat(path) 78 if os.IsNotExist(err) { 79 return true 80 } 81 82 if fi.Size() == 0 { 83 return true 84 } 85 86 return false 87 } 88 89 // Test a directory with a legacy state and no config continues to 90 // use the legacy state. 91 func TestMetaBackend_emptyWithDefaultState(t *testing.T) { 92 // Create a temporary working directory that is empty 93 td := t.TempDir() 94 os.MkdirAll(td, 0755) 95 defer testChdir(t, td)() 96 97 // Write the legacy state 98 statePath := DefaultStateFilename 99 { 100 f, err := os.Create(statePath) 101 if err != nil { 102 t.Fatalf("err: %s", err) 103 } 104 err = writeStateForTesting(testState(), f) 105 f.Close() 106 if err != nil { 107 t.Fatalf("err: %s", err) 108 } 109 } 110 111 // Get the backend 112 m := testMetaBackend(t, nil) 113 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 114 if diags.HasErrors() { 115 t.Fatal(diags.Err()) 116 } 117 118 // Check the state 119 s, err := b.StateMgr(backend.DefaultStateName) 120 if err != nil { 121 t.Fatalf("unexpected error: %s", err) 122 } 123 if err := s.RefreshState(); err != nil { 124 t.Fatalf("err: %s", err) 125 } 126 if actual := s.State().String(); actual != testState().String() { 127 t.Fatalf("bad: %s", actual) 128 } 129 130 // Verify it exists where we expect it to 131 if _, err := os.Stat(DefaultStateFilename); err != nil { 132 t.Fatalf("err: %s", err) 133 } 134 135 stateName := filepath.Join(m.DataDir(), DefaultStateFilename) 136 if !isEmptyState(stateName) { 137 t.Fatal("expected no state at", stateName) 138 } 139 140 // Write some state 141 next := testState() 142 next.RootModule().SetOutputValue("foo", cty.StringVal("bar"), false) 143 s.WriteState(next) 144 if err := s.PersistState(nil); err != nil { 145 t.Fatalf("unexpected error: %s", err) 146 } 147 148 // Verify a backup was made since we're modifying a pre-existing state 149 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 150 t.Fatal("backup state should not be empty") 151 } 152 } 153 154 // Test an empty directory with an explicit state path (outside the dir) 155 func TestMetaBackend_emptyWithExplicitState(t *testing.T) { 156 // Create a temporary working directory that is empty 157 td := t.TempDir() 158 os.MkdirAll(td, 0755) 159 defer testChdir(t, td)() 160 161 // Create another directory to store our state 162 stateDir := t.TempDir() 163 os.MkdirAll(stateDir, 0755) 164 165 // Write the legacy state 166 statePath := filepath.Join(stateDir, "foo") 167 { 168 f, err := os.Create(statePath) 169 if err != nil { 170 t.Fatalf("err: %s", err) 171 } 172 err = writeStateForTesting(testState(), f) 173 f.Close() 174 if err != nil { 175 t.Fatalf("err: %s", err) 176 } 177 } 178 179 // Setup the meta 180 m := testMetaBackend(t, nil) 181 m.statePath = statePath 182 183 // Get the backend 184 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 185 if diags.HasErrors() { 186 t.Fatal(diags.Err()) 187 } 188 189 // Check the state 190 s, err := b.StateMgr(backend.DefaultStateName) 191 if err != nil { 192 t.Fatalf("unexpected error: %s", err) 193 } 194 if err := s.RefreshState(); err != nil { 195 t.Fatalf("err: %s", err) 196 } 197 if actual := s.State().String(); actual != testState().String() { 198 t.Fatalf("bad: %s", actual) 199 } 200 201 // Verify neither defaults exist 202 if _, err := os.Stat(DefaultStateFilename); err == nil { 203 t.Fatal("file should not exist") 204 } 205 206 stateName := filepath.Join(m.DataDir(), DefaultStateFilename) 207 if !isEmptyState(stateName) { 208 t.Fatal("expected no state at", stateName) 209 } 210 211 // Write some state 212 next := testState() 213 markStateForMatching(next, "bar") // just any change so it shows as different than before 214 s.WriteState(next) 215 if err := s.PersistState(nil); err != nil { 216 t.Fatalf("unexpected error: %s", err) 217 } 218 219 // Verify a backup was made since we're modifying a pre-existing state 220 if isEmptyState(statePath + DefaultBackupExtension) { 221 t.Fatal("backup state should not be empty") 222 } 223 } 224 225 // Verify that interpolations result in an error 226 func TestMetaBackend_configureInterpolation(t *testing.T) { 227 // Create a temporary working directory that is empty 228 td := t.TempDir() 229 testCopyDir(t, testFixturePath("backend-new-interp"), td) 230 defer testChdir(t, td)() 231 232 // Setup the meta 233 m := testMetaBackend(t, nil) 234 235 // Get the backend 236 _, err := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 237 if err == nil { 238 t.Fatal("should error") 239 } 240 } 241 242 // Newly configured backend 243 func TestMetaBackend_configureNew(t *testing.T) { 244 td := t.TempDir() 245 testCopyDir(t, testFixturePath("backend-new"), td) 246 defer testChdir(t, td)() 247 248 // Setup the meta 249 m := testMetaBackend(t, nil) 250 251 // Get the backend 252 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 253 if diags.HasErrors() { 254 t.Fatal(diags.Err()) 255 } 256 257 // Check the state 258 s, err := b.StateMgr(backend.DefaultStateName) 259 if err != nil { 260 t.Fatalf("unexpected error: %s", err) 261 } 262 if err := s.RefreshState(); err != nil { 263 t.Fatalf("unexpected error: %s", err) 264 } 265 state := s.State() 266 if state != nil { 267 t.Fatal("state should be nil") 268 } 269 270 // Write some state 271 state = states.NewState() 272 mark := markStateForMatching(state, "changing") 273 274 s.WriteState(state) 275 if err := s.PersistState(nil); err != nil { 276 t.Fatalf("unexpected error: %s", err) 277 } 278 279 // Verify the state is where we expect 280 { 281 f, err := os.Open("local-state.tfstate") 282 if err != nil { 283 t.Fatalf("err: %s", err) 284 } 285 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 286 f.Close() 287 if err != nil { 288 t.Fatalf("err: %s", err) 289 } 290 291 assertStateHasMarker(t, actual.State, mark) 292 } 293 294 // Verify the default paths don't exist 295 if _, err := os.Stat(DefaultStateFilename); err == nil { 296 t.Fatal("file should not exist") 297 } 298 299 // Verify a backup doesn't exist 300 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 301 t.Fatal("file should not exist") 302 } 303 } 304 305 // Newly configured backend with prior local state and no remote state 306 func TestMetaBackend_configureNewWithState(t *testing.T) { 307 // Create a temporary working directory that is empty 308 td := t.TempDir() 309 testCopyDir(t, testFixturePath("backend-new-migrate"), td) 310 defer testChdir(t, td)() 311 312 // Ask input 313 defer testInteractiveInput(t, []string{"yes"})() 314 315 // Setup the meta 316 m := testMetaBackend(t, nil) 317 318 // This combination should not require the extra -migrate-state flag, since 319 // there is no existing backend config 320 m.migrateState = false 321 322 // Get the backend 323 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 324 if diags.HasErrors() { 325 t.Fatal(diags.Err()) 326 } 327 328 // Check the state 329 s, err := b.StateMgr(backend.DefaultStateName) 330 if err != nil { 331 t.Fatalf("unexpected error: %s", err) 332 } 333 state, err := statemgr.RefreshAndRead(s) 334 if err != nil { 335 t.Fatalf("unexpected error: %s", err) 336 } 337 if state == nil { 338 t.Fatal("state is nil") 339 } 340 341 if got, want := testStateMgrCurrentLineage(s), "backend-new-migrate"; got != want { 342 t.Fatalf("lineage changed during migration\nnow: %s\nwas: %s", got, want) 343 } 344 345 // Write some state 346 state = states.NewState() 347 mark := markStateForMatching(state, "changing") 348 349 if err := statemgr.WriteAndPersist(s, state, nil); err != nil { 350 t.Fatalf("unexpected error: %s", err) 351 } 352 353 // Verify the state is where we expect 354 { 355 f, err := os.Open("local-state.tfstate") 356 if err != nil { 357 t.Fatalf("err: %s", err) 358 } 359 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 360 f.Close() 361 if err != nil { 362 t.Fatalf("err: %s", err) 363 } 364 365 assertStateHasMarker(t, actual.State, mark) 366 } 367 368 // Verify the default paths don't exist 369 if !isEmptyState(DefaultStateFilename) { 370 data, _ := os.ReadFile(DefaultStateFilename) 371 372 t.Fatal("state should not exist, but contains:\n", string(data)) 373 } 374 375 // Verify a backup does exist 376 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 377 t.Fatal("backup state is empty or missing") 378 } 379 } 380 381 // Newly configured backend with matching local and remote state doesn't prompt 382 // for copy. 383 func TestMetaBackend_configureNewWithoutCopy(t *testing.T) { 384 // Create a temporary working directory that is empty 385 td := t.TempDir() 386 testCopyDir(t, testFixturePath("backend-new-migrate"), td) 387 defer testChdir(t, td)() 388 389 if err := copy.CopyFile(DefaultStateFilename, "local-state.tfstate"); err != nil { 390 t.Fatal(err) 391 } 392 393 // Setup the meta 394 m := testMetaBackend(t, nil) 395 m.input = false 396 397 // init the backend 398 _, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 399 if diags.HasErrors() { 400 t.Fatal(diags.Err()) 401 } 402 403 // Verify the state is where we expect 404 f, err := os.Open("local-state.tfstate") 405 if err != nil { 406 t.Fatalf("err: %s", err) 407 } 408 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 409 f.Close() 410 if err != nil { 411 t.Fatalf("err: %s", err) 412 } 413 414 if actual.Lineage != "backend-new-migrate" { 415 t.Fatalf("incorrect state lineage: %q", actual.Lineage) 416 } 417 418 // Verify the default paths don't exist 419 if !isEmptyState(DefaultStateFilename) { 420 data, _ := os.ReadFile(DefaultStateFilename) 421 422 t.Fatal("state should not exist, but contains:\n", string(data)) 423 } 424 425 // Verify a backup does exist 426 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 427 t.Fatal("backup state is empty or missing") 428 } 429 } 430 431 // Newly configured backend with prior local state and no remote state, 432 // but opting to not migrate. 433 func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) { 434 // Create a temporary working directory that is empty 435 td := t.TempDir() 436 testCopyDir(t, testFixturePath("backend-new-migrate"), td) 437 defer testChdir(t, td)() 438 439 // Ask input 440 defer testInteractiveInput(t, []string{"no"})() 441 442 // Setup the meta 443 m := testMetaBackend(t, nil) 444 445 // Get the backend 446 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 447 if diags.HasErrors() { 448 t.Fatal(diags.Err()) 449 } 450 451 // Check the state 452 s, err := b.StateMgr(backend.DefaultStateName) 453 if err != nil { 454 t.Fatalf("unexpected error: %s", err) 455 } 456 if err := s.RefreshState(); err != nil { 457 t.Fatalf("unexpected error: %s", err) 458 } 459 if state := s.State(); state != nil { 460 t.Fatal("state is not nil") 461 } 462 463 // Verify the default paths don't exist 464 if !isEmptyState(DefaultStateFilename) { 465 data, _ := os.ReadFile(DefaultStateFilename) 466 467 t.Fatal("state should not exist, but contains:\n", string(data)) 468 } 469 470 // Verify a backup does exist 471 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 472 t.Fatal("backup state is empty or missing") 473 } 474 } 475 476 // Newly configured backend with prior local state and remote state 477 func TestMetaBackend_configureNewWithStateExisting(t *testing.T) { 478 // Create a temporary working directory that is empty 479 td := t.TempDir() 480 testCopyDir(t, testFixturePath("backend-new-migrate-existing"), td) 481 defer testChdir(t, td)() 482 483 // Setup the meta 484 m := testMetaBackend(t, nil) 485 // suppress input 486 m.forceInitCopy = true 487 488 // Get the backend 489 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 490 if diags.HasErrors() { 491 t.Fatal(diags.Err()) 492 } 493 494 // Check the state 495 s, err := b.StateMgr(backend.DefaultStateName) 496 if err != nil { 497 t.Fatalf("unexpected error: %s", err) 498 } 499 if err := s.RefreshState(); err != nil { 500 t.Fatalf("unexpected error: %s", err) 501 } 502 state := s.State() 503 if state == nil { 504 t.Fatal("state is nil") 505 } 506 if got, want := testStateMgrCurrentLineage(s), "local"; got != want { 507 t.Fatalf("wrong lineage %q; want %q", got, want) 508 } 509 510 // Write some state 511 state = states.NewState() 512 mark := markStateForMatching(state, "changing") 513 514 s.WriteState(state) 515 if err := s.PersistState(nil); err != nil { 516 t.Fatalf("unexpected error: %s", err) 517 } 518 519 // Verify the state is where we expect 520 { 521 f, err := os.Open("local-state.tfstate") 522 if err != nil { 523 t.Fatalf("err: %s", err) 524 } 525 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 526 f.Close() 527 if err != nil { 528 t.Fatalf("err: %s", err) 529 } 530 531 assertStateHasMarker(t, actual.State, mark) 532 } 533 534 // Verify the default paths don't exist 535 if !isEmptyState(DefaultStateFilename) { 536 data, _ := os.ReadFile(DefaultStateFilename) 537 538 t.Fatal("state should not exist, but contains:\n", string(data)) 539 } 540 541 // Verify a backup does exist 542 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 543 t.Fatal("backup state is empty or missing") 544 } 545 } 546 547 // Newly configured backend with prior local state and remote state 548 func TestMetaBackend_configureNewWithStateExistingNoMigrate(t *testing.T) { 549 // Create a temporary working directory that is empty 550 td := t.TempDir() 551 testCopyDir(t, testFixturePath("backend-new-migrate-existing"), td) 552 defer testChdir(t, td)() 553 554 // Ask input 555 defer testInteractiveInput(t, []string{"no"})() 556 557 // Setup the meta 558 m := testMetaBackend(t, nil) 559 560 // Get the backend 561 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 562 if diags.HasErrors() { 563 t.Fatal(diags.Err()) 564 } 565 566 // Check the state 567 s, err := b.StateMgr(backend.DefaultStateName) 568 if err != nil { 569 t.Fatalf("unexpected error: %s", err) 570 } 571 if err := s.RefreshState(); err != nil { 572 t.Fatalf("unexpected error: %s", err) 573 } 574 state := s.State() 575 if state == nil { 576 t.Fatal("state is nil") 577 } 578 if testStateMgrCurrentLineage(s) != "remote" { 579 t.Fatalf("bad: %#v", state) 580 } 581 582 // Write some state 583 state = states.NewState() 584 mark := markStateForMatching(state, "changing") 585 s.WriteState(state) 586 if err := s.PersistState(nil); err != nil { 587 t.Fatalf("unexpected error: %s", err) 588 } 589 590 // Verify the state is where we expect 591 { 592 f, err := os.Open("local-state.tfstate") 593 if err != nil { 594 t.Fatalf("err: %s", err) 595 } 596 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 597 f.Close() 598 if err != nil { 599 t.Fatalf("err: %s", err) 600 } 601 602 assertStateHasMarker(t, actual.State, mark) 603 } 604 605 // Verify the default paths don't exist 606 if !isEmptyState(DefaultStateFilename) { 607 data, _ := os.ReadFile(DefaultStateFilename) 608 609 t.Fatal("state should not exist, but contains:\n", string(data)) 610 } 611 612 // Verify a backup does exist 613 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 614 t.Fatal("backup state is empty or missing") 615 } 616 } 617 618 // Saved backend state matching config 619 func TestMetaBackend_configuredUnchanged(t *testing.T) { 620 defer testChdir(t, testFixturePath("backend-unchanged"))() 621 622 // Setup the meta 623 m := testMetaBackend(t, nil) 624 625 // Get the backend 626 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 627 if diags.HasErrors() { 628 t.Fatal(diags.Err()) 629 } 630 631 // Check the state 632 s, err := b.StateMgr(backend.DefaultStateName) 633 if err != nil { 634 t.Fatalf("unexpected error: %s", err) 635 } 636 if err := s.RefreshState(); err != nil { 637 t.Fatalf("unexpected error: %s", err) 638 } 639 state := s.State() 640 if state == nil { 641 t.Fatal("nil state") 642 } 643 if testStateMgrCurrentLineage(s) != "configuredUnchanged" { 644 t.Fatalf("bad: %#v", state) 645 } 646 647 // Verify the default paths don't exist 648 if _, err := os.Stat(DefaultStateFilename); err == nil { 649 t.Fatal("file should not exist") 650 } 651 652 // Verify a backup doesn't exist 653 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 654 t.Fatal("file should not exist") 655 } 656 } 657 658 // Changing a configured backend 659 func TestMetaBackend_configuredChange(t *testing.T) { 660 // Create a temporary working directory that is empty 661 td := t.TempDir() 662 testCopyDir(t, testFixturePath("backend-change"), td) 663 defer testChdir(t, td)() 664 665 // Ask input 666 defer testInteractiveInput(t, []string{"no"})() 667 668 // Setup the meta 669 m := testMetaBackend(t, nil) 670 671 // Get the backend 672 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 673 if diags.HasErrors() { 674 t.Fatal(diags.Err()) 675 } 676 677 // Check the state 678 s, err := b.StateMgr(backend.DefaultStateName) 679 if err != nil { 680 t.Fatalf("unexpected error: %s", err) 681 } 682 if err := s.RefreshState(); err != nil { 683 t.Fatalf("unexpected error: %s", err) 684 } 685 state := s.State() 686 if state != nil { 687 t.Fatal("state should be nil") 688 } 689 690 // Verify the default paths don't exist 691 if _, err := os.Stat(DefaultStateFilename); err == nil { 692 t.Fatal("file should not exist") 693 } 694 695 // Verify a backup doesn't exist 696 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 697 t.Fatal("file should not exist") 698 } 699 700 // Write some state 701 state = states.NewState() 702 mark := markStateForMatching(state, "changing") 703 704 s.WriteState(state) 705 if err := s.PersistState(nil); err != nil { 706 t.Fatalf("unexpected error: %s", err) 707 } 708 709 // Verify the state is where we expect 710 { 711 f, err := os.Open("local-state-2.tfstate") 712 if err != nil { 713 t.Fatalf("err: %s", err) 714 } 715 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 716 f.Close() 717 if err != nil { 718 t.Fatalf("err: %s", err) 719 } 720 721 assertStateHasMarker(t, actual.State, mark) 722 } 723 724 // Verify no local state 725 if _, err := os.Stat(DefaultStateFilename); err == nil { 726 t.Fatal("file should not exist") 727 } 728 729 // Verify no local backup 730 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 731 t.Fatal("file should not exist") 732 } 733 } 734 735 // Reconfiguring with an already configured backend. 736 // This should ignore the existing backend config, and configure the new 737 // backend is if this is the first time. 738 func TestMetaBackend_reconfigureChange(t *testing.T) { 739 // Create a temporary working directory that is empty 740 td := t.TempDir() 741 testCopyDir(t, testFixturePath("backend-change-single-to-single"), td) 742 defer testChdir(t, td)() 743 744 // Register the single-state backend 745 backendInit.Set("local-single", backendLocal.TestNewLocalSingle) 746 defer backendInit.Set("local-single", nil) 747 748 // Setup the meta 749 m := testMetaBackend(t, nil) 750 751 // this should not ask for input 752 m.input = false 753 754 // cli flag -reconfigure 755 m.reconfigure = true 756 757 // Get the backend 758 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 759 if diags.HasErrors() { 760 t.Fatal(diags.Err()) 761 } 762 763 // Check the state 764 s, err := b.StateMgr(backend.DefaultStateName) 765 if err != nil { 766 t.Fatalf("unexpected error: %s", err) 767 } 768 if err := s.RefreshState(); err != nil { 769 t.Fatalf("unexpected error: %s", err) 770 } 771 newState := s.State() 772 if newState != nil || !newState.Empty() { 773 t.Fatal("state should be nil/empty after forced reconfiguration") 774 } 775 776 // verify that the old state is still there 777 s = statemgr.NewFilesystem("local-state.tfstate", encryption.StateEncryptionDisabled()) 778 if err := s.RefreshState(); err != nil { 779 t.Fatal(err) 780 } 781 oldState := s.State() 782 if oldState == nil || oldState.Empty() { 783 t.Fatal("original state should be untouched") 784 } 785 } 786 787 // Initializing a backend which supports workspaces and does *not* have 788 // the currently selected workspace should prompt the user with a list of 789 // workspaces to choose from to select a valid one, if more than one workspace 790 // is available. 791 func TestMetaBackend_initSelectedWorkspaceDoesNotExist(t *testing.T) { 792 // Create a temporary working directory that is empty 793 td := t.TempDir() 794 testCopyDir(t, testFixturePath("init-backend-selected-workspace-doesnt-exist-multi"), td) 795 defer testChdir(t, td)() 796 797 // Setup the meta 798 m := testMetaBackend(t, nil) 799 800 defer testInputMap(t, map[string]string{ 801 "select-workspace": "2", 802 })() 803 804 // Get the backend 805 _, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 806 if diags.HasErrors() { 807 t.Fatal(diags.Err()) 808 } 809 810 expected := "foo" 811 actual, err := m.Workspace() 812 if err != nil { 813 t.Fatal(err) 814 } 815 816 if actual != expected { 817 t.Fatalf("expected selected workspace to be %q, but was %q", expected, actual) 818 } 819 } 820 821 // Initializing a backend which supports workspaces and does *not* have the 822 // currently selected workspace - and which only has a single workspace - should 823 // automatically select that single workspace. 824 func TestMetaBackend_initSelectedWorkspaceDoesNotExistAutoSelect(t *testing.T) { 825 // Create a temporary working directory that is empty 826 td := t.TempDir() 827 testCopyDir(t, testFixturePath("init-backend-selected-workspace-doesnt-exist-single"), td) 828 defer testChdir(t, td)() 829 830 // Setup the meta 831 m := testMetaBackend(t, nil) 832 833 // this should not ask for input 834 m.input = false 835 836 // Assert test precondition: The current selected workspace is "bar" 837 previousName, err := m.Workspace() 838 if err != nil { 839 t.Fatal(err) 840 } 841 842 if previousName != "bar" { 843 t.Fatalf("expected test fixture to start with 'bar' as the current selected workspace") 844 } 845 846 // Get the backend 847 _, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 848 if diags.HasErrors() { 849 t.Fatal(diags.Err()) 850 } 851 852 expected := "default" 853 actual, err := m.Workspace() 854 if err != nil { 855 t.Fatal(err) 856 } 857 858 if actual != expected { 859 t.Fatalf("expected selected workspace to be %q, but was %q", expected, actual) 860 } 861 } 862 863 // Initializing a backend which supports workspaces and does *not* have 864 // the currently selected workspace with input=false should fail. 865 func TestMetaBackend_initSelectedWorkspaceDoesNotExistInputFalse(t *testing.T) { 866 // Create a temporary working directory that is empty 867 td := t.TempDir() 868 testCopyDir(t, testFixturePath("init-backend-selected-workspace-doesnt-exist-multi"), td) 869 defer testChdir(t, td)() 870 871 // Setup the meta 872 m := testMetaBackend(t, nil) 873 m.input = false 874 875 // Get the backend 876 _, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 877 878 // Should fail immediately 879 if got, want := diags.ErrWithWarnings().Error(), `Currently selected workspace "bar" does not exist`; !strings.Contains(got, want) { 880 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 881 } 882 } 883 884 // Changing a configured backend, copying state 885 func TestMetaBackend_configuredChangeCopy(t *testing.T) { 886 // Create a temporary working directory that is empty 887 td := t.TempDir() 888 testCopyDir(t, testFixturePath("backend-change"), td) 889 defer testChdir(t, td)() 890 891 // Ask input 892 defer testInteractiveInput(t, []string{"yes", "yes"})() 893 894 // Setup the meta 895 m := testMetaBackend(t, nil) 896 897 // Get the backend 898 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 899 if diags.HasErrors() { 900 t.Fatal(diags.Err()) 901 } 902 903 // Check the state 904 s, err := b.StateMgr(backend.DefaultStateName) 905 if err != nil { 906 t.Fatalf("unexpected error: %s", err) 907 } 908 if err := s.RefreshState(); err != nil { 909 t.Fatalf("unexpected error: %s", err) 910 } 911 state := s.State() 912 if state == nil { 913 t.Fatal("state should not be nil") 914 } 915 if testStateMgrCurrentLineage(s) != "backend-change" { 916 t.Fatalf("bad: %#v", state) 917 } 918 919 // Verify no local state 920 if _, err := os.Stat(DefaultStateFilename); err == nil { 921 t.Fatal("file should not exist") 922 } 923 924 // Verify no local backup 925 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 926 t.Fatal("file should not exist") 927 } 928 } 929 930 // Changing a configured backend that supports only single states to another 931 // backend that only supports single states. 932 func TestMetaBackend_configuredChangeCopy_singleState(t *testing.T) { 933 // Create a temporary working directory that is empty 934 td := t.TempDir() 935 testCopyDir(t, testFixturePath("backend-change-single-to-single"), td) 936 defer testChdir(t, td)() 937 938 // Register the single-state backend 939 backendInit.Set("local-single", backendLocal.TestNewLocalSingle) 940 defer backendInit.Set("local-single", nil) 941 942 // Ask input 943 defer testInputMap(t, map[string]string{ 944 "backend-migrate-copy-to-empty": "yes", 945 })() 946 947 // Setup the meta 948 m := testMetaBackend(t, nil) 949 950 // Get the backend 951 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 952 if diags.HasErrors() { 953 t.Fatal(diags.Err()) 954 } 955 956 // Check the state 957 s, err := b.StateMgr(backend.DefaultStateName) 958 if err != nil { 959 t.Fatalf("unexpected error: %s", err) 960 } 961 if err := s.RefreshState(); err != nil { 962 t.Fatalf("unexpected error: %s", err) 963 } 964 state := s.State() 965 if state == nil { 966 t.Fatal("state should not be nil") 967 } 968 if testStateMgrCurrentLineage(s) != "backend-change" { 969 t.Fatalf("bad: %#v", state) 970 } 971 972 // Verify no local state 973 if _, err := os.Stat(DefaultStateFilename); err == nil { 974 t.Fatal("file should not exist") 975 } 976 977 // Verify no local backup 978 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 979 t.Fatal("file should not exist") 980 } 981 } 982 983 // Changing a configured backend that supports multi-state to a 984 // backend that only supports single states. The multi-state only has 985 // a default state. 986 func TestMetaBackend_configuredChangeCopy_multiToSingleDefault(t *testing.T) { 987 // Create a temporary working directory that is empty 988 td := t.TempDir() 989 testCopyDir(t, testFixturePath("backend-change-multi-default-to-single"), td) 990 defer testChdir(t, td)() 991 992 // Register the single-state backend 993 backendInit.Set("local-single", backendLocal.TestNewLocalSingle) 994 defer backendInit.Set("local-single", nil) 995 996 // Ask input 997 defer testInputMap(t, map[string]string{ 998 "backend-migrate-copy-to-empty": "yes", 999 })() 1000 1001 // Setup the meta 1002 m := testMetaBackend(t, nil) 1003 1004 // Get the backend 1005 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1006 if diags.HasErrors() { 1007 t.Fatal(diags.Err()) 1008 } 1009 1010 // Check the state 1011 s, err := b.StateMgr(backend.DefaultStateName) 1012 if err != nil { 1013 t.Fatalf("unexpected error: %s", err) 1014 } 1015 if err := s.RefreshState(); err != nil { 1016 t.Fatalf("unexpected error: %s", err) 1017 } 1018 state := s.State() 1019 if state == nil { 1020 t.Fatal("state should not be nil") 1021 } 1022 if testStateMgrCurrentLineage(s) != "backend-change" { 1023 t.Fatalf("bad: %#v", state) 1024 } 1025 1026 // Verify no local state 1027 if _, err := os.Stat(DefaultStateFilename); err == nil { 1028 t.Fatal("file should not exist") 1029 } 1030 1031 // Verify no local backup 1032 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 1033 t.Fatal("file should not exist") 1034 } 1035 } 1036 1037 // Changing a configured backend that supports multi-state to a 1038 // backend that only supports single states. 1039 func TestMetaBackend_configuredChangeCopy_multiToSingle(t *testing.T) { 1040 // Create a temporary working directory that is empty 1041 td := t.TempDir() 1042 testCopyDir(t, testFixturePath("backend-change-multi-to-single"), td) 1043 defer testChdir(t, td)() 1044 1045 // Register the single-state backend 1046 backendInit.Set("local-single", backendLocal.TestNewLocalSingle) 1047 defer backendInit.Set("local-single", nil) 1048 1049 // Ask input 1050 defer testInputMap(t, map[string]string{ 1051 "backend-migrate-multistate-to-single": "yes", 1052 "backend-migrate-copy-to-empty": "yes", 1053 })() 1054 1055 // Setup the meta 1056 m := testMetaBackend(t, nil) 1057 1058 // Get the backend 1059 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1060 if diags.HasErrors() { 1061 t.Fatal(diags.Err()) 1062 } 1063 1064 // Check the state 1065 s, err := b.StateMgr(backend.DefaultStateName) 1066 if err != nil { 1067 t.Fatalf("unexpected error: %s", err) 1068 } 1069 if err := s.RefreshState(); err != nil { 1070 t.Fatalf("unexpected error: %s", err) 1071 } 1072 state := s.State() 1073 if state == nil { 1074 t.Fatal("state should not be nil") 1075 } 1076 if testStateMgrCurrentLineage(s) != "backend-change" { 1077 t.Fatalf("bad: %#v", state) 1078 } 1079 1080 // Verify no local state 1081 if _, err := os.Stat(DefaultStateFilename); err == nil { 1082 t.Fatal("file should not exist") 1083 } 1084 1085 // Verify no local backup 1086 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 1087 t.Fatal("file should not exist") 1088 } 1089 1090 // Verify existing workspaces exist 1091 envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename) 1092 if _, err := os.Stat(envPath); err != nil { 1093 t.Fatal("env should exist") 1094 } 1095 1096 // Verify we are now in the default env, or we may not be able to access the new backend 1097 env, err := m.Workspace() 1098 if err != nil { 1099 t.Fatal(err) 1100 } 1101 if env != backend.DefaultStateName { 1102 t.Fatal("using non-default env with single-env backend") 1103 } 1104 } 1105 1106 // Changing a configured backend that supports multi-state to a 1107 // backend that only supports single states. 1108 func TestMetaBackend_configuredChangeCopy_multiToSingleCurrentEnv(t *testing.T) { 1109 // Create a temporary working directory that is empty 1110 td := t.TempDir() 1111 testCopyDir(t, testFixturePath("backend-change-multi-to-single"), td) 1112 defer testChdir(t, td)() 1113 1114 // Register the single-state backend 1115 backendInit.Set("local-single", backendLocal.TestNewLocalSingle) 1116 defer backendInit.Set("local-single", nil) 1117 1118 // Ask input 1119 defer testInputMap(t, map[string]string{ 1120 "backend-migrate-multistate-to-single": "yes", 1121 "backend-migrate-copy-to-empty": "yes", 1122 })() 1123 1124 // Setup the meta 1125 m := testMetaBackend(t, nil) 1126 1127 // Change env 1128 if err := m.SetWorkspace("env2"); err != nil { 1129 t.Fatalf("unexpected error: %s", err) 1130 } 1131 1132 // Get the backend 1133 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1134 if diags.HasErrors() { 1135 t.Fatal(diags.Err()) 1136 } 1137 1138 // Check the state 1139 s, err := b.StateMgr(backend.DefaultStateName) 1140 if err != nil { 1141 t.Fatalf("unexpected error: %s", err) 1142 } 1143 if err := s.RefreshState(); err != nil { 1144 t.Fatalf("unexpected error: %s", err) 1145 } 1146 state := s.State() 1147 if state == nil { 1148 t.Fatal("state should not be nil") 1149 } 1150 if testStateMgrCurrentLineage(s) != "backend-change-env2" { 1151 t.Fatalf("bad: %#v", state) 1152 } 1153 1154 // Verify no local state 1155 if _, err := os.Stat(DefaultStateFilename); err == nil { 1156 t.Fatal("file should not exist") 1157 } 1158 1159 // Verify no local backup 1160 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 1161 t.Fatal("file should not exist") 1162 } 1163 1164 // Verify existing workspaces exist 1165 envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename) 1166 if _, err := os.Stat(envPath); err != nil { 1167 t.Fatal("env should exist") 1168 } 1169 } 1170 1171 // Changing a configured backend that supports multi-state to a 1172 // backend that also supports multi-state. 1173 func TestMetaBackend_configuredChangeCopy_multiToMulti(t *testing.T) { 1174 // Create a temporary working directory that is empty 1175 td := t.TempDir() 1176 testCopyDir(t, testFixturePath("backend-change-multi-to-multi"), td) 1177 defer testChdir(t, td)() 1178 1179 // Ask input 1180 defer testInputMap(t, map[string]string{ 1181 "backend-migrate-multistate-to-multistate": "yes", 1182 })() 1183 1184 // Setup the meta 1185 m := testMetaBackend(t, nil) 1186 1187 // Get the backend 1188 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1189 if diags.HasErrors() { 1190 t.Fatal(diags.Err()) 1191 } 1192 1193 // Check resulting states 1194 workspaces, err := b.Workspaces() 1195 if err != nil { 1196 t.Fatalf("unexpected error: %s", err) 1197 } 1198 1199 sort.Strings(workspaces) 1200 expected := []string{"default", "env2"} 1201 if !reflect.DeepEqual(workspaces, expected) { 1202 t.Fatalf("bad: %#v", workspaces) 1203 } 1204 1205 { 1206 // Check the default state 1207 s, err := b.StateMgr(backend.DefaultStateName) 1208 if err != nil { 1209 t.Fatalf("unexpected error: %s", err) 1210 } 1211 if err := s.RefreshState(); err != nil { 1212 t.Fatalf("unexpected error: %s", err) 1213 } 1214 state := s.State() 1215 if state == nil { 1216 t.Fatal("state should not be nil") 1217 } 1218 if testStateMgrCurrentLineage(s) != "backend-change" { 1219 t.Fatalf("bad: %#v", state) 1220 } 1221 } 1222 1223 { 1224 // Check the other state 1225 s, err := b.StateMgr("env2") 1226 if err != nil { 1227 t.Fatalf("unexpected error: %s", err) 1228 } 1229 if err := s.RefreshState(); err != nil { 1230 t.Fatalf("unexpected error: %s", err) 1231 } 1232 state := s.State() 1233 if state == nil { 1234 t.Fatal("state should not be nil") 1235 } 1236 if testStateMgrCurrentLineage(s) != "backend-change-env2" { 1237 t.Fatalf("bad: %#v", state) 1238 } 1239 } 1240 1241 // Verify no local backup 1242 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 1243 t.Fatal("file should not exist") 1244 } 1245 1246 { 1247 // Verify existing workspaces exist 1248 envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename) 1249 if _, err := os.Stat(envPath); err != nil { 1250 t.Fatalf("%s should exist, but does not", envPath) 1251 } 1252 } 1253 1254 { 1255 // Verify new workspaces exist 1256 envPath := filepath.Join("envdir-new", "env2", backendLocal.DefaultStateFilename) 1257 if _, err := os.Stat(envPath); err != nil { 1258 t.Fatalf("%s should exist, but does not", envPath) 1259 } 1260 } 1261 } 1262 1263 // Changing a configured backend that supports multi-state to a 1264 // backend that also supports multi-state, but doesn't allow a 1265 // default state while the default state is non-empty. 1266 func TestMetaBackend_configuredChangeCopy_multiToNoDefaultWithDefault(t *testing.T) { 1267 // Create a temporary working directory that is empty 1268 td := t.TempDir() 1269 testCopyDir(t, testFixturePath("backend-change-multi-to-no-default-with-default"), td) 1270 defer testChdir(t, td)() 1271 1272 // Register the single-state backend 1273 backendInit.Set("local-no-default", backendLocal.TestNewLocalNoDefault) 1274 defer backendInit.Set("local-no-default", nil) 1275 1276 // Ask input 1277 defer testInputMap(t, map[string]string{ 1278 "backend-migrate-multistate-to-multistate": "yes", 1279 "new-state-name": "env1", 1280 })() 1281 1282 // Setup the meta 1283 m := testMetaBackend(t, nil) 1284 1285 // Get the backend 1286 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1287 if diags.HasErrors() { 1288 t.Fatal(diags.Err()) 1289 } 1290 1291 // Check resulting states 1292 workspaces, err := b.Workspaces() 1293 if err != nil { 1294 t.Fatalf("unexpected error: %s", err) 1295 } 1296 1297 sort.Strings(workspaces) 1298 expected := []string{"env1", "env2"} 1299 if !reflect.DeepEqual(workspaces, expected) { 1300 t.Fatalf("bad: %#v", workspaces) 1301 } 1302 1303 { 1304 // Check the renamed default state 1305 s, err := b.StateMgr("env1") 1306 if err != nil { 1307 t.Fatalf("unexpected error: %s", err) 1308 } 1309 if err := s.RefreshState(); err != nil { 1310 t.Fatalf("unexpected error: %s", err) 1311 } 1312 state := s.State() 1313 if state == nil { 1314 t.Fatal("state should not be nil") 1315 } 1316 if testStateMgrCurrentLineage(s) != "backend-change-env1" { 1317 t.Fatalf("bad: %#v", state) 1318 } 1319 } 1320 1321 { 1322 // Verify existing workspaces exist 1323 envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename) 1324 if _, err := os.Stat(envPath); err != nil { 1325 t.Fatal("env should exist") 1326 } 1327 } 1328 1329 { 1330 // Verify new workspaces exist 1331 envPath := filepath.Join("envdir-new", "env2", backendLocal.DefaultStateFilename) 1332 if _, err := os.Stat(envPath); err != nil { 1333 t.Fatal("env should exist") 1334 } 1335 } 1336 } 1337 1338 // Changing a configured backend that supports multi-state to a 1339 // backend that also supports multi-state, but doesn't allow a 1340 // default state while the default state is empty. 1341 func TestMetaBackend_configuredChangeCopy_multiToNoDefaultWithoutDefault(t *testing.T) { 1342 // Create a temporary working directory that is empty 1343 td := t.TempDir() 1344 testCopyDir(t, testFixturePath("backend-change-multi-to-no-default-without-default"), td) 1345 defer testChdir(t, td)() 1346 1347 // Register the single-state backend 1348 backendInit.Set("local-no-default", backendLocal.TestNewLocalNoDefault) 1349 defer backendInit.Set("local-no-default", nil) 1350 1351 // Ask input 1352 defer testInputMap(t, map[string]string{ 1353 "backend-migrate-multistate-to-multistate": "yes", 1354 })() 1355 1356 // Setup the meta 1357 m := testMetaBackend(t, nil) 1358 1359 // Get the backend 1360 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1361 if diags.HasErrors() { 1362 t.Fatal(diags.Err()) 1363 } 1364 1365 // Check resulting states 1366 workspaces, err := b.Workspaces() 1367 if err != nil { 1368 t.Fatalf("unexpected error: %s", err) 1369 } 1370 1371 sort.Strings(workspaces) 1372 expected := []string{"env2"} // default is skipped because it is absent in the source backend 1373 if !reflect.DeepEqual(workspaces, expected) { 1374 t.Fatalf("wrong workspaces\ngot: %#v\nwant: %#v", workspaces, expected) 1375 } 1376 1377 { 1378 // Check the named state 1379 s, err := b.StateMgr("env2") 1380 if err != nil { 1381 t.Fatalf("unexpected error: %s", err) 1382 } 1383 if err := s.RefreshState(); err != nil { 1384 t.Fatalf("unexpected error: %s", err) 1385 } 1386 state := s.State() 1387 if state == nil { 1388 t.Fatal("state should not be nil") 1389 } 1390 if testStateMgrCurrentLineage(s) != "backend-change-env2" { 1391 t.Fatalf("bad: %#v", state) 1392 } 1393 } 1394 1395 { 1396 // Verify existing workspaces exist 1397 envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename) 1398 if _, err := os.Stat(envPath); err != nil { 1399 t.Fatalf("%s should exist, but does not", envPath) 1400 } 1401 } 1402 1403 { 1404 // Verify new workspaces exist 1405 envPath := filepath.Join("envdir-new", "env2", backendLocal.DefaultStateFilename) 1406 if _, err := os.Stat(envPath); err != nil { 1407 t.Fatalf("%s should exist, but does not", envPath) 1408 } 1409 } 1410 } 1411 1412 // Unsetting a saved backend 1413 func TestMetaBackend_configuredUnset(t *testing.T) { 1414 // Create a temporary working directory that is empty 1415 td := t.TempDir() 1416 testCopyDir(t, testFixturePath("backend-unset"), td) 1417 defer testChdir(t, td)() 1418 1419 // Ask input 1420 defer testInteractiveInput(t, []string{"no"})() 1421 1422 // Setup the meta 1423 m := testMetaBackend(t, nil) 1424 1425 // Get the backend 1426 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1427 if diags.HasErrors() { 1428 t.Fatal(diags.Err()) 1429 } 1430 1431 // Check the state 1432 s, err := b.StateMgr(backend.DefaultStateName) 1433 if err != nil { 1434 t.Fatalf("unexpected error: %s", err) 1435 } 1436 if err := s.RefreshState(); err != nil { 1437 t.Fatalf("unexpected error: %s", err) 1438 } 1439 state := s.State() 1440 if state != nil { 1441 t.Fatal("state should be nil") 1442 } 1443 1444 // Verify the default paths don't exist 1445 if !isEmptyState(DefaultStateFilename) { 1446 data, _ := os.ReadFile(DefaultStateFilename) 1447 t.Fatal("state should not exist, but contains:\n", string(data)) 1448 } 1449 1450 // Verify a backup doesn't exist 1451 if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 1452 data, _ := os.ReadFile(DefaultStateFilename + DefaultBackupExtension) 1453 t.Fatal("backup should not exist, but contains:\n", string(data)) 1454 } 1455 1456 // Write some state 1457 s.WriteState(testState()) 1458 if err := s.PersistState(nil); err != nil { 1459 t.Fatalf("unexpected error: %s", err) 1460 } 1461 1462 // Verify it exists where we expect it to 1463 if isEmptyState(DefaultStateFilename) { 1464 t.Fatal(DefaultStateFilename, "is empty") 1465 } 1466 1467 // Verify no backup since it was empty to start 1468 if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 1469 data, _ := os.ReadFile(DefaultStateFilename + DefaultBackupExtension) 1470 t.Fatal("backup state should be empty, but contains:\n", string(data)) 1471 } 1472 } 1473 1474 // Unsetting a saved backend and copying the remote state 1475 func TestMetaBackend_configuredUnsetCopy(t *testing.T) { 1476 // Create a temporary working directory that is empty 1477 td := t.TempDir() 1478 testCopyDir(t, testFixturePath("backend-unset"), td) 1479 defer testChdir(t, td)() 1480 1481 // Ask input 1482 defer testInteractiveInput(t, []string{"yes", "yes"})() 1483 1484 // Setup the meta 1485 m := testMetaBackend(t, nil) 1486 1487 // Get the backend 1488 b, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1489 if diags.HasErrors() { 1490 t.Fatal(diags.Err()) 1491 } 1492 1493 // Check the state 1494 s, err := b.StateMgr(backend.DefaultStateName) 1495 if err != nil { 1496 t.Fatalf("unexpected error: %s", err) 1497 } 1498 if err := s.RefreshState(); err != nil { 1499 t.Fatalf("unexpected error: %s", err) 1500 } 1501 state := s.State() 1502 if state == nil { 1503 t.Fatal("state is nil") 1504 } 1505 if got, want := testStateMgrCurrentLineage(s), "configuredUnset"; got != want { 1506 t.Fatalf("wrong state lineage %q; want %q", got, want) 1507 } 1508 1509 // Verify a backup doesn't exist 1510 if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 1511 t.Fatalf("backup state should be empty") 1512 } 1513 1514 // Write some state 1515 s.WriteState(testState()) 1516 if err := s.PersistState(nil); err != nil { 1517 t.Fatalf("unexpected error: %s", err) 1518 } 1519 1520 // Verify it exists where we expect it to 1521 if _, err := os.Stat(DefaultStateFilename); err != nil { 1522 t.Fatalf("err: %s", err) 1523 } 1524 1525 // Verify a backup since it wasn't empty to start 1526 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 1527 t.Fatal("backup is empty") 1528 } 1529 } 1530 1531 // A plan that has uses the local backend 1532 func TestMetaBackend_planLocal(t *testing.T) { 1533 // Create a temporary working directory that is empty 1534 td := t.TempDir() 1535 testCopyDir(t, testFixturePath("backend-plan-local"), td) 1536 defer testChdir(t, td)() 1537 1538 backendConfigBlock := cty.ObjectVal(map[string]cty.Value{ 1539 "path": cty.NullVal(cty.String), 1540 "workspace_dir": cty.NullVal(cty.String), 1541 }) 1542 backendConfigRaw, err := plans.NewDynamicValue(backendConfigBlock, backendConfigBlock.Type()) 1543 if err != nil { 1544 t.Fatal(err) 1545 } 1546 backendConfig := plans.Backend{ 1547 Type: "local", 1548 Config: backendConfigRaw, 1549 Workspace: "default", 1550 } 1551 1552 // Setup the meta 1553 m := testMetaBackend(t, nil) 1554 1555 // Get the backend 1556 b, diags := m.BackendForLocalPlan(backendConfig, encryption.StateEncryptionDisabled()) 1557 if diags.HasErrors() { 1558 t.Fatal(diags.Err()) 1559 } 1560 1561 // Check the state 1562 s, err := b.StateMgr(backend.DefaultStateName) 1563 if err != nil { 1564 t.Fatalf("unexpected error: %s", err) 1565 } 1566 if err := s.RefreshState(); err != nil { 1567 t.Fatalf("unexpected error: %s", err) 1568 } 1569 state := s.State() 1570 if state != nil { 1571 t.Fatalf("state should be nil: %#v", state) 1572 } 1573 1574 // The default state file should not exist yet 1575 if !isEmptyState(DefaultStateFilename) { 1576 t.Fatal("expected empty state") 1577 } 1578 1579 // A backup file shouldn't exist yet either. 1580 if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 1581 t.Fatal("expected empty backup") 1582 } 1583 1584 // Verify we have no configured backend 1585 path := filepath.Join(m.DataDir(), DefaultStateFilename) 1586 if _, err := os.Stat(path); err == nil { 1587 t.Fatalf("should not have backend configured") 1588 } 1589 1590 // Write some state 1591 state = states.NewState() 1592 mark := markStateForMatching(state, "changing") 1593 1594 s.WriteState(state) 1595 if err := s.PersistState(nil); err != nil { 1596 t.Fatalf("unexpected error: %s", err) 1597 } 1598 1599 // Verify the state is where we expect 1600 { 1601 f, err := os.Open(DefaultStateFilename) 1602 if err != nil { 1603 t.Fatalf("err: %s", err) 1604 } 1605 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 1606 f.Close() 1607 if err != nil { 1608 t.Fatalf("err: %s", err) 1609 } 1610 1611 assertStateHasMarker(t, actual.State, mark) 1612 } 1613 1614 // Verify no local backup 1615 if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 1616 t.Fatalf("backup state should be empty") 1617 } 1618 } 1619 1620 // A plan with a custom state save path 1621 func TestMetaBackend_planLocalStatePath(t *testing.T) { 1622 td := t.TempDir() 1623 testCopyDir(t, testFixturePath("backend-plan-local"), td) 1624 defer testChdir(t, td)() 1625 1626 original := testState() 1627 markStateForMatching(original, "hello") 1628 1629 backendConfigBlock := cty.ObjectVal(map[string]cty.Value{ 1630 "path": cty.NullVal(cty.String), 1631 "workspace_dir": cty.NullVal(cty.String), 1632 }) 1633 backendConfigRaw, err := plans.NewDynamicValue(backendConfigBlock, backendConfigBlock.Type()) 1634 if err != nil { 1635 t.Fatal(err) 1636 } 1637 plannedBackend := plans.Backend{ 1638 Type: "local", 1639 Config: backendConfigRaw, 1640 Workspace: "default", 1641 } 1642 1643 // Create an alternate output path 1644 statePath := "foo.tfstate" 1645 1646 // put an initial state there that needs to be backed up 1647 err = statemgr.WriteAndPersist(statemgr.NewFilesystem(statePath, encryption.StateEncryptionDisabled()), original, nil) 1648 if err != nil { 1649 t.Fatal(err) 1650 } 1651 1652 // Setup the meta 1653 m := testMetaBackend(t, nil) 1654 m.stateOutPath = statePath 1655 1656 // Get the backend 1657 b, diags := m.BackendForLocalPlan(plannedBackend, encryption.StateEncryptionDisabled()) 1658 if diags.HasErrors() { 1659 t.Fatal(diags.Err()) 1660 } 1661 1662 // Check the state 1663 s, err := b.StateMgr(backend.DefaultStateName) 1664 if err != nil { 1665 t.Fatalf("unexpected error: %s", err) 1666 } 1667 if err := s.RefreshState(); err != nil { 1668 t.Fatalf("unexpected error: %s", err) 1669 } 1670 state := s.State() 1671 if state != nil { 1672 t.Fatal("default workspace state is not nil, but should be because we've not put anything there") 1673 } 1674 1675 // Verify the default path doesn't exist 1676 if _, err := os.Stat(DefaultStateFilename); err == nil { 1677 t.Fatalf("err: %s", err) 1678 } 1679 1680 // Verify a backup doesn't exists 1681 if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil { 1682 t.Fatal("file should not exist") 1683 } 1684 1685 // Verify we have no configured backend/legacy 1686 path := filepath.Join(m.DataDir(), DefaultStateFilename) 1687 if _, err := os.Stat(path); err == nil { 1688 t.Fatalf("should not have backend configured") 1689 } 1690 1691 // Write some state 1692 state = states.NewState() 1693 mark := markStateForMatching(state, "changing") 1694 1695 s.WriteState(state) 1696 if err := s.PersistState(nil); err != nil { 1697 t.Fatalf("unexpected error: %s", err) 1698 } 1699 1700 // Verify the state is where we expect 1701 { 1702 f, err := os.Open(statePath) 1703 if err != nil { 1704 t.Fatalf("err: %s", err) 1705 } 1706 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 1707 f.Close() 1708 if err != nil { 1709 t.Fatalf("err: %s", err) 1710 } 1711 1712 assertStateHasMarker(t, actual.State, mark) 1713 } 1714 1715 // Verify we have a backup 1716 if isEmptyState(statePath + DefaultBackupExtension) { 1717 t.Fatal("backup is empty") 1718 } 1719 } 1720 1721 // A plan that has no backend config, matching local state 1722 func TestMetaBackend_planLocalMatch(t *testing.T) { 1723 // Create a temporary working directory that is empty 1724 td := t.TempDir() 1725 testCopyDir(t, testFixturePath("backend-plan-local-match"), td) 1726 defer testChdir(t, td)() 1727 1728 backendConfigBlock := cty.ObjectVal(map[string]cty.Value{ 1729 "path": cty.NullVal(cty.String), 1730 "workspace_dir": cty.NullVal(cty.String), 1731 }) 1732 backendConfigRaw, err := plans.NewDynamicValue(backendConfigBlock, backendConfigBlock.Type()) 1733 if err != nil { 1734 t.Fatal(err) 1735 } 1736 backendConfig := plans.Backend{ 1737 Type: "local", 1738 Config: backendConfigRaw, 1739 Workspace: "default", 1740 } 1741 1742 // Setup the meta 1743 m := testMetaBackend(t, nil) 1744 1745 // Get the backend 1746 b, diags := m.BackendForLocalPlan(backendConfig, encryption.StateEncryptionDisabled()) 1747 if diags.HasErrors() { 1748 t.Fatal(diags.Err()) 1749 } 1750 1751 // Check the state 1752 s, err := b.StateMgr(backend.DefaultStateName) 1753 if err != nil { 1754 t.Fatalf("unexpected error: %s", err) 1755 } 1756 if err := s.RefreshState(); err != nil { 1757 t.Fatalf("unexpected error: %s", err) 1758 } 1759 state := s.State() 1760 if state == nil { 1761 t.Fatal("should is nil") 1762 } 1763 if testStateMgrCurrentLineage(s) != "hello" { 1764 t.Fatalf("bad: %#v", state) 1765 } 1766 1767 // Verify the default path 1768 if isEmptyState(DefaultStateFilename) { 1769 t.Fatal("state is empty") 1770 } 1771 1772 // Verify we have no configured backend/legacy 1773 path := filepath.Join(m.DataDir(), DefaultStateFilename) 1774 if _, err := os.Stat(path); err == nil { 1775 t.Fatalf("should not have backend configured") 1776 } 1777 1778 // Write some state 1779 state = states.NewState() 1780 mark := markStateForMatching(state, "changing") 1781 1782 s.WriteState(state) 1783 if err := s.PersistState(nil); err != nil { 1784 t.Fatalf("unexpected error: %s", err) 1785 } 1786 1787 // Verify the state is where we expect 1788 { 1789 f, err := os.Open(DefaultStateFilename) 1790 if err != nil { 1791 t.Fatalf("err: %s", err) 1792 } 1793 actual, err := statefile.Read(f, encryption.StateEncryptionDisabled()) 1794 f.Close() 1795 if err != nil { 1796 t.Fatalf("err: %s", err) 1797 } 1798 1799 assertStateHasMarker(t, actual.State, mark) 1800 } 1801 1802 // Verify local backup 1803 if isEmptyState(DefaultStateFilename + DefaultBackupExtension) { 1804 t.Fatal("backup is empty") 1805 } 1806 } 1807 1808 // init a backend using -backend-config options multiple times 1809 func TestMetaBackend_configureWithExtra(t *testing.T) { 1810 // Create a temporary working directory that is empty 1811 td := t.TempDir() 1812 testCopyDir(t, testFixturePath("init-backend-empty"), td) 1813 defer testChdir(t, td)() 1814 1815 extras := map[string]cty.Value{"path": cty.StringVal("hello")} 1816 m := testMetaBackend(t, nil) 1817 opts := &BackendOpts{ 1818 ConfigOverride: configs.SynthBody("synth", extras), 1819 Init: true, 1820 } 1821 1822 _, cHash, err := m.backendConfig(opts) 1823 if err != nil { 1824 t.Fatal(err) 1825 } 1826 1827 // init the backend 1828 _, diags := m.Backend(&BackendOpts{ 1829 ConfigOverride: configs.SynthBody("synth", extras), 1830 Init: true, 1831 }, encryption.StateEncryptionDisabled()) 1832 if diags.HasErrors() { 1833 t.Fatal(diags.Err()) 1834 } 1835 1836 // Check the state 1837 s := testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename)) 1838 if s.Backend.Hash != uint64(cHash) { 1839 t.Fatal("mismatched state and config backend hashes") 1840 } 1841 1842 // init the backend again with the same options 1843 m = testMetaBackend(t, nil) 1844 _, err = m.Backend(&BackendOpts{ 1845 ConfigOverride: configs.SynthBody("synth", extras), 1846 Init: true, 1847 }, encryption.StateEncryptionDisabled()) 1848 if err != nil { 1849 t.Fatalf("unexpected error: %s", err) 1850 } 1851 1852 // Check the state 1853 s = testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename)) 1854 if s.Backend.Hash != uint64(cHash) { 1855 t.Fatal("mismatched state and config backend hashes") 1856 } 1857 } 1858 1859 // when configuring a default local state, don't delete local state 1860 func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) { 1861 // Create a temporary working directory that is empty 1862 td := t.TempDir() 1863 testCopyDir(t, testFixturePath("init-backend-empty"), td) 1864 defer testChdir(t, td)() 1865 1866 //// create our local state 1867 orig := states.NewState() 1868 orig.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false) 1869 testStateFileDefault(t, orig) 1870 1871 m := testMetaBackend(t, nil) 1872 m.forceInitCopy = true 1873 // init the backend 1874 _, diags := m.Backend(&BackendOpts{Init: true}, encryption.StateEncryptionDisabled()) 1875 if diags.HasErrors() { 1876 t.Fatal(diags.Err()) 1877 } 1878 1879 // check that we can read the state 1880 s := testStateRead(t, DefaultStateFilename) 1881 if s.Empty() { 1882 t.Fatal("our state was deleted") 1883 } 1884 } 1885 1886 // move options from config to -backend-config 1887 func TestMetaBackend_configToExtra(t *testing.T) { 1888 // Create a temporary working directory that is empty 1889 td := t.TempDir() 1890 testCopyDir(t, testFixturePath("init-backend"), td) 1891 defer testChdir(t, td)() 1892 1893 // init the backend 1894 m := testMetaBackend(t, nil) 1895 _, err := m.Backend(&BackendOpts{ 1896 Init: true, 1897 }, encryption.StateEncryptionDisabled()) 1898 if err != nil { 1899 t.Fatalf("unexpected error: %s", err) 1900 } 1901 1902 // Check the state 1903 s := testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename)) 1904 backendHash := s.Backend.Hash 1905 1906 // init again but remove the path option from the config 1907 cfg := "terraform {\n backend \"local\" {}\n}\n" 1908 if err := os.WriteFile("main.tf", []byte(cfg), 0644); err != nil { 1909 t.Fatal(err) 1910 } 1911 1912 // init the backend again with the options 1913 extras := map[string]cty.Value{"path": cty.StringVal("hello")} 1914 m = testMetaBackend(t, nil) 1915 m.forceInitCopy = true 1916 _, diags := m.Backend(&BackendOpts{ 1917 ConfigOverride: configs.SynthBody("synth", extras), 1918 Init: true, 1919 }, encryption.StateEncryptionDisabled()) 1920 if diags.HasErrors() { 1921 t.Fatal(diags.Err()) 1922 } 1923 1924 s = testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename)) 1925 1926 if s.Backend.Hash == backendHash { 1927 t.Fatal("state.Backend.Hash was not updated") 1928 } 1929 } 1930 1931 // no config; return inmem backend stored in state 1932 func TestBackendFromState(t *testing.T) { 1933 wd := tempWorkingDirFixture(t, "backend-from-state") 1934 defer testChdir(t, wd.RootModuleDir())() 1935 1936 // Setup the meta 1937 m := testMetaBackend(t, nil) 1938 m.WorkingDir = wd 1939 // tofu caches a small "state" file that stores the backend config. 1940 // This test must override m.dataDir so it loads the "terraform.tfstate" file in the 1941 // test directory as the backend config cache. This fixture is really a 1942 // fixture for the data dir rather than the module dir, so we'll override 1943 // them to match just for this test. 1944 wd.OverrideDataDir(".") 1945 1946 stateBackend, diags := m.backendFromState(context.Background(), encryption.StateEncryptionDisabled()) 1947 if diags.HasErrors() { 1948 t.Fatal(diags.Err()) 1949 } 1950 1951 if _, ok := stateBackend.(*backendInmem.Backend); !ok { 1952 t.Fatal("did not get expected inmem backend") 1953 } 1954 } 1955 1956 func testMetaBackend(t *testing.T, args []string) *Meta { 1957 var m Meta 1958 m.Ui = new(cli.MockUi) 1959 view, _ := testView(t) 1960 m.View = view 1961 m.process(args) 1962 f := m.extendedFlagSet("test") 1963 if err := f.Parse(args); err != nil { 1964 t.Fatalf("unexpected error: %s", err) 1965 } 1966 1967 // metaBackend tests are verifying migrate actions 1968 m.migrateState = true 1969 1970 return &m 1971 }