From 2fdfd7c1729ac2d33f7a5794457fc70eef7c16f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Barthelet?= Date: Fri, 24 Feb 2023 11:19:43 +0100 Subject: [PATCH 1/2] fix alarm extension on queues constructs --- src/constructs/aws/Queue.ts | 15 ++++++++--- test/unit/queues.test.ts | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/constructs/aws/Queue.ts b/src/constructs/aws/Queue.ts index de935d91..8bf0f4d5 100644 --- a/src/constructs/aws/Queue.ts +++ b/src/constructs/aws/Queue.ts @@ -2,6 +2,7 @@ import { Key } from "aws-cdk-lib/aws-kms"; import type { CfnQueue } from "aws-cdk-lib/aws-sqs"; import { Queue as CdkQueue, QueueEncryption } from "aws-cdk-lib/aws-sqs"; import type { FromSchema } from "json-schema-to-ts"; +import type { CfnAlarm } from "aws-cdk-lib/aws-cloudwatch"; import { Alarm, ComparisonOperator, Metric } from "aws-cdk-lib/aws-cloudwatch"; import { Subscription, SubscriptionProtocol, Topic } from "aws-cdk-lib/aws-sns"; import type { AlarmActionConfig } from "aws-cdk-lib/aws-cloudwatch/lib/alarm-action"; @@ -117,6 +118,7 @@ export class Queue extends AwsConstruct { private readonly queue: CdkQueue; private readonly dlq: CdkQueue; + private readonly alarm?: Alarm; private readonly queueArnOutput: CfnOutput; private readonly queueUrlOutput: CfnOutput; private readonly dlqUrlOutput: CfnOutput; @@ -218,7 +220,7 @@ export class Queue extends AwsConstruct { endpoint: alarmEmail, }); - const alarm = new Alarm(this, "Alarm", { + this.alarm = new Alarm(this, "Alarm", { alarmName: `${this.provider.stackName}-${id}-dlq-alarm`, alarmDescription: "Alert triggered when there are failed jobs in the dead letter queue.", metric: new Metric({ @@ -235,7 +237,7 @@ export class Queue extends AwsConstruct { threshold: 0, comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, }); - alarm.addAlarmAction({ + this.alarm.addAlarmAction({ bind(): AlarmActionConfig { return { alarmActionArn: alarmTopic.topicArn }; }, @@ -279,11 +281,16 @@ export class Queue extends AwsConstruct { } extend(): Record { - return { + const extensions = { queue: this.queue.node.defaultChild as CfnQueue, dlq: this.dlq.node.defaultChild as CfnQueue, - alarm: this.dlq.node.defaultChild as CfnQueue, }; + + if (this.alarm !== undefined) { + Object.assign(extensions, { alarm: this.alarm.node.defaultChild as CfnAlarm }); + } + + return extensions; } private getMaximumBatchingWindow(): number { diff --git a/test/unit/queues.test.ts b/test/unit/queues.test.ts index 196ce6e5..35076fd1 100644 --- a/test/unit/queues.test.ts +++ b/test/unit/queues.test.ts @@ -620,4 +620,58 @@ describe("queues", () => { MaximumMessageSize: 1024, }); }); + + it("allows overriding alarm properties", async () => { + const { cfTemplate, computeLogicalId } = await runServerless({ + fixture: "queues", + configExt: merge({}, pluginConfigExt, { + constructs: { + emails: { + alarm: "myemail@mycompany.com", + extensions: { + alarm: { + Properties: { + AlarmActions: ["arn:aws:sns:region:account-id:sns-topic-name"], + }, + }, + }, + }, + }, + }), + command: "package", + }); + expect(cfTemplate.Resources[computeLogicalId("emails", "Alarm")].Properties).toMatchObject({ + AlarmActions: ["arn:aws:sns:region:account-id:sns-topic-name"], + }); + }); + + it("should throw if overriding alarm properties while no alarm is configured", async () => { + expect.assertions(2); + + try { + await runServerless({ + fixture: "queues", + configExt: merge({}, pluginConfigExt, { + constructs: { + emails: { + extensions: { + alarm: { + Properties: { + AlarmActions: ["arn:aws:sns:region:account-id:sns-topic-name"], + }, + }, + }, + }, + }, + }), + command: "package", + }); + } catch (error) { + expect(error).toBeInstanceOf(ServerlessError); + expect(error).toHaveProperty( + "message", + "There is no extension 'alarm' available on this construct. Available extensions are: queue, dlq." + ); + } + }); }); From 519148d2b3a7a99c743cfeb8af2810fcca17de1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Barthelet?= Date: Fri, 24 Feb 2023 11:22:04 +0100 Subject: [PATCH 2/2] Update documentation to reflect the conditional aspect of the alarm extension key --- docs/queue.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/queue.md b/docs/queue.md index 4e5b3d0e..96498f35 100644 --- a/docs/queue.md +++ b/docs/queue.md @@ -424,6 +424,8 @@ constructs: | dlq | AWS::SQS::Queue | [Link](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sqs-queue.html) | | alarm | AWS::CloudWatch::Alarm | [Link](https://docs.aws.amazon.com/fr_fr/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html) | +> ⚠️ The `alarm` extension key is only available if an alarm email destination has been configured on the construct. + ### More options Feel like a common extension pattern should be implemented as part of the construct configuration? [Open a GitHub issue](https://github.com/getlift/lift/issues/new).