kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/command/refresh_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "reflect" 10 "strings" 11 "testing" 12 13 "github.com/davecgh/go-spew/spew" 14 "github.com/google/go-cmp/cmp" 15 "github.com/google/go-cmp/cmp/cmpopts" 16 "github.com/mitchellh/cli" 17 "github.com/zclconf/go-cty/cty" 18 19 "kubeform.dev/terraform-backend-sdk/addrs" 20 "kubeform.dev/terraform-backend-sdk/configs/configschema" 21 "kubeform.dev/terraform-backend-sdk/providers" 22 "kubeform.dev/terraform-backend-sdk/states" 23 "kubeform.dev/terraform-backend-sdk/states/statefile" 24 "kubeform.dev/terraform-backend-sdk/states/statemgr" 25 "kubeform.dev/terraform-backend-sdk/tfdiags" 26 ) 27 28 var equateEmpty = cmpopts.EquateEmpty() 29 30 func TestRefresh(t *testing.T) { 31 // Create a temporary working directory that is empty 32 td := tempDir(t) 33 testCopyDir(t, testFixturePath("refresh"), td) 34 defer os.RemoveAll(td) 35 defer testChdir(t, td)() 36 37 state := testState() 38 statePath := testStateFile(t, state) 39 40 p := testProvider() 41 view, done := testView(t) 42 c := &RefreshCommand{ 43 Meta: Meta{ 44 testingOverrides: metaOverridesForProvider(p), 45 View: view, 46 }, 47 } 48 49 p.GetProviderSchemaResponse = refreshFixtureSchema() 50 p.ReadResourceFn = nil 51 p.ReadResourceResponse = &providers.ReadResourceResponse{ 52 NewState: cty.ObjectVal(map[string]cty.Value{ 53 "id": cty.StringVal("yes"), 54 }), 55 } 56 57 args := []string{ 58 "-state", statePath, 59 } 60 code := c.Run(args) 61 output := done(t) 62 if code != 0 { 63 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 64 } 65 66 if !p.ReadResourceCalled { 67 t.Fatal("ReadResource should have been called") 68 } 69 70 f, err := os.Open(statePath) 71 if err != nil { 72 t.Fatalf("err: %s", err) 73 } 74 75 newStateFile, err := statefile.Read(f) 76 f.Close() 77 if err != nil { 78 t.Fatalf("err: %s", err) 79 } 80 81 actual := strings.TrimSpace(newStateFile.State.String()) 82 expected := strings.TrimSpace(testRefreshStr) 83 if actual != expected { 84 t.Fatalf("bad:\n\n%s", actual) 85 } 86 } 87 88 func TestRefresh_empty(t *testing.T) { 89 // Create a temporary working directory that is empty 90 td := tempDir(t) 91 testCopyDir(t, testFixturePath("refresh-empty"), td) 92 defer os.RemoveAll(td) 93 defer testChdir(t, td)() 94 95 p := testProvider() 96 view, done := testView(t) 97 c := &RefreshCommand{ 98 Meta: Meta{ 99 testingOverrides: metaOverridesForProvider(p), 100 View: view, 101 }, 102 } 103 104 p.ReadResourceFn = nil 105 p.ReadResourceResponse = &providers.ReadResourceResponse{ 106 NewState: cty.ObjectVal(map[string]cty.Value{ 107 "id": cty.StringVal("yes"), 108 }), 109 } 110 111 args := []string{} 112 code := c.Run(args) 113 output := done(t) 114 if code != 0 { 115 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 116 } 117 118 if p.ReadResourceCalled { 119 t.Fatal("ReadResource should not have been called") 120 } 121 } 122 123 func TestRefresh_lockedState(t *testing.T) { 124 // Create a temporary working directory that is empty 125 td := tempDir(t) 126 testCopyDir(t, testFixturePath("refresh"), td) 127 defer os.RemoveAll(td) 128 defer testChdir(t, td)() 129 130 state := testState() 131 statePath := testStateFile(t, state) 132 133 unlock, err := testLockState(testDataDir, statePath) 134 if err != nil { 135 t.Fatal(err) 136 } 137 defer unlock() 138 139 p := testProvider() 140 view, done := testView(t) 141 c := &RefreshCommand{ 142 Meta: Meta{ 143 testingOverrides: metaOverridesForProvider(p), 144 View: view, 145 }, 146 } 147 148 p.GetProviderSchemaResponse = refreshFixtureSchema() 149 p.ReadResourceFn = nil 150 p.ReadResourceResponse = &providers.ReadResourceResponse{ 151 NewState: cty.ObjectVal(map[string]cty.Value{ 152 "id": cty.StringVal("yes"), 153 }), 154 } 155 156 args := []string{ 157 "-state", statePath, 158 } 159 160 code := c.Run(args) 161 output := done(t) 162 if code == 0 { 163 t.Fatal("expected error") 164 } 165 166 got := output.Stderr() 167 if !strings.Contains(got, "lock") { 168 t.Fatal("command output does not look like a lock error:", got) 169 } 170 } 171 172 func TestRefresh_cwd(t *testing.T) { 173 cwd, err := os.Getwd() 174 if err != nil { 175 t.Fatalf("err: %s", err) 176 } 177 if err := os.Chdir(testFixturePath("refresh")); err != nil { 178 t.Fatalf("err: %s", err) 179 } 180 defer os.Chdir(cwd) 181 182 state := testState() 183 statePath := testStateFile(t, state) 184 185 p := testProvider() 186 view, done := testView(t) 187 c := &RefreshCommand{ 188 Meta: Meta{ 189 testingOverrides: metaOverridesForProvider(p), 190 View: view, 191 }, 192 } 193 194 p.GetProviderSchemaResponse = refreshFixtureSchema() 195 p.ReadResourceFn = nil 196 p.ReadResourceResponse = &providers.ReadResourceResponse{ 197 NewState: cty.ObjectVal(map[string]cty.Value{ 198 "id": cty.StringVal("yes"), 199 }), 200 } 201 202 args := []string{ 203 "-state", statePath, 204 } 205 code := c.Run(args) 206 output := done(t) 207 if code != 0 { 208 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 209 } 210 211 if !p.ReadResourceCalled { 212 t.Fatal("ReadResource should have been called") 213 } 214 215 f, err := os.Open(statePath) 216 if err != nil { 217 t.Fatalf("err: %s", err) 218 } 219 220 newStateFile, err := statefile.Read(f) 221 f.Close() 222 if err != nil { 223 t.Fatalf("err: %s", err) 224 } 225 226 actual := strings.TrimSpace(newStateFile.State.String()) 227 expected := strings.TrimSpace(testRefreshCwdStr) 228 if actual != expected { 229 t.Fatalf("bad:\n\n%s", actual) 230 } 231 } 232 233 func TestRefresh_defaultState(t *testing.T) { 234 // Create a temporary working directory that is empty 235 td := tempDir(t) 236 testCopyDir(t, testFixturePath("refresh"), td) 237 defer os.RemoveAll(td) 238 defer testChdir(t, td)() 239 240 originalState := testState() 241 242 // Write the state file in a temporary directory with the 243 // default filename. 244 statePath := testStateFile(t, originalState) 245 246 localState := statemgr.NewFilesystem(statePath) 247 if err := localState.RefreshState(); err != nil { 248 t.Fatal(err) 249 } 250 s := localState.State() 251 if s == nil { 252 t.Fatal("empty test state") 253 } 254 255 // Change to that directory 256 cwd, err := os.Getwd() 257 if err != nil { 258 t.Fatalf("err: %s", err) 259 } 260 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 261 t.Fatalf("err: %s", err) 262 } 263 defer os.Chdir(cwd) 264 265 p := testProvider() 266 view, done := testView(t) 267 c := &RefreshCommand{ 268 Meta: Meta{ 269 testingOverrides: metaOverridesForProvider(p), 270 View: view, 271 }, 272 } 273 274 p.GetProviderSchemaResponse = refreshFixtureSchema() 275 p.ReadResourceFn = nil 276 p.ReadResourceResponse = &providers.ReadResourceResponse{ 277 NewState: cty.ObjectVal(map[string]cty.Value{ 278 "id": cty.StringVal("yes"), 279 }), 280 } 281 282 args := []string{ 283 "-state", statePath, 284 } 285 code := c.Run(args) 286 output := done(t) 287 if code != 0 { 288 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 289 } 290 291 if !p.ReadResourceCalled { 292 t.Fatal("ReadResource should have been called") 293 } 294 295 newState := testStateRead(t, statePath) 296 297 actual := newState.RootModule().Resources["test_instance.foo"].Instances[addrs.NoKey].Current 298 expected := &states.ResourceInstanceObjectSrc{ 299 Status: states.ObjectReady, 300 AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"), 301 Dependencies: []addrs.ConfigResource{}, 302 } 303 if !reflect.DeepEqual(actual, expected) { 304 t.Fatalf("wrong new object\ngot: %swant: %s", spew.Sdump(actual), spew.Sdump(expected)) 305 } 306 307 backupState := testStateRead(t, statePath+DefaultBackupExtension) 308 309 actual = backupState.RootModule().Resources["test_instance.foo"].Instances[addrs.NoKey].Current 310 expected = originalState.RootModule().Resources["test_instance.foo"].Instances[addrs.NoKey].Current 311 if !reflect.DeepEqual(actual, expected) { 312 t.Fatalf("wrong new object\ngot: %swant: %s", spew.Sdump(actual), spew.Sdump(expected)) 313 } 314 } 315 316 func TestRefresh_outPath(t *testing.T) { 317 // Create a temporary working directory that is empty 318 td := tempDir(t) 319 testCopyDir(t, testFixturePath("refresh"), td) 320 defer os.RemoveAll(td) 321 defer testChdir(t, td)() 322 323 state := testState() 324 statePath := testStateFile(t, state) 325 326 // Output path 327 outf, err := ioutil.TempFile(testingDir, "tf") 328 if err != nil { 329 t.Fatalf("err: %s", err) 330 } 331 outPath := outf.Name() 332 outf.Close() 333 os.Remove(outPath) 334 335 p := testProvider() 336 view, done := testView(t) 337 c := &RefreshCommand{ 338 Meta: Meta{ 339 testingOverrides: metaOverridesForProvider(p), 340 View: view, 341 }, 342 } 343 344 p.GetProviderSchemaResponse = refreshFixtureSchema() 345 p.ReadResourceFn = nil 346 p.ReadResourceResponse = &providers.ReadResourceResponse{ 347 NewState: cty.ObjectVal(map[string]cty.Value{ 348 "id": cty.StringVal("yes"), 349 }), 350 } 351 352 args := []string{ 353 "-state", statePath, 354 "-state-out", outPath, 355 } 356 code := c.Run(args) 357 output := done(t) 358 if code != 0 { 359 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 360 } 361 362 newState := testStateRead(t, statePath) 363 if !reflect.DeepEqual(newState, state) { 364 t.Fatalf("bad: %#v", newState) 365 } 366 367 newState = testStateRead(t, outPath) 368 actual := newState.RootModule().Resources["test_instance.foo"].Instances[addrs.NoKey].Current 369 expected := &states.ResourceInstanceObjectSrc{ 370 Status: states.ObjectReady, 371 AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"), 372 Dependencies: []addrs.ConfigResource{}, 373 } 374 if !reflect.DeepEqual(actual, expected) { 375 t.Fatalf("wrong new object\ngot: %swant: %s", spew.Sdump(actual), spew.Sdump(expected)) 376 } 377 378 if _, err := os.Stat(outPath + DefaultBackupExtension); !os.IsNotExist(err) { 379 if err != nil { 380 t.Fatalf("failed to test for backup file: %s", err) 381 } 382 t.Fatalf("backup file exists, but it should not because output file did not initially exist") 383 } 384 } 385 386 func TestRefresh_var(t *testing.T) { 387 // Create a temporary working directory that is empty 388 td := tempDir(t) 389 testCopyDir(t, testFixturePath("refresh-var"), td) 390 defer os.RemoveAll(td) 391 defer testChdir(t, td)() 392 393 state := testState() 394 statePath := testStateFile(t, state) 395 396 p := testProvider() 397 view, done := testView(t) 398 c := &RefreshCommand{ 399 Meta: Meta{ 400 testingOverrides: metaOverridesForProvider(p), 401 View: view, 402 }, 403 } 404 p.GetProviderSchemaResponse = refreshVarFixtureSchema() 405 406 args := []string{ 407 "-var", "foo=bar", 408 "-state", statePath, 409 } 410 code := c.Run(args) 411 output := done(t) 412 if code != 0 { 413 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 414 } 415 416 if !p.ConfigureProviderCalled { 417 t.Fatal("configure should be called") 418 } 419 if got, want := p.ConfigureProviderRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { 420 t.Fatalf("wrong provider configuration\ngot: %#v\nwant: %#v", got, want) 421 } 422 } 423 424 func TestRefresh_varFile(t *testing.T) { 425 // Create a temporary working directory that is empty 426 td := tempDir(t) 427 testCopyDir(t, testFixturePath("refresh-var"), td) 428 defer os.RemoveAll(td) 429 defer testChdir(t, td)() 430 431 state := testState() 432 statePath := testStateFile(t, state) 433 434 p := testProvider() 435 view, done := testView(t) 436 c := &RefreshCommand{ 437 Meta: Meta{ 438 testingOverrides: metaOverridesForProvider(p), 439 View: view, 440 }, 441 } 442 p.GetProviderSchemaResponse = refreshVarFixtureSchema() 443 444 varFilePath := testTempFile(t) 445 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 446 t.Fatalf("err: %s", err) 447 } 448 449 args := []string{ 450 "-var-file", varFilePath, 451 "-state", statePath, 452 } 453 code := c.Run(args) 454 output := done(t) 455 if code != 0 { 456 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 457 } 458 459 if !p.ConfigureProviderCalled { 460 t.Fatal("configure should be called") 461 } 462 if got, want := p.ConfigureProviderRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { 463 t.Fatalf("wrong provider configuration\ngot: %#v\nwant: %#v", got, want) 464 } 465 } 466 467 func TestRefresh_varFileDefault(t *testing.T) { 468 // Create a temporary working directory that is empty 469 td := tempDir(t) 470 testCopyDir(t, testFixturePath("refresh-var"), td) 471 defer os.RemoveAll(td) 472 defer testChdir(t, td)() 473 474 state := testState() 475 statePath := testStateFile(t, state) 476 477 p := testProvider() 478 view, done := testView(t) 479 c := &RefreshCommand{ 480 Meta: Meta{ 481 testingOverrides: metaOverridesForProvider(p), 482 View: view, 483 }, 484 } 485 p.GetProviderSchemaResponse = refreshVarFixtureSchema() 486 487 varFilePath := filepath.Join(td, "terraform.tfvars") 488 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 489 t.Fatalf("err: %s", err) 490 } 491 492 args := []string{ 493 "-state", statePath, 494 } 495 code := c.Run(args) 496 output := done(t) 497 if code != 0 { 498 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 499 } 500 501 if !p.ConfigureProviderCalled { 502 t.Fatal("configure should be called") 503 } 504 if got, want := p.ConfigureProviderRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { 505 t.Fatalf("wrong provider configuration\ngot: %#v\nwant: %#v", got, want) 506 } 507 } 508 509 func TestRefresh_varsUnset(t *testing.T) { 510 // Create a temporary working directory that is empty 511 td := tempDir(t) 512 testCopyDir(t, testFixturePath("refresh-unset-var"), td) 513 defer os.RemoveAll(td) 514 defer testChdir(t, td)() 515 516 // Disable test mode so input would be asked 517 test = false 518 defer func() { test = true }() 519 520 defaultInputReader = bytes.NewBufferString("bar\n") 521 522 state := testState() 523 statePath := testStateFile(t, state) 524 525 p := testProvider() 526 ui := new(cli.MockUi) 527 view, done := testView(t) 528 c := &RefreshCommand{ 529 Meta: Meta{ 530 testingOverrides: metaOverridesForProvider(p), 531 Ui: ui, 532 View: view, 533 }, 534 } 535 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 536 ResourceTypes: map[string]providers.Schema{ 537 "test_instance": { 538 Block: &configschema.Block{ 539 Attributes: map[string]*configschema.Attribute{ 540 "id": {Type: cty.String, Optional: true, Computed: true}, 541 "ami": {Type: cty.String, Optional: true}, 542 }, 543 }, 544 }, 545 }, 546 } 547 548 args := []string{ 549 "-state", statePath, 550 } 551 code := c.Run(args) 552 output := done(t) 553 if code != 0 { 554 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 555 } 556 } 557 558 func TestRefresh_backup(t *testing.T) { 559 // Create a temporary working directory that is empty 560 td := tempDir(t) 561 testCopyDir(t, testFixturePath("refresh"), td) 562 defer os.RemoveAll(td) 563 defer testChdir(t, td)() 564 565 state := testState() 566 statePath := testStateFile(t, state) 567 568 // Output path 569 outf, err := ioutil.TempFile(testingDir, "tf") 570 if err != nil { 571 t.Fatalf("err: %s", err) 572 } 573 outPath := outf.Name() 574 defer outf.Close() 575 576 // Need to put some state content in the output file so that there's 577 // something to back up. 578 err = statefile.Write(statefile.New(state, "baz", 0), outf) 579 if err != nil { 580 t.Fatalf("error writing initial output state file %s", err) 581 } 582 583 // Backup path 584 backupf, err := ioutil.TempFile(testingDir, "tf") 585 if err != nil { 586 t.Fatalf("err: %s", err) 587 } 588 backupPath := backupf.Name() 589 backupf.Close() 590 os.Remove(backupPath) 591 592 p := testProvider() 593 view, done := testView(t) 594 c := &RefreshCommand{ 595 Meta: Meta{ 596 testingOverrides: metaOverridesForProvider(p), 597 View: view, 598 }, 599 } 600 601 p.GetProviderSchemaResponse = refreshFixtureSchema() 602 p.ReadResourceFn = nil 603 p.ReadResourceResponse = &providers.ReadResourceResponse{ 604 NewState: cty.ObjectVal(map[string]cty.Value{ 605 "id": cty.StringVal("changed"), 606 }), 607 } 608 609 args := []string{ 610 "-state", statePath, 611 "-state-out", outPath, 612 "-backup", backupPath, 613 } 614 code := c.Run(args) 615 output := done(t) 616 if code != 0 { 617 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 618 } 619 620 newState := testStateRead(t, statePath) 621 if !cmp.Equal(newState, state, cmpopts.EquateEmpty()) { 622 t.Fatalf("got:\n%s\nexpected:\n%s\n", newState, state) 623 } 624 625 newState = testStateRead(t, outPath) 626 actual := newState.RootModule().Resources["test_instance.foo"].Instances[addrs.NoKey].Current 627 expected := &states.ResourceInstanceObjectSrc{ 628 Status: states.ObjectReady, 629 AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"changed\"\n }"), 630 Dependencies: []addrs.ConfigResource{}, 631 } 632 if !reflect.DeepEqual(actual, expected) { 633 t.Fatalf("wrong new object\ngot: %swant: %s", spew.Sdump(actual), spew.Sdump(expected)) 634 } 635 636 backupState := testStateRead(t, backupPath) 637 actualStr := strings.TrimSpace(backupState.String()) 638 expectedStr := strings.TrimSpace(state.String()) 639 if actualStr != expectedStr { 640 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 641 } 642 } 643 644 func TestRefresh_disableBackup(t *testing.T) { 645 // Create a temporary working directory that is empty 646 td := tempDir(t) 647 testCopyDir(t, testFixturePath("refresh"), td) 648 defer os.RemoveAll(td) 649 defer testChdir(t, td)() 650 651 state := testState() 652 statePath := testStateFile(t, state) 653 654 // Output path 655 outf, err := ioutil.TempFile(testingDir, "tf") 656 if err != nil { 657 t.Fatalf("err: %s", err) 658 } 659 outPath := outf.Name() 660 outf.Close() 661 os.Remove(outPath) 662 663 p := testProvider() 664 view, done := testView(t) 665 c := &RefreshCommand{ 666 Meta: Meta{ 667 testingOverrides: metaOverridesForProvider(p), 668 View: view, 669 }, 670 } 671 672 p.GetProviderSchemaResponse = refreshFixtureSchema() 673 p.ReadResourceFn = nil 674 p.ReadResourceResponse = &providers.ReadResourceResponse{ 675 NewState: cty.ObjectVal(map[string]cty.Value{ 676 "id": cty.StringVal("yes"), 677 }), 678 } 679 680 args := []string{ 681 "-state", statePath, 682 "-state-out", outPath, 683 "-backup", "-", 684 } 685 code := c.Run(args) 686 output := done(t) 687 if code != 0 { 688 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 689 } 690 691 newState := testStateRead(t, statePath) 692 if !cmp.Equal(state, newState, equateEmpty) { 693 spew.Config.DisableMethods = true 694 fmt.Println(cmp.Diff(state, newState, equateEmpty)) 695 t.Fatalf("bad: %s", newState) 696 } 697 698 newState = testStateRead(t, outPath) 699 actual := newState.RootModule().Resources["test_instance.foo"].Instances[addrs.NoKey].Current 700 expected := &states.ResourceInstanceObjectSrc{ 701 Status: states.ObjectReady, 702 AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"), 703 Dependencies: []addrs.ConfigResource{}, 704 } 705 if !reflect.DeepEqual(actual, expected) { 706 t.Fatalf("wrong new object\ngot: %swant: %s", spew.Sdump(actual), spew.Sdump(expected)) 707 } 708 709 // Ensure there is no backup 710 _, err = os.Stat(outPath + DefaultBackupExtension) 711 if err == nil || !os.IsNotExist(err) { 712 t.Fatalf("backup should not exist") 713 } 714 _, err = os.Stat("-") 715 if err == nil || !os.IsNotExist(err) { 716 t.Fatalf("backup should not exist") 717 } 718 } 719 720 func TestRefresh_displaysOutputs(t *testing.T) { 721 // Create a temporary working directory that is empty 722 td := tempDir(t) 723 testCopyDir(t, testFixturePath("refresh-output"), td) 724 defer os.RemoveAll(td) 725 defer testChdir(t, td)() 726 727 state := testState() 728 statePath := testStateFile(t, state) 729 730 p := testProvider() 731 view, done := testView(t) 732 c := &RefreshCommand{ 733 Meta: Meta{ 734 testingOverrides: metaOverridesForProvider(p), 735 View: view, 736 }, 737 } 738 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 739 ResourceTypes: map[string]providers.Schema{ 740 "test_instance": { 741 Block: &configschema.Block{ 742 Attributes: map[string]*configschema.Attribute{ 743 "id": {Type: cty.String, Optional: true, Computed: true}, 744 "ami": {Type: cty.String, Optional: true}, 745 }, 746 }, 747 }, 748 }, 749 } 750 751 args := []string{ 752 "-state", statePath, 753 } 754 code := c.Run(args) 755 output := done(t) 756 if code != 0 { 757 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 758 } 759 760 // Test that outputs were displayed 761 outputValue := "foo.example.com" 762 actual := output.Stdout() 763 if !strings.Contains(actual, outputValue) { 764 t.Fatalf("Expected:\n%s\n\nTo include: %q", actual, outputValue) 765 } 766 } 767 768 // Config with multiple resources, targeting refresh of a subset 769 func TestRefresh_targeted(t *testing.T) { 770 td := tempDir(t) 771 testCopyDir(t, testFixturePath("refresh-targeted"), td) 772 defer os.RemoveAll(td) 773 defer testChdir(t, td)() 774 775 state := testState() 776 statePath := testStateFile(t, state) 777 778 p := testProvider() 779 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 780 ResourceTypes: map[string]providers.Schema{ 781 "test_instance": { 782 Block: &configschema.Block{ 783 Attributes: map[string]*configschema.Attribute{ 784 "id": {Type: cty.String, Computed: true}, 785 }, 786 }, 787 }, 788 }, 789 } 790 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 791 return providers.PlanResourceChangeResponse{ 792 PlannedState: req.ProposedNewState, 793 } 794 } 795 796 view, done := testView(t) 797 c := &RefreshCommand{ 798 Meta: Meta{ 799 testingOverrides: metaOverridesForProvider(p), 800 View: view, 801 }, 802 } 803 804 args := []string{ 805 "-target", "test_instance.foo", 806 "-state", statePath, 807 } 808 code := c.Run(args) 809 output := done(t) 810 if code != 0 { 811 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 812 } 813 814 got := output.Stdout() 815 if want := "test_instance.foo: Refreshing"; !strings.Contains(got, want) { 816 t.Fatalf("expected output to contain %q, got:\n%s", want, got) 817 } 818 if doNotWant := "test_instance.bar: Refreshing"; strings.Contains(got, doNotWant) { 819 t.Fatalf("expected output not to contain %q, got:\n%s", doNotWant, got) 820 } 821 } 822 823 // Diagnostics for invalid -target flags 824 func TestRefresh_targetFlagsDiags(t *testing.T) { 825 testCases := map[string]string{ 826 "test_instance.": "Dot must be followed by attribute name.", 827 "test_instance": "Resource specification must include a resource type and name.", 828 } 829 830 for target, wantDiag := range testCases { 831 t.Run(target, func(t *testing.T) { 832 td := testTempDir(t) 833 defer os.RemoveAll(td) 834 defer testChdir(t, td)() 835 836 view, done := testView(t) 837 c := &RefreshCommand{ 838 Meta: Meta{ 839 View: view, 840 }, 841 } 842 843 args := []string{ 844 "-target", target, 845 } 846 code := c.Run(args) 847 output := done(t) 848 if code != 1 { 849 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 850 } 851 852 got := output.Stderr() 853 if !strings.Contains(got, target) { 854 t.Fatalf("bad error output, want %q, got:\n%s", target, got) 855 } 856 if !strings.Contains(got, wantDiag) { 857 t.Fatalf("bad error output, want %q, got:\n%s", wantDiag, got) 858 } 859 }) 860 } 861 } 862 863 func TestRefresh_warnings(t *testing.T) { 864 // Create a temporary working directory that is empty 865 td := tempDir(t) 866 testCopyDir(t, testFixturePath("apply"), td) 867 defer os.RemoveAll(td) 868 defer testChdir(t, td)() 869 870 p := testProvider() 871 p.GetProviderSchemaResponse = refreshFixtureSchema() 872 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 873 return providers.PlanResourceChangeResponse{ 874 PlannedState: req.ProposedNewState, 875 Diagnostics: tfdiags.Diagnostics{ 876 tfdiags.SimpleWarning("warning 1"), 877 tfdiags.SimpleWarning("warning 2"), 878 }, 879 } 880 } 881 882 t.Run("full warnings", func(t *testing.T) { 883 view, done := testView(t) 884 c := &RefreshCommand{ 885 Meta: Meta{ 886 testingOverrides: metaOverridesForProvider(p), 887 View: view, 888 }, 889 } 890 891 code := c.Run([]string{}) 892 output := done(t) 893 if code != 0 { 894 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 895 } 896 wantWarnings := []string{ 897 "warning 1", 898 "warning 2", 899 } 900 for _, want := range wantWarnings { 901 if !strings.Contains(output.Stdout(), want) { 902 t.Errorf("missing warning %s", want) 903 } 904 } 905 }) 906 907 t.Run("compact warnings", func(t *testing.T) { 908 view, done := testView(t) 909 c := &RefreshCommand{ 910 Meta: Meta{ 911 testingOverrides: metaOverridesForProvider(p), 912 View: view, 913 }, 914 } 915 916 code := c.Run([]string{"-compact-warnings"}) 917 output := done(t) 918 if code != 0 { 919 t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) 920 } 921 // the output should contain 2 warnings and a message about -compact-warnings 922 wantWarnings := []string{ 923 "warning 1", 924 "warning 2", 925 "To see the full warning notes, run Terraform without -compact-warnings.", 926 } 927 for _, want := range wantWarnings { 928 if !strings.Contains(output.Stdout(), want) { 929 t.Errorf("missing warning %s", want) 930 } 931 } 932 }) 933 } 934 935 // configuration in testdata/refresh . This schema should be 936 // assigned to a mock provider named "test". 937 func refreshFixtureSchema() *providers.GetProviderSchemaResponse { 938 return &providers.GetProviderSchemaResponse{ 939 ResourceTypes: map[string]providers.Schema{ 940 "test_instance": { 941 Block: &configschema.Block{ 942 Attributes: map[string]*configschema.Attribute{ 943 "id": {Type: cty.String, Optional: true, Computed: true}, 944 "ami": {Type: cty.String, Optional: true}, 945 }, 946 }, 947 }, 948 }, 949 } 950 } 951 952 // refreshVarFixtureSchema returns a schema suitable for processing the 953 // configuration in testdata/refresh-var . This schema should be 954 // assigned to a mock provider named "test". 955 func refreshVarFixtureSchema() *providers.GetProviderSchemaResponse { 956 return &providers.GetProviderSchemaResponse{ 957 Provider: providers.Schema{ 958 Block: &configschema.Block{ 959 Attributes: map[string]*configschema.Attribute{ 960 "value": {Type: cty.String, Optional: true}, 961 }, 962 }, 963 }, 964 ResourceTypes: map[string]providers.Schema{ 965 "test_instance": { 966 Block: &configschema.Block{ 967 Attributes: map[string]*configschema.Attribute{ 968 "id": {Type: cty.String, Optional: true, Computed: true}, 969 }, 970 }, 971 }, 972 }, 973 } 974 } 975 976 const refreshVarFile = ` 977 foo = "bar" 978 ` 979 980 const testRefreshStr = ` 981 test_instance.foo: 982 ID = yes 983 provider = provider["registry.terraform.io/hashicorp/test"] 984 ` 985 const testRefreshCwdStr = ` 986 test_instance.foo: 987 ID = yes 988 provider = provider["registry.terraform.io/hashicorp/test"] 989 `