Skip to content

Commit 4f9ee58

Browse files
committed
feat: add ability to make workspace for other user from cli
1 parent a0fedeb commit 4f9ee58

File tree

3 files changed

+78
-11
lines changed

3 files changed

+78
-11
lines changed

cli/create.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ func (r *RootCmd) create() *clibase.Cmd {
3636
return err
3737
}
3838

39+
workspaceOwner := codersdk.Me
3940
if len(inv.Args) >= 1 {
40-
workspaceName = inv.Args[0]
41+
workspaceOwner, workspaceName, err = splitNamedWorkspace(inv.Args[0])
42+
if err != nil {
43+
return err
44+
}
4145
}
4246

4347
if workspaceName == "" {
@@ -56,7 +60,7 @@ func (r *RootCmd) create() *clibase.Cmd {
5660
}
5761
}
5862

59-
_, err = client.WorkspaceByOwnerAndName(inv.Context(), codersdk.Me, workspaceName, codersdk.WorkspaceOptions{})
63+
_, err = client.WorkspaceByOwnerAndName(inv.Context(), workspaceOwner, workspaceName, codersdk.WorkspaceOptions{})
6064
if err == nil {
6165
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
6266
}
@@ -143,7 +147,7 @@ func (r *RootCmd) create() *clibase.Cmd {
143147
ttlMillis = &template.MaxTTLMillis
144148
}
145149

146-
workspace, err := client.CreateWorkspace(inv.Context(), organization.ID, codersdk.Me, codersdk.CreateWorkspaceRequest{
150+
workspace, err := client.CreateWorkspace(inv.Context(), organization.ID, workspaceOwner, codersdk.CreateWorkspaceRequest{
147151
TemplateID: template.ID,
148152
Name: workspaceName,
149153
AutostartSchedule: schedSpec,

cli/create_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,63 @@ func TestCreate(t *testing.T) {
7979
}
8080
})
8181

82+
t.Run("CreateForOtherUser", func(t *testing.T) {
83+
t.Parallel()
84+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
85+
owner := coderdtest.CreateFirstUser(t, client)
86+
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
87+
Parse: echo.ParseComplete,
88+
ProvisionApply: provisionCompleteWithAgent,
89+
ProvisionPlan: provisionCompleteWithAgent,
90+
})
91+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
92+
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
93+
_, user := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
94+
args := []string{
95+
"create",
96+
user.Username + "/their-workspace",
97+
"--template", template.Name,
98+
"--start-at", "9:30AM Mon-Fri US/Central",
99+
"--stop-after", "8h",
100+
}
101+
102+
inv, root := clitest.New(t, args...)
103+
clitest.SetupConfig(t, client, root)
104+
doneChan := make(chan struct{})
105+
pty := ptytest.New(t).Attach(inv)
106+
go func() {
107+
defer close(doneChan)
108+
err := inv.Run()
109+
assert.NoError(t, err)
110+
}()
111+
matches := []struct {
112+
match string
113+
write string
114+
}{
115+
{match: "compute.main"},
116+
{match: "smith (linux, i386)"},
117+
{match: "Confirm create", write: "yes"},
118+
}
119+
for _, m := range matches {
120+
pty.ExpectMatch(m.match)
121+
if len(m.write) > 0 {
122+
pty.WriteLine(m.write)
123+
}
124+
}
125+
<-doneChan
126+
127+
ws, err := client.WorkspaceByOwnerAndName(context.Background(), user.Username, "their-workspace", codersdk.WorkspaceOptions{})
128+
if assert.NoError(t, err, "expected workspace to be created") {
129+
assert.Equal(t, ws.TemplateName, template.Name)
130+
if assert.NotNil(t, ws.AutostartSchedule) {
131+
assert.Equal(t, *ws.AutostartSchedule, "CRON_TZ=US/Central 30 9 * * Mon-Fri")
132+
}
133+
if assert.NotNil(t, ws.TTLMillis) {
134+
assert.Equal(t, *ws.TTLMillis, 8*time.Hour.Milliseconds())
135+
}
136+
}
137+
})
138+
82139
t.Run("InheritStopAfterFromTemplate", func(t *testing.T) {
83140
t.Parallel()
84141
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})

cli/root.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -626,24 +626,30 @@ func CurrentOrganization(inv *clibase.Invocation, client *codersdk.Client) (code
626626
return orgs[0], nil
627627
}
628628

629-
// namedWorkspace fetches and returns a workspace by an identifier, which may be either
630-
// a bare name (for a workspace owned by the current user) or a "user/workspace" combination,
631-
// where user is either a username or UUID.
632-
func namedWorkspace(ctx context.Context, client *codersdk.Client, identifier string) (codersdk.Workspace, error) {
629+
func splitNamedWorkspace(identifier string) (owner string, workspaceName string, err error) {
633630
parts := strings.Split(identifier, "/")
634631

635-
var owner, name string
636632
switch len(parts) {
637633
case 1:
638634
owner = codersdk.Me
639-
name = parts[0]
635+
workspaceName = parts[0]
640636
case 2:
641637
owner = parts[0]
642-
name = parts[1]
638+
workspaceName = parts[1]
643639
default:
644-
return codersdk.Workspace{}, xerrors.Errorf("invalid workspace name: %q", identifier)
640+
return "", "", xerrors.Errorf("invalid workspace name: %q", identifier)
645641
}
642+
return owner, workspaceName, nil
643+
}
646644

645+
// namedWorkspace fetches and returns a workspace by an identifier, which may be either
646+
// a bare name (for a workspace owned by the current user) or a "user/workspace" combination,
647+
// where user is either a username or UUID.
648+
func namedWorkspace(ctx context.Context, client *codersdk.Client, identifier string) (codersdk.Workspace, error) {
649+
owner, name, err := splitNamedWorkspace(identifier)
650+
if err != nil {
651+
return codersdk.Workspace{}, err
652+
}
647653
return client.WorkspaceByOwnerAndName(ctx, owner, name, codersdk.WorkspaceOptions{})
648654
}
649655

0 commit comments

Comments
 (0)