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 }