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