Skip to content

Commit eee1232

Browse files
committed
Merge branch 'main' into fixwsroutes
2 parents 12b6c73 + 582d636 commit eee1232

File tree

16 files changed

+121
-78
lines changed

16 files changed

+121
-78
lines changed

cli/autostart.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func autostartShow() *cobra.Command {
4242
return err
4343
}
4444

45-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
45+
workspace, err := namedWorkspace(cmd, client, args[0])
4646
if err != nil {
4747
return err
4848
}
@@ -96,7 +96,7 @@ func autostartEnable() *cobra.Command {
9696
return err
9797
}
9898

99-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
99+
workspace, err := namedWorkspace(cmd, client, args[0])
100100
if err != nil {
101101
return err
102102
}
@@ -135,7 +135,7 @@ func autostartDisable() *cobra.Command {
135135
return err
136136
}
137137

138-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
138+
workspace, err := namedWorkspace(cmd, client, args[0])
139139
if err != nil {
140140
return err
141141
}

cli/bump.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func bump() *cobra.Command {
4343
if err != nil {
4444
return xerrors.Errorf("create client: %w", err)
4545
}
46-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
46+
workspace, err := namedWorkspace(cmd, client, args[0])
4747
if err != nil {
4848
return xerrors.Errorf("get workspace: %w", err)
4949
}

cli/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func delete() *cobra.Command {
3030
if err != nil {
3131
return err
3232
}
33-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
33+
workspace, err := namedWorkspace(cmd, client, args[0])
3434
if err != nil {
3535
return err
3636
}

cli/delete_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package cli_test
22

33
import (
4+
"context"
45
"io"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
810

911
"github.com/coder/coder/cli/clitest"
1012
"github.com/coder/coder/coderd/coderdtest"
13+
"github.com/coder/coder/codersdk"
1114
"github.com/coder/coder/pty/ptytest"
1215
)
1316

@@ -38,4 +41,55 @@ func TestDelete(t *testing.T) {
3841
pty.ExpectMatch("Cleaning Up")
3942
<-doneChan
4043
})
44+
45+
t.Run("DifferentUser", func(t *testing.T) {
46+
t.Parallel()
47+
adminClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
48+
adminUser := coderdtest.CreateFirstUser(t, adminClient)
49+
orgID := adminUser.OrganizationID
50+
client := coderdtest.CreateAnotherUser(t, adminClient, orgID)
51+
user, err := client.User(context.Background(), codersdk.Me)
52+
require.NoError(t, err)
53+
54+
version := coderdtest.CreateTemplateVersion(t, adminClient, orgID, nil)
55+
coderdtest.AwaitTemplateVersionJob(t, adminClient, version.ID)
56+
template := coderdtest.CreateTemplate(t, adminClient, orgID, version.ID)
57+
workspace := coderdtest.CreateWorkspace(t, client, orgID, template.ID)
58+
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
59+
60+
cmd, root := clitest.New(t, "delete", user.Username+"/"+workspace.Name, "-y")
61+
clitest.SetupConfig(t, adminClient, root)
62+
doneChan := make(chan struct{})
63+
pty := ptytest.New(t)
64+
cmd.SetIn(pty.Input())
65+
cmd.SetOut(pty.Output())
66+
go func() {
67+
defer close(doneChan)
68+
err := cmd.Execute()
69+
// When running with the race detector on, we sometimes get an EOF.
70+
if err != nil {
71+
assert.ErrorIs(t, err, io.EOF)
72+
}
73+
}()
74+
75+
pty.ExpectMatch("Cleaning Up")
76+
<-doneChan
77+
78+
workspace, err = client.Workspace(context.Background(), workspace.ID)
79+
require.ErrorContains(t, err, "was deleted")
80+
})
81+
82+
t.Run("InvalidWorkspaceIdentifier", func(t *testing.T) {
83+
t.Parallel()
84+
client := coderdtest.New(t, nil)
85+
cmd, root := clitest.New(t, "delete", "a/b/c", "-y")
86+
clitest.SetupConfig(t, client, root)
87+
doneChan := make(chan struct{})
88+
go func() {
89+
defer close(doneChan)
90+
err := cmd.Execute()
91+
assert.ErrorContains(t, err, "invalid workspace name: \"a/b/c\"")
92+
}()
93+
<-doneChan
94+
})
4195
}

cli/root.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,27 @@ func currentOrganization(cmd *cobra.Command, client *codersdk.Client) (codersdk.
176176
return orgs[0], nil
177177
}
178178

179+
// namedWorkspace fetches and returns a workspace by an identifier, which may be either
180+
// a bare name (for a workspace owned by the current user) or a "user/workspace" combination,
181+
// where user is either a username or UUID.
182+
func namedWorkspace(cmd *cobra.Command, client *codersdk.Client, identifier string) (codersdk.Workspace, error) {
183+
parts := strings.Split(identifier, "/")
184+
185+
var owner, name string
186+
switch len(parts) {
187+
case 1:
188+
owner = codersdk.Me
189+
name = parts[0]
190+
case 2:
191+
owner = parts[0]
192+
name = parts[1]
193+
default:
194+
return codersdk.Workspace{}, xerrors.Errorf("invalid workspace name: %q", identifier)
195+
}
196+
197+
return client.WorkspaceByOwnerAndName(cmd.Context(), owner, name)
198+
}
199+
179200
// createConfig consumes the global configuration flag to produce a config root.
180201
func createConfig(cmd *cobra.Command) config.Root {
181202
globalRoot, err := cmd.Flags().GetString(varGlobalConfig)

cli/show.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"golang.org/x/xerrors"
66

77
"github.com/coder/coder/cli/cliui"
8-
"github.com/coder/coder/codersdk"
98
)
109

1110
func show() *cobra.Command {
@@ -19,7 +18,7 @@ func show() *cobra.Command {
1918
if err != nil {
2019
return err
2120
}
22-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
21+
workspace, err := namedWorkspace(cmd, client, args[0])
2322
if err != nil {
2423
return xerrors.Errorf("get workspace: %w", err)
2524
}

cli/ssh.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ func getWorkspaceAndAgent(cmd *cobra.Command, client *codersdk.Client, userID st
205205
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
206206
}
207207
} else {
208-
workspace, err = client.WorkspaceByOwnerAndName(cmd.Context(), userID, workspaceParts[0])
208+
workspace, err = namedWorkspace(cmd, client, workspaceParts[0])
209209
if err != nil {
210210
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
211211
}

cli/start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func start() *cobra.Command {
2828
if err != nil {
2929
return err
3030
}
31-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
31+
workspace, err := namedWorkspace(cmd, client, args[0])
3232
if err != nil {
3333
return err
3434
}

cli/state.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func statePull() *cobra.Command {
3030
if err != nil {
3131
return err
3232
}
33-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
33+
workspace, err := namedWorkspace(cmd, client, args[0])
3434
if err != nil {
3535
return err
3636
}
@@ -71,7 +71,7 @@ func statePush() *cobra.Command {
7171
if err != nil {
7272
return err
7373
}
74-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
74+
workspace, err := namedWorkspace(cmd, client, args[0])
7575
if err != nil {
7676
return err
7777
}

cli/stop.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func stop() *cobra.Command {
2828
if err != nil {
2929
return err
3030
}
31-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
31+
workspace, err := namedWorkspace(cmd, client, args[0])
3232
if err != nil {
3333
return err
3434
}

cli/ttl.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func ttlShow() *cobra.Command {
4040
return xerrors.Errorf("create client: %w", err)
4141
}
4242

43-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
43+
workspace, err := namedWorkspace(cmd, client, args[0])
4444
if err != nil {
4545
return xerrors.Errorf("get workspace: %w", err)
4646
}
@@ -69,7 +69,7 @@ func ttlset() *cobra.Command {
6969
return xerrors.Errorf("create client: %w", err)
7070
}
7171

72-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
72+
workspace, err := namedWorkspace(cmd, client, args[0])
7373
if err != nil {
7474
return xerrors.Errorf("get workspace: %w", err)
7575
}
@@ -113,7 +113,7 @@ func ttlunset() *cobra.Command {
113113
return xerrors.Errorf("create client: %w", err)
114114
}
115115

116-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
116+
workspace, err := namedWorkspace(cmd, client, args[0])
117117
if err != nil {
118118
return xerrors.Errorf("get workspace: %w", err)
119119
}

cli/update.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func update() *cobra.Command {
1919
if err != nil {
2020
return err
2121
}
22-
workspace, err := client.WorkspaceByOwnerAndName(cmd.Context(), codersdk.Me, args[0])
22+
workspace, err := namedWorkspace(cmd, client, args[0])
2323
if err != nil {
2424
return err
2525
}

coderd/coderd.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,7 @@ func New(options *Options) *API {
152152
r.Get("/", api.templatesByOrganization)
153153
r.Get("/{templatename}", api.templateByOrganizationAndName)
154154
})
155-
r.Route("/workspaces", func(r chi.Router) {
156-
r.Post("/", api.postWorkspacesByOrganization)
157-
r.Route("/{user}", func(r chi.Router) {
158-
r.Use(httpmw.ExtractUserParam(options.Database))
159-
r.Get("/{workspacename}", api.workspaceByOwnerAndName)
160-
r.Get("/", api.workspacesByOwner)
161-
})
162-
})
155+
r.Post("/workspaces", api.postWorkspacesByOrganization)
163156
r.Route("/members", func(r chi.Router) {
164157
r.Get("/roles", api.assignableOrgRoles)
165158
r.Route("/{user}", func(r chi.Router) {

coderd/workspaces.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -160,35 +160,6 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
160160
httpapi.Write(rw, http.StatusOK, apiWorkspaces)
161161
}
162162

163-
func (api *API) workspacesByOwner(rw http.ResponseWriter, r *http.Request) {
164-
owner := httpmw.UserParam(r)
165-
workspaces, err := api.Database.GetWorkspacesWithFilter(r.Context(), database.GetWorkspacesWithFilterParams{
166-
OwnerID: owner.ID,
167-
Deleted: false,
168-
})
169-
if errors.Is(err, sql.ErrNoRows) {
170-
err = nil
171-
}
172-
if err != nil {
173-
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
174-
Message: fmt.Sprintf("get workspaces: %s", err),
175-
})
176-
return
177-
}
178-
179-
// Only return workspaces the user can read
180-
workspaces = AuthorizeFilter(api, r, rbac.ActionRead, workspaces)
181-
182-
apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, workspaces)
183-
if err != nil {
184-
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
185-
Message: fmt.Sprintf("convert workspaces: %s", err),
186-
})
187-
return
188-
}
189-
httpapi.Write(rw, http.StatusOK, apiWorkspaces)
190-
}
191-
192163
func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) {
193164
owner := httpmw.UserParam(r)
194165
workspaceName := chi.URLParam(r, "workspacename")

site/src/components/ConfirmDialog/ConfirmDialog.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,11 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
105105
</Typography>
106106

107107
{description && (
108-
<Typography className={styles.description} variant="body2">
108+
<Typography
109+
component={typeof description === "string" ? "p" : "div"}
110+
className={styles.description}
111+
variant="body2"
112+
>
109113
{description}
110114
</Typography>
111115
)}

site/src/components/ResetPasswordDialog/ResetPasswordDialog.tsx

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import DialogActions from "@material-ui/core/DialogActions"
2-
import DialogContent from "@material-ui/core/DialogContent"
31
import DialogContentText from "@material-ui/core/DialogContentText"
42
import { makeStyles } from "@material-ui/core/styles"
53
import { FC } from "react"
64
import * as TypesGen from "../../api/typesGenerated"
7-
import { CodeBlock } from "../CodeBlock/CodeBlock"
8-
import { Dialog, DialogActionButtons, DialogTitle } from "../Dialog/Dialog"
5+
import { CodeExample } from "../CodeExample/CodeExample"
6+
import { ConfirmDialog } from "../ConfirmDialog/ConfirmDialog"
97

108
export interface ResetPasswordDialogProps {
119
open: boolean
@@ -36,32 +34,35 @@ export const ResetPasswordDialog: FC<ResetPasswordDialogProps> = ({
3634
}) => {
3735
const styles = useStyles()
3836

39-
return (
40-
<Dialog open={open} onClose={onClose}>
41-
<DialogTitle title={Language.title} />
42-
43-
<DialogContent>
44-
<DialogContentText variant="subtitle2">{Language.message(user?.username)}</DialogContentText>
45-
46-
<DialogContentText component="div">
47-
<CodeBlock lines={[newPassword ?? ""]} className={styles.codeBlock} />
48-
</DialogContentText>
49-
</DialogContent>
37+
const description = (
38+
<>
39+
<DialogContentText variant="subtitle2">{Language.message(user?.username)}</DialogContentText>
40+
<DialogContentText component="div" className={styles.codeBlock}>
41+
<CodeExample code={newPassword ?? ""} className={styles.codeExample} />
42+
</DialogContentText>
43+
</>
44+
)
5045

51-
<DialogActions>
52-
<DialogActionButtons
53-
onCancel={onClose}
54-
confirmText={Language.confirmText}
55-
onConfirm={onConfirm}
56-
confirmLoading={loading}
57-
/>
58-
</DialogActions>
59-
</Dialog>
46+
return (
47+
<ConfirmDialog
48+
type="info"
49+
hideCancel={false}
50+
open={open}
51+
onConfirm={onConfirm}
52+
onClose={onClose}
53+
title={Language.title}
54+
confirmLoading={loading}
55+
confirmText={Language.confirmText}
56+
description={description}
57+
/>
6058
)
6159
}
6260

6361
const useStyles = makeStyles(() => ({
6462
codeBlock: {
63+
marginBottom: 0,
64+
},
65+
codeExample: {
6566
minHeight: "auto",
6667
userSelect: "all",
6768
width: "100%",

0 commit comments

Comments
 (0)