Beispiel #1
0
// updateRemoteConfig is used to update the configuration of the
// remote state store
func (c *RemoteConfigCommand) updateRemoteConfig() int {
	// Validate the remote configuration
	if err := c.validateRemoteConfig(); err != nil {
		return 1
	}

	// Read in the local state, which is just the cache of the remote state
	remote := c.stateResult.Remote.Cache

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

	// Success!
	c.Ui.Output("Remote configuration updated")
	return 0
}
Beispiel #2
0
// disableRemoteState is used to disable remote state management,
// and move the state file into place.
func (c *RemoteConfigCommand) disableRemoteState() int {
	if c.stateResult == nil {
		c.Ui.Error(fmt.Sprintf(
			"Internal error. State() must be called internally before remote\n" +
				"state can be disabled. Please report this as a bug."))
		return 1
	}
	if !c.stateResult.State.State().IsRemote() {
		c.Ui.Error(fmt.Sprintf(
			"Remote state is not enabled. Can't disable remote state."))
		return 1
	}
	local := c.stateResult.Local
	remote := c.stateResult.Remote

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

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

	// Clear the remote management, and copy into place
	newState := remote.State()
	newState.Remote = nil
	if err := local.WriteState(newState); err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to encode state file '%s': %s",
			c.conf.statePath, err))
		return 1
	}
	if err := local.PersistState(); err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to encode state file '%s': %s",
			c.conf.statePath, err))
		return 1
	}

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

	return 0
}
Beispiel #3
0
func (c *RemoteConfigCommand) Run(args []string) int {
	args = c.Meta.process(args, false)
	config := make(map[string]string)
	cmdFlags := flag.NewFlagSet("remote", flag.ContinueOnError)
	cmdFlags.BoolVar(&c.conf.disableRemote, "disable", false, "")
	cmdFlags.BoolVar(&c.conf.pullOnDisable, "pull", true, "")
	cmdFlags.StringVar(&c.conf.statePath, "state", DefaultStateFilename, "path")
	cmdFlags.StringVar(&c.conf.backupPath, "backup", "", "path")
	cmdFlags.StringVar(&c.remoteConf.Type, "backend", "atlas", "")
	cmdFlags.Var((*FlagKV)(&config), "backend-config", "config")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := cmdFlags.Parse(args); err != nil {
		c.Ui.Error(fmt.Sprintf("\nError parsing CLI flags: %s", err))
		return 1
	}

	// Lowercase the type
	c.remoteConf.Type = strings.ToLower(c.remoteConf.Type)

	// Set the local state path
	c.statePath = c.conf.statePath

	// Populate the various configurations
	c.remoteConf.Config = config

	// Get the state information. We specifically request the cache only
	// for the remote state here because it is possible the remote state
	// is invalid and we don't want to error.
	stateOpts := c.StateOpts()
	stateOpts.RemoteCacheOnly = true
	if _, err := c.StateRaw(stateOpts); err != nil {
		c.Ui.Error(fmt.Sprintf("Error loading local state: %s", err))
		return 1
	}

	// Get the local and remote [cached] state
	localState := c.stateResult.Local.State()
	var remoteState *terraform.State
	if remote := c.stateResult.Remote; remote != nil {
		remoteState = remote.State()
	}

	// Check if remote state is being disabled
	if c.conf.disableRemote {
		if !remoteState.IsRemote() {
			c.Ui.Error(fmt.Sprintf("Remote state management not enabled! Aborting."))
			return 1
		}
		if !localState.Empty() {
			c.Ui.Error(fmt.Sprintf("State file already exists at '%s'. Aborting.",
				c.conf.statePath))
			return 1
		}

		return c.disableRemoteState()
	}

	// Ensure there is no conflict, and then do the correct operation
	var result int
	haveCache := !remoteState.Empty()
	haveLocal := !localState.Empty()
	switch {
	case haveCache && haveLocal:
		c.Ui.Error(fmt.Sprintf("Remote state is enabled, but non-managed state file '%s' is also present!",
			c.conf.statePath))
		result = 1

	case !haveCache && !haveLocal:
		// If we don't have either state file, initialize a blank state file
		result = c.initBlankState()

	case haveCache && !haveLocal:
		// Update the remote state target potentially
		result = c.updateRemoteConfig()

	case !haveCache && haveLocal:
		// Enable remote state management
		result = c.enableRemoteState()
	}

	// If there was an error, return right away
	if result != 0 {
		return result
	}

	// If we're not pulling, then do nothing
	if !c.conf.pullOnDisable {
		return result
	}

	// Otherwise, refresh the state
	stateResult, err := c.StateRaw(c.StateOpts())
	if err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error while performing the initial pull. The error message is shown\n"+
				"below. Note that remote state was properly configured, so you don't\n"+
				"need to reconfigure. You can now use `push` and `pull` directly.\n"+
				"\n%s", err))
		return 1
	}

	state := stateResult.State
	if err := state.RefreshState(); err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error while performing the initial pull. The error message is shown\n"+
				"below. Note that remote state was properly configured, so you don't\n"+
				"need to reconfigure. You can now use `push` and `pull` directly.\n"+
				"\n%s", err))
		return 1
	}

	c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
		"[reset][bold][green]Remote state configured and pulled.")))
	return 0
}