split the env command into subcommands
This commit is contained in:
parent
31f033827f
commit
c8526484b3
|
@ -1,13 +1,10 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
|
@ -15,33 +12,19 @@ import (
|
|||
// environments.
|
||||
type EnvCommand struct {
|
||||
Meta
|
||||
|
||||
newEnv string
|
||||
delEnv string
|
||||
statePath string
|
||||
force bool
|
||||
|
||||
// backend returns by Meta.Backend
|
||||
b backend.Backend
|
||||
// MultiState Backend
|
||||
multi backend.MultiState
|
||||
}
|
||||
|
||||
func (c *EnvCommand) Run(args []string) int {
|
||||
args = c.Meta.process(args, true)
|
||||
|
||||
cmdFlags := c.Meta.flagSet("env")
|
||||
cmdFlags.StringVar(&c.newEnv, "new", "", "create a new environment")
|
||||
cmdFlags.StringVar(&c.delEnv, "delete", "", "delete an existing environment")
|
||||
cmdFlags.StringVar(&c.statePath, "state", "", "terraform state file")
|
||||
cmdFlags.BoolVar(&c.force, "force", false, "force removal of a non-empty environment")
|
||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
args = cmdFlags.Args()
|
||||
if len(args) > 1 {
|
||||
c.Ui.Error("0 or 1 arguments expected.\n")
|
||||
if len(args) > 0 {
|
||||
c.Ui.Error("0 arguments expected.\n")
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
|
||||
|
@ -51,240 +34,36 @@ func (c *EnvCommand) Run(args []string) int {
|
|||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
return 1
|
||||
}
|
||||
c.b = b
|
||||
|
||||
multi, ok := b.(backend.MultiState)
|
||||
if !ok {
|
||||
c.Ui.Error(envNotSupported)
|
||||
return 1
|
||||
}
|
||||
c.multi = multi
|
||||
|
||||
if c.newEnv != "" {
|
||||
return c.createEnv()
|
||||
}
|
||||
|
||||
if c.delEnv != "" {
|
||||
return c.deleteEnv()
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
return c.changeEnv(args[0])
|
||||
}
|
||||
|
||||
return c.listEnvs()
|
||||
}
|
||||
|
||||
func (c *EnvCommand) createEnv() int {
|
||||
states, _, err := c.multi.States()
|
||||
for _, s := range states {
|
||||
if c.newEnv == s {
|
||||
c.Ui.Error(fmt.Sprintf(envExists, c.newEnv))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
err = c.multi.ChangeState(c.newEnv)
|
||||
_, current, err := multi.States()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envCreated, c.newEnv),
|
||||
),
|
||||
)
|
||||
|
||||
if c.statePath == "" {
|
||||
// if we're not loading a state, then we're done
|
||||
return 0
|
||||
}
|
||||
|
||||
// load the new state
|
||||
sMgr, err := c.b.State()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
// load the existing state
|
||||
stateFile, err := os.Open(c.statePath)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
s, err := terraform.ReadState(stateFile)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
err = sMgr.WriteState(s)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *EnvCommand) deleteEnv() int {
|
||||
states, current, err := c.multi.States()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
exists := false
|
||||
for _, s := range states {
|
||||
if c.delEnv == s {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
c.Ui.Error(fmt.Sprintf(envDoesNotExist, c.delEnv))
|
||||
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 != c.delEnv {
|
||||
if err := c.multi.ChangeState(c.delEnv); err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
// always try to change back after
|
||||
defer func() {
|
||||
if err := c.multi.ChangeState(current); err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// we need the actual state to see if it's empty
|
||||
sMgr, err := c.b.State()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := sMgr.RefreshState(); err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
empty := sMgr.State().Empty()
|
||||
|
||||
if !empty && !c.force {
|
||||
c.Ui.Error(fmt.Sprintf(envNotEmpty, c.delEnv))
|
||||
return 1
|
||||
}
|
||||
|
||||
err = c.multi.DeleteState(c.delEnv)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envDeleted, c.delEnv),
|
||||
),
|
||||
)
|
||||
|
||||
if !empty {
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envWarnNotEmpty, c.delEnv),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *EnvCommand) changeEnv(name string) int {
|
||||
states, current, err := c.multi.States()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
if current == name {
|
||||
return 0
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, s := range states {
|
||||
if name == s {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
c.Ui.Error(fmt.Sprintf(envDoesNotExist, name))
|
||||
return 1
|
||||
}
|
||||
|
||||
err = c.multi.ChangeState(name)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envChanged, name),
|
||||
),
|
||||
)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *EnvCommand) listEnvs() int {
|
||||
states, current, err := c.multi.States()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
for _, s := range states {
|
||||
if s == current {
|
||||
out.WriteString("* ")
|
||||
} else {
|
||||
out.WriteString(" ")
|
||||
}
|
||||
out.WriteString(s + "\n")
|
||||
}
|
||||
|
||||
c.Ui.Output(out.String())
|
||||
c.Ui.Output(fmt.Sprintf("Current environment is %q\n", current))
|
||||
c.Ui.Output(c.Help())
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *EnvCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: terraform env [options] [NAME]
|
||||
Usage: terraform env
|
||||
|
||||
Create, change and delete Terraform environments.
|
||||
|
||||
By default env will list all configured environments. If NAME is provided,
|
||||
env will change into to that named environment.
|
||||
|
||||
Subcommands:
|
||||
|
||||
Options:
|
||||
|
||||
-new=name Create a new environment.
|
||||
-delete=name Delete an existing environment,
|
||||
|
||||
-state=path Used with -new to copy a state file into the new environment.
|
||||
-force Used with -delete to remove a non-empty environment.
|
||||
list List environments.
|
||||
select Select an environment.
|
||||
new Create a new environment.
|
||||
delete Delete an existing environment.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func TestEnv_createAndChange(t *testing.T) {
|
|||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
c := &EnvCommand{}
|
||||
newCmd := &EnvNewCommand{}
|
||||
|
||||
current, err := currentEnv()
|
||||
if err != nil {
|
||||
|
@ -32,10 +32,10 @@ func TestEnv_createAndChange(t *testing.T) {
|
|||
t.Fatal("current env should be 'default'")
|
||||
}
|
||||
|
||||
args := []string{"-new", "test"}
|
||||
args := []string{"test"}
|
||||
ui := new(cli.MockUi)
|
||||
c.Meta = Meta{Ui: ui}
|
||||
if code := c.Run(args); code != 0 {
|
||||
newCmd.Meta = Meta{Ui: ui}
|
||||
if code := newCmd.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
|
||||
}
|
||||
|
||||
|
@ -47,10 +47,11 @@ func TestEnv_createAndChange(t *testing.T) {
|
|||
t.Fatal("current env should be 'test'")
|
||||
}
|
||||
|
||||
selCmd := &EnvSelectCommand{}
|
||||
args = []string{backend.DefaultStateName}
|
||||
ui = new(cli.MockUi)
|
||||
c.Meta = Meta{Ui: ui}
|
||||
if code := c.Run(args); code != 0 {
|
||||
selCmd.Meta = Meta{Ui: ui}
|
||||
if code := selCmd.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
|
||||
}
|
||||
|
||||
|
@ -74,31 +75,30 @@ func TestEnv_createAndList(t *testing.T) {
|
|||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
c := &EnvCommand{}
|
||||
newCmd := &EnvNewCommand{}
|
||||
|
||||
envs := []string{"test_a", "test_b", "test_c"}
|
||||
|
||||
// create multiple envs
|
||||
for _, env := range envs {
|
||||
args := []string{"-new", env}
|
||||
ui := new(cli.MockUi)
|
||||
c.Meta = Meta{Ui: ui}
|
||||
if code := c.Run(args); code != 0 {
|
||||
newCmd.Meta = Meta{Ui: ui}
|
||||
if code := newCmd.Run([]string{env}); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
|
||||
}
|
||||
}
|
||||
|
||||
// now check the listing
|
||||
expected := "default\n test_a\n test_b\n* test_c"
|
||||
|
||||
listCmd := &EnvListCommand{}
|
||||
ui := new(cli.MockUi)
|
||||
c.Meta = Meta{Ui: ui}
|
||||
listCmd.Meta = Meta{Ui: ui}
|
||||
|
||||
if code := c.Run(nil); code != 0 {
|
||||
if code := listCmd.Run(nil); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(ui.OutputWriter.String())
|
||||
expected := "default\n test_a\n test_b\n* test_c"
|
||||
|
||||
if actual != expected {
|
||||
t.Fatalf("\nexpcted: %q\nactual: %q", expected, actual)
|
||||
}
|
||||
|
@ -132,12 +132,12 @@ func TestEnv_createWithState(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
args := []string{"-new", "test", "-state", "test.tfstate"}
|
||||
args := []string{"-state", "test.tfstate", "test"}
|
||||
ui := new(cli.MockUi)
|
||||
c := &EnvCommand{
|
||||
newCmd := &EnvNewCommand{
|
||||
Meta: Meta{Ui: ui},
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
if code := newCmd.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
|
||||
}
|
||||
|
||||
|
@ -183,11 +183,11 @@ func TestEnv_delete(t *testing.T) {
|
|||
}
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &EnvCommand{
|
||||
delCmd := &EnvDeleteCommand{
|
||||
Meta: Meta{Ui: ui},
|
||||
}
|
||||
args := []string{"-delete", "test"}
|
||||
if code := c.Run(args); code != 0 {
|
||||
args := []string{"test"}
|
||||
if code := delCmd.Run(args); code != 0 {
|
||||
t.Fatalf("failure: %s", ui.ErrorWriter)
|
||||
}
|
||||
|
||||
|
@ -235,19 +235,19 @@ func TestEnv_deleteWithState(t *testing.T) {
|
|||
}
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &EnvCommand{
|
||||
delCmd := &EnvDeleteCommand{
|
||||
Meta: Meta{Ui: ui},
|
||||
}
|
||||
args := []string{"-delete", "test"}
|
||||
if code := c.Run(args); code == 0 {
|
||||
args := []string{"test"}
|
||||
if code := delCmd.Run(args); code == 0 {
|
||||
t.Fatalf("expected failure without -force.\noutput: %s", ui.OutputWriter)
|
||||
}
|
||||
|
||||
ui = new(cli.MockUi)
|
||||
c.Meta.Ui = ui
|
||||
delCmd.Meta.Ui = ui
|
||||
|
||||
args = []string{"-delete", "test", "-force"}
|
||||
if code := c.Run(args); code != 0 {
|
||||
args = []string{"-force", "test"}
|
||||
if code := delCmd.Run(args); code != 0 {
|
||||
t.Fatalf("failure: %s", ui.ErrorWriter)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/mitchellh/cli"
|
||||
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
)
|
||||
|
||||
type EnvDeleteCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *EnvDeleteCommand) Run(args []string) int {
|
||||
args = c.Meta.process(args, true)
|
||||
|
||||
force := false
|
||||
cmdFlags := c.Meta.flagSet("env")
|
||||
cmdFlags.BoolVar(&force, "force", false, "force removal of a non-empty environment")
|
||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
args = cmdFlags.Args()
|
||||
if len(args) != 1 {
|
||||
c.Ui.Error("expected NAME.\n")
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
|
||||
delEnv := args[0]
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
multi, ok := b.(backend.MultiState)
|
||||
if !ok {
|
||||
c.Ui.Error(envNotSupported)
|
||||
return 1
|
||||
}
|
||||
|
||||
states, current, err := multi.States()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
exists := false
|
||||
for _, s := range states {
|
||||
if delEnv == s {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
c.Ui.Error(fmt.Sprintf(envDoesNotExist, delEnv))
|
||||
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())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// we need the actual state to see if it's empty
|
||||
sMgr, err := b.State()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := sMgr.RefreshState(); err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
empty := sMgr.State().Empty()
|
||||
|
||||
if !empty && !force {
|
||||
c.Ui.Error(fmt.Sprintf(envNotEmpty, delEnv))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Lock the state if we can
|
||||
lockInfo := state.NewLockInfo()
|
||||
lockInfo.Operation = "env new"
|
||||
lockID, err := clistate.Lock(sMgr, lockInfo, c.Ui, c.Colorize())
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
||||
return 1
|
||||
}
|
||||
defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize())
|
||||
|
||||
err = multi.DeleteState(delEnv)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envDeleted, delEnv),
|
||||
),
|
||||
)
|
||||
|
||||
if !empty {
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envWarnNotEmpty, delEnv),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
func (c *EnvDeleteCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: terraform env delete [OPTIONS] NAME
|
||||
|
||||
Delete a Terraform environment
|
||||
|
||||
|
||||
Options:
|
||||
|
||||
-force remove a non-empty environment.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *EnvDeleteCommand) Synopsis() string {
|
||||
return "Delete an environment"
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
)
|
||||
|
||||
type EnvListCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *EnvListCommand) Run(args []string) int {
|
||||
args = c.Meta.process(args, true)
|
||||
|
||||
cmdFlags := c.Meta.flagSet("env list")
|
||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
multi, ok := b.(backend.MultiState)
|
||||
if !ok {
|
||||
c.Ui.Error(envNotSupported)
|
||||
return 1
|
||||
}
|
||||
|
||||
states, current, err := multi.States()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
for _, s := range states {
|
||||
if s == current {
|
||||
out.WriteString("* ")
|
||||
} else {
|
||||
out.WriteString(" ")
|
||||
}
|
||||
out.WriteString(s + "\n")
|
||||
}
|
||||
|
||||
c.Ui.Output(out.String())
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *EnvListCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: terraform env list
|
||||
|
||||
List Terraform environments.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *EnvListCommand) Synopsis() string {
|
||||
return "List Environments"
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/mitchellh/cli"
|
||||
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
)
|
||||
|
||||
type EnvNewCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *EnvNewCommand) Run(args []string) int {
|
||||
args = c.Meta.process(args, true)
|
||||
|
||||
statePath := ""
|
||||
|
||||
cmdFlags := c.Meta.flagSet("env new")
|
||||
cmdFlags.StringVar(&statePath, "state", "", "terraform state file")
|
||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
args = cmdFlags.Args()
|
||||
if len(args) != 1 {
|
||||
c.Ui.Error("expected NAME.\n")
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
|
||||
newEnv := args[0]
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
multi, ok := b.(backend.MultiState)
|
||||
if !ok {
|
||||
c.Ui.Error(envNotSupported)
|
||||
return 1
|
||||
}
|
||||
|
||||
states, _, err := multi.States()
|
||||
for _, s := range states {
|
||||
if newEnv == s {
|
||||
c.Ui.Error(fmt.Sprintf(envExists, newEnv))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
err = multi.ChangeState(newEnv)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envCreated, newEnv),
|
||||
),
|
||||
)
|
||||
|
||||
if statePath == "" {
|
||||
// if we're not loading a state, then we're done
|
||||
return 0
|
||||
}
|
||||
|
||||
// load the new Backend state
|
||||
sMgr, err := b.State()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
// Lock the state if we can
|
||||
lockInfo := state.NewLockInfo()
|
||||
lockInfo.Operation = "env new"
|
||||
lockID, err := clistate.Lock(sMgr, lockInfo, c.Ui, c.Colorize())
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
||||
return 1
|
||||
}
|
||||
defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize())
|
||||
|
||||
// read the existing state file
|
||||
stateFile, err := os.Open(statePath)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
s, err := terraform.ReadState(stateFile)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
// save the existing state in the new Backend.
|
||||
err = sMgr.WriteState(s)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *EnvNewCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: terraform env new [OPTIONS] NAME
|
||||
|
||||
Create a new Terraform environment.
|
||||
|
||||
|
||||
Options:
|
||||
|
||||
-state=path Copy an existing state file into the new environment.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *EnvNewCommand) Synopsis() string {
|
||||
return "Create a new environment"
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
type EnvSelectCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *EnvSelectCommand) Run(args []string) int {
|
||||
args = c.Meta.process(args, true)
|
||||
|
||||
cmdFlags := c.Meta.flagSet("env select")
|
||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
args = cmdFlags.Args()
|
||||
if len(args) != 1 {
|
||||
c.Ui.Error("expected NAME.\n")
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
|
||||
multi, ok := b.(backend.MultiState)
|
||||
if !ok {
|
||||
c.Ui.Error(envNotSupported)
|
||||
return 1
|
||||
}
|
||||
|
||||
states, current, err := multi.States()
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
if current == name {
|
||||
return 0
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, s := range states {
|
||||
if name == s {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
c.Ui.Error(fmt.Sprintf(envDoesNotExist, name))
|
||||
return 1
|
||||
}
|
||||
|
||||
err = multi.ChangeState(name)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(
|
||||
c.Colorize().Color(
|
||||
fmt.Sprintf(envChanged, name),
|
||||
),
|
||||
)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *EnvSelectCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: terraform env select NAME
|
||||
|
||||
Change Terraform environment.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *EnvSelectCommand) Synopsis() string {
|
||||
return "Change environments"
|
||||
}
|
24
commands.go
24
commands.go
|
@ -75,6 +75,30 @@ func init() {
|
|||
}, nil
|
||||
},
|
||||
|
||||
"env list": func() (cli.Command, error) {
|
||||
return &command.EnvListCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"env select": func() (cli.Command, error) {
|
||||
return &command.EnvSelectCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"env new": func() (cli.Command, error) {
|
||||
return &command.EnvNewCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"env delete": func() (cli.Command, error) {
|
||||
return &command.EnvDeleteCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"fmt": func() (cli.Command, error) {
|
||||
return &command.FmtCommand{
|
||||
Meta: meta,
|
||||
|
|
Loading…
Reference in New Issue