diff --git a/agent/agent.go b/agent/agent.go index fe81786b14..820856d423 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1660,52 +1660,50 @@ func (a *agent) isClosed() bool { return a.hardCtx.Err() != nil } +func (a *agent) requireNetwork() (*tailnet.Conn, bool) { + a.closeMutex.Lock() + defer a.closeMutex.Unlock() + return a.network, a.network != nil +} + +func (a *agent) HandleHTTPDebugMagicsock(w http.ResponseWriter, r *http.Request) { + network, ok := a.requireNetwork() + if !ok { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte("network is not ready yet")) + return + } + network.MagicsockServeHTTPDebug(w, r) +} + +func (a *agent) HandleHTTPMagicsockDebugLoggingState(w http.ResponseWriter, r *http.Request) { + state := chi.URLParam(r, "state") + stateBool, err := strconv.ParseBool(state) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintf(w, "invalid state %q, must be a boolean", state) + return + } + + network, ok := a.requireNetwork() + if !ok { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte("network is not ready yet")) + return + } + + network.MagicsockSetDebugLoggingEnabled(stateBool) + a.logger.Info(r.Context(), "updated magicsock debug logging due to debug request", slog.F("new_state", stateBool)) + + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintf(w, "updated magicsock debug logging to %v", stateBool) +} + func (a *agent) HTTPDebug() http.Handler { r := chi.NewRouter() - requireNetwork := func(w http.ResponseWriter) (*tailnet.Conn, bool) { - a.closeMutex.Lock() - network := a.network - a.closeMutex.Unlock() - - if network == nil { - w.WriteHeader(http.StatusNotFound) - _, _ = w.Write([]byte("network is not ready yet")) - return nil, false - } - - return network, true - } - - r.Get("/debug/magicsock", func(w http.ResponseWriter, r *http.Request) { - network, ok := requireNetwork(w) - if !ok { - return - } - network.MagicsockServeHTTPDebug(w, r) - }) - - r.Get("/debug/magicsock/debug-logging/{state}", func(w http.ResponseWriter, r *http.Request) { - state := chi.URLParam(r, "state") - stateBool, err := strconv.ParseBool(state) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - _, _ = fmt.Fprintf(w, "invalid state %q, must be a boolean", state) - return - } - - network, ok := requireNetwork(w) - if !ok { - return - } - - network.MagicsockSetDebugLoggingEnabled(stateBool) - a.logger.Info(r.Context(), "updated magicsock debug logging due to debug request", slog.F("new_state", stateBool)) - - w.WriteHeader(http.StatusOK) - _, _ = fmt.Fprintf(w, "updated magicsock debug logging to %v", stateBool) - }) - + r.Get("/debug/magicsock", a.HandleHTTPDebugMagicsock) + r.Get("/debug/magicsock/debug-logging/{state}", a.HandleHTTPMagicsockDebugLoggingState) r.NotFound(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) _, _ = w.Write([]byte("404 not found")) diff --git a/agent/api.go b/agent/api.go index cb4639c0e0..b81a7b9b44 100644 --- a/agent/api.go +++ b/agent/api.go @@ -36,6 +36,8 @@ func (a *agent) apiHandler() http.Handler { cacheDuration: cacheDuration, } r.Get("/api/v0/listening-ports", lp.handler) + r.Get("/debug/magicsock", a.HandleHTTPDebugMagicsock) + r.Get("/debug/magicsock/debug-logging/{state}", a.HandleHTTPMagicsockDebugLoggingState) return r } diff --git a/codersdk/workspaceagentconn.go b/codersdk/workspaceagentconn.go index 9fe229293d..e5451052bc 100644 --- a/codersdk/workspaceagentconn.go +++ b/codersdk/workspaceagentconn.go @@ -356,6 +356,22 @@ func (c *WorkspaceAgentConn) ListeningPorts(ctx context.Context) (WorkspaceAgent return resp, json.NewDecoder(res.Body).Decode(&resp) } +// DebugMagicsock makes a request to the workspace agent's magicsock debug endpoint. +func (c *WorkspaceAgentConn) DebugMagicsock(ctx context.Context) ([]byte, error) { + ctx, span := tracing.StartSpan(ctx) + defer span.End() + res, err := c.apiRequest(ctx, http.MethodGet, "/debug/magicsock", nil) + if err != nil { + return nil, xerrors.Errorf("do request: %w", err) + } + defer res.Body.Close() + bs, err := io.ReadAll(res.Body) + if err != nil { + return nil, xerrors.Errorf("read response body: %w", err) + } + return bs, nil +} + // apiRequest makes a request to the workspace agent's HTTP API server. func (c *WorkspaceAgentConn) apiRequest(ctx context.Context, method, path string, body io.Reader) (*http.Response, error) { ctx, span := tracing.StartSpan(ctx)