github.com/pulumi/terraform@v1.4.0/pkg/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/pulumi/terraform/pkg/addrs"
    20  	"github.com/pulumi/terraform/pkg/configs/configschema"
    21  	"github.com/pulumi/terraform/pkg/providers"
    22  	"github.com/pulumi/terraform/pkg/states"
    23  	"github.com/pulumi/terraform/pkg/states/statefile"
    24  	"github.com/pulumi/terraform/pkg/states/statemgr"
    25  	"github.com/pulumi/terraform/pkg/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  `