Skip to content

Commit 3eb7f06

Browse files
authored
feat(agent): add http debug routes for magicsock (#7287)
1 parent 272573e commit 3eb7f06

File tree

4 files changed

+58
-9
lines changed

4 files changed

+58
-9
lines changed

agent/agent.go

+29-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type Options struct {
6060
ReconnectingPTYTimeout time.Duration
6161
EnvironmentVariables map[string]string
6262
Logger slog.Logger
63-
AgentPorts map[int]string
63+
IgnorePorts map[int]string
6464
SSHMaxTimeout time.Duration
6565
TailnetListenPort uint16
6666
}
@@ -76,7 +76,12 @@ type Client interface {
7676
PatchStartupLogs(ctx context.Context, req agentsdk.PatchStartupLogs) error
7777
}
7878

79-
func New(options Options) io.Closer {
79+
type Agent interface {
80+
HTTPDebug() http.Handler
81+
io.Closer
82+
}
83+
84+
func New(options Options) Agent {
8085
if options.ReconnectingPTYTimeout == 0 {
8186
options.ReconnectingPTYTimeout = 5 * time.Minute
8287
}
@@ -112,7 +117,7 @@ func New(options Options) io.Closer {
112117
tempDir: options.TempDir,
113118
lifecycleUpdate: make(chan struct{}, 1),
114119
lifecycleReported: make(chan codersdk.WorkspaceAgentLifecycle, 1),
115-
ignorePorts: options.AgentPorts,
120+
ignorePorts: options.IgnorePorts,
116121
connStatsChan: make(chan *agentsdk.Stats, 1),
117122
sshMaxTimeout: options.SSHMaxTimeout,
118123
}
@@ -1264,6 +1269,27 @@ func (a *agent) isClosed() bool {
12641269
}
12651270
}
12661271

1272+
func (a *agent) HTTPDebug() http.Handler {
1273+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1274+
a.closeMutex.Lock()
1275+
network := a.network
1276+
a.closeMutex.Unlock()
1277+
1278+
if network == nil {
1279+
w.WriteHeader(http.StatusOK)
1280+
_, _ = w.Write([]byte("network is not ready yet"))
1281+
return
1282+
}
1283+
1284+
if r.URL.Path == "/debug/magicsock" {
1285+
network.MagicsockServeHTTPDebug(w, r)
1286+
} else {
1287+
w.WriteHeader(http.StatusNotFound)
1288+
_, _ = w.Write([]byte("404 not found"))
1289+
}
1290+
})
1291+
}
1292+
12671293
func (a *agent) Close() error {
12681294
a.closeMutex.Lock()
12691295
defer a.closeMutex.Unlock()

cli/agent.go

+22-6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
3838
sshMaxTimeout time.Duration
3939
tailnetListenPort int64
4040
prometheusAddress string
41+
debugAddress string
4142
)
4243
cmd := &clibase.Cmd{
4344
Use: "agent",
@@ -48,7 +49,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
4849
ctx, cancel := context.WithCancel(inv.Context())
4950
defer cancel()
5051

51-
agentPorts := map[int]string{}
52+
ignorePorts := map[int]string{}
5253

5354
isLinux := runtime.GOOS == "linux"
5455

@@ -125,14 +126,14 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
125126
defer pprofSrvClose()
126127
// Do a best effort here. If this fails, it's not a big deal.
127128
if port, err := urlPort(pprofAddress); err == nil {
128-
agentPorts[port] = "pprof"
129+
ignorePorts[port] = "pprof"
129130
}
130131

131132
prometheusSrvClose := ServeHandler(ctx, logger, prometheusMetricsHandler(), prometheusAddress, "prometheus")
132133
defer prometheusSrvClose()
133134
// Do a best effort here. If this fails, it's not a big deal.
134135
if port, err := urlPort(prometheusAddress); err == nil {
135-
agentPorts[port] = "prometheus"
136+
ignorePorts[port] = "prometheus"
136137
}
137138

138139
// exchangeToken returns a session token.
@@ -196,7 +197,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
196197
return xerrors.Errorf("add executable to $PATH: %w", err)
197198
}
198199

199-
closer := agent.New(agent.Options{
200+
agnt := agent.New(agent.Options{
200201
Client: client,
201202
Logger: logger,
202203
LogDir: logDir,
@@ -215,11 +216,19 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
215216
EnvironmentVariables: map[string]string{
216217
"GIT_ASKPASS": executablePath,
217218
},
218-
AgentPorts: agentPorts,
219+
IgnorePorts: ignorePorts,
219220
SSHMaxTimeout: sshMaxTimeout,
220221
})
222+
223+
debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug")
224+
defer debugSrvClose()
225+
// Do a best effort here. If this fails, it's not a big deal.
226+
if port, err := urlPort(debugAddress); err == nil {
227+
ignorePorts[port] = "debug"
228+
}
229+
221230
<-ctx.Done()
222-
return closer.Close()
231+
return agnt.Close()
223232
},
224233
}
225234

@@ -273,6 +282,13 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
273282
Value: clibase.StringOf(&prometheusAddress),
274283
Description: "The bind address to serve Prometheus metrics.",
275284
},
285+
{
286+
Flag: "debug-address",
287+
Default: "127.0.0.1:2113",
288+
Env: "CODER_AGENT_DEBUG_ADDRESS",
289+
Value: clibase.StringOf(&debugAddress),
290+
Description: "The bind address to serve a debug HTTP server.",
291+
},
276292
}
277293

278294
return cmd

cli/testdata/coder_agent_--help.golden

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Starts the Coder workspace agent.
66
--auth string, $CODER_AGENT_AUTH (default: token)
77
Specify the authentication type to use for the agent.
88

9+
--debug-address string, $CODER_AGENT_DEBUG_ADDRESS (default: 127.0.0.1:2113)
10+
The bind address to serve a debug HTTP server.
11+
912
--log-dir string, $CODER_AGENT_LOG_DIR (default: /tmp)
1013
Specify the location for the agent log files.
1114

tailnet/conn.go

+4
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,10 @@ func (c *Conn) SetConnStatsCallback(maxPeriod time.Duration, maxConns int, dump
828828
c.tunDevice.SetStatistics(connStats)
829829
}
830830

831+
func (c *Conn) MagicsockServeHTTPDebug(w http.ResponseWriter, r *http.Request) {
832+
c.magicConn.ServeHTTPDebug(w, r)
833+
}
834+
831835
type listenKey struct {
832836
network string
833837
host string

0 commit comments

Comments
 (0)