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