From 31d556894f63c630fddedad5e3a59d854d6febac Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 30 May 2017 15:06:13 -0700 Subject: [PATCH] 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. --- command/apply_test.go | 4 +- command/env_command.go | 123 --------------- command/meta.go | 23 +-- command/meta_backend_migrate.go | 34 ++--- command/meta_backend_test.go | 8 +- command/push.go | 4 +- command/workspace_command.go | 144 ++++++++++++++++++ ...mand_test.go => workspace_command_test.go} | 60 ++++---- .../{env_delete.go => workspace_delete.go} | 25 +-- command/{env_list.go => workspace_list.go} | 19 ++- command/{env_new.go => workspace_new.go} | 31 ++-- .../{env_select.go => workspace_select.go} | 21 +-- commands.go | 55 +++++-- 13 files changed, 310 insertions(+), 241 deletions(-) delete mode 100644 command/env_command.go create mode 100644 command/workspace_command.go rename command/{env_command_test.go => workspace_command_test.go} (82%) rename command/{env_delete.go => workspace_delete.go} (83%) rename command/{env_list.go => workspace_list.go} (73%) rename command/{env_new.go => workspace_new.go} (78%) rename command/{env_select.go => workspace_select.go} (76%) diff --git a/command/apply_test.go b/command/apply_test.go index 0937eee07..73baba5d4 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -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) diff --git a/command/env_command.go b/command/env_command.go deleted file mode 100644 index f32e1cb20..000000000 --- a/command/env_command.go +++ /dev/null @@ -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. -` -) diff --git a/command/meta.go b/command/meta.go index acf37e516..018f84636 100644 --- a/command/meta.go +++ b/command/meta.go @@ -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 == "" { diff --git a/command/meta_backend_migrate.go b/command/meta_backend_migrate.go index 8dce064ad..f0e42d234 100644 --- a/command/meta_backend_migrate.go +++ b/command/meta_backend_migrate.go @@ -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 diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index aa4a02d2c..c91b72030 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -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") diff --git a/command/push.go b/command/push.go index 564889210..1c206aef5 100644 --- a/command/push.go +++ b/command/push.go @@ -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 { diff --git a/command/workspace_command.go b/command/workspace_command.go new file mode 100644 index 000000000..f6d584747 --- /dev/null +++ b/command/workspace_command.go @@ -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. +` +) diff --git a/command/env_command_test.go b/command/workspace_command_test.go similarity index 82% rename from command/env_command_test.go rename to command/workspace_command_test.go index 5a04d8db0..382c0f935 100644 --- a/command/env_command_test.go +++ b/command/workspace_command_test.go @@ -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"} diff --git a/command/env_delete.go b/command/workspace_delete.go similarity index 83% rename from command/env_delete.go rename to command/workspace_delete.go index 3975f9157..3d49cfc6e 100644 --- a/command/env_delete.go +++ b/command/workspace_delete.go @@ -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" } diff --git a/command/env_list.go b/command/workspace_list.go similarity index 73% rename from command/env_list.go rename to command/workspace_list.go index 793afe9ba..f5b7b2855 100644 --- a/command/env_list.go +++ b/command/workspace_list.go @@ -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" } diff --git a/command/env_new.go b/command/workspace_new.go similarity index 78% rename from command/env_new.go rename to command/workspace_new.go index 6c6f9cdd8..50846183c 100644 --- a/command/env_new.go +++ b/command/workspace_new.go @@ -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" } diff --git a/command/env_select.go b/command/workspace_select.go similarity index 76% rename from command/env_select.go rename to command/workspace_select.go index c2f69dc18..f2aa70330 100644 --- a/command/env_select.go +++ b/command/workspace_select.go @@ -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" } diff --git a/commands.go b/commands.go index 780b4652e..7e1468955 100644 --- a/commands.go +++ b/commands.go @@ -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 //-----------------------------------------------------------