From 2c89a6e1493d33386d5a4487b2ca3ba3dfcb9fab Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Tue, 19 Jan 2021 15:22:38 +0000 Subject: [PATCH 01/13] Add simple pattern for scheduled lambda --- src/constructs/lambda/lambda.ts | 2 +- .../scheduled-lambda.test.ts.snap | 200 ++++++++++++++++++ src/patterns/scheduled-lambda.test.ts | 21 ++ src/patterns/scheduled-lambda.ts | 18 ++ 4 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 src/patterns/__snapshots__/scheduled-lambda.test.ts.snap create mode 100644 src/patterns/scheduled-lambda.test.ts create mode 100644 src/patterns/scheduled-lambda.ts diff --git a/src/constructs/lambda/lambda.ts b/src/constructs/lambda/lambda.ts index ef0f2977a5..8cc3d11ad7 100644 --- a/src/constructs/lambda/lambda.ts +++ b/src/constructs/lambda/lambda.ts @@ -12,7 +12,7 @@ interface ApiProps extends Omit { id: string; } -interface GuFunctionProps extends Omit { +export interface GuFunctionProps extends Omit { code: { bucket: string; key: string }; rules?: Array<{ schedule: Schedule; diff --git a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap new file mode 100644 index 0000000000..9421b0d34c --- /dev/null +++ b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap @@ -0,0 +1,200 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`The GuScheduledLambda pattern should create the correct resources with minimal config 1`] = ` +Object { + "Parameters": Object { + "Stack": Object { + "Default": "deploy", + "Description": "Name of this stack", + "Type": "String", + }, + "Stage": Object { + "AllowedValues": Array [ + "CODE", + "PROD", + ], + "Default": "CODE", + "Description": "Stage name", + "Type": "String", + }, + }, + "Resources": Object { + "mylambdafunction8D341B54": Object { + "DependsOn": Array [ + "mylambdafunctionServiceRoleDefaultPolicy769897D4", + "mylambdafunctionServiceRoleE82C2E25", + ], + "Properties": Object { + "Code": Object { + "S3Bucket": "test-dist", + "S3Key": "lambda.zip", + }, + "FunctionName": "my-lambda-function", + "Handler": "my-lambda/handler", + "Role": Object { + "Fn::GetAtt": Array [ + "mylambdafunctionServiceRoleE82C2E25", + "Arn", + ], + }, + "Runtime": "nodejs12.x", + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::Lambda::Function", + }, + "mylambdafunctionServiceRoleDefaultPolicy769897D4": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::test-dist", + ], + ], + }, + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::test-dist/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "mylambdafunctionServiceRoleDefaultPolicy769897D4", + "Roles": Array [ + Object { + "Ref": "mylambdafunctionServiceRoleE82C2E25", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "mylambdafunctionServiceRoleE82C2E25": 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", + ], + ], + }, + ], + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "mylambdafunctionmylambdafunctionrate1minute06AD0015D": Object { + "Properties": Object { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": Array [ + Object { + "Arn": Object { + "Fn::GetAtt": Array [ + "mylambdafunction8D341B54", + "Arn", + ], + }, + "Id": "Target0", + }, + ], + }, + "Type": "AWS::Events::Rule", + }, + "mylambdafunctionmylambdafunctionrate1minute0AllowEventRuleTestmylambdafunctionmylambdafunctionrate1minute0A609CD2636B92639": Object { + "Properties": Object { + "Action": "lambda:InvokeFunction", + "FunctionName": Object { + "Fn::GetAtt": Array [ + "mylambdafunction8D341B54", + "Arn", + ], + }, + "Principal": "events.amazonaws.com", + "SourceArn": Object { + "Fn::GetAtt": Array [ + "mylambdafunctionmylambdafunctionrate1minute06AD0015D", + "Arn", + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + }, +} +`; diff --git a/src/patterns/scheduled-lambda.test.ts b/src/patterns/scheduled-lambda.test.ts new file mode 100644 index 0000000000..f432114209 --- /dev/null +++ b/src/patterns/scheduled-lambda.test.ts @@ -0,0 +1,21 @@ +import { SynthUtils } from "@aws-cdk/assert"; +import { Schedule } from "@aws-cdk/aws-events"; +import { Runtime } from "@aws-cdk/aws-lambda"; +import { Duration } from "@aws-cdk/core"; +import { simpleGuStackForTesting } from "../../test/utils"; +import { GuScheduledLambda } from "./scheduled-lambda"; + +describe("The GuScheduledLambda pattern", () => { + it("should create the correct resources with minimal config", () => { + const stack = simpleGuStackForTesting(); + const props = { + code: { bucket: "test-dist", key: "lambda.zip" }, + functionName: "my-lambda-function", + handler: "my-lambda/handler", + runtime: Runtime.NODEJS_12_X, + schedule: Schedule.rate(Duration.seconds(60)), + }; + new GuScheduledLambda(stack, "my-lambda-function", props); + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + }); +}); diff --git a/src/patterns/scheduled-lambda.ts b/src/patterns/scheduled-lambda.ts new file mode 100644 index 0000000000..ad9babbc72 --- /dev/null +++ b/src/patterns/scheduled-lambda.ts @@ -0,0 +1,18 @@ +import type { Schedule } from "@aws-cdk/aws-events"; +import type { GuStack } from "../constructs/core"; +import { GuLambdaFunction } from "../constructs/lambda"; +import type { GuFunctionProps } from "../constructs/lambda"; + +interface GuScheduledLambdaProps extends Omit { + schedule: Schedule; +} + +export class GuScheduledLambda extends GuLambdaFunction { + constructor(scope: GuStack, id: string, props: GuScheduledLambdaProps) { + const lambdaProps: GuFunctionProps = { + ...props, + rules: [{ schedule: props.schedule }], + }; + super(scope, id, lambdaProps); + } +} From 489bbffdaace2113b1497680f198aba7051ef38c Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Wed, 20 Jan 2021 12:41:48 +0000 Subject: [PATCH 02/13] Add construct for CloudWatch Alarms --- package-lock.json | 14 ++++++++ package.json | 1 + src/constructs/cloudwatch/alarm.test.ts | 48 +++++++++++++++++++++++++ src/constructs/cloudwatch/alarm.ts | 18 ++++++++++ 4 files changed, 81 insertions(+) create mode 100644 src/constructs/cloudwatch/alarm.test.ts create mode 100644 src/constructs/cloudwatch/alarm.ts diff --git a/package-lock.json b/package-lock.json index d9be519d6b..9b4ea90481 100644 --- a/package-lock.json +++ b/package-lock.json @@ -176,6 +176,20 @@ "constructs": "^3.2.0" } }, + "@aws-cdk/aws-cloudwatch-actions": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudwatch-actions/-/aws-cloudwatch-actions-1.74.0.tgz", + "integrity": "sha512-N90JLaj3jDNr5hX0g+Wj/+4wtJGPJz5h94N2DYQd0Ho7NU+p4GIP8BESJ4yWxYOKtO6VcH59Fz3s6r7kwvEOcg==", + "requires": { + "@aws-cdk/aws-applicationautoscaling": "1.74.0", + "@aws-cdk/aws-autoscaling": "1.74.0", + "@aws-cdk/aws-cloudwatch": "1.74.0", + "@aws-cdk/aws-iam": "1.74.0", + "@aws-cdk/aws-sns": "1.74.0", + "@aws-cdk/core": "1.74.0", + "constructs": "^3.2.0" + } + }, "@aws-cdk/aws-codebuild": { "version": "1.74.0", "resolved": "https://registry.npmjs.org/@aws-cdk/aws-codebuild/-/aws-codebuild-1.74.0.tgz", diff --git a/package.json b/package.json index 4af6cd91e2..5c672cca4f 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "dependencies": { "@aws-cdk/assert": "~1.74.0", "@aws-cdk/aws-autoscaling": "~1.74.0", + "@aws-cdk/aws-cloudwatch-actions": "~1.74.0", "@aws-cdk/aws-ec2": "~1.74.0", "@aws-cdk/aws-apigateway": "~1.74.0", "@aws-cdk/aws-elasticloadbalancing": "~1.74.0", diff --git a/src/constructs/cloudwatch/alarm.test.ts b/src/constructs/cloudwatch/alarm.test.ts new file mode 100644 index 0000000000..b3ac6d45f8 --- /dev/null +++ b/src/constructs/cloudwatch/alarm.test.ts @@ -0,0 +1,48 @@ +import "@aws-cdk/assert/jest"; +import { ComparisonOperator } from "@aws-cdk/aws-cloudwatch"; +import { Runtime } from "@aws-cdk/aws-lambda"; +import { simpleGuStackForTesting } from "../../../test/utils"; +import { GuLambdaFunction } from "../lambda"; +import { GuAlarm } from "./alarm"; + +describe("The GuAlarm class", () => { + it("should create a CloudWatch alarm", () => { + const stack = simpleGuStackForTesting(); + const lambda = new GuLambdaFunction(stack, "lambda", { + code: { bucket: "bucket1", key: "folder/to/key" }, + handler: "handler.ts", + runtime: Runtime.NODEJS_12_X, + }); + new GuAlarm(stack, "alarm", { + alarmName: `Alarm in ${stack.stage}`, + alarmDescription: "It's broken", + metric: lambda.metricErrors(), + comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, + threshold: 1, + evaluationPeriods: 1, + snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + }); + expect(stack).toHaveResource("AWS::CloudWatch::Alarm"); + }); + + it("should send alerts to the provided SNS Topic", () => { + const stack = simpleGuStackForTesting(); + const lambda = new GuLambdaFunction(stack, "lambda", { + code: { bucket: "bucket1", key: "folder/to/key" }, + handler: "handler.ts", + runtime: Runtime.NODEJS_12_X, + }); + new GuAlarm(stack, "alarm", { + alarmName: `Alarm in ${stack.stage}`, + alarmDescription: "It's broken", + metric: lambda.metricErrors(), + comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, + threshold: 1, + evaluationPeriods: 1, + snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + }); + expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { + AlarmActions: ["arn:aws:sns:eu-west-1:123456789012:alerts-topic"], + }); + }); +}); diff --git a/src/constructs/cloudwatch/alarm.ts b/src/constructs/cloudwatch/alarm.ts new file mode 100644 index 0000000000..f6b8ceb7b8 --- /dev/null +++ b/src/constructs/cloudwatch/alarm.ts @@ -0,0 +1,18 @@ +import type { AlarmProps } from "@aws-cdk/aws-cloudwatch"; +import { Alarm } from "@aws-cdk/aws-cloudwatch"; +import { SnsAction } from "@aws-cdk/aws-cloudwatch-actions"; +import type { ITopic } from "@aws-cdk/aws-sns"; +import { Topic } from "@aws-cdk/aws-sns"; +import type { GuStack } from "../core"; + +interface GuAlarmProps extends AlarmProps { + snsTopicArn: string; +} + +export class GuAlarm extends Alarm { + constructor(scope: GuStack, id: string, props: GuAlarmProps) { + super(scope, id, props); + const snsTopic: ITopic = Topic.fromTopicArn(scope, "sns-topic-for-alarm-notifications", props.snsTopicArn); + this.addAlarmAction(new SnsAction(snsTopic)); + } +} From 3d6c3bea1a82bf9735c079ad3cb6549250276186 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Wed, 20 Jan 2021 15:25:47 +0000 Subject: [PATCH 03/13] Add optional monitoring for scheduled lambdas --- src/constructs/cloudwatch/alarm.ts | 2 +- .../__snapshots__/lambda-alarms.test.ts.snap | 437 ++++++++++++++++++ .../scheduled-lambda.test.ts.snap | 256 ++++++++++ src/patterns/lambda-alarms.test.ts | 44 ++ src/patterns/lambda-alarms.ts | 32 ++ src/patterns/scheduled-lambda.test.ts | 19 + src/patterns/scheduled-lambda.ts | 9 + 7 files changed, 798 insertions(+), 1 deletion(-) create mode 100644 src/patterns/__snapshots__/lambda-alarms.test.ts.snap create mode 100644 src/patterns/lambda-alarms.test.ts create mode 100644 src/patterns/lambda-alarms.ts diff --git a/src/constructs/cloudwatch/alarm.ts b/src/constructs/cloudwatch/alarm.ts index f6b8ceb7b8..0b54e7454a 100644 --- a/src/constructs/cloudwatch/alarm.ts +++ b/src/constructs/cloudwatch/alarm.ts @@ -5,7 +5,7 @@ import type { ITopic } from "@aws-cdk/aws-sns"; import { Topic } from "@aws-cdk/aws-sns"; import type { GuStack } from "../core"; -interface GuAlarmProps extends AlarmProps { +export interface GuAlarmProps extends AlarmProps { snsTopicArn: string; } diff --git a/src/patterns/__snapshots__/lambda-alarms.test.ts.snap b/src/patterns/__snapshots__/lambda-alarms.test.ts.snap new file mode 100644 index 0000000000..cde29e70eb --- /dev/null +++ b/src/patterns/__snapshots__/lambda-alarms.test.ts.snap @@ -0,0 +1,437 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`The GuLambdaErrorPercentageAlarm pattern should adjust the number of evaluation periods if a custom value is provided 1`] = ` +Object { + "Parameters": Object { + "Stack": Object { + "Default": "deploy", + "Description": "Name of this stack", + "Type": "String", + }, + "Stage": Object { + "AllowedValues": Array [ + "CODE", + "PROD", + ], + "Default": "CODE", + "Description": "Stage name", + "Type": "String", + }, + }, + "Resources": Object { + "lambda8B5974B5": Object { + "DependsOn": Array [ + "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "lambdaServiceRole494E4CA6", + ], + "Properties": Object { + "Code": Object { + "S3Bucket": "bucket1", + "S3Key": "folder/to/key", + }, + "Handler": "handler.ts", + "Role": Object { + "Fn::GetAtt": Array [ + "lambdaServiceRole494E4CA6", + "Arn", + ], + }, + "Runtime": "nodejs12.x", + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::Lambda::Function", + }, + "lambdaServiceRole494E4CA6": 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", + ], + ], + }, + ], + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "lambdaServiceRoleDefaultPolicyBF6FA5E7": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1", + ], + ], + }, + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "Roles": Array [ + Object { + "Ref": "lambdaServiceRole494E4CA6", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "mylambdafunction8D341B54": Object { + "Properties": Object { + "AlarmActions": Array [ + "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + ], + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 12, + "Metrics": Array [ + Object { + "Expression": "100*m1/m2", + "Id": "expr_1", + }, + Object { + "Id": "m1", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Errors", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + Object { + "Id": "m2", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Invocations", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + ], + "Threshold": 65, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + }, +} +`; + +exports[`The GuLambdaErrorPercentageAlarm pattern should create the correct alarm resource with minimal config 1`] = ` +Object { + "Parameters": Object { + "Stack": Object { + "Default": "deploy", + "Description": "Name of this stack", + "Type": "String", + }, + "Stage": Object { + "AllowedValues": Array [ + "CODE", + "PROD", + ], + "Default": "CODE", + "Description": "Stage name", + "Type": "String", + }, + }, + "Resources": Object { + "lambda8B5974B5": Object { + "DependsOn": Array [ + "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "lambdaServiceRole494E4CA6", + ], + "Properties": Object { + "Code": Object { + "S3Bucket": "bucket1", + "S3Key": "folder/to/key", + }, + "Handler": "handler.ts", + "Role": Object { + "Fn::GetAtt": Array [ + "lambdaServiceRole494E4CA6", + "Arn", + ], + }, + "Runtime": "nodejs12.x", + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::Lambda::Function", + }, + "lambdaServiceRole494E4CA6": 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", + ], + ], + }, + ], + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "lambdaServiceRoleDefaultPolicyBF6FA5E7": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1", + ], + ], + }, + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "Roles": Array [ + Object { + "Ref": "lambdaServiceRole494E4CA6", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "mylambdafunction8D341B54": Object { + "Properties": Object { + "AlarmActions": Array [ + "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + ], + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "Metrics": Array [ + Object { + "Expression": "100*m1/m2", + "Id": "expr_1", + }, + Object { + "Id": "m1", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Errors", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + Object { + "Id": "m2", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Invocations", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + ], + "Threshold": 80, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + }, +} +`; diff --git a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap index 9421b0d34c..f0d9bb9216 100644 --- a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap +++ b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap @@ -1,5 +1,261 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`The GuScheduledLambda pattern should create an alarm if monitoring configuration is provided 1`] = ` +Object { + "Parameters": Object { + "Stack": Object { + "Default": "deploy", + "Description": "Name of this stack", + "Type": "String", + }, + "Stage": Object { + "AllowedValues": Array [ + "CODE", + "PROD", + ], + "Default": "CODE", + "Description": "Stage name", + "Type": "String", + }, + }, + "Resources": Object { + "errorpercentagealarmforscheduledlambdaF6DA7824": Object { + "Properties": Object { + "AlarmActions": Array [ + "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + ], + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "Metrics": Array [ + Object { + "Expression": "100*m1/m2", + "Id": "expr_1", + }, + Object { + "Id": "m1", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "mylambdafunction8D341B54", + }, + }, + ], + "MetricName": "Errors", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + Object { + "Id": "m2", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "mylambdafunction8D341B54", + }, + }, + ], + "MetricName": "Invocations", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + ], + "Threshold": 99, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "mylambdafunction8D341B54": Object { + "DependsOn": Array [ + "mylambdafunctionServiceRoleDefaultPolicy769897D4", + "mylambdafunctionServiceRoleE82C2E25", + ], + "Properties": Object { + "Code": Object { + "S3Bucket": "test-dist", + "S3Key": "lambda.zip", + }, + "FunctionName": "my-lambda-function", + "Handler": "my-lambda/handler", + "Role": Object { + "Fn::GetAtt": Array [ + "mylambdafunctionServiceRoleE82C2E25", + "Arn", + ], + }, + "Runtime": "nodejs12.x", + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::Lambda::Function", + }, + "mylambdafunctionServiceRoleDefaultPolicy769897D4": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::test-dist", + ], + ], + }, + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::test-dist/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "mylambdafunctionServiceRoleDefaultPolicy769897D4", + "Roles": Array [ + Object { + "Ref": "mylambdafunctionServiceRoleE82C2E25", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "mylambdafunctionServiceRoleE82C2E25": 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", + ], + ], + }, + ], + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "mylambdafunctionmylambdafunctionrate1minute06AD0015D": Object { + "Properties": Object { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": Array [ + Object { + "Arn": Object { + "Fn::GetAtt": Array [ + "mylambdafunction8D341B54", + "Arn", + ], + }, + "Id": "Target0", + }, + ], + }, + "Type": "AWS::Events::Rule", + }, + "mylambdafunctionmylambdafunctionrate1minute0AllowEventRuleTestmylambdafunctionmylambdafunctionrate1minute0A609CD2636B92639": Object { + "Properties": Object { + "Action": "lambda:InvokeFunction", + "FunctionName": Object { + "Fn::GetAtt": Array [ + "mylambdafunction8D341B54", + "Arn", + ], + }, + "Principal": "events.amazonaws.com", + "SourceArn": Object { + "Fn::GetAtt": Array [ + "mylambdafunctionmylambdafunctionrate1minute06AD0015D", + "Arn", + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + }, +} +`; + exports[`The GuScheduledLambda pattern should create the correct resources with minimal config 1`] = ` Object { "Parameters": Object { diff --git a/src/patterns/lambda-alarms.test.ts b/src/patterns/lambda-alarms.test.ts new file mode 100644 index 0000000000..e0b91aaa99 --- /dev/null +++ b/src/patterns/lambda-alarms.test.ts @@ -0,0 +1,44 @@ +import "@aws-cdk/assert/jest"; +import { SynthUtils } from "@aws-cdk/assert"; +import { Runtime } from "@aws-cdk/aws-lambda"; +import { simpleGuStackForTesting } from "../../test/utils"; +import { GuLambdaFunction } from "../constructs/lambda"; +import { GuLambdaErrorPercentageAlarm } from "./lambda-alarms"; + +describe("The GuLambdaErrorPercentageAlarm pattern", () => { + it("should create the correct alarm resource with minimal config", () => { + const stack = simpleGuStackForTesting(); + const lambda = new GuLambdaFunction(stack, "lambda", { + code: { bucket: "bucket1", key: "folder/to/key" }, + handler: "handler.ts", + runtime: Runtime.NODEJS_12_X, + }); + const props = { + toleratedErrorPercentage: 80, + snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + lambda: lambda, + }; + new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + }); + + it("should adjust the number of evaluation periods if a custom value is provided", () => { + const stack = simpleGuStackForTesting(); + const lambda = new GuLambdaFunction(stack, "lambda", { + code: { bucket: "bucket1", key: "folder/to/key" }, + handler: "handler.ts", + runtime: Runtime.NODEJS_12_X, + }); + const props = { + toleratedErrorPercentage: 65, + numberOfFiveMinutePeriodsToEvaluate: 12, + snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + lambda: lambda, + }; + new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { + EvaluationPeriods: 12, + }); + }); +}); diff --git a/src/patterns/lambda-alarms.ts b/src/patterns/lambda-alarms.ts new file mode 100644 index 0000000000..990d4a3fcd --- /dev/null +++ b/src/patterns/lambda-alarms.ts @@ -0,0 +1,32 @@ +import { ComparisonOperator, MathExpression } from "@aws-cdk/aws-cloudwatch"; +import type { GuAlarmProps } from "../constructs/cloudwatch/alarm"; +import { GuAlarm } from "../constructs/cloudwatch/alarm"; +import type { GuStack } from "../constructs/core"; +import type { GuLambdaFunction } from "../constructs/lambda"; + +export interface ErrorPercentageMonitoring + extends Omit { + toleratedErrorPercentage: number; + numberOfFiveMinutePeriodsToEvaluate?: number; +} + +interface GuLambdaAlarmProps extends ErrorPercentageMonitoring { + lambda: GuLambdaFunction; +} + +export class GuLambdaErrorPercentageAlarm extends GuAlarm { + constructor(scope: GuStack, id: string, props: GuLambdaAlarmProps) { + const mathExpression = new MathExpression({ + expression: "100*m1/m2", + usingMetrics: { m1: props.lambda.metricErrors(), m2: props.lambda.metricInvocations() }, + }); + const alarmProps = { + ...props, + metric: mathExpression, + threshold: props.toleratedErrorPercentage, + comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, + evaluationPeriods: props.numberOfFiveMinutePeriodsToEvaluate ? props.numberOfFiveMinutePeriodsToEvaluate : 1, + }; + super(scope, id, alarmProps); + } +} diff --git a/src/patterns/scheduled-lambda.test.ts b/src/patterns/scheduled-lambda.test.ts index f432114209..56d6e99c59 100644 --- a/src/patterns/scheduled-lambda.test.ts +++ b/src/patterns/scheduled-lambda.test.ts @@ -1,3 +1,4 @@ +import "@aws-cdk/assert/jest"; import { SynthUtils } from "@aws-cdk/assert"; import { Schedule } from "@aws-cdk/aws-events"; import { Runtime } from "@aws-cdk/aws-lambda"; @@ -18,4 +19,22 @@ describe("The GuScheduledLambda pattern", () => { new GuScheduledLambda(stack, "my-lambda-function", props); expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); }); + + it("should create an alarm if monitoring configuration is provided", () => { + const stack = simpleGuStackForTesting(); + const props = { + code: { bucket: "test-dist", key: "lambda.zip" }, + functionName: "my-lambda-function", + handler: "my-lambda/handler", + runtime: Runtime.NODEJS_12_X, + schedule: Schedule.rate(Duration.seconds(60)), + monitoringConfiguration: { + toleratedErrorPercentage: 99, + snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + }, + }; + new GuScheduledLambda(stack, "my-lambda-function", props); + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + expect(stack).toHaveResource("AWS::CloudWatch::Alarm"); + }); }); diff --git a/src/patterns/scheduled-lambda.ts b/src/patterns/scheduled-lambda.ts index ad9babbc72..9deb9fdd2f 100644 --- a/src/patterns/scheduled-lambda.ts +++ b/src/patterns/scheduled-lambda.ts @@ -2,9 +2,12 @@ import type { Schedule } from "@aws-cdk/aws-events"; import type { GuStack } from "../constructs/core"; import { GuLambdaFunction } from "../constructs/lambda"; import type { GuFunctionProps } from "../constructs/lambda"; +import type { ErrorPercentageMonitoring } from "./lambda-alarms"; +import { GuLambdaErrorPercentageAlarm } from "./lambda-alarms"; interface GuScheduledLambdaProps extends Omit { schedule: Schedule; + monitoringConfiguration?: ErrorPercentageMonitoring; } export class GuScheduledLambda extends GuLambdaFunction { @@ -14,5 +17,11 @@ export class GuScheduledLambda extends GuLambdaFunction { rules: [{ schedule: props.schedule }], }; super(scope, id, lambdaProps); + if (props.monitoringConfiguration) { + new GuLambdaErrorPercentageAlarm(scope, "error-percentage-alarm-for-scheduled-lambda", { + ...props.monitoringConfiguration, + lambda: this, + }); + } } } From 69769c806c55e7a721d6d0059e3b0431dcde5b04 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Thu, 21 Jan 2021 16:24:27 +0000 Subject: [PATCH 04/13] Move lambda-alarms from patterns into constructs --- .../cloudwatch}/__snapshots__/lambda-alarms.test.ts.snap | 0 .../cloudwatch}/lambda-alarms.test.ts | 4 ++-- src/{patterns => constructs/cloudwatch}/lambda-alarms.ts | 8 ++++---- src/patterns/scheduled-lambda.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename src/{patterns => constructs/cloudwatch}/__snapshots__/lambda-alarms.test.ts.snap (100%) rename src/{patterns => constructs/cloudwatch}/lambda-alarms.test.ts (93%) rename src/{patterns => constructs/cloudwatch}/lambda-alarms.ts (81%) diff --git a/src/patterns/__snapshots__/lambda-alarms.test.ts.snap b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap similarity index 100% rename from src/patterns/__snapshots__/lambda-alarms.test.ts.snap rename to src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap diff --git a/src/patterns/lambda-alarms.test.ts b/src/constructs/cloudwatch/lambda-alarms.test.ts similarity index 93% rename from src/patterns/lambda-alarms.test.ts rename to src/constructs/cloudwatch/lambda-alarms.test.ts index e0b91aaa99..719bfb6b3d 100644 --- a/src/patterns/lambda-alarms.test.ts +++ b/src/constructs/cloudwatch/lambda-alarms.test.ts @@ -1,8 +1,8 @@ import "@aws-cdk/assert/jest"; import { SynthUtils } from "@aws-cdk/assert"; import { Runtime } from "@aws-cdk/aws-lambda"; -import { simpleGuStackForTesting } from "../../test/utils"; -import { GuLambdaFunction } from "../constructs/lambda"; +import { simpleGuStackForTesting } from "../../../test/utils"; +import { GuLambdaFunction } from "../lambda"; import { GuLambdaErrorPercentageAlarm } from "./lambda-alarms"; describe("The GuLambdaErrorPercentageAlarm pattern", () => { diff --git a/src/patterns/lambda-alarms.ts b/src/constructs/cloudwatch/lambda-alarms.ts similarity index 81% rename from src/patterns/lambda-alarms.ts rename to src/constructs/cloudwatch/lambda-alarms.ts index 990d4a3fcd..22c7312a22 100644 --- a/src/patterns/lambda-alarms.ts +++ b/src/constructs/cloudwatch/lambda-alarms.ts @@ -1,8 +1,8 @@ import { ComparisonOperator, MathExpression } from "@aws-cdk/aws-cloudwatch"; -import type { GuAlarmProps } from "../constructs/cloudwatch/alarm"; -import { GuAlarm } from "../constructs/cloudwatch/alarm"; -import type { GuStack } from "../constructs/core"; -import type { GuLambdaFunction } from "../constructs/lambda"; +import type { GuStack } from "../core"; +import type { GuLambdaFunction } from "../lambda"; +import type { GuAlarmProps } from "./alarm"; +import { GuAlarm } from "./alarm"; export interface ErrorPercentageMonitoring extends Omit { diff --git a/src/patterns/scheduled-lambda.ts b/src/patterns/scheduled-lambda.ts index 9deb9fdd2f..0af9da1cc3 100644 --- a/src/patterns/scheduled-lambda.ts +++ b/src/patterns/scheduled-lambda.ts @@ -1,9 +1,9 @@ import type { Schedule } from "@aws-cdk/aws-events"; +import type { ErrorPercentageMonitoring } from "../constructs/cloudwatch/lambda-alarms"; +import { GuLambdaErrorPercentageAlarm } from "../constructs/cloudwatch/lambda-alarms"; import type { GuStack } from "../constructs/core"; import { GuLambdaFunction } from "../constructs/lambda"; import type { GuFunctionProps } from "../constructs/lambda"; -import type { ErrorPercentageMonitoring } from "./lambda-alarms"; -import { GuLambdaErrorPercentageAlarm } from "./lambda-alarms"; interface GuScheduledLambdaProps extends Omit { schedule: Schedule; From c11f6074727f85531165c76489bb9db1d40b73d2 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Fri, 22 Jan 2021 10:13:51 +0000 Subject: [PATCH 05/13] Change snsTopicArn to snsTopicName --- .../__snapshots__/lambda-alarms.test.ts.snap | 34 +++++++++++++++++-- src/constructs/cloudwatch/alarm.test.ts | 23 +++++++++++-- src/constructs/cloudwatch/alarm.ts | 5 +-- .../cloudwatch/lambda-alarms.test.ts | 4 +-- .../scheduled-lambda.test.ts.snap | 17 +++++++++- src/patterns/scheduled-lambda.test.ts | 2 +- 6 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap index cde29e70eb..9ba27307b4 100644 --- a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap +++ b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap @@ -160,7 +160,22 @@ Object { "mylambdafunction8D341B54": Object { "Properties": Object { "AlarmActions": Array [ - "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:aws:sns:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alerts-topic", + ], + ], + }, ], "ComparisonOperator": "GreaterThanThreshold", "EvaluationPeriods": 12, @@ -378,7 +393,22 @@ Object { "mylambdafunction8D341B54": Object { "Properties": Object { "AlarmActions": Array [ - "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:aws:sns:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alerts-topic", + ], + ], + }, ], "ComparisonOperator": "GreaterThanThreshold", "EvaluationPeriods": 1, diff --git a/src/constructs/cloudwatch/alarm.test.ts b/src/constructs/cloudwatch/alarm.test.ts index b3ac6d45f8..f25e01d192 100644 --- a/src/constructs/cloudwatch/alarm.test.ts +++ b/src/constructs/cloudwatch/alarm.test.ts @@ -20,7 +20,7 @@ describe("The GuAlarm class", () => { comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, threshold: 1, evaluationPeriods: 1, - snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + snsTopicName: "alerts-topic", }); expect(stack).toHaveResource("AWS::CloudWatch::Alarm"); }); @@ -39,10 +39,27 @@ describe("The GuAlarm class", () => { comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, threshold: 1, evaluationPeriods: 1, - snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + snsTopicName: "alerts-topic", }); expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { - AlarmActions: ["arn:aws:sns:eu-west-1:123456789012:alerts-topic"], + AlarmActions: [ + { + "Fn::Join": [ + "", + [ + "arn:aws:sns:", + { + Ref: "AWS::Region", + }, + ":", + { + Ref: "AWS::AccountId", + }, + ":alerts-topic", + ], + ], + }, + ], }); }); }); diff --git a/src/constructs/cloudwatch/alarm.ts b/src/constructs/cloudwatch/alarm.ts index 0b54e7454a..b0dcdf41c0 100644 --- a/src/constructs/cloudwatch/alarm.ts +++ b/src/constructs/cloudwatch/alarm.ts @@ -6,13 +6,14 @@ import { Topic } from "@aws-cdk/aws-sns"; import type { GuStack } from "../core"; export interface GuAlarmProps extends AlarmProps { - snsTopicArn: string; + snsTopicName: string; } export class GuAlarm extends Alarm { constructor(scope: GuStack, id: string, props: GuAlarmProps) { super(scope, id, props); - const snsTopic: ITopic = Topic.fromTopicArn(scope, "sns-topic-for-alarm-notifications", props.snsTopicArn); + const topicArn: string = `arn:aws:sns:${scope.region}:${scope.account}:${props.snsTopicName}`; + const snsTopic: ITopic = Topic.fromTopicArn(scope, "sns-topic-for-alarm-notifications", topicArn); this.addAlarmAction(new SnsAction(snsTopic)); } } diff --git a/src/constructs/cloudwatch/lambda-alarms.test.ts b/src/constructs/cloudwatch/lambda-alarms.test.ts index 719bfb6b3d..24c85bae30 100644 --- a/src/constructs/cloudwatch/lambda-alarms.test.ts +++ b/src/constructs/cloudwatch/lambda-alarms.test.ts @@ -15,7 +15,7 @@ describe("The GuLambdaErrorPercentageAlarm pattern", () => { }); const props = { toleratedErrorPercentage: 80, - snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + snsTopicName: "alerts-topic", lambda: lambda, }; new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); @@ -32,7 +32,7 @@ describe("The GuLambdaErrorPercentageAlarm pattern", () => { const props = { toleratedErrorPercentage: 65, numberOfFiveMinutePeriodsToEvaluate: 12, - snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + snsTopicName: "alerts-topic", lambda: lambda, }; new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); diff --git a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap index f0d9bb9216..118ecc1a4a 100644 --- a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap +++ b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap @@ -22,7 +22,22 @@ Object { "errorpercentagealarmforscheduledlambdaF6DA7824": Object { "Properties": Object { "AlarmActions": Array [ - "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:aws:sns:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alerts-topic", + ], + ], + }, ], "ComparisonOperator": "GreaterThanThreshold", "EvaluationPeriods": 1, diff --git a/src/patterns/scheduled-lambda.test.ts b/src/patterns/scheduled-lambda.test.ts index 56d6e99c59..9020e3332d 100644 --- a/src/patterns/scheduled-lambda.test.ts +++ b/src/patterns/scheduled-lambda.test.ts @@ -30,7 +30,7 @@ describe("The GuScheduledLambda pattern", () => { schedule: Schedule.rate(Duration.seconds(60)), monitoringConfiguration: { toleratedErrorPercentage: 99, - snsTopicArn: "arn:aws:sns:eu-west-1:123456789012:alerts-topic", + snsTopicName: "alerts-topic", }, }; new GuScheduledLambda(stack, "my-lambda-function", props); From c2f4d5888d791466071c758a28be100385007de5 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Fri, 22 Jan 2021 10:32:01 +0000 Subject: [PATCH 06/13] Update test snapshots --- .../cloudwatch/__snapshots__/lambda-alarms.test.ts.snap | 4 ++++ src/patterns/__snapshots__/scheduled-lambda.test.ts.snap | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap index 9ba27307b4..ea8d938189 100644 --- a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap +++ b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap @@ -30,6 +30,7 @@ Object { "S3Key": "folder/to/key", }, "Handler": "handler.ts", + "MemorySize": 512, "Role": Object { "Fn::GetAtt": Array [ "lambdaServiceRole494E4CA6", @@ -55,6 +56,7 @@ Object { }, }, ], + "Timeout": 30, }, "Type": "AWS::Lambda::Function", }, @@ -263,6 +265,7 @@ Object { "S3Key": "folder/to/key", }, "Handler": "handler.ts", + "MemorySize": 512, "Role": Object { "Fn::GetAtt": Array [ "lambdaServiceRole494E4CA6", @@ -288,6 +291,7 @@ Object { }, }, ], + "Timeout": 30, }, "Type": "AWS::Lambda::Function", }, diff --git a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap index 118ecc1a4a..3b9e16c2a8 100644 --- a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap +++ b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap @@ -103,6 +103,7 @@ Object { }, "FunctionName": "my-lambda-function", "Handler": "my-lambda/handler", + "MemorySize": 512, "Role": Object { "Fn::GetAtt": Array [ "mylambdafunctionServiceRoleE82C2E25", @@ -128,6 +129,7 @@ Object { }, }, ], + "Timeout": 30, }, "Type": "AWS::Lambda::Function", }, @@ -302,6 +304,7 @@ Object { }, "FunctionName": "my-lambda-function", "Handler": "my-lambda/handler", + "MemorySize": 512, "Role": Object { "Fn::GetAtt": Array [ "mylambdafunctionServiceRoleE82C2E25", @@ -327,6 +330,7 @@ Object { }, }, ], + "Timeout": 30, }, "Type": "AWS::Lambda::Function", }, From dc3972c3d7f68386314c7e1644137cbc5e5f91de Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Fri, 22 Jan 2021 15:25:39 +0000 Subject: [PATCH 07/13] Make configuring alarms opt-out rather than opt-in --- src/constructs/cloudwatch/lambda-alarms.ts | 7 +++++++ src/patterns/scheduled-lambda.test.ts | 3 +++ src/patterns/scheduled-lambda.ts | 10 ++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/constructs/cloudwatch/lambda-alarms.ts b/src/constructs/cloudwatch/lambda-alarms.ts index 22c7312a22..d6511e697a 100644 --- a/src/constructs/cloudwatch/lambda-alarms.ts +++ b/src/constructs/cloudwatch/lambda-alarms.ts @@ -4,10 +4,17 @@ import type { GuLambdaFunction } from "../lambda"; import type { GuAlarmProps } from "./alarm"; import { GuAlarm } from "./alarm"; +export type LambdaMonitoring = NoMonitoring | ErrorPercentageMonitoring; + +export interface NoMonitoring { + noMonitoring: true; +} + export interface ErrorPercentageMonitoring extends Omit { toleratedErrorPercentage: number; numberOfFiveMinutePeriodsToEvaluate?: number; + noMonitoring?: false; } interface GuLambdaAlarmProps extends ErrorPercentageMonitoring { diff --git a/src/patterns/scheduled-lambda.test.ts b/src/patterns/scheduled-lambda.test.ts index 9020e3332d..fa780f18dc 100644 --- a/src/patterns/scheduled-lambda.test.ts +++ b/src/patterns/scheduled-lambda.test.ts @@ -4,17 +4,20 @@ import { Schedule } from "@aws-cdk/aws-events"; import { Runtime } from "@aws-cdk/aws-lambda"; import { Duration } from "@aws-cdk/core"; import { simpleGuStackForTesting } from "../../test/utils"; +import type { NoMonitoring } from "../constructs/cloudwatch/lambda-alarms"; import { GuScheduledLambda } from "./scheduled-lambda"; describe("The GuScheduledLambda pattern", () => { it("should create the correct resources with minimal config", () => { const stack = simpleGuStackForTesting(); + const noMonitoring: NoMonitoring = { noMonitoring: true }; const props = { code: { bucket: "test-dist", key: "lambda.zip" }, functionName: "my-lambda-function", handler: "my-lambda/handler", runtime: Runtime.NODEJS_12_X, schedule: Schedule.rate(Duration.seconds(60)), + monitoringConfiguration: noMonitoring, }; new GuScheduledLambda(stack, "my-lambda-function", props); expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); diff --git a/src/patterns/scheduled-lambda.ts b/src/patterns/scheduled-lambda.ts index 0af9da1cc3..028f5c5344 100644 --- a/src/patterns/scheduled-lambda.ts +++ b/src/patterns/scheduled-lambda.ts @@ -1,5 +1,6 @@ import type { Schedule } from "@aws-cdk/aws-events"; -import type { ErrorPercentageMonitoring } from "../constructs/cloudwatch/lambda-alarms"; +import type { ErrorPercentageMonitoring } from "../../lib/patterns/lambda-alarms"; +import type { LambdaMonitoring } from "../constructs/cloudwatch/lambda-alarms"; import { GuLambdaErrorPercentageAlarm } from "../constructs/cloudwatch/lambda-alarms"; import type { GuStack } from "../constructs/core"; import { GuLambdaFunction } from "../constructs/lambda"; @@ -7,7 +8,7 @@ import type { GuFunctionProps } from "../constructs/lambda"; interface GuScheduledLambdaProps extends Omit { schedule: Schedule; - monitoringConfiguration?: ErrorPercentageMonitoring; + monitoringConfiguration: LambdaMonitoring; } export class GuScheduledLambda extends GuLambdaFunction { @@ -17,9 +18,10 @@ export class GuScheduledLambda extends GuLambdaFunction { rules: [{ schedule: props.schedule }], }; super(scope, id, lambdaProps); - if (props.monitoringConfiguration) { + if (!props.monitoringConfiguration.noMonitoring) { + const errorPercentageMonitoring = props.monitoringConfiguration as ErrorPercentageMonitoring; new GuLambdaErrorPercentageAlarm(scope, "error-percentage-alarm-for-scheduled-lambda", { - ...props.monitoringConfiguration, + ...errorPercentageMonitoring, lambda: this, }); } From b1d168157bc1692adaa1306b0312af0324fe8d85 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Fri, 22 Jan 2021 16:13:03 +0000 Subject: [PATCH 08/13] Add label for % based alarm --- .../__snapshots__/lambda-alarms.test.ts.snap | 22 +++++++++++++++++++ src/constructs/cloudwatch/lambda-alarms.ts | 1 + .../scheduled-lambda.test.ts.snap | 11 ++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap index ea8d938189..d385ab74ca 100644 --- a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap +++ b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap @@ -185,6 +185,17 @@ Object { Object { "Expression": "100*m1/m2", "Id": "expr_1", + "Label": Object { + "Fn::Join": Array [ + "", + Array [ + "Error % of ", + Object { + "Ref": "lambda8B5974B5", + }, + ], + ], + }, }, Object { "Id": "m1", @@ -420,6 +431,17 @@ Object { Object { "Expression": "100*m1/m2", "Id": "expr_1", + "Label": Object { + "Fn::Join": Array [ + "", + Array [ + "Error % of ", + Object { + "Ref": "lambda8B5974B5", + }, + ], + ], + }, }, Object { "Id": "m1", diff --git a/src/constructs/cloudwatch/lambda-alarms.ts b/src/constructs/cloudwatch/lambda-alarms.ts index d6511e697a..2f2e746f5b 100644 --- a/src/constructs/cloudwatch/lambda-alarms.ts +++ b/src/constructs/cloudwatch/lambda-alarms.ts @@ -26,6 +26,7 @@ export class GuLambdaErrorPercentageAlarm extends GuAlarm { const mathExpression = new MathExpression({ expression: "100*m1/m2", usingMetrics: { m1: props.lambda.metricErrors(), m2: props.lambda.metricInvocations() }, + label: `Error % of ${props.lambda.functionName}`, }); const alarmProps = { ...props, diff --git a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap index 3b9e16c2a8..c828d0a7e7 100644 --- a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap +++ b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap @@ -45,6 +45,17 @@ Object { Object { "Expression": "100*m1/m2", "Id": "expr_1", + "Label": Object { + "Fn::Join": Array [ + "", + Array [ + "Error % of ", + Object { + "Ref": "mylambdafunction8D341B54", + }, + ], + ], + }, }, Object { "Id": "m1", From ddfafe65abe3520fffb466417976ff120f6d1d10 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Fri, 22 Jan 2021 16:27:04 +0000 Subject: [PATCH 09/13] Add default alarm names and descriptions --- .../__snapshots__/lambda-alarms.test.ts.snap | 572 ++++++++++++++++++ .../cloudwatch/lambda-alarms.test.ts | 42 ++ src/constructs/cloudwatch/lambda-alarms.ts | 6 +- .../scheduled-lambda.test.ts.snap | 26 + 4 files changed, 645 insertions(+), 1 deletion(-) diff --git a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap index d385ab74ca..7bfed997ee 100644 --- a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap +++ b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap @@ -179,6 +179,32 @@ Object { ], }, ], + "AlarmDescription": Object { + "Fn::Join": Array [ + "", + Array [ + Object { + "Ref": "lambda8B5974B5", + }, + " exceeded 65% error rate", + ], + ], + }, + "AlarmName": Object { + "Fn::Join": Array [ + "", + Array [ + "High error % from ", + Object { + "Ref": "lambda8B5974B5", + }, + " lambda in ", + Object { + "Ref": "Stage", + }, + ], + ], + }, "ComparisonOperator": "GreaterThanThreshold", "EvaluationPeriods": 12, "Metrics": Array [ @@ -425,6 +451,32 @@ Object { ], }, ], + "AlarmDescription": Object { + "Fn::Join": Array [ + "", + Array [ + Object { + "Ref": "lambda8B5974B5", + }, + " exceeded 80% error rate", + ], + ], + }, + "AlarmName": Object { + "Fn::Join": Array [ + "", + Array [ + "High error % from ", + Object { + "Ref": "lambda8B5974B5", + }, + " lambda in ", + Object { + "Ref": "Stage", + }, + ], + ], + }, "ComparisonOperator": "GreaterThanThreshold", "EvaluationPeriods": 1, "Metrics": Array [ @@ -491,3 +543,523 @@ Object { }, } `; + +exports[`The GuLambdaErrorPercentageAlarm pattern should use a custom alarm name if one is provided 1`] = ` +Object { + "Parameters": Object { + "Stack": Object { + "Default": "deploy", + "Description": "Name of this stack", + "Type": "String", + }, + "Stage": Object { + "AllowedValues": Array [ + "CODE", + "PROD", + ], + "Default": "CODE", + "Description": "Stage name", + "Type": "String", + }, + }, + "Resources": Object { + "lambda8B5974B5": Object { + "DependsOn": Array [ + "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "lambdaServiceRole494E4CA6", + ], + "Properties": Object { + "Code": Object { + "S3Bucket": "bucket1", + "S3Key": "folder/to/key", + }, + "Handler": "handler.ts", + "MemorySize": 512, + "Role": Object { + "Fn::GetAtt": Array [ + "lambdaServiceRole494E4CA6", + "Arn", + ], + }, + "Runtime": "nodejs12.x", + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + "Timeout": 30, + }, + "Type": "AWS::Lambda::Function", + }, + "lambdaServiceRole494E4CA6": 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", + ], + ], + }, + ], + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "lambdaServiceRoleDefaultPolicyBF6FA5E7": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1", + ], + ], + }, + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "Roles": Array [ + Object { + "Ref": "lambdaServiceRole494E4CA6", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "mylambdafunction8D341B54": Object { + "Properties": Object { + "AlarmActions": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:aws:sns:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alerts-topic", + ], + ], + }, + ], + "AlarmDescription": Object { + "Fn::Join": Array [ + "", + Array [ + Object { + "Ref": "lambda8B5974B5", + }, + " exceeded 65% error rate", + ], + ], + }, + "AlarmName": "test-custom-alarm-name", + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 12, + "Metrics": Array [ + Object { + "Expression": "100*m1/m2", + "Id": "expr_1", + "Label": Object { + "Fn::Join": Array [ + "", + Array [ + "Error % of ", + Object { + "Ref": "lambda8B5974B5", + }, + ], + ], + }, + }, + Object { + "Id": "m1", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Errors", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + Object { + "Id": "m2", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Invocations", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + ], + "Threshold": 65, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + }, +} +`; + +exports[`The GuLambdaErrorPercentageAlarm pattern should use a custom description if one is provided 1`] = ` +Object { + "Parameters": Object { + "Stack": Object { + "Default": "deploy", + "Description": "Name of this stack", + "Type": "String", + }, + "Stage": Object { + "AllowedValues": Array [ + "CODE", + "PROD", + ], + "Default": "CODE", + "Description": "Stage name", + "Type": "String", + }, + }, + "Resources": Object { + "lambda8B5974B5": Object { + "DependsOn": Array [ + "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "lambdaServiceRole494E4CA6", + ], + "Properties": Object { + "Code": Object { + "S3Bucket": "bucket1", + "S3Key": "folder/to/key", + }, + "Handler": "handler.ts", + "MemorySize": 512, + "Role": Object { + "Fn::GetAtt": Array [ + "lambdaServiceRole494E4CA6", + "Arn", + ], + }, + "Runtime": "nodejs12.x", + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + "Timeout": 30, + }, + "Type": "AWS::Lambda::Function", + }, + "lambdaServiceRole494E4CA6": 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", + ], + ], + }, + ], + "Tags": Array [ + Object { + "Key": "App", + "Value": "testing", + }, + Object { + "Key": "Stack", + "Value": Object { + "Ref": "Stack", + }, + }, + Object { + "Key": "Stage", + "Value": Object { + "Ref": "Stage", + }, + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "lambdaServiceRoleDefaultPolicyBF6FA5E7": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1", + ], + ], + }, + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":s3:::bucket1/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "lambdaServiceRoleDefaultPolicyBF6FA5E7", + "Roles": Array [ + Object { + "Ref": "lambdaServiceRole494E4CA6", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "mylambdafunction8D341B54": Object { + "Properties": Object { + "AlarmActions": Array [ + Object { + "Fn::Join": Array [ + "", + Array [ + "arn:aws:sns:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alerts-topic", + ], + ], + }, + ], + "AlarmDescription": "test-custom-alarm-description", + "AlarmName": Object { + "Fn::Join": Array [ + "", + Array [ + "High error % from ", + Object { + "Ref": "lambda8B5974B5", + }, + " lambda in ", + Object { + "Ref": "Stage", + }, + ], + ], + }, + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 12, + "Metrics": Array [ + Object { + "Expression": "100*m1/m2", + "Id": "expr_1", + "Label": Object { + "Fn::Join": Array [ + "", + Array [ + "Error % of ", + Object { + "Ref": "lambda8B5974B5", + }, + ], + ], + }, + }, + Object { + "Id": "m1", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Errors", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + Object { + "Id": "m2", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "FunctionName", + "Value": Object { + "Ref": "lambda8B5974B5", + }, + }, + ], + "MetricName": "Invocations", + "Namespace": "AWS/Lambda", + }, + "Period": 300, + "Stat": "Sum", + }, + "ReturnData": false, + }, + ], + "Threshold": 65, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + }, +} +`; diff --git a/src/constructs/cloudwatch/lambda-alarms.test.ts b/src/constructs/cloudwatch/lambda-alarms.test.ts index 24c85bae30..11e49ee62e 100644 --- a/src/constructs/cloudwatch/lambda-alarms.test.ts +++ b/src/constructs/cloudwatch/lambda-alarms.test.ts @@ -41,4 +41,46 @@ describe("The GuLambdaErrorPercentageAlarm pattern", () => { EvaluationPeriods: 12, }); }); + + it("should use a custom description if one is provided", () => { + const stack = simpleGuStackForTesting(); + const lambda = new GuLambdaFunction(stack, "lambda", { + code: { bucket: "bucket1", key: "folder/to/key" }, + handler: "handler.ts", + runtime: Runtime.NODEJS_12_X, + }); + const props = { + toleratedErrorPercentage: 65, + numberOfFiveMinutePeriodsToEvaluate: 12, + snsTopicName: "alerts-topic", + alarmDescription: "test-custom-alarm-description", + lambda: lambda, + }; + new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { + AlarmDescription: "test-custom-alarm-description", + }); + }); + + it("should use a custom alarm name if one is provided", () => { + const stack = simpleGuStackForTesting(); + const lambda = new GuLambdaFunction(stack, "lambda", { + code: { bucket: "bucket1", key: "folder/to/key" }, + handler: "handler.ts", + runtime: Runtime.NODEJS_12_X, + }); + const props = { + toleratedErrorPercentage: 65, + numberOfFiveMinutePeriodsToEvaluate: 12, + snsTopicName: "alerts-topic", + lambda: lambda, + alarmName: "test-custom-alarm-name", + }; + new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { + AlarmName: "test-custom-alarm-name", + }); + }); }); diff --git a/src/constructs/cloudwatch/lambda-alarms.ts b/src/constructs/cloudwatch/lambda-alarms.ts index 2f2e746f5b..5d27d9c351 100644 --- a/src/constructs/cloudwatch/lambda-alarms.ts +++ b/src/constructs/cloudwatch/lambda-alarms.ts @@ -28,12 +28,16 @@ export class GuLambdaErrorPercentageAlarm extends GuAlarm { usingMetrics: { m1: props.lambda.metricErrors(), m2: props.lambda.metricInvocations() }, label: `Error % of ${props.lambda.functionName}`, }); + const defaultAlarmName = `High error % from ${props.lambda.functionName} lambda in ${scope.stage}`; + const defaultDescription = `${props.lambda.functionName} exceeded ${props.toleratedErrorPercentage}% error rate`; const alarmProps = { ...props, metric: mathExpression, threshold: props.toleratedErrorPercentage, comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, - evaluationPeriods: props.numberOfFiveMinutePeriodsToEvaluate ? props.numberOfFiveMinutePeriodsToEvaluate : 1, + evaluationPeriods: props.numberOfFiveMinutePeriodsToEvaluate ?? 1, + alarmName: props.alarmName ?? defaultAlarmName, + alarmDescription: props.alarmDescription ?? defaultDescription, }; super(scope, id, alarmProps); } diff --git a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap index c828d0a7e7..49d61226ae 100644 --- a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap +++ b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap @@ -39,6 +39,32 @@ Object { ], }, ], + "AlarmDescription": Object { + "Fn::Join": Array [ + "", + Array [ + Object { + "Ref": "mylambdafunction8D341B54", + }, + " exceeded 99% error rate", + ], + ], + }, + "AlarmName": Object { + "Fn::Join": Array [ + "", + Array [ + "High error % from ", + Object { + "Ref": "mylambdafunction8D341B54", + }, + " lambda in ", + Object { + "Ref": "Stage", + }, + ], + ], + }, "ComparisonOperator": "GreaterThanThreshold", "EvaluationPeriods": 1, "Metrics": Array [ From a8994eceef5d293044b88e2732022754caeeb8d0 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Fri, 22 Jan 2021 16:54:36 +0000 Subject: [PATCH 10/13] Fix bad import --- src/patterns/scheduled-lambda.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/patterns/scheduled-lambda.ts b/src/patterns/scheduled-lambda.ts index 028f5c5344..4ee6d01e5f 100644 --- a/src/patterns/scheduled-lambda.ts +++ b/src/patterns/scheduled-lambda.ts @@ -1,6 +1,5 @@ import type { Schedule } from "@aws-cdk/aws-events"; -import type { ErrorPercentageMonitoring } from "../../lib/patterns/lambda-alarms"; -import type { LambdaMonitoring } from "../constructs/cloudwatch/lambda-alarms"; +import type { ErrorPercentageMonitoring, LambdaMonitoring } from "../constructs/cloudwatch/lambda-alarms"; import { GuLambdaErrorPercentageAlarm } from "../constructs/cloudwatch/lambda-alarms"; import type { GuStack } from "../constructs/core"; import { GuLambdaFunction } from "../constructs/lambda"; From 628e72c768acff3edee7c0eba45a84afdf4c5d76 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Fri, 22 Jan 2021 17:02:43 +0000 Subject: [PATCH 11/13] Tidy up --- src/patterns/scheduled-lambda.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/patterns/scheduled-lambda.ts b/src/patterns/scheduled-lambda.ts index 4ee6d01e5f..8b297767a4 100644 --- a/src/patterns/scheduled-lambda.ts +++ b/src/patterns/scheduled-lambda.ts @@ -1,5 +1,5 @@ import type { Schedule } from "@aws-cdk/aws-events"; -import type { ErrorPercentageMonitoring, LambdaMonitoring } from "../constructs/cloudwatch/lambda-alarms"; +import type { LambdaMonitoring } from "../constructs/cloudwatch/lambda-alarms"; import { GuLambdaErrorPercentageAlarm } from "../constructs/cloudwatch/lambda-alarms"; import type { GuStack } from "../constructs/core"; import { GuLambdaFunction } from "../constructs/lambda"; @@ -18,9 +18,8 @@ export class GuScheduledLambda extends GuLambdaFunction { }; super(scope, id, lambdaProps); if (!props.monitoringConfiguration.noMonitoring) { - const errorPercentageMonitoring = props.monitoringConfiguration as ErrorPercentageMonitoring; new GuLambdaErrorPercentageAlarm(scope, "error-percentage-alarm-for-scheduled-lambda", { - ...errorPercentageMonitoring, + ...props.monitoringConfiguration, lambda: this, }); } From f2b937b0e39278005bf5371ca0202d59578dbe06 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Mon, 25 Jan 2021 10:04:07 +0000 Subject: [PATCH 12/13] Add up to date cloudwatch-actions dependency --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 941797d0b8..a8825ad0cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -182,16 +182,16 @@ } }, "@aws-cdk/aws-cloudwatch-actions": { - "version": "1.74.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudwatch-actions/-/aws-cloudwatch-actions-1.74.0.tgz", - "integrity": "sha512-N90JLaj3jDNr5hX0g+Wj/+4wtJGPJz5h94N2DYQd0Ho7NU+p4GIP8BESJ4yWxYOKtO6VcH59Fz3s6r7kwvEOcg==", - "requires": { - "@aws-cdk/aws-applicationautoscaling": "1.74.0", - "@aws-cdk/aws-autoscaling": "1.74.0", - "@aws-cdk/aws-cloudwatch": "1.74.0", - "@aws-cdk/aws-iam": "1.74.0", - "@aws-cdk/aws-sns": "1.74.0", - "@aws-cdk/core": "1.74.0", + "version": "1.86.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudwatch-actions/-/aws-cloudwatch-actions-1.86.0.tgz", + "integrity": "sha512-dUFCwinO9JGE/zrh8BoDiwEaPQbHKGpeu3ArJ5u66Adbo02Nzn3C6m5OJB1cJjSPpSyWllm3nybLjVyA5FYQ+A==", + "requires": { + "@aws-cdk/aws-applicationautoscaling": "1.86.0", + "@aws-cdk/aws-autoscaling": "1.86.0", + "@aws-cdk/aws-cloudwatch": "1.86.0", + "@aws-cdk/aws-iam": "1.86.0", + "@aws-cdk/aws-sns": "1.86.0", + "@aws-cdk/core": "1.86.0", "constructs": "^3.2.0" } }, diff --git a/package.json b/package.json index 3bc92bca36..304a95455f 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "dependencies": { "@aws-cdk/assert": "1.86.0", "@aws-cdk/aws-autoscaling": "1.86.0", - "@aws-cdk/aws-cloudwatch-actions": "~1.74.0", + "@aws-cdk/aws-cloudwatch-actions": "1.86.0", "@aws-cdk/aws-ec2": "1.86.0", "@aws-cdk/aws-apigateway": "1.86.0", "@aws-cdk/aws-elasticloadbalancing": "1.86.0", From 139d5241156b516aef5dc4c219874bddc8963687 Mon Sep 17 00:00:00 2001 From: Jacob Winch Date: Mon, 25 Jan 2021 14:08:59 +0000 Subject: [PATCH 13/13] Remove snapshot assertions if we have a direct assertion --- .../__snapshots__/lambda-alarms.test.ts.snap | 800 +----------------- .../cloudwatch/lambda-alarms.test.ts | 3 - 2 files changed, 4 insertions(+), 799 deletions(-) diff --git a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap index 7bfed997ee..67793394d5 100644 --- a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap +++ b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap @@ -1,550 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`The GuLambdaErrorPercentageAlarm pattern should adjust the number of evaluation periods if a custom value is provided 1`] = ` -Object { - "Parameters": Object { - "Stack": Object { - "Default": "deploy", - "Description": "Name of this stack", - "Type": "String", - }, - "Stage": Object { - "AllowedValues": Array [ - "CODE", - "PROD", - ], - "Default": "CODE", - "Description": "Stage name", - "Type": "String", - }, - }, - "Resources": Object { - "lambda8B5974B5": Object { - "DependsOn": Array [ - "lambdaServiceRoleDefaultPolicyBF6FA5E7", - "lambdaServiceRole494E4CA6", - ], - "Properties": Object { - "Code": Object { - "S3Bucket": "bucket1", - "S3Key": "folder/to/key", - }, - "Handler": "handler.ts", - "MemorySize": 512, - "Role": Object { - "Fn::GetAtt": Array [ - "lambdaServiceRole494E4CA6", - "Arn", - ], - }, - "Runtime": "nodejs12.x", - "Tags": Array [ - Object { - "Key": "App", - "Value": "testing", - }, - Object { - "Key": "Stack", - "Value": Object { - "Ref": "Stack", - }, - }, - Object { - "Key": "Stage", - "Value": Object { - "Ref": "Stage", - }, - }, - ], - "Timeout": 30, - }, - "Type": "AWS::Lambda::Function", - }, - "lambdaServiceRole494E4CA6": 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", - ], - ], - }, - ], - "Tags": Array [ - Object { - "Key": "App", - "Value": "testing", - }, - Object { - "Key": "Stack", - "Value": Object { - "Ref": "Stack", - }, - }, - Object { - "Key": "Stage", - "Value": Object { - "Ref": "Stage", - }, - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "lambdaServiceRoleDefaultPolicyBF6FA5E7": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":s3:::bucket1", - ], - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":s3:::bucket1/*", - ], - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "lambdaServiceRoleDefaultPolicyBF6FA5E7", - "Roles": Array [ - Object { - "Ref": "lambdaServiceRole494E4CA6", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "mylambdafunction8D341B54": Object { - "Properties": Object { - "AlarmActions": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:aws:sns:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":alerts-topic", - ], - ], - }, - ], - "AlarmDescription": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Ref": "lambda8B5974B5", - }, - " exceeded 65% error rate", - ], - ], - }, - "AlarmName": Object { - "Fn::Join": Array [ - "", - Array [ - "High error % from ", - Object { - "Ref": "lambda8B5974B5", - }, - " lambda in ", - Object { - "Ref": "Stage", - }, - ], - ], - }, - "ComparisonOperator": "GreaterThanThreshold", - "EvaluationPeriods": 12, - "Metrics": Array [ - Object { - "Expression": "100*m1/m2", - "Id": "expr_1", - "Label": Object { - "Fn::Join": Array [ - "", - Array [ - "Error % of ", - Object { - "Ref": "lambda8B5974B5", - }, - ], - ], - }, - }, - Object { - "Id": "m1", - "MetricStat": Object { - "Metric": Object { - "Dimensions": Array [ - Object { - "Name": "FunctionName", - "Value": Object { - "Ref": "lambda8B5974B5", - }, - }, - ], - "MetricName": "Errors", - "Namespace": "AWS/Lambda", - }, - "Period": 300, - "Stat": "Sum", - }, - "ReturnData": false, - }, - Object { - "Id": "m2", - "MetricStat": Object { - "Metric": Object { - "Dimensions": Array [ - Object { - "Name": "FunctionName", - "Value": Object { - "Ref": "lambda8B5974B5", - }, - }, - ], - "MetricName": "Invocations", - "Namespace": "AWS/Lambda", - }, - "Period": 300, - "Stat": "Sum", - }, - "ReturnData": false, - }, - ], - "Threshold": 65, - }, - "Type": "AWS::CloudWatch::Alarm", - }, - }, -} -`; - -exports[`The GuLambdaErrorPercentageAlarm pattern should create the correct alarm resource with minimal config 1`] = ` -Object { - "Parameters": Object { - "Stack": Object { - "Default": "deploy", - "Description": "Name of this stack", - "Type": "String", - }, - "Stage": Object { - "AllowedValues": Array [ - "CODE", - "PROD", - ], - "Default": "CODE", - "Description": "Stage name", - "Type": "String", - }, - }, - "Resources": Object { - "lambda8B5974B5": Object { - "DependsOn": Array [ - "lambdaServiceRoleDefaultPolicyBF6FA5E7", - "lambdaServiceRole494E4CA6", - ], - "Properties": Object { - "Code": Object { - "S3Bucket": "bucket1", - "S3Key": "folder/to/key", - }, - "Handler": "handler.ts", - "MemorySize": 512, - "Role": Object { - "Fn::GetAtt": Array [ - "lambdaServiceRole494E4CA6", - "Arn", - ], - }, - "Runtime": "nodejs12.x", - "Tags": Array [ - Object { - "Key": "App", - "Value": "testing", - }, - Object { - "Key": "Stack", - "Value": Object { - "Ref": "Stack", - }, - }, - Object { - "Key": "Stage", - "Value": Object { - "Ref": "Stage", - }, - }, - ], - "Timeout": 30, - }, - "Type": "AWS::Lambda::Function", - }, - "lambdaServiceRole494E4CA6": 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", - ], - ], - }, - ], - "Tags": Array [ - Object { - "Key": "App", - "Value": "testing", - }, - Object { - "Key": "Stack", - "Value": Object { - "Ref": "Stack", - }, - }, - Object { - "Key": "Stage", - "Value": Object { - "Ref": "Stage", - }, - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "lambdaServiceRoleDefaultPolicyBF6FA5E7": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":s3:::bucket1", - ], - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":s3:::bucket1/*", - ], - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "lambdaServiceRoleDefaultPolicyBF6FA5E7", - "Roles": Array [ - Object { - "Ref": "lambdaServiceRole494E4CA6", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "mylambdafunction8D341B54": Object { - "Properties": Object { - "AlarmActions": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:aws:sns:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":alerts-topic", - ], - ], - }, - ], - "AlarmDescription": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Ref": "lambda8B5974B5", - }, - " exceeded 80% error rate", - ], - ], - }, - "AlarmName": Object { - "Fn::Join": Array [ - "", - Array [ - "High error % from ", - Object { - "Ref": "lambda8B5974B5", - }, - " lambda in ", - Object { - "Ref": "Stage", - }, - ], - ], - }, - "ComparisonOperator": "GreaterThanThreshold", - "EvaluationPeriods": 1, - "Metrics": Array [ - Object { - "Expression": "100*m1/m2", - "Id": "expr_1", - "Label": Object { - "Fn::Join": Array [ - "", - Array [ - "Error % of ", - Object { - "Ref": "lambda8B5974B5", - }, - ], - ], - }, - }, - Object { - "Id": "m1", - "MetricStat": Object { - "Metric": Object { - "Dimensions": Array [ - Object { - "Name": "FunctionName", - "Value": Object { - "Ref": "lambda8B5974B5", - }, - }, - ], - "MetricName": "Errors", - "Namespace": "AWS/Lambda", - }, - "Period": 300, - "Stat": "Sum", - }, - "ReturnData": false, - }, - Object { - "Id": "m2", - "MetricStat": Object { - "Metric": Object { - "Dimensions": Array [ - Object { - "Name": "FunctionName", - "Value": Object { - "Ref": "lambda8B5974B5", - }, - }, - ], - "MetricName": "Invocations", - "Namespace": "AWS/Lambda", - }, - "Period": 300, - "Stat": "Sum", - }, - "ReturnData": false, - }, - ], - "Threshold": 80, - }, - "Type": "AWS::CloudWatch::Alarm", - }, - }, -} -`; - -exports[`The GuLambdaErrorPercentageAlarm pattern should use a custom alarm name if one is provided 1`] = ` +exports[`The GuLambdaErrorPercentageAlarm pattern should create the correct alarm resource with minimal config 1`] = ` Object { "Parameters": Object { "Stack": Object { @@ -730,258 +186,10 @@ Object { Object { "Ref": "lambda8B5974B5", }, - " exceeded 65% error rate", - ], - ], - }, - "AlarmName": "test-custom-alarm-name", - "ComparisonOperator": "GreaterThanThreshold", - "EvaluationPeriods": 12, - "Metrics": Array [ - Object { - "Expression": "100*m1/m2", - "Id": "expr_1", - "Label": Object { - "Fn::Join": Array [ - "", - Array [ - "Error % of ", - Object { - "Ref": "lambda8B5974B5", - }, - ], - ], - }, - }, - Object { - "Id": "m1", - "MetricStat": Object { - "Metric": Object { - "Dimensions": Array [ - Object { - "Name": "FunctionName", - "Value": Object { - "Ref": "lambda8B5974B5", - }, - }, - ], - "MetricName": "Errors", - "Namespace": "AWS/Lambda", - }, - "Period": 300, - "Stat": "Sum", - }, - "ReturnData": false, - }, - Object { - "Id": "m2", - "MetricStat": Object { - "Metric": Object { - "Dimensions": Array [ - Object { - "Name": "FunctionName", - "Value": Object { - "Ref": "lambda8B5974B5", - }, - }, - ], - "MetricName": "Invocations", - "Namespace": "AWS/Lambda", - }, - "Period": 300, - "Stat": "Sum", - }, - "ReturnData": false, - }, - ], - "Threshold": 65, - }, - "Type": "AWS::CloudWatch::Alarm", - }, - }, -} -`; - -exports[`The GuLambdaErrorPercentageAlarm pattern should use a custom description if one is provided 1`] = ` -Object { - "Parameters": Object { - "Stack": Object { - "Default": "deploy", - "Description": "Name of this stack", - "Type": "String", - }, - "Stage": Object { - "AllowedValues": Array [ - "CODE", - "PROD", - ], - "Default": "CODE", - "Description": "Stage name", - "Type": "String", - }, - }, - "Resources": Object { - "lambda8B5974B5": Object { - "DependsOn": Array [ - "lambdaServiceRoleDefaultPolicyBF6FA5E7", - "lambdaServiceRole494E4CA6", - ], - "Properties": Object { - "Code": Object { - "S3Bucket": "bucket1", - "S3Key": "folder/to/key", - }, - "Handler": "handler.ts", - "MemorySize": 512, - "Role": Object { - "Fn::GetAtt": Array [ - "lambdaServiceRole494E4CA6", - "Arn", - ], - }, - "Runtime": "nodejs12.x", - "Tags": Array [ - Object { - "Key": "App", - "Value": "testing", - }, - Object { - "Key": "Stack", - "Value": Object { - "Ref": "Stack", - }, - }, - Object { - "Key": "Stage", - "Value": Object { - "Ref": "Stage", - }, - }, - ], - "Timeout": 30, - }, - "Type": "AWS::Lambda::Function", - }, - "lambdaServiceRole494E4CA6": 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", - ], + " exceeded 80% error rate", ], - }, - ], - "Tags": Array [ - Object { - "Key": "App", - "Value": "testing", - }, - Object { - "Key": "Stack", - "Value": Object { - "Ref": "Stack", - }, - }, - Object { - "Key": "Stage", - "Value": Object { - "Ref": "Stage", - }, - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "lambdaServiceRoleDefaultPolicyBF6FA5E7": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":s3:::bucket1", - ], - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":s3:::bucket1/*", - ], - ], - }, - ], - }, ], - "Version": "2012-10-17", }, - "PolicyName": "lambdaServiceRoleDefaultPolicyBF6FA5E7", - "Roles": Array [ - Object { - "Ref": "lambdaServiceRole494E4CA6", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "mylambdafunction8D341B54": Object { - "Properties": Object { - "AlarmActions": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:aws:sns:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":alerts-topic", - ], - ], - }, - ], - "AlarmDescription": "test-custom-alarm-description", "AlarmName": Object { "Fn::Join": Array [ "", @@ -998,7 +206,7 @@ Object { ], }, "ComparisonOperator": "GreaterThanThreshold", - "EvaluationPeriods": 12, + "EvaluationPeriods": 1, "Metrics": Array [ Object { "Expression": "100*m1/m2", @@ -1056,7 +264,7 @@ Object { "ReturnData": false, }, ], - "Threshold": 65, + "Threshold": 80, }, "Type": "AWS::CloudWatch::Alarm", }, diff --git a/src/constructs/cloudwatch/lambda-alarms.test.ts b/src/constructs/cloudwatch/lambda-alarms.test.ts index 11e49ee62e..c387f39a02 100644 --- a/src/constructs/cloudwatch/lambda-alarms.test.ts +++ b/src/constructs/cloudwatch/lambda-alarms.test.ts @@ -36,7 +36,6 @@ describe("The GuLambdaErrorPercentageAlarm pattern", () => { lambda: lambda, }; new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); - expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { EvaluationPeriods: 12, }); @@ -57,7 +56,6 @@ describe("The GuLambdaErrorPercentageAlarm pattern", () => { lambda: lambda, }; new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); - expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { AlarmDescription: "test-custom-alarm-description", }); @@ -78,7 +76,6 @@ describe("The GuLambdaErrorPercentageAlarm pattern", () => { alarmName: "test-custom-alarm-name", }; new GuLambdaErrorPercentageAlarm(stack, "my-lambda-function", props); - expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); expect(stack).toHaveResource("AWS::CloudWatch::Alarm", { AlarmName: "test-custom-alarm-name", });