Report SNS as available only once its client is built
A valid CARLOS_ALERT_SNS_TOPIC_ARN with no recorded initialisation (the sink is still being built, or the metrics loop exited before it got there) fell into the same branch as a built client, so status could show alerts=sns for a sink that did not exist. alertDeliveryProbe now sets SNS only for the initialised state and SNSUnavailable only for a failed build; the unknown state is neither. Tested for unknown, ok and failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+16
−3
internal/host/alertsink.go+7 −3internal/host/alertsink_test.go+9 −0
CI passed — run details
diff --git a/internal/host/alertsink.go b/internal/host/alertsink.go| index 3fe144e02..c9aca34d8 100644 |
| --- a/internal/host/alertsink.go |
| +++ b/internal/host/alertsink.go |
| @@ -173,10 +173,14 @@ func alertDeliveryProbe(getenv func(string) string, counts *alertCounters) *stat |
| SNSFailures: counts.sns.Load(), |
| } |
| if _, err := parseAlertTopicARN(getenv("CARLOS_ALERT_SNS_TOPIC_ARN")); err == nil { |
| - if counts.snsInit.Load() == snsInitFailed { |
| - out.SNSUnavailable = true |
| - } else { |
| + // Only an initialised client is a sink. snsInitUnknown (not built |
| + // yet, or the metrics loop never got there) is neither available |
| + // nor unavailable: the box has no working SNS sink to report. |
| + switch counts.snsInit.Load() { |
| + case snsInitOK: |
| out.SNS = true |
| + case snsInitFailed: |
| + out.SNSUnavailable = true |
| } |
| } |
| return out |
diff --git a/internal/host/alertsink_test.go b/internal/host/alertsink_test.go| index 412bd840d..bd162e674 100644 |
| --- a/internal/host/alertsink_test.go |
| +++ b/internal/host/alertsink_test.go |
| @@ -317,6 +317,15 @@ func TestAlertDeliveryProbe(t *testing.T) { |
| t.Errorf("webhook only: %+v", got) |
| } |
| + // A valid ARN alone is not a sink until buildAlertSinks has built the |
| + // client: before that (a stalled build, or a metrics loop that exited |
| + // first) the box must not claim sns. |
| + got = alertDeliveryProbe(env(map[string]string{"CARLOS_ALERT_SNS_TOPIC_ARN": testTopicARN}), counts) |
| + if got.SNS || got.SNSUnavailable || got.Delivered() { |
| + t.Errorf("sink not yet built must not read as sns: %+v", got) |
| + } |
| + |
| + counts.snsInit.Store(snsInitOK) |
| got = alertDeliveryProbe(env(map[string]string{"CARLOS_ALERT_SNS_TOPIC_ARN": testTopicARN}), counts) |
| if got.Webhook || !got.SNS || !got.Delivered() { |
| t.Errorf("sns only: %+v", got) |