Skip to content

Commit 736767e

Browse files
jonathan-luoJonathan Luo
andauthored
feat(AlarmFactory): add suppressor alarm props to AddCompositeAlarmProps (#518)
### Description Closes #516 CloudWatch `CompositeAlarm` now supports specifying a suppressor alarm `actionsSuppressor` when initializing (see [CompositeAlarm API reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-compositealarm.html)). However, this additional parameter is not present within `AddCompositeAlarmProps`, and thus, it is not possible to create a `CompositeAlarm` using `AlarmFactory.addCompositeAlarm(...)` while simultaneously specifying a suppressor alarm. This pull request adds this functionality, while being backwards-compatible, since all new props are optional. ### Testing - `yarn build` - Added suite of unit tests to test suppressor alarm prop behavior. --- _By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_ Co-authored-by: Jonathan Luo <[email protected]>
1 parent 619690d commit 736767e

File tree

4 files changed

+429
-0
lines changed

4 files changed

+429
-0
lines changed

API.md

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/common/alarm/AlarmFactory.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ComparisonOperator,
77
CompositeAlarm,
88
HorizontalAnnotation,
9+
IAlarm,
910
IAlarmRule,
1011
IMetric,
1112
MathExpression,
@@ -388,6 +389,29 @@ export interface AddCompositeAlarmProps {
388389
* @default - OR
389390
*/
390391
readonly compositeOperator?: CompositeAlarmOperator;
392+
393+
/**
394+
* Actions will be suppressed if the suppressor alarm is in the ALARM state.
395+
*
396+
* @default - no suppressor alarm
397+
*/
398+
readonly actionsSuppressor?: IAlarm;
399+
400+
/**
401+
* The maximum time in seconds that the composite alarm waits after suppressor alarm goes out of the ALARM state.
402+
* After this time, the composite alarm performs its actions.
403+
*
404+
* @default - 60 seconds
405+
*/
406+
readonly actionsSuppressorExtensionPeriod?: Duration;
407+
408+
/**
409+
* The maximum duration that the composite alarm waits for the suppressor alarm to go into the ALARM state.
410+
* After this time, the composite alarm performs its actions.
411+
*
412+
* @default - 60 seconds
413+
*/
414+
readonly actionsSuppressorWaitPeriod?: Duration;
391415
}
392416

393417
/**
@@ -779,6 +803,9 @@ export class AlarmFactory {
779803
alarmDescription,
780804
alarmRule,
781805
actionsEnabled,
806+
actionsSuppressor: props?.actionsSuppressor,
807+
actionsSuppressorExtensionPeriod: props?.actionsSuppressorExtensionPeriod,
808+
actionsSuppressorWaitPeriod: props?.actionsSuppressorWaitPeriod,
782809
});
783810

784811
action.addAlarmActions({

test/common/alarm/AlarmFactory.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,93 @@ test("addCompositeAlarm: snapshot for operator", () => {
584584
expect(Template.fromStack(stack)).toMatchSnapshot();
585585
});
586586

587+
test("addCompositeAlarm: snapshot for suppressor alarm props", () => {
588+
const stack = new Stack();
589+
const factory = new AlarmFactory(stack, {
590+
globalMetricDefaults,
591+
globalAlarmDefaults: globalAlarmDefaultsWithDisambiguator,
592+
localAlarmNamePrefix: "prefix",
593+
});
594+
const alarm1 = factory.addAlarm(metric, {
595+
alarmNameSuffix: "Alarm1",
596+
alarmDescription: "Testing alarm 1",
597+
threshold: 1,
598+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
599+
treatMissingData: TreatMissingData.MISSING,
600+
});
601+
const alarm2 = factory.addAlarm(metric, {
602+
alarmNameSuffix: "Alarm2",
603+
alarmDescription: "Testing alarm 2",
604+
threshold: 2,
605+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
606+
treatMissingData: TreatMissingData.MISSING,
607+
});
608+
const suppressorAlarm = factory.addCompositeAlarm([alarm1, alarm2], {
609+
disambiguator: "SuppressorAlarm",
610+
alarmNameSuffix: "SuppressorAlarm",
611+
});
612+
factory.addCompositeAlarm([alarm1, alarm2], {
613+
disambiguator: "SuppressedAlarmDefault",
614+
alarmNameSuffix: "SuppressedAlarmDefault",
615+
actionsSuppressor: suppressorAlarm,
616+
});
617+
factory.addCompositeAlarm([alarm1, alarm2], {
618+
disambiguator: "SuppressedAlarmTestExtensionPeriod",
619+
alarmNameSuffix: "SuppressedAlarmTestExtensionPeriod",
620+
actionsSuppressor: suppressorAlarm,
621+
actionsSuppressorExtensionPeriod: Duration.seconds(100),
622+
});
623+
factory.addCompositeAlarm([alarm1, alarm2], {
624+
disambiguator: "SuppressedAlarmTestWaitPeriod",
625+
alarmNameSuffix: "SuppressedAlarmTestWaitPeriod",
626+
actionsSuppressor: suppressorAlarm,
627+
actionsSuppressorWaitPeriod: Duration.seconds(100),
628+
});
629+
factory.addCompositeAlarm([alarm1, alarm2], {
630+
disambiguator: "SuppressedAlarmTestBothExtensionAndWaitPeriod",
631+
alarmNameSuffix: "SuppressedAlarmTestBothExtensionAndWaitPeriod",
632+
actionsSuppressor: suppressorAlarm,
633+
actionsSuppressorExtensionPeriod: Duration.seconds(100),
634+
actionsSuppressorWaitPeriod: Duration.seconds(100),
635+
});
636+
637+
expect(Template.fromStack(stack)).toMatchSnapshot();
638+
});
639+
640+
test("addCompositeAlarm: actions suppressor extension period specified but no actions suppressor throws error", () => {
641+
expect(() => {
642+
factory.addCompositeAlarm([], {
643+
disambiguator: "CompositeAlarmExtensionPeriodSpecifiedNoSuppressorAlarm",
644+
alarmNameSuffix:
645+
"CompositeAlarmExtensionPeriodSpecifiedNoSuppressorAlarm",
646+
actionsSuppressorExtensionPeriod: Duration.seconds(100),
647+
});
648+
}).toThrow(Error);
649+
});
650+
651+
test("addCompositeAlarm: actions suppressor wait period specified but no actions suppressor throws error", () => {
652+
expect(() => {
653+
factory.addCompositeAlarm([], {
654+
disambiguator: "CompositeAlarmWaitPeriodSpecifiedNoSuppressorAlarm",
655+
alarmNameSuffix: "CompositeAlarmWaitPeriodSpecifiedNoSuppressorAlarm",
656+
actionsSuppressorWaitPeriod: Duration.seconds(100),
657+
});
658+
}).toThrow(Error);
659+
});
660+
661+
test("addCompositeAlarm: actions suppressor both extension and wait periods specified but no actions suppressor throws error", () => {
662+
expect(() => {
663+
factory.addCompositeAlarm([], {
664+
disambiguator:
665+
"CompositeAlarmBothExtensionAndWaitPeriodSpecifiedNoSuppressorAlarm",
666+
alarmNameSuffix:
667+
"CompositeAlarmBothExtensionAndWaitPeriodSpecifiedNoSuppressorAlarm",
668+
actionsSuppressorExtensionPeriod: Duration.seconds(100),
669+
actionsSuppressorWaitPeriod: Duration.seconds(100),
670+
});
671+
}).toThrow(Error);
672+
});
673+
587674
test("addAlarm: original actionOverride with a different action gets preserved", () => {
588675
const originalActionOverride = new SnsAlarmActionStrategy({
589676
onAlarmTopic: new Topic(stack, "Dummy1"),

0 commit comments

Comments
 (0)