github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/command/apply_test.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/google/go-cmp/cmp"
    19  	"github.com/google/go-cmp/cmp/cmpopts"
    20  	"github.com/mitchellh/cli"
    21  	"github.com/zclconf/go-cty/cty"
    22  
    23  	"github.com/iaas-resource-provision/iaas-rpc/internal/addrs"
    24  	"github.com/iaas-resource-provision/iaas-rpc/internal/command/views"
    25  	"github.com/iaas-resource-provision/iaas-rpc/internal/configs/configschema"
    26  	"github.com/iaas-resource-provision/iaas-rpc/internal/plans"
    27  	"github.com/iaas-resource-provision/iaas-rpc/internal/providers"
    28  	"github.com/iaas-resource-provision/iaas-rpc/internal/states"
    29  	"github.com/iaas-resource-provision/iaas-rpc/internal/states/statemgr"
    30  	"github.com/iaas-resource-provision/iaas-rpc/internal/terraform"
    31  	tfversion "github.com/iaas-resource-provision/iaas-rpc/version"
    32  )
    33  
    34  func TestApply(t *testing.T) {
    35  	// Create a temporary working directory that is empty
    36  	td := tempDir(t)
    37  	testCopyDir(t, testFixturePath("apply"), td)
    38  	defer os.RemoveAll(td)
    39  	defer testChdir(t, td)()
    40  
    41  	statePath := testTempFile(t)
    42  
    43  	p := applyFixtureProvider()
    44  
    45  	view, done := testView(t)
    46  	c := &ApplyCommand{
    47  		Meta: Meta{
    48  			testingOverrides: metaOverridesForProvider(p),
    49  			View:             view,
    50  		},
    51  	}
    52  
    53  	args := []string{
    54  		"-state", statePath,
    55  		"-auto-approve",
    56  	}
    57  	code := c.Run(args)
    58  	output := done(t)
    59  	if code != 0 {
    60  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
    61  	}
    62  
    63  	if _, err := os.Stat(statePath); err != nil {
    64  		t.Fatalf("err: %s", err)
    65  	}
    66  
    67  	state := testStateRead(t, statePath)
    68  	if state == nil {
    69  		t.Fatal("state should not be nil")
    70  	}
    71  }
    72  
    73  func TestApply_path(t *testing.T) {
    74  	// Create a temporary working directory that is empty
    75  	td := tempDir(t)
    76  	testCopyDir(t, testFixturePath("apply"), td)
    77  	defer os.RemoveAll(td)
    78  	defer testChdir(t, td)()
    79  
    80  	p := applyFixtureProvider()
    81  
    82  	view, done := testView(t)
    83  	c := &ApplyCommand{
    84  		Meta: Meta{
    85  			testingOverrides: metaOverridesForProvider(p),
    86  			View:             view,
    87  		},
    88  	}
    89  
    90  	args := []string{
    91  		"-auto-approve",
    92  		testFixturePath("apply"),
    93  	}
    94  	code := c.Run(args)
    95  	output := done(t)
    96  	if code != 1 {
    97  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
    98  	}
    99  	if !strings.Contains(output.Stderr(), "-chdir") {
   100  		t.Fatal("expected command output to refer to -chdir flag, but got:", output.Stderr())
   101  	}
   102  }
   103  
   104  func TestApply_approveNo(t *testing.T) {
   105  	// Create a temporary working directory that is empty
   106  	td := tempDir(t)
   107  	testCopyDir(t, testFixturePath("apply"), td)
   108  	defer os.RemoveAll(td)
   109  	defer testChdir(t, td)()
   110  
   111  	statePath := testTempFile(t)
   112  
   113  	defer testInputMap(t, map[string]string{
   114  		"approve": "no",
   115  	})()
   116  
   117  	// Do not use the NewMockUi initializer here, as we want to delay
   118  	// the call to init until after setting up the input mocks
   119  	ui := new(cli.MockUi)
   120  
   121  	p := applyFixtureProvider()
   122  	view, done := testView(t)
   123  	c := &ApplyCommand{
   124  		Meta: Meta{
   125  			testingOverrides: metaOverridesForProvider(p),
   126  			Ui:               ui,
   127  			View:             view,
   128  		},
   129  	}
   130  
   131  	args := []string{
   132  		"-state", statePath,
   133  	}
   134  	code := c.Run(args)
   135  	output := done(t)
   136  	if code != 1 {
   137  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   138  	}
   139  	if got, want := output.Stdout(), "Apply cancelled"; !strings.Contains(got, want) {
   140  		t.Fatalf("expected output to include %q, but was:\n%s", want, got)
   141  	}
   142  
   143  	if _, err := os.Stat(statePath); err == nil || !os.IsNotExist(err) {
   144  		t.Fatalf("state file should not exist")
   145  	}
   146  }
   147  
   148  func TestApply_approveYes(t *testing.T) {
   149  	// Create a temporary working directory that is empty
   150  	td := tempDir(t)
   151  	testCopyDir(t, testFixturePath("apply"), td)
   152  	defer os.RemoveAll(td)
   153  	defer testChdir(t, td)()
   154  
   155  	statePath := testTempFile(t)
   156  
   157  	p := applyFixtureProvider()
   158  
   159  	defer testInputMap(t, map[string]string{
   160  		"approve": "yes",
   161  	})()
   162  
   163  	// Do not use the NewMockUi initializer here, as we want to delay
   164  	// the call to init until after setting up the input mocks
   165  	ui := new(cli.MockUi)
   166  
   167  	view, done := testView(t)
   168  	c := &ApplyCommand{
   169  		Meta: Meta{
   170  			testingOverrides: metaOverridesForProvider(p),
   171  			Ui:               ui,
   172  			View:             view,
   173  		},
   174  	}
   175  
   176  	args := []string{
   177  		"-state", statePath,
   178  	}
   179  	code := c.Run(args)
   180  	output := done(t)
   181  	if code != 0 {
   182  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   183  	}
   184  
   185  	if _, err := os.Stat(statePath); err != nil {
   186  		t.Fatalf("err: %s", err)
   187  	}
   188  
   189  	state := testStateRead(t, statePath)
   190  	if state == nil {
   191  		t.Fatal("state should not be nil")
   192  	}
   193  }
   194  
   195  // test apply with locked state
   196  func TestApply_lockedState(t *testing.T) {
   197  	// Create a temporary working directory that is empty
   198  	td := tempDir(t)
   199  	testCopyDir(t, testFixturePath("apply"), td)
   200  	defer os.RemoveAll(td)
   201  	defer testChdir(t, td)()
   202  
   203  	statePath := testTempFile(t)
   204  
   205  	unlock, err := testLockState(testDataDir, statePath)
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	defer unlock()
   210  
   211  	p := applyFixtureProvider()
   212  	view, done := testView(t)
   213  	c := &ApplyCommand{
   214  		Meta: Meta{
   215  			testingOverrides: metaOverridesForProvider(p),
   216  			View:             view,
   217  		},
   218  	}
   219  
   220  	args := []string{
   221  		"-state", statePath,
   222  		"-auto-approve",
   223  	}
   224  	code := c.Run(args)
   225  	output := done(t)
   226  	if code == 0 {
   227  		t.Fatal("expected error")
   228  	}
   229  
   230  	if !strings.Contains(output.Stderr(), "lock") {
   231  		t.Fatal("command output does not look like a lock error:", output.Stderr())
   232  	}
   233  }
   234  
   235  // test apply with locked state, waiting for unlock
   236  func TestApply_lockedStateWait(t *testing.T) {
   237  	// Create a temporary working directory that is empty
   238  	td := tempDir(t)
   239  	testCopyDir(t, testFixturePath("apply"), td)
   240  	defer os.RemoveAll(td)
   241  	defer testChdir(t, td)()
   242  
   243  	statePath := testTempFile(t)
   244  
   245  	unlock, err := testLockState(testDataDir, statePath)
   246  	if err != nil {
   247  		t.Fatal(err)
   248  	}
   249  
   250  	// unlock during apply
   251  	go func() {
   252  		time.Sleep(500 * time.Millisecond)
   253  		unlock()
   254  	}()
   255  
   256  	p := applyFixtureProvider()
   257  	view, done := testView(t)
   258  	c := &ApplyCommand{
   259  		Meta: Meta{
   260  			testingOverrides: metaOverridesForProvider(p),
   261  			View:             view,
   262  		},
   263  	}
   264  
   265  	// wait 4s just in case the lock process doesn't release in under a second,
   266  	// and we want our context to be alive for a second retry at the 3s mark.
   267  	args := []string{
   268  		"-state", statePath,
   269  		"-lock-timeout", "4s",
   270  		"-auto-approve",
   271  	}
   272  	code := c.Run(args)
   273  	output := done(t)
   274  	if code != 0 {
   275  		t.Fatalf("lock should have succeeded in less than 3s: %s", output.Stderr())
   276  	}
   277  }
   278  
   279  // Verify that the parallelism flag allows no more than the desired number of
   280  // concurrent calls to ApplyResourceChange.
   281  func TestApply_parallelism(t *testing.T) {
   282  	// Create a temporary working directory that is empty
   283  	td := tempDir(t)
   284  	testCopyDir(t, testFixturePath("parallelism"), td)
   285  	defer os.RemoveAll(td)
   286  	defer testChdir(t, td)()
   287  
   288  	statePath := testTempFile(t)
   289  
   290  	par := 4
   291  
   292  	// started is a semaphore that we use to ensure that we never have more
   293  	// than "par" apply operations happening concurrently
   294  	started := make(chan struct{}, par)
   295  
   296  	// beginCtx is used as a starting gate to hold back ApplyResourceChange
   297  	// calls until we reach the desired concurrency. The cancel func "begin" is
   298  	// called once we reach the desired concurrency, allowing all apply calls
   299  	// to proceed in unison.
   300  	beginCtx, begin := context.WithCancel(context.Background())
   301  
   302  	// Since our mock provider has its own mutex preventing concurrent calls
   303  	// to ApplyResourceChange, we need to use a number of separate providers
   304  	// here. They will all have the same mock implementation function assigned
   305  	// but crucially they will each have their own mutex.
   306  	providerFactories := map[addrs.Provider]providers.Factory{}
   307  	for i := 0; i < 10; i++ {
   308  		name := fmt.Sprintf("test%d", i)
   309  		provider := &terraform.MockProvider{}
   310  		provider.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
   311  			ResourceTypes: map[string]providers.Schema{
   312  				name + "_instance": {Block: &configschema.Block{}},
   313  			},
   314  		}
   315  		provider.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
   316  			return providers.PlanResourceChangeResponse{
   317  				PlannedState: req.ProposedNewState,
   318  			}
   319  		}
   320  		provider.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
   321  
   322  			// If we ever have more than our intended parallelism number of
   323  			// apply operations running concurrently, the semaphore will fail.
   324  			select {
   325  			case started <- struct{}{}:
   326  				defer func() {
   327  					<-started
   328  				}()
   329  			default:
   330  				t.Fatal("too many concurrent apply operations")
   331  			}
   332  
   333  			// If we never reach our intended parallelism, the context will
   334  			// never be canceled and the test will time out.
   335  			if len(started) >= par {
   336  				begin()
   337  			}
   338  			<-beginCtx.Done()
   339  
   340  			// do some "work"
   341  			// Not required for correctness, but makes it easier to spot a
   342  			// failure when there is more overlap.
   343  			time.Sleep(10 * time.Millisecond)
   344  
   345  			return providers.ApplyResourceChangeResponse{
   346  				NewState: cty.EmptyObjectVal,
   347  			}
   348  		}
   349  		providerFactories[addrs.NewDefaultProvider(name)] = providers.FactoryFixed(provider)
   350  	}
   351  	testingOverrides := &testingOverrides{
   352  		Providers: providerFactories,
   353  	}
   354  
   355  	view, done := testView(t)
   356  	c := &ApplyCommand{
   357  		Meta: Meta{
   358  			testingOverrides: testingOverrides,
   359  			View:             view,
   360  		},
   361  	}
   362  
   363  	args := []string{
   364  		"-state", statePath,
   365  		"-auto-approve",
   366  		fmt.Sprintf("-parallelism=%d", par),
   367  	}
   368  
   369  	res := c.Run(args)
   370  	output := done(t)
   371  	if res != 0 {
   372  		t.Fatal(output.Stdout())
   373  	}
   374  }
   375  
   376  func TestApply_configInvalid(t *testing.T) {
   377  	// Create a temporary working directory that is empty
   378  	td := tempDir(t)
   379  	testCopyDir(t, testFixturePath("apply-config-invalid"), td)
   380  	defer os.RemoveAll(td)
   381  	defer testChdir(t, td)()
   382  
   383  	p := testProvider()
   384  	view, done := testView(t)
   385  	c := &ApplyCommand{
   386  		Meta: Meta{
   387  			testingOverrides: metaOverridesForProvider(p),
   388  			View:             view,
   389  		},
   390  	}
   391  
   392  	args := []string{
   393  		"-state", testTempFile(t),
   394  		"-auto-approve",
   395  	}
   396  	code := c.Run(args)
   397  	output := done(t)
   398  	if code != 1 {
   399  		t.Fatalf("bad: \n%s", output.Stdout())
   400  	}
   401  }
   402  
   403  func TestApply_defaultState(t *testing.T) {
   404  	// Create a temporary working directory that is empty
   405  	td := tempDir(t)
   406  	testCopyDir(t, testFixturePath("apply"), td)
   407  	defer os.RemoveAll(td)
   408  	defer testChdir(t, td)()
   409  
   410  	statePath := filepath.Join(td, DefaultStateFilename)
   411  
   412  	// Change to the temporary directory
   413  	cwd, err := os.Getwd()
   414  	if err != nil {
   415  		t.Fatalf("err: %s", err)
   416  	}
   417  	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
   418  		t.Fatalf("err: %s", err)
   419  	}
   420  	defer os.Chdir(cwd)
   421  
   422  	p := applyFixtureProvider()
   423  	view, done := testView(t)
   424  	c := &ApplyCommand{
   425  		Meta: Meta{
   426  			testingOverrides: metaOverridesForProvider(p),
   427  			View:             view,
   428  		},
   429  	}
   430  
   431  	// create an existing state file
   432  	localState := statemgr.NewFilesystem(statePath)
   433  	if err := localState.WriteState(states.NewState()); err != nil {
   434  		t.Fatal(err)
   435  	}
   436  
   437  	args := []string{
   438  		"-auto-approve",
   439  	}
   440  	code := c.Run(args)
   441  	output := done(t)
   442  	if code != 0 {
   443  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   444  	}
   445  
   446  	if _, err := os.Stat(statePath); err != nil {
   447  		t.Fatalf("err: %s", err)
   448  	}
   449  
   450  	state := testStateRead(t, statePath)
   451  	if state == nil {
   452  		t.Fatal("state should not be nil")
   453  	}
   454  }
   455  
   456  func TestApply_error(t *testing.T) {
   457  	// Create a temporary working directory that is empty
   458  	td := tempDir(t)
   459  	testCopyDir(t, testFixturePath("apply-error"), td)
   460  	defer os.RemoveAll(td)
   461  	defer testChdir(t, td)()
   462  
   463  	statePath := testTempFile(t)
   464  
   465  	p := testProvider()
   466  	view, done := testView(t)
   467  	c := &ApplyCommand{
   468  		Meta: Meta{
   469  			testingOverrides: metaOverridesForProvider(p),
   470  			View:             view,
   471  		},
   472  	}
   473  
   474  	var lock sync.Mutex
   475  	errored := false
   476  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
   477  		lock.Lock()
   478  		defer lock.Unlock()
   479  
   480  		if !errored {
   481  			errored = true
   482  			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error"))
   483  		}
   484  
   485  		s := req.PlannedState.AsValueMap()
   486  		s["id"] = cty.StringVal("foo")
   487  
   488  		resp.NewState = cty.ObjectVal(s)
   489  		return
   490  	}
   491  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
   492  		s := req.ProposedNewState.AsValueMap()
   493  		s["id"] = cty.UnknownVal(cty.String)
   494  		resp.PlannedState = cty.ObjectVal(s)
   495  		return
   496  	}
   497  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
   498  		ResourceTypes: map[string]providers.Schema{
   499  			"test_instance": {
   500  				Block: &configschema.Block{
   501  					Attributes: map[string]*configschema.Attribute{
   502  						"id":    {Type: cty.String, Optional: true, Computed: true},
   503  						"ami":   {Type: cty.String, Optional: true},
   504  						"error": {Type: cty.Bool, Optional: true},
   505  					},
   506  				},
   507  			},
   508  		},
   509  	}
   510  
   511  	args := []string{
   512  		"-state", statePath,
   513  		"-auto-approve",
   514  	}
   515  	code := c.Run(args)
   516  	output := done(t)
   517  	if code != 1 {
   518  		t.Fatalf("wrong exit code %d; want 1\n%s", code, output.Stdout())
   519  	}
   520  
   521  	if _, err := os.Stat(statePath); err != nil {
   522  		t.Fatalf("err: %s", err)
   523  	}
   524  
   525  	state := testStateRead(t, statePath)
   526  	if state == nil {
   527  		t.Fatal("state should not be nil")
   528  	}
   529  	if len(state.RootModule().Resources) == 0 {
   530  		t.Fatal("no resources in state")
   531  	}
   532  }
   533  
   534  func TestApply_input(t *testing.T) {
   535  	// Create a temporary working directory that is empty
   536  	td := tempDir(t)
   537  	testCopyDir(t, testFixturePath("apply-input"), td)
   538  	defer os.RemoveAll(td)
   539  	defer testChdir(t, td)()
   540  
   541  	// Disable test mode so input would be asked
   542  	test = false
   543  	defer func() { test = true }()
   544  
   545  	// The configuration for this test includes a declaration of variable
   546  	// "foo" with no default, and we don't set it on the command line below,
   547  	// so the apply command will produce an interactive prompt for the
   548  	// value of var.foo. We'll answer "foo" here, and we expect the output
   549  	// value "result" to echo that back to us below.
   550  	defaultInputReader = bytes.NewBufferString("foo\n")
   551  	defaultInputWriter = new(bytes.Buffer)
   552  
   553  	statePath := testTempFile(t)
   554  
   555  	p := testProvider()
   556  	view, done := testView(t)
   557  	c := &ApplyCommand{
   558  		Meta: Meta{
   559  			testingOverrides: metaOverridesForProvider(p),
   560  			View:             view,
   561  		},
   562  	}
   563  
   564  	args := []string{
   565  		"-state", statePath,
   566  		"-auto-approve",
   567  	}
   568  	code := c.Run(args)
   569  	output := done(t)
   570  	if code != 0 {
   571  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   572  	}
   573  
   574  	expected := strings.TrimSpace(`
   575  <no state>
   576  Outputs:
   577  
   578  result = foo
   579  	`)
   580  	testStateOutput(t, statePath, expected)
   581  }
   582  
   583  // When only a partial set of the variables are set, Terraform
   584  // should still ask for the unset ones by default (with -input=true)
   585  func TestApply_inputPartial(t *testing.T) {
   586  	// Create a temporary working directory that is empty
   587  	td := tempDir(t)
   588  	testCopyDir(t, testFixturePath("apply-input-partial"), td)
   589  	defer os.RemoveAll(td)
   590  	defer testChdir(t, td)()
   591  
   592  	// Disable test mode so input would be asked
   593  	test = false
   594  	defer func() { test = true }()
   595  
   596  	// Set some default reader/writers for the inputs
   597  	defaultInputReader = bytes.NewBufferString("one\ntwo\n")
   598  	defaultInputWriter = new(bytes.Buffer)
   599  
   600  	statePath := testTempFile(t)
   601  
   602  	p := testProvider()
   603  	view, done := testView(t)
   604  	c := &ApplyCommand{
   605  		Meta: Meta{
   606  			testingOverrides: metaOverridesForProvider(p),
   607  			View:             view,
   608  		},
   609  	}
   610  
   611  	args := []string{
   612  		"-state", statePath,
   613  		"-auto-approve",
   614  		"-var", "foo=foovalue",
   615  	}
   616  	code := c.Run(args)
   617  	output := done(t)
   618  	if code != 0 {
   619  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   620  	}
   621  
   622  	expected := strings.TrimSpace(`
   623  <no state>
   624  Outputs:
   625  
   626  bar = one
   627  foo = foovalue
   628  	`)
   629  	testStateOutput(t, statePath, expected)
   630  }
   631  
   632  func TestApply_noArgs(t *testing.T) {
   633  	// Create a temporary working directory that is empty
   634  	td := tempDir(t)
   635  	testCopyDir(t, testFixturePath("apply"), td)
   636  	defer os.RemoveAll(td)
   637  	defer testChdir(t, td)()
   638  
   639  	statePath := testTempFile(t)
   640  
   641  	p := applyFixtureProvider()
   642  	view, done := testView(t)
   643  	c := &ApplyCommand{
   644  		Meta: Meta{
   645  			testingOverrides: metaOverridesForProvider(p),
   646  			View:             view,
   647  		},
   648  	}
   649  
   650  	args := []string{
   651  		"-state", statePath,
   652  		"-auto-approve",
   653  	}
   654  	code := c.Run(args)
   655  	output := done(t)
   656  	if code != 0 {
   657  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   658  	}
   659  
   660  	if _, err := os.Stat(statePath); err != nil {
   661  		t.Fatalf("err: %s", err)
   662  	}
   663  
   664  	state := testStateRead(t, statePath)
   665  	if state == nil {
   666  		t.Fatal("state should not be nil")
   667  	}
   668  }
   669  
   670  func TestApply_plan(t *testing.T) {
   671  	// Disable test mode so input would be asked
   672  	test = false
   673  	defer func() { test = true }()
   674  
   675  	// Set some default reader/writers for the inputs
   676  	defaultInputReader = new(bytes.Buffer)
   677  	defaultInputWriter = new(bytes.Buffer)
   678  
   679  	planPath := applyFixturePlanFile(t)
   680  	statePath := testTempFile(t)
   681  
   682  	p := applyFixtureProvider()
   683  	view, done := testView(t)
   684  	c := &ApplyCommand{
   685  		Meta: Meta{
   686  			testingOverrides: metaOverridesForProvider(p),
   687  			View:             view,
   688  		},
   689  	}
   690  
   691  	args := []string{
   692  		"-state-out", statePath,
   693  		planPath,
   694  	}
   695  	code := c.Run(args)
   696  	output := done(t)
   697  	if code != 0 {
   698  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   699  	}
   700  
   701  	if _, err := os.Stat(statePath); err != nil {
   702  		t.Fatalf("err: %s", err)
   703  	}
   704  
   705  	state := testStateRead(t, statePath)
   706  	if state == nil {
   707  		t.Fatal("state should not be nil")
   708  	}
   709  }
   710  
   711  func TestApply_plan_backup(t *testing.T) {
   712  	planPath := applyFixturePlanFile(t)
   713  	statePath := testTempFile(t)
   714  	backupPath := testTempFile(t)
   715  
   716  	p := applyFixtureProvider()
   717  	view, done := testView(t)
   718  	c := &ApplyCommand{
   719  		Meta: Meta{
   720  			testingOverrides: metaOverridesForProvider(p),
   721  			View:             view,
   722  		},
   723  	}
   724  
   725  	// create a state file that needs to be backed up
   726  	err := statemgr.NewFilesystem(statePath).WriteState(states.NewState())
   727  	if err != nil {
   728  		t.Fatal(err)
   729  	}
   730  
   731  	args := []string{
   732  		"-state", statePath,
   733  		"-backup", backupPath,
   734  		planPath,
   735  	}
   736  	code := c.Run(args)
   737  	output := done(t)
   738  	if code != 0 {
   739  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   740  	}
   741  
   742  	// Should have a backup file
   743  	testStateRead(t, backupPath)
   744  }
   745  
   746  func TestApply_plan_noBackup(t *testing.T) {
   747  	planPath := applyFixturePlanFile(t)
   748  	statePath := testTempFile(t)
   749  
   750  	p := applyFixtureProvider()
   751  	view, done := testView(t)
   752  	c := &ApplyCommand{
   753  		Meta: Meta{
   754  			testingOverrides: metaOverridesForProvider(p),
   755  			View:             view,
   756  		},
   757  	}
   758  
   759  	args := []string{
   760  		"-state-out", statePath,
   761  		"-backup", "-",
   762  		planPath,
   763  	}
   764  	code := c.Run(args)
   765  	output := done(t)
   766  	if code != 0 {
   767  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   768  	}
   769  
   770  	// Ensure there is no backup
   771  	_, err := os.Stat(statePath + DefaultBackupExtension)
   772  	if err == nil || !os.IsNotExist(err) {
   773  		t.Fatalf("backup should not exist")
   774  	}
   775  
   776  	// Ensure there is no literal "-"
   777  	_, err = os.Stat("-")
   778  	if err == nil || !os.IsNotExist(err) {
   779  		t.Fatalf("backup should not exist")
   780  	}
   781  }
   782  
   783  func TestApply_plan_remoteState(t *testing.T) {
   784  	// Disable test mode so input would be asked
   785  	test = false
   786  	defer func() { test = true }()
   787  	tmp, cwd := testCwd(t)
   788  	defer testFixCwd(t, tmp, cwd)
   789  	remoteStatePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)
   790  	if err := os.MkdirAll(filepath.Dir(remoteStatePath), 0755); err != nil {
   791  		t.Fatalf("err: %s", err)
   792  	}
   793  
   794  	// Set some default reader/writers for the inputs
   795  	defaultInputReader = new(bytes.Buffer)
   796  	defaultInputWriter = new(bytes.Buffer)
   797  
   798  	// Create a remote state
   799  	state := testState()
   800  	_, srv := testRemoteState(t, state, 200)
   801  	defer srv.Close()
   802  
   803  	_, snap := testModuleWithSnapshot(t, "apply")
   804  	backendConfig := cty.ObjectVal(map[string]cty.Value{
   805  		"address":                cty.StringVal(srv.URL),
   806  		"update_method":          cty.NullVal(cty.String),
   807  		"lock_address":           cty.NullVal(cty.String),
   808  		"unlock_address":         cty.NullVal(cty.String),
   809  		"lock_method":            cty.NullVal(cty.String),
   810  		"unlock_method":          cty.NullVal(cty.String),
   811  		"username":               cty.NullVal(cty.String),
   812  		"password":               cty.NullVal(cty.String),
   813  		"skip_cert_verification": cty.NullVal(cty.Bool),
   814  		"retry_max":              cty.NullVal(cty.String),
   815  		"retry_wait_min":         cty.NullVal(cty.String),
   816  		"retry_wait_max":         cty.NullVal(cty.String),
   817  	})
   818  	backendConfigRaw, err := plans.NewDynamicValue(backendConfig, backendConfig.Type())
   819  	if err != nil {
   820  		t.Fatal(err)
   821  	}
   822  	planPath := testPlanFile(t, snap, state, &plans.Plan{
   823  		Backend: plans.Backend{
   824  			Type:   "http",
   825  			Config: backendConfigRaw,
   826  		},
   827  		Changes: plans.NewChanges(),
   828  	})
   829  
   830  	p := testProvider()
   831  	view, done := testView(t)
   832  	c := &ApplyCommand{
   833  		Meta: Meta{
   834  			testingOverrides: metaOverridesForProvider(p),
   835  			View:             view,
   836  		},
   837  	}
   838  
   839  	args := []string{
   840  		planPath,
   841  	}
   842  	code := c.Run(args)
   843  	output := done(t)
   844  	if code != 0 {
   845  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   846  	}
   847  
   848  	// State file should be not be installed
   849  	if _, err := os.Stat(filepath.Join(tmp, DefaultStateFilename)); err == nil {
   850  		data, _ := ioutil.ReadFile(DefaultStateFilename)
   851  		t.Fatalf("State path should not exist: %s", string(data))
   852  	}
   853  
   854  	// Check that there is no remote state config
   855  	if src, err := ioutil.ReadFile(remoteStatePath); err == nil {
   856  		t.Fatalf("has %s file; should not\n%s", remoteStatePath, src)
   857  	}
   858  }
   859  
   860  func TestApply_planWithVarFile(t *testing.T) {
   861  	varFileDir := testTempDir(t)
   862  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
   863  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
   864  		t.Fatalf("err: %s", err)
   865  	}
   866  
   867  	planPath := applyFixturePlanFile(t)
   868  	statePath := testTempFile(t)
   869  
   870  	cwd, err := os.Getwd()
   871  	if err != nil {
   872  		t.Fatalf("err: %s", err)
   873  	}
   874  	if err := os.Chdir(varFileDir); err != nil {
   875  		t.Fatalf("err: %s", err)
   876  	}
   877  	defer os.Chdir(cwd)
   878  
   879  	p := applyFixtureProvider()
   880  	view, done := testView(t)
   881  	c := &ApplyCommand{
   882  		Meta: Meta{
   883  			testingOverrides: metaOverridesForProvider(p),
   884  			View:             view,
   885  		},
   886  	}
   887  
   888  	args := []string{
   889  		"-state-out", statePath,
   890  		planPath,
   891  	}
   892  	code := c.Run(args)
   893  	output := done(t)
   894  	if code != 0 {
   895  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   896  	}
   897  
   898  	if _, err := os.Stat(statePath); err != nil {
   899  		t.Fatalf("err: %s", err)
   900  	}
   901  
   902  	state := testStateRead(t, statePath)
   903  	if state == nil {
   904  		t.Fatal("state should not be nil")
   905  	}
   906  }
   907  
   908  func TestApply_planVars(t *testing.T) {
   909  	planPath := applyFixturePlanFile(t)
   910  	statePath := testTempFile(t)
   911  
   912  	p := applyFixtureProvider()
   913  	view, done := testView(t)
   914  	c := &ApplyCommand{
   915  		Meta: Meta{
   916  			testingOverrides: metaOverridesForProvider(p),
   917  			View:             view,
   918  		},
   919  	}
   920  
   921  	args := []string{
   922  		"-state", statePath,
   923  		"-var", "foo=bar",
   924  		planPath,
   925  	}
   926  	code := c.Run(args)
   927  	output := done(t)
   928  	if code == 0 {
   929  		t.Fatal("should've failed: ", output.Stdout())
   930  	}
   931  }
   932  
   933  // we should be able to apply a plan file with no other file dependencies
   934  func TestApply_planNoModuleFiles(t *testing.T) {
   935  	// temporary data directory which we can remove between commands
   936  	td := testTempDir(t)
   937  	defer os.RemoveAll(td)
   938  
   939  	defer testChdir(t, td)()
   940  
   941  	p := applyFixtureProvider()
   942  	planPath := applyFixturePlanFile(t)
   943  	view, done := testView(t)
   944  	apply := &ApplyCommand{
   945  		Meta: Meta{
   946  			testingOverrides: metaOverridesForProvider(p),
   947  			Ui:               new(cli.MockUi),
   948  			View:             view,
   949  		},
   950  	}
   951  	args := []string{
   952  		planPath,
   953  	}
   954  	apply.Run(args)
   955  	done(t)
   956  }
   957  
   958  func TestApply_refresh(t *testing.T) {
   959  	// Create a temporary working directory that is empty
   960  	td := tempDir(t)
   961  	testCopyDir(t, testFixturePath("apply"), td)
   962  	defer os.RemoveAll(td)
   963  	defer testChdir(t, td)()
   964  
   965  	originalState := states.BuildState(func(s *states.SyncState) {
   966  		s.SetResourceInstanceCurrent(
   967  			addrs.Resource{
   968  				Mode: addrs.ManagedResourceMode,
   969  				Type: "test_instance",
   970  				Name: "foo",
   971  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   972  			&states.ResourceInstanceObjectSrc{
   973  				AttrsJSON: []byte(`{"ami":"bar"}`),
   974  				Status:    states.ObjectReady,
   975  			},
   976  			addrs.AbsProviderConfig{
   977  				Provider: addrs.NewDefaultProvider("test"),
   978  				Module:   addrs.RootModule,
   979  			},
   980  		)
   981  	})
   982  	statePath := testStateFile(t, originalState)
   983  
   984  	p := applyFixtureProvider()
   985  	view, done := testView(t)
   986  	c := &ApplyCommand{
   987  		Meta: Meta{
   988  			testingOverrides: metaOverridesForProvider(p),
   989  			View:             view,
   990  		},
   991  	}
   992  
   993  	args := []string{
   994  		"-state", statePath,
   995  		"-auto-approve",
   996  	}
   997  	code := c.Run(args)
   998  	output := done(t)
   999  	if code != 0 {
  1000  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1001  	}
  1002  
  1003  	if !p.ReadResourceCalled {
  1004  		t.Fatal("should call ReadResource")
  1005  	}
  1006  
  1007  	if _, err := os.Stat(statePath); err != nil {
  1008  		t.Fatalf("err: %s", err)
  1009  	}
  1010  
  1011  	state := testStateRead(t, statePath)
  1012  	if state == nil {
  1013  		t.Fatal("state should not be nil")
  1014  	}
  1015  
  1016  	// Should have a backup file
  1017  	backupState := testStateRead(t, statePath+DefaultBackupExtension)
  1018  
  1019  	actualStr := strings.TrimSpace(backupState.String())
  1020  	expectedStr := strings.TrimSpace(originalState.String())
  1021  	if actualStr != expectedStr {
  1022  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
  1023  	}
  1024  }
  1025  
  1026  func TestApply_shutdown(t *testing.T) {
  1027  	// Create a temporary working directory that is empty
  1028  	td := tempDir(t)
  1029  	testCopyDir(t, testFixturePath("apply-shutdown"), td)
  1030  	defer os.RemoveAll(td)
  1031  	defer testChdir(t, td)()
  1032  
  1033  	cancelled := make(chan struct{})
  1034  	shutdownCh := make(chan struct{})
  1035  
  1036  	statePath := testTempFile(t)
  1037  	p := testProvider()
  1038  
  1039  	view, done := testView(t)
  1040  	c := &ApplyCommand{
  1041  		Meta: Meta{
  1042  			testingOverrides: metaOverridesForProvider(p),
  1043  			View:             view,
  1044  			ShutdownCh:       shutdownCh,
  1045  		},
  1046  	}
  1047  
  1048  	p.StopFn = func() error {
  1049  		close(cancelled)
  1050  		return nil
  1051  	}
  1052  
  1053  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  1054  		resp.PlannedState = req.ProposedNewState
  1055  		return
  1056  	}
  1057  
  1058  	var once sync.Once
  1059  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
  1060  		// only cancel once
  1061  		once.Do(func() {
  1062  			shutdownCh <- struct{}{}
  1063  		})
  1064  
  1065  		// Because of the internal lock in the MockProvider, we can't
  1066  		// coordiante directly with the calling of Stop, and making the
  1067  		// MockProvider concurrent is disruptive to a lot of existing tests.
  1068  		// Wait here a moment to help make sure the main goroutine gets to the
  1069  		// Stop call before we exit, or the plan may finish before it can be
  1070  		// canceled.
  1071  		time.Sleep(200 * time.Millisecond)
  1072  
  1073  		resp.NewState = req.PlannedState
  1074  		return
  1075  	}
  1076  
  1077  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  1078  		ResourceTypes: map[string]providers.Schema{
  1079  			"test_instance": {
  1080  				Block: &configschema.Block{
  1081  					Attributes: map[string]*configschema.Attribute{
  1082  						"ami": {Type: cty.String, Optional: true},
  1083  					},
  1084  				},
  1085  			},
  1086  		},
  1087  	}
  1088  
  1089  	args := []string{
  1090  		"-state", statePath,
  1091  		"-auto-approve",
  1092  	}
  1093  	code := c.Run(args)
  1094  	output := done(t)
  1095  	if code != 1 {
  1096  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1097  	}
  1098  
  1099  	if _, err := os.Stat(statePath); err != nil {
  1100  		t.Fatalf("err: %s", err)
  1101  	}
  1102  
  1103  	select {
  1104  	case <-cancelled:
  1105  	default:
  1106  		t.Fatal("command not cancelled")
  1107  	}
  1108  
  1109  	state := testStateRead(t, statePath)
  1110  	if state == nil {
  1111  		t.Fatal("state should not be nil")
  1112  	}
  1113  }
  1114  
  1115  func TestApply_state(t *testing.T) {
  1116  	// Create a temporary working directory that is empty
  1117  	td := tempDir(t)
  1118  	testCopyDir(t, testFixturePath("apply"), td)
  1119  	defer os.RemoveAll(td)
  1120  	defer testChdir(t, td)()
  1121  
  1122  	originalState := states.BuildState(func(s *states.SyncState) {
  1123  		s.SetResourceInstanceCurrent(
  1124  			addrs.Resource{
  1125  				Mode: addrs.ManagedResourceMode,
  1126  				Type: "test_instance",
  1127  				Name: "foo",
  1128  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
  1129  			&states.ResourceInstanceObjectSrc{
  1130  				AttrsJSON: []byte(`{"ami":"foo"}`),
  1131  				Status:    states.ObjectReady,
  1132  			},
  1133  			addrs.AbsProviderConfig{
  1134  				Provider: addrs.NewDefaultProvider("test"),
  1135  				Module:   addrs.RootModule,
  1136  			},
  1137  		)
  1138  	})
  1139  	statePath := testStateFile(t, originalState)
  1140  
  1141  	p := applyFixtureProvider()
  1142  	p.PlanResourceChangeResponse = &providers.PlanResourceChangeResponse{
  1143  		PlannedState: cty.ObjectVal(map[string]cty.Value{
  1144  			"ami": cty.StringVal("bar"),
  1145  		}),
  1146  	}
  1147  	p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{
  1148  		NewState: cty.ObjectVal(map[string]cty.Value{
  1149  			"ami": cty.StringVal("bar"),
  1150  		}),
  1151  	}
  1152  
  1153  	view, done := testView(t)
  1154  	c := &ApplyCommand{
  1155  		Meta: Meta{
  1156  			testingOverrides: metaOverridesForProvider(p),
  1157  			View:             view,
  1158  		},
  1159  	}
  1160  
  1161  	// Run the apply command pointing to our existing state
  1162  	args := []string{
  1163  		"-state", statePath,
  1164  		"-auto-approve",
  1165  	}
  1166  	code := c.Run(args)
  1167  	output := done(t)
  1168  	if code != 0 {
  1169  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1170  	}
  1171  
  1172  	// Verify that the provider was called with the existing state
  1173  	actual := p.PlanResourceChangeRequest.PriorState
  1174  	expected := cty.ObjectVal(map[string]cty.Value{
  1175  		"id":  cty.NullVal(cty.String),
  1176  		"ami": cty.StringVal("foo"),
  1177  	})
  1178  	if !expected.RawEquals(actual) {
  1179  		t.Fatalf("wrong prior state during plan\ngot: %#v\nwant: %#v", actual, expected)
  1180  	}
  1181  
  1182  	actual = p.ApplyResourceChangeRequest.PriorState
  1183  	expected = cty.ObjectVal(map[string]cty.Value{
  1184  		"id":  cty.NullVal(cty.String),
  1185  		"ami": cty.StringVal("foo"),
  1186  	})
  1187  	if !expected.RawEquals(actual) {
  1188  		t.Fatalf("wrong prior state during apply\ngot: %#v\nwant: %#v", actual, expected)
  1189  	}
  1190  
  1191  	// Verify a new state exists
  1192  	if _, err := os.Stat(statePath); err != nil {
  1193  		t.Fatalf("err: %s", err)
  1194  	}
  1195  
  1196  	state := testStateRead(t, statePath)
  1197  	if state == nil {
  1198  		t.Fatal("state should not be nil")
  1199  	}
  1200  
  1201  	backupState := testStateRead(t, statePath+DefaultBackupExtension)
  1202  
  1203  	actualStr := strings.TrimSpace(backupState.String())
  1204  	expectedStr := strings.TrimSpace(originalState.String())
  1205  	if actualStr != expectedStr {
  1206  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
  1207  	}
  1208  }
  1209  
  1210  func TestApply_stateNoExist(t *testing.T) {
  1211  	// Create a temporary working directory that is empty
  1212  	td := tempDir(t)
  1213  	testCopyDir(t, testFixturePath("apply"), td)
  1214  	defer os.RemoveAll(td)
  1215  	defer testChdir(t, td)()
  1216  
  1217  	p := applyFixtureProvider()
  1218  	view, done := testView(t)
  1219  	c := &ApplyCommand{
  1220  		Meta: Meta{
  1221  			testingOverrides: metaOverridesForProvider(p),
  1222  			View:             view,
  1223  		},
  1224  	}
  1225  
  1226  	args := []string{
  1227  		"idontexist.tfstate",
  1228  	}
  1229  	code := c.Run(args)
  1230  	output := done(t)
  1231  	if code != 1 {
  1232  		t.Fatalf("bad: \n%s", output.Stdout())
  1233  	}
  1234  }
  1235  
  1236  func TestApply_sensitiveOutput(t *testing.T) {
  1237  	// Create a temporary working directory that is empty
  1238  	td := tempDir(t)
  1239  	testCopyDir(t, testFixturePath("apply-sensitive-output"), td)
  1240  	defer os.RemoveAll(td)
  1241  	defer testChdir(t, td)()
  1242  
  1243  	p := testProvider()
  1244  	view, done := testView(t)
  1245  	c := &ApplyCommand{
  1246  		Meta: Meta{
  1247  			testingOverrides: metaOverridesForProvider(p),
  1248  			View:             view,
  1249  		},
  1250  	}
  1251  
  1252  	statePath := testTempFile(t)
  1253  
  1254  	args := []string{
  1255  		"-state", statePath,
  1256  		"-auto-approve",
  1257  	}
  1258  
  1259  	code := c.Run(args)
  1260  	output := done(t)
  1261  	if code != 0 {
  1262  		t.Fatalf("bad: \n%s", output.Stdout())
  1263  	}
  1264  
  1265  	stdout := output.Stdout()
  1266  	if !strings.Contains(stdout, "notsensitive = \"Hello world\"") {
  1267  		t.Fatalf("bad: output should contain 'notsensitive' output\n%s", stdout)
  1268  	}
  1269  	if !strings.Contains(stdout, "sensitive = <sensitive>") {
  1270  		t.Fatalf("bad: output should contain 'sensitive' output\n%s", stdout)
  1271  	}
  1272  }
  1273  
  1274  func TestApply_vars(t *testing.T) {
  1275  	// Create a temporary working directory that is empty
  1276  	td := tempDir(t)
  1277  	testCopyDir(t, testFixturePath("apply-vars"), td)
  1278  	defer os.RemoveAll(td)
  1279  	defer testChdir(t, td)()
  1280  
  1281  	statePath := testTempFile(t)
  1282  
  1283  	p := testProvider()
  1284  	view, done := testView(t)
  1285  	c := &ApplyCommand{
  1286  		Meta: Meta{
  1287  			testingOverrides: metaOverridesForProvider(p),
  1288  			View:             view,
  1289  		},
  1290  	}
  1291  
  1292  	actual := ""
  1293  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  1294  		ResourceTypes: map[string]providers.Schema{
  1295  			"test_instance": {
  1296  				Block: &configschema.Block{
  1297  					Attributes: map[string]*configschema.Attribute{
  1298  						"value": {Type: cty.String, Optional: true},
  1299  					},
  1300  				},
  1301  			},
  1302  		},
  1303  	}
  1304  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
  1305  		return providers.ApplyResourceChangeResponse{
  1306  			NewState: req.PlannedState,
  1307  		}
  1308  	}
  1309  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1310  		actual = req.ProposedNewState.GetAttr("value").AsString()
  1311  		return providers.PlanResourceChangeResponse{
  1312  			PlannedState: req.ProposedNewState,
  1313  		}
  1314  	}
  1315  
  1316  	args := []string{
  1317  		"-auto-approve",
  1318  		"-var", "foo=bar",
  1319  		"-state", statePath,
  1320  	}
  1321  	code := c.Run(args)
  1322  	output := done(t)
  1323  	if code != 0 {
  1324  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1325  	}
  1326  
  1327  	if actual != "bar" {
  1328  		t.Fatal("didn't work")
  1329  	}
  1330  }
  1331  
  1332  func TestApply_varFile(t *testing.T) {
  1333  	// Create a temporary working directory that is empty
  1334  	td := tempDir(t)
  1335  	testCopyDir(t, testFixturePath("apply-vars"), td)
  1336  	defer os.RemoveAll(td)
  1337  	defer testChdir(t, td)()
  1338  
  1339  	varFilePath := testTempFile(t)
  1340  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
  1341  		t.Fatalf("err: %s", err)
  1342  	}
  1343  
  1344  	statePath := testTempFile(t)
  1345  
  1346  	p := testProvider()
  1347  	view, done := testView(t)
  1348  	c := &ApplyCommand{
  1349  		Meta: Meta{
  1350  			testingOverrides: metaOverridesForProvider(p),
  1351  			View:             view,
  1352  		},
  1353  	}
  1354  
  1355  	actual := ""
  1356  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  1357  		ResourceTypes: map[string]providers.Schema{
  1358  			"test_instance": {
  1359  				Block: &configschema.Block{
  1360  					Attributes: map[string]*configschema.Attribute{
  1361  						"value": {Type: cty.String, Optional: true},
  1362  					},
  1363  				},
  1364  			},
  1365  		},
  1366  	}
  1367  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
  1368  		return providers.ApplyResourceChangeResponse{
  1369  			NewState: req.PlannedState,
  1370  		}
  1371  	}
  1372  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1373  		actual = req.ProposedNewState.GetAttr("value").AsString()
  1374  		return providers.PlanResourceChangeResponse{
  1375  			PlannedState: req.ProposedNewState,
  1376  		}
  1377  	}
  1378  
  1379  	args := []string{
  1380  		"-auto-approve",
  1381  		"-var-file", varFilePath,
  1382  		"-state", statePath,
  1383  	}
  1384  	code := c.Run(args)
  1385  	output := done(t)
  1386  	if code != 0 {
  1387  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1388  	}
  1389  
  1390  	if actual != "bar" {
  1391  		t.Fatal("didn't work")
  1392  	}
  1393  }
  1394  
  1395  func TestApply_varFileDefault(t *testing.T) {
  1396  	// Create a temporary working directory that is empty
  1397  	td := tempDir(t)
  1398  	testCopyDir(t, testFixturePath("apply-vars"), td)
  1399  	defer os.RemoveAll(td)
  1400  	defer testChdir(t, td)()
  1401  
  1402  	varFilePath := filepath.Join(td, "terraform.tfvars")
  1403  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
  1404  		t.Fatalf("err: %s", err)
  1405  	}
  1406  
  1407  	statePath := testTempFile(t)
  1408  
  1409  	p := testProvider()
  1410  	view, done := testView(t)
  1411  	c := &ApplyCommand{
  1412  		Meta: Meta{
  1413  			testingOverrides: metaOverridesForProvider(p),
  1414  			View:             view,
  1415  		},
  1416  	}
  1417  
  1418  	actual := ""
  1419  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  1420  		ResourceTypes: map[string]providers.Schema{
  1421  			"test_instance": {
  1422  				Block: &configschema.Block{
  1423  					Attributes: map[string]*configschema.Attribute{
  1424  						"value": {Type: cty.String, Optional: true},
  1425  					},
  1426  				},
  1427  			},
  1428  		},
  1429  	}
  1430  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
  1431  		return providers.ApplyResourceChangeResponse{
  1432  			NewState: req.PlannedState,
  1433  		}
  1434  	}
  1435  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1436  		actual = req.ProposedNewState.GetAttr("value").AsString()
  1437  		return providers.PlanResourceChangeResponse{
  1438  			PlannedState: req.ProposedNewState,
  1439  		}
  1440  	}
  1441  
  1442  	args := []string{
  1443  		"-auto-approve",
  1444  		"-state", statePath,
  1445  	}
  1446  	code := c.Run(args)
  1447  	output := done(t)
  1448  	if code != 0 {
  1449  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1450  	}
  1451  
  1452  	if actual != "bar" {
  1453  		t.Fatal("didn't work")
  1454  	}
  1455  }
  1456  
  1457  func TestApply_varFileDefaultJSON(t *testing.T) {
  1458  	// Create a temporary working directory that is empty
  1459  	td := tempDir(t)
  1460  	testCopyDir(t, testFixturePath("apply-vars"), td)
  1461  	defer os.RemoveAll(td)
  1462  	defer testChdir(t, td)()
  1463  
  1464  	varFilePath := filepath.Join(td, "terraform.tfvars.json")
  1465  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil {
  1466  		t.Fatalf("err: %s", err)
  1467  	}
  1468  
  1469  	statePath := testTempFile(t)
  1470  
  1471  	p := testProvider()
  1472  	view, done := testView(t)
  1473  	c := &ApplyCommand{
  1474  		Meta: Meta{
  1475  			testingOverrides: metaOverridesForProvider(p),
  1476  			View:             view,
  1477  		},
  1478  	}
  1479  
  1480  	actual := ""
  1481  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  1482  		ResourceTypes: map[string]providers.Schema{
  1483  			"test_instance": {
  1484  				Block: &configschema.Block{
  1485  					Attributes: map[string]*configschema.Attribute{
  1486  						"value": {Type: cty.String, Optional: true},
  1487  					},
  1488  				},
  1489  			},
  1490  		},
  1491  	}
  1492  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
  1493  		return providers.ApplyResourceChangeResponse{
  1494  			NewState: req.PlannedState,
  1495  		}
  1496  	}
  1497  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1498  		actual = req.ProposedNewState.GetAttr("value").AsString()
  1499  		return providers.PlanResourceChangeResponse{
  1500  			PlannedState: req.ProposedNewState,
  1501  		}
  1502  	}
  1503  
  1504  	args := []string{
  1505  		"-auto-approve",
  1506  		"-state", statePath,
  1507  	}
  1508  	code := c.Run(args)
  1509  	output := done(t)
  1510  	if code != 0 {
  1511  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1512  	}
  1513  
  1514  	if actual != "bar" {
  1515  		t.Fatal("didn't work")
  1516  	}
  1517  }
  1518  
  1519  func TestApply_backup(t *testing.T) {
  1520  	// Create a temporary working directory that is empty
  1521  	td := tempDir(t)
  1522  	testCopyDir(t, testFixturePath("apply"), td)
  1523  	defer os.RemoveAll(td)
  1524  	defer testChdir(t, td)()
  1525  
  1526  	originalState := states.BuildState(func(s *states.SyncState) {
  1527  		s.SetResourceInstanceCurrent(
  1528  			addrs.Resource{
  1529  				Mode: addrs.ManagedResourceMode,
  1530  				Type: "test_instance",
  1531  				Name: "foo",
  1532  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
  1533  			&states.ResourceInstanceObjectSrc{
  1534  				AttrsJSON: []byte("{\n            \"id\": \"bar\"\n          }"),
  1535  				Status:    states.ObjectReady,
  1536  			},
  1537  			addrs.AbsProviderConfig{
  1538  				Provider: addrs.NewDefaultProvider("test"),
  1539  				Module:   addrs.RootModule,
  1540  			},
  1541  		)
  1542  	})
  1543  	statePath := testStateFile(t, originalState)
  1544  	backupPath := testTempFile(t)
  1545  
  1546  	p := applyFixtureProvider()
  1547  	p.PlanResourceChangeResponse = &providers.PlanResourceChangeResponse{
  1548  		PlannedState: cty.ObjectVal(map[string]cty.Value{
  1549  			"ami": cty.StringVal("bar"),
  1550  		}),
  1551  	}
  1552  
  1553  	view, done := testView(t)
  1554  	c := &ApplyCommand{
  1555  		Meta: Meta{
  1556  			testingOverrides: metaOverridesForProvider(p),
  1557  			View:             view,
  1558  		},
  1559  	}
  1560  
  1561  	// Run the apply command pointing to our existing state
  1562  	args := []string{
  1563  		"-auto-approve",
  1564  		"-state", statePath,
  1565  		"-backup", backupPath,
  1566  	}
  1567  	code := c.Run(args)
  1568  	output := done(t)
  1569  	if code != 0 {
  1570  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1571  	}
  1572  
  1573  	// Verify a new state exists
  1574  	if _, err := os.Stat(statePath); err != nil {
  1575  		t.Fatalf("err: %s", err)
  1576  	}
  1577  
  1578  	state := testStateRead(t, statePath)
  1579  	if state == nil {
  1580  		t.Fatal("state should not be nil")
  1581  	}
  1582  
  1583  	backupState := testStateRead(t, backupPath)
  1584  
  1585  	actual := backupState.RootModule().Resources["test_instance.foo"]
  1586  	expected := originalState.RootModule().Resources["test_instance.foo"]
  1587  	if !cmp.Equal(actual, expected, cmpopts.EquateEmpty()) {
  1588  		t.Fatalf(
  1589  			"wrong aws_instance.foo state\n%s",
  1590  			cmp.Diff(expected, actual, cmp.Transformer("bytesAsString", func(b []byte) string {
  1591  				return string(b)
  1592  			})),
  1593  		)
  1594  	}
  1595  }
  1596  
  1597  func TestApply_disableBackup(t *testing.T) {
  1598  	// Create a temporary working directory that is empty
  1599  	td := tempDir(t)
  1600  	testCopyDir(t, testFixturePath("apply"), td)
  1601  	defer os.RemoveAll(td)
  1602  	defer testChdir(t, td)()
  1603  
  1604  	originalState := testState()
  1605  	statePath := testStateFile(t, originalState)
  1606  
  1607  	p := applyFixtureProvider()
  1608  	p.PlanResourceChangeResponse = &providers.PlanResourceChangeResponse{
  1609  		PlannedState: cty.ObjectVal(map[string]cty.Value{
  1610  			"ami": cty.StringVal("bar"),
  1611  		}),
  1612  	}
  1613  
  1614  	view, done := testView(t)
  1615  	c := &ApplyCommand{
  1616  		Meta: Meta{
  1617  			testingOverrides: metaOverridesForProvider(p),
  1618  			View:             view,
  1619  		},
  1620  	}
  1621  
  1622  	// Run the apply command pointing to our existing state
  1623  	args := []string{
  1624  		"-auto-approve",
  1625  		"-state", statePath,
  1626  		"-backup", "-",
  1627  	}
  1628  	code := c.Run(args)
  1629  	output := done(t)
  1630  	if code != 0 {
  1631  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1632  	}
  1633  
  1634  	// Verify that the provider was called with the existing state
  1635  	actual := p.PlanResourceChangeRequest.PriorState
  1636  	expected := cty.ObjectVal(map[string]cty.Value{
  1637  		"id":  cty.StringVal("bar"),
  1638  		"ami": cty.NullVal(cty.String),
  1639  	})
  1640  	if !expected.RawEquals(actual) {
  1641  		t.Fatalf("wrong prior state during plan\ngot:  %#v\nwant: %#v", actual, expected)
  1642  	}
  1643  
  1644  	actual = p.ApplyResourceChangeRequest.PriorState
  1645  	expected = cty.ObjectVal(map[string]cty.Value{
  1646  		"id":  cty.StringVal("bar"),
  1647  		"ami": cty.NullVal(cty.String),
  1648  	})
  1649  	if !expected.RawEquals(actual) {
  1650  		t.Fatalf("wrong prior state during apply\ngot:  %#v\nwant: %#v", actual, expected)
  1651  	}
  1652  
  1653  	// Verify a new state exists
  1654  	if _, err := os.Stat(statePath); err != nil {
  1655  		t.Fatalf("err: %s", err)
  1656  	}
  1657  
  1658  	state := testStateRead(t, statePath)
  1659  	if state == nil {
  1660  		t.Fatal("state should not be nil")
  1661  	}
  1662  
  1663  	// Ensure there is no backup
  1664  	_, err := os.Stat(statePath + DefaultBackupExtension)
  1665  	if err == nil || !os.IsNotExist(err) {
  1666  		t.Fatalf("backup should not exist")
  1667  	}
  1668  
  1669  	// Ensure there is no literal "-"
  1670  	_, err = os.Stat("-")
  1671  	if err == nil || !os.IsNotExist(err) {
  1672  		t.Fatalf("backup should not exist")
  1673  	}
  1674  }
  1675  
  1676  // Test that the Terraform env is passed through
  1677  func TestApply_terraformEnv(t *testing.T) {
  1678  	// Create a temporary working directory that is empty
  1679  	td := tempDir(t)
  1680  	testCopyDir(t, testFixturePath("apply-terraform-env"), td)
  1681  	defer os.RemoveAll(td)
  1682  	defer testChdir(t, td)()
  1683  
  1684  	statePath := testTempFile(t)
  1685  
  1686  	p := testProvider()
  1687  	view, done := testView(t)
  1688  	c := &ApplyCommand{
  1689  		Meta: Meta{
  1690  			testingOverrides: metaOverridesForProvider(p),
  1691  			View:             view,
  1692  		},
  1693  	}
  1694  
  1695  	args := []string{
  1696  		"-auto-approve",
  1697  		"-state", statePath,
  1698  	}
  1699  	code := c.Run(args)
  1700  	output := done(t)
  1701  	if code != 0 {
  1702  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1703  	}
  1704  
  1705  	expected := strings.TrimSpace(`
  1706  <no state>
  1707  Outputs:
  1708  
  1709  output = default
  1710  	`)
  1711  	testStateOutput(t, statePath, expected)
  1712  }
  1713  
  1714  // Test that the Terraform env is passed through
  1715  func TestApply_terraformEnvNonDefault(t *testing.T) {
  1716  	// Create a temporary working directory that is empty
  1717  	td := tempDir(t)
  1718  	testCopyDir(t, testFixturePath("apply-terraform-env"), td)
  1719  	defer os.RemoveAll(td)
  1720  	defer testChdir(t, td)()
  1721  
  1722  	// Create new env
  1723  	{
  1724  		ui := new(cli.MockUi)
  1725  		newCmd := &WorkspaceNewCommand{
  1726  			Meta: Meta{
  1727  				Ui: ui,
  1728  			},
  1729  		}
  1730  		if code := newCmd.Run([]string{"test"}); code != 0 {
  1731  		}
  1732  	}
  1733  
  1734  	// Switch to it
  1735  	{
  1736  		args := []string{"test"}
  1737  		ui := new(cli.MockUi)
  1738  		selCmd := &WorkspaceSelectCommand{
  1739  			Meta: Meta{
  1740  				Ui: ui,
  1741  			},
  1742  		}
  1743  		if code := selCmd.Run(args); code != 0 {
  1744  		}
  1745  	}
  1746  
  1747  	p := testProvider()
  1748  	view, done := testView(t)
  1749  	c := &ApplyCommand{
  1750  		Meta: Meta{
  1751  			testingOverrides: metaOverridesForProvider(p),
  1752  			View:             view,
  1753  		},
  1754  	}
  1755  
  1756  	args := []string{
  1757  		"-auto-approve",
  1758  	}
  1759  	code := c.Run(args)
  1760  	output := done(t)
  1761  	if code != 0 {
  1762  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1763  	}
  1764  
  1765  	statePath := filepath.Join("resource_state.json.d", "test", "resource_state.json")
  1766  	expected := strings.TrimSpace(`
  1767  <no state>
  1768  Outputs:
  1769  
  1770  output = test
  1771  	`)
  1772  	testStateOutput(t, statePath, expected)
  1773  }
  1774  
  1775  // Config with multiple resources, targeting apply of a subset
  1776  func TestApply_targeted(t *testing.T) {
  1777  	td := tempDir(t)
  1778  	testCopyDir(t, testFixturePath("apply-targeted"), td)
  1779  	defer os.RemoveAll(td)
  1780  	defer testChdir(t, td)()
  1781  
  1782  	p := testProvider()
  1783  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  1784  		ResourceTypes: map[string]providers.Schema{
  1785  			"test_instance": {
  1786  				Block: &configschema.Block{
  1787  					Attributes: map[string]*configschema.Attribute{
  1788  						"id": {Type: cty.String, Computed: true},
  1789  					},
  1790  				},
  1791  			},
  1792  		},
  1793  	}
  1794  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1795  		return providers.PlanResourceChangeResponse{
  1796  			PlannedState: req.ProposedNewState,
  1797  		}
  1798  	}
  1799  
  1800  	view, done := testView(t)
  1801  	c := &ApplyCommand{
  1802  		Meta: Meta{
  1803  			testingOverrides: metaOverridesForProvider(p),
  1804  			View:             view,
  1805  		},
  1806  	}
  1807  
  1808  	args := []string{
  1809  		"-auto-approve",
  1810  		"-target", "test_instance.foo",
  1811  		"-target", "test_instance.baz",
  1812  	}
  1813  	code := c.Run(args)
  1814  	output := done(t)
  1815  	if code != 0 {
  1816  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1817  	}
  1818  
  1819  	if got, want := output.Stdout(), "3 added, 0 changed, 0 destroyed"; !strings.Contains(got, want) {
  1820  		t.Fatalf("bad change summary, want %q, got:\n%s", want, got)
  1821  	}
  1822  }
  1823  
  1824  // Diagnostics for invalid -target flags
  1825  func TestApply_targetFlagsDiags(t *testing.T) {
  1826  	testCases := map[string]string{
  1827  		"test_instance.": "Dot must be followed by attribute name.",
  1828  		"test_instance":  "Resource specification must include a resource type and name.",
  1829  	}
  1830  
  1831  	for target, wantDiag := range testCases {
  1832  		t.Run(target, func(t *testing.T) {
  1833  			td := testTempDir(t)
  1834  			defer os.RemoveAll(td)
  1835  			defer testChdir(t, td)()
  1836  
  1837  			view, done := testView(t)
  1838  			c := &ApplyCommand{
  1839  				Meta: Meta{
  1840  					View: view,
  1841  				},
  1842  			}
  1843  
  1844  			args := []string{
  1845  				"-auto-approve",
  1846  				"-target", target,
  1847  			}
  1848  			code := c.Run(args)
  1849  			output := done(t)
  1850  			if code != 1 {
  1851  				t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1852  			}
  1853  
  1854  			got := output.Stderr()
  1855  			if !strings.Contains(got, target) {
  1856  				t.Fatalf("bad error output, want %q, got:\n%s", target, got)
  1857  			}
  1858  			if !strings.Contains(got, wantDiag) {
  1859  				t.Fatalf("bad error output, want %q, got:\n%s", wantDiag, got)
  1860  			}
  1861  		})
  1862  	}
  1863  }
  1864  
  1865  func TestApply_replace(t *testing.T) {
  1866  	td := tempDir(t)
  1867  	testCopyDir(t, testFixturePath("apply-replace"), td)
  1868  	defer os.RemoveAll(td)
  1869  	defer testChdir(t, td)()
  1870  
  1871  	originalState := states.BuildState(func(s *states.SyncState) {
  1872  		s.SetResourceInstanceCurrent(
  1873  			addrs.Resource{
  1874  				Mode: addrs.ManagedResourceMode,
  1875  				Type: "test_instance",
  1876  				Name: "a",
  1877  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
  1878  			&states.ResourceInstanceObjectSrc{
  1879  				AttrsJSON: []byte(`{"id":"hello"}`),
  1880  				Status:    states.ObjectReady,
  1881  			},
  1882  			addrs.AbsProviderConfig{
  1883  				Provider: addrs.NewDefaultProvider("test"),
  1884  				Module:   addrs.RootModule,
  1885  			},
  1886  		)
  1887  	})
  1888  	statePath := testStateFile(t, originalState)
  1889  
  1890  	p := testProvider()
  1891  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  1892  		ResourceTypes: map[string]providers.Schema{
  1893  			"test_instance": {
  1894  				Block: &configschema.Block{
  1895  					Attributes: map[string]*configschema.Attribute{
  1896  						"id": {Type: cty.String, Computed: true},
  1897  					},
  1898  				},
  1899  			},
  1900  		},
  1901  	}
  1902  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1903  		return providers.PlanResourceChangeResponse{
  1904  			PlannedState: req.ProposedNewState,
  1905  		}
  1906  	}
  1907  	createCount := 0
  1908  	deleteCount := 0
  1909  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
  1910  		if req.PriorState.IsNull() {
  1911  			createCount++
  1912  		}
  1913  		if req.PlannedState.IsNull() {
  1914  			deleteCount++
  1915  		}
  1916  		return providers.ApplyResourceChangeResponse{
  1917  			NewState: req.PlannedState,
  1918  		}
  1919  	}
  1920  
  1921  	view, done := testView(t)
  1922  	c := &ApplyCommand{
  1923  		Meta: Meta{
  1924  			testingOverrides: metaOverridesForProvider(p),
  1925  			View:             view,
  1926  		},
  1927  	}
  1928  
  1929  	args := []string{
  1930  		"-auto-approve",
  1931  		"-state", statePath,
  1932  		"-replace", "test_instance.a",
  1933  	}
  1934  	code := c.Run(args)
  1935  	output := done(t)
  1936  	if code != 0 {
  1937  		t.Fatalf("wrong exit code %d\n\n%s", code, output.Stderr())
  1938  	}
  1939  
  1940  	if got, want := output.Stdout(), "1 added, 0 changed, 1 destroyed"; !strings.Contains(got, want) {
  1941  		t.Errorf("wrong change summary\ngot output:\n%s\n\nwant substring: %s", got, want)
  1942  	}
  1943  
  1944  	if got, want := createCount, 1; got != want {
  1945  		t.Errorf("wrong create count %d; want %d", got, want)
  1946  	}
  1947  	if got, want := deleteCount, 1; got != want {
  1948  		t.Errorf("wrong create count %d; want %d", got, want)
  1949  	}
  1950  }
  1951  
  1952  func TestApply_pluginPath(t *testing.T) {
  1953  	// Create a temporary working directory that is empty
  1954  	td := tempDir(t)
  1955  	testCopyDir(t, testFixturePath("apply"), td)
  1956  	defer os.RemoveAll(td)
  1957  	defer testChdir(t, td)()
  1958  
  1959  	statePath := testTempFile(t)
  1960  
  1961  	p := applyFixtureProvider()
  1962  
  1963  	view, done := testView(t)
  1964  	c := &ApplyCommand{
  1965  		Meta: Meta{
  1966  			testingOverrides: metaOverridesForProvider(p),
  1967  			View:             view,
  1968  		},
  1969  	}
  1970  
  1971  	pluginPath := []string{"a", "b", "c"}
  1972  
  1973  	if err := c.Meta.storePluginPath(pluginPath); err != nil {
  1974  		t.Fatal(err)
  1975  	}
  1976  	c.Meta.pluginPath = nil
  1977  
  1978  	args := []string{
  1979  		"-state", statePath,
  1980  		"-auto-approve",
  1981  	}
  1982  	code := c.Run(args)
  1983  	output := done(t)
  1984  	if code != 0 {
  1985  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  1986  	}
  1987  
  1988  	if !reflect.DeepEqual(pluginPath, c.Meta.pluginPath) {
  1989  		t.Fatalf("expected plugin path %#v, got %#v", pluginPath, c.Meta.pluginPath)
  1990  	}
  1991  }
  1992  
  1993  func TestApply_jsonGoldenReference(t *testing.T) {
  1994  	// Create a temporary working directory that is empty
  1995  	td := tempDir(t)
  1996  	testCopyDir(t, testFixturePath("apply"), td)
  1997  	defer os.RemoveAll(td)
  1998  	defer testChdir(t, td)()
  1999  
  2000  	statePath := testTempFile(t)
  2001  
  2002  	p := applyFixtureProvider()
  2003  
  2004  	view, done := testView(t)
  2005  	c := &ApplyCommand{
  2006  		Meta: Meta{
  2007  			testingOverrides: metaOverridesForProvider(p),
  2008  			View:             view,
  2009  		},
  2010  	}
  2011  
  2012  	args := []string{
  2013  		"-json",
  2014  		"-state", statePath,
  2015  		"-auto-approve",
  2016  	}
  2017  	code := c.Run(args)
  2018  	output := done(t)
  2019  	if code != 0 {
  2020  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
  2021  	}
  2022  
  2023  	if _, err := os.Stat(statePath); err != nil {
  2024  		t.Fatalf("err: %s", err)
  2025  	}
  2026  
  2027  	state := testStateRead(t, statePath)
  2028  	if state == nil {
  2029  		t.Fatal("state should not be nil")
  2030  	}
  2031  
  2032  	// Load the golden reference fixture
  2033  	wantFile, err := os.Open(path.Join(testFixturePath("apply"), "output.jsonlog"))
  2034  	if err != nil {
  2035  		t.Fatalf("failed to open output file: %s", err)
  2036  	}
  2037  	defer wantFile.Close()
  2038  	wantBytes, err := ioutil.ReadAll(wantFile)
  2039  	if err != nil {
  2040  		t.Fatalf("failed to read output file: %s", err)
  2041  	}
  2042  	want := string(wantBytes)
  2043  
  2044  	got := output.Stdout()
  2045  
  2046  	// Split the output and the reference into lines so that we can compare
  2047  	// messages
  2048  	got = strings.TrimSuffix(got, "\n")
  2049  	gotLines := strings.Split(got, "\n")
  2050  
  2051  	want = strings.TrimSuffix(want, "\n")
  2052  	wantLines := strings.Split(want, "\n")
  2053  
  2054  	if len(gotLines) != len(wantLines) {
  2055  		t.Errorf("unexpected number of log lines: got %d, want %d", len(gotLines), len(wantLines))
  2056  	}
  2057  
  2058  	// Verify that the log starts with a version message
  2059  	type versionMessage struct {
  2060  		Level     string `json:"@level"`
  2061  		Message   string `json:"@message"`
  2062  		Type      string `json:"type"`
  2063  		Terraform string `json:"terraform"`
  2064  		UI        string `json:"ui"`
  2065  	}
  2066  	var gotVersion versionMessage
  2067  	if err := json.Unmarshal([]byte(gotLines[0]), &gotVersion); err != nil {
  2068  		t.Errorf("failed to unmarshal version line: %s\n%s", err, gotLines[0])
  2069  	}
  2070  	wantVersion := versionMessage{
  2071  		"info",
  2072  		fmt.Sprintf("Terraform %s", tfversion.String()),
  2073  		"version",
  2074  		tfversion.String(),
  2075  		views.JSON_UI_VERSION,
  2076  	}
  2077  	if !cmp.Equal(wantVersion, gotVersion) {
  2078  		t.Errorf("unexpected first message:\n%s", cmp.Diff(wantVersion, gotVersion))
  2079  	}
  2080  
  2081  	// Compare the rest of the lines against the golden reference
  2082  	var gotLineMaps []map[string]interface{}
  2083  	for i, line := range gotLines[1:] {
  2084  		index := i + 1
  2085  		var gotMap map[string]interface{}
  2086  		if err := json.Unmarshal([]byte(line), &gotMap); err != nil {
  2087  			t.Errorf("failed to unmarshal got line %d: %s\n%s", index, err, gotLines[index])
  2088  		}
  2089  		if _, ok := gotMap["@timestamp"]; !ok {
  2090  			t.Errorf("missing @timestamp field in log: %s", gotLines[index])
  2091  		}
  2092  		delete(gotMap, "@timestamp")
  2093  		gotLineMaps = append(gotLineMaps, gotMap)
  2094  	}
  2095  	var wantLineMaps []map[string]interface{}
  2096  	for i, line := range wantLines[1:] {
  2097  		index := i + 1
  2098  		var wantMap map[string]interface{}
  2099  		if err := json.Unmarshal([]byte(line), &wantMap); err != nil {
  2100  			t.Errorf("failed to unmarshal want line %d: %s\n%s", index, err, gotLines[index])
  2101  		}
  2102  		wantLineMaps = append(wantLineMaps, wantMap)
  2103  	}
  2104  	if diff := cmp.Diff(wantLineMaps, gotLineMaps); diff != "" {
  2105  		t.Errorf("wrong output lines\n%s", diff)
  2106  	}
  2107  }
  2108  
  2109  // applyFixtureSchema returns a schema suitable for processing the
  2110  // configuration in testdata/apply . This schema should be
  2111  // assigned to a mock provider named "test".
  2112  func applyFixtureSchema() *providers.GetProviderSchemaResponse {
  2113  	return &providers.GetProviderSchemaResponse{
  2114  		ResourceTypes: map[string]providers.Schema{
  2115  			"test_instance": {
  2116  				Block: &configschema.Block{
  2117  					Attributes: map[string]*configschema.Attribute{
  2118  						"id":  {Type: cty.String, Optional: true, Computed: true},
  2119  						"ami": {Type: cty.String, Optional: true},
  2120  					},
  2121  				},
  2122  			},
  2123  		},
  2124  	}
  2125  }
  2126  
  2127  // applyFixtureProvider returns a mock provider that is configured for basic
  2128  // operation with the configuration in testdata/apply. This mock has
  2129  // GetSchemaResponse, PlanResourceChangeFn, and ApplyResourceChangeFn populated,
  2130  // with the plan/apply steps just passing through the data determined by
  2131  // Terraform Core.
  2132  func applyFixtureProvider() *terraform.MockProvider {
  2133  	p := testProvider()
  2134  	p.GetProviderSchemaResponse = applyFixtureSchema()
  2135  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  2136  		return providers.PlanResourceChangeResponse{
  2137  			PlannedState: req.ProposedNewState,
  2138  		}
  2139  	}
  2140  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
  2141  		return providers.ApplyResourceChangeResponse{
  2142  			NewState: cty.UnknownAsNull(req.PlannedState),
  2143  		}
  2144  	}
  2145  	return p
  2146  }
  2147  
  2148  // applyFixturePlanFile creates a plan file at a temporary location containing
  2149  // a single change to create the test_instance.foo that is included in the
  2150  // "apply" test fixture, returning the location of that plan file.
  2151  func applyFixturePlanFile(t *testing.T) string {
  2152  	_, snap := testModuleWithSnapshot(t, "apply")
  2153  	plannedVal := cty.ObjectVal(map[string]cty.Value{
  2154  		"id":  cty.UnknownVal(cty.String),
  2155  		"ami": cty.StringVal("bar"),
  2156  	})
  2157  	priorValRaw, err := plans.NewDynamicValue(cty.NullVal(plannedVal.Type()), plannedVal.Type())
  2158  	if err != nil {
  2159  		t.Fatal(err)
  2160  	}
  2161  	plannedValRaw, err := plans.NewDynamicValue(plannedVal, plannedVal.Type())
  2162  	if err != nil {
  2163  		t.Fatal(err)
  2164  	}
  2165  	plan := testPlan(t)
  2166  	plan.Changes.SyncWrapper().AppendResourceInstanceChange(&plans.ResourceInstanceChangeSrc{
  2167  		Addr: addrs.Resource{
  2168  			Mode: addrs.ManagedResourceMode,
  2169  			Type: "test_instance",
  2170  			Name: "foo",
  2171  		}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
  2172  		ProviderAddr: addrs.AbsProviderConfig{
  2173  			Provider: addrs.NewDefaultProvider("test"),
  2174  			Module:   addrs.RootModule,
  2175  		},
  2176  		ChangeSrc: plans.ChangeSrc{
  2177  			Action: plans.Create,
  2178  			Before: priorValRaw,
  2179  			After:  plannedValRaw,
  2180  		},
  2181  	})
  2182  	return testPlanFile(
  2183  		t,
  2184  		snap,
  2185  		states.NewState(),
  2186  		plan,
  2187  	)
  2188  }
  2189  
  2190  const applyVarFile = `
  2191  foo = "bar"
  2192  `
  2193  
  2194  const applyVarFileJSON = `
  2195  { "foo": "bar" }
  2196  `