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