Skip to content

Commit bb050d4

Browse files
committed
[cmd/tunnel/run] Add health endpoint
1 parent 0645c37 commit bb050d4

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

pkg/cmd/tunnel/cmd.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ func loadTunnelNodeFromStdin() (*corev1alpha.TunnelNode, error) {
212212

213213
func init() {
214214
createCmd.Flags().StringVarP(&tunnelNodeFile, "file", "f", "", "Path to the TunnelNode file to create.")
215+
215216
updateCmd.Flags().StringVarP(&tunnelNodeFile, "file", "f", "", "Path to the TunnelNode file to update.")
217+
216218
tunnelRunCmd.Flags().StringVarP(&tunnelNodePcapPath, "pcap", "p", "", "Path to the TunnelNode file to create.")
217219
tunnelRunCmd.Flags().StringVarP(&tunnelModeS, "mode", "m", "user", "Mode to run the TunnelNode in.")
218220
tunnelRunCmd.Flags().BoolVar(&insecureSkipVerify, "insecure-skip-verify", false, "Skip TLS certificate verification.")
@@ -221,6 +223,7 @@ func init() {
221223
tunnelRunCmd.Flags().IntVar(&minConns, "min-conns", 1, "Minimum number of connections to maintain.")
222224
tunnelRunCmd.Flags().StringVar(&dnsListenAddr, "dns-addr", "127.0.0.1:8053", "Listen address for the DNS proxy. Note that you must configure backplane to use this address as well.")
223225
tunnelRunCmd.Flags().BoolVar(&autoCreate, "auto", false, "Automatically create TunnelNode if it doesn't exist.")
226+
tunnelRunCmd.Flags().StringVar(&healthAddr, "health-addr", "localhost:8080", "Listen address for health endpoint (default: localhost:8080).")
224227

225228
tunnelCmd.AddCommand(createCmd)
226229
tunnelCmd.AddCommand(getCmd)

pkg/cmd/tunnel/run.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"log/slog"
77
"math/rand"
8+
"net/http"
89
"net/netip"
910
"os"
1011
"sync"
@@ -72,6 +73,7 @@ var (
7273
minConns int
7374
dnsListenAddr string
7475
autoCreate bool
76+
healthAddr string
7577

7678
preserveDefaultGwDsts []netip.Prefix
7779
)
@@ -232,6 +234,33 @@ func (t *tunnelNodeReconciler) run(ctx context.Context, tn *corev1alpha.TunnelNo
232234
return nil
233235
})
234236

237+
// Start health endpoint server if configured
238+
if healthAddr != "" {
239+
mux := http.NewServeMux()
240+
mux.HandleFunc("/healthz", t.healthHandler)
241+
242+
healthServer := &http.Server{
243+
Addr: healthAddr,
244+
Handler: mux,
245+
}
246+
247+
g.Go(func() error {
248+
slog.Info("Starting health endpoint server", slog.String("address", healthAddr))
249+
if err := healthServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
250+
slog.Error("Health server failed", slog.Any("error", err))
251+
return err
252+
}
253+
return nil
254+
})
255+
256+
g.Go(func() error {
257+
<-gctx.Done()
258+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
259+
defer cancel()
260+
return healthServer.Shutdown(shutdownCtx)
261+
})
262+
}
263+
235264
r, err := tunnel.BuildClientRouter(
236265
tunnel.WithPcapPath(tunnelNodePcapPath),
237266
tunnel.WithMode(tunnelMode),
@@ -403,3 +432,32 @@ func (t *tunnelNodeReconciler) reconcile(ctx context.Context, req ctrl.Request)
403432

404433
return ctrl.Result{}, nil
405434
}
435+
436+
// healthHandler returns 200 OK when at least one tunnel connection is active, 503 otherwise.
437+
// This endpoint is used for health checks to determine if the tunnel node has active connections.
438+
// The health endpoint is only started when the --health-endpoint flag is provided with a valid
439+
// address (e.g., ":8080" or "0.0.0.0:8080").
440+
//
441+
// Response codes:
442+
// - 200 OK: At least one tunnel connection is active
443+
// - 503 Service Unavailable: No active tunnel connections
444+
func (t *tunnelNodeReconciler) healthHandler(w http.ResponseWriter, r *http.Request) {
445+
t.tunMu.RLock()
446+
defer t.tunMu.RUnlock()
447+
448+
// Check if we have at least one active connection
449+
activeConns := 0
450+
for _, conn := range t.tunDialerWorkers {
451+
if conn.conn != nil && conn.conn.Context().Err() == nil {
452+
activeConns++
453+
}
454+
}
455+
456+
if activeConns > 0 {
457+
w.WriteHeader(http.StatusOK)
458+
fmt.Fprintf(w, "OK - %d active connection(s)\n", activeConns)
459+
} else {
460+
w.WriteHeader(http.StatusServiceUnavailable)
461+
fmt.Fprintf(w, "UNHEALTHY - no active connections\n")
462+
}
463+
}

0 commit comments

Comments
 (0)