From f758ea31f2435c61d09671f5cc76957b8198eca3 Mon Sep 17 00:00:00 2001 From: rldyourmnd Date: Tue, 1 Sep 2026 21:14:15 +0500 Subject: [PATCH] fix(controller): absent telemetry must stay an untyped nil FromEnvironment yields a typed-nil *Exporter when GDS_OTLP_ENDPOINT is unset, and main stored it straight into the service's TelemetryFlusher interface -- so the nil guard passed, the flush loop spawned, and the first tick panicked on a nil receiver. Measured live on the estate controller's first real run: one successful reconciliation, one backup, then a crash loop every ten seconds. The seam now keeps absence as an untyped nil, and the test pins exactly that. Claude-Session: https://claude.ai/code/session_01LsGid6U5RrQdFvJmvYdGCF --- core/cmd/gds-controller/main.go | 15 ++++++++++++++- core/cmd/gds-controller/main_test.go | 6 ++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/core/cmd/gds-controller/main.go b/core/cmd/gds-controller/main.go index aa73e63..07a50f5 100644 --- a/core/cmd/gds-controller/main.go +++ b/core/cmd/gds-controller/main.go @@ -139,7 +139,7 @@ func run(ctx context.Context, arguments []string, stdout io.Writer, stderr io.Wr } service := &controller.Service{ Store: store, Webhook: receiver, Worker: worker, Reconciler: runner, Backup: backups, - Telemetry: telemetryExporter, + Telemetry: telemetryFlusher(telemetryExporter), WebhookPath: runtime.Config.Controller.WebhookPath, WebhookPoll: time.Duration( runtime.Config.Controller.Schedule.WebhookPollMilliseconds, @@ -177,6 +177,19 @@ func openState(ctx context.Context, path string) (*state.Store, error) { } } +// telemetryFlusher hands the exporter to the service's optional sink slot. +// FromEnvironment yields a typed-nil *Exporter when no endpoint is +// configured, and storing that inside the interface field would defeat the +// service's nil guard: the loop would spawn and Flush would panic on a nil +// receiver — measured live as a crash after the first successful +// reconciliation. Absence must stay an untyped nil. +func telemetryFlusher(exporter *telemetry.Exporter) controller.TelemetryFlusher { + if exporter == nil { + return nil + } + return exporter +} + func startupError(writer io.Writer, phase string, err error) int { _, _ = fmt.Fprintf(writer, "gds-controller: %s startup failed (%T)\n", phase, err) return 1 diff --git a/core/cmd/gds-controller/main_test.go b/core/cmd/gds-controller/main_test.go index f71e1b1..237ce48 100644 --- a/core/cmd/gds-controller/main_test.go +++ b/core/cmd/gds-controller/main_test.go @@ -26,3 +26,9 @@ func TestRunRequiresExplicitPrivateRuntime(t *testing.T) { t.Fatalf("stderr=%q", stderr.String()) } } + +func TestTelemetryFlusherKeepsAbsentExporterUntyped(t *testing.T) { + if telemetryFlusher(nil) != nil { + t.Fatal("absent telemetry became a typed nil inside the interface") + } +}