Example #1
0
// blankState is used to return a serialized form of a blank state
// with only the remote info.
func blankState(conf *terraform.RemoteState) ([]byte, error) {
	blank := terraform.NewState()
	blank.Remote = conf
	buf := bytes.NewBuffer(nil)
	err := terraform.WriteState(blank, buf)
	return buf.Bytes(), err
}
Example #2
0
func TestOutput_stateDefault(t *testing.T) {
	originalState := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Outputs: map[string]string{
					"foo": "bar",
				},
			},
		},
	}

	// Write the state file in a temporary directory with the
	// default filename.
	td, err := ioutil.TempDir("", "tf")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	statePath := filepath.Join(td, DefaultStateFilename)

	f, err := os.Create(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	err = terraform.WriteState(originalState, f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Change to that directory
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Chdir(cwd)

	ui := new(cli.MockUi)
	c := &OutputCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

	args := []string{
		"foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}

	actual := strings.TrimSpace(ui.OutputWriter.String())
	if actual != "bar" {
		t.Fatalf("bad: %#v", actual)
	}
}
Example #3
0
func TestPlan_stateDefault(t *testing.T) {
	originalState := &terraform.State{
		Resources: map[string]*terraform.ResourceState{
			"test_instance.foo": &terraform.ResourceState{
				ID:   "bar",
				Type: "test_instance",
			},
		},
	}

	// Write the state file in a temporary directory with the
	// default filename.
	td, err := ioutil.TempDir("", "tf")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	statePath := filepath.Join(td, DefaultStateFilename)

	f, err := os.Create(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	err = terraform.WriteState(originalState, f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Change to that directory
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Chdir(cwd)

	p := testProvider()
	ui := new(cli.MockUi)
	c := &PlanCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{
		testFixturePath("plan"),
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify that the provider was called with the existing state
	expectedState := originalState.Resources["test_instance.foo"]
	if !reflect.DeepEqual(p.DiffState, expectedState) {
		t.Fatalf("bad: %#v", p.DiffState)
	}
}
Example #4
0
func TestPull_local(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	s := terraform.NewState()
	s.Serial = 10
	conf, srv := testRemoteState(t, s, 200)

	s = terraform.NewState()
	s.Serial = 5
	s.Remote = conf
	defer srv.Close()

	// Store the local state
	buf := bytes.NewBuffer(nil)
	terraform.WriteState(s, buf)
	remote.EnsureDirectory()
	remote.Persist(buf)

	ui := new(cli.MockUi)
	c := &PullCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}
	args := []string{}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}
}
Example #5
0
func TestInit_remoteStateWithLocal(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	statePath := filepath.Join(tmp, DefaultStateFilename)

	// Write some state
	f, err := os.Create(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	err = terraform.WriteState(testState(), f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	ui := new(cli.MockUi)
	c := &InitCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

	args := []string{
		"-backend", "http",
		"-backend-config", "address=http://google.com",
		testFixturePath("init"),
	}
	if code := c.Run(args); code == 0 {
		t.Fatalf("should have failed: \n%s", ui.OutputWriter.String())
	}
}
Example #6
0
func TestAtlasClient_LegitimateConflict(t *testing.T) {
	fakeAtlas := newFakeAtlas(t, testStateSimple)
	srv := fakeAtlas.Server()
	defer srv.Close()
	client, err := atlasFactory(map[string]string{
		"access_token": "sometoken",
		"name":         "someuser/some-test-remote-state",
		"address":      srv.URL,
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	state, err := terraform.ReadState(bytes.NewReader(testStateSimple))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Changing the state but not the serial. Should generate a conflict.
	state.RootModule().Outputs["drift"] = &terraform.OutputState{
		Type:      "string",
		Sensitive: false,
		Value:     "happens",
	}

	var stateJson bytes.Buffer
	if err := terraform.WriteState(state, &stateJson); err != nil {
		t.Fatalf("err: %s", err)
	}
	if err := client.Put(stateJson.Bytes()); err == nil {
		t.Fatal("Expected error from state conflict, got none.")
	}
}
Example #7
0
func TestAtlasClient_NoConflict(t *testing.T) {
	fakeAtlas := newFakeAtlas(t, testStateSimple)
	srv := fakeAtlas.Server()
	defer srv.Close()
	client, err := atlasFactory(map[string]string{
		"access_token": "sometoken",
		"name":         "someuser/some-test-remote-state",
		"address":      srv.URL,
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	state, err := terraform.ReadState(bytes.NewReader(testStateSimple))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	fakeAtlas.NoConflictAllowed(true)

	var stateJson bytes.Buffer
	if err := terraform.WriteState(state, &stateJson); err != nil {
		t.Fatalf("err: %s", err)
	}

	if err := client.Put(stateJson.Bytes()); err != nil {
		t.Fatalf("err: %s", err)
	}
}
Example #8
0
// testClient is a generic function to test any client.
func testClient(t *testing.T, c Client) {
	var buf bytes.Buffer
	s := state.TestStateInitial()
	if err := terraform.WriteState(s, &buf); err != nil {
		t.Fatalf("err: %s", err)
	}
	data := buf.Bytes()

	if err := c.Put(data); err != nil {
		t.Fatalf("put: %s", err)
	}

	p, err := c.Get()
	if err != nil {
		t.Fatalf("get: %s", err)
	}
	if !bytes.Equal(p.Data, data) {
		t.Fatalf("bad: %#v", p)
	}

	if err := c.Delete(); err != nil {
		t.Fatalf("delete: %s", err)
	}

	p, err = c.Get()
	if err != nil {
		t.Fatalf("get: %s", err)
	}
	if p != nil {
		t.Fatalf("bad: %#v", p)
	}
}
// Test enabling remote state
func TestRemoteConfig_enableRemote(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Create a non-remote enabled state
	s := terraform.NewState()
	s.Serial = 5

	// Add the state at the default path
	fh, err := os.Create(DefaultStateFilename)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = terraform.WriteState(s, fh)
	fh.Close()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	ui := new(cli.MockUi)
	c := &RemoteConfigCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

	args := []string{
		"-backend=http",
		"-backend-config", "address=http://example.com",
		"-backend-config", "access_token=test",
		"-pull=false",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}

	remotePath := filepath.Join(DefaultDataDir, DefaultStateFilename)
	ls := &state.LocalState{Path: remotePath}
	if err := ls.RefreshState(); err != nil {
		t.Fatalf("err: %s", err)
	}
	local := ls.State()

	if local.Remote.Type != "http" {
		t.Fatalf("Bad: %#v", local.Remote)
	}
	if local.Remote.Config["address"] != "http://example.com" {
		t.Fatalf("Bad: %#v", local.Remote)
	}
	if local.Remote.Config["access_token"] != "test" {
		t.Fatalf("Bad: %#v", local.Remote)
	}

	// Backup file should exist, state file should not
	testRemoteLocal(t, false)
	testRemoteLocalBackup(t, true)
}
Example #10
0
// StatePersister impl.
func (s *State) PersistState() error {
	s.state.IncrementSerialMaybe(s.readState)

	var buf bytes.Buffer
	if err := terraform.WriteState(s.state, &buf); err != nil {
		return err
	}

	return s.Client.Put(buf.Bytes())
}
Example #11
0
// PersistState is used to persist out the given terraform state
// in our local state cache location.
func PersistState(s *terraform.State) error {
	buf := bytes.NewBuffer(nil)
	if err := terraform.WriteState(s, buf); err != nil {
		return fmt.Errorf("Failed to encode state: %v", err)
	}
	if err := Persist(buf); err != nil {
		return err
	}
	return nil
}
Example #12
0
func TestPlan_disableBackup(t *testing.T) {
	// Write out some prior state
	tf, err := ioutil.TempFile("", "tf")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	statePath := tf.Name()
	defer os.Remove(tf.Name())

	originalState := &terraform.State{
		Resources: map[string]*terraform.ResourceState{
			"test_instance.foo": &terraform.ResourceState{
				ID:   "bar",
				Type: "test_instance",
			},
		},
	}

	err = terraform.WriteState(originalState, tf)
	tf.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	p := testProvider()
	ui := new(cli.MockUi)
	c := &PlanCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{
		"-state", statePath,
		"-backup", "-",
		testFixturePath("plan"),
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify that the provider was called with the existing state
	expectedState := originalState.Resources["test_instance.foo"]
	if !reflect.DeepEqual(p.DiffState, expectedState) {
		t.Fatalf("bad: %#v", p.DiffState)
	}

	// Ensure there is no backup
	_, err = os.Stat(statePath + DefaultBackupExtention)
	if err == nil || !os.IsNotExist(err) {
		t.Fatalf("backup should not exist")
	}
}
Example #13
0
func TestPlan_stateDefault(t *testing.T) {
	originalState := testState()

	// Write the state file in a temporary directory with the
	// default filename.
	td, err := ioutil.TempDir("", "tf")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	statePath := filepath.Join(td, DefaultStateFilename)

	f, err := os.Create(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	err = terraform.WriteState(originalState, f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Change to that directory
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Chdir(cwd)

	p := testProvider()
	ui := new(cli.MockUi)
	c := &PlanCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{
		testFixturePath("plan"),
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify that the provider was called with the existing state
	actual := strings.TrimSpace(p.DiffState.String())
	expected := strings.TrimSpace(testPlanStateDefaultStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}
Example #14
0
// testStateFileDefault writes the state out to the default statefile
// in the cwd. Use `testCwd` to change into a temp cwd.
func testStateFileDefault(t *testing.T, s *terraform.State) string {
	f, err := os.Create(DefaultStateFilename)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer f.Close()

	if err := terraform.WriteState(s, f); err != nil {
		t.Fatalf("err: %s", err)
	}

	return DefaultStateFilename
}
Example #15
0
func WriteState(state *terraform.State, outFile string) error {
	// TODO: Make this file location customizable, and also don't re-open this
	// file every time.
	f, err := os.OpenFile(outFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("error opening state file: %s\n", err)
	}
	err = terraform.WriteState(state, f)
	if err != nil {
		return fmt.Errorf("error writing state: %s\n", err)
	}
	return nil
}
Example #16
0
func testStateFile(t *testing.T, s *terraform.State) string {
	path := testTempFile(t)

	f, err := os.Create(path)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer f.Close()

	if err := terraform.WriteState(s, f); err != nil {
		t.Fatalf("err: %s", err)
	}

	return path
}
Example #17
0
// TestStateInitial is the initial state that a State should have
// for TestState.
func TestStateInitial() *terraform.State {
	initial := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root", "child"},
				Outputs: map[string]interface{}{
					"foo": "bar",
				},
			},
		},
	}

	var scratch bytes.Buffer
	terraform.WriteState(initial, &scratch)
	return initial
}
Example #18
0
// testStateFileRemote writes the state out to the remote statefile
// in the cwd. Use `testCwd` to change into a temp cwd.
func testStateFileRemote(t *testing.T, s *terraform.State) string {
	path := filepath.Join(DefaultDataDir, DefaultStateFilename)
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		t.Fatalf("err: %s", err)
	}

	f, err := os.Create(path)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer f.Close()

	if err := terraform.WriteState(s, f); err != nil {
		t.Fatalf("err: %s", err)
	}

	return path
}
Example #19
0
func testLocalState(t *testing.T) *LocalState {
	f, err := ioutil.TempFile("", "tf")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = terraform.WriteState(TestStateInitial(), f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	ls := &LocalState{Path: f.Name()}
	if err := ls.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}

	return ls
}
// Test the case where both managed and non managed state present
func TestRemoteConfig_managedAndNonManaged(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Persist local remote state
	s := terraform.NewState()
	s.Serial = 5

	// Write the state
	statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)
	state := &state.LocalState{Path: statePath}
	if err := state.WriteState(s); err != nil {
		t.Fatalf("err: %s", err)
	}
	if err := state.PersistState(); err != nil {
		t.Fatalf("err: %s", err)
	}

	// Also put a file at the default path
	fh, err := os.Create(DefaultStateFilename)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = terraform.WriteState(s, fh)
	fh.Close()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	ui := new(cli.MockUi)
	c := &RemoteConfigCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

	args := []string{}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}
}
Example #21
0
// TestStateInitial is the initial state that a State should have
// for TestState.
func TestStateInitial() *terraform.State {
	initial := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root", "child"},
				Outputs: map[string]*terraform.OutputState{
					"foo": &terraform.OutputState{
						Type:      "string",
						Sensitive: false,
						Value:     "bar",
					},
				},
			},
		},
	}

	var scratch bytes.Buffer
	terraform.WriteState(initial, &scratch)
	return initial
}
Example #22
0
func TestAtlasClient_UnresolvableConflict(t *testing.T) {
	fakeAtlas := newFakeAtlas(t, testStateSimple)

	// Something unexpected causes Atlas to conflict in a way that we can't fix.
	fakeAtlas.AlwaysConflict(true)

	srv := fakeAtlas.Server()
	defer srv.Close()
	client, err := atlasFactory(map[string]string{
		"access_token": "sometoken",
		"name":         "someuser/some-test-remote-state",
		"address":      srv.URL,
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	state, err := terraform.ReadState(bytes.NewReader(testStateSimple))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	var stateJson bytes.Buffer
	if err := terraform.WriteState(state, &stateJson); err != nil {
		t.Fatalf("err: %s", err)
	}
	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)
		if err := client.Put(stateJson.Bytes()); err == nil {
			t.Fatal("Expected error from state conflict, got none.")
		}
	}()

	select {
	case <-doneCh:
		// OK
	case <-time.After(500 * time.Millisecond):
		t.Fatalf("Timed out after 500ms, probably because retrying infinitely.")
	}
}
Example #23
0
func TestShow_noArgs(t *testing.T) {
	// Create the default state
	td, err := ioutil.TempDir("", "tf")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	statePath := filepath.Join(td, DefaultStateFilename)

	f, err := os.Create(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	err = terraform.WriteState(testState(), f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Change to the temporary directory
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Chdir(cwd)

	ui := new(cli.MockUi)
	c := &ShowCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

	args := []string{}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
	}
}
Example #24
0
// Atlas returns an HTTP 409 - Conflict if the pushed state reports the same
// Serial number but the checksum of the raw content differs. This can
// sometimes happen when Terraform changes state representation internally
// between versions in a way that's semantically neutral but affects the JSON
// output and therefore the checksum.
//
// Here we detect and handle this situation by ticking the serial and retrying
// iff for the previous state and the proposed state:
//
//   * the serials match
//   * the parsed states are Equal (semantically equivalent)
//
// In other words, in this situation Terraform can override Atlas's detected
// conflict by asserting that the state it is pushing is indeed correct.
func (c *AtlasClient) handleConflict(msg string, state []byte) error {
	log.Printf("[DEBUG] Handling Atlas conflict response: %s", msg)

	if c.conflictHandlingAttempted {
		log.Printf("[DEBUG] Already attempted conflict resolution; returning conflict.")
	} else {
		c.conflictHandlingAttempted = true
		log.Printf("[DEBUG] Atlas reported conflict, checking for equivalent states.")

		payload, err := c.Get()
		if err != nil {
			return conflictHandlingError(err)
		}

		currentState, err := terraform.ReadState(bytes.NewReader(payload.Data))
		if err != nil {
			return conflictHandlingError(err)
		}

		proposedState, err := terraform.ReadState(bytes.NewReader(state))
		if err != nil {
			return conflictHandlingError(err)
		}

		if statesAreEquivalent(currentState, proposedState) {
			log.Printf("[DEBUG] States are equivalent, incrementing serial and retrying.")
			proposedState.Serial++
			var buf bytes.Buffer
			if err := terraform.WriteState(proposedState, &buf); err != nil {
				return conflictHandlingError(err)

			}
			return c.Put(buf.Bytes())
		} else {
			log.Printf("[DEBUG] States are not equivalent, returning conflict.")
		}
	}

	return fmt.Errorf(
		"Atlas detected a remote state conflict.\n\nMessage: %s", msg)
}
func TestRemotePull_local(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	s := terraform.NewState()
	s.Serial = 10
	conf, srv := testRemoteState(t, s, 200)

	s = terraform.NewState()
	s.Serial = 5
	s.Remote = conf
	defer srv.Close()

	// Store the local state
	statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)
	if err := os.MkdirAll(filepath.Dir(statePath), 0755); err != nil {
		t.Fatalf("err: %s", err)
	}
	f, err := os.Create(statePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	err = terraform.WriteState(s, f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	ui := new(cli.MockUi)
	c := &RemotePullCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}
	args := []string{}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}
}
Example #26
0
func TestPlan_state(t *testing.T) {
	// Write out some prior state
	tf, err := ioutil.TempFile("", "tf")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	statePath := tf.Name()
	defer os.Remove(tf.Name())

	originalState := testState()
	err = terraform.WriteState(originalState, tf)
	tf.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	p := testProvider()
	ui := new(cli.MockUi)
	c := &PlanCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{
		"-state", statePath,
		testFixturePath("plan"),
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	// Verify that the provider was called with the existing state
	actual := strings.TrimSpace(p.DiffState.String())
	expected := strings.TrimSpace(testPlanStateStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}
Example #27
0
// WriteState for LocalState always persists the state as well.
//
// StateWriter impl.
func (s *LocalState) WriteState(state *terraform.State) error {
	s.state = state

	path := s.PathOut
	if path == "" {
		path = s.Path
	}

	// If we don't have any state, we actually delete the file if it exists
	if state == nil {
		err := os.Remove(path)
		if err != nil && os.IsNotExist(err) {
			return nil
		}

		return err
	}

	// Create all the directories
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		return err
	}

	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()

	s.state.IncrementSerialMaybe(s.readState)
	s.readState = s.state

	if err := terraform.WriteState(s.state, f); err != nil {
		return err
	}

	s.written = true
	return nil
}
Example #28
0
// Test disabling remote management with a state file in the way
func TestRemote_disable_otherState(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Persist local remote state
	s := terraform.NewState()
	s.Serial = 5
	if err := remote.EnsureDirectory(); err != nil {
		t.Fatalf("err: %v", err)
	}
	if err := remote.PersistState(s); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Also put a file at the default path
	fh, err := os.Create(DefaultStateFilename)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = terraform.WriteState(s, fh)
	fh.Close()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	ui := new(cli.MockUi)
	c := &RemoteCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

	args := []string{"-disable"}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}
}
Example #29
0
func TestShow_noArgsRemoteState(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Pretend like we have a local cache of remote state
	remoteStatePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)
	if err := os.MkdirAll(filepath.Dir(remoteStatePath), 0755); err != nil {
		t.Fatalf("err: %s", err)
	}
	f, err := os.Create(remoteStatePath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	err = terraform.WriteState(testState(), f)
	f.Close()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	ui := new(cli.MockUi)
	c := &ShowCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

	args := []string{}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
	}

	expected := "test_instance.foo"
	actual := ui.OutputWriter.String()
	if !strings.Contains(actual, expected) {
		t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected)
	}
}
Example #30
0
// persistLocalState is used to handle persisting a state file
// when remote state management is disabled.
func (m *Meta) persistLocalState(s *terraform.State) error {
	m.initStatePaths()

	// Create a backup of the state before updating
	if m.backupPath != "-" {
		log.Printf("[INFO] Writing backup state to: %s", m.backupPath)
		if err := remote.CopyFile(m.statePath, m.backupPath); err != nil {
			return fmt.Errorf("Failed to backup state: %v", err)
		}
	}

	// Open the new state file
	fh, err := os.Create(m.stateOutPath)
	if err != nil {
		return fmt.Errorf("Failed to open state file: %v", err)
	}
	defer fh.Close()

	// Write out the state
	if err := terraform.WriteState(s, fh); err != nil {
		return fmt.Errorf("Failed to encode the state: %v", err)
	}
	return nil
}