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