Skip to content

Commit 839ae15

Browse files
committed
feat(agent): add http debug routes for magicsock
1 parent 9afad82 commit 839ae15

File tree

3 files changed

+61
-21
lines changed

3 files changed

+61
-21
lines changed

agent/agent.go

Lines changed: 22 additions & 3 deletions
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+
MagicsockServeHTTPDebug(w http.ResponseWriter, r *http.Request)
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
}
@@ -1267,6 +1272,20 @@ func (a *agent) isClosed() bool {
12671272
}
12681273
}
12691274

1275+
func (a *agent) MagicsockServeHTTPDebug(w http.ResponseWriter, r *http.Request) {
1276+
a.closeMutex.Lock()
1277+
net := a.network
1278+
a.closeMutex.Unlock()
1279+
1280+
if net == nil {
1281+
w.WriteHeader(http.StatusOK)
1282+
w.Write([]byte("network is not ready yet"))
1283+
return
1284+
}
1285+
1286+
net.MagicsockServeHTTPDebug(w, r)
1287+
}
1288+
12701289
func (a *agent) Close() error {
12711290
a.closeMutex.Lock()
12721291
defer a.closeMutex.Unlock()

cli/agent.go

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"time"
1717

1818
"cloud.google.com/go/compute/metadata"
19+
"github.com/go-chi/chi/v5"
1920
"golang.org/x/xerrors"
2021
"gopkg.in/natefinch/lumberjack.v2"
2122
"tailscale.com/util/clientmetric"
@@ -37,7 +38,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
3738
noReap bool
3839
sshMaxTimeout time.Duration
3940
tailnetListenPort int64
40-
prometheusAddress string
41+
metricsAddress 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,7 @@ 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-
}
130-
131-
prometheusSrvClose := ServeHandler(ctx, logger, prometheusMetricsHandler(), prometheusAddress, "prometheus")
132-
defer prometheusSrvClose()
133-
// Do a best effort here. If this fails, it's not a big deal.
134-
if port, err := urlPort(prometheusAddress); err == nil {
135-
agentPorts[port] = "prometheus"
129+
ignorePorts[port] = "pprof"
136130
}
137131

138132
// exchangeToken returns a session token.
@@ -196,7 +190,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
196190
return xerrors.Errorf("add executable to $PATH: %w", err)
197191
}
198192

199-
closer := agent.New(agent.Options{
193+
agnt := agent.New(agent.Options{
200194
Client: client,
201195
Logger: logger,
202196
LogDir: logDir,
@@ -215,11 +209,19 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
215209
EnvironmentVariables: map[string]string{
216210
"GIT_ASKPASS": executablePath,
217211
},
218-
AgentPorts: agentPorts,
212+
IgnorePorts: ignorePorts,
219213
SSHMaxTimeout: sshMaxTimeout,
220214
})
215+
216+
metricsSrvClose := ServeHandler(ctx, logger, metricsHandler(agnt), metricsAddress, "metrics")
217+
defer metricsSrvClose()
218+
// Do a best effort here. If this fails, it's not a big deal.
219+
if port, err := urlPort(metricsAddress); err == nil {
220+
ignorePorts[port] = "metrics"
221+
}
222+
221223
<-ctx.Done()
222-
return closer.Close()
224+
return agnt.Close()
223225
},
224226
}
225227

@@ -270,7 +272,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
270272
Flag: "prometheus-address",
271273
Default: "127.0.0.1:2112",
272274
Env: "CODER_AGENT_PROMETHEUS_ADDRESS",
273-
Value: clibase.StringOf(&prometheusAddress),
275+
Value: clibase.StringOf(&metricsAddress),
274276
Description: "The bind address to serve Prometheus metrics.",
275277
},
276278
}
@@ -360,11 +362,18 @@ func urlPort(u string) (int, error) {
360362
return -1, xerrors.Errorf("invalid port: %s", u)
361363
}
362364

363-
func prometheusMetricsHandler() http.Handler {
364-
// We don't have any other internal metrics so far, so it's safe to expose metrics this way.
365-
// Based on: https://github.com/tailscale/tailscale/blob/280255acae604796a1113861f5a84e6fa2dc6121/ipn/localapi/localapi.go#L489
366-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
365+
func metricsHandler(a agent.Agent) http.Handler {
366+
r := chi.NewRouter()
367+
368+
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
369+
fmt.Println("get prom metrics")
367370
w.Header().Set("Content-Type", "text/plain")
368371
clientmetric.WritePrometheusExpositionFormat(w)
369372
})
373+
374+
r.Route("/debug", func(r chi.Router) {
375+
r.Get("/magicsock", a.MagicsockServeHTTPDebug)
376+
})
377+
378+
return r
370379
}

tailnet/conn.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"time"
1515

1616
"github.com/cenkalti/backoff/v4"
17+
"github.com/go-chi/chi"
1718
"github.com/google/uuid"
1819
"go4.org/netipx"
1920
"golang.org/x/xerrors"
@@ -224,6 +225,7 @@ func NewConn(options *Options) (conn *Conn, err error) {
224225
LocalAddrs: netMap.Addresses,
225226
},
226227
wireguardEngine: wireguardEngine,
228+
debugRouter: chi.NewRouter(),
227229
}
228230
defer func() {
229231
if err != nil {
@@ -281,6 +283,11 @@ func NewConn(options *Options) (conn *Conn, err error) {
281283
return nil, xerrors.Errorf("start netstack: %w", err)
282284
}
283285

286+
server.debugRouter.Get("/debug/magicsock", func(w http.ResponseWriter, r *http.Request) {
287+
fmt.Println("serve magicsock debug")
288+
server.magicConn.ServeHTTPDebug(w, r)
289+
})
290+
284291
return server, nil
285292
}
286293

@@ -331,6 +338,7 @@ type Conn struct {
331338
lastNetInfo *tailcfg.NetInfo
332339
nodeCallback func(node *Node)
333340

341+
debugRouter *chi.Mux
334342
trafficStats *connstats.Statistics
335343
}
336344

@@ -828,6 +836,10 @@ func (c *Conn) SetConnStatsCallback(maxPeriod time.Duration, maxConns int, dump
828836
c.tunDevice.SetStatistics(connStats)
829837
}
830838

839+
func (c *Conn) MagicsockServeHTTPDebug(w http.ResponseWriter, r *http.Request) {
840+
c.magicConn.ServeHTTPDebug(w, r)
841+
}
842+
831843
type listenKey struct {
832844
network string
833845
host string

0 commit comments

Comments
 (0)