terraform/command/state.go

122 lines
3.2 KiB
Go

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
}