diff --git a/API.md b/API.md index 7ff6dc96..e9425caf 100644 --- a/API.md +++ b/API.md @@ -127,7 +127,7 @@ to each other. --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { BitmapDashboard } from 'cdk-monitoring-constructs' @@ -137,20 +137,6 @@ BitmapDashboard.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -341,7 +327,7 @@ public asBitmap(widget: IWidget): CustomWidget --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { BitmapWidgetRenderingSupport } from 'cdk-monitoring-constructs' @@ -351,20 +337,6 @@ BitmapWidgetRenderingSupport.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -532,7 +504,7 @@ to each other. --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { DashboardWithBitmapCopy } from 'cdk-monitoring-constructs' @@ -542,20 +514,6 @@ DashboardWithBitmapCopy.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -802,7 +760,7 @@ Gets the dashboard for the requested dashboard type. --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { DefaultDashboardFactory } from 'cdk-monitoring-constructs' @@ -812,20 +770,6 @@ DefaultDashboardFactory.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -1002,7 +946,7 @@ Gets the dashboard for the requested dashboard type. --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { DynamicDashboardFactory } from 'cdk-monitoring-constructs' @@ -1012,20 +956,6 @@ DynamicDashboardFactory.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -2218,7 +2148,7 @@ public monitorWebApplicationFirewallAclV2(props: WafV2MonitoringProps): Monitori --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { MonitoringFacade } from 'cdk-monitoring-constructs' @@ -2228,20 +2158,6 @@ MonitoringFacade.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -2393,7 +2309,7 @@ Creates a new widget factory. --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { MonitoringScope } from 'cdk-monitoring-constructs' @@ -2403,20 +2319,6 @@ MonitoringScope.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -2486,7 +2388,7 @@ public addSecret(secret: ISecret): void --- -##### `isConstruct` +##### ~~`isConstruct`~~ ```typescript import { SecretsManagerMetricsPublisher } from 'cdk-monitoring-constructs' @@ -2496,20 +2398,6 @@ SecretsManagerMetricsPublisher.isConstruct(x: any) Checks if `x` is a construct. -Use this method instead of `instanceof` to properly detect `Construct` -instances, even when the construct library is symlinked. - -Explanation: in JavaScript, multiple copies of the `constructs` library on -disk are seen as independent, completely different libraries. As a -consequence, the class `Construct` in each copy of the `constructs` library -is seen as a different class, and an instance of one class will not test as -`instanceof` the other class. `npm install` will not create installations -like this, but users may manually symlink construct libraries together or -use a monorepo tool: in those cases, multiple copies of the `constructs` -library can be accidentally installed, and `instanceof` will behave -unpredictably. It is safest to avoid using `instanceof`, and using -this type-testing method instead. - ###### `x`Required - *Type:* any @@ -3029,6 +2917,7 @@ const addCompositeAlarmProps: AddCompositeAlarmProps = { ... } | alarmDescriptionOverride | string | A text included in the generated ticket description body, which fully replaces the generated text. | | alarmNameOverride | string | If this is defined, the alarm name is set to this exact value. | | alarmNameSuffix | string | Suffix added to base alarm name. | +| atLeastOptions | CompositeAlarmAtLeastOptions | Options for AT_LEAST operator. | | compositeOperator | CompositeAlarmOperator | Logical operator used to aggregate the status individual alarms. | | customParams | {[ key: string ]: any} | This allows user to attach custom parameters to this alarm, which can later be accessed from the "useCreatedAlarms" method. | | customTags | string[] | This allows user to attach custom values to this alarm, which can later be accessed from the "useCreatedAlarms" method. | @@ -3191,6 +3080,21 @@ Alarm names need to be unique. --- +##### `atLeastOptions`Optional + +```typescript +public readonly atLeastOptions: CompositeAlarmAtLeastOptions; +``` + +- *Type:* CompositeAlarmAtLeastOptions +- *Default:* undefined + +Options for AT_LEAST operator. + +Required when compositeOperator is AT_LEAST. + +--- + ##### `compositeOperator`Optional ```typescript @@ -12812,6 +12716,54 @@ public readonly addFailedBuildRateAlarm: {[ key: string ]: ErrorRateThreshold}; --- +### CompositeAlarmAtLeastOptions + +Configuration for AT_LEAST composite alarm operator. + +#### Initializer + +```typescript +import { CompositeAlarmAtLeastOptions } from 'cdk-monitoring-constructs' + +const compositeAlarmAtLeastOptions: CompositeAlarmAtLeastOptions = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| threshold | AtLeastThreshold | Threshold for AT_LEAST operator. | +| state | aws-cdk-lib.aws_cloudwatch.AlarmState | Alarm state for AT_LEAST operator. | + +--- + +##### `threshold`Required + +```typescript +public readonly threshold: AtLeastThreshold; +``` + +- *Type:* AtLeastThreshold + +Threshold for AT_LEAST operator. + +Use AtLeastThreshold.count() or AtLeastThreshold.percentage(). + +--- + +##### `state`Optional + +```typescript +public readonly state: AlarmState; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.AlarmState +- *Default:* ALARM + +Alarm state for AT_LEAST operator. + +--- + ### ConsumedCapacityThreshold #### Initializer @@ -62587,6 +62539,71 @@ public readonly tpsMetric: Metric | MathExpression; --- +### AtLeastThreshold + +Threshold for AT_LEAST operator. + +#### Initializers + +```typescript +import { AtLeastThreshold } from 'cdk-monitoring-constructs' + +new AtLeastThreshold() +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | + +--- + + +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| count | Create a count-based threshold. | +| percentage | Create a percentage-based threshold. | + +--- + +##### `count` + +```typescript +import { AtLeastThreshold } from 'cdk-monitoring-constructs' + +AtLeastThreshold.count(count: number) +``` + +Create a count-based threshold. + +###### `count`Required + +- *Type:* number + +The minimum number of alarms that must be in the specified state. + +--- + +##### `percentage` + +```typescript +import { AtLeastThreshold } from 'cdk-monitoring-constructs' + +AtLeastThreshold.percentage(percentage: number) +``` + +Create a percentage-based threshold. + +###### `percentage`Required + +- *Type:* number + +The minimum percentage of alarms (0-100) that must be in the specified state. + +--- + + + ### AuroraAlarmFactory #### Initializers @@ -89167,6 +89184,7 @@ alarm to represent. | --- | --- | | AND | trigger only if all the alarms are triggered. | | OR | trigger if any of the alarms is triggered. | +| AT_LEAST | trigger if at least M alarms are in the specified state Requires atLeastOptions to be specified. | --- @@ -89184,6 +89202,13 @@ trigger if any of the alarms is triggered. --- +##### `AT_LEAST` + +trigger if at least M alarms are in the specified state Requires atLeastOptions to be specified. + +--- + + ### DashboardRenderingPreference Preferred way of rendering dashboard widgets. diff --git a/lib/common/alarm/AlarmFactory.ts b/lib/common/alarm/AlarmFactory.ts index 990f8d5e..8de29a87 100644 --- a/lib/common/alarm/AlarmFactory.ts +++ b/lib/common/alarm/AlarmFactory.ts @@ -421,6 +421,14 @@ export interface AddCompositeAlarmProps { */ readonly compositeOperator?: CompositeAlarmOperator; + /** + * Options for AT_LEAST operator. + * Required when compositeOperator is AT_LEAST. + * + * @default - undefined + */ + readonly atLeastOptions?: CompositeAlarmAtLeastOptions; + /** * Actions will be suppressed if the suppressor alarm is in the ALARM state. * @@ -462,6 +470,86 @@ export enum CompositeAlarmOperator { * trigger if any of the alarms is triggered */ OR, + + /** + * trigger if at least M alarms are in the specified state + * Requires atLeastOptions to be specified + */ + AT_LEAST, +} + +/** + * Threshold for AT_LEAST operator. + */ +export abstract class AtLeastThreshold { + /** + * Create a count-based threshold. + * @param count The minimum number of alarms that must be in the specified state + */ + public static count(count: number): AtLeastThreshold { + return new AtLeastThresholdCount(count); + } + + /** + * Create a percentage-based threshold. + * @param percentage The minimum percentage of alarms (0-100) that must be in the specified state + */ + public static percentage(percentage: number): AtLeastThreshold { + return new AtLeastThresholdPercentage(percentage); + } + + /** + * @internal + */ + public abstract _renderThreshold(alarms: IAlarm[]): string; +} + +class AtLeastThresholdCount extends AtLeastThreshold { + constructor(private readonly count: number) { + super(); + } + + public _renderThreshold(alarms: IAlarm[]): string { + if (this.count < 0 || this.count > alarms.length) { + throw new Error( + `atLeastOptions.threshold count (${this.count}) must be between 0 and ${alarms.length} (number of alarms)`, + ); + } + return this.count.toString(); + } +} + +class AtLeastThresholdPercentage extends AtLeastThreshold { + constructor(private readonly percentage: number) { + super(); + } + + public _renderThreshold(_alarms: IAlarm[]): string { + if (this.percentage < 0 || this.percentage > 100) { + throw new Error( + `atLeastOptions.threshold percentage (${this.percentage}) must be between 0 and 100`, + ); + } + return `${this.percentage}%`; + } +} + +/** + * Configuration for AT_LEAST composite alarm operator. + */ +export interface CompositeAlarmAtLeastOptions { + /** + * Threshold for AT_LEAST operator. + * Use AtLeastThreshold.count() or AtLeastThreshold.percentage(). + */ + readonly threshold: AtLeastThreshold; + + /** + * Alarm state for AT_LEAST operator. + * + * @default - ALARM + */ + readonly state?: AlarmState; } export interface AlarmFactoryDefaults { @@ -868,13 +956,33 @@ export class AlarmFactory { alarms: AlarmWithAnnotation[], props: AddCompositeAlarmProps, ): IAlarmRule { - const alarmRules = alarms.map((alarm) => alarm.alarmRuleWhenAlarming); const operator = props.compositeOperator ?? CompositeAlarmOperator.OR; switch (operator) { - case CompositeAlarmOperator.AND: + case CompositeAlarmOperator.AND: { + const alarmRules = alarms.map((alarm) => alarm.alarmRuleWhenAlarming); return AlarmRule.allOf(...alarmRules); - case CompositeAlarmOperator.OR: + } + case CompositeAlarmOperator.OR: { + const alarmRules = alarms.map((alarm) => alarm.alarmRuleWhenAlarming); return AlarmRule.anyOf(...alarmRules); + } + case CompositeAlarmOperator.AT_LEAST: { + if (!props.atLeastOptions) { + throw new Error( + "atLeastOptions must be specified when using AT_LEAST operator", + ); + } + const threshold = props.atLeastOptions.threshold; + const state = props.atLeastOptions.state ?? AlarmState.ALARM; + const alarmObjects = alarms.map((alarm) => alarm.alarm); + const alarmNames = alarms.map((alarm) => alarm.alarm.alarmName); + const thresholdString = threshold._renderThreshold(alarmObjects); + const stateString = state.toString(); + const alarmList = alarmNames.join(", "); + return AlarmRule.fromString( + `AT_LEAST(${thresholdString}, ${stateString}, (${alarmList}))`, + ); + } default: throw new Error(`Unsupported composite alarm operator: ${operator}`); } diff --git a/test/common/alarm/AlarmFactory.test.ts b/test/common/alarm/AlarmFactory.test.ts index 524cca09..9dd7b7df 100644 --- a/test/common/alarm/AlarmFactory.test.ts +++ b/test/common/alarm/AlarmFactory.test.ts @@ -18,6 +18,7 @@ import { AlarmFactory, AlarmFactoryDefaults, AlarmNamingInput, + AtLeastThreshold, CompositeAlarmOperator, IAlarmActionStrategy, IAlarmNamingStrategy, @@ -596,6 +597,174 @@ test("addCompositeAlarm: snapshot for operator", () => { expect(Template.fromStack(stack)).toMatchSnapshot(); }); +test("addCompositeAlarm: AT_LEAST operator with absolute threshold", () => { + const stack = new Stack(); + const factory = new AlarmFactory(stack, { + globalMetricDefaults, + globalAlarmDefaults: globalAlarmDefaultsWithDisambiguator, + localAlarmNamePrefix: "prefix", + }); + const metric = new Metric({ + namespace: "DummyNamespace", + metricName: "DummyMetric", + }); + const alarm1 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm1", + alarmDescription: "Testing alarm 1", + threshold: 1, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + const alarm2 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm2", + alarmDescription: "Testing alarm 2", + threshold: 2, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + const alarm3 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm3", + alarmDescription: "Testing alarm 3", + threshold: 3, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + factory.addCompositeAlarm([alarm1, alarm2, alarm3], { + disambiguator: "CompositeAtLeast", + alarmNameSuffix: "CompositeAtLeast", + compositeOperator: CompositeAlarmOperator.AT_LEAST, + atLeastOptions: { threshold: AtLeastThreshold.count(2) }, + }); + + expect(Template.fromStack(stack)).toMatchSnapshot(); +}); + +test("addCompositeAlarm: AT_LEAST operator with percentage threshold", () => { + const stack = new Stack(); + const factory = new AlarmFactory(stack, { + globalMetricDefaults, + globalAlarmDefaults: globalAlarmDefaultsWithDisambiguator, + localAlarmNamePrefix: "prefix", + }); + const metric = new Metric({ + namespace: "DummyNamespace", + metricName: "DummyMetric", + }); + const alarm1 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm1", + alarmDescription: "Testing alarm 1", + threshold: 1, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + const alarm2 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm2", + alarmDescription: "Testing alarm 2", + threshold: 2, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + const alarm3 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm3", + alarmDescription: "Testing alarm 3", + threshold: 3, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + const alarm4 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm4", + alarmDescription: "Testing alarm 4", + threshold: 4, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + factory.addCompositeAlarm([alarm1, alarm2, alarm3, alarm4], { + disambiguator: "CompositeAtLeast50Percent", + alarmNameSuffix: "CompositeAtLeast50Percent", + compositeOperator: CompositeAlarmOperator.AT_LEAST, + atLeastOptions: { threshold: AtLeastThreshold.percentage(50) }, + }); + + expect(Template.fromStack(stack)).toMatchSnapshot(); +}); + +test("addCompositeAlarm: AT_LEAST operator throws error when options is missing", () => { + expect(() => { + factory.addCompositeAlarm([], { + disambiguator: "CompositeAtLeastNoOptions", + alarmNameSuffix: "CompositeAtLeastNoOptions", + compositeOperator: CompositeAlarmOperator.AT_LEAST, + }); + }).toThrow("atLeastOptions must be specified when using AT_LEAST operator"); +}); + +test("addCompositeAlarm: AT_LEAST operator throws error when count exceeds alarm count", () => { + const stack = new Stack(); + const factory = new AlarmFactory(stack, { + globalMetricDefaults, + globalAlarmDefaults: globalAlarmDefaultsWithDisambiguator, + localAlarmNamePrefix: "prefix", + }); + const metric = new Metric({ + namespace: "DummyNamespace", + metricName: "DummyMetric", + }); + const alarm1 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm1", + alarmDescription: "Testing alarm 1", + threshold: 1, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + const alarm2 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm2", + alarmDescription: "Testing alarm 2", + threshold: 2, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + expect(() => { + factory.addCompositeAlarm([alarm1, alarm2], { + disambiguator: "CompositeAtLeastTooHigh", + alarmNameSuffix: "CompositeAtLeastTooHigh", + compositeOperator: CompositeAlarmOperator.AT_LEAST, + atLeastOptions: { threshold: AtLeastThreshold.count(5) }, + }); + }).toThrow( + "atLeastOptions.threshold count (5) must be between 0 and 2 (number of alarms)", + ); +}); + +test("addCompositeAlarm: AT_LEAST operator throws error when percentage is out of range", () => { + const stack = new Stack(); + const factory = new AlarmFactory(stack, { + globalMetricDefaults, + globalAlarmDefaults: globalAlarmDefaultsWithDisambiguator, + localAlarmNamePrefix: "prefix", + }); + const metric = new Metric({ + namespace: "DummyNamespace", + metricName: "DummyMetric", + }); + const alarm1 = factory.addAlarm(metric, { + alarmNameSuffix: "Alarm1", + alarmDescription: "Testing alarm 1", + threshold: 1, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + treatMissingData: TreatMissingData.MISSING, + }); + expect(() => { + factory.addCompositeAlarm([alarm1], { + disambiguator: "CompositeAtLeastInvalidPercentage", + alarmNameSuffix: "CompositeAtLeastInvalidPercentage", + compositeOperator: CompositeAlarmOperator.AT_LEAST, + atLeastOptions: { threshold: AtLeastThreshold.percentage(150) }, + }); + }).toThrow( + "atLeastOptions.threshold percentage (150) must be between 0 and 100", + ); +}); + test("addCompositeAlarm: snapshot for suppressor alarm props", () => { const stack = new Stack(); const factory = new AlarmFactory(stack, { diff --git a/test/common/alarm/__snapshots__/AlarmFactory.test.ts.snap b/test/common/alarm/__snapshots__/AlarmFactory.test.ts.snap index 7dbd76be..cafaef2f 100644 --- a/test/common/alarm/__snapshots__/AlarmFactory.test.ts.snap +++ b/test/common/alarm/__snapshots__/AlarmFactory.test.ts.snap @@ -111,6 +111,267 @@ Object { } `; +exports[`addCompositeAlarm: AT_LEAST operator with absolute threshold 1`] = ` +Object { + "Parameters": Object { + "BootstrapVersion": Object { + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", + "Type": "AWS::SSM::Parameter::Value", + }, + }, + "Resources": Object { + "DummyServiceAlarmsprefixAlarm1F4FCF957": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Testing alarm 1", + "AlarmName": "DummyServiceAlarms-prefix-Alarm1", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 6, + "EvaluationPeriods": 6, + "MetricName": "DummyMetric", + "Namespace": "DummyNamespace", + "Period": 300, + "Statistic": "Average", + "Threshold": 1, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "DummyServiceAlarmsprefixAlarm25ADF8B37": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Testing alarm 2", + "AlarmName": "DummyServiceAlarms-prefix-Alarm2", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 6, + "EvaluationPeriods": 6, + "MetricName": "DummyMetric", + "Namespace": "DummyNamespace", + "Period": 300, + "Statistic": "Average", + "Threshold": 2, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "DummyServiceAlarmsprefixAlarm308E4C14F": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Testing alarm 3", + "AlarmName": "DummyServiceAlarms-prefix-Alarm3", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 6, + "EvaluationPeriods": 6, + "MetricName": "DummyMetric", + "Namespace": "DummyNamespace", + "Period": 300, + "Statistic": "Average", + "Threshold": 3, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "DummyServiceAlarmsprefixCompositeAtLeastBE84311C": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Composite alarm", + "AlarmName": "DummyServiceAlarms-prefix-CompositeAtLeast", + "AlarmRule": Object { + "Fn::Join": Array [ + "", + Array [ + "AT_LEAST(2, ALARM, (", + Object { + "Ref": "DummyServiceAlarmsprefixAlarm1F4FCF957", + }, + ", ", + Object { + "Ref": "DummyServiceAlarmsprefixAlarm25ADF8B37", + }, + ", ", + Object { + "Ref": "DummyServiceAlarmsprefixAlarm308E4C14F", + }, + "))", + ], + ], + }, + }, + "Type": "AWS::CloudWatch::CompositeAlarm", + }, + }, + "Rules": Object { + "CheckBootstrapVersion": Object { + "Assertions": Array [ + Object { + "Assert": Object { + "Fn::Not": Array [ + Object { + "Fn::Contains": Array [ + Array [ + "1", + "2", + "3", + "4", + "5", + ], + Object { + "Ref": "BootstrapVersion", + }, + ], + }, + ], + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", + }, + ], + }, + }, +} +`; + +exports[`addCompositeAlarm: AT_LEAST operator with percentage threshold 1`] = ` +Object { + "Parameters": Object { + "BootstrapVersion": Object { + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", + "Type": "AWS::SSM::Parameter::Value", + }, + }, + "Resources": Object { + "DummyServiceAlarmsprefixAlarm1F4FCF957": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Testing alarm 1", + "AlarmName": "DummyServiceAlarms-prefix-Alarm1", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 6, + "EvaluationPeriods": 6, + "MetricName": "DummyMetric", + "Namespace": "DummyNamespace", + "Period": 300, + "Statistic": "Average", + "Threshold": 1, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "DummyServiceAlarmsprefixAlarm25ADF8B37": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Testing alarm 2", + "AlarmName": "DummyServiceAlarms-prefix-Alarm2", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 6, + "EvaluationPeriods": 6, + "MetricName": "DummyMetric", + "Namespace": "DummyNamespace", + "Period": 300, + "Statistic": "Average", + "Threshold": 2, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "DummyServiceAlarmsprefixAlarm308E4C14F": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Testing alarm 3", + "AlarmName": "DummyServiceAlarms-prefix-Alarm3", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 6, + "EvaluationPeriods": 6, + "MetricName": "DummyMetric", + "Namespace": "DummyNamespace", + "Period": 300, + "Statistic": "Average", + "Threshold": 3, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "DummyServiceAlarmsprefixAlarm4782A133C": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Testing alarm 4", + "AlarmName": "DummyServiceAlarms-prefix-Alarm4", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 6, + "EvaluationPeriods": 6, + "MetricName": "DummyMetric", + "Namespace": "DummyNamespace", + "Period": 300, + "Statistic": "Average", + "Threshold": 4, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "DummyServiceAlarmsprefixCompositeAtLeast50Percent54242EDB": Object { + "Properties": Object { + "ActionsEnabled": false, + "AlarmDescription": "Composite alarm", + "AlarmName": "DummyServiceAlarms-prefix-CompositeAtLeast50Percent", + "AlarmRule": Object { + "Fn::Join": Array [ + "", + Array [ + "AT_LEAST(50%, ALARM, (", + Object { + "Ref": "DummyServiceAlarmsprefixAlarm1F4FCF957", + }, + ", ", + Object { + "Ref": "DummyServiceAlarmsprefixAlarm25ADF8B37", + }, + ", ", + Object { + "Ref": "DummyServiceAlarmsprefixAlarm308E4C14F", + }, + ", ", + Object { + "Ref": "DummyServiceAlarmsprefixAlarm4782A133C", + }, + "))", + ], + ], + }, + }, + "Type": "AWS::CloudWatch::CompositeAlarm", + }, + }, + "Rules": Object { + "CheckBootstrapVersion": Object { + "Assertions": Array [ + Object { + "Assert": Object { + "Fn::Not": Array [ + Object { + "Fn::Contains": Array [ + Array [ + "1", + "2", + "3", + "4", + "5", + ], + Object { + "Ref": "BootstrapVersion", + }, + ], + }, + ], + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", + }, + ], + }, + }, +} +`; + exports[`addCompositeAlarm: snapshot for operator 1`] = ` Object { "Parameters": Object {