Skip to content

Commit 560e629

Browse files
dilyevskyclaude
andcommitted
[tunnel] close active connections when TunnelNode is deleted
When a TunnelNode is deleted, the server now cancels all active connections for that node instead of leaving them open indefinitely. Each connection gets a cancelable context that is triggered on deletion, unblocking the handler's select loop and running cleanup. Adds CloseConnectionsByName for the case where the object is already gone and only the name is available (e.g. multicluster reconciler IsNotFound path). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d7e13ac commit 560e629

File tree

1 file changed

+36
-3
lines changed

1 file changed

+36
-3
lines changed

pkg/tunnel/server.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ type conn struct {
168168
connID string
169169
obj *corev1alpha.TunnelNode
170170
addrv4, addrv6 netip.Prefix
171+
cancel context.CancelFunc
171172
}
172173

173174
func (c *conn) String() string {
@@ -522,9 +523,13 @@ func (t *TunnelServer) makeSingleConnectHandler(ctx context.Context, qConn quic.
522523
logger = logger.With(slog.String("connUUID", connID))
523524
logger.Info("Establishing CONNECT-IP connection")
524525

526+
connCtx, connCancel := context.WithCancel(ctx)
527+
defer connCancel()
528+
525529
conn := &conn{
526530
connID: connID,
527531
obj: tn.DeepCopy(),
532+
cancel: connCancel,
528533
}
529534
p := connectip.Proxy{}
530535
if conn.Conn, err = p.Proxy(w, req); err != nil {
@@ -579,8 +584,8 @@ func (t *TunnelServer) makeSingleConnectHandler(ctx context.Context, qConn quic.
579584
select {
580585
case <-r.Context().Done():
581586
logger.Info("Tunnel connection closed")
582-
case <-ctx.Done():
583-
logger.Info("Server context closed", slog.Any("error", ctx.Err()))
587+
case <-connCtx.Done():
588+
logger.Info("Connection context canceled", slog.Any("error", connCtx.Err()))
584589
}
585590

586591
if err := conn.Close(); err != nil &&
@@ -735,6 +740,27 @@ func (t *TunnelServer) setupConn(
735740
return nil
736741
}
737742

743+
// CloseConnectionsByName closes all active connections for the TunnelNode with the given name.
744+
func (t *TunnelServer) CloseConnectionsByName(name string) {
745+
t.conns.ForEach(func(connID string, c *conn) bool {
746+
if c.obj.Name == name {
747+
slog.Info("Closing connection for removed TunnelNode",
748+
slog.String("connID", connID),
749+
slog.String("tunnelNode", name),
750+
)
751+
c.cancel()
752+
}
753+
return true
754+
})
755+
// Remove from tunnels map by scanning for the name.
756+
t.tunnels.ForEach(func(uid string, tn *corev1alpha.TunnelNode) bool {
757+
if tn.Name == name {
758+
t.tunnels.Del(uid)
759+
}
760+
return true
761+
})
762+
}
763+
738764
// ReconcileWithClient reconciles a TunnelNode using the provided client.
739765
// This method can be used by both standard reconcilers and multicluster reconcilers.
740766
func (t *TunnelServer) ReconcileWithClient(ctx context.Context, c client.Client, request reconcile.Request) (reconcile.Result, error) {
@@ -753,7 +779,14 @@ func (t *TunnelServer) ReconcileWithClient(ctx context.Context, c client.Client,
753779
if !node.DeletionTimestamp.IsZero() {
754780
log.Info("Deleting TunnelNode")
755781

756-
// TODO: Send GOAWAY to all connected clients for the associated tunnel node.
782+
// Close all active connections for this tunnel node.
783+
t.conns.ForEach(func(connID string, c *conn) bool {
784+
if c.obj.UID == node.UID {
785+
log.Info("Closing connection for deleted TunnelNode", "connID", connID)
786+
c.cancel()
787+
}
788+
return true
789+
})
757790

758791
t.tunnels.Del(string(node.UID))
759792

0 commit comments

Comments
 (0)