Skip to content

Commit fa8153a

Browse files
authored
chore: make default workspace proxy editable (#7903)
* chore: add editing the default workspace proxy
1 parent 5e647ba commit fa8153a

20 files changed

+453
-52
lines changed

coderd/coderdtest/coderdtest.go

+11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"crypto/tls"
1111
"crypto/x509"
1212
"crypto/x509/pkix"
13+
"database/sql"
1314
"encoding/base64"
1415
"encoding/json"
1516
"encoding/pem"
@@ -206,6 +207,16 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
206207
options.Database = dbauthz.New(options.Database, options.Authorizer, slogtest.Make(t, nil).Leveled(slog.LevelDebug))
207208
}
208209

210+
// Some routes expect a deployment ID, so just make sure one exists.
211+
// Check first incase the caller already set up this database.
212+
// nolint:gocritic // Setting up unit test data inside test helper
213+
depID, err := options.Database.GetDeploymentID(dbauthz.AsSystemRestricted(context.Background()))
214+
if xerrors.Is(err, sql.ErrNoRows) || depID == "" {
215+
// nolint:gocritic // Setting up unit test data inside test helper
216+
err := options.Database.InsertDeploymentID(dbauthz.AsSystemRestricted(context.Background()), uuid.NewString())
217+
require.NoError(t, err, "insert a deployment id")
218+
}
219+
209220
if options.DeploymentValues == nil {
210221
options.DeploymentValues = DeploymentValues(t)
211222
}

coderd/database/dbauthz/querier.go

+5
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,11 @@ func (q *querier) DeleteLicense(ctx context.Context, id int32) (int32, error) {
369369
return id, nil
370370
}
371371

372+
func (q *querier) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) {
373+
// No authz checks
374+
return q.db.GetDefaultProxyConfig(ctx)
375+
}
376+
372377
func (q *querier) GetDeploymentID(ctx context.Context) (string, error) {
373378
// No authz checks
374379
return q.db.GetDeploymentID(ctx)

coderd/database/dbauthz/querier_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ func (s *MethodTestSuite) TestLicense() {
335335
s.Run("GetDeploymentID", s.Subtest(func(db database.Store, check *expects) {
336336
check.Args().Asserts().Returns("")
337337
}))
338+
s.Run("GetDefaultProxyConfig", s.Subtest(func(db database.Store, check *expects) {
339+
check.Args().Asserts().Returns(database.GetDefaultProxyConfigRow{
340+
DisplayName: "Default",
341+
IconUrl: "/emojis/1f3e1.png",
342+
})
343+
}))
338344
s.Run("GetLogoURL", s.Subtest(func(db database.Store, check *expects) {
339345
err := db.UpsertLogoURL(context.Background(), "value")
340346
require.NoError(s.T(), err)

coderd/database/dbauthz/system.go

+7
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,10 @@ func (q *querier) GetWorkspaceProxyByHostname(ctx context.Context, params databa
431431
}
432432
return q.db.GetWorkspaceProxyByHostname(ctx, params)
433433
}
434+
435+
func (q *querier) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error {
436+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
437+
return err
438+
}
439+
return q.db.UpsertDefaultProxy(ctx, arg)
440+
}

coderd/database/dbauthz/system_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ func (s *MethodTestSuite) TestSystemFunctions() {
2525
LoginType: database.LoginTypeGithub,
2626
}).Asserts(rbac.ResourceSystem, rbac.ActionUpdate).Returns(l)
2727
}))
28+
s.Run("UpsertDefaultProxy", s.Subtest(func(db database.Store, check *expects) {
29+
check.Args(database.UpsertDefaultProxyParams{}).Asserts(rbac.ResourceSystem, rbac.ActionUpdate).Returns()
30+
}))
2831
s.Run("GetUserLinkByLinkedID", s.Subtest(func(db database.Store, check *expects) {
2932
l := dbgen.UserLink(s.T(), db, database.UserLink{})
3033
check.Args(l.LinkedID).Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(l)

coderd/database/dbfake/databasefake.go

+27-9
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var errDuplicateKey = &pq.Error{
4141

4242
// New returns an in-memory fake of the database.
4343
func New() database.Store {
44-
return &fakeQuerier{
44+
q := &fakeQuerier{
4545
mutex: &sync.RWMutex{},
4646
data: &data{
4747
apiKeys: make([]database.APIKey, 0),
@@ -73,6 +73,9 @@ func New() database.Store {
7373
locks: map[int64]struct{}{},
7474
},
7575
}
76+
q.defaultProxyDisplayName = "Default"
77+
q.defaultProxyIconURL = "/emojis/1f3e1.png"
78+
return q
7679
}
7780

7881
type rwMutex interface {
@@ -144,14 +147,16 @@ type data struct {
144147

145148
// Locks is a map of lock names. Any keys within the map are currently
146149
// locked.
147-
locks map[int64]struct{}
148-
deploymentID string
149-
derpMeshKey string
150-
lastUpdateCheck []byte
151-
serviceBanner []byte
152-
logoURL string
153-
appSecurityKey string
154-
lastLicenseID int32
150+
locks map[int64]struct{}
151+
deploymentID string
152+
derpMeshKey string
153+
lastUpdateCheck []byte
154+
serviceBanner []byte
155+
logoURL string
156+
appSecurityKey string
157+
lastLicenseID int32
158+
defaultProxyDisplayName string
159+
defaultProxyIconURL string
155160
}
156161

157162
func validateDatabaseTypeWithValid(v reflect.Value) (handled bool, err error) {
@@ -5171,3 +5176,16 @@ func isNull(v interface{}) bool {
51715176
func isNotNull(v interface{}) bool {
51725177
return reflect.ValueOf(v).FieldByName("Valid").Bool()
51735178
}
5179+
5180+
func (q *fakeQuerier) GetDefaultProxyConfig(_ context.Context) (database.GetDefaultProxyConfigRow, error) {
5181+
return database.GetDefaultProxyConfigRow{
5182+
DisplayName: q.defaultProxyDisplayName,
5183+
IconUrl: q.defaultProxyIconURL,
5184+
}, nil
5185+
}
5186+
5187+
func (q *fakeQuerier) UpsertDefaultProxy(_ context.Context, arg database.UpsertDefaultProxyParams) error {
5188+
q.defaultProxyDisplayName = arg.DisplayName
5189+
q.defaultProxyIconURL = arg.IconUrl
5190+
return nil
5191+
}

coderd/database/dbmetrics/dbmetrics.go

+14
Original file line numberDiff line numberDiff line change
@@ -1518,3 +1518,17 @@ func (m metricsStore) GetAuthorizedUserCount(ctx context.Context, arg database.G
15181518
m.queryLatencies.WithLabelValues("GetAuthorizedUserCount").Observe(time.Since(start).Seconds())
15191519
return count, err
15201520
}
1521+
1522+
func (m metricsStore) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error {
1523+
start := time.Now()
1524+
err := m.s.UpsertDefaultProxy(ctx, arg)
1525+
m.queryLatencies.WithLabelValues("UpsertDefaultProxy").Observe(time.Since(start).Seconds())
1526+
return err
1527+
}
1528+
1529+
func (m metricsStore) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) {
1530+
start := time.Now()
1531+
resp, err := m.s.GetDefaultProxyConfig(ctx)
1532+
m.queryLatencies.WithLabelValues("GetDefaultProxyConfig").Observe(time.Since(start).Seconds())
1533+
return resp, err
1534+
}

coderd/database/dbmock/store.go

+29
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/modelmethods.go

+4
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ func (w WorkspaceProxy) RBACObject() rbac.Object {
186186
WithID(w.ID)
187187
}
188188

189+
func (w WorkspaceProxy) IsPrimary() bool {
190+
return w.Name == "primary"
191+
}
192+
189193
func (f File) RBACObject() rbac.Object {
190194
return rbac.ResourceFile.
191195
WithID(f.ID).

coderd/database/querier.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/coder/coder/coderd/database"
1515
"github.com/coder/coder/coderd/database/dbgen"
1616
"github.com/coder/coder/coderd/database/migrations"
17+
"github.com/coder/coder/testutil"
1718
)
1819

1920
func TestGetDeploymentWorkspaceAgentStats(t *testing.T) {
@@ -257,3 +258,57 @@ func TestProxyByHostname(t *testing.T) {
257258
})
258259
}
259260
}
261+
262+
func TestDefaultProxy(t *testing.T) {
263+
t.Parallel()
264+
if testing.Short() {
265+
t.SkipNow()
266+
}
267+
sqlDB := testSQLDB(t)
268+
err := migrations.Up(sqlDB)
269+
require.NoError(t, err)
270+
db := database.New(sqlDB)
271+
272+
ctx := testutil.Context(t, testutil.WaitLong)
273+
depID := uuid.NewString()
274+
err = db.InsertDeploymentID(ctx, depID)
275+
require.NoError(t, err, "insert deployment id")
276+
277+
// Fetch empty proxy values
278+
defProxy, err := db.GetDefaultProxyConfig(ctx)
279+
require.NoError(t, err, "get def proxy")
280+
281+
require.Equal(t, defProxy.DisplayName, "Default")
282+
require.Equal(t, defProxy.IconUrl, "/emojis/1f3e1.png")
283+
284+
// Set the proxy values
285+
args := database.UpsertDefaultProxyParams{
286+
DisplayName: "displayname",
287+
IconUrl: "/icon.png",
288+
}
289+
err = db.UpsertDefaultProxy(ctx, args)
290+
require.NoError(t, err, "insert def proxy")
291+
292+
defProxy, err = db.GetDefaultProxyConfig(ctx)
293+
require.NoError(t, err, "get def proxy")
294+
require.Equal(t, defProxy.DisplayName, args.DisplayName)
295+
require.Equal(t, defProxy.IconUrl, args.IconUrl)
296+
297+
// Upsert values
298+
args = database.UpsertDefaultProxyParams{
299+
DisplayName: "newdisplayname",
300+
IconUrl: "/newicon.png",
301+
}
302+
err = db.UpsertDefaultProxy(ctx, args)
303+
require.NoError(t, err, "upsert def proxy")
304+
305+
defProxy, err = db.GetDefaultProxyConfig(ctx)
306+
require.NoError(t, err, "get def proxy")
307+
require.Equal(t, defProxy.DisplayName, args.DisplayName)
308+
require.Equal(t, defProxy.IconUrl, args.IconUrl)
309+
310+
// Ensure other site configs are the same
311+
found, err := db.GetDeploymentID(ctx)
312+
require.NoError(t, err, "get deployment id")
313+
require.Equal(t, depID, found)
314+
}

coderd/database/queries.sql.go

+41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/siteconfig.sql

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
-- name: UpsertDefaultProxy :exec
2+
-- The default proxy is implied and not actually stored in the database.
3+
-- So we need to store it's configuration here for display purposes.
4+
-- The functional values are immutable and controlled implicitly.
5+
INSERT INTO site_configs (key, value)
6+
VALUES
7+
('default_proxy_display_name', @display_name :: text),
8+
('default_proxy_icon_url', @icon_url :: text)
9+
ON CONFLICT
10+
(key)
11+
DO UPDATE SET value = EXCLUDED.value WHERE site_configs.key = EXCLUDED.key
12+
;
13+
14+
-- name: GetDefaultProxyConfig :one
15+
SELECT
16+
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_display_name'), 'Default') :: text AS display_name,
17+
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_icon_url'), '/emojis/1f3e1.png') :: text AS icon_url
18+
;
19+
20+
121
-- name: InsertDeploymentID :exec
222
INSERT INTO site_configs (key, value) VALUES ('deployment_id', $1);
323

coderd/httpmw/workspaceproxy.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func WorkspaceProxyParam(r *http.Request) database.WorkspaceProxy {
173173
// parameter.
174174
//
175175
//nolint:revive
176-
func ExtractWorkspaceProxyParam(db database.Store) func(http.Handler) http.Handler {
176+
func ExtractWorkspaceProxyParam(db database.Store, deploymentID string, fetchPrimaryProxy func(ctx context.Context) (database.WorkspaceProxy, error)) func(http.Handler) http.Handler {
177177
return func(next http.Handler) http.Handler {
178178
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
179179
ctx := r.Context()
@@ -188,9 +188,14 @@ func ExtractWorkspaceProxyParam(db database.Store) func(http.Handler) http.Handl
188188

189189
var proxy database.WorkspaceProxy
190190
var dbErr error
191-
if proxyID, err := uuid.Parse(proxyQuery); err == nil {
191+
if proxyQuery == "primary" || proxyQuery == deploymentID {
192+
// Requesting primary proxy
193+
proxy, dbErr = fetchPrimaryProxy(ctx)
194+
} else if proxyID, err := uuid.Parse(proxyQuery); err == nil {
195+
// Request proxy by id
192196
proxy, dbErr = db.GetWorkspaceProxyByID(ctx, proxyID)
193197
} else {
198+
// Request proxy by name
194199
proxy, dbErr = db.GetWorkspaceProxyByName(ctx, proxyQuery)
195200
}
196201
if httpapi.Is404Error(dbErr) {

0 commit comments

Comments
 (0)