1package command
2
3import (
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/hashicorp/terraform/internal/addrs"
24	"github.com/hashicorp/terraform/internal/command/views"
25	"github.com/hashicorp/terraform/internal/configs/configschema"
26	"github.com/hashicorp/terraform/internal/plans"
27	"github.com/hashicorp/terraform/internal/providers"
28	"github.com/hashicorp/terraform/internal/states"
29	"github.com/hashicorp/terraform/internal/states/statemgr"
30	"github.com/hashicorp/terraform/internal/terraform"
31	tfversion "github.com/hashicorp/terraform/version"
32)
33
34func 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
73func 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
104func 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
148func 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
196func 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
236func 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.
281func 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
376func 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
403func 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
456func 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
534func 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>
576Outputs:
577
578result = 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)
585func 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>
624Outputs:
625
626bar = one
627foo = foovalue
628	`)
629	testStateOutput(t, statePath, expected)
630}
631
632func 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
670func 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
711func 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
746func 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
783func 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
860func 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
908func 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
934func 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
958func 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
1026func 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
1115func 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
1210func 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
1236func 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
1274func 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
1332func 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
1395func 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
1457func 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
1519func 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
1597func 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
1677func 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>
1707Outputs:
1708
1709output = default
1710	`)
1711	testStateOutput(t, statePath, expected)
1712}
1713
1714// Test that the Terraform env is passed through
1715func 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("terraform.tfstate.d", "test", "terraform.tfstate")
1766	expected := strings.TrimSpace(`
1767<no state>
1768Outputs:
1769
1770output = test
1771	`)
1772	testStateOutput(t, statePath, expected)
1773}
1774
1775// Config with multiple resources, targeting apply of a subset
1776func 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
1825func 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
1865func 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
1952func 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
1993func 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".
2112func 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.
2132func 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.
2151func 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
2190const applyVarFile = `
2191foo = "bar"
2192`
2193
2194const applyVarFileJSON = `
2195{ "foo": "bar" }
2196`
2197