From 490de43e28e1997aa7cdb2a7a71bef2608e81475 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Fri, 15 Dec 2023 13:42:12 +0400 Subject: [PATCH] feat: add tailnet v2 API support to coordinate endpoint --- coderd/coderd.go | 6 ++++ coderd/workspaceagents.go | 19 ++++++++++-- coderd/workspaceagents_test.go | 32 +++++++++++++++++++++ helm/provisioner/charts/libcoder-0.1.0.tgz | Bin 3013 -> 3012 bytes 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index 3bfb087ec0c55..8dd8cf36936fb 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -473,6 +473,11 @@ func New(options *Options) *API { Cache: wsconncache.New(api._dialWorkspaceAgentTailnet, 0), } } + api.TailnetClientService, err = tailnet.NewClientService( + api.Logger.Named("tailnetclient"), &api.TailnetCoordinator) + if err != nil { + api.Logger.Fatal(api.ctx, "failed to initialize tailnet client service", slog.Error(err)) + } workspaceAppsLogger := options.Logger.Named("workspaceapps") if options.WorkspaceAppsStatsCollectorOptions.Logger == nil { @@ -1061,6 +1066,7 @@ type API struct { Auditor atomic.Pointer[audit.Auditor] WorkspaceClientCoordinateOverride atomic.Pointer[func(rw http.ResponseWriter) bool] TailnetCoordinator atomic.Pointer[tailnet.Coordinator] + TailnetClientService *tailnet.ClientService QuotaCommitter atomic.Pointer[proto.QuotaCommitter] // WorkspaceProxyHostsFn returns the hosts of healthy workspace proxies // for header reasons. diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 992337915d03d..25ea30bba1eb0 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1387,6 +1387,21 @@ func (api *API) workspaceAgentClientCoordinate(rw http.ResponseWriter, r *http.R } } + version := "1.0" + qv := r.URL.Query().Get("version") + if qv != "" { + version = qv + } + if err := tailnet.ValidateVersion(version); err != nil { + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: "Unknown or unsupported API version", + Validations: []codersdk.ValidationError{ + {Field: "version", Detail: err.Error()}, + }, + }) + return + } + api.WebsocketWaitMutex.Lock() api.WebsocketWaitGroup.Add(1) api.WebsocketWaitMutex.Unlock() @@ -1407,8 +1422,8 @@ func (api *API) workspaceAgentClientCoordinate(rw http.ResponseWriter, r *http.R go httpapi.Heartbeat(ctx, conn) defer conn.Close(websocket.StatusNormalClosure, "") - err = (*api.TailnetCoordinator.Load()).ServeClient(wsNetConn, uuid.New(), workspaceAgent.ID) - if err != nil { + err = api.TailnetClientService.ServeClient(ctx, version, wsNetConn, uuid.New(), workspaceAgent.ID) + if err != nil && !xerrors.Is(err, io.EOF) && !xerrors.Is(err, context.Canceled) { _ = conn.Close(websocket.StatusInternalError, err.Error()) return } diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index 5f27cec39835f..5232b71113ea9 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -444,6 +444,38 @@ func TestWorkspaceAgentTailnet(t *testing.T) { require.Equal(t, "test", strings.TrimSpace(string(output))) } +func TestWorkspaceAgentClientCoordinate_BadVersion(t *testing.T) { + t.Parallel() + client, db := coderdtest.NewWithDatabase(t, nil) + user := coderdtest.CreateFirstUser(t, client) + + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ + OrganizationID: user.OrganizationID, + OwnerID: user.UserID, + }).WithAgent().Do() + + ctx := testutil.Context(t, testutil.WaitShort) + agentToken, err := uuid.Parse(r.AgentToken) + require.NoError(t, err) + //nolint: gocritic // testing + ao, err := db.GetWorkspaceAgentAndOwnerByAuthToken(dbauthz.AsSystemRestricted(ctx), agentToken) + require.NoError(t, err) + + //nolint: bodyclose // closed by ReadBodyAsError + resp, err := client.Request(ctx, http.MethodGet, + fmt.Sprintf("api/v2/workspaceagents/%s/coordinate", ao.WorkspaceAgent.ID), + nil, + codersdk.WithQueryParam("version", "99.99")) + require.NoError(t, err) + require.Equal(t, http.StatusBadRequest, resp.StatusCode) + err = codersdk.ReadBodyAsError(resp) + var sdkErr *codersdk.Error + require.ErrorAs(t, err, &sdkErr) + require.Equal(t, "Unknown or unsupported API version", sdkErr.Message) + require.Len(t, sdkErr.Validations, 1) + require.Equal(t, "version", sdkErr.Validations[0].Field) +} + func TestWorkspaceAgentTailnetDirectDisabled(t *testing.T) { t.Parallel() diff --git a/helm/provisioner/charts/libcoder-0.1.0.tgz b/helm/provisioner/charts/libcoder-0.1.0.tgz index 939508140df952a07a34051605d05484d4dcaea4..4fa45395783fb2a9238f0c5df521e3ced1b323ba 100644 GIT binary patch delta 2604 zcmV+{3e)w)7sMBkO$#-Jfd$T+WV^*?+mTo;*k9LKzC0Dl>GVT?H5&2_ohYYM1e*JjOmul}cs5+nrD~%|=1UT)yhy^p1IPp` zrz$e~iy#OjMv!=0j((@T!S|q^a!A_Pi^aM143d zpuHFsiHIWoB9L!yfhHl#A_U(O*W*3}i-iZk$OxI#&;Xi~2B`h)Lja#3p-F^^f}iRdBX~8EYU)?1 zy2LOORL!-k;f-2d<{E%ae>%&`wGQ>0&XLBeHu0pIF~7!{mc3$Ae{$KKxa{7#;3?KR z7qwE*zTWxF;<3@1qd{fG5(K_#QFW)W>YDl*YqjWn*<9ayY5YI&wDo^eWGM=%&2leu ztndH!_K$kKX8(6^bnwvseT(Pzw!7<{-K4rssVSN)27GMZ&Hhf{f9-Y~38C7@RgwkC zn1aCWBkGlOk-{|Os~HMGBc1z{$dVAA9++1eUu5Gky#c@DSCf!U*WF$3L?BU)7t5Vy z)otVek&sD@U^Fi|wh3@0-v*{+JQXxiWAJ~FogZ{otnzQ)T@`>&rmrI*g8%7umpPw^6(0UZ64vx@Wqog1yBJwlZFK&4fzFNLPiXum6=B|d1jNg z1z~@y)3f1+%kzs5XD<41qd@=1yN#yP(AeO@V)5km*6Fm1D(p(hx*-ZB-I>bmZH@e5 z(bWV)p-Nk{asBJr+rJ#sS2(XSU2&!rewBa6DhWfB65b5YSNW5J92mei!}BllEK`t_ zN~Ko|2t8CwKgC&8vnI-vn4BHRSr~*YlS-F69ul@nLfc1MVW{cxR)&lPJ*QGtg}Wn} zXeej&&oOr;#x$+Yl0AL1nXLzG?^%8{b*u_pD}`;Dr1VRRwalJzEytkEdIfhO>F9s2 z6%K9Ny(x=JO?r(-t>KA+ZH7sMCAXut1HBxywU>8btA$^71_^T#uU^s?TY04q=+)?w zi&xzxuoc^(_Pv!mgbuu9GLL~A72f^1w0E%Dtz)x4bcZNr6X`SDy2WEQ&S4vPwc+_S zQPV#7o8>m&oC1;PRl??dTZg~7qpyGAdKp1i4-*~P$f8t0u5e!8vRWtEUEyK(2RDlm z3;Q{N;@oSKaLG3+H;S2xL)HSv5?K>HRm4>xLrTUHPsli$WqIm~yF%70qFT zUO5+^r0qwg5;RTMtV|uvKOEjKToxU2cNZ?FRD!?>-V`0M8Tgn{fsyXP^`w7X&ub9Xgv!W9r;I5FT>9xX-ye;_1?5hxAs_s`%@1LQh)Dxn8lK+J{RO(3B{A)mlj z$MR4`-rjQSXhR<&9ut~CNK${5351E@Sz1Da<|nWims8B4<&K|c7&VH46A*?dRlaRw;eraIJ2|6|_q2o>XlpCL8xp4sgOJBos@yk>sfX6m z1IECA|9;mfYWw~BAlTjUmrk^d1%vB~`+&du_;zpMw|nx>x=G{-j5B}6<`7{jFf^i@ zb*a=AXbwWibZl!5h0Dm@o;5fEkMQasgqD6g=r&H^atcz#+;<1#^a*E?OFdbxJk z_EtOc2+bZ;WuiNpG^lpUtpHS3x;s2Ph6gTQgeYK=PW4E7O1JzD5z-Ua`}u9ibYU zBvVr^=)X-C53ZisUDNz5=MU(J6T7q?!p&WXzticYvhyxh}_gk>&gLQbc(S z5AXbxN79YYP>kH^X$L}c`rv*<$guGvsYZx_w_yTm3h;H@;o{?;RiSH_!ijhkFn8|66}NIwrchX>Pf@qq}Pc_ElYT zcsuy*yl51p>Bf1cbhEEJFM%>+O1GV+H&f)wzyJ|~%x!Nf5M76tXYjmbbBpcjsGFyD zBZ0eH1QNC*l*+Y(J9nXth(y@7m1SqS{b;?ml&^dqYxep-!k7xFDtkDOOJ4L>!DEx; zi|wOro#{AlAb`CcLu03U&S)B^oyr_3yMxO9-Bete*|r9&@TbW?T(5d~9-fD%`TQpU O0RR6&)E4LfIsgFtfC~r! delta 2605 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 P00960a5caJ06G8w%E~QO