From 1f8ca082a122af8775f01be2d30c92c3de1344a1 Mon Sep 17 00:00:00 2001 From: Eugene Cheung <81188333+echeung-amzn@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:29:17 -0500 Subject: [PATCH] feat: support Lambda actions (#597) Closes #560 --- _By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_ --- API.md | 51 ++- .../alarm/action/LambdaAlarmActionStrategy.ts | 28 ++ lib/common/alarm/action/index.ts | 1 + .../action/LambdaAlarmActionStrategy.test.ts | 65 +++ .../LambdaAlarmActionStrategy.test.ts.snap | 422 ++++++++++++++++++ 5 files changed, 566 insertions(+), 1 deletion(-) create mode 100644 lib/common/alarm/action/LambdaAlarmActionStrategy.ts create mode 100644 test/common/alarm/action/LambdaAlarmActionStrategy.test.ts create mode 100644 test/common/alarm/action/__snapshots__/LambdaAlarmActionStrategy.test.ts.snap diff --git a/API.md b/API.md index 42307b75..c2b6bc70 100644 --- a/API.md +++ b/API.md @@ -65597,6 +65597,55 @@ public readonly streamUrl: string; --- +### LambdaAlarmActionStrategy + +- *Implements:* IAlarmActionStrategy + +Alarm action strategy that triggers a Lambda function. + +#### Initializers + +```typescript +import { LambdaAlarmActionStrategy } from 'cdk-monitoring-constructs' + +new LambdaAlarmActionStrategy(lambdaFunction: IAlias | IFunction | IVersion) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| lambdaFunction | aws-cdk-lib.aws_lambda.IAlias \| aws-cdk-lib.aws_lambda.IFunction \| aws-cdk-lib.aws_lambda.IVersion | *No description.* | + +--- + +##### `lambdaFunction`Required + +- *Type:* aws-cdk-lib.aws_lambda.IAlias | aws-cdk-lib.aws_lambda.IFunction | aws-cdk-lib.aws_lambda.IVersion + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| addAlarmActions | *No description.* | + +--- + +##### `addAlarmActions` + +```typescript +public addAlarmActions(props: AlarmActionStrategyProps): void +``` + +###### `props`Required + +- *Type:* AlarmActionStrategyProps + +--- + + + + ### LambdaFunctionEnhancedMetricFactory #### Initializers @@ -77818,7 +77867,7 @@ public readonly humanReadableName: string; ### IAlarmActionStrategy -- *Implemented By:* MultipleAlarmActionStrategy, NoopAlarmActionStrategy, OpsItemAlarmActionStrategy, SnsAlarmActionStrategy, IAlarmActionStrategy +- *Implemented By:* LambdaAlarmActionStrategy, MultipleAlarmActionStrategy, NoopAlarmActionStrategy, OpsItemAlarmActionStrategy, SnsAlarmActionStrategy, IAlarmActionStrategy An object that appends actions to alarms. diff --git a/lib/common/alarm/action/LambdaAlarmActionStrategy.ts b/lib/common/alarm/action/LambdaAlarmActionStrategy.ts new file mode 100644 index 00000000..4be0a129 --- /dev/null +++ b/lib/common/alarm/action/LambdaAlarmActionStrategy.ts @@ -0,0 +1,28 @@ +import { LambdaAction } from "aws-cdk-lib/aws-cloudwatch-actions"; +import { IAlias, IFunction, IVersion } from "aws-cdk-lib/aws-lambda"; + +import { + AlarmActionStrategyProps, + IAlarmActionStrategy, +} from "./IAlarmActionStrategy"; + +export function triggerLambda( + lambdaFunction: IAlias | IVersion | IFunction, +): IAlarmActionStrategy { + return new LambdaAlarmActionStrategy(lambdaFunction); +} + +/** + * Alarm action strategy that triggers a Lambda function. + */ +export class LambdaAlarmActionStrategy implements IAlarmActionStrategy { + protected readonly lambdaFunction: IAlias | IVersion | IFunction; + + constructor(lambdaFunction: IAlias | IVersion | IFunction) { + this.lambdaFunction = lambdaFunction; + } + + addAlarmActions(props: AlarmActionStrategyProps): void { + props.alarm.addAlarmAction(new LambdaAction(this.lambdaFunction)); + } +} diff --git a/lib/common/alarm/action/index.ts b/lib/common/alarm/action/index.ts index 7dc6bb1f..9b0e98bb 100644 --- a/lib/common/alarm/action/index.ts +++ b/lib/common/alarm/action/index.ts @@ -1,4 +1,5 @@ export * from "./IAlarmActionStrategy"; +export * from "./LambdaAlarmActionStrategy"; export * from "./MultipleAlarmActionStrategy"; export * from "./NoopAlarmActionStrategy"; export * from "./OpsItemAlarmActionStrategy"; diff --git a/test/common/alarm/action/LambdaAlarmActionStrategy.test.ts b/test/common/alarm/action/LambdaAlarmActionStrategy.test.ts new file mode 100644 index 00000000..53c92060 --- /dev/null +++ b/test/common/alarm/action/LambdaAlarmActionStrategy.test.ts @@ -0,0 +1,65 @@ +import { Stack } from "aws-cdk-lib"; +import { Template } from "aws-cdk-lib/assertions"; +import { Alarm, Metric } from "aws-cdk-lib/aws-cloudwatch"; +import { Function, InlineCode, Runtime } from "aws-cdk-lib/aws-lambda"; + +import { LambdaAlarmActionStrategy } from "../../../../lib"; + +test("snapshot test: Lambda function", () => { + const stack = new Stack(); + const onAlarmFunction = new Function(stack, "alarmLambda", { + functionName: "DummyLambda", + runtime: Runtime.NODEJS_18_X, + code: InlineCode.fromInline("{}"), + handler: "Dummy::handler", + }); + const alarm = new Alarm(stack, "DummyAlarm", { + evaluationPeriods: 1, + threshold: 0, + metric: new Metric({ namespace: "Dummy", metricName: "Dummy" }), + }); + const action = new LambdaAlarmActionStrategy(onAlarmFunction); + action.addAlarmActions({ alarm, action }); + + expect(Template.fromStack(stack)).toMatchSnapshot(); +}); + +test("snapshot test: Lambda alias", () => { + const stack = new Stack(); + const onAlarmFunction = new Function(stack, "alarmLambda", { + functionName: "DummyLambda", + runtime: Runtime.NODEJS_18_X, + code: InlineCode.fromInline("{}"), + handler: "Dummy::handler", + }); + const alias = onAlarmFunction.addAlias("aliasName"); + const alarm = new Alarm(stack, "DummyAlarm", { + evaluationPeriods: 1, + threshold: 0, + metric: new Metric({ namespace: "Dummy", metricName: "Dummy" }), + }); + const action = new LambdaAlarmActionStrategy(alias); + action.addAlarmActions({ alarm, action }); + + expect(Template.fromStack(stack)).toMatchSnapshot(); +}); + +test("snapshot test: Lambda version", () => { + const stack = new Stack(); + const onAlarmFunction = new Function(stack, "alarmLambda", { + functionName: "DummyLambda", + runtime: Runtime.NODEJS_18_X, + code: InlineCode.fromInline("{}"), + handler: "Dummy::handler", + }); + const version = onAlarmFunction.currentVersion; + const alarm = new Alarm(stack, "DummyAlarm", { + evaluationPeriods: 1, + threshold: 0, + metric: new Metric({ namespace: "Dummy", metricName: "Dummy" }), + }); + const action = new LambdaAlarmActionStrategy(version); + action.addAlarmActions({ alarm, action }); + + expect(Template.fromStack(stack)).toMatchSnapshot(); +}); diff --git a/test/common/alarm/action/__snapshots__/LambdaAlarmActionStrategy.test.ts.snap b/test/common/alarm/action/__snapshots__/LambdaAlarmActionStrategy.test.ts.snap new file mode 100644 index 00000000..a2b7bc8e --- /dev/null +++ b/test/common/alarm/action/__snapshots__/LambdaAlarmActionStrategy.test.ts.snap @@ -0,0 +1,422 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`snapshot test: Lambda alias 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 { + "DummyAlarm234203A9": Object { + "Properties": Object { + "AlarmActions": Array [ + Object { + "Ref": "alarmLambdaAliasaliasName41B27313", + }, + ], + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "MetricName": "Dummy", + "Namespace": "Dummy", + "Period": 300, + "Statistic": "Average", + "Threshold": 0, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "alarmLambda131DB691": Object { + "DependsOn": Array [ + "alarmLambdaServiceRoleCDAABB9D", + ], + "Properties": Object { + "Code": Object { + "ZipFile": "{}", + }, + "FunctionName": "DummyLambda", + "Handler": "Dummy::handler", + "Role": Object { + "Fn::GetAtt": Array [ + "alarmLambdaServiceRoleCDAABB9D", + "Arn", + ], + }, + "Runtime": "nodejs18.x", + }, + "Type": "AWS::Lambda::Function", + }, + "alarmLambdaAliasaliasName41B27313": Object { + "Properties": Object { + "FunctionName": Object { + "Ref": "alarmLambda131DB691", + }, + "FunctionVersion": Object { + "Fn::GetAtt": Array [ + "alarmLambdaCurrentVersionBDCE825C8cbdd693b2754df9b113fa5d7cbd1972", + "Version", + ], + }, + "Name": "aliasName", + }, + "Type": "AWS::Lambda::Alias", + }, + "alarmLambdaAliasaliasNameAlarmPermission64A91652": Object { + "Properties": Object { + "Action": "lambda:InvokeFunction", + "FunctionName": Object { + "Ref": "alarmLambdaAliasaliasName41B27313", + }, + "Principal": "lambda.alarms.cloudwatch.amazonaws.com", + "SourceAccount": Object { + "Ref": "AWS::AccountId", + }, + "SourceArn": Object { + "Fn::GetAtt": Array [ + "DummyAlarm234203A9", + "Arn", + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + "alarmLambdaCurrentVersionBDCE825C8cbdd693b2754df9b113fa5d7cbd1972": Object { + "Properties": Object { + "FunctionName": Object { + "Ref": "alarmLambda131DB691", + }, + }, + "Type": "AWS::Lambda::Version", + }, + "alarmLambdaServiceRoleCDAABB9D": Object { + "Properties": Object { + "AssumeRolePolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": Object { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + }, + "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[`snapshot test: Lambda function 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 { + "DummyAlarm234203A9": Object { + "Properties": Object { + "AlarmActions": Array [ + Object { + "Fn::GetAtt": Array [ + "alarmLambda131DB691", + "Arn", + ], + }, + ], + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "MetricName": "Dummy", + "Namespace": "Dummy", + "Period": 300, + "Statistic": "Average", + "Threshold": 0, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "alarmLambda131DB691": Object { + "DependsOn": Array [ + "alarmLambdaServiceRoleCDAABB9D", + ], + "Properties": Object { + "Code": Object { + "ZipFile": "{}", + }, + "FunctionName": "DummyLambda", + "Handler": "Dummy::handler", + "Role": Object { + "Fn::GetAtt": Array [ + "alarmLambdaServiceRoleCDAABB9D", + "Arn", + ], + }, + "Runtime": "nodejs18.x", + }, + "Type": "AWS::Lambda::Function", + }, + "alarmLambdaAlarmPermission43E41C89": Object { + "Properties": Object { + "Action": "lambda:InvokeFunction", + "FunctionName": Object { + "Fn::GetAtt": Array [ + "alarmLambda131DB691", + "Arn", + ], + }, + "Principal": "lambda.alarms.cloudwatch.amazonaws.com", + "SourceAccount": Object { + "Ref": "AWS::AccountId", + }, + "SourceArn": Object { + "Fn::GetAtt": Array [ + "DummyAlarm234203A9", + "Arn", + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + "alarmLambdaServiceRoleCDAABB9D": Object { + "Properties": Object { + "AssumeRolePolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": Object { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + }, + "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[`snapshot test: Lambda version 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 { + "DummyAlarm234203A9": Object { + "Properties": Object { + "AlarmActions": Array [ + Object { + "Ref": "alarmLambdaCurrentVersionBDCE825C8cbdd693b2754df9b113fa5d7cbd1972", + }, + ], + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "MetricName": "Dummy", + "Namespace": "Dummy", + "Period": 300, + "Statistic": "Average", + "Threshold": 0, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "alarmLambda131DB691": Object { + "DependsOn": Array [ + "alarmLambdaServiceRoleCDAABB9D", + ], + "Properties": Object { + "Code": Object { + "ZipFile": "{}", + }, + "FunctionName": "DummyLambda", + "Handler": "Dummy::handler", + "Role": Object { + "Fn::GetAtt": Array [ + "alarmLambdaServiceRoleCDAABB9D", + "Arn", + ], + }, + "Runtime": "nodejs18.x", + }, + "Type": "AWS::Lambda::Function", + }, + "alarmLambdaCurrentVersionAlarmPermissionFEBD056F": Object { + "Properties": Object { + "Action": "lambda:InvokeFunction", + "FunctionName": Object { + "Ref": "alarmLambdaCurrentVersionBDCE825C8cbdd693b2754df9b113fa5d7cbd1972", + }, + "Principal": "lambda.alarms.cloudwatch.amazonaws.com", + "SourceAccount": Object { + "Ref": "AWS::AccountId", + }, + "SourceArn": Object { + "Fn::GetAtt": Array [ + "DummyAlarm234203A9", + "Arn", + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + "alarmLambdaCurrentVersionBDCE825C8cbdd693b2754df9b113fa5d7cbd1972": Object { + "Properties": Object { + "FunctionName": Object { + "Ref": "alarmLambda131DB691", + }, + }, + "Type": "AWS::Lambda::Version", + }, + "alarmLambdaServiceRoleCDAABB9D": Object { + "Properties": Object { + "AssumeRolePolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": Object { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + }, + "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.", + }, + ], + }, + }, +} +`;