// 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 }
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 }
// 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 }