Skip to content

Commit 94e38e8

Browse files
committed
chore: Add ability to update workspace proxy fields
1 parent 164146c commit 94e38e8

File tree

10 files changed

+355
-16
lines changed

10 files changed

+355
-16
lines changed

coderd/database/dbauthz/querier.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,13 @@ func (q *querier) InsertWorkspaceProxy(ctx context.Context, arg database.InsertW
17051705
return insert(q.log, q.auth, rbac.ResourceWorkspaceProxy, q.db.InsertWorkspaceProxy)(ctx, arg)
17061706
}
17071707

1708+
func (q *querier) UpdateWorkspaceProxy(ctx context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) {
1709+
fetch := func(ctx context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) {
1710+
return q.db.GetWorkspaceProxyByID(ctx, arg.ID)
1711+
}
1712+
return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateWorkspaceProxy)(ctx, arg)
1713+
}
1714+
17081715
func (q *querier) RegisterWorkspaceProxy(ctx context.Context, arg database.RegisterWorkspaceProxyParams) (database.WorkspaceProxy, error) {
17091716
fetch := func(ctx context.Context, arg database.RegisterWorkspaceProxyParams) (database.WorkspaceProxy, error) {
17101717
return q.db.GetWorkspaceProxyByID(ctx, arg.ID)

coderd/database/dbfake/databasefake.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5231,6 +5231,31 @@ func (q *fakeQuerier) RegisterWorkspaceProxy(_ context.Context, arg database.Reg
52315231
return database.WorkspaceProxy{}, sql.ErrNoRows
52325232
}
52335233

5234+
func (q *fakeQuerier) UpdateWorkspaceProxy(_ context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) {
5235+
q.mutex.Lock()
5236+
defer q.mutex.Unlock()
5237+
5238+
for _, p := range q.workspaceProxies {
5239+
if p.Name == arg.Name && p.ID != arg.ID {
5240+
return database.WorkspaceProxy{}, errDuplicateKey
5241+
}
5242+
}
5243+
5244+
for i, p := range q.workspaceProxies {
5245+
if p.ID == arg.ID {
5246+
p.Name = arg.Name
5247+
p.DisplayName = arg.DisplayName
5248+
p.Icon = arg.Icon
5249+
if len(p.TokenHashedSecret) > 0 {
5250+
p.TokenHashedSecret = arg.TokenHashedSecret
5251+
}
5252+
q.workspaceProxies[i] = p
5253+
return p, nil
5254+
}
5255+
}
5256+
return database.WorkspaceProxy{}, sql.ErrNoRows
5257+
}
5258+
52345259
func (q *fakeQuerier) UpdateWorkspaceProxyDeleted(_ context.Context, arg database.UpdateWorkspaceProxyDeletedParams) error {
52355260
q.mutex.Lock()
52365261
defer q.mutex.Unlock()

coderd/database/models.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 56 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/proxies.sql

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,28 @@ SET
3636
WHERE
3737
id = @id;
3838

39+
-- name: UpdateWorkspaceProxy :one
40+
-- This allows editing the properties of a workspace proxy.
41+
UPDATE
42+
workspace_proxies
43+
SET
44+
-- These values should always be provided.
45+
name = @name,
46+
display_name = @display_name,
47+
icon = @icon,
48+
-- Only update the token if a new one is provided.
49+
-- So this is an optional field.
50+
token_hashed_secret = CASE
51+
WHEN length(@token_hashed_secret :: bytea) > 0 THEN @token_hashed_secret :: bytea
52+
ELSE workspace_proxies.token_hashed_secret
53+
END,
54+
-- Always update this timestamp.
55+
updated_at = Now()
56+
WHERE
57+
id = @id
58+
RETURNING *
59+
;
60+
3961
-- name: GetWorkspaceProxyByID :one
4062
SELECT
4163
*
@@ -57,6 +79,14 @@ WHERE
5779
LIMIT
5880
1;
5981

82+
-- name: GetWorkspaceProxies :many
83+
SELECT
84+
*
85+
FROM
86+
workspace_proxies
87+
WHERE
88+
deleted = false;
89+
6090
-- Finds a workspace proxy that has an access URL or app hostname that matches
6191
-- the provided hostname. This is to check if a hostname matches any workspace
6292
-- proxy.
@@ -94,11 +124,3 @@ WHERE
94124
)
95125
LIMIT
96126
1;
97-
98-
-- name: GetWorkspaceProxies :many
99-
SELECT
100-
*
101-
FROM
102-
workspace_proxies
103-
WHERE
104-
deleted = false;

codersdk/workspaceproxy.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,37 @@ func (c *Client) WorkspaceProxies(ctx context.Context) ([]WorkspaceProxy, error)
110110
return proxies, json.NewDecoder(res.Body).Decode(&proxies)
111111
}
112112

113+
type PatchWorkspaceProxy struct {
114+
ID uuid.UUID `json:"id" format:"uuid" validate:"required"`
115+
Name string `json:"name" validate:"required"`
116+
DisplayName string `json:"display_name" validate:"required"`
117+
Icon string `json:"icon" validate:"required"`
118+
RegenerateToken bool `json:"regenerate_token"`
119+
}
120+
121+
type PatchWorkspaceProxyResponse struct {
122+
Proxy WorkspaceProxy `json:"proxy" table:"proxy,recursive"`
123+
// ProxyToken is only returned if 'RegenerateToken' is set.
124+
ProxyToken string `json:"proxy_token" table:"proxy token,default_sort"`
125+
}
126+
127+
func (c *Client) PatchWorkspaceProxy(ctx context.Context, req PatchWorkspaceProxy) (WorkspaceProxy, error) {
128+
res, err := c.Request(ctx, http.MethodPatch,
129+
fmt.Sprintf("/api/v2/workspaceproxies/%s", req.ID.String()),
130+
nil,
131+
)
132+
if err != nil {
133+
return WorkspaceProxy{}, xerrors.Errorf("make request: %w", err)
134+
}
135+
defer res.Body.Close()
136+
137+
if res.StatusCode != http.StatusCreated {
138+
return WorkspaceProxy{}, ReadBodyAsError(res)
139+
}
140+
var resp WorkspaceProxy
141+
return resp, json.NewDecoder(res.Body).Decode(&resp)
142+
}
143+
113144
func (c *Client) DeleteWorkspaceProxyByName(ctx context.Context, name string) error {
114145
res, err := c.Request(ctx, http.MethodDelete,
115146
fmt.Sprintf("/api/v2/workspaceproxies/%s", name),
@@ -131,6 +162,28 @@ func (c *Client) DeleteWorkspaceProxyByID(ctx context.Context, id uuid.UUID) err
131162
return c.DeleteWorkspaceProxyByName(ctx, id.String())
132163
}
133164

165+
func (c *Client) WorkspaceProxyByName(ctx context.Context, name string) (WorkspaceProxy, error) {
166+
res, err := c.Request(ctx, http.MethodGet,
167+
fmt.Sprintf("/api/v2/workspaceproxies/%s", name),
168+
nil,
169+
)
170+
if err != nil {
171+
return WorkspaceProxy{}, xerrors.Errorf("make request: %w", err)
172+
}
173+
defer res.Body.Close()
174+
175+
if res.StatusCode != http.StatusOK {
176+
return WorkspaceProxy{}, ReadBodyAsError(res)
177+
}
178+
179+
var resp WorkspaceProxy
180+
return resp, json.NewDecoder(res.Body).Decode(&resp)
181+
}
182+
183+
func (c *Client) WorkspaceProxyByID(ctx context.Context, id uuid.UUID) (WorkspaceProxy, error) {
184+
return c.WorkspaceProxyByName(ctx, id.String())
185+
}
186+
134187
type RegionsResponse struct {
135188
Regions []Region `json:"regions"`
136189
}

enterprise/cli/workspaceproxy.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,83 @@ func (r *RootCmd) workspaceProxy() *clibase.Cmd {
3232
return cmd
3333
}
3434

35+
func (r *RootCmd) patchProxy() *clibase.Cmd {
36+
var (
37+
proxyName string
38+
displayName string
39+
proxyIcon string
40+
formatter = cliui.NewOutputFormatter(
41+
// Text formatter should be human readable.
42+
cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) {
43+
response, ok := data.(codersdk.WorkspaceProxy)
44+
if !ok {
45+
return nil, xerrors.Errorf("unexpected type %T", data)
46+
}
47+
return fmt.Sprintf("Workspace Proxy %q updated successfully.", response.Name), nil
48+
}),
49+
cliui.JSONFormat(),
50+
// Table formatter expects a slice, make a slice of one.
51+
cliui.ChangeFormatterData(cliui.TableFormat([]codersdk.WorkspaceProxy{}, []string{"proxy name", "proxy url"}),
52+
func(data any) (any, error) {
53+
response, ok := data.(codersdk.WorkspaceProxy)
54+
if !ok {
55+
return nil, xerrors.Errorf("unexpected type %T", data)
56+
}
57+
return []codersdk.WorkspaceProxy{response}, nil
58+
}),
59+
)
60+
)
61+
client := new(codersdk.Client)
62+
cmd := &clibase.Cmd{
63+
Use: "edit <name|id>",
64+
Short: "Edit a workspace proxy",
65+
Middleware: clibase.Chain(
66+
clibase.RequireNArgs(1),
67+
r.InitClient(client),
68+
),
69+
Handler: func(inv *clibase.Invocation) error {
70+
ctx := inv.Context()
71+
// This is cheeky, but you can also use a uuid string in
72+
// 'DeleteWorkspaceProxyByName' and it will work.
73+
proxy, err := client.WorkspaceProxyByName(ctx, inv.Args[0])
74+
if err != nil {
75+
return xerrors.Errorf("fetch workspace proxy %q: %w", inv.Args[0], err)
76+
}
77+
78+
updated, err := client.PatchWorkspaceProxy(ctx, codersdk.PatchWorkspaceProxy{
79+
ID: proxy.ID,
80+
Name: proxyName,
81+
DisplayName: displayName,
82+
Icon: proxyIcon,
83+
})
84+
85+
_, _ = formatter.Format(ctx, updated)
86+
return nil
87+
},
88+
}
89+
90+
formatter.AttachOptions(&cmd.Options)
91+
cmd.Options.Add(
92+
clibase.Option{
93+
Flag: "name",
94+
Description: "(Optional) Name of the proxy. This is used to identify the proxy.",
95+
Value: clibase.StringOf(&proxyName),
96+
},
97+
clibase.Option{
98+
Flag: "display-name",
99+
Description: "(Optional) Display of the proxy. If omitted, the name is reused as the display name.",
100+
Value: clibase.StringOf(&displayName),
101+
},
102+
clibase.Option{
103+
Flag: "icon",
104+
Description: "(Optional) Display icon of the proxy.",
105+
Value: clibase.StringOf(&proxyIcon),
106+
},
107+
)
108+
109+
return cmd
110+
}
111+
35112
func (r *RootCmd) deleteProxy() *clibase.Cmd {
36113
client := new(codersdk.Client)
37114
cmd := &clibase.Cmd{

enterprise/coderd/coderd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ func New(ctx context.Context, options *Options) (*API, error) {
120120
httpmw.ExtractWorkspaceProxyParam(api.Database),
121121
)
122122

123+
r.Get("/", api.getWorkspaceProxy)
124+
r.Patch("/", api.patchWorkspaceProxy)
123125
r.Delete("/", api.deleteWorkspaceProxy)
124126
})
125127
})

0 commit comments

Comments
 (0)