command: start migrating to new state package
This commit is contained in:
parent
1eec77378b
commit
579f102f37
|
@ -10,6 +10,9 @@ import (
|
||||||
// Set to true when we're testing
|
// Set to true when we're testing
|
||||||
var test bool = false
|
var test bool = false
|
||||||
|
|
||||||
|
// DefaultDataDir is the default directory for storing local data.
|
||||||
|
const DefaultDataDir = ".terraform"
|
||||||
|
|
||||||
// DefaultStateFilename is the default filename used for the state file.
|
// DefaultStateFilename is the default filename used for the state file.
|
||||||
const DefaultStateFilename = "terraform.tfstate"
|
const DefaultStateFilename = "terraform.tfstate"
|
||||||
|
|
||||||
|
|
134
command/meta.go
134
command/meta.go
|
@ -10,7 +10,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
"github.com/hashicorp/terraform/remote"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
|
@ -24,7 +24,7 @@ type Meta struct {
|
||||||
|
|
||||||
// State read when calling `Context`. This is available after calling
|
// State read when calling `Context`. This is available after calling
|
||||||
// `Context`.
|
// `Context`.
|
||||||
state *terraform.State
|
state state.State
|
||||||
|
|
||||||
// This can be set by the command itself to provide extra hooks.
|
// This can be set by the command itself to provide extra hooks.
|
||||||
extraHooks []terraform.Hook
|
extraHooks []terraform.Hook
|
||||||
|
@ -78,11 +78,6 @@ func (m *Meta) initStatePaths() {
|
||||||
|
|
||||||
// StateOutPath returns the true output path for the state file
|
// StateOutPath returns the true output path for the state file
|
||||||
func (m *Meta) StateOutPath() string {
|
func (m *Meta) StateOutPath() string {
|
||||||
m.initStatePaths()
|
|
||||||
if m.useRemoteState {
|
|
||||||
path, _ := remote.HiddenStatePath()
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
return m.stateOutPath
|
return m.stateOutPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,11 +127,12 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the loaded state
|
// Store the loaded state
|
||||||
state, err := m.loadState()
|
state, statePath, err := State(m.statePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
m.state = state
|
m.state = state
|
||||||
|
m.stateOutPath = statePath
|
||||||
|
|
||||||
// Load the root module
|
// Load the root module
|
||||||
mod, err := module.NewTreeModule("", copts.Path)
|
mod, err := module.NewTreeModule("", copts.Path)
|
||||||
|
@ -154,7 +150,7 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Module = mod
|
opts.Module = mod
|
||||||
opts.State = state
|
opts.State = state.State()
|
||||||
ctx := terraform.NewContext(opts)
|
ctx := terraform.NewContext(opts)
|
||||||
return ctx, false, nil
|
return ctx, false, nil
|
||||||
}
|
}
|
||||||
|
@ -175,6 +171,21 @@ func (m *Meta) InputMode() terraform.InputMode {
|
||||||
return mode
|
return mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// State returns the state for this meta.
|
||||||
|
func (m *Meta) State() (state.State, error) {
|
||||||
|
if m.state != nil {
|
||||||
|
return m.state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
state, _, err := State(m.statePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.state = state
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UIInput returns a UIInput object to be used for asking for input.
|
// UIInput returns a UIInput object to be used for asking for input.
|
||||||
func (m *Meta) UIInput() terraform.UIInput {
|
func (m *Meta) UIInput() terraform.UIInput {
|
||||||
return &UIInput{
|
return &UIInput{
|
||||||
|
@ -182,115 +193,14 @@ func (m *Meta) UIInput() terraform.UIInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// PersistState is used to write out the state, handling backup of
|
// PersistState is used to write out the state, handling backup of
|
||||||
// the existing state file and respecting path configurations.
|
// the existing state file and respecting path configurations.
|
||||||
func (m *Meta) PersistState(s *terraform.State) error {
|
func (m *Meta) PersistState(s *terraform.State) error {
|
||||||
if m.useRemoteState {
|
if err := m.state.WriteState(s); err != nil {
|
||||||
return m.persistRemoteState(s)
|
|
||||||
}
|
|
||||||
return m.persistLocalState(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// persistRemoteState is used to handle persisting a state file
|
|
||||||
// when remote state management is enabled
|
|
||||||
func (m *Meta) persistRemoteState(s *terraform.State) error {
|
|
||||||
log.Printf("[INFO] Persisting state to local cache")
|
|
||||||
if err := remote.PersistState(s); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("[INFO] Uploading state to remote store")
|
|
||||||
change, err := remote.PushState(s.Remote, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !change.SuccessfulPush() {
|
|
||||||
return fmt.Errorf("Failed to upload state: %s", change)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// persistLocalState is used to handle persisting a state file
|
return m.state.PersistState()
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input returns true if we should ask for input for context.
|
// Input returns true if we should ask for input for context.
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/remote"
|
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -182,156 +181,3 @@ func TestMeta_initStatePaths(t *testing.T) {
|
||||||
t.Fatalf("bad: %#v", m)
|
t.Fatalf("bad: %#v", m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMeta_persistLocal(t *testing.T) {
|
|
||||||
tmp, cwd := testCwd(t)
|
|
||||||
defer testFixCwd(t, tmp, cwd)
|
|
||||||
|
|
||||||
m := new(Meta)
|
|
||||||
s := terraform.NewState()
|
|
||||||
if err := m.persistLocalState(s); err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exists, err := remote.ExistsFile(m.stateOutPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
t.Fatalf("state should exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write again, shoudl backup
|
|
||||||
if err := m.persistLocalState(s); err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exists, err = remote.ExistsFile(m.backupPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
t.Fatalf("backup should exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMeta_loadState_remote(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()
|
|
||||||
s.Serial = 1000
|
|
||||||
conf, srv := testRemoteState(t, s, 200)
|
|
||||||
s.Remote = conf
|
|
||||||
defer srv.Close()
|
|
||||||
|
|
||||||
s.Serial = 500
|
|
||||||
if err := remote.PersistState(s); err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m := new(Meta)
|
|
||||||
s1, err := m.loadState()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
if s1.Serial < 1000 {
|
|
||||||
t.Fatalf("Bad: %#v", s1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !m.useRemoteState {
|
|
||||||
t.Fatalf("should enable remote")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMeta_loadState_statePath(t *testing.T) {
|
|
||||||
tmp, cwd := testCwd(t)
|
|
||||||
defer testFixCwd(t, tmp, cwd)
|
|
||||||
|
|
||||||
m := new(Meta)
|
|
||||||
|
|
||||||
s := terraform.NewState()
|
|
||||||
s.Serial = 1000
|
|
||||||
if err := m.persistLocalState(s); err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s1, err := m.loadState()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
if s1.Serial < 1000 {
|
|
||||||
t.Fatalf("Bad: %#v", s1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMeta_loadState_conflict(t *testing.T) {
|
|
||||||
tmp, cwd := testCwd(t)
|
|
||||||
defer testFixCwd(t, tmp, cwd)
|
|
||||||
|
|
||||||
err := remote.EnsureDirectory()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m := new(Meta)
|
|
||||||
|
|
||||||
s := terraform.NewState()
|
|
||||||
if err := remote.PersistState(s); err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
if err := m.persistLocalState(s); err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = m.loadState()
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should error with conflict")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,12 +32,13 @@ func (c *OutputCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
state, err := c.Meta.loadState()
|
stateStore, err := c.Meta.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Error reading state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Error reading state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state := stateStore.State()
|
||||||
if len(state.RootModule().Outputs) == 0 {
|
if len(state.RootModule().Outputs) == 0 {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"The state file has no outputs defined. Define an output\n" +
|
"The state file has no outputs defined. Define an output\n" +
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
statelib "github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var err, planErr, stateErr error
|
var planErr, stateErr error
|
||||||
var path string
|
var path string
|
||||||
var plan *terraform.Plan
|
var plan *terraform.Plan
|
||||||
var state *terraform.State
|
var state *terraform.State
|
||||||
|
@ -68,12 +69,13 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// We should use the default state if it exists.
|
// We should use the default state if it exists.
|
||||||
c.Meta.statePath = DefaultStateFilename
|
stateStore := &statelib.LocalState{Path: DefaultStateFilename}
|
||||||
state, err = c.Meta.loadState()
|
if err := stateStore.RefreshState(); err != nil {
|
||||||
if err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf("Error reading state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Error reading state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = stateStore.State()
|
||||||
if state == nil {
|
if state == nil {
|
||||||
c.Ui.Output("No state.")
|
c.Ui.Output("No state.")
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/hashicorp/terraform/state"
|
||||||
|
"github.com/hashicorp/terraform/state/remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State returns the proper state.State implementation to represent the
|
||||||
|
// current environment.
|
||||||
|
//
|
||||||
|
// localPath is the path to where state would be if stored locally.
|
||||||
|
// dataDir is the path to the local data directory where the remote state
|
||||||
|
// cache would be stored.
|
||||||
|
func State(localPath string) (state.State, string, error) {
|
||||||
|
var result state.State
|
||||||
|
var resultPath string
|
||||||
|
|
||||||
|
// Get the remote state cache path
|
||||||
|
remoteCachePath := filepath.Join(DefaultDataDir, DefaultStateFilename)
|
||||||
|
if _, err := os.Stat(remoteCachePath); err == nil {
|
||||||
|
// We have a remote state, initialize that.
|
||||||
|
result, err = remoteState(remoteCachePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
resultPath = remoteCachePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have a local state?
|
||||||
|
if localPath != "" {
|
||||||
|
local := &state.LocalState{Path: localPath}
|
||||||
|
err := local.RefreshState()
|
||||||
|
if err != nil {
|
||||||
|
isNotExist := false
|
||||||
|
errwrap.Walk(err, func(e error) {
|
||||||
|
if !isNotExist && os.IsNotExist(e) {
|
||||||
|
isNotExist = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if isNotExist {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if result != nil {
|
||||||
|
// We already have a remote state... that is an error.
|
||||||
|
return nil, "", fmt.Errorf(
|
||||||
|
"Remote state found, but state file '%s' also present.",
|
||||||
|
localPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", errwrap.Wrapf(
|
||||||
|
"Error reading local state: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = local
|
||||||
|
resultPath = localPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return whatever state we have
|
||||||
|
return result, resultPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func remoteState(path string) (state.State, error) {
|
||||||
|
// First create the local state for the path
|
||||||
|
local := &state.LocalState{Path: path}
|
||||||
|
if err := local.RefreshState(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
localState := local.State()
|
||||||
|
|
||||||
|
// If there is no remote settings, it is an error
|
||||||
|
if localState.Remote == nil {
|
||||||
|
return nil, fmt.Errorf("Remote state cache has no remote info")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the remote client based on the local state
|
||||||
|
client, err := remote.NewClient(localState.Remote.Type, localState.Remote.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errwrap.Wrapf(fmt.Sprintf(
|
||||||
|
"Error initializing remote driver '%s': {{err}}",
|
||||||
|
localState.Remote.Type), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the remote client
|
||||||
|
durable := &remote.State{Client: client}
|
||||||
|
|
||||||
|
// Create the cached client
|
||||||
|
cache := &state.CacheState{
|
||||||
|
Cache: local,
|
||||||
|
Durable: durable,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the cache
|
||||||
|
if err := cache.RefreshState(); err != nil {
|
||||||
|
return nil, errwrap.Wrapf(
|
||||||
|
"Error reloading remote state: {{err}}", err)
|
||||||
|
}
|
||||||
|
switch cache.RefreshResult() {
|
||||||
|
case state.CacheRefreshNoop:
|
||||||
|
case state.CacheRefreshInit:
|
||||||
|
case state.CacheRefreshLocalNewer:
|
||||||
|
case state.CacheRefreshUpdateLocal:
|
||||||
|
// Write our local state out to the durable storage to start.
|
||||||
|
if err := cache.WriteState(localState); err != nil {
|
||||||
|
return nil, errwrap.Wrapf("Error preparing remote state: {{err}}", err)
|
||||||
|
}
|
||||||
|
if err := cache.PersistState(); err != nil {
|
||||||
|
return nil, errwrap.Wrapf("Error preparing remote state: {{err}}", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errwrap.Wrapf("Error initilizing remote state: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache, nil
|
||||||
|
}
|
|
@ -14,8 +14,10 @@ var Commands map[string]cli.CommandFactory
|
||||||
// Ui is the cli.Ui used for communicating to the outside world.
|
// Ui is the cli.Ui used for communicating to the outside world.
|
||||||
var Ui cli.Ui
|
var Ui cli.Ui
|
||||||
|
|
||||||
const ErrorPrefix = "e:"
|
const (
|
||||||
const OutputPrefix = "o:"
|
ErrorPrefix = "e:"
|
||||||
|
OutputPrefix = "o:"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Ui = &cli.PrefixedUi{
|
Ui = &cli.PrefixedUi{
|
||||||
|
|
|
@ -4,6 +4,14 @@ import (
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// State is the collection of all state interfaces.
|
||||||
|
type State interface {
|
||||||
|
StateReader
|
||||||
|
StateWriter
|
||||||
|
StateRefresher
|
||||||
|
StatePersister
|
||||||
|
}
|
||||||
|
|
||||||
// StateReader is the interface for things that can return a state. Retrieving
|
// StateReader is the interface for things that can return a state. Retrieving
|
||||||
// the state here must not error. Loading the state fresh (an operation that
|
// the state here must not error. Loading the state fresh (an operation that
|
||||||
// can likely error) should be implemented by RefreshState. If a state hasn't
|
// can likely error) should be implemented by RefreshState. If a state hasn't
|
||||||
|
|
Loading…
Reference in New Issue