1package command
2
3import (
4	"io/ioutil"
5	"os"
6	"path/filepath"
7	"reflect"
8	"sort"
9	"strings"
10	"testing"
11
12	"github.com/hashicorp/terraform/internal/addrs"
13	"github.com/hashicorp/terraform/internal/backend"
14	"github.com/hashicorp/terraform/internal/configs"
15	"github.com/hashicorp/terraform/internal/copy"
16	"github.com/hashicorp/terraform/internal/plans"
17	"github.com/hashicorp/terraform/internal/states"
18	"github.com/hashicorp/terraform/internal/states/statefile"
19	"github.com/hashicorp/terraform/internal/states/statemgr"
20	"github.com/mitchellh/cli"
21	"github.com/zclconf/go-cty/cty"
22
23	backendInit "github.com/hashicorp/terraform/internal/backend/init"
24	backendLocal "github.com/hashicorp/terraform/internal/backend/local"
25	backendInmem "github.com/hashicorp/terraform/internal/backend/remote-state/inmem"
26)
27
28// Test empty directory with no config/state creates a local state.
29func TestMetaBackend_emptyDir(t *testing.T) {
30	// Create a temporary working directory that is empty
31	td := tempDir(t)
32	os.MkdirAll(td, 0755)
33	defer os.RemoveAll(td)
34	defer testChdir(t, td)()
35
36	// Get the backend
37	m := testMetaBackend(t, nil)
38	b, diags := m.Backend(&BackendOpts{Init: true})
39	if diags.HasErrors() {
40		t.Fatal(diags.Err())
41	}
42
43	// Write some state
44	s, err := b.StateMgr(backend.DefaultStateName)
45	if err != nil {
46		t.Fatalf("unexpected error: %s", err)
47	}
48	s.WriteState(testState())
49	if err := s.PersistState(); err != nil {
50		t.Fatalf("unexpected error: %s", err)
51	}
52
53	// Verify it exists where we expect it to
54	if isEmptyState(DefaultStateFilename) {
55		t.Fatalf("no state was written")
56	}
57
58	// Verify no backup since it was empty to start
59	if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
60		t.Fatal("backup state should be empty")
61	}
62
63	// Verify no backend state was made
64	if !isEmptyState(filepath.Join(m.DataDir(), DefaultStateFilename)) {
65		t.Fatal("backend state should be empty")
66	}
67}
68
69// check for no state. Either the file doesn't exist, or is empty
70func isEmptyState(path string) bool {
71	fi, err := os.Stat(path)
72	if os.IsNotExist(err) {
73		return true
74	}
75
76	if fi.Size() == 0 {
77		return true
78	}
79
80	return false
81}
82
83// Test a directory with a legacy state and no config continues to
84// use the legacy state.
85func TestMetaBackend_emptyWithDefaultState(t *testing.T) {
86	// Create a temporary working directory that is empty
87	td := tempDir(t)
88	os.MkdirAll(td, 0755)
89	defer os.RemoveAll(td)
90	defer testChdir(t, td)()
91
92	// Write the legacy state
93	statePath := DefaultStateFilename
94	{
95		f, err := os.Create(statePath)
96		if err != nil {
97			t.Fatalf("err: %s", err)
98		}
99		err = writeStateForTesting(testState(), f)
100		f.Close()
101		if err != nil {
102			t.Fatalf("err: %s", err)
103		}
104	}
105
106	// Get the backend
107	m := testMetaBackend(t, nil)
108	b, diags := m.Backend(&BackendOpts{Init: true})
109	if diags.HasErrors() {
110		t.Fatal(diags.Err())
111	}
112
113	// Check the state
114	s, err := b.StateMgr(backend.DefaultStateName)
115	if err != nil {
116		t.Fatalf("unexpected error: %s", err)
117	}
118	if err := s.RefreshState(); err != nil {
119		t.Fatalf("err: %s", err)
120	}
121	if actual := s.State().String(); actual != testState().String() {
122		t.Fatalf("bad: %s", actual)
123	}
124
125	// Verify it exists where we expect it to
126	if _, err := os.Stat(DefaultStateFilename); err != nil {
127		t.Fatalf("err: %s", err)
128	}
129
130	stateName := filepath.Join(m.DataDir(), DefaultStateFilename)
131	if !isEmptyState(stateName) {
132		t.Fatal("expected no state at", stateName)
133	}
134
135	// Write some state
136	next := testState()
137	next.RootModule().SetOutputValue("foo", cty.StringVal("bar"), false)
138	s.WriteState(next)
139	if err := s.PersistState(); err != nil {
140		t.Fatalf("unexpected error: %s", err)
141	}
142
143	// Verify a backup was made since we're modifying a pre-existing state
144	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
145		t.Fatal("backup state should not be empty")
146	}
147}
148
149// Test an empty directory with an explicit state path (outside the dir)
150func TestMetaBackend_emptyWithExplicitState(t *testing.T) {
151	// Create a temporary working directory that is empty
152	td := tempDir(t)
153	os.MkdirAll(td, 0755)
154	defer os.RemoveAll(td)
155	defer testChdir(t, td)()
156
157	// Create another directory to store our state
158	stateDir := tempDir(t)
159	os.MkdirAll(stateDir, 0755)
160	defer os.RemoveAll(stateDir)
161
162	// Write the legacy state
163	statePath := filepath.Join(stateDir, "foo")
164	{
165		f, err := os.Create(statePath)
166		if err != nil {
167			t.Fatalf("err: %s", err)
168		}
169		err = writeStateForTesting(testState(), f)
170		f.Close()
171		if err != nil {
172			t.Fatalf("err: %s", err)
173		}
174	}
175
176	// Setup the meta
177	m := testMetaBackend(t, nil)
178	m.statePath = statePath
179
180	// Get the backend
181	b, diags := m.Backend(&BackendOpts{Init: true})
182	if diags.HasErrors() {
183		t.Fatal(diags.Err())
184	}
185
186	// Check the state
187	s, err := b.StateMgr(backend.DefaultStateName)
188	if err != nil {
189		t.Fatalf("unexpected error: %s", err)
190	}
191	if err := s.RefreshState(); err != nil {
192		t.Fatalf("err: %s", err)
193	}
194	if actual := s.State().String(); actual != testState().String() {
195		t.Fatalf("bad: %s", actual)
196	}
197
198	// Verify neither defaults exist
199	if _, err := os.Stat(DefaultStateFilename); err == nil {
200		t.Fatal("file should not exist")
201	}
202
203	stateName := filepath.Join(m.DataDir(), DefaultStateFilename)
204	if !isEmptyState(stateName) {
205		t.Fatal("expected no state at", stateName)
206	}
207
208	// Write some state
209	next := testState()
210	markStateForMatching(next, "bar") // just any change so it shows as different than before
211	s.WriteState(next)
212	if err := s.PersistState(); err != nil {
213		t.Fatalf("unexpected error: %s", err)
214	}
215
216	// Verify a backup was made since we're modifying a pre-existing state
217	if isEmptyState(statePath + DefaultBackupExtension) {
218		t.Fatal("backup state should not be empty")
219	}
220}
221
222// Verify that interpolations result in an error
223func TestMetaBackend_configureInterpolation(t *testing.T) {
224	// Create a temporary working directory that is empty
225	td := tempDir(t)
226	testCopyDir(t, testFixturePath("backend-new-interp"), td)
227	defer os.RemoveAll(td)
228	defer testChdir(t, td)()
229
230	// Setup the meta
231	m := testMetaBackend(t, nil)
232
233	// Get the backend
234	_, err := m.Backend(&BackendOpts{Init: true})
235	if err == nil {
236		t.Fatal("should error")
237	}
238}
239
240// Newly configured backend
241func TestMetaBackend_configureNew(t *testing.T) {
242	td := tempDir(t)
243	testCopyDir(t, testFixturePath("backend-new"), td)
244	defer os.RemoveAll(td)
245	defer testChdir(t, td)()
246
247	// Setup the meta
248	m := testMetaBackend(t, nil)
249
250	// Get the backend
251	b, diags := m.Backend(&BackendOpts{Init: true})
252	if diags.HasErrors() {
253		t.Fatal(diags.Err())
254	}
255
256	// Check the state
257	s, err := b.StateMgr(backend.DefaultStateName)
258	if err != nil {
259		t.Fatalf("unexpected error: %s", err)
260	}
261	if err := s.RefreshState(); err != nil {
262		t.Fatalf("unexpected error: %s", err)
263	}
264	state := s.State()
265	if state != nil {
266		t.Fatal("state should be nil")
267	}
268
269	// Write some state
270	state = states.NewState()
271	mark := markStateForMatching(state, "changing")
272
273	s.WriteState(state)
274	if err := s.PersistState(); err != nil {
275		t.Fatalf("unexpected error: %s", err)
276	}
277
278	// Verify the state is where we expect
279	{
280		f, err := os.Open("local-state.tfstate")
281		if err != nil {
282			t.Fatalf("err: %s", err)
283		}
284		actual, err := statefile.Read(f)
285		f.Close()
286		if err != nil {
287			t.Fatalf("err: %s", err)
288		}
289
290		assertStateHasMarker(t, actual.State, mark)
291	}
292
293	// Verify the default paths don't exist
294	if _, err := os.Stat(DefaultStateFilename); err == nil {
295		t.Fatal("file should not exist")
296	}
297
298	// Verify a backup doesn't exist
299	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
300		t.Fatal("file should not exist")
301	}
302}
303
304// Newly configured backend with prior local state and no remote state
305func TestMetaBackend_configureNewWithState(t *testing.T) {
306	// Create a temporary working directory that is empty
307	td := tempDir(t)
308	testCopyDir(t, testFixturePath("backend-new-migrate"), td)
309	defer os.RemoveAll(td)
310	defer testChdir(t, td)()
311
312	// Ask input
313	defer testInteractiveInput(t, []string{"yes"})()
314
315	// Setup the meta
316	m := testMetaBackend(t, nil)
317
318	// This combination should not require the extra -migrate-state flag, since
319	// there is no existing backend config
320	m.migrateState = false
321
322	// Get the backend
323	b, diags := m.Backend(&BackendOpts{Init: true})
324	if diags.HasErrors() {
325		t.Fatal(diags.Err())
326	}
327
328	// Check the state
329	s, err := b.StateMgr(backend.DefaultStateName)
330	if err != nil {
331		t.Fatalf("unexpected error: %s", err)
332	}
333	state, err := statemgr.RefreshAndRead(s)
334	if err != nil {
335		t.Fatalf("unexpected error: %s", err)
336	}
337	if state == nil {
338		t.Fatal("state is nil")
339	}
340
341	if got, want := testStateMgrCurrentLineage(s), "backend-new-migrate"; got != want {
342		t.Fatalf("lineage changed during migration\nnow: %s\nwas: %s", got, want)
343	}
344
345	// Write some state
346	state = states.NewState()
347	mark := markStateForMatching(state, "changing")
348
349	if err := statemgr.WriteAndPersist(s, state); err != nil {
350		t.Fatalf("unexpected error: %s", err)
351	}
352
353	// Verify the state is where we expect
354	{
355		f, err := os.Open("local-state.tfstate")
356		if err != nil {
357			t.Fatalf("err: %s", err)
358		}
359		actual, err := statefile.Read(f)
360		f.Close()
361		if err != nil {
362			t.Fatalf("err: %s", err)
363		}
364
365		assertStateHasMarker(t, actual.State, mark)
366	}
367
368	// Verify the default paths don't exist
369	if !isEmptyState(DefaultStateFilename) {
370		data, _ := ioutil.ReadFile(DefaultStateFilename)
371
372		t.Fatal("state should not exist, but contains:\n", string(data))
373	}
374
375	// Verify a backup does exist
376	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
377		t.Fatal("backup state is empty or missing")
378	}
379}
380
381// Newly configured backend with matching local and remote state doesn't prompt
382// for copy.
383func TestMetaBackend_configureNewWithoutCopy(t *testing.T) {
384	// Create a temporary working directory that is empty
385	td := tempDir(t)
386	testCopyDir(t, testFixturePath("backend-new-migrate"), td)
387	defer os.RemoveAll(td)
388	defer testChdir(t, td)()
389
390	if err := copy.CopyFile(DefaultStateFilename, "local-state.tfstate"); err != nil {
391		t.Fatal(err)
392	}
393
394	// Setup the meta
395	m := testMetaBackend(t, nil)
396	m.input = false
397
398	// init the backend
399	_, diags := m.Backend(&BackendOpts{Init: true})
400	if diags.HasErrors() {
401		t.Fatal(diags.Err())
402	}
403
404	// Verify the state is where we expect
405	f, err := os.Open("local-state.tfstate")
406	if err != nil {
407		t.Fatalf("err: %s", err)
408	}
409	actual, err := statefile.Read(f)
410	f.Close()
411	if err != nil {
412		t.Fatalf("err: %s", err)
413	}
414
415	if actual.Lineage != "backend-new-migrate" {
416		t.Fatalf("incorrect state lineage: %q", actual.Lineage)
417	}
418
419	// Verify the default paths don't exist
420	if !isEmptyState(DefaultStateFilename) {
421		data, _ := ioutil.ReadFile(DefaultStateFilename)
422
423		t.Fatal("state should not exist, but contains:\n", string(data))
424	}
425
426	// Verify a backup does exist
427	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
428		t.Fatal("backup state is empty or missing")
429	}
430}
431
432// Newly configured backend with prior local state and no remote state,
433// but opting to not migrate.
434func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) {
435	// Create a temporary working directory that is empty
436	td := tempDir(t)
437	testCopyDir(t, testFixturePath("backend-new-migrate"), td)
438	defer os.RemoveAll(td)
439	defer testChdir(t, td)()
440
441	// Ask input
442	defer testInteractiveInput(t, []string{"no"})()
443
444	// Setup the meta
445	m := testMetaBackend(t, nil)
446
447	// Get the backend
448	b, diags := m.Backend(&BackendOpts{Init: true})
449	if diags.HasErrors() {
450		t.Fatal(diags.Err())
451	}
452
453	// Check the state
454	s, err := b.StateMgr(backend.DefaultStateName)
455	if err != nil {
456		t.Fatalf("unexpected error: %s", err)
457	}
458	if err := s.RefreshState(); err != nil {
459		t.Fatalf("unexpected error: %s", err)
460	}
461	if state := s.State(); state != nil {
462		t.Fatal("state is not nil")
463	}
464
465	// Verify the default paths don't exist
466	if !isEmptyState(DefaultStateFilename) {
467		data, _ := ioutil.ReadFile(DefaultStateFilename)
468
469		t.Fatal("state should not exist, but contains:\n", string(data))
470	}
471
472	// Verify a backup does exist
473	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
474		t.Fatal("backup state is empty or missing")
475	}
476}
477
478// Newly configured backend with prior local state and remote state
479func TestMetaBackend_configureNewWithStateExisting(t *testing.T) {
480	// Create a temporary working directory that is empty
481	td := tempDir(t)
482	testCopyDir(t, testFixturePath("backend-new-migrate-existing"), td)
483	defer os.RemoveAll(td)
484	defer testChdir(t, td)()
485
486	// Setup the meta
487	m := testMetaBackend(t, nil)
488	// suppress input
489	m.forceInitCopy = true
490
491	// Get the backend
492	b, diags := m.Backend(&BackendOpts{Init: true})
493	if diags.HasErrors() {
494		t.Fatal(diags.Err())
495	}
496
497	// Check the state
498	s, err := b.StateMgr(backend.DefaultStateName)
499	if err != nil {
500		t.Fatalf("unexpected error: %s", err)
501	}
502	if err := s.RefreshState(); err != nil {
503		t.Fatalf("unexpected error: %s", err)
504	}
505	state := s.State()
506	if state == nil {
507		t.Fatal("state is nil")
508	}
509	if got, want := testStateMgrCurrentLineage(s), "local"; got != want {
510		t.Fatalf("wrong lineage %q; want %q", got, want)
511	}
512
513	// Write some state
514	state = states.NewState()
515	mark := markStateForMatching(state, "changing")
516
517	s.WriteState(state)
518	if err := s.PersistState(); err != nil {
519		t.Fatalf("unexpected error: %s", err)
520	}
521
522	// Verify the state is where we expect
523	{
524		f, err := os.Open("local-state.tfstate")
525		if err != nil {
526			t.Fatalf("err: %s", err)
527		}
528		actual, err := statefile.Read(f)
529		f.Close()
530		if err != nil {
531			t.Fatalf("err: %s", err)
532		}
533
534		assertStateHasMarker(t, actual.State, mark)
535	}
536
537	// Verify the default paths don't exist
538	if !isEmptyState(DefaultStateFilename) {
539		data, _ := ioutil.ReadFile(DefaultStateFilename)
540
541		t.Fatal("state should not exist, but contains:\n", string(data))
542	}
543
544	// Verify a backup does exist
545	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
546		t.Fatal("backup state is empty or missing")
547	}
548}
549
550// Newly configured backend with prior local state and remote state
551func TestMetaBackend_configureNewWithStateExistingNoMigrate(t *testing.T) {
552	// Create a temporary working directory that is empty
553	td := tempDir(t)
554	testCopyDir(t, testFixturePath("backend-new-migrate-existing"), td)
555	defer os.RemoveAll(td)
556	defer testChdir(t, td)()
557
558	// Ask input
559	defer testInteractiveInput(t, []string{"no"})()
560
561	// Setup the meta
562	m := testMetaBackend(t, nil)
563
564	// Get the backend
565	b, diags := m.Backend(&BackendOpts{Init: true})
566	if diags.HasErrors() {
567		t.Fatal(diags.Err())
568	}
569
570	// Check the state
571	s, err := b.StateMgr(backend.DefaultStateName)
572	if err != nil {
573		t.Fatalf("unexpected error: %s", err)
574	}
575	if err := s.RefreshState(); err != nil {
576		t.Fatalf("unexpected error: %s", err)
577	}
578	state := s.State()
579	if state == nil {
580		t.Fatal("state is nil")
581	}
582	if testStateMgrCurrentLineage(s) != "remote" {
583		t.Fatalf("bad: %#v", state)
584	}
585
586	// Write some state
587	state = states.NewState()
588	mark := markStateForMatching(state, "changing")
589	s.WriteState(state)
590	if err := s.PersistState(); err != nil {
591		t.Fatalf("unexpected error: %s", err)
592	}
593
594	// Verify the state is where we expect
595	{
596		f, err := os.Open("local-state.tfstate")
597		if err != nil {
598			t.Fatalf("err: %s", err)
599		}
600		actual, err := statefile.Read(f)
601		f.Close()
602		if err != nil {
603			t.Fatalf("err: %s", err)
604		}
605
606		assertStateHasMarker(t, actual.State, mark)
607	}
608
609	// Verify the default paths don't exist
610	if !isEmptyState(DefaultStateFilename) {
611		data, _ := ioutil.ReadFile(DefaultStateFilename)
612
613		t.Fatal("state should not exist, but contains:\n", string(data))
614	}
615
616	// Verify a backup does exist
617	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
618		t.Fatal("backup state is empty or missing")
619	}
620}
621
622// Saved backend state matching config
623func TestMetaBackend_configuredUnchanged(t *testing.T) {
624	defer testChdir(t, testFixturePath("backend-unchanged"))()
625
626	// Setup the meta
627	m := testMetaBackend(t, nil)
628
629	// Get the backend
630	b, diags := m.Backend(&BackendOpts{Init: true})
631	if diags.HasErrors() {
632		t.Fatal(diags.Err())
633	}
634
635	// Check the state
636	s, err := b.StateMgr(backend.DefaultStateName)
637	if err != nil {
638		t.Fatalf("unexpected error: %s", err)
639	}
640	if err := s.RefreshState(); err != nil {
641		t.Fatalf("unexpected error: %s", err)
642	}
643	state := s.State()
644	if state == nil {
645		t.Fatal("nil state")
646	}
647	if testStateMgrCurrentLineage(s) != "configuredUnchanged" {
648		t.Fatalf("bad: %#v", state)
649	}
650
651	// Verify the default paths don't exist
652	if _, err := os.Stat(DefaultStateFilename); err == nil {
653		t.Fatal("file should not exist")
654	}
655
656	// Verify a backup doesn't exist
657	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
658		t.Fatal("file should not exist")
659	}
660}
661
662// Changing a configured backend
663func TestMetaBackend_configuredChange(t *testing.T) {
664	// Create a temporary working directory that is empty
665	td := tempDir(t)
666	testCopyDir(t, testFixturePath("backend-change"), td)
667	defer os.RemoveAll(td)
668	defer testChdir(t, td)()
669
670	// Ask input
671	defer testInteractiveInput(t, []string{"no"})()
672
673	// Setup the meta
674	m := testMetaBackend(t, nil)
675
676	// Get the backend
677	b, diags := m.Backend(&BackendOpts{Init: true})
678	if diags.HasErrors() {
679		t.Fatal(diags.Err())
680	}
681
682	// Check the state
683	s, err := b.StateMgr(backend.DefaultStateName)
684	if err != nil {
685		t.Fatalf("unexpected error: %s", err)
686	}
687	if err := s.RefreshState(); err != nil {
688		t.Fatalf("unexpected error: %s", err)
689	}
690	state := s.State()
691	if state != nil {
692		t.Fatal("state should be nil")
693	}
694
695	// Verify the default paths don't exist
696	if _, err := os.Stat(DefaultStateFilename); err == nil {
697		t.Fatal("file should not exist")
698	}
699
700	// Verify a backup doesn't exist
701	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
702		t.Fatal("file should not exist")
703	}
704
705	// Write some state
706	state = states.NewState()
707	mark := markStateForMatching(state, "changing")
708
709	s.WriteState(state)
710	if err := s.PersistState(); err != nil {
711		t.Fatalf("unexpected error: %s", err)
712	}
713
714	// Verify the state is where we expect
715	{
716		f, err := os.Open("local-state-2.tfstate")
717		if err != nil {
718			t.Fatalf("err: %s", err)
719		}
720		actual, err := statefile.Read(f)
721		f.Close()
722		if err != nil {
723			t.Fatalf("err: %s", err)
724		}
725
726		assertStateHasMarker(t, actual.State, mark)
727	}
728
729	// Verify no local state
730	if _, err := os.Stat(DefaultStateFilename); err == nil {
731		t.Fatal("file should not exist")
732	}
733
734	// Verify no local backup
735	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
736		t.Fatal("file should not exist")
737	}
738}
739
740// Reconfiguring with an already configured backend.
741// This should ignore the existing backend config, and configure the new
742// backend is if this is the first time.
743func TestMetaBackend_reconfigureChange(t *testing.T) {
744	// Create a temporary working directory that is empty
745	td := tempDir(t)
746	testCopyDir(t, testFixturePath("backend-change-single-to-single"), td)
747	defer os.RemoveAll(td)
748	defer testChdir(t, td)()
749
750	// Register the single-state backend
751	backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
752	defer backendInit.Set("local-single", nil)
753
754	// Setup the meta
755	m := testMetaBackend(t, nil)
756
757	// this should not ask for input
758	m.input = false
759
760	// cli flag -reconfigure
761	m.reconfigure = true
762
763	// Get the backend
764	b, diags := m.Backend(&BackendOpts{Init: true})
765	if diags.HasErrors() {
766		t.Fatal(diags.Err())
767	}
768
769	// Check the state
770	s, err := b.StateMgr(backend.DefaultStateName)
771	if err != nil {
772		t.Fatalf("unexpected error: %s", err)
773	}
774	if err := s.RefreshState(); err != nil {
775		t.Fatalf("unexpected error: %s", err)
776	}
777	newState := s.State()
778	if newState != nil || !newState.Empty() {
779		t.Fatal("state should be nil/empty after forced reconfiguration")
780	}
781
782	// verify that the old state is still there
783	s = statemgr.NewFilesystem("local-state.tfstate")
784	if err := s.RefreshState(); err != nil {
785		t.Fatal(err)
786	}
787	oldState := s.State()
788	if oldState == nil || oldState.Empty() {
789		t.Fatal("original state should be untouched")
790	}
791}
792
793// Initializing a backend which supports workspaces and does *not* have
794// the currently selected workspace should prompt the user with a list of
795// workspaces to choose from to select a valid one, if more than one workspace
796// is available.
797func TestMetaBackend_initSelectedWorkspaceDoesNotExist(t *testing.T) {
798	// Create a temporary working directory that is empty
799	td := tempDir(t)
800	testCopyDir(t, testFixturePath("init-backend-selected-workspace-doesnt-exist-multi"), td)
801	defer os.RemoveAll(td)
802	defer testChdir(t, td)()
803
804	// Setup the meta
805	m := testMetaBackend(t, nil)
806
807	defer testInputMap(t, map[string]string{
808		"select-workspace": "2",
809	})()
810
811	// Get the backend
812	_, diags := m.Backend(&BackendOpts{Init: true})
813	if diags.HasErrors() {
814		t.Fatal(diags.Err())
815	}
816
817	expected := "foo"
818	actual, err := m.Workspace()
819	if err != nil {
820		t.Fatal(err)
821	}
822
823	if actual != expected {
824		t.Fatalf("expected selected workspace to be %q, but was %q", expected, actual)
825	}
826}
827
828// Initializing a backend which supports workspaces and does *not* have
829// the currently selected workspace with input=false should fail.
830func TestMetaBackend_initSelectedWorkspaceDoesNotExistInputFalse(t *testing.T) {
831	// Create a temporary working directory that is empty
832	td := tempDir(t)
833	testCopyDir(t, testFixturePath("init-backend-selected-workspace-doesnt-exist-multi"), td)
834	defer os.RemoveAll(td)
835	defer testChdir(t, td)()
836
837	// Setup the meta
838	m := testMetaBackend(t, nil)
839	m.input = false
840
841	// Get the backend
842	_, diags := m.Backend(&BackendOpts{Init: true})
843
844	// Should fail immediately
845	if got, want := diags.ErrWithWarnings().Error(), `Currently selected workspace "bar" does not exist`; !strings.Contains(got, want) {
846		t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
847	}
848}
849
850// Changing a configured backend, copying state
851func TestMetaBackend_configuredChangeCopy(t *testing.T) {
852	// Create a temporary working directory that is empty
853	td := tempDir(t)
854	testCopyDir(t, testFixturePath("backend-change"), td)
855	defer os.RemoveAll(td)
856	defer testChdir(t, td)()
857
858	// Ask input
859	defer testInteractiveInput(t, []string{"yes", "yes"})()
860
861	// Setup the meta
862	m := testMetaBackend(t, nil)
863
864	// Get the backend
865	b, diags := m.Backend(&BackendOpts{Init: true})
866	if diags.HasErrors() {
867		t.Fatal(diags.Err())
868	}
869
870	// Check the state
871	s, err := b.StateMgr(backend.DefaultStateName)
872	if err != nil {
873		t.Fatalf("unexpected error: %s", err)
874	}
875	if err := s.RefreshState(); err != nil {
876		t.Fatalf("unexpected error: %s", err)
877	}
878	state := s.State()
879	if state == nil {
880		t.Fatal("state should not be nil")
881	}
882	if testStateMgrCurrentLineage(s) != "backend-change" {
883		t.Fatalf("bad: %#v", state)
884	}
885
886	// Verify no local state
887	if _, err := os.Stat(DefaultStateFilename); err == nil {
888		t.Fatal("file should not exist")
889	}
890
891	// Verify no local backup
892	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
893		t.Fatal("file should not exist")
894	}
895}
896
897// Changing a configured backend that supports only single states to another
898// backend that only supports single states.
899func TestMetaBackend_configuredChangeCopy_singleState(t *testing.T) {
900	// Create a temporary working directory that is empty
901	td := tempDir(t)
902	testCopyDir(t, testFixturePath("backend-change-single-to-single"), td)
903	defer os.RemoveAll(td)
904	defer testChdir(t, td)()
905
906	// Register the single-state backend
907	backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
908	defer backendInit.Set("local-single", nil)
909
910	// Ask input
911	defer testInputMap(t, map[string]string{
912		"backend-migrate-copy-to-empty": "yes",
913	})()
914
915	// Setup the meta
916	m := testMetaBackend(t, nil)
917
918	// Get the backend
919	b, diags := m.Backend(&BackendOpts{Init: true})
920	if diags.HasErrors() {
921		t.Fatal(diags.Err())
922	}
923
924	// Check the state
925	s, err := b.StateMgr(backend.DefaultStateName)
926	if err != nil {
927		t.Fatalf("unexpected error: %s", err)
928	}
929	if err := s.RefreshState(); err != nil {
930		t.Fatalf("unexpected error: %s", err)
931	}
932	state := s.State()
933	if state == nil {
934		t.Fatal("state should not be nil")
935	}
936	if testStateMgrCurrentLineage(s) != "backend-change" {
937		t.Fatalf("bad: %#v", state)
938	}
939
940	// Verify no local state
941	if _, err := os.Stat(DefaultStateFilename); err == nil {
942		t.Fatal("file should not exist")
943	}
944
945	// Verify no local backup
946	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
947		t.Fatal("file should not exist")
948	}
949}
950
951// Changing a configured backend that supports multi-state to a
952// backend that only supports single states. The multi-state only has
953// a default state.
954func TestMetaBackend_configuredChangeCopy_multiToSingleDefault(t *testing.T) {
955	// Create a temporary working directory that is empty
956	td := tempDir(t)
957	testCopyDir(t, testFixturePath("backend-change-multi-default-to-single"), td)
958	defer os.RemoveAll(td)
959	defer testChdir(t, td)()
960
961	// Register the single-state backend
962	backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
963	defer backendInit.Set("local-single", nil)
964
965	// Ask input
966	defer testInputMap(t, map[string]string{
967		"backend-migrate-copy-to-empty": "yes",
968	})()
969
970	// Setup the meta
971	m := testMetaBackend(t, nil)
972
973	// Get the backend
974	b, diags := m.Backend(&BackendOpts{Init: true})
975	if diags.HasErrors() {
976		t.Fatal(diags.Err())
977	}
978
979	// Check the state
980	s, err := b.StateMgr(backend.DefaultStateName)
981	if err != nil {
982		t.Fatalf("unexpected error: %s", err)
983	}
984	if err := s.RefreshState(); err != nil {
985		t.Fatalf("unexpected error: %s", err)
986	}
987	state := s.State()
988	if state == nil {
989		t.Fatal("state should not be nil")
990	}
991	if testStateMgrCurrentLineage(s) != "backend-change" {
992		t.Fatalf("bad: %#v", state)
993	}
994
995	// Verify no local state
996	if _, err := os.Stat(DefaultStateFilename); err == nil {
997		t.Fatal("file should not exist")
998	}
999
1000	// Verify no local backup
1001	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
1002		t.Fatal("file should not exist")
1003	}
1004}
1005
1006// Changing a configured backend that supports multi-state to a
1007// backend that only supports single states.
1008func TestMetaBackend_configuredChangeCopy_multiToSingle(t *testing.T) {
1009	// Create a temporary working directory that is empty
1010	td := tempDir(t)
1011	testCopyDir(t, testFixturePath("backend-change-multi-to-single"), td)
1012	defer os.RemoveAll(td)
1013	defer testChdir(t, td)()
1014
1015	// Register the single-state backend
1016	backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
1017	defer backendInit.Set("local-single", nil)
1018
1019	// Ask input
1020	defer testInputMap(t, map[string]string{
1021		"backend-migrate-multistate-to-single": "yes",
1022		"backend-migrate-copy-to-empty":        "yes",
1023	})()
1024
1025	// Setup the meta
1026	m := testMetaBackend(t, nil)
1027
1028	// Get the backend
1029	b, diags := m.Backend(&BackendOpts{Init: true})
1030	if diags.HasErrors() {
1031		t.Fatal(diags.Err())
1032	}
1033
1034	// Check the state
1035	s, err := b.StateMgr(backend.DefaultStateName)
1036	if err != nil {
1037		t.Fatalf("unexpected error: %s", err)
1038	}
1039	if err := s.RefreshState(); err != nil {
1040		t.Fatalf("unexpected error: %s", err)
1041	}
1042	state := s.State()
1043	if state == nil {
1044		t.Fatal("state should not be nil")
1045	}
1046	if testStateMgrCurrentLineage(s) != "backend-change" {
1047		t.Fatalf("bad: %#v", state)
1048	}
1049
1050	// Verify no local state
1051	if _, err := os.Stat(DefaultStateFilename); err == nil {
1052		t.Fatal("file should not exist")
1053	}
1054
1055	// Verify no local backup
1056	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
1057		t.Fatal("file should not exist")
1058	}
1059
1060	// Verify existing workspaces exist
1061	envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
1062	if _, err := os.Stat(envPath); err != nil {
1063		t.Fatal("env should exist")
1064	}
1065
1066	// Verify we are now in the default env, or we may not be able to access the new backend
1067	env, err := m.Workspace()
1068	if err != nil {
1069		t.Fatal(err)
1070	}
1071	if env != backend.DefaultStateName {
1072		t.Fatal("using non-default env with single-env backend")
1073	}
1074}
1075
1076// Changing a configured backend that supports multi-state to a
1077// backend that only supports single states.
1078func TestMetaBackend_configuredChangeCopy_multiToSingleCurrentEnv(t *testing.T) {
1079	// Create a temporary working directory that is empty
1080	td := tempDir(t)
1081	testCopyDir(t, testFixturePath("backend-change-multi-to-single"), td)
1082	defer os.RemoveAll(td)
1083	defer testChdir(t, td)()
1084
1085	// Register the single-state backend
1086	backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
1087	defer backendInit.Set("local-single", nil)
1088
1089	// Ask input
1090	defer testInputMap(t, map[string]string{
1091		"backend-migrate-multistate-to-single": "yes",
1092		"backend-migrate-copy-to-empty":        "yes",
1093	})()
1094
1095	// Setup the meta
1096	m := testMetaBackend(t, nil)
1097
1098	// Change env
1099	if err := m.SetWorkspace("env2"); err != nil {
1100		t.Fatalf("unexpected error: %s", err)
1101	}
1102
1103	// Get the backend
1104	b, diags := m.Backend(&BackendOpts{Init: true})
1105	if diags.HasErrors() {
1106		t.Fatal(diags.Err())
1107	}
1108
1109	// Check the state
1110	s, err := b.StateMgr(backend.DefaultStateName)
1111	if err != nil {
1112		t.Fatalf("unexpected error: %s", err)
1113	}
1114	if err := s.RefreshState(); err != nil {
1115		t.Fatalf("unexpected error: %s", err)
1116	}
1117	state := s.State()
1118	if state == nil {
1119		t.Fatal("state should not be nil")
1120	}
1121	if testStateMgrCurrentLineage(s) != "backend-change-env2" {
1122		t.Fatalf("bad: %#v", state)
1123	}
1124
1125	// Verify no local state
1126	if _, err := os.Stat(DefaultStateFilename); err == nil {
1127		t.Fatal("file should not exist")
1128	}
1129
1130	// Verify no local backup
1131	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
1132		t.Fatal("file should not exist")
1133	}
1134
1135	// Verify existing workspaces exist
1136	envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
1137	if _, err := os.Stat(envPath); err != nil {
1138		t.Fatal("env should exist")
1139	}
1140}
1141
1142// Changing a configured backend that supports multi-state to a
1143// backend that also supports multi-state.
1144func TestMetaBackend_configuredChangeCopy_multiToMulti(t *testing.T) {
1145	// Create a temporary working directory that is empty
1146	td := tempDir(t)
1147	testCopyDir(t, testFixturePath("backend-change-multi-to-multi"), td)
1148	defer os.RemoveAll(td)
1149	defer testChdir(t, td)()
1150
1151	// Ask input
1152	defer testInputMap(t, map[string]string{
1153		"backend-migrate-multistate-to-multistate": "yes",
1154	})()
1155
1156	// Setup the meta
1157	m := testMetaBackend(t, nil)
1158
1159	// Get the backend
1160	b, diags := m.Backend(&BackendOpts{Init: true})
1161	if diags.HasErrors() {
1162		t.Fatal(diags.Err())
1163	}
1164
1165	// Check resulting states
1166	workspaces, err := b.Workspaces()
1167	if err != nil {
1168		t.Fatalf("unexpected error: %s", err)
1169	}
1170
1171	sort.Strings(workspaces)
1172	expected := []string{"default", "env2"}
1173	if !reflect.DeepEqual(workspaces, expected) {
1174		t.Fatalf("bad: %#v", workspaces)
1175	}
1176
1177	{
1178		// Check the default state
1179		s, err := b.StateMgr(backend.DefaultStateName)
1180		if err != nil {
1181			t.Fatalf("unexpected error: %s", err)
1182		}
1183		if err := s.RefreshState(); err != nil {
1184			t.Fatalf("unexpected error: %s", err)
1185		}
1186		state := s.State()
1187		if state == nil {
1188			t.Fatal("state should not be nil")
1189		}
1190		if testStateMgrCurrentLineage(s) != "backend-change" {
1191			t.Fatalf("bad: %#v", state)
1192		}
1193	}
1194
1195	{
1196		// Check the other state
1197		s, err := b.StateMgr("env2")
1198		if err != nil {
1199			t.Fatalf("unexpected error: %s", err)
1200		}
1201		if err := s.RefreshState(); err != nil {
1202			t.Fatalf("unexpected error: %s", err)
1203		}
1204		state := s.State()
1205		if state == nil {
1206			t.Fatal("state should not be nil")
1207		}
1208		if testStateMgrCurrentLineage(s) != "backend-change-env2" {
1209			t.Fatalf("bad: %#v", state)
1210		}
1211	}
1212
1213	// Verify no local backup
1214	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
1215		t.Fatal("file should not exist")
1216	}
1217
1218	{
1219		// Verify existing workspaces exist
1220		envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
1221		if _, err := os.Stat(envPath); err != nil {
1222			t.Fatalf("%s should exist, but does not", envPath)
1223		}
1224	}
1225
1226	{
1227		// Verify new workspaces exist
1228		envPath := filepath.Join("envdir-new", "env2", backendLocal.DefaultStateFilename)
1229		if _, err := os.Stat(envPath); err != nil {
1230			t.Fatalf("%s should exist, but does not", envPath)
1231		}
1232	}
1233}
1234
1235// Changing a configured backend that supports multi-state to a
1236// backend that also supports multi-state, but doesn't allow a
1237// default state while the default state is non-empty.
1238func TestMetaBackend_configuredChangeCopy_multiToNoDefaultWithDefault(t *testing.T) {
1239	// Create a temporary working directory that is empty
1240	td := tempDir(t)
1241	testCopyDir(t, testFixturePath("backend-change-multi-to-no-default-with-default"), td)
1242	defer os.RemoveAll(td)
1243	defer testChdir(t, td)()
1244
1245	// Register the single-state backend
1246	backendInit.Set("local-no-default", backendLocal.TestNewLocalNoDefault)
1247	defer backendInit.Set("local-no-default", nil)
1248
1249	// Ask input
1250	defer testInputMap(t, map[string]string{
1251		"backend-migrate-multistate-to-multistate": "yes",
1252		"new-state-name": "env1",
1253	})()
1254
1255	// Setup the meta
1256	m := testMetaBackend(t, nil)
1257
1258	// Get the backend
1259	b, diags := m.Backend(&BackendOpts{Init: true})
1260	if diags.HasErrors() {
1261		t.Fatal(diags.Err())
1262	}
1263
1264	// Check resulting states
1265	workspaces, err := b.Workspaces()
1266	if err != nil {
1267		t.Fatalf("unexpected error: %s", err)
1268	}
1269
1270	sort.Strings(workspaces)
1271	expected := []string{"env1", "env2"}
1272	if !reflect.DeepEqual(workspaces, expected) {
1273		t.Fatalf("bad: %#v", workspaces)
1274	}
1275
1276	{
1277		// Check the renamed default state
1278		s, err := b.StateMgr("env1")
1279		if err != nil {
1280			t.Fatalf("unexpected error: %s", err)
1281		}
1282		if err := s.RefreshState(); err != nil {
1283			t.Fatalf("unexpected error: %s", err)
1284		}
1285		state := s.State()
1286		if state == nil {
1287			t.Fatal("state should not be nil")
1288		}
1289		if testStateMgrCurrentLineage(s) != "backend-change-env1" {
1290			t.Fatalf("bad: %#v", state)
1291		}
1292	}
1293
1294	{
1295		// Verify existing workspaces exist
1296		envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
1297		if _, err := os.Stat(envPath); err != nil {
1298			t.Fatal("env should exist")
1299		}
1300	}
1301
1302	{
1303		// Verify new workspaces exist
1304		envPath := filepath.Join("envdir-new", "env2", backendLocal.DefaultStateFilename)
1305		if _, err := os.Stat(envPath); err != nil {
1306			t.Fatal("env should exist")
1307		}
1308	}
1309}
1310
1311// Changing a configured backend that supports multi-state to a
1312// backend that also supports multi-state, but doesn't allow a
1313// default state while the default state is empty.
1314func TestMetaBackend_configuredChangeCopy_multiToNoDefaultWithoutDefault(t *testing.T) {
1315	// Create a temporary working directory that is empty
1316	td := tempDir(t)
1317	testCopyDir(t, testFixturePath("backend-change-multi-to-no-default-without-default"), td)
1318	defer os.RemoveAll(td)
1319	defer testChdir(t, td)()
1320
1321	// Register the single-state backend
1322	backendInit.Set("local-no-default", backendLocal.TestNewLocalNoDefault)
1323	defer backendInit.Set("local-no-default", nil)
1324
1325	// Ask input
1326	defer testInputMap(t, map[string]string{
1327		"backend-migrate-multistate-to-multistate": "yes",
1328		"select-workspace":                         "1",
1329	})()
1330
1331	// Setup the meta
1332	m := testMetaBackend(t, nil)
1333
1334	// Get the backend
1335	b, diags := m.Backend(&BackendOpts{Init: true})
1336	if diags.HasErrors() {
1337		t.Fatal(diags.Err())
1338	}
1339
1340	// Check resulting states
1341	workspaces, err := b.Workspaces()
1342	if err != nil {
1343		t.Fatalf("unexpected error: %s", err)
1344	}
1345
1346	sort.Strings(workspaces)
1347	expected := []string{"env2"} // default is skipped because it is absent in the source backend
1348	if !reflect.DeepEqual(workspaces, expected) {
1349		t.Fatalf("wrong workspaces\ngot:  %#v\nwant: %#v", workspaces, expected)
1350	}
1351
1352	{
1353		// Check the named state
1354		s, err := b.StateMgr("env2")
1355		if err != nil {
1356			t.Fatalf("unexpected error: %s", err)
1357		}
1358		if err := s.RefreshState(); err != nil {
1359			t.Fatalf("unexpected error: %s", err)
1360		}
1361		state := s.State()
1362		if state == nil {
1363			t.Fatal("state should not be nil")
1364		}
1365		if testStateMgrCurrentLineage(s) != "backend-change-env2" {
1366			t.Fatalf("bad: %#v", state)
1367		}
1368	}
1369
1370	{
1371		// Verify existing workspaces exist
1372		envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
1373		if _, err := os.Stat(envPath); err != nil {
1374			t.Fatalf("%s should exist, but does not", envPath)
1375		}
1376	}
1377
1378	{
1379		// Verify new workspaces exist
1380		envPath := filepath.Join("envdir-new", "env2", backendLocal.DefaultStateFilename)
1381		if _, err := os.Stat(envPath); err != nil {
1382			t.Fatalf("%s should exist, but does not", envPath)
1383		}
1384	}
1385}
1386
1387// Unsetting a saved backend
1388func TestMetaBackend_configuredUnset(t *testing.T) {
1389	// Create a temporary working directory that is empty
1390	td := tempDir(t)
1391	testCopyDir(t, testFixturePath("backend-unset"), td)
1392	defer os.RemoveAll(td)
1393	defer testChdir(t, td)()
1394
1395	// Ask input
1396	defer testInteractiveInput(t, []string{"no"})()
1397
1398	// Setup the meta
1399	m := testMetaBackend(t, nil)
1400
1401	// Get the backend
1402	b, diags := m.Backend(&BackendOpts{Init: true})
1403	if diags.HasErrors() {
1404		t.Fatal(diags.Err())
1405	}
1406
1407	// Check the state
1408	s, err := b.StateMgr(backend.DefaultStateName)
1409	if err != nil {
1410		t.Fatalf("unexpected error: %s", err)
1411	}
1412	if err := s.RefreshState(); err != nil {
1413		t.Fatalf("unexpected error: %s", err)
1414	}
1415	state := s.State()
1416	if state != nil {
1417		t.Fatal("state should be nil")
1418	}
1419
1420	// Verify the default paths don't exist
1421	if !isEmptyState(DefaultStateFilename) {
1422		data, _ := ioutil.ReadFile(DefaultStateFilename)
1423		t.Fatal("state should not exist, but contains:\n", string(data))
1424	}
1425
1426	// Verify a backup doesn't exist
1427	if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
1428		data, _ := ioutil.ReadFile(DefaultStateFilename + DefaultBackupExtension)
1429		t.Fatal("backup should not exist, but contains:\n", string(data))
1430	}
1431
1432	// Write some state
1433	s.WriteState(testState())
1434	if err := s.PersistState(); err != nil {
1435		t.Fatalf("unexpected error: %s", err)
1436	}
1437
1438	// Verify it exists where we expect it to
1439	if isEmptyState(DefaultStateFilename) {
1440		t.Fatal(DefaultStateFilename, "is empty")
1441	}
1442
1443	// Verify no backup since it was empty to start
1444	if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
1445		data, _ := ioutil.ReadFile(DefaultStateFilename + DefaultBackupExtension)
1446		t.Fatal("backup state should be empty, but contains:\n", string(data))
1447	}
1448}
1449
1450// Unsetting a saved backend and copying the remote state
1451func TestMetaBackend_configuredUnsetCopy(t *testing.T) {
1452	// Create a temporary working directory that is empty
1453	td := tempDir(t)
1454	testCopyDir(t, testFixturePath("backend-unset"), td)
1455	defer os.RemoveAll(td)
1456	defer testChdir(t, td)()
1457
1458	// Ask input
1459	defer testInteractiveInput(t, []string{"yes", "yes"})()
1460
1461	// Setup the meta
1462	m := testMetaBackend(t, nil)
1463
1464	// Get the backend
1465	b, diags := m.Backend(&BackendOpts{Init: true})
1466	if diags.HasErrors() {
1467		t.Fatal(diags.Err())
1468	}
1469
1470	// Check the state
1471	s, err := b.StateMgr(backend.DefaultStateName)
1472	if err != nil {
1473		t.Fatalf("unexpected error: %s", err)
1474	}
1475	if err := s.RefreshState(); err != nil {
1476		t.Fatalf("unexpected error: %s", err)
1477	}
1478	state := s.State()
1479	if state == nil {
1480		t.Fatal("state is nil")
1481	}
1482	if got, want := testStateMgrCurrentLineage(s), "configuredUnset"; got != want {
1483		t.Fatalf("wrong state lineage %q; want %q", got, want)
1484	}
1485
1486	// Verify a backup doesn't exist
1487	if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
1488		t.Fatalf("backup state should be empty")
1489	}
1490
1491	// Write some state
1492	s.WriteState(testState())
1493	if err := s.PersistState(); err != nil {
1494		t.Fatalf("unexpected error: %s", err)
1495	}
1496
1497	// Verify it exists where we expect it to
1498	if _, err := os.Stat(DefaultStateFilename); err != nil {
1499		t.Fatalf("err: %s", err)
1500	}
1501
1502	// Verify a backup since it wasn't empty to start
1503	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
1504		t.Fatal("backup is empty")
1505	}
1506}
1507
1508// A plan that has uses the local backend
1509func TestMetaBackend_planLocal(t *testing.T) {
1510	// Create a temporary working directory that is empty
1511	td := tempDir(t)
1512	testCopyDir(t, testFixturePath("backend-plan-local"), td)
1513	defer os.RemoveAll(td)
1514	defer testChdir(t, td)()
1515
1516	backendConfigBlock := cty.ObjectVal(map[string]cty.Value{
1517		"path":          cty.NullVal(cty.String),
1518		"workspace_dir": cty.NullVal(cty.String),
1519	})
1520	backendConfigRaw, err := plans.NewDynamicValue(backendConfigBlock, backendConfigBlock.Type())
1521	if err != nil {
1522		t.Fatal(err)
1523	}
1524	backendConfig := plans.Backend{
1525		Type:      "local",
1526		Config:    backendConfigRaw,
1527		Workspace: "default",
1528	}
1529
1530	// Setup the meta
1531	m := testMetaBackend(t, nil)
1532
1533	// Get the backend
1534	b, diags := m.BackendForPlan(backendConfig)
1535	if diags.HasErrors() {
1536		t.Fatal(diags.Err())
1537	}
1538
1539	// Check the state
1540	s, err := b.StateMgr(backend.DefaultStateName)
1541	if err != nil {
1542		t.Fatalf("unexpected error: %s", err)
1543	}
1544	if err := s.RefreshState(); err != nil {
1545		t.Fatalf("unexpected error: %s", err)
1546	}
1547	state := s.State()
1548	if state != nil {
1549		t.Fatalf("state should be nil: %#v", state)
1550	}
1551
1552	// The default state file should not exist yet
1553	if !isEmptyState(DefaultStateFilename) {
1554		t.Fatal("expected empty state")
1555	}
1556
1557	// A backup file shouldn't exist yet either.
1558	if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
1559		t.Fatal("expected empty backup")
1560	}
1561
1562	// Verify we have no configured backend
1563	path := filepath.Join(m.DataDir(), DefaultStateFilename)
1564	if _, err := os.Stat(path); err == nil {
1565		t.Fatalf("should not have backend configured")
1566	}
1567
1568	// Write some state
1569	state = states.NewState()
1570	mark := markStateForMatching(state, "changing")
1571
1572	s.WriteState(state)
1573	if err := s.PersistState(); err != nil {
1574		t.Fatalf("unexpected error: %s", err)
1575	}
1576
1577	// Verify the state is where we expect
1578	{
1579		f, err := os.Open(DefaultStateFilename)
1580		if err != nil {
1581			t.Fatalf("err: %s", err)
1582		}
1583		actual, err := statefile.Read(f)
1584		f.Close()
1585		if err != nil {
1586			t.Fatalf("err: %s", err)
1587		}
1588
1589		assertStateHasMarker(t, actual.State, mark)
1590	}
1591
1592	// Verify no local backup
1593	if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
1594		t.Fatalf("backup state should be empty")
1595	}
1596}
1597
1598// A plan with a custom state save path
1599func TestMetaBackend_planLocalStatePath(t *testing.T) {
1600	td := tempDir(t)
1601	testCopyDir(t, testFixturePath("backend-plan-local"), td)
1602	defer os.RemoveAll(td)
1603	defer testChdir(t, td)()
1604
1605	original := testState()
1606	markStateForMatching(original, "hello")
1607
1608	backendConfigBlock := cty.ObjectVal(map[string]cty.Value{
1609		"path":          cty.NullVal(cty.String),
1610		"workspace_dir": cty.NullVal(cty.String),
1611	})
1612	backendConfigRaw, err := plans.NewDynamicValue(backendConfigBlock, backendConfigBlock.Type())
1613	if err != nil {
1614		t.Fatal(err)
1615	}
1616	plannedBackend := plans.Backend{
1617		Type:      "local",
1618		Config:    backendConfigRaw,
1619		Workspace: "default",
1620	}
1621
1622	// Create an alternate output path
1623	statePath := "foo.tfstate"
1624
1625	// put an initial state there that needs to be backed up
1626	err = (statemgr.NewFilesystem(statePath)).WriteState(original)
1627	if err != nil {
1628		t.Fatal(err)
1629	}
1630
1631	// Setup the meta
1632	m := testMetaBackend(t, nil)
1633	m.stateOutPath = statePath
1634
1635	// Get the backend
1636	b, diags := m.BackendForPlan(plannedBackend)
1637	if diags.HasErrors() {
1638		t.Fatal(diags.Err())
1639	}
1640
1641	// Check the state
1642	s, err := b.StateMgr(backend.DefaultStateName)
1643	if err != nil {
1644		t.Fatalf("unexpected error: %s", err)
1645	}
1646	if err := s.RefreshState(); err != nil {
1647		t.Fatalf("unexpected error: %s", err)
1648	}
1649	state := s.State()
1650	if state != nil {
1651		t.Fatal("default workspace state is not nil, but should be because we've not put anything there")
1652	}
1653
1654	// Verify the default path doesn't exist
1655	if _, err := os.Stat(DefaultStateFilename); err == nil {
1656		t.Fatalf("err: %s", err)
1657	}
1658
1659	// Verify a backup doesn't exists
1660	if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
1661		t.Fatal("file should not exist")
1662	}
1663
1664	// Verify we have no configured backend/legacy
1665	path := filepath.Join(m.DataDir(), DefaultStateFilename)
1666	if _, err := os.Stat(path); err == nil {
1667		t.Fatalf("should not have backend configured")
1668	}
1669
1670	// Write some state
1671	state = states.NewState()
1672	mark := markStateForMatching(state, "changing")
1673
1674	s.WriteState(state)
1675	if err := s.PersistState(); err != nil {
1676		t.Fatalf("unexpected error: %s", err)
1677	}
1678
1679	// Verify the state is where we expect
1680	{
1681		f, err := os.Open(statePath)
1682		if err != nil {
1683			t.Fatalf("err: %s", err)
1684		}
1685		actual, err := statefile.Read(f)
1686		f.Close()
1687		if err != nil {
1688			t.Fatalf("err: %s", err)
1689		}
1690
1691		assertStateHasMarker(t, actual.State, mark)
1692	}
1693
1694	// Verify we have a backup
1695	if isEmptyState(statePath + DefaultBackupExtension) {
1696		t.Fatal("backup is empty")
1697	}
1698}
1699
1700// A plan that has no backend config, matching local state
1701func TestMetaBackend_planLocalMatch(t *testing.T) {
1702	// Create a temporary working directory that is empty
1703	td := tempDir(t)
1704	testCopyDir(t, testFixturePath("backend-plan-local-match"), td)
1705	defer os.RemoveAll(td)
1706	defer testChdir(t, td)()
1707
1708	backendConfigBlock := cty.ObjectVal(map[string]cty.Value{
1709		"path":          cty.NullVal(cty.String),
1710		"workspace_dir": cty.NullVal(cty.String),
1711	})
1712	backendConfigRaw, err := plans.NewDynamicValue(backendConfigBlock, backendConfigBlock.Type())
1713	if err != nil {
1714		t.Fatal(err)
1715	}
1716	backendConfig := plans.Backend{
1717		Type:      "local",
1718		Config:    backendConfigRaw,
1719		Workspace: "default",
1720	}
1721
1722	// Setup the meta
1723	m := testMetaBackend(t, nil)
1724
1725	// Get the backend
1726	b, diags := m.BackendForPlan(backendConfig)
1727	if diags.HasErrors() {
1728		t.Fatal(diags.Err())
1729	}
1730
1731	// Check the state
1732	s, err := b.StateMgr(backend.DefaultStateName)
1733	if err != nil {
1734		t.Fatalf("unexpected error: %s", err)
1735	}
1736	if err := s.RefreshState(); err != nil {
1737		t.Fatalf("unexpected error: %s", err)
1738	}
1739	state := s.State()
1740	if state == nil {
1741		t.Fatal("should is nil")
1742	}
1743	if testStateMgrCurrentLineage(s) != "hello" {
1744		t.Fatalf("bad: %#v", state)
1745	}
1746
1747	// Verify the default path
1748	if isEmptyState(DefaultStateFilename) {
1749		t.Fatal("state is empty")
1750	}
1751
1752	// Verify we have no configured backend/legacy
1753	path := filepath.Join(m.DataDir(), DefaultStateFilename)
1754	if _, err := os.Stat(path); err == nil {
1755		t.Fatalf("should not have backend configured")
1756	}
1757
1758	// Write some state
1759	state = states.NewState()
1760	mark := markStateForMatching(state, "changing")
1761
1762	s.WriteState(state)
1763	if err := s.PersistState(); err != nil {
1764		t.Fatalf("unexpected error: %s", err)
1765	}
1766
1767	// Verify the state is where we expect
1768	{
1769		f, err := os.Open(DefaultStateFilename)
1770		if err != nil {
1771			t.Fatalf("err: %s", err)
1772		}
1773		actual, err := statefile.Read(f)
1774		f.Close()
1775		if err != nil {
1776			t.Fatalf("err: %s", err)
1777		}
1778
1779		assertStateHasMarker(t, actual.State, mark)
1780	}
1781
1782	// Verify local backup
1783	if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
1784		t.Fatal("backup is empty")
1785	}
1786}
1787
1788// init a backend using -backend-config options multiple times
1789func TestMetaBackend_configureWithExtra(t *testing.T) {
1790	// Create a temporary working directory that is empty
1791	td := tempDir(t)
1792	testCopyDir(t, testFixturePath("init-backend-empty"), td)
1793	defer os.RemoveAll(td)
1794	defer testChdir(t, td)()
1795
1796	extras := map[string]cty.Value{"path": cty.StringVal("hello")}
1797	m := testMetaBackend(t, nil)
1798	opts := &BackendOpts{
1799		ConfigOverride: configs.SynthBody("synth", extras),
1800		Init:           true,
1801	}
1802
1803	_, cHash, err := m.backendConfig(opts)
1804	if err != nil {
1805		t.Fatal(err)
1806	}
1807
1808	// init the backend
1809	_, diags := m.Backend(&BackendOpts{
1810		ConfigOverride: configs.SynthBody("synth", extras),
1811		Init:           true,
1812	})
1813	if diags.HasErrors() {
1814		t.Fatal(diags.Err())
1815	}
1816
1817	// Check the state
1818	s := testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
1819	if s.Backend.Hash != uint64(cHash) {
1820		t.Fatal("mismatched state and config backend hashes")
1821	}
1822
1823	// init the backend again with the same options
1824	m = testMetaBackend(t, nil)
1825	_, err = m.Backend(&BackendOpts{
1826		ConfigOverride: configs.SynthBody("synth", extras),
1827		Init:           true,
1828	})
1829	if err != nil {
1830		t.Fatalf("unexpected error: %s", err)
1831	}
1832
1833	// Check the state
1834	s = testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
1835	if s.Backend.Hash != uint64(cHash) {
1836		t.Fatal("mismatched state and config backend hashes")
1837	}
1838}
1839
1840// when configuring a default local state, don't delete local state
1841func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) {
1842	// Create a temporary working directory that is empty
1843	td := tempDir(t)
1844	testCopyDir(t, testFixturePath("init-backend-empty"), td)
1845	defer os.RemoveAll(td)
1846	defer testChdir(t, td)()
1847
1848	// // create our local state
1849	orig := states.NewState()
1850	orig.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false)
1851	testStateFileDefault(t, orig)
1852
1853	m := testMetaBackend(t, nil)
1854	m.forceInitCopy = true
1855	// init the backend
1856	_, diags := m.Backend(&BackendOpts{Init: true})
1857	if diags.HasErrors() {
1858		t.Fatal(diags.Err())
1859	}
1860
1861	// check that we can read the state
1862	s := testStateRead(t, DefaultStateFilename)
1863	if s.Empty() {
1864		t.Fatal("our state was deleted")
1865	}
1866}
1867
1868// move options from config to -backend-config
1869func TestMetaBackend_configToExtra(t *testing.T) {
1870	// Create a temporary working directory that is empty
1871	td := tempDir(t)
1872	testCopyDir(t, testFixturePath("init-backend"), td)
1873	defer os.RemoveAll(td)
1874	defer testChdir(t, td)()
1875
1876	// init the backend
1877	m := testMetaBackend(t, nil)
1878	_, err := m.Backend(&BackendOpts{
1879		Init: true,
1880	})
1881	if err != nil {
1882		t.Fatalf("unexpected error: %s", err)
1883	}
1884
1885	// Check the state
1886	s := testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
1887	backendHash := s.Backend.Hash
1888
1889	// init again but remove the path option from the config
1890	cfg := "terraform {\n  backend \"local\" {}\n}\n"
1891	if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil {
1892		t.Fatal(err)
1893	}
1894
1895	// init the backend again with the  options
1896	extras := map[string]cty.Value{"path": cty.StringVal("hello")}
1897	m = testMetaBackend(t, nil)
1898	m.forceInitCopy = true
1899	_, diags := m.Backend(&BackendOpts{
1900		ConfigOverride: configs.SynthBody("synth", extras),
1901		Init:           true,
1902	})
1903	if diags.HasErrors() {
1904		t.Fatal(diags.Err())
1905	}
1906
1907	s = testDataStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
1908
1909	if s.Backend.Hash == backendHash {
1910		t.Fatal("state.Backend.Hash was not updated")
1911	}
1912}
1913
1914// no config; return inmem backend stored in state
1915func TestBackendFromState(t *testing.T) {
1916	td := tempDir(t)
1917	testCopyDir(t, testFixturePath("backend-from-state"), td)
1918	defer os.RemoveAll(td)
1919	defer testChdir(t, td)()
1920
1921	// Setup the meta
1922	m := testMetaBackend(t, nil)
1923	// terraform caches a small "state" file that stores the backend config.
1924	// This test must override m.dataDir so it loads the "terraform.tfstate" file in the
1925	// test directory as the backend config cache
1926	m.OverrideDataDir = td
1927
1928	stateBackend, diags := m.backendFromState()
1929	if diags.HasErrors() {
1930		t.Fatal(diags.Err())
1931	}
1932
1933	if _, ok := stateBackend.(*backendInmem.Backend); !ok {
1934		t.Fatal("did not get expected inmem backend")
1935	}
1936}
1937
1938func testMetaBackend(t *testing.T, args []string) *Meta {
1939	var m Meta
1940	m.Ui = new(cli.MockUi)
1941	view, _ := testView(t)
1942	m.View = view
1943	m.process(args)
1944	f := m.extendedFlagSet("test")
1945	if err := f.Parse(args); err != nil {
1946		t.Fatalf("unexpected error: %s", err)
1947	}
1948
1949	// metaBackend tests are verifying migrate actions
1950	m.migrateState = true
1951
1952	return &m
1953}
1954