Пример #1
0
// laodState is used to load the Terraform state. We give precedence
// to a remote state if enabled, and then check the normal state path.
func (m *Meta) loadState() (*terraform.State, error) {
	// Check if we remote state is enabled
	localCache, _, err := remote.ReadLocalState()
	if err != nil {
		return nil, fmt.Errorf("Error loading state: %s", err)
	}

	// Set the state if enabled
	var state *terraform.State
	if localCache != nil {
		// Refresh the state
		log.Printf("[INFO] Refreshing local state...")
		changes, err := remote.RefreshState(localCache.Remote)
		if err != nil {
			return nil, fmt.Errorf("Failed to refresh state: %v", err)
		}
		switch changes {
		case remote.StateChangeNoop:
		case remote.StateChangeInit:
		case remote.StateChangeLocalNewer:
		case remote.StateChangeUpdateLocal:
			// Reload the state since we've udpated
			localCache, _, err = remote.ReadLocalState()
			if err != nil {
				return nil, fmt.Errorf("Error loading state: %s", err)
			}
		default:
			return nil, fmt.Errorf("%s", changes)
		}

		state = localCache
		m.useRemoteState = true
	}

	// Load up the state
	if m.statePath != "" {
		f, err := os.Open(m.statePath)
		if err != nil && os.IsNotExist(err) {
			// If the state file doesn't exist, it is okay, since it
			// is probably a new infrastructure.
			err = nil
		} else if m.useRemoteState && err == nil {
			err = fmt.Errorf("Remote state enabled, but state file '%s' also present.", m.statePath)
			f.Close()
		} else if err == nil {
			state, err = terraform.ReadState(f)
			f.Close()
		}
		if err != nil {
			return nil, fmt.Errorf("Error loading state: %s", err)
		}
	}
	return state, nil
}
Пример #2
0
// Test updating remote config
func TestRemote_updateRemote(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

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

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

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

	local, _, err := remote.ReadLocalState()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	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)
	}
}
Пример #3
0
func TestMeta_persistRemote(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	err := remote.EnsureDirectory()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	s := terraform.NewState()
	conf, srv := testRemoteState(t, s, 200)
	s.Remote = conf
	defer srv.Close()

	m := new(Meta)
	if err := m.persistRemoteState(s); err != nil {
		t.Fatalf("err: %v", err)
	}

	local, _, err := remote.ReadLocalState()
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if local == nil {
		t.Fatalf("state should exist")
	}

	if err := m.persistRemoteState(s); err != nil {
		t.Fatalf("err: %v", err)
	}

	backup := remote.LocalDirectory + "/" + remote.BackupHiddenStateFile
	exists, err := remote.ExistsFile(backup)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if !exists {
		t.Fatalf("backup should exist")
	}
}
Пример #4
0
func (c *PushCommand) Run(args []string) int {
	var force bool
	args = c.Meta.process(args, false)
	cmdFlags := flag.NewFlagSet("push", flag.ContinueOnError)
	cmdFlags.BoolVar(&force, "force", false, "")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Check for a remote state file
	local, _, err := remote.ReadLocalState()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("%s", err))
		return 1
	}
	if local == nil || local.Remote == nil {
		c.Ui.Error("Remote state not enabled!")
		return 1
	}

	// Attempt to push the state
	change, err := remote.PushState(local.Remote, force)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to push state: %v", err))
		return 1
	}

	// Use an error exit code if the update was not a success
	if !change.SuccessfulPush() {
		c.Ui.Error(fmt.Sprintf("%s", change))
		return 1
	} else {
		c.Ui.Output(fmt.Sprintf("%s", change))
	}
	return 0
}
Пример #5
0
// Test initializing blank state
func TestRemote_initBlank(t *testing.T) {
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

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

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

	local, _, err := remote.ReadLocalState()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	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)
	}
}
Пример #6
0
// updateRemoteConfig is used to update the configuration of the
// remote state store
func (c *RemoteCommand) updateRemoteConfig() int {
	// Validate the remote configuration
	if err := c.validateRemoteConfig(); err != nil {
		return 1
	}

	// Read in the local state
	local, _, err := remote.ReadLocalState()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to read local state: %v", err))
		return 1
	}

	// Update the configuration
	local.Remote = &c.remoteConf
	if err := remote.PersistState(local); err != nil {
		c.Ui.Error(fmt.Sprintf("%s", err))
		return 1
	}

	// Success!
	c.Ui.Output("Remote configuration updated")
	return 0
}
Пример #7
0
func (c *PullCommand) Run(args []string) int {
	args = c.Meta.process(args, false)
	cmdFlags := flag.NewFlagSet("pull", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Recover the local state if any
	local, _, err := remote.ReadLocalState()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("%s", err))
		return 1
	}
	if local == nil || local.Remote == nil {
		c.Ui.Error("Remote state not enabled!")
		return 1
	}

	// Attempt the state refresh
	change, err := remote.RefreshState(local.Remote)
	if err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Failed to refresh from remote state: %v", err))
		return 1
	}

	// Use an error exit code if the update was not a success
	if !change.SuccessfulPull() {
		c.Ui.Error(fmt.Sprintf("%s", change))
		return 1
	} else {
		c.Ui.Output(fmt.Sprintf("%s", change))
	}
	return 0
}
Пример #8
0
// Test enabling remote state
func TestRemote_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 := &RemoteCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(testProvider()),
			Ui:          ui,
		},
	}

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

	local, _, err := remote.ReadLocalState()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	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
	exist, err := remote.ExistsFile(DefaultStateFilename + DefaultBackupExtention)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if !exist {
		t.Fatalf("backup should exist")
	}

	// State file should not
	exist, err = remote.ExistsFile(DefaultStateFilename)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if exist {
		t.Fatalf("state file should not exist")
	}
}
Пример #9
0
// disableRemoteState is used to disable remote state management,
// and move the state file into place.
func (c *RemoteCommand) disableRemoteState() int {
	// Get the local state
	local, _, err := remote.ReadLocalState()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to read local state: %v", err))
		return 1
	}

	// Ensure we have the latest state before disabling
	if c.conf.pullOnDisable {
		log.Printf("[INFO] Refreshing local state from remote server")
		change, err := remote.RefreshState(local.Remote)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Failed to refresh from remote state: %v", err))
			return 1
		}

		// Exit if we were unable to update
		if !change.SuccessfulPull() {
			c.Ui.Error(fmt.Sprintf("%s", change))
			return 1
		} else {
			log.Printf("[INFO] %s", change)
		}

		// Reload the local state after the refresh
		local, _, err = remote.ReadLocalState()
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Failed to read local state: %v", err))
			return 1
		}
	}

	// Clear the remote management, and copy into place
	local.Remote = nil
	fh, err := os.Create(c.conf.statePath)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to create state file '%s': %v",
			c.conf.statePath, err))
		return 1
	}
	defer fh.Close()
	if err := terraform.WriteState(local, fh); err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to encode state file '%s': %v",
			c.conf.statePath, err))
		return 1
	}

	// Remove the old state file
	path, err := remote.HiddenStatePath()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to get local state path: %v", err))
		return 1
	}
	if err := os.Remove(path); err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to remove the local state file: %v", err))
		return 1
	}
	return 0
}