Skip to content

Commit 3a7ae83

Browse files
committed
feat: add debug server for tailnet coordinators
Resolves: #5845
1 parent 08412fd commit 3a7ae83

File tree

10 files changed

+180
-32
lines changed

10 files changed

+180
-32
lines changed

agent/agent_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ func (c *client) ListenWorkspaceAgent(_ context.Context) (net.Conn, error) {
11931193
}
11941194
c.t.Cleanup(c.lastWorkspaceAgent)
11951195
go func() {
1196-
_ = c.coordinator.ServeAgent(serverConn, c.agentID)
1196+
_ = c.coordinator.ServeAgent(serverConn, c.agentID, "")
11971197
close(closed)
11981198
}()
11991199
return clientConn, nil

coderd/coderd.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,27 @@ func New(options *Options) *API {
613613
r.Get("/", api.workspaceApplicationAuth)
614614
})
615615
})
616+
617+
r.Route("/debug", func(r chi.Router) {
618+
r.Use(
619+
apiKeyMiddleware,
620+
// Ensure only owners can access debug endpoints.
621+
func(next http.Handler) http.Handler {
622+
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
623+
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceDebugInfo) {
624+
httpapi.ResourceNotFound(rw)
625+
return
626+
}
627+
628+
next.ServeHTTP(rw, r)
629+
})
630+
},
631+
)
632+
633+
r.HandleFunc("/coordinator", func(w http.ResponseWriter, r *http.Request) {
634+
(*api.TailnetCoordinator.Load()).ServeHTTPDebug(w, r)
635+
})
636+
})
616637
})
617638

618639
if options.SwaggerEndpoint {

coderd/coderdtest/authorize.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) {
272272
AssertAction: rbac.ActionRead,
273273
AssertObject: rbac.ResourceTemplate,
274274
},
275+
276+
"GET:/api/v2/debug/coordinator": {
277+
AssertAction: rbac.ActionRead,
278+
AssertObject: rbac.ResourceDebugInfo,
279+
},
275280
}
276281

277282
// Routes like proxy routes support all HTTP methods. A helper func to expand

coderd/rbac/object.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ var (
150150
ResourceReplicas = Object{
151151
Type: "replicas",
152152
}
153+
154+
// ResourceDebugInfo controls access to the debug routes `/api/v2/debug/*`.
155+
ResourceDebugInfo = Object{
156+
Type: "debug_info",
157+
}
153158
)
154159

155160
// Object is used to create objects for authz checks when you have none in

coderd/workspaceagents.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,16 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request
521521
})
522522
return
523523
}
524+
525+
workspace, err := api.Database.GetWorkspaceByID(ctx, build.WorkspaceID)
526+
if err != nil {
527+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
528+
Message: "Internal error fetching workspace.",
529+
Detail: err.Error(),
530+
})
531+
return
532+
}
533+
524534
// Ensure the resource is still valid!
525535
// We only accept agents for resources on the latest build.
526536
ensureLatestBuild := func() error {
@@ -618,7 +628,7 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request
618628
closeChan := make(chan struct{})
619629
go func() {
620630
defer close(closeChan)
621-
err := (*api.TailnetCoordinator.Load()).ServeAgent(wsNetConn, workspaceAgent.ID)
631+
err := (*api.TailnetCoordinator.Load()).ServeAgent(wsNetConn, workspaceAgent.ID, fmt.Sprintf("%s-%s", workspace.Name, workspaceAgent.Name))
622632
if err != nil {
623633
api.Logger.Warn(ctx, "tailnet coordinator agent error", slog.Error(err))
624634
_ = conn.Close(websocket.StatusInternalError, err.Error())

coderd/wsconncache/wsconncache_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func (c *client) ListenWorkspaceAgent(_ context.Context) (net.Conn, error) {
207207
<-closed
208208
})
209209
go func() {
210-
_ = c.coordinator.ServeAgent(serverConn, c.agentID)
210+
_ = c.coordinator.ServeAgent(serverConn, c.agentID, "")
211211
close(closed)
212212
}()
213213
return clientConn, nil

enterprise/tailnet/coordinator.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"context"
66
"encoding/json"
77
"errors"
8+
"fmt"
89
"io"
910
"net"
11+
"net/http"
1012
"sync"
1113
"time"
1214

@@ -174,7 +176,7 @@ func (c *haCoordinator) handleNextClientMessage(id, agent uuid.UUID, decoder *js
174176

175177
// ServeAgent accepts a WebSocket connection to an agent that listens to
176178
// incoming connections and publishes node updates.
177-
func (c *haCoordinator) ServeAgent(conn net.Conn, id uuid.UUID) error {
179+
func (c *haCoordinator) ServeAgent(conn net.Conn, id uuid.UUID, _ string) error {
178180
// Tell clients on other instances to send a callmemaybe to us.
179181
err := c.publishAgentHello(id)
180182
if err != nil {
@@ -573,3 +575,9 @@ func (c *haCoordinator) formatAgentUpdate(id uuid.UUID, node *agpl.Node) ([]byte
573575

574576
return buf.Bytes(), nil
575577
}
578+
579+
func (*haCoordinator) ServeHTTPDebug(w http.ResponseWriter, _ *http.Request) {
580+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
581+
fmt.Fprintf(w, "<h1>coordinator</h1>")
582+
fmt.Fprintf(w, "<h2>ha debug coming soon</h2>")
583+
}

enterprise/tailnet/coordinator_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestCoordinatorSingle(t *testing.T) {
6060
id := uuid.New()
6161
closeChan := make(chan struct{})
6262
go func() {
63-
err := coordinator.ServeAgent(server, id)
63+
err := coordinator.ServeAgent(server, id, "")
6464
assert.NoError(t, err)
6565
close(closeChan)
6666
}()
@@ -91,7 +91,7 @@ func TestCoordinatorSingle(t *testing.T) {
9191
agentID := uuid.New()
9292
closeAgentChan := make(chan struct{})
9393
go func() {
94-
err := coordinator.ServeAgent(agentServerWS, agentID)
94+
err := coordinator.ServeAgent(agentServerWS, agentID, "")
9595
assert.NoError(t, err)
9696
close(closeAgentChan)
9797
}()
@@ -142,7 +142,7 @@ func TestCoordinatorSingle(t *testing.T) {
142142
})
143143
closeAgentChan = make(chan struct{})
144144
go func() {
145-
err := coordinator.ServeAgent(agentServerWS, agentID)
145+
err := coordinator.ServeAgent(agentServerWS, agentID, "")
146146
assert.NoError(t, err)
147147
close(closeAgentChan)
148148
}()
@@ -184,7 +184,7 @@ func TestCoordinatorHA(t *testing.T) {
184184
agentID := uuid.New()
185185
closeAgentChan := make(chan struct{})
186186
go func() {
187-
err := coordinator1.ServeAgent(agentServerWS, agentID)
187+
err := coordinator1.ServeAgent(agentServerWS, agentID, "")
188188
assert.NoError(t, err)
189189
close(closeAgentChan)
190190
}()
@@ -240,7 +240,7 @@ func TestCoordinatorHA(t *testing.T) {
240240
})
241241
closeAgentChan = make(chan struct{})
242242
go func() {
243-
err := coordinator1.ServeAgent(agentServerWS, agentID)
243+
err := coordinator1.ServeAgent(agentServerWS, agentID, "")
244244
assert.NoError(t, err)
245245
close(closeAgentChan)
246246
}()

0 commit comments

Comments
 (0)