fix(monitoring): wire backup-verification alarms to site-alerts topic (#24)

Both alarms changed state but notified nobody (no AlarmActions). Wire
each to the org operational alarm topic (site-alerts, CMK-encrypted)
with an ALARM action only — no OK action per org convention.
This commit is contained in:
Adam Moussa 2026-06-05 14:47:09 -04:00 • committed by GitHub
parent a7536f0bd7
commit 149178e1e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,5 +1,7 @@
import * as cdk from "aws-cdk-lib"; import * as cdk from "aws-cdk-lib";
import * as cloudwatch from "aws-cdk-lib/aws-cloudwatch"; import * as cloudwatch from "aws-cdk-lib/aws-cloudwatch";
import * as cloudwatch_actions from "aws-cdk-lib/aws-cloudwatch-actions";
import * as sns from "aws-cdk-lib/aws-sns";
import * as events from "aws-cdk-lib/aws-events"; import * as events from "aws-cdk-lib/aws-events";
import * as events_targets from "aws-cdk-lib/aws-events-targets"; import * as events_targets from "aws-cdk-lib/aws-events-targets";
import * as iam from "aws-cdk-lib/aws-iam"; import * as iam from "aws-cdk-lib/aws-iam";
@ -105,7 +107,16 @@ export class BackupVerification extends Construct {
// 11:01 UTC even though no error ever occurred. NOT_BREACHING means "no // 11:01 UTC even though no error ever occurred. NOT_BREACHING means "no
// data = no errors = healthy"; the separate not-running alarm below covers // data = no errors = healthy"; the separate not-running alarm below covers
// the "verification never ran" case. // the "verification never ran" case.
new cloudwatch.Alarm(this, "ErrorAlarm", { // Org operational alarm topic (site-alerts, CMK-encrypted — never
// alias/aws/sns, which CloudWatch cannot publish to). Alarm action only,
// no OK action, per org convention.
const alertTopic = sns.Topic.fromTopicArn(
this,
"AlertTopic",
"arn:aws:sns:us-east-1:328440206208:site-alerts"
);
const errorAlarm = new cloudwatch.Alarm(this, "ErrorAlarm", {
alarmName: "forgejo-backup-verification-errors", alarmName: "forgejo-backup-verification-errors",
alarmDescription: "Backup verification Lambda is failing — Slack notifications may not be firing", alarmDescription: "Backup verification Lambda is failing — Slack notifications may not be firing",
metric: fn.metricErrors({ period: cdk.Duration.hours(1) }), metric: fn.metricErrors({ period: cdk.Duration.hours(1) }),
@ -114,11 +125,12 @@ export class BackupVerification extends Construct {
treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING, treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
}); });
errorAlarm.addAlarmAction(new cloudwatch_actions.SnsAction(alertTopic));
// Not-running alarm: fires if the daily verification did not invoke at all // Not-running alarm: fires if the daily verification did not invoke at all
// in a 24h window. This is the real "missing run" guard that the errors // in a 24h window. This is the real "missing run" guard that the errors
// alarm's BREACHING setting was previously (and incorrectly) providing. // alarm's BREACHING setting was previously (and incorrectly) providing.
new cloudwatch.Alarm(this, "NotRunningAlarm", { const notRunningAlarm = new cloudwatch.Alarm(this, "NotRunningAlarm", {
alarmName: "forgejo-backup-verification-not-running", alarmName: "forgejo-backup-verification-not-running",
alarmDescription: "Backup verification Lambda has not run in the last 24h — daily verification may be broken", alarmDescription: "Backup verification Lambda has not run in the last 24h — daily verification may be broken",
metric: fn.metricInvocations({ metric: fn.metricInvocations({
@ -130,6 +142,7 @@ export class BackupVerification extends Construct {
treatMissingData: cloudwatch.TreatMissingData.BREACHING, treatMissingData: cloudwatch.TreatMissingData.BREACHING,
comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD, comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
}); });
notRunningAlarm.addAlarmAction(new cloudwatch_actions.SnsAction(alertTopic));
this.functionArn = fn.functionArn; this.functionArn = fn.functionArn;
} }