From b53704ed8747b4740dd0a876d2284c2f5fd0c9e5 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 28 Feb 2017 13:13:03 -0500 Subject: [PATCH] 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. --- backend/local/backend.go | 2 +- command/env_command.go | 5 ++ command/env_command_test.go | 108 ++++++++--------------------------- command/env_delete.go | 30 ++-------- command/env_list.go | 14 ++--- command/env_new.go | 19 +++--- command/env_select.go | 14 ++--- command/meta.go | 44 ++++++++++++++ command/meta_backend.go | 46 ++++++++++----- command/meta_backend_test.go | 65 ++++++++++----------- command/meta_test.go | 35 ++++++++++++ command/output.go | 4 +- command/show.go | 4 +- command/state_command.go | 2 +- command/state_list.go | 5 +- command/state_list_test.go | 16 ++++-- command/state_meta.go | 9 ++- command/state_mv.go | 1 - command/state_mv_test.go | 48 ++++++++++------ command/state_pull.go | 4 +- command/state_pull_test.go | 8 ++- command/state_push.go | 4 +- command/state_push_test.go | 40 ++++++++----- command/state_rm.go | 1 - command/state_rm_test.go | 16 ++++-- command/state_show.go | 4 +- command/state_show_test.go | 40 ++++++++----- command/taint.go | 3 +- command/unlock.go | 4 +- command/untaint.go | 3 +- commands.go | 19 +++--- 31 files changed, 343 insertions(+), 274 deletions(-) diff --git a/backend/local/backend.go b/backend/local/backend.go index a6329404c..84965850b 100644 --- a/backend/local/backend.go +++ b/backend/local/backend.go @@ -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) diff --git a/command/env_command.go b/command/env_command.go index 7701a2b78..425013401 100644 --- a/command/env_command.go +++ b/command/env_command.go @@ -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. ` ) diff --git a/command/env_command_test.go b/command/env_command_test.go index 7caa11932..356c8d66a 100644 --- a/command/env_command_test.go +++ b/command/env_command_test.go @@ -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 -} diff --git a/command/env_delete.go b/command/env_delete.go index b573e57d5..1a773c0cc 100644 --- a/command/env_delete.go +++ b/command/env_delete.go @@ -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 diff --git a/command/env_list.go b/command/env_list.go index cffeb38f3..219b32bd0 100644 --- a/command/env_list.go +++ b/command/env_list.go @@ -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(" ") diff --git a/command/env_new.go b/command/env_new.go index eb5c99076..47457a0c1 100644 --- a/command/env_new.go +++ b/command/env_new.go @@ -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 diff --git a/command/env_select.go b/command/env_select.go index adfd2f056..d13bdd556 100644 --- a/command/env_select.go +++ b/command/env_select.go @@ -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 diff --git a/command/meta.go b/command/meta.go index cc29b2aaf..12f767d2a 100644 --- a/command/meta.go +++ b/command/meta.go @@ -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 +} diff --git a/command/meta_backend.go b/command/meta_backend.go index 629fc9642..d47d6c513 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -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) } diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index fc9cfb5b9..4ce7c1b80 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -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) } diff --git a/command/meta_test.go b/command/meta_test.go index 5dde0f1ff..1a74484fb 100644 --- a/command/meta_test.go +++ b/command/meta_test.go @@ -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) + } +} diff --git a/command/output.go b/command/output.go index 2b12dee62..2f5f6468b 100644 --- a/command/output.go +++ b/command/output.go @@ -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 diff --git a/command/show.go b/command/show.go index 07afae053..9be482811 100644 --- a/command/show.go +++ b/command/show.go @@ -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 diff --git a/command/state_command.go b/command/state_command.go index ce4e0a2ec..7e3a6af1b 100644 --- a/command/state_command.go +++ b/command/state_command.go @@ -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 { diff --git a/command/state_list.go b/command/state_list.go index afc5c9889..0e7436397 100644 --- a/command/state_list.go +++ b/command/state_list.go @@ -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 diff --git a/command/state_list_test.go b/command/state_list_test.go index 86c4f7194..f2c85e26f 100644 --- a/command/state_list_test.go +++ b/command/state_list_test.go @@ -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, + }, }, } diff --git a/command/state_meta.go b/command/state_meta.go index eccfba447..0e31b874b 100644 --- a/command/state_meta.go +++ b/command/state_meta.go @@ -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 } diff --git a/command/state_mv.go b/command/state_mv.go index 7982d7b92..ad6b5c048 100644 --- a/command/state_mv.go +++ b/command/state_mv.go @@ -10,7 +10,6 @@ import ( // StateMvCommand is a Command implementation that shows a single resource. type StateMvCommand struct { - Meta StateMeta } diff --git a/command/state_mv_test.go b/command/state_mv_test.go index d479b4ccb..9fff6ed12 100644 --- a/command/state_mv_test.go +++ b/command/state_mv_test.go @@ -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, + }, }, } diff --git a/command/state_pull.go b/command/state_pull.go index 73059c6cc..a3679a26a 100644 --- a/command/state_pull.go +++ b/command/state_pull.go @@ -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 diff --git a/command/state_pull_test.go b/command/state_pull_test.go index 3176a4c54..d468dbae4 100644 --- a/command/state_pull_test.go +++ b/command/state_pull_test.go @@ -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, + }, }, } diff --git a/command/state_push.go b/command/state_push.go index a2b130f30..b6e7a90d6 100644 --- a/command/state_push.go +++ b/command/state_push.go @@ -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 diff --git a/command/state_push_test.go b/command/state_push_test.go index d63b193f6..43e0e14c6 100644 --- a/command/state_push_test.go +++ b/command/state_push_test.go @@ -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, + }, }, } diff --git a/command/state_rm.go b/command/state_rm.go index b2491c4e8..a80a0d80d 100644 --- a/command/state_rm.go +++ b/command/state_rm.go @@ -9,7 +9,6 @@ import ( // StateRmCommand is a Command implementation that shows a single resource. type StateRmCommand struct { - Meta StateMeta } diff --git a/command/state_rm_test.go b/command/state_rm_test.go index bb3734502..0c3eacb3a 100644 --- a/command/state_rm_test.go +++ b/command/state_rm_test.go @@ -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, + }, }, } diff --git a/command/state_show.go b/command/state_show.go index be6df8bf9..a7a939bff 100644 --- a/command/state_show.go +++ b/command/state_show.go @@ -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 diff --git a/command/state_show_test.go b/command/state_show_test.go index 9e0ede396..f8b56c25f 100644 --- a/command/state_show_test.go +++ b/command/state_show_test.go @@ -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, + }, }, } diff --git a/command/taint.go b/command/taint.go index 0fd2b4de9..487099187 100644 --- a/command/taint.go +++ b/command/taint.go @@ -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 diff --git a/command/unlock.go b/command/unlock.go index b50713aaa..010fd9332 100644 --- a/command/unlock.go +++ b/command/unlock.go @@ -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 diff --git a/command/untaint.go b/command/untaint.go index c3b413252..ab697b823 100644 --- a/command/untaint.go +++ b/command/untaint.go @@ -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 diff --git a/commands.go b/commands.go index f2f7b4eda..e1618003e 100644 --- a/commands.go +++ b/commands.go @@ -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 }, }