diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 991f5095870f3..ebb91be8bc2a9 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -427,6 +427,28 @@ const docTemplate = `{ } } }, + "/debug/tailnet": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "text/html" + ], + "tags": [ + "Debug" + ], + "summary": "Debug Info Tailnet", + "operationId": "debug-info-tailnet", + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/debug/ws": { "get": { "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 34c599bb9a04b..acf47c6e469f2 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -363,6 +363,24 @@ } } }, + "/debug/tailnet": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["text/html"], + "tags": ["Debug"], + "summary": "Debug Info Tailnet", + "operationId": "debug-info-tailnet", + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/debug/ws": { "get": { "security": [ diff --git a/coderd/coderd.go b/coderd/coderd.go index 81ec1e87e6b18..056beac10909b 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -943,6 +943,7 @@ func New(options *Options) *API { ) r.Get("/coordinator", api.debugCoordinator) + r.Get("/tailnet", api.debugTailnet) r.Get("/health", api.debugDeploymentHealth) r.Get("/ws", (&healthcheck.WebsocketEchoServer{}).ServeHTTP) }) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index df223205a2f5a..8ba4ddb507528 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -352,7 +352,8 @@ func assertProduce(t *testing.T, comment SwaggerComment) { (comment.router == "/workspaceagents/me/startup" && comment.method == "post") || (comment.router == "/workspaceagents/me/startup/logs" && comment.method == "patch") || (comment.router == "/licenses/{id}" && comment.method == "delete") || - (comment.router == "/debug/coordinator" && comment.method == "get") { + (comment.router == "/debug/coordinator" && comment.method == "get") || + (comment.router == "/debug/tailnet" && comment.method == "get") { return // Exception: HTTP 200 is returned without response entity } diff --git a/coderd/debug.go b/coderd/debug.go index 1e50b91ba69d3..017dc488b9b72 100644 --- a/coderd/debug.go +++ b/coderd/debug.go @@ -23,6 +23,17 @@ func (api *API) debugCoordinator(rw http.ResponseWriter, r *http.Request) { (*api.TailnetCoordinator.Load()).ServeHTTPDebug(rw, r) } +// @Summary Debug Info Tailnet +// @ID debug-info-tailnet +// @Security CoderSessionToken +// @Produce text/html +// @Tags Debug +// @Success 200 +// @Router /debug/tailnet [get] +func (api *API) debugTailnet(rw http.ResponseWriter, r *http.Request) { + api.agentProvider.ServeHTTPDebug(rw, r) +} + // @Summary Debug Info Deployment Health // @ID debug-info-deployment-health // @Security CoderSessionToken diff --git a/coderd/tailnet.go b/coderd/tailnet.go index c5b2345728606..b04f3dc519fec 100644 --- a/coderd/tailnet.go +++ b/coderd/tailnet.go @@ -21,6 +21,7 @@ import ( "cdr.dev/slog" "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/coderd/workspaceapps" "github.com/coder/coder/v2/coderd/wsconncache" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/site" @@ -38,6 +39,8 @@ func init() { } } +var _ workspaceapps.AgentProvider = (*ServerTailnet)(nil) + // NewServerTailnet creates a new tailnet intended for use by coderd. It // automatically falls back to wsconncache if a legacy agent is encountered. func NewServerTailnet( @@ -419,6 +422,10 @@ func (s *ServerTailnet) DialAgentNetConn(ctx context.Context, agentID uuid.UUID, }}, err } +func (s *ServerTailnet) ServeHTTPDebug(w http.ResponseWriter, r *http.Request) { + s.conn.MagicsockServeHTTPDebug(w, r) +} + type netConnCloser struct { net.Conn close func() diff --git a/coderd/workspaceapps/proxy.go b/coderd/workspaceapps/proxy.go index c883194faf372..9e32778153075 100644 --- a/coderd/workspaceapps/proxy.go +++ b/coderd/workspaceapps/proxy.go @@ -74,6 +74,8 @@ type AgentProvider interface { // func. AgentConn(ctx context.Context, agentID uuid.UUID) (_ *codersdk.WorkspaceAgentConn, release func(), _ error) + ServeHTTPDebug(w http.ResponseWriter, r *http.Request) + Close() error } diff --git a/coderd/wsconncache/wsconncache.go b/coderd/wsconncache/wsconncache.go index b9d362eac3163..12c738908dfb3 100644 --- a/coderd/wsconncache/wsconncache.go +++ b/coderd/wsconncache/wsconncache.go @@ -16,10 +16,13 @@ import ( "golang.org/x/sync/singleflight" "golang.org/x/xerrors" + "github.com/coder/coder/v2/coderd/workspaceapps" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/site" ) +var _ workspaceapps.AgentProvider = (*AgentProvider)(nil) + type AgentProvider struct { Cache *Cache } @@ -56,6 +59,8 @@ func (a *AgentProvider) ReverseProxy(targetURL *url.URL, dashboardURL *url.URL, return proxy, release, nil } +func (*AgentProvider) ServeHTTPDebug(http.ResponseWriter, *http.Request) {} + func (a *AgentProvider) Close() error { return a.Cache.Close() } diff --git a/docs/api/debug.md b/docs/api/debug.md index 5016f6a87b256..170b513437e23 100644 --- a/docs/api/debug.md +++ b/docs/api/debug.md @@ -240,3 +240,23 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [healthcheck.Report](schemas.md#healthcheckreport) | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Debug Info Tailnet + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/debug/tailnet \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /debug/tailnet` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md).