Documentation
¶
Overview ¶
Package healthx is the generated control-plane health subsystem: the user hook API a service registers dependency checks against, plus the executor and HTTP dispatch behind /livez, /readyz, and /startupz. It is copied and import-rewritten into generated projects by APIC's emitPkg mechanism. See docs/HEALTH_CHECK_ENHANCEMENT.md §3 for the hook API this file implements verbatim, and §5 for the Manager.
Index ¶
- Variables
- type CheckFunc
- type Checker
- type Lifecycle
- type Manager
- func (m *Manager) BeginDrain()
- func (m *Manager) Dispatch(app http.Handler) http.Handler
- func (m *Manager) DrainDelay() time.Duration
- func (m *Manager) Evaluate(ctx context.Context, probe Probe) ProbeResult
- func (m *Manager) ReservePaths(mux *http.ServeMux)
- func (m *Manager) ShutdownTimeout() time.Duration
- func (m *Manager) Stop()
- type Option
- type Probe
- type ProbeResult
- type Reason
- type Registration
- type Result
- type Status
Constants ¶
This section is empty.
Variables ¶
var ErrHealthConfig = errors.New("healthx: invalid health check configuration")
ErrHealthConfig is the sentinel wrapped by every fail-closed registration matching error New returns (docs/HEALTH_CHECK_ENHANCEMENT.md §3's declared-check/registration matching table).
Functions ¶
This section is empty.
Types ¶
type Lifecycle ¶
type Lifecycle uint32
Lifecycle is the Manager's coarse operating state (docs §5, §7).
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns the matched set of health checks plus their execution and lifecycle state, and backs the /livez, /readyz, and /startupz probe dispatch. All methods are nil-receiver safe so a disabled subsystem (New returning a nil *Manager) is a legal, inert value callers can use without a nil check at every call site.
func New ¶
func New(cfg *configx.HealthConfig, regs []Registration, opts ...Option) (*Manager, error)
New constructs a Manager from the generated health configuration and the checks registered at boot (via WithHealthCheck/WithHealthChecker), enforcing the fail-closed matching rules from docs/HEALTH_CHECK_ENHANCEMENT.md §3:
- A nil or disabled config with no registrations is a legal no-op: New returns (nil, nil).
- A nil or disabled config with any registrations is a configuration mistake: New refuses to start.
- Every entry in health.checks must have exactly one registration; a missing registration, a registration used more than once, or a registration whose name is not declared in health.checks each refuse to start.
All errors wrap ErrHealthConfig and name the offending check(s).
func (*Manager) BeginDrain ¶
func (m *Manager) BeginDrain()
BeginDrain transitions the Manager to Draining so readiness flips to failure instantly (§7) while liveness stays successful (§1). It logs server_draining once on the first transition (§9) and records the readiness change. Nil-receiver safe.
func (*Manager) Dispatch ¶
Dispatch wraps app so the three configured probe paths are served before application middleware runs (§6), independent of authentication, CSRF, and rate limiting. Matching is an EXACT string comparison against the configured paths — no prefix matching, no path cleaning, since the paths were already validated at generation. Any path that doesn't match one of the three probes falls straight through to app untouched. A nil receiver (the disabled subsystem) returns app unchanged.
func (*Manager) DrainDelay ¶
DrainDelay reports how long readiness reports failure before shutdown proceeds, giving load balancers time to stop routing new traffic (configx.HealthConfig.DrainDelayMS). Nil-receiver safe (returns 0).
func (*Manager) Evaluate ¶
func (m *Manager) Evaluate(ctx context.Context, probe Probe) ProbeResult
Evaluate runs the probe and returns its aggregated ProbeResult. It is the single entry point the Task 4 HTTP handler calls. A nil receiver — the legal representation of a disabled subsystem — reports pass with an empty summary so callers need no nil check.
func (*Manager) ReservePaths ¶
ReservePaths registers sentinel 404 handlers on each configured probe path so that a generated route or ExtraRoutes registration colliding with a probe path fails LOUDLY at boot — http.ServeMux panics on a duplicate pattern registration — instead of being silently shadowed. Each path is claimed both as the bare pattern ("/livez") and in every method-prefixed form the generator can emit ("GET /livez", "HEAD /livez", ...): Go 1.22 ServeMux precedence lets a method-specific pattern coexist with a bare one, so the bare sentinel alone would NOT trip on the generator's method-prefixed registrations — the generated route would just go silently dead behind Dispatch. Registering bare + method forms ourselves is safe (more-specific coexists with generic), while any colliding registration — bare or method-prefixed — now hits an identical pattern and panics.
In production Dispatch intercepts every probe path before a request ever reaches mux, so these sentinels never actually serve; they exist purely as a runtime backstop behind the (Task 5) config-time validation that rejects path collisions before generation. Nil-receiver and nil-mux safe no-op.
func (*Manager) ShutdownTimeout ¶
ShutdownTimeout bounds the graceful HTTP shutdown once draining completes (configx.HealthConfig.ShutdownTimeoutMS). Nil-receiver safe and defaults to the package default (30s) when unset.
type Option ¶
type Option func(*Manager)
Option configures a Manager at construction time.
func WithLogger ¶
WithLogger sets the structured logger the Manager uses for lifecycle and check-transition logging. A nil logger is ignored.
func WithMetrics ¶
func WithMetrics(m obsx.MetricsProvider) Option
WithMetrics sets the MetricsProvider the Manager reports check outcomes to. A nil provider is ignored.
type Probe ¶
type Probe string
Probe identifies one of the three generated probe endpoints (see docs/HEALTH_CHECK_ENHANCEMENT.md §2 and §6).
type ProbeResult ¶
type ProbeResult struct {
Status Status
Probe Probe
Duration time.Duration
Total int
Passed int
Warned int
Failed int
}
ProbeResult is the aggregated outcome of evaluating one probe. It is the data source for the §8 public response shape (docs/HEALTH_CHECK_ENHANCEMENT.md): it carries the overall Status, the probe it describes, the wall-clock Duration of the evaluation, and the pass/warn/fail summary counts — but never the names of individual checks, which would disclose architecture on an unauthenticated endpoint (§8).
type Reason ¶
type Reason string
Reason further qualifies a non-pass Status. This is the closed value space for apic_health_check_total's reason label (docs/OBSERVABILITY.md): every value here must be reachable on that metric, or be removed from both this enum and that doc together (GAP-0101). The check-outcome metric (and the transition logs) are emitted exclusively by invoke() on a real check completion, whose hook runs on a context.Background()-rooted context bounded by the per-check timeout — so the only cancellation it can ever observe is its own deadline (context.DeadlineExceeded -> ReasonTimeout), never a caller's cancellation. Hence the five reasons below are the complete set.
ReasonCanceled/ReasonStarting/ReasonDraining were previously declared here with no reachable emission and have been removed:
- canceled: a caller canceling its probe (client/LB disconnect, a kubelet probe timeout shorter than the check budget, shutdown) is NOT a check outcome. runCheck's give-up branch returns fail without emitting any check metric or log — the still-running invoke() emits the real outcome once — so "canceled" could only ever reach the metric by double-counting (a spurious second emission on top of the real one), which is exactly the regression this removal closes. A canceled probe surfaces at the probe level (apic_health_probe_total{status=fail}), not as a per-check reason.
- starting/draining: probe-level lifecycle states, not per-check reasons. evaluateReadiness's Draining/Stopped branches short-circuit to a bare ProbeResult{Status: StatusFail} without running any check. A not-yet- latched Starting DOES run its startup checks (via ensureStartupLatched -> evaluateChecks), but each reports its OWN real reason (pass/error/ timeout/panic/busy), never "starting". Draining is signaled via ProbeResult.Status and the readiness_state_changed/server_draining logs.
type Registration ¶
Registration binds a declared health.checks name (see docs/HEALTH_CHECK_ENHANCEMENT.md §2) to the Checker implementation a service provides at startup via WithHealthCheck/WithHealthChecker.