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  `