Skip to content

feat: monitor source-deployment probes against local app - #9

Merged
vercel-eddie merged 1 commit into
mainfrom
eddie/probe-monitoring
May 20, 2026
Merged

vercel-eddie merged 1 commit into
mainfrom
eddie/probe-monitoring

Conversation

@vercel-eddie

Copy link
Copy Markdown
Collaborator

Summary

  • Add an InterceptorService with GetStatus that surfaces the live result of running the source deployment's liveness / readiness / startup probes against the developer's local app inside the devcontainer.
  • Carry the source probes through GetMetadata from the proxy and run them on a per-probe pkg/probe Monitor goroutine.
  • Remap probe ports: if the source probe targets the deployment's primary app port, the interceptor checks --app-port locally; other ports (e.g. dedicated /healthz ports) pass through.

Stacked on eddie/fuse-mount-discovery — the discover work touches the same server.go flag-parsing block. Once that PR merges, this one will rebase cleanly onto main.

Architecture

source deployment             proxy pod (in cluster)              devcontainer (local)
─────────────────             ──────────────────────              ────────────────────
LivenessProbe ─────┐
ReadinessProbe ────┼── injectProxyImage:  ──► bridge server      bridge intercept
StartupProbe ──────┘  protojson-encode and    --liveness-probe   ▲
                      pass as CLI flags       --readiness-probe  │  GetMetadata
                                              --startup-probe    │  returns probes
                                              --source-app-port  │
                                                       │         │
                                                       ▼         │
                                              GetMetadata returns Probe protos
                                                                 │
                                                                 ▼
                                                        pkg/probe.Monitor
                                                        ─ HTTP / TCP / gRPC / Exec
                                                        ─ per-probe goroutine
                                                        ─ tracks consecutive succ/fail
                                                                 │
                                                                 ▼
                                                  InterceptorService.GetStatus()
                                                        ─ ready
                                                        ─ liveness  ProbeStatus
                                                        ─ readiness ProbeStatus
                                                        ─ startup   ProbeStatus

What's in this PR

  • proto/bridge/v1/proxy.proto — Probe + HTTPGetAction / TCPSocketAction / GRPCAction / ExecAction / HTTPHeader; new fields on GetMetadataResponse: source_app_port, liveness_probe, readiness_probe, startup_probe.
  • proto/bridge/v1/interceptor.proto (new) — InterceptorService.GetStatus, ProbeStatus, ProbeHealth enum.
  • pkg/k8s/resources/probes.go (new) — convertProbe(corev1.Probe → bridge.v1.Probe), named-port resolution, JSON-encoded flag emission.
  • pkg/k8s/resources/resources.go — injectProxyImage captures the source's probes before clobbering them and threads --liveness-probe / --readiness-probe / --startup-probe / --source-app-port into the proxy command.
  • pkg/commands/server.go — parses the new flags, passes them through proxy.Config.
  • pkg/proxy/grpcserver.go — stores probes + source app port; surfaces them via GetMetadata.
  • pkg/probe/ (new) — Monitor with HTTP, TCP, gRPC, and Exec runners; tracks consecutive successes/failures; thread-safe Status() snapshot via proto.Clone.
  • pkg/commands/intercept.go — builds one Monitor per configured probe with port remap (source_app_port → --app-port).
  • pkg/commands/intercept_health.go — interceptServer implements InterceptorService; GetStatus returns Ready + per-probe ProbeStatus.

Design notes

  • All four handler types implemented in-tree (pkg/probe): HTTP (2xx/3xx success, follows headers, TLS skip for local self-signed dev), TCP (dial-and-close), gRPC (uses grpc_health_v1 with optional service name), Exec (exec.CommandContext, exit code 0 = healthy).
  • Port remap is hint-based. The proxy advertises source_app_port (the source's first non-grpc port). The interceptor only remaps when a probe targets exactly that port — management ports (e.g. /metrics on 9090) keep their original target so health checks against dedicated endpoints still work.
  • Defaults match the kubelet. Period 10s, timeout 1s, success threshold 1, failure threshold 3 — matching corev1 defaults so behaviour is identical to in-cluster.
  • State is read-only over gRPC. GetStatus is safe to poll; it returns a proto.Clone of the cached status (no fresh probe runs triggered).
  • Pending → Healthy → Unhealthy. A probe starts PROBE_HEALTH_PENDING until enough consecutive successes/failures meet the thresholds.

Test plan

  • go test ./pkg/... — all pass
  • pkg/probe tests: HTTP healthy, HTTP failing (≥failure_threshold transitions to UNHEALTHY), TCP open/closed, Exec success/failure, nil-handler rejection, context-cancel stops the goroutine.
  • pkg/k8s/resources/probes_test.go: HTTP numeric port, HTTP named-port resolution, unresolvable named port → nil, TCP/gRPC/Exec conversions, primaryAppPort corner cases, injectProxyImage emits the new flags.
  • pkg/commands/intercept_health_test.go: GetStatus before ready, after ready + one monitor, all three monitors attached.
  • Manual: bridge create -c userservice, run the local app, bridge exec against the interceptor's GetStatus (or curl the intercept gRPC) and confirm probe statuses match.

🤖 Generated with Claude Code

@vercel-eddie
vercel-eddie force-pushed the eddie/probe-monitoring branch from 5382fe2 to a0a40ee Compare May 20, 2026 17:05
@vercel-eddie
vercel-eddie changed the base branch from eddie/fuse-mount-discovery to main May 20, 2026 17:05
@vercel-eddie
vercel-eddie force-pushed the eddie/probe-monitoring branch 4 times, most recently from 0d34126 to 34c4c96 Compare May 20, 2026 17:37
Carry the source deployment's liveness, readiness, and startup probes
through to the interceptor and run them continuously against the
developer's local app inside the devcontainer. Surface the latest
results through a new InterceptorService.GetStatus RPC so the CLI can
report whether the local app is matching the cluster's expected health
contract.

- proxy.proto: add Probe + HTTPGet/TCPSocket/GRPC/Exec actions to
  GetMetadataResponse, plus source_app_port hint for port remapping.
- interceptor.proto: new InterceptorService with GetStatus returning
  ProbeStatus per probe (pending/healthy/unhealthy + counters).
- pkg/k8s/resources: convert corev1.Probe -> bridge.v1.Probe in
  injectProxyImage, JSON-encode via protojson, pass as CLI flags on
  the bridge server before nulling out the original probes.
- pkg/proxy: accept and serve the probes via GetMetadata.
- pkg/probe: per-probe Monitor goroutine implementing HTTP, TCP, gRPC
  health, and Exec checks with configurable timing/thresholds.
- pkg/commands/intercept: build monitors from GetMetadata, applying
  source_app_port -> --app-port remap when the probe targets the
  primary port.
- pkg/commands/intercept_health: interceptServer implements
  InterceptorService, surfacing each monitor's current ProbeStatus.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel-eddie
vercel-eddie force-pushed the eddie/probe-monitoring branch from 34c4c96 to b91084c Compare May 20, 2026 18:27
@vercel-eddie
vercel-eddie merged commit 8eb3b6e into main May 20, 2026
5 checks passed
@vercel-eddie
vercel-eddie deleted the eddie/probe-monitoring branch May 20, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant