healthx

package
v0.16.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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 CheckFunc

type CheckFunc func(context.Context) error

CheckFunc is the ergonomic adapter for normal error-returning checks.

func (CheckFunc) Check

func (f CheckFunc) Check(ctx context.Context) Result

type Checker

type Checker interface {
	Check(context.Context) Result
}

Checker supports checks that need pass/warn/fail control.

type Lifecycle

type Lifecycle uint32

Lifecycle is the Manager's coarse operating state (docs §5, §7).

const (
	LifecycleStarting Lifecycle = iota
	LifecycleRunning
	LifecycleDraining
	LifecycleStopped
)

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

func (m *Manager) Dispatch(app http.Handler) http.Handler

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

func (m *Manager) DrainDelay() time.Duration

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

func (m *Manager) ReservePaths(mux *http.ServeMux)

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

func (m *Manager) ShutdownTimeout() time.Duration

ShutdownTimeout bounds the graceful HTTP shutdown once draining completes (configx.HealthConfig.ShutdownTimeoutMS). Nil-receiver safe and defaults to the package default (30s) when unset.

func (*Manager) Stop

func (m *Manager) Stop()

Stop marks the Manager Stopped: liveness fails and readiness stays failed. Nil-receiver safe.

type Option

type Option func(*Manager)

Option configures a Manager at construction time.

func WithLogger

func WithLogger(l *slog.Logger) Option

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).

const (
	ProbeLiveness  Probe = "liveness"
	ProbeReadiness Probe = "readiness"
	ProbeStartup   Probe = "startup"
)

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.
const (
	ReasonNone    Reason = ""
	ReasonError   Reason = "error"
	ReasonTimeout Reason = "timeout"
	ReasonPanic   Reason = "panic"
	ReasonBusy    Reason = "busy"
)

type Registration

type Registration struct {
	Name    string
	Checker Checker
}

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.

type Result

type Result struct {
	Status Status
	Reason Reason
}

Result is the outcome of one Checker invocation.

type Status

type Status string

Status is the outcome of a single check invocation, using the pass/warn/fail vocabulary of the (expired) IETF health+json draft as a compatibility convention rather than a standards guarantee.

const (
	StatusPass Status = "pass"
	StatusWarn Status = "warn"
	StatusFail Status = "fail"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL