Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Terraform Workspace select flag: -or-create #31633

Merged
merged 6 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion internal/command/workspace_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ const (
envDoesNotExist = `
Workspace %q doesn't exist.

You can create this workspace with the "new" subcommand.`
You can create this workspace with the "new" subcommand
or include the "-or-create" flag with the "select" subcommand.`

envChanged = `[reset][green]Switched to workspace %q.`

Expand Down
28 changes: 28 additions & 0 deletions internal/command/workspace_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,3 +435,31 @@ func TestWorkspace_deleteWithState(t *testing.T) {
t.Fatal("env 'test' still exists!")
}
}

func TestWorkspace_selectWithOrCreate(t *testing.T) {
// Create a temporary working directory that is empty
td := t.TempDir()
os.MkdirAll(td, 0755)
defer testChdir(t, td)()

selectCmd := &WorkspaceSelectCommand{}

current, _ := selectCmd.Workspace()
if current != backend.DefaultStateName {
t.Fatal("current workspace should be 'default'")
}

args := []string{"-or-create", "test"}
ui := new(cli.MockUi)
view, _ := testView(t)
selectCmd.Meta = Meta{Ui: ui, View: view}
if code := selectCmd.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}

current, _ = selectCmd.Workspace()
if current != "test" {
t.Fatalf("current workspace should be 'test', got %q", current)
}

}
36 changes: 29 additions & 7 deletions internal/command/workspace_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
args = c.Meta.process(args)
envCommandShowWarning(c.Ui, c.LegacyName)

var orCreate bool
cmdFlags := c.Meta.defaultFlagSet("workspace select")
cmdFlags.BoolVar(&orCreate, "or-create", false, "create workspace if it does not exist")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
Expand Down Expand Up @@ -95,9 +97,20 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
}
}

var newState bool

if !found {
c.Ui.Error(fmt.Sprintf(envDoesNotExist, name))
return 1
if orCreate {
_, err = b.StateMgr(name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a "Time of Check Time of Use" race condition, but the workspace select command doesn't have the ability to take a lock. I think this may need to incorporate the new command. I'll have to look into the other code path to see what that entails.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, double checked and the StateMgr implementations are expected to handle the check race again internally, so that's not an issue here. The missing options are then only to exclude the lock, but I think for the sake of keeping this simple, we can leave that to using the new subcommand directly,

Copy link
Contributor Author

@brittandeyoung brittandeyoung Sep 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbardin are there any changes needed? From reading your comments we are good to leave state locking for the new command?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbardin Are changes actually needed here? or am I able to resolve this conversation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, no changes needed here

if err != nil {
c.Ui.Error(err.Error())
return 1
}
newState = true
} else {
c.Ui.Error(fmt.Sprintf(envDoesNotExist, name))
return 1
}
}

err = c.SetWorkspace(name)
Expand All @@ -106,11 +119,16 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
return 1
}

c.Ui.Output(
c.Colorize().Color(
fmt.Sprintf(envChanged, name),
),
)
if newState {
c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
strings.TrimSpace(envCreated), name)))
} else {
c.Ui.Output(
c.Colorize().Color(
fmt.Sprintf(envChanged, name),
),
)
}

return 0
}
Expand All @@ -132,6 +150,10 @@ Usage: terraform [global options] workspace select NAME

Select a different Terraform workspace.

Options:

-or-create=false Create the Terraform workspace if it doesn't exist.

`
return strings.TrimSpace(helpText)
}
Expand Down