Skip to content

Commit 5f5d4ff

Browse files
committed
more tests
1 parent 2baa362 commit 5f5d4ff

File tree

2 files changed

+340
-4
lines changed

2 files changed

+340
-4
lines changed

enterprise/coderd/workspaceproxy.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,7 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request)
418418
if err != nil {
419419
return xerrors.Errorf("update replica: %w", err)
420420
}
421-
}
422-
if xerrors.Is(err, sql.ErrNoRows) {
421+
} else if xerrors.Is(err, sql.ErrNoRows) {
423422
// Replica doesn't exist, create it.
424423
replica, err = db.InsertReplica(ctx, database.InsertReplicaParams{
425424
ID: req.ReplicaID,
@@ -436,8 +435,7 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request)
436435
if err != nil {
437436
return xerrors.Errorf("insert replica: %w", err)
438437
}
439-
}
440-
if err != nil {
438+
} else if err != nil {
441439
return xerrors.Errorf("get replica: %w", err)
442440
}
443441

enterprise/coderd/workspaceproxy_test.go

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http/httputil"
88
"net/url"
99
"testing"
10+
"time"
1011

1112
"github.com/google/uuid"
1213
"github.com/moby/moby/pkg/namesgenerator"
@@ -16,7 +17,9 @@ import (
1617
"cdr.dev/slog"
1718
"cdr.dev/slog/sloggers/slogtest"
1819
"github.com/coder/coder/agent"
20+
"github.com/coder/coder/buildinfo"
1921
"github.com/coder/coder/coderd/coderdtest"
22+
"github.com/coder/coder/coderd/database"
2023
"github.com/coder/coder/coderd/database/dbtestutil"
2124
"github.com/coder/coder/coderd/workspaceapps"
2225
"github.com/coder/coder/codersdk"
@@ -245,6 +248,341 @@ func TestWorkspaceProxyCRUD(t *testing.T) {
245248
})
246249
}
247250

251+
func TestProxyRegisterDeregister(t *testing.T) {
252+
t.Parallel()
253+
254+
setup := func(t *testing.T) (*codersdk.Client, database.Store) {
255+
dv := coderdtest.DeploymentValues(t)
256+
dv.Experiments = []string{
257+
string(codersdk.ExperimentMoons),
258+
"*",
259+
}
260+
261+
db, pubsub := dbtestutil.NewDB(t)
262+
client := coderdenttest.New(t, &coderdenttest.Options{
263+
Options: &coderdtest.Options{
264+
DeploymentValues: dv,
265+
Database: db,
266+
Pubsub: pubsub,
267+
IncludeProvisionerDaemon: true,
268+
},
269+
})
270+
271+
_ = coderdtest.CreateFirstUser(t, client)
272+
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
273+
Features: license.Features{
274+
codersdk.FeatureWorkspaceProxy: 1,
275+
},
276+
})
277+
278+
return client, db
279+
}
280+
281+
t.Run("OK", func(t *testing.T) {
282+
t.Parallel()
283+
284+
client, db := setup(t)
285+
286+
ctx := testutil.Context(t, testutil.WaitLong)
287+
const (
288+
proxyName = "hello"
289+
proxyDisplayName = "Hello World"
290+
proxyIcon = "/emojis/flag.png"
291+
)
292+
createRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
293+
Name: proxyName,
294+
DisplayName: proxyDisplayName,
295+
Icon: proxyIcon,
296+
})
297+
require.NoError(t, err)
298+
299+
proxyClient := wsproxysdk.New(client.URL)
300+
proxyClient.SetSessionToken(createRes.ProxyToken)
301+
302+
// Register
303+
req := wsproxysdk.RegisterWorkspaceProxyRequest{
304+
AccessURL: "https://proxy.coder.test",
305+
WildcardHostname: "*.proxy.coder.test",
306+
DerpEnabled: true,
307+
ReplicaID: uuid.New(),
308+
ReplicaHostname: "mars",
309+
ReplicaError: "",
310+
ReplicaRelayAddress: "http://127.0.0.1:8080",
311+
Version: buildinfo.Version(),
312+
}
313+
registerRes1, err := proxyClient.RegisterWorkspaceProxy(ctx, req)
314+
require.NoError(t, err)
315+
require.NotEmpty(t, registerRes1.AppSecurityKey)
316+
require.NotEmpty(t, registerRes1.DERPMeshKey)
317+
require.EqualValues(t, 10001, registerRes1.DERPRegionID)
318+
require.Empty(t, registerRes1.SiblingReplicas)
319+
320+
// Get the proxy to ensure fields have updated.
321+
// TODO: we don't have a way to get the proxy by ID yet.
322+
proxies, err := client.WorkspaceProxies(ctx)
323+
require.NoError(t, err)
324+
require.Len(t, proxies, 1)
325+
require.Equal(t, createRes.Proxy.ID, proxies[0].ID)
326+
require.Equal(t, proxyName, proxies[0].Name)
327+
require.Equal(t, proxyDisplayName, proxies[0].DisplayName)
328+
require.Equal(t, proxyIcon, proxies[0].Icon)
329+
require.Equal(t, req.AccessURL, proxies[0].URL)
330+
require.Equal(t, req.AccessURL, proxies[0].URL)
331+
require.Equal(t, req.WildcardHostname, proxies[0].WildcardHostname)
332+
require.Equal(t, req.DerpEnabled, proxies[0].DerpEnabled)
333+
require.False(t, proxies[0].Deleted)
334+
335+
// Get the replica from the DB.
336+
replica, err := db.GetReplicaByID(ctx, req.ReplicaID)
337+
require.NoError(t, err)
338+
require.Equal(t, req.ReplicaID, replica.ID)
339+
require.Equal(t, req.ReplicaHostname, replica.Hostname)
340+
require.Equal(t, req.ReplicaError, replica.Error)
341+
require.Equal(t, req.ReplicaRelayAddress, replica.RelayAddress)
342+
require.Equal(t, req.Version, replica.Version)
343+
require.EqualValues(t, 10001, replica.RegionID)
344+
require.False(t, replica.StoppedAt.Valid)
345+
require.Zero(t, replica.DatabaseLatency)
346+
require.False(t, replica.Primary)
347+
348+
// Re-register with most fields changed.
349+
req = wsproxysdk.RegisterWorkspaceProxyRequest{
350+
AccessURL: "https://cool.proxy.coder.test",
351+
WildcardHostname: "*.cool.proxy.coder.test",
352+
DerpEnabled: false,
353+
ReplicaID: req.ReplicaID,
354+
ReplicaHostname: "venus",
355+
ReplicaError: "error",
356+
ReplicaRelayAddress: "http://127.0.0.1:9090",
357+
Version: buildinfo.Version(),
358+
}
359+
registerRes2, err := proxyClient.RegisterWorkspaceProxy(ctx, req)
360+
require.NoError(t, err)
361+
require.Equal(t, registerRes1, registerRes2)
362+
363+
// Get the proxy to ensure nothing has changed except updated_at.
364+
// TODO: we don't have a way to get the proxy by ID yet.
365+
proxiesNew, err := client.WorkspaceProxies(ctx)
366+
require.NoError(t, err)
367+
require.Len(t, proxiesNew, 1)
368+
require.Equal(t, createRes.Proxy.ID, proxiesNew[0].ID)
369+
require.Equal(t, proxyName, proxiesNew[0].Name)
370+
require.Equal(t, proxyDisplayName, proxiesNew[0].DisplayName)
371+
require.Equal(t, proxyIcon, proxiesNew[0].Icon)
372+
require.Equal(t, req.AccessURL, proxiesNew[0].URL)
373+
require.Equal(t, req.AccessURL, proxiesNew[0].URL)
374+
require.Equal(t, req.WildcardHostname, proxiesNew[0].WildcardHostname)
375+
require.Equal(t, req.DerpEnabled, proxiesNew[0].DerpEnabled)
376+
require.False(t, proxiesNew[0].Deleted)
377+
378+
// Get the replica from the DB and ensure the fields have been updated,
379+
// especially the updated_at.
380+
replica, err = db.GetReplicaByID(ctx, req.ReplicaID)
381+
require.NoError(t, err)
382+
require.Equal(t, req.ReplicaID, replica.ID)
383+
require.Equal(t, req.ReplicaHostname, replica.Hostname)
384+
require.Equal(t, req.ReplicaError, replica.Error)
385+
require.Equal(t, req.ReplicaRelayAddress, replica.RelayAddress)
386+
require.Equal(t, req.Version, replica.Version)
387+
require.EqualValues(t, 10001, replica.RegionID)
388+
require.False(t, replica.StoppedAt.Valid)
389+
require.Zero(t, replica.DatabaseLatency)
390+
require.False(t, replica.Primary)
391+
392+
// Deregister
393+
err = proxyClient.DeregisterWorkspaceProxy(ctx, wsproxysdk.DeregisterWorkspaceProxyRequest{
394+
ReplicaID: req.ReplicaID,
395+
})
396+
require.NoError(t, err)
397+
398+
// Ensure the replica has been fully stopped.
399+
replica, err = db.GetReplicaByID(ctx, req.ReplicaID)
400+
require.NoError(t, err)
401+
require.Equal(t, req.ReplicaID, replica.ID)
402+
require.True(t, replica.StoppedAt.Valid)
403+
404+
// Re-register should fail
405+
_, err = proxyClient.RegisterWorkspaceProxy(ctx, wsproxysdk.RegisterWorkspaceProxyRequest{})
406+
require.Error(t, err)
407+
})
408+
409+
t.Run("BlockMismatchingVersion", func(t *testing.T) {
410+
t.Parallel()
411+
412+
client, _ := setup(t)
413+
414+
ctx := testutil.Context(t, testutil.WaitLong)
415+
createRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
416+
Name: "hi",
417+
})
418+
require.NoError(t, err)
419+
420+
proxyClient := wsproxysdk.New(client.URL)
421+
proxyClient.SetSessionToken(createRes.ProxyToken)
422+
423+
_, err = proxyClient.RegisterWorkspaceProxy(ctx, wsproxysdk.RegisterWorkspaceProxyRequest{
424+
AccessURL: "https://proxy.coder.test",
425+
WildcardHostname: "*.proxy.coder.test",
426+
DerpEnabled: true,
427+
ReplicaID: uuid.New(),
428+
ReplicaHostname: "mars",
429+
ReplicaError: "",
430+
ReplicaRelayAddress: "http://127.0.0.1:8080",
431+
Version: "v0.0.0",
432+
})
433+
require.Error(t, err)
434+
var sdkErr *codersdk.Error
435+
require.ErrorAs(t, err, &sdkErr)
436+
require.Equal(t, http.StatusBadRequest, sdkErr.StatusCode())
437+
require.Contains(t, sdkErr.Response.Message, "Version mismatch")
438+
})
439+
440+
t.Run("ReregisterUpdateReplica", func(t *testing.T) {
441+
t.Parallel()
442+
443+
client, db := setup(t)
444+
445+
ctx := testutil.Context(t, testutil.WaitLong)
446+
createRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
447+
Name: "hi",
448+
})
449+
require.NoError(t, err)
450+
451+
proxyClient := wsproxysdk.New(client.URL)
452+
proxyClient.SetSessionToken(createRes.ProxyToken)
453+
454+
req := wsproxysdk.RegisterWorkspaceProxyRequest{
455+
AccessURL: "https://proxy.coder.test",
456+
WildcardHostname: "*.proxy.coder.test",
457+
DerpEnabled: true,
458+
ReplicaID: uuid.New(),
459+
ReplicaHostname: "mars",
460+
ReplicaError: "",
461+
ReplicaRelayAddress: "http://127.0.0.1:8080",
462+
Version: buildinfo.Version(),
463+
}
464+
_, err = proxyClient.RegisterWorkspaceProxy(ctx, req)
465+
require.NoError(t, err)
466+
467+
// Get the replica from the DB.
468+
replica, err := db.GetReplicaByID(ctx, req.ReplicaID)
469+
require.NoError(t, err)
470+
require.Equal(t, req.ReplicaID, replica.ID)
471+
472+
time.Sleep(time.Millisecond)
473+
474+
// Re-register with no changed fields.
475+
_, err = proxyClient.RegisterWorkspaceProxy(ctx, req)
476+
require.NoError(t, err)
477+
478+
// Get the replica from the DB and make sure updated_at has changed.
479+
replica, err = db.GetReplicaByID(ctx, req.ReplicaID)
480+
require.NoError(t, err)
481+
require.Equal(t, req.ReplicaID, replica.ID)
482+
require.Greater(t, replica.UpdatedAt.UnixNano(), replica.CreatedAt.UnixNano())
483+
})
484+
485+
t.Run("DeregisterNonExistentReplica", func(t *testing.T) {
486+
t.Parallel()
487+
488+
client, _ := setup(t)
489+
490+
ctx := testutil.Context(t, testutil.WaitLong)
491+
createRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
492+
Name: "hi",
493+
})
494+
require.NoError(t, err)
495+
496+
proxyClient := wsproxysdk.New(client.URL)
497+
proxyClient.SetSessionToken(createRes.ProxyToken)
498+
499+
err = proxyClient.DeregisterWorkspaceProxy(ctx, wsproxysdk.DeregisterWorkspaceProxyRequest{
500+
ReplicaID: uuid.New(),
501+
})
502+
require.Error(t, err)
503+
var sdkErr *codersdk.Error
504+
require.ErrorAs(t, err, &sdkErr)
505+
require.Equal(t, http.StatusNotFound, sdkErr.StatusCode())
506+
})
507+
508+
t.Run("ReturnSiblings", func(t *testing.T) {
509+
t.Parallel()
510+
511+
client, _ := setup(t)
512+
513+
ctx := testutil.Context(t, testutil.WaitLong)
514+
createRes1, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
515+
Name: "one",
516+
})
517+
require.NoError(t, err)
518+
createRes2, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
519+
Name: "two",
520+
})
521+
require.NoError(t, err)
522+
523+
// Register a replica on proxy 2. This shouldn't be returned by replicas
524+
// for proxy 1.
525+
proxyClient2 := wsproxysdk.New(client.URL)
526+
proxyClient2.SetSessionToken(createRes2.ProxyToken)
527+
_, err = proxyClient2.RegisterWorkspaceProxy(ctx, wsproxysdk.RegisterWorkspaceProxyRequest{
528+
AccessURL: "https://other.proxy.coder.test",
529+
WildcardHostname: "*.other.proxy.coder.test",
530+
DerpEnabled: true,
531+
ReplicaID: uuid.New(),
532+
ReplicaHostname: "venus",
533+
ReplicaError: "",
534+
ReplicaRelayAddress: "http://127.0.0.1:9090",
535+
Version: buildinfo.Version(),
536+
})
537+
require.NoError(t, err)
538+
539+
// Register replica 1.
540+
proxyClient1 := wsproxysdk.New(client.URL)
541+
proxyClient1.SetSessionToken(createRes1.ProxyToken)
542+
req1 := wsproxysdk.RegisterWorkspaceProxyRequest{
543+
AccessURL: "https://one.proxy.coder.test",
544+
WildcardHostname: "*.one.proxy.coder.test",
545+
DerpEnabled: true,
546+
ReplicaID: uuid.New(),
547+
ReplicaHostname: "mars1",
548+
ReplicaError: "",
549+
ReplicaRelayAddress: "http://127.0.0.1:8081",
550+
Version: buildinfo.Version(),
551+
}
552+
registerRes1, err := proxyClient1.RegisterWorkspaceProxy(ctx, req1)
553+
require.NoError(t, err)
554+
require.Empty(t, registerRes1.SiblingReplicas)
555+
556+
// Register replica 2 and expect to get replica 1 as a sibling.
557+
req2 := wsproxysdk.RegisterWorkspaceProxyRequest{
558+
AccessURL: "https://two.proxy.coder.test",
559+
WildcardHostname: "*.two.proxy.coder.test",
560+
DerpEnabled: true,
561+
ReplicaID: uuid.New(),
562+
ReplicaHostname: "mars2",
563+
ReplicaError: "",
564+
ReplicaRelayAddress: "http://127.0.0.1:8082",
565+
Version: buildinfo.Version(),
566+
}
567+
registerRes2, err := proxyClient1.RegisterWorkspaceProxy(ctx, req2)
568+
require.NoError(t, err)
569+
require.Len(t, registerRes2.SiblingReplicas, 1)
570+
require.Equal(t, req1.ReplicaID, registerRes2.SiblingReplicas[0].ID)
571+
require.Equal(t, req1.ReplicaHostname, registerRes2.SiblingReplicas[0].Hostname)
572+
require.Equal(t, req1.ReplicaRelayAddress, registerRes2.SiblingReplicas[0].RelayAddress)
573+
require.EqualValues(t, 10001, registerRes2.SiblingReplicas[0].RegionID)
574+
575+
// Re-register replica 1 and expect to get replica 2 as a sibling.
576+
registerRes1, err = proxyClient1.RegisterWorkspaceProxy(ctx, req1)
577+
require.NoError(t, err)
578+
require.Len(t, registerRes1.SiblingReplicas, 1)
579+
require.Equal(t, req2.ReplicaID, registerRes1.SiblingReplicas[0].ID)
580+
require.Equal(t, req2.ReplicaHostname, registerRes1.SiblingReplicas[0].Hostname)
581+
require.Equal(t, req2.ReplicaRelayAddress, registerRes1.SiblingReplicas[0].RelayAddress)
582+
require.EqualValues(t, 10001, registerRes1.SiblingReplicas[0].RegionID)
583+
})
584+
}
585+
248586
func TestIssueSignedAppToken(t *testing.T) {
249587
t.Parallel()
250588

0 commit comments

Comments
 (0)