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.",
+ },
+ ],
+ },
+ },
+}
+`;