Thread the environment through all commands

Add Env and SetEnv methods to command.Meta to retrieve the current
environment name inside any command.

Make sure all calls to Backend.State contain an environment name, and
make the package compile against the update backend package.
This commit is contained in:
James Bardin 2017-02-28 13:13:03 -05:00
parent f866bb545c
commit b53704ed87
31 changed files with 343 additions and 274 deletions

View File

@ -307,7 +307,7 @@ func (b *Local) StatePaths(name string) (string, string, string, error) {
if name == backend.DefaultStateName {
if statePath == "" {
statePath = name
statePath = DefaultStateFilename
}
} else {
statePath = filepath.Join(DefaultEnvDir, name, DefaultStateFilename)

View File

@ -64,5 +64,10 @@ anyways and risk dangling resources, use the '-force' flag.
The resources managed by the deleted environment may still exist,
but are no longer manageable by Terraform since the state has
been deleted.
`
envDelCurrent = `Environment %[1]q is your active environment!
You cannot delete the currently active environment. Please switch
to another environment and try again.
`
)

View File

@ -4,7 +4,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"testing"
@ -24,10 +23,7 @@ func TestEnv_createAndChange(t *testing.T) {
newCmd := &EnvNewCommand{}
current, err := currentEnv()
if err != nil {
t.Fatal(err)
}
current := newCmd.Env()
if current != backend.DefaultStateName {
t.Fatal("current env should be 'default'")
}
@ -39,12 +35,9 @@ func TestEnv_createAndChange(t *testing.T) {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}
current, err = currentEnv()
if err != nil {
t.Fatal(err)
}
current = newCmd.Env()
if current != "test" {
t.Fatal("current env should be 'test'")
t.Fatalf("current env should be 'test', got %q", current)
}
selCmd := &EnvSelectCommand{}
@ -55,11 +48,7 @@ func TestEnv_createAndChange(t *testing.T) {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}
current, err = currentEnv()
if err != nil {
t.Fatal(err)
}
current = newCmd.Env()
if current != backend.DefaultStateName {
t.Fatal("current env should be 'default'")
}
@ -173,29 +162,35 @@ func TestEnv_delete(t *testing.T) {
t.Fatal(err)
}
current, err := currentEnv()
if err != nil {
t.Fatal(err)
}
if current != "test" {
t.Fatal("wrong env:", current)
}
ui := new(cli.MockUi)
delCmd := &EnvDeleteCommand{
Meta: Meta{Ui: ui},
}
args := []string{"test"}
if code := delCmd.Run(args); code != 0 {
t.Fatalf("failure: %s", ui.ErrorWriter)
current := delCmd.Env()
if current != "test" {
t.Fatal("wrong env:", current)
}
current, err = currentEnv()
if err != nil {
// we can't delete out current environment
args := []string{"test"}
if code := delCmd.Run(args); code == 0 {
t.Fatal("expected error deleting current env")
}
// change back to default
if err := delCmd.SetEnv(backend.DefaultStateName); err != nil {
t.Fatal(err)
}
// try the delete again
ui = new(cli.MockUi)
delCmd.Meta.Ui = ui
if code := delCmd.Run(args); code != 0 {
t.Fatalf("error deleting env: %s", ui.ErrorWriter)
}
current = delCmd.Env()
if current != backend.DefaultStateName {
t.Fatalf("wrong env: %q", current)
}
@ -255,58 +250,3 @@ func TestEnv_deleteWithState(t *testing.T) {
t.Fatal("env 'test' still exists!")
}
}
func currentEnv() (string, error) {
contents, err := ioutil.ReadFile(filepath.Join(DefaultDataDir, local.DefaultEnvFile))
if os.IsNotExist(err) {
return backend.DefaultStateName, nil
}
if err != nil {
return "", err
}
current := strings.TrimSpace(string(contents))
if current == "" {
current = backend.DefaultStateName
}
return current, nil
}
func envStatePath() (string, error) {
currentEnv, err := currentEnv()
if err != nil {
return "", err
}
if currentEnv == backend.DefaultStateName {
return DefaultStateFilename, nil
}
return filepath.Join(local.DefaultEnvDir, currentEnv, DefaultStateFilename), nil
}
func listEnvs() ([]string, error) {
entries, err := ioutil.ReadDir(local.DefaultEnvDir)
// no error if there's no envs configured
if os.IsNotExist(err) {
return []string{backend.DefaultStateName}, nil
}
if err != nil {
return nil, err
}
var envs []string
for _, entry := range entries {
if entry.IsDir() {
envs = append(envs, filepath.Base(entry.Name()))
}
}
sort.Strings(envs)
// always start with "default"
envs = append([]string{backend.DefaultStateName}, envs...)
return envs, nil
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"strings"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/mitchellh/cli"
@ -46,13 +45,7 @@ func (c *EnvDeleteCommand) Run(args []string) int {
return 1
}
multi, ok := b.(backend.MultiState)
if !ok {
c.Ui.Error(envNotSupported)
return 1
}
states, current, err := multi.States()
states, err := b.States()
if err != nil {
c.Ui.Error(err.Error())
return 1
@ -71,24 +64,13 @@ func (c *EnvDeleteCommand) Run(args []string) int {
return 1
}
// In order to check if the state being deleted is empty, we need to change
// to that state and load it.
if current != delEnv {
if err := multi.ChangeState(delEnv); err != nil {
c.Ui.Error(err.Error())
return 1
}
// always try to change back after
defer func() {
if err := multi.ChangeState(current); err != nil {
c.Ui.Error(err.Error())
}
}()
if delEnv == c.Env() {
c.Ui.Error(fmt.Sprintf(envDelCurrent, delEnv))
return 1
}
// we need the actual state to see if it's empty
sMgr, err := b.State()
sMgr, err := b.State(delEnv)
if err != nil {
c.Ui.Error(err.Error())
return 1
@ -116,7 +98,7 @@ func (c *EnvDeleteCommand) Run(args []string) int {
}
defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize())
err = multi.DeleteState(delEnv)
err = b.DeleteState(delEnv)
if err != nil {
c.Ui.Error(err.Error())
return 1

View File

@ -4,8 +4,6 @@ import (
"bytes"
"fmt"
"strings"
"github.com/hashicorp/terraform/backend"
)
type EnvListCommand struct {
@ -34,21 +32,17 @@ func (c *EnvListCommand) Run(args []string) int {
return 1
}
multi, ok := b.(backend.MultiState)
if !ok {
c.Ui.Error(envNotSupported)
return 1
}
states, current, err := multi.States()
states, err := b.States()
if err != nil {
c.Ui.Error(err.Error())
return 1
}
env := c.Env()
var out bytes.Buffer
for _, s := range states {
if s == current {
if s == env {
out.WriteString("* ")
} else {
out.WriteString(" ")

View File

@ -5,7 +5,6 @@ import (
"os"
"strings"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
@ -49,13 +48,7 @@ func (c *EnvNewCommand) Run(args []string) int {
return 1
}
multi, ok := b.(backend.MultiState)
if !ok {
c.Ui.Error(envNotSupported)
return 1
}
states, _, err := multi.States()
states, err := b.States()
for _, s := range states {
if newEnv == s {
c.Ui.Error(fmt.Sprintf(envExists, newEnv))
@ -63,12 +56,18 @@ func (c *EnvNewCommand) Run(args []string) int {
}
}
err = multi.ChangeState(newEnv)
_, err = b.State(newEnv)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
// now save the current env locally
if err := c.SetEnv(newEnv); err != nil {
c.Ui.Error(fmt.Sprintf("error saving new environment name: %s", err))
return 1
}
c.Ui.Output(
c.Colorize().Color(
fmt.Sprintf(envCreated, newEnv),
@ -81,7 +80,7 @@ func (c *EnvNewCommand) Run(args []string) int {
}
// load the new Backend state
sMgr, err := b.State()
sMgr, err := b.State(newEnv)
if err != nil {
c.Ui.Error(err.Error())
return 1

View File

@ -4,7 +4,6 @@ import (
"fmt"
"strings"
"github.com/hashicorp/terraform/backend"
"github.com/mitchellh/cli"
)
@ -41,19 +40,14 @@ func (c *EnvSelectCommand) Run(args []string) int {
name := args[0]
multi, ok := b.(backend.MultiState)
if !ok {
c.Ui.Error(envNotSupported)
return 1
}
states, current, err := multi.States()
states, err := b.States()
if err != nil {
c.Ui.Error(err.Error())
return 1
}
if current == name {
if name == c.Env() {
// already using this env
return 0
}
@ -70,7 +64,7 @@ func (c *EnvSelectCommand) Run(args []string) int {
return 1
}
err = multi.ChangeState(name)
err = c.SetEnv(name)
if err != nil {
c.Ui.Error(err.Error())
return 1

View File

@ -2,6 +2,7 @@ package command
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
@ -14,6 +15,8 @@ import (
"time"
"github.com/hashicorp/go-getter"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/backend/local"
"github.com/hashicorp/terraform/helper/experiment"
"github.com/hashicorp/terraform/helper/variables"
"github.com/hashicorp/terraform/helper/wrappedstreams"
@ -406,3 +409,44 @@ func (m *Meta) outputShadowError(err error, output bool) bool {
return true
}
// Env returns the name of the currently configured environment, corresponding
// to the desired named state.
func (m *Meta) Env() string {
dataDir := m.dataDir
if m.dataDir == "" {
dataDir = DefaultDataDir
}
envData, err := ioutil.ReadFile(filepath.Join(dataDir, local.DefaultEnvFile))
current := string(bytes.TrimSpace(envData))
if current == "" {
current = backend.DefaultStateName
}
// return default if the file simply doesn't exist
if err != nil && !os.IsNotExist(err) {
log.Printf("[ERROR] failed to read current environment: %s", err)
}
return current
}
// SetEnv saves the named environment to the local filesystem.
func (m *Meta) SetEnv(name string) error {
dataDir := m.dataDir
if m.dataDir == "" {
dataDir = DefaultDataDir
}
err := os.MkdirAll(dataDir, 0755)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dataDir, local.DefaultEnvFile), []byte(name), 0644)
if err != nil {
return err
}
return nil
}

View File

@ -148,6 +148,7 @@ func (m *Meta) Operation() *backend.Operation {
PlanOutBackend: m.backendState,
Targets: m.targets,
UIIn: m.UIInput(),
Environment: m.Env(),
}
}
@ -526,8 +527,10 @@ func (m *Meta) backendFromPlan(opts *BackendOpts) (backend.Backend, error) {
return nil, err
}
env := m.Env()
// Get the state so we can determine the effect of using this plan
realMgr, err := b.State()
realMgr, err := b.State(env)
if err != nil {
return nil, fmt.Errorf("Error reading state: %s", err)
}
@ -642,7 +645,10 @@ func (m *Meta) backend_c_r_S(
if err != nil {
return nil, fmt.Errorf(strings.TrimSpace(errBackendLocalRead), err)
}
localState, err := localB.State()
env := m.Env()
localState, err := localB.State(env)
if err != nil {
return nil, fmt.Errorf(strings.TrimSpace(errBackendLocalRead), err)
}
@ -656,7 +662,7 @@ func (m *Meta) backend_c_r_S(
return nil, fmt.Errorf(
strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err)
}
backendState, err := b.State()
backendState, err := b.State(env)
if err != nil {
return nil, fmt.Errorf(
strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err)
@ -751,7 +757,10 @@ func (m *Meta) backend_c_R_S(
if err != nil {
return nil, fmt.Errorf(errBackendLocalRead, err)
}
localState, err := localB.State()
env := m.Env()
localState, err := localB.State(env)
if err != nil {
return nil, fmt.Errorf(errBackendLocalRead, err)
}
@ -782,7 +791,7 @@ func (m *Meta) backend_c_R_S(
if err != nil {
return nil, err
}
oldState, err := oldB.State()
oldState, err := oldB.State(env)
if err != nil {
return nil, fmt.Errorf(
strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err)
@ -884,7 +893,10 @@ func (m *Meta) backend_C_R_s(
if err != nil {
return nil, err
}
oldState, err := oldB.State()
env := m.Env()
oldState, err := oldB.State(env)
if err != nil {
return nil, fmt.Errorf(
strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err)
@ -895,7 +907,7 @@ func (m *Meta) backend_C_R_s(
}
// Get the new state
newState, err := b.State()
newState, err := b.State(env)
if err != nil {
return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err)
}
@ -949,7 +961,10 @@ func (m *Meta) backend_C_r_s(
if err != nil {
return nil, fmt.Errorf(errBackendLocalRead, err)
}
localState, err := localB.State()
env := m.Env()
localState, err := localB.State(env)
if err != nil {
return nil, fmt.Errorf(errBackendLocalRead, err)
}
@ -960,7 +975,7 @@ func (m *Meta) backend_C_r_s(
// If the local state is not empty, we need to potentially do a
// state migration to the new backend (with user permission).
if localS := localState.State(); !localS.Empty() {
backendState, err := b.State()
backendState, err := b.State(env)
if err != nil {
return nil, fmt.Errorf(errBackendRemoteRead, err)
}
@ -1065,7 +1080,9 @@ func (m *Meta) backend_C_r_S_changed(
"Error loading previously configured backend: %s", err)
}
oldState, err := oldB.State()
env := m.Env()
oldState, err := oldB.State(env)
if err != nil {
return nil, fmt.Errorf(
strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err)
@ -1076,7 +1093,7 @@ func (m *Meta) backend_C_r_S_changed(
}
// Get the new state
newState, err := b.State()
newState, err := b.State(env)
if err != nil {
return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err)
}
@ -1226,7 +1243,10 @@ func (m *Meta) backend_C_R_S_unchanged(
if err != nil {
return nil, err
}
oldState, err := oldB.State()
env := m.Env()
oldState, err := oldB.State(env)
if err != nil {
return nil, fmt.Errorf(
strings.TrimSpace(errBackendSavedUnsetConfig), s.Remote.Type, err)
@ -1237,7 +1257,7 @@ func (m *Meta) backend_C_R_S_unchanged(
}
// Get the new state
newState, err := b.State()
newState, err := b.State(env)
if err != nil {
return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err)
}

View File

@ -7,6 +7,7 @@ import (
"strings"
"testing"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/helper/copy"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform"
@ -29,7 +30,7 @@ func TestMetaBackend_emptyDir(t *testing.T) {
}
// Write some state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -99,7 +100,7 @@ func TestMetaBackend_emptyWithDefaultState(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -172,7 +173,7 @@ func TestMetaBackend_emptyWithExplicitState(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -231,7 +232,7 @@ func TestMetaBackend_emptyLegacyRemote(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -280,7 +281,7 @@ func TestMetaBackend_configureNew(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -349,7 +350,7 @@ func TestMetaBackend_configureNewWithState(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -425,7 +426,7 @@ func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -470,7 +471,7 @@ func TestMetaBackend_configureNewWithStateExisting(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -544,7 +545,7 @@ func TestMetaBackend_configureNewWithStateExistingNoMigrate(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -618,7 +619,7 @@ func TestMetaBackend_configureNewLegacy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -712,7 +713,7 @@ func TestMetaBackend_configureNewLegacyCopy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -798,7 +799,7 @@ func TestMetaBackend_configuredUnchanged(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -845,7 +846,7 @@ func TestMetaBackend_configuredChange(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -924,7 +925,7 @@ func TestMetaBackend_configuredChangeCopy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -971,7 +972,7 @@ func TestMetaBackend_configuredUnset(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1055,7 +1056,7 @@ func TestMetaBackend_configuredUnsetCopy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1134,7 +1135,7 @@ func TestMetaBackend_configuredUnchangedLegacy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1237,7 +1238,7 @@ func TestMetaBackend_configuredUnchangedLegacyCopy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1340,7 +1341,7 @@ func TestMetaBackend_configuredChangedLegacy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1440,7 +1441,7 @@ func TestMetaBackend_configuredChangedLegacyCopyBackend(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1543,7 +1544,7 @@ func TestMetaBackend_configuredChangedLegacyCopyLegacy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1646,7 +1647,7 @@ func TestMetaBackend_configuredChangedLegacyCopyBoth(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1749,7 +1750,7 @@ func TestMetaBackend_configuredUnsetWithLegacyNoCopy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1839,7 +1840,7 @@ func TestMetaBackend_configuredUnsetWithLegacyCopyBackend(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -1937,7 +1938,7 @@ func TestMetaBackend_configuredUnsetWithLegacyCopyLegacy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -2035,7 +2036,7 @@ func TestMetaBackend_configuredUnsetWithLegacyCopyBoth(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -2136,7 +2137,7 @@ func TestMetaBackend_planLocal(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -2233,7 +2234,7 @@ func TestMetaBackend_planLocalStatePath(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -2319,7 +2320,7 @@ func TestMetaBackend_planLocalMatch(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -2519,7 +2520,7 @@ func TestMetaBackend_planBackendEmptyDir(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -2621,7 +2622,7 @@ func TestMetaBackend_planBackendMatch(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}
@ -2784,7 +2785,7 @@ func TestMetaBackend_planLegacy(t *testing.T) {
}
// Check the state
s, err := b.State()
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("bad: %s", err)
}

View File

@ -8,6 +8,7 @@ import (
"reflect"
"testing"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/terraform"
)
@ -272,3 +273,37 @@ func TestMeta_addModuleDepthFlag(t *testing.T) {
}
}
}
func TestMeta_Env(t *testing.T) {
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
m := new(Meta)
env := m.Env()
if env != backend.DefaultStateName {
t.Fatalf("expected env %q, got env %q", backend.DefaultStateName, env)
}
testEnv := "test_env"
if err := m.SetEnv(testEnv); err != nil {
t.Fatal("error setting env:", err)
}
env = m.Env()
if env != testEnv {
t.Fatalf("expected env %q, got env %q", testEnv, env)
}
if err := m.SetEnv(backend.DefaultStateName); err != nil {
t.Fatal("error setting env:", err)
}
env = m.Env()
if env != backend.DefaultStateName {
t.Fatalf("expected env %q, got env %q", backend.DefaultStateName, env)
}
}

View File

@ -50,8 +50,10 @@ func (c *OutputCommand) Run(args []string) int {
return 1
}
env := c.Env()
// Get the state
stateStore, err := b.State()
stateStore, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1

View File

@ -74,8 +74,10 @@ func (c *ShowCommand) Run(args []string) int {
return 1
}
env := c.Env()
// Get the state
stateStore, err := b.State()
stateStore, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1

View File

@ -9,7 +9,7 @@ import (
// StateCommand is a Command implementation that just shows help for
// the subcommands nested below it.
type StateCommand struct {
Meta
StateMeta
}
func (c *StateCommand) Run(args []string) int {

View File

@ -11,7 +11,7 @@ import (
// StateListCommand is a Command implementation that lists the resources
// within a state file.
type StateListCommand struct {
Meta
StateMeta
}
func (c *StateListCommand) Run(args []string) int {
@ -31,8 +31,9 @@ func (c *StateListCommand) Run(args []string) int {
return 1
}
env := c.Env()
// Get the state
state, err := b.State()
state, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1

View File

@ -16,9 +16,11 @@ func TestStateList(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateListCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -47,9 +49,11 @@ func TestStateList_backendState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateListCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}

View File

@ -11,7 +11,9 @@ import (
)
// StateMeta is the meta struct that should be embedded in state subcommands.
type StateMeta struct{}
type StateMeta struct {
Meta
}
// State returns the state for this meta. This is different then Meta.State
// in the way that backups are done. This configures backups to be timestamped
@ -23,8 +25,9 @@ func (c *StateMeta) State(m *Meta) (state.State, error) {
return nil, err
}
env := c.Env()
// Get the state
s, err := b.State()
s, err := b.State(env)
if err != nil {
return nil, err
}
@ -36,7 +39,7 @@ func (c *StateMeta) State(m *Meta) (state.State, error) {
panic(err)
}
localB := localRaw.(*backendlocal.Local)
_, stateOutPath, _, err := localB.StatePaths()
_, stateOutPath, _, err := localB.StatePaths(env)
if err != nil {
return nil, err
}

View File

@ -10,7 +10,6 @@ import (
// StateMvCommand is a Command implementation that shows a single resource.
type StateMvCommand struct {
Meta
StateMeta
}

View File

@ -46,9 +46,11 @@ func TestStateMv(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -113,9 +115,11 @@ func TestStateMv_backupExplicit(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -168,9 +172,11 @@ func TestStateMv_stateOutNew(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -240,9 +246,11 @@ func TestStateMv_stateOutExisting(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -281,9 +289,11 @@ func TestStateMv_noState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -342,9 +352,11 @@ func TestStateMv_stateOutNew_count(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}

View File

@ -11,7 +11,6 @@ import (
// StatePullCommand is a Command implementation that shows a single resource.
type StatePullCommand struct {
Meta
StateMeta
}
@ -32,7 +31,8 @@ func (c *StatePullCommand) Run(args []string) int {
}
// Get the state
state, err := b.State()
env := c.Env()
state, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1

View File

@ -20,9 +20,11 @@ func TestStatePull(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StatePullCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}

View File

@ -11,7 +11,6 @@ import (
// StatePushCommand is a Command implementation that shows a single resource.
type StatePushCommand struct {
Meta
StateMeta
}
@ -52,7 +51,8 @@ func (c *StatePushCommand) Run(args []string) int {
}
// Get the state
state, err := b.State()
env := c.Env()
state, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load destination state: %s", err))
return 1

View File

@ -20,9 +20,11 @@ func TestStatePush_empty(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StatePushCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -49,9 +51,11 @@ func TestStatePush_replaceMatch(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StatePushCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -78,9 +82,11 @@ func TestStatePush_lineageMismatch(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StatePushCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -107,9 +113,11 @@ func TestStatePush_serialNewer(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StatePushCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -136,9 +144,11 @@ func TestStatePush_serialOlder(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StatePushCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}

View File

@ -9,7 +9,6 @@ import (
// StateRmCommand is a Command implementation that shows a single resource.
type StateRmCommand struct {
Meta
StateMeta
}

View File

@ -46,9 +46,11 @@ func TestStateRm(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateRmCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -112,9 +114,11 @@ func TestStateRm_backupExplicit(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateRmCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}

View File

@ -12,7 +12,6 @@ import (
// StateShowCommand is a Command implementation that shows a single resource.
type StateShowCommand struct {
Meta
StateMeta
}
@ -34,7 +33,8 @@ func (c *StateShowCommand) Run(args []string) int {
}
// Get the state
state, err := b.State()
env := c.Env()
state, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1

View File

@ -34,9 +34,11 @@ func TestStateShow(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateShowCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -92,9 +94,11 @@ func TestStateShow_multi(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateShowCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -114,9 +118,11 @@ func TestStateShow_noState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateShowCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -134,9 +140,11 @@ func TestStateShow_emptyState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateShowCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}
@ -163,9 +171,11 @@ func TestStateShow_emptyStateWithModule(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &StateShowCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
StateMeta: StateMeta{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
},
}

View File

@ -67,7 +67,8 @@ func (c *TaintCommand) Run(args []string) int {
}
// Get the state
st, err := b.State()
env := c.Env()
st, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1

View File

@ -52,7 +52,8 @@ func (c *UnlockCommand) Run(args []string) int {
return 1
}
st, err := b.State()
env := c.Env()
st, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1
@ -102,7 +103,6 @@ func (c *UnlockCommand) Run(args []string) int {
}
}
// FIXME: unlock should require the lock ID
if err := s.Unlock(lockID); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to unlock state: %s", err))
return 1

View File

@ -55,7 +55,8 @@ func (c *UntaintCommand) Run(args []string) int {
}
// Get the state
st, err := b.State()
env := c.Env()
st, err := b.State(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1

View File

@ -46,6 +46,11 @@ func init() {
"debug": struct{}{}, // includes all subcommands
}
// meta struct used in state commands
stateMeta := command.StateMeta{
Meta: meta,
}
Commands = map[string]cli.CommandFactory{
"apply": func() (cli.Command, error) {
return &command.ApplyCommand{
@ -217,43 +222,43 @@ func init() {
"state": func() (cli.Command, error) {
return &command.StateCommand{
Meta: meta,
StateMeta: stateMeta,
}, nil
},
"state list": func() (cli.Command, error) {
return &command.StateListCommand{
Meta: meta,
StateMeta: stateMeta,
}, nil
},
"state rm": func() (cli.Command, error) {
return &command.StateRmCommand{
Meta: meta,
StateMeta: stateMeta,
}, nil
},
"state mv": func() (cli.Command, error) {
return &command.StateMvCommand{
Meta: meta,
StateMeta: stateMeta,
}, nil
},
"state pull": func() (cli.Command, error) {
return &command.StatePullCommand{
Meta: meta,
StateMeta: stateMeta,
}, nil
},
"state push": func() (cli.Command, error) {
return &command.StatePushCommand{
Meta: meta,
StateMeta: stateMeta,
}, nil
},
"state show": func() (cli.Command, error) {
return &command.StateShowCommand{
Meta: meta,
StateMeta: stateMeta,
}, nil
},
}