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