github.com/opentofu/opentofu@v1.7.1/internal/command/apply_test.go (about)

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