package health import ( "context" "encoding/json" "fmt" "net" "net/http" "time" ) type Handler struct { Runtime string SemanticGeneration int ArchitectureGeneration int } func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]any{ "status": "ok", "runtime": h.Runtime, "semantic_generation": h.SemanticGeneration, "architecture_generation": h.ArchitectureGeneration, }) } type Probe struct { Name string Check func(context.Context) error Optional bool Timeout time.Duration } type Result struct { Name string `json:"name"` State string `json:"state"` Reason string `json:"reason"` Detail string `json:"detail,omitempty"` } type ReadinessHandler struct { Runtime string Probes []Probe Timeout time.Duration } func (h ReadinessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { timeout := h.Timeout if timeout <= 0 { timeout = 2 * time.Second } results := make([]Result, 0, len(h.Probes)) ready := true for _, probe := range h.Probes { result := Result{Name: probe.Name, State: "READY", Reason: "AVAILABLE"} if probe.Check == nil { result.State, result.Reason, result.Detail = "DEGRADED", "PROBE_NOT_CONFIGURED", "probe is not configured" } else { probeTimeout := timeout if probe.Timeout > 0 { probeTimeout = probe.Timeout } ctx, cancel := context.WithTimeout(r.Context(), probeTimeout) err := probe.Check(ctx) cancel() if err != nil { result.State, result.Reason, result.Detail = "DEGRADED", "DEPENDENCY_UNAVAILABLE", err.Error() if !probe.Optional { ready = false } } } if result.State != "READY" && !probe.Optional { ready = false } results = append(results, result) } status := "ready" code := http.StatusOK if !ready { status, code = "degraded", http.StatusServiceUnavailable } w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) _ = json.NewEncoder(w).Encode(map[string]any{"status": status, "runtime": h.Runtime, "checks": results}) } func HTTPProbe(client *http.Client, endpoint string) Probe { return HTTPProbeWithHeaders(client, endpoint, nil) } func HTTPProbeWithHeaders(client *http.Client, endpoint string, headers map[string]string) Probe { return Probe{Name: endpoint, Check: func(ctx context.Context) error { req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return err } for name, value := range headers { req.Header.Set(name, value) } response, err := client.Do(req) if err != nil { return err } defer response.Body.Close() if response.StatusCode < 200 || response.StatusCode >= 300 { return fmt.Errorf("status=%s", response.Status) } return nil }} } // OptionalHTTPProbe reports dependency degradation without making the whole // runtime unavailable. This is used for opportunistic fleet members when a // qualified local fallback remains available. func OptionalHTTPProbe(client *http.Client, endpoint string) Probe { probe := HTTPProbe(client, endpoint) probe.Optional = true return probe } func OptionalHTTPProbeWithHeaders(client *http.Client, endpoint string, headers map[string]string) Probe { probe := HTTPProbeWithHeaders(client, endpoint, headers) probe.Optional = true return probe } func TCPProbe(name, host, port string) Probe { return Probe{Name: name, Check: func(ctx context.Context) error { connection, err := (&net.Dialer{}).DialContext(ctx, "tcp", net.JoinHostPort(host, port)) if err != nil { return err } return connection.Close() }} }