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