command: shallow UI-focused rename of "environment" to "workspace"

Feedback after 0.9 was that the term "environment" was confusing due to
it colliding with several other concepts, such as OS environment
variables, a non-aligned Terraform Enterprise concept, and differing ideas
of "environment" within various organizations.

This new term "workspace" is intended to ease some of that confusion. This
term is not used anywhere else in Terraform today, and we expect it to not
be used in a manner that would be confusing within user organizations.

This begins a deprecation cycle for the "terraform env" family of commands,
instead moving to an equivalent set of "terraform workspace" commands.

There are some remaining references to the old "environment" concept in
the code, which will be cleaned up in a separate change. This change is
instead focused on text visible in the UI and wording within code comments
for the benefit of human maintainers of the code.
This commit is contained in:
Martin Atkins 2017-05-30 15:06:13 -07:00
parent c25d848ffb
commit 31d556894f
13 changed files with 310 additions and 241 deletions

View File

@ -1639,7 +1639,7 @@ func TestApply_terraformEnvNonDefault(t *testing.T) {
// Create new env
{
ui := new(cli.MockUi)
newCmd := &EnvNewCommand{}
newCmd := &WorkspaceNewCommand{}
newCmd.Meta = Meta{Ui: ui}
if code := newCmd.Run([]string{"test"}); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -1650,7 +1650,7 @@ func TestApply_terraformEnvNonDefault(t *testing.T) {
{
args := []string{"test"}
ui := new(cli.MockUi)
selCmd := &EnvSelectCommand{}
selCmd := &WorkspaceSelectCommand{}
selCmd.Meta = Meta{Ui: ui}
if code := selCmd.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)

View File

@ -1,123 +0,0 @@
package command
import (
"net/url"
"strings"
)
// EnvCommand is a Command Implementation that manipulates local state
// environments.
type EnvCommand struct {
Meta
}
func (c *EnvCommand) Run(args []string) int {
args = c.Meta.process(args, true)
cmdFlags := c.Meta.flagSet("env")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
c.Ui.Output(c.Help())
return 0
}
func (c *EnvCommand) Help() string {
helpText := `
Usage: terraform env
Create, change and delete Terraform environments.
Subcommands:
list List environments.
select Select an environment.
new Create a new environment.
delete Delete an existing environment.
`
return strings.TrimSpace(helpText)
}
func (c *EnvCommand) Synopsis() string {
return "Environment management"
}
// validEnvName returns true is this name is valid to use as an environment name.
// Since most named states are accessed via a filesystem path or URL, check if
// escaping the name would be required.
func validEnvName(name string) bool {
return name == url.PathEscape(name)
}
const (
envNotSupported = `Backend does not support environments`
envExists = `Environment %q already exists`
envDoesNotExist = `
Environment %q doesn't exist!
You can create this environment with the "new" option.`
envChanged = `[reset][green]Switched to environment %q!`
envCreated = `
[reset][green][bold]Created and switched to environment %q![reset][green]
You're now on a new, empty environment. Environments isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
`
envDeleted = `[reset][green]Deleted environment %q!`
envNotEmpty = `
Environment %[1]q is not empty!
Deleting %[1]q can result in dangling resources: resources that
exist but are no longer manageable by Terraform. Please destroy
these resources first. If you want to delete this environment
anyways and risk dangling resources, use the '-force' flag.
`
envWarnNotEmpty = `[reset][yellow]WARNING: %q was non-empty.
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.
`
envInvalidName = `
The environment name %q is not allowed. The name must contain only URL safe
characters, and no path separators.
`
envIsOverriddenNote = `
The active environment is being overridden using the TF_ENVIRONMENT environment
variable.
`
envIsOverriddenSelectError = `
The environment is currently overridden using the TF_ENVIRONMENT environment
variable.
To select a new environment, either update this environment variable or unset
it and then run this command again.
`
envIsOverriddenNewError = `
The environment is currently overridden using the TF_ENVIRONMENT environment
variable. You cannot create a different environment when using this setting.
To create a new environment, either unset this environment variable or update it
to match the environment name you are trying to create, and then run this command
again.
`
)

View File

@ -455,22 +455,22 @@ func (m *Meta) outputShadowError(err error, output bool) bool {
}
// EnvironmentNameEnvVar is the name of the environment variable (ie, the POSIX
// feature) that can be used to set the name of the Terraform environment
// (overriding the environment chosen by `terraform env select`). Note that
// this environment variable is ignored by `terraform env new` and `terraform
// env delete`.
const EnvironmentNameEnvVar = "TF_ENVIRONMENT"
// feature) that can be used to set the name of the Terraform workspace
// (overriding the workspace chosen by `terraform workspace select`). Note that
// this environment variable is ignored by `terraform workspace new` and
// `terraform workspace delete`.
const EnvironmentNameEnvVar = "TF_WORKSPACE"
// Env returns the name of the currently configured environment, corresponding
// Env returns the name of the currently configured workspace, corresponding
// to the desired named state.
func (m *Meta) Env() string {
current, _ := m.EnvOverridden()
return current
}
// EnvOverridden returns the name of the currently configured environment,
// EnvOverridden returns the name of the currently configured workspace,
// corresponding to the desired named state, as well as a bool saying whether
// this was set via the TF_ENVIRONMENT environment variable.
// this was set via the TF_WORKSPACE environment variable.
func (m *Meta) EnvOverridden() (string, bool) {
if envVar := os.Getenv(EnvironmentNameEnvVar); envVar != "" {
return envVar, true
@ -488,14 +488,15 @@ func (m *Meta) EnvOverridden() (string, bool) {
}
if err != nil && !os.IsNotExist(err) {
// always return the default if we can't get an environment name
log.Printf("[ERROR] failed to read current environment: %s", err)
// always return the default if we can't get a workspace name
log.Printf("[ERROR] failed to read current workspace: %s", err)
}
return current, false
}
// SetEnv saves the named environment to the local filesystem.
// SetEnv saves the given name as the current workspace in the local
// filesystem.
func (m *Meta) SetEnv(name string) error {
dataDir := m.dataDir
if m.dataDir == "" {

View File

@ -117,7 +117,7 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
migrate, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-multistate-to-multistate",
Query: fmt.Sprintf(
"Do you want to migrate all environments to %q?",
"Do you want to migrate all workspaces to %q?",
opts.TwoType),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateMultiToMulti),
@ -171,8 +171,8 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
migrate, err = m.confirm(&terraform.InputOpts{
Id: "backend-migrate-multistate-to-single",
Query: fmt.Sprintf(
"Destination state %q doesn't support environments (named states).\n"+
"Do you want to copy only your current environment?",
"Destination state %q doesn't support workspaces.\n"+
"Do you want to copy only your current workspace?",
opts.TwoType),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateMultiToSingle),
@ -458,17 +458,17 @@ above error and try again.
`
const errMigrateMulti = `
Error migrating the environment %q from %q to %q:
Error migrating the workspace %q from %q to %q:
%s
Terraform copies environments in alphabetical order. Any environments
alphabetically earlier than this one have been copied. Any environments
later than this haven't been modified in the destination. No environments
Terraform copies workspaces in alphabetical order. Any workspaces
alphabetically earlier than this one have been copied. Any workspaces
later than this haven't been modified in the destination. No workspaces
in the source state have been modified.
Please resolve the error above and run the initialization command again.
This will attempt to copy (with permission) all environments again.
This will attempt to copy (with permission) all workspaces again.
`
const errBackendStateCopy = `
@ -497,22 +497,22 @@ and "no" to start with the existing state in %[2]q.
`
const inputBackendMigrateMultiToSingle = `
The existing backend %[1]q supports environments and you currently are
using more than one. The target backend %[2]q doesn't support environments.
If you continue, Terraform will offer to copy your current environment
%[3]q to the default environment in the target. Your existing environments
in the source backend won't be modified. If you want to switch environments,
The existing backend %[1]q supports workspaces and you currently are
using more than one. The target backend %[2]q doesn't support workspaces.
If you continue, Terraform will offer to copy your current workspace
%[3]q to the default workspace in the target. Your existing workspaces
in the source backend won't be modified. If you want to switch workspaces,
back them up, or cancel altogether, answer "no" and Terraform will abort.
`
const inputBackendMigrateMultiToMulti = `
Both the existing backend %[1]q and the target backend %[2]q support
environments. When migrating between backends, Terraform will copy all
environments (with the same names). THIS WILL OVERWRITE any conflicting
workspaces. When migrating between backends, Terraform will copy all
workspaces (with the same names). THIS WILL OVERWRITE any conflicting
states in the destination.
Terraform initialization doesn't currently migrate only select environments.
If you want to migrate a select number of environments, you must manually
Terraform initialization doesn't currently migrate only select workspaces.
If you want to migrate a select number of workspaces, you must manually
pull and push those states.
If you answer "yes", Terraform will migrate all states. If you answer

View File

@ -1249,7 +1249,7 @@ func TestMetaBackend_configuredChangeCopy_multiToSingle(t *testing.T) {
t.Fatal("file should not exist")
}
// Verify existing environments exist
// Verify existing workspaces exist
envPath := filepath.Join(backendlocal.DefaultEnvDir, "env2", backendlocal.DefaultStateFilename)
if _, err := os.Stat(envPath); err != nil {
t.Fatal("env should exist")
@ -1321,7 +1321,7 @@ func TestMetaBackend_configuredChangeCopy_multiToSingleCurrentEnv(t *testing.T)
t.Fatal("file should not exist")
}
// Verify existing environments exist
// Verify existing workspaces exist
envPath := filepath.Join(backendlocal.DefaultEnvDir, "env2", backendlocal.DefaultStateFilename)
if _, err := os.Stat(envPath); err != nil {
t.Fatal("env should exist")
@ -1406,7 +1406,7 @@ func TestMetaBackend_configuredChangeCopy_multiToMulti(t *testing.T) {
}
{
// Verify existing environments exist
// Verify existing workspaces exist
envPath := filepath.Join(backendlocal.DefaultEnvDir, "env2", backendlocal.DefaultStateFilename)
if _, err := os.Stat(envPath); err != nil {
t.Fatal("env should exist")
@ -1414,7 +1414,7 @@ func TestMetaBackend_configuredChangeCopy_multiToMulti(t *testing.T) {
}
{
// Verify new environments exist
// Verify new workspaces exist
envPath := filepath.Join("envdir-new", "env2", backendlocal.DefaultStateFilename)
if _, err := os.Stat(envPath); err != nil {
t.Fatal("env should exist")

View File

@ -51,8 +51,8 @@ func (c *PushCommand) Run(args []string) int {
// This is a map of variables specifically from the CLI that we want to overwrite.
// We need this because there is a chance that the user is trying to modify
// a variable we don't see in our context, but which exists in this atlas
// environment.
// a variable we don't see in our context, but which exists in this Terraform
// Enterprise workspace.
cliVars := make(map[string]string)
for k, v := range c.variables {
if _, ok := overwriteMap[k]; ok {

View File

@ -0,0 +1,144 @@
package command
import (
"net/url"
"strings"
"github.com/mitchellh/cli"
)
// WorkspaceCommand is a Command Implementation that manipulates workspaces,
// which allow multiple distinct states and variables from a single config.
type WorkspaceCommand struct {
Meta
LegacyName bool
}
func (c *WorkspaceCommand) Run(args []string) int {
args = c.Meta.process(args, true)
envCommandShowWarning(c.Ui, c.LegacyName)
cmdFlags := c.Meta.flagSet("workspace")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
c.Ui.Output(c.Help())
return 0
}
func (c *WorkspaceCommand) Help() string {
helpText := `
Usage: terraform workspace
Create, change and delete Terraform workspaces.
Subcommands:
list List workspaces.
select Select a workspace.
new Create a new workspace.
delete Delete an existing workspace.
`
return strings.TrimSpace(helpText)
}
func (c *WorkspaceCommand) Synopsis() string {
return "Workspace management"
}
// validEnvName returns true is this name is valid to use as a workspace name.
// Since most named states are accessed via a filesystem path or URL, check if
// escaping the name would be required.
func validEnvName(name string) bool {
return name == url.PathEscape(name)
}
func envCommandShowWarning(ui cli.Ui, show bool) {
if !show {
return
}
ui.Warn(`Warning: the "terraform env" family of commands is deprecated.
"Workspace" is now the preferred term for what earlier Terraform versions
called "environment", to reduce ambiguity caused by the latter term colliding
with other concepts.
The "terraform workspace" commands should be used instead. "terraform env"
will be removed in a future Terraform version.
`)
}
const (
envNotSupported = `Backend does not support multiple workspaces`
envExists = `Workspace %q already exists`
envDoesNotExist = `
Workspace %q doesn't exist.
You can create this workspace with the "new" subcommand.`
envChanged = `[reset][green]Switched to workspace %q.`
envCreated = `
[reset][green][bold]Created and switched to workspace %q![reset][green]
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
`
envDeleted = `[reset][green]Deleted workspace %q!`
envNotEmpty = `
Workspace %[1]q is not empty.
Deleting %[1]q can result in dangling resources: resources that
exist but are no longer manageable by Terraform. Please destroy
these resources first. If you want to delete this workspace
anyway and risk dangling resources, use the '-force' flag.
`
envWarnNotEmpty = `[reset][yellow]WARNING: %q was non-empty.
The resources managed by the deleted workspace may still exist,
but are no longer manageable by Terraform since the state has
been deleted.
`
envDelCurrent = `
Workspace %[1]q is your active workspace.
You cannot delete the currently active workspace. Please switch
to another workspace and try again.
`
envInvalidName = `
The workspace name %q is not allowed. The name must contain only URL safe
characters, and no path separators.
`
envIsOverriddenNote = `
The active workspace is being overridden using the TF_WORKSPACE environment
variable.
`
envIsOverriddenSelectError = `
The selected workspace is currently overridden using the TF_WORKSPACE
environment variable.
To select a new workspace, either update this environment variable or unset
it and then run this command again.
`
envIsOverriddenNewError = `
The workspace is currently overridden using the TF_WORKSPACE environment
variable. You cannot create a new workspace when using this setting.
To create a new workspace, either unset this environment variable or update it
to match the workspace name you are trying to create, and then run this command
again.
`
)

View File

@ -14,18 +14,18 @@ import (
"github.com/mitchellh/cli"
)
func TestEnv_createAndChange(t *testing.T) {
func TestWorkspace_createAndChange(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
newCmd := &EnvNewCommand{}
newCmd := &WorkspaceNewCommand{}
current := newCmd.Env()
if current != backend.DefaultStateName {
t.Fatal("current env should be 'default'")
t.Fatal("current workspace should be 'default'")
}
args := []string{"test"}
@ -37,10 +37,10 @@ func TestEnv_createAndChange(t *testing.T) {
current = newCmd.Env()
if current != "test" {
t.Fatalf("current env should be 'test', got %q", current)
t.Fatalf("current workspace should be 'test', got %q", current)
}
selCmd := &EnvSelectCommand{}
selCmd := &WorkspaceSelectCommand{}
args = []string{backend.DefaultStateName}
ui = new(cli.MockUi)
selCmd.Meta = Meta{Ui: ui}
@ -50,14 +50,14 @@ func TestEnv_createAndChange(t *testing.T) {
current = newCmd.Env()
if current != backend.DefaultStateName {
t.Fatal("current env should be 'default'")
t.Fatal("current workspace should be 'default'")
}
}
// Create some environments and test the list output.
// Create some workspaces and test the list output.
// This also ensures we switch to the correct env after each call
func TestEnv_createAndList(t *testing.T) {
func TestWorkspace_createAndList(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
os.MkdirAll(td, 0755)
@ -74,11 +74,11 @@ func TestEnv_createAndList(t *testing.T) {
t.Fatal(err)
}
newCmd := &EnvNewCommand{}
newCmd := &WorkspaceNewCommand{}
envs := []string{"test_a", "test_b", "test_c"}
// create multiple envs
// create multiple workspaces
for _, env := range envs {
ui := new(cli.MockUi)
newCmd.Meta = Meta{Ui: ui}
@ -87,7 +87,7 @@ func TestEnv_createAndList(t *testing.T) {
}
}
listCmd := &EnvListCommand{}
listCmd := &WorkspaceListCommand{}
ui := new(cli.MockUi)
listCmd.Meta = Meta{Ui: ui}
@ -104,18 +104,18 @@ func TestEnv_createAndList(t *testing.T) {
}
// Don't allow names that aren't URL safe
func TestEnv_createInvalid(t *testing.T) {
func TestWorkspace_createInvalid(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
newCmd := &EnvNewCommand{}
newCmd := &WorkspaceNewCommand{}
envs := []string{"test_a*", "test_b/foo", "../../../test_c", "好_d"}
// create multiple envs
// create multiple workspaces
for _, env := range envs {
ui := new(cli.MockUi)
newCmd.Meta = Meta{Ui: ui}
@ -124,8 +124,8 @@ func TestEnv_createInvalid(t *testing.T) {
}
}
// list envs to make sure none were created
listCmd := &EnvListCommand{}
// list workspaces to make sure none were created
listCmd := &WorkspaceListCommand{}
ui := new(cli.MockUi)
listCmd.Meta = Meta{Ui: ui}
@ -141,7 +141,7 @@ func TestEnv_createInvalid(t *testing.T) {
}
}
func TestEnv_createWithState(t *testing.T) {
func TestWorkspace_createWithState(t *testing.T) {
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
@ -171,7 +171,7 @@ func TestEnv_createWithState(t *testing.T) {
args := []string{"-state", "test.tfstate", "test"}
ui := new(cli.MockUi)
newCmd := &EnvNewCommand{
newCmd := &WorkspaceNewCommand{
Meta: Meta{Ui: ui},
}
if code := newCmd.Run(args); code != 0 {
@ -191,18 +191,18 @@ func TestEnv_createWithState(t *testing.T) {
}
}
func TestEnv_delete(t *testing.T) {
func TestWorkspace_delete(t *testing.T) {
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// create the env directories
// create the workspace directories
if err := os.MkdirAll(filepath.Join(local.DefaultEnvDir, "test"), 0755); err != nil {
t.Fatal(err)
}
// create the environment file
// create the workspace file
if err := os.MkdirAll(DefaultDataDir, 0755); err != nil {
t.Fatal(err)
}
@ -211,19 +211,19 @@ func TestEnv_delete(t *testing.T) {
}
ui := new(cli.MockUi)
delCmd := &EnvDeleteCommand{
delCmd := &WorkspaceDeleteCommand{
Meta: Meta{Ui: ui},
}
current := delCmd.Env()
if current != "test" {
t.Fatal("wrong env:", current)
t.Fatal("wrong workspace:", current)
}
// we can't delete out current environment
// we can't delete our current workspace
args := []string{"test"}
if code := delCmd.Run(args); code == 0 {
t.Fatal("expected error deleting current env")
t.Fatal("expected error deleting current workspace")
}
// change back to default
@ -235,21 +235,21 @@ func TestEnv_delete(t *testing.T) {
ui = new(cli.MockUi)
delCmd.Meta.Ui = ui
if code := delCmd.Run(args); code != 0 {
t.Fatalf("error deleting env: %s", ui.ErrorWriter)
t.Fatalf("error deleting workspace: %s", ui.ErrorWriter)
}
current = delCmd.Env()
if current != backend.DefaultStateName {
t.Fatalf("wrong env: %q", current)
t.Fatalf("wrong workspace: %q", current)
}
}
func TestEnv_deleteWithState(t *testing.T) {
func TestWorkspace_deleteWithState(t *testing.T) {
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// create the env directories
// create the workspace directories
if err := os.MkdirAll(filepath.Join(local.DefaultEnvDir, "test"), 0755); err != nil {
t.Fatal(err)
}
@ -278,7 +278,7 @@ func TestEnv_deleteWithState(t *testing.T) {
}
ui := new(cli.MockUi)
delCmd := &EnvDeleteCommand{
delCmd := &WorkspaceDeleteCommand{
Meta: Meta{Ui: ui},
}
args := []string{"test"}

View File

@ -10,16 +10,19 @@ import (
"github.com/mitchellh/cli"
)
type EnvDeleteCommand struct {
type WorkspaceDeleteCommand struct {
Meta
LegacyName bool
}
func (c *EnvDeleteCommand) Run(args []string) int {
func (c *WorkspaceDeleteCommand) Run(args []string) int {
args = c.Meta.process(args, true)
envCommandShowWarning(c.Ui, c.LegacyName)
force := false
cmdFlags := c.Meta.flagSet("env")
cmdFlags.BoolVar(&force, "force", false, "force removal of a non-empty environment")
cmdFlags := c.Meta.flagSet("workspace")
cmdFlags.BoolVar(&force, "force", false, "force removal of a non-empty workspace")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -108,7 +111,7 @@ func (c *EnvDeleteCommand) Run(args []string) int {
// Lock the state if we can
lockInfo := state.NewLockInfo()
lockInfo.Operation = "env delete"
lockInfo.Operation = "workspace delete"
lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, c.Ui, c.Colorize())
if err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
@ -139,20 +142,20 @@ func (c *EnvDeleteCommand) Run(args []string) int {
return 0
}
func (c *EnvDeleteCommand) Help() string {
func (c *WorkspaceDeleteCommand) Help() string {
helpText := `
Usage: terraform env delete [OPTIONS] NAME [DIR]
Usage: terraform workspace delete [OPTIONS] NAME [DIR]
Delete a Terraform environment
Delete a Terraform workspace
Options:
-force remove a non-empty environment.
-force remove a non-empty workspace.
`
return strings.TrimSpace(helpText)
}
func (c *EnvDeleteCommand) Synopsis() string {
return "Delete an environment"
func (c *WorkspaceDeleteCommand) Synopsis() string {
return "Delete a workspace"
}

View File

@ -6,14 +6,17 @@ import (
"strings"
)
type EnvListCommand struct {
type WorkspaceListCommand struct {
Meta
LegacyName bool
}
func (c *EnvListCommand) Run(args []string) int {
func (c *WorkspaceListCommand) Run(args []string) int {
args = c.Meta.process(args, true)
cmdFlags := c.Meta.flagSet("env list")
envCommandShowWarning(c.Ui, c.LegacyName)
cmdFlags := c.Meta.flagSet("workspace list")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -69,15 +72,15 @@ func (c *EnvListCommand) Run(args []string) int {
return 0
}
func (c *EnvListCommand) Help() string {
func (c *WorkspaceListCommand) Help() string {
helpText := `
Usage: terraform env list [DIR]
Usage: terraform workspace list [DIR]
List Terraform environments.
List Terraform workspaces.
`
return strings.TrimSpace(helpText)
}
func (c *EnvListCommand) Synopsis() string {
return "List Environments"
func (c *WorkspaceListCommand) Synopsis() string {
return "List Workspaces"
}

View File

@ -12,16 +12,19 @@ import (
"github.com/mitchellh/cli"
)
type EnvNewCommand struct {
type WorkspaceNewCommand struct {
Meta
LegacyName bool
}
func (c *EnvNewCommand) Run(args []string) int {
func (c *WorkspaceNewCommand) Run(args []string) int {
args = c.Meta.process(args, true)
envCommandShowWarning(c.Ui, c.LegacyName)
statePath := ""
cmdFlags := c.Meta.flagSet("env new")
cmdFlags := c.Meta.flagSet("workspace new")
cmdFlags.StringVar(&statePath, "state", "", "terraform state file")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
@ -40,8 +43,8 @@ func (c *EnvNewCommand) Run(args []string) int {
return 1
}
// You can't ask to create an environment when you're overriding the
// environment name to be something different.
// You can't ask to create a workspace when you're overriding the
// workspace name to be something different.
if current, isOverridden := c.EnvOverridden(); current != newEnv && isOverridden {
c.Ui.Error(envIsOverriddenNewError)
return 1
@ -82,9 +85,9 @@ func (c *EnvNewCommand) Run(args []string) int {
return 1
}
// now save the current env locally
// now set the current workspace locally
if err := c.SetEnv(newEnv); err != nil {
c.Ui.Error(fmt.Sprintf("error saving new environment name: %s", err))
c.Ui.Error(fmt.Sprintf("Error selecting new workspace: %s", err))
return 1
}
@ -109,7 +112,7 @@ func (c *EnvNewCommand) Run(args []string) int {
// Lock the state if we can
lockInfo := state.NewLockInfo()
lockInfo.Operation = "env new"
lockInfo.Operation = "workspace new"
lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, c.Ui, c.Colorize())
if err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
@ -146,20 +149,20 @@ func (c *EnvNewCommand) Run(args []string) int {
return 0
}
func (c *EnvNewCommand) Help() string {
func (c *WorkspaceNewCommand) Help() string {
helpText := `
Usage: terraform env new [OPTIONS] NAME [DIR]
Usage: terraform workspace new [OPTIONS] NAME [DIR]
Create a new Terraform environment.
Create a new Terraform workspace.
Options:
-state=path Copy an existing state file into the new environment.
-state=path Copy an existing state file into the new workspace.
`
return strings.TrimSpace(helpText)
}
func (c *EnvNewCommand) Synopsis() string {
return "Create a new environment"
func (c *WorkspaceNewCommand) Synopsis() string {
return "Create a new workspace"
}

View File

@ -7,14 +7,17 @@ import (
"github.com/mitchellh/cli"
)
type EnvSelectCommand struct {
type WorkspaceSelectCommand struct {
Meta
LegacyName bool
}
func (c *EnvSelectCommand) Run(args []string) int {
func (c *WorkspaceSelectCommand) Run(args []string) int {
args = c.Meta.process(args, true)
cmdFlags := c.Meta.flagSet("env select")
envCommandShowWarning(c.Ui, c.LegacyName)
cmdFlags := c.Meta.flagSet("workspace select")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -65,7 +68,7 @@ func (c *EnvSelectCommand) Run(args []string) int {
}
if name == current {
// already using this env
// already using this workspace
return 0
}
@ -97,15 +100,15 @@ func (c *EnvSelectCommand) Run(args []string) int {
return 0
}
func (c *EnvSelectCommand) Help() string {
func (c *WorkspaceSelectCommand) Help() string {
helpText := `
Usage: terraform env select NAME [DIR]
Usage: terraform workspace select NAME [DIR]
Change Terraform environment.
Select a different Terraform workspace.
`
return strings.TrimSpace(helpText)
}
func (c *EnvSelectCommand) Synopsis() string {
return "Change environments"
func (c *WorkspaceSelectCommand) Synopsis() string {
return "Select a workspace"
}

View File

@ -72,32 +72,37 @@ func init() {
},
"env": func() (cli.Command, error) {
return &command.EnvCommand{
Meta: meta,
return &command.WorkspaceCommand{
Meta: meta,
LegacyName: true,
}, nil
},
"env list": func() (cli.Command, error) {
return &command.EnvListCommand{
Meta: meta,
return &command.WorkspaceListCommand{
Meta: meta,
LegacyName: true,
}, nil
},
"env select": func() (cli.Command, error) {
return &command.EnvSelectCommand{
Meta: meta,
return &command.WorkspaceSelectCommand{
Meta: meta,
LegacyName: true,
}, nil
},
"env new": func() (cli.Command, error) {
return &command.EnvNewCommand{
Meta: meta,
return &command.WorkspaceNewCommand{
Meta: meta,
LegacyName: true,
}, nil
},
"env delete": func() (cli.Command, error) {
return &command.EnvDeleteCommand{
Meta: meta,
return &command.WorkspaceDeleteCommand{
Meta: meta,
LegacyName: true,
}, nil
},
@ -201,6 +206,36 @@ func init() {
}, nil
},
"workspace": func() (cli.Command, error) {
return &command.WorkspaceCommand{
Meta: meta,
}, nil
},
"workspace list": func() (cli.Command, error) {
return &command.WorkspaceListCommand{
Meta: meta,
}, nil
},
"workspace select": func() (cli.Command, error) {
return &command.WorkspaceSelectCommand{
Meta: meta,
}, nil
},
"workspace new": func() (cli.Command, error) {
return &command.WorkspaceNewCommand{
Meta: meta,
}, nil
},
"workspace delete": func() (cli.Command, error) {
return &command.WorkspaceDeleteCommand{
Meta: meta,
}, nil
},
//-----------------------------------------------------------
// Plumbing
//-----------------------------------------------------------