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