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