Skip to content

Commit 9c37e16

Browse files
committed
Add deleting proxies
1 parent 913aef1 commit 9c37e16

File tree

11 files changed

+224
-8
lines changed

11 files changed

+224
-8
lines changed

coderd/database/dbauthz/querier.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,10 @@ func (q *querier) GetWorkspaceProxyByID(ctx context.Context, id uuid.UUID) (data
16971697
return fetch(q.log, q.auth, q.db.GetWorkspaceProxyByID)(ctx, id)
16981698
}
16991699

1700+
func (q *querier) GetWorkspaceProxyByName(ctx context.Context, name string) (database.WorkspaceProxy, error) {
1701+
return fetch(q.log, q.auth, q.db.GetWorkspaceProxyByName)(ctx, name)
1702+
}
1703+
17001704
func (q *querier) GetWorkspaceProxyByHostname(ctx context.Context, hostname string) (database.WorkspaceProxy, error) {
17011705
return fetch(q.log, q.auth, q.db.GetWorkspaceProxyByHostname)(ctx, hostname)
17021706
}

coderd/database/dbfake/databasefake.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5097,6 +5097,18 @@ func (q *fakeQuerier) GetWorkspaceProxyByID(_ context.Context, id uuid.UUID) (da
50975097
return database.WorkspaceProxy{}, sql.ErrNoRows
50985098
}
50995099

5100+
func (q *fakeQuerier) GetWorkspaceProxyByName(_ context.Context, name string) (database.WorkspaceProxy, error) {
5101+
q.mutex.Lock()
5102+
defer q.mutex.Unlock()
5103+
5104+
for _, proxy := range q.workspaceProxies {
5105+
if proxy.Name == name {
5106+
return proxy, nil
5107+
}
5108+
}
5109+
return database.WorkspaceProxy{}, sql.ErrNoRows
5110+
}
5111+
51005112
func (q *fakeQuerier) GetWorkspaceProxyByHostname(_ context.Context, hostname string) (database.WorkspaceProxy, error) {
51015113
q.mutex.Lock()
51025114
defer q.mutex.Unlock()

coderd/database/querier.go

Lines changed: 1 addition & 0 deletions
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: 29 additions & 0 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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ WHERE
4949
LIMIT
5050
1;
5151

52+
-- name: GetWorkspaceProxyByName :one
53+
SELECT
54+
*
55+
FROM
56+
workspace_proxies
57+
WHERE
58+
name = $1
59+
LIMIT
60+
1;
61+
5262
-- Finds a workspace proxy that has an access URL or app hostname that matches
5363
-- the provided hostname. This is to check if a hostname matches any workspace
5464
-- proxy.

coderd/httpmw/workspaceproxy.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"net/http"
99
"strings"
1010

11+
"github.com/go-chi/chi/v5"
12+
1113
"github.com/google/uuid"
1214
"golang.org/x/xerrors"
1315

@@ -156,3 +158,53 @@ func ExtractWorkspaceProxy(opts ExtractWorkspaceProxyConfig) func(http.Handler)
156158
})
157159
}
158160
}
161+
162+
type workspaceProxyParamContextKey struct{}
163+
164+
// WorkspaceProxy returns the worksace proxy from the ExtractWorkspaceProxyParam handler.
165+
func WorkspaceProxy(r *http.Request) database.WorkspaceProxy {
166+
user, ok := r.Context().Value(workspaceProxyParamContextKey{}).(database.WorkspaceProxy)
167+
if !ok {
168+
panic("developer error: workspace proxy parameter middleware not provided")
169+
}
170+
return user
171+
}
172+
173+
// ExtractWorkspaceProxyParam extracts a workspace proxy from an ID/name in the {workspaceproxy} URL
174+
// parameter.
175+
//
176+
//nolint:revive
177+
func ExtractWorkspaceProxyParam(db database.Store) func(http.Handler) http.Handler {
178+
return func(next http.Handler) http.Handler {
179+
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
180+
ctx := r.Context()
181+
182+
proxyQuery := chi.URLParam(r, "workspaceproxy")
183+
if proxyQuery == "" {
184+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
185+
Message: "\"workspaceproxy\" must be provided.",
186+
})
187+
return
188+
}
189+
190+
var proxy database.WorkspaceProxy
191+
var dbErr error
192+
if proxyID, err := uuid.Parse(proxyQuery); err == nil {
193+
proxy, dbErr = db.GetWorkspaceProxyByID(ctx, proxyID)
194+
} else {
195+
proxy, dbErr = db.GetWorkspaceProxyByName(ctx, proxyQuery)
196+
}
197+
if httpapi.Is404Error(dbErr) {
198+
httpapi.ResourceNotFound(rw)
199+
return
200+
}
201+
if dbErr != nil {
202+
httpapi.InternalServerError(rw, dbErr)
203+
return
204+
}
205+
206+
ctx = context.WithValue(ctx, workspaceProxyParamContextKey{}, proxy)
207+
next.ServeHTTP(rw, r.WithContext(ctx))
208+
})
209+
}
210+
}

codersdk/workspaceproxy.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package codersdk
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"net/http"
78
"time"
89

@@ -72,3 +73,24 @@ func (c *Client) WorkspaceProxies(ctx context.Context) ([]WorkspaceProxy, error)
7273
var proxies []WorkspaceProxy
7374
return proxies, json.NewDecoder(res.Body).Decode(&proxies)
7475
}
76+
77+
func (c *Client) DeleteWorkspaceProxyByName(ctx context.Context, name string) error {
78+
res, err := c.Request(ctx, http.MethodDelete,
79+
fmt.Sprintf("/api/v2/workspaceproxies/%s", name),
80+
nil,
81+
)
82+
if err != nil {
83+
return xerrors.Errorf("make request: %w", err)
84+
}
85+
defer res.Body.Close()
86+
87+
if res.StatusCode != http.StatusOK {
88+
return ReadBodyAsError(res)
89+
}
90+
91+
return nil
92+
}
93+
94+
func (c *Client) DeleteWorkspaceProxyByID(ctx context.Context, id uuid.UUID) error {
95+
return c.DeleteWorkspaceProxyByName(ctx, id.String())
96+
}

enterprise/cli/workspaceproxy.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,31 @@ func (r *RootCmd) workspaceProxy() *clibase.Cmd {
4646
Children: []*clibase.Cmd{
4747
r.proxyServer(),
4848
r.registerProxy(),
49+
r.deleteProxy(),
50+
},
51+
}
52+
53+
return cmd
54+
}
55+
56+
func (r *RootCmd) deleteProxy() *clibase.Cmd {
57+
client := new(codersdk.Client)
58+
cmd := &clibase.Cmd{
59+
Use: "delete",
60+
Short: "Delete a workspace proxy",
61+
Middleware: clibase.Chain(
62+
clibase.RequireNArgs(1),
63+
r.InitClient(client),
64+
),
65+
Handler: func(inv *clibase.Invocation) error {
66+
ctx := inv.Context()
67+
err := client.DeleteWorkspaceProxyByName(ctx, inv.Args[0])
68+
if err != nil {
69+
return xerrors.Errorf("delete workspace proxy %q: %w", inv.Args[0], err)
70+
}
71+
72+
_, _ = fmt.Fprintln(inv.Stdout, "Workspace proxy %q deleted successfully", inv.Args[0])
73+
return nil
4974
},
5075
}
5176

enterprise/coderd/coderd.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ func New(ctx context.Context, options *Options) (*API, error) {
102102
r.Post("/issue-signed-app-token", api.workspaceProxyIssueSignedAppToken)
103103
})
104104
// TODO: Add specific workspace proxy endpoints.
105-
// r.Route("/{proxyName}", func(r chi.Router) {
106-
// r.Use(
107-
// httpmw.ExtractWorkspaceProxyByNameParam(api.Database),
108-
// )
109-
//
110-
// r.Get("/", api.workspaceProxyByName)
111-
// })
105+
r.Route("/{workspaceproxy}", func(r chi.Router) {
106+
r.Use(
107+
httpmw.ExtractWorkspaceProxyParam(api.Database),
108+
)
109+
110+
r.Delete("/", api.deleteWorkspaceProxy)
111+
})
112112
})
113113
r.Route("/organizations/{organization}/groups", func(r chi.Router) {
114114
r.Use(

enterprise/coderd/workspaceproxy.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"net/http"
88
"net/url"
99

10+
"github.com/coder/coder/coderd/httpmw"
11+
1012
"github.com/google/uuid"
1113
"golang.org/x/xerrors"
1214

@@ -19,6 +21,49 @@ import (
1921
"github.com/coder/coder/enterprise/wsproxy/wsproxysdk"
2022
)
2123

24+
// @Summary Delete workspace proxy
25+
// @ID delete-workspace-proxy
26+
// @Security CoderSessionToken
27+
// @Accept json
28+
// @Produce json
29+
// @Tags Enterprise
30+
// @Param workspaceproxy path string true "Proxy ID or name" format(uuid)
31+
// @Success 200 {object} codersdk.Response
32+
// @Router /workspaceproxies/{workspaceproxy} [delete]
33+
func (api *API) deleteWorkspaceProxy(rw http.ResponseWriter, r *http.Request) {
34+
var (
35+
ctx = r.Context()
36+
proxy = httpmw.WorkspaceProxy(r)
37+
auditor = api.AGPL.Auditor.Load()
38+
aReq, commitAudit = audit.InitRequest[database.WorkspaceProxy](rw, &audit.RequestParams{
39+
Audit: *auditor,
40+
Log: api.Logger,
41+
Request: r,
42+
Action: database.AuditActionCreate,
43+
})
44+
)
45+
aReq.Old = proxy
46+
defer commitAudit()
47+
48+
err := api.Database.UpdateWorkspaceProxyDeleted(ctx, database.UpdateWorkspaceProxyDeletedParams{
49+
ID: proxy.ID,
50+
Deleted: true,
51+
})
52+
if httpapi.Is404Error(err) {
53+
httpapi.ResourceNotFound(rw)
54+
return
55+
}
56+
if err != nil {
57+
httpapi.InternalServerError(rw, err)
58+
return
59+
}
60+
61+
aReq.New = database.WorkspaceProxy{}
62+
httpapi.Write(ctx, rw, http.StatusOK, codersdk.Response{
63+
Message: "Proxy has been deleted!",
64+
})
65+
}
66+
2267
// @Summary Create workspace proxy
2368
// @ID create-workspace-proxy
2469
// @Security CoderSessionToken

scripts/develop.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ set -euo pipefail
1515

1616
DEFAULT_PASSWORD="SomeSecurePassword!"
1717
password="${CODER_DEV_ADMIN_PASSWORD:-${DEFAULT_PASSWORD}}"
18+
use_proxy=0
1819

19-
args="$(getopt -o "" -l agpl,password: -- "$@")"
20+
args="$(getopt -o "" -l use-proxy,agpl,password: -- "$@")"
2021
eval set -- "$args"
2122
while true; do
2223
case "$1" in
@@ -28,6 +29,10 @@ while true; do
2829
password="$2"
2930
shift 2
3031
;;
32+
--use-proxy)
33+
use_proxy=1
34+
shift
35+
;;
3136
--)
3237
shift
3338
break
@@ -38,6 +43,10 @@ while true; do
3843
esac
3944
done
4045

46+
if [ "${CODER_BUILD_AGPL:-0}" -gt "0" ] && [ "${use_proxy}" -gt "0" ]; then
47+
echo '== ERROR: cannot use both external proxies and APGL build.' && exit 1
48+
fi
49+
4150
# Preflight checks: ensure we have our required dependencies, and make sure nothing is listening on port 3000 or 8080
4251
dependencies curl git go make yarn
4352
curl --fail http://127.0.0.1:3000 >/dev/null 2>&1 && echo '== ERROR: something is listening on port 3000. Kill it and re-run this script.' && exit 1
@@ -168,6 +177,13 @@ fatal() {
168177
) || echo "Failed to create a template. The template files are in ${temp_template_dir}"
169178
fi
170179

180+
if [ "${use_proxy}" -gt "0" ]; then
181+
# Create the proxy
182+
"${CODER_DEV_SHIM}" proxy register --name=local-proxy --display-name="Local Proxy" --icon="/emojis/1f4bb.png" --access-url=http://localhost:3010 --only-token
183+
# Start the proxy
184+
start_cmd PROXY proxy "" "${CODER_DEV_SHIM}" proxy --listen-addr
185+
fi
186+
171187
# Start the frontend once we have a template up and running
172188
CODER_HOST=http://127.0.0.1:3000 start_cmd SITE date yarn --cwd=./site dev --host
173189

0 commit comments

Comments
 (0)