Skip to content

Commit

Permalink
fix(aws-apigateway-kinesisstreams): Update construct to match DESIGN_…
Browse files Browse the repository at this point in the history
…GUIDELINES.md (#395)

* enhance aws-apigateway-kinesisstreams construct with best practice cloudwatch.Alarms

* add missing package.json file

* fix tests

Co-authored-by: santhosh <>
Co-authored-by: biffgaut <78155736+biffgaut@users.noreply.github.com>
  • Loading branch information
surukonda and biffgaut authored Sep 28, 2021
1 parent 3aca3c0 commit 9dbec8a
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ _Parameters_
|existingStreamObj?|[`kinesis.Stream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.Stream.html)|Existing instance of Kinesis Stream, providing both this and `kinesisStreamProps` will cause an error.|
|kinesisStreamProps?|[`kinesis.StreamProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.StreamProps.html)|Optional user-provided props to override the default props for the Kinesis stream.|
|logGroupProps?|[`logs.LogGroupProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-logs.LogGroupProps.html)|User provided props to override the default props for for the CloudWatchLogs LogGroup.|
|createCloudWatchAlarms|`boolean`|Whether to create recommended CloudWatch alarms for Kinesis Data Stream. Default value is set to `true`|

## Pattern Properties

Expand All @@ -69,6 +70,7 @@ _Parameters_
|apiGatewayCloudWatchRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for API Gateway for CloudWatch access.|
|apiGatewayLogGroup|[`logs.LogGroup`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-logs.LogGroup.html)|Returns an instance of the LogGroup created by the construct for API Gateway access logging to CloudWatch.|
|kinesisStream|[`kinesis.Stream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.Stream.html)|Returns an instance of the Kinesis stream created or used by the pattern.|
|cloudwatchAlarms?|[`cloudwatch.Alarm[]`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cloudwatch.Alarm.html)|Returns an array of recommended CloudWatch Alarms created by the construct for Kinesis Data stream|

## Sample API Usage

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as defaults from '@aws-solutions-constructs/core';
// Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate
import { Construct } from '@aws-cdk/core';
import * as logs from '@aws-cdk/aws-logs';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';

/**
* @summary The properties for the ApiGatewayToKinesisStreamsProps class.
Expand Down Expand Up @@ -81,6 +82,12 @@ export interface ApiGatewayToKinesisStreamsProps {
* @default - Default props are used
*/
readonly logGroupProps?: logs.LogGroupProps
/**
* Whether to create recommended CloudWatch alarms
*
* @default - Alarms are created
*/
readonly createCloudWatchAlarms?: boolean;
}

/**
Expand All @@ -92,6 +99,7 @@ export class ApiGatewayToKinesisStreams extends Construct {
public readonly apiGatewayCloudWatchRole: iam.Role;
public readonly apiGatewayLogGroup: logs.LogGroup;
public readonly kinesisStream: kinesis.Stream;
public readonly cloudwatchAlarms?: cloudwatch.Alarm[];

/**
* @summary Constructs a new instance of the ApiGatewayToKinesisStreams class.
Expand Down Expand Up @@ -154,6 +162,11 @@ export class ApiGatewayToKinesisStreams extends Construct {
requestValidator,
requestModel: { 'application/json': this.getPutRecordsModel(props.putRecordsRequestModel) }
});

if (props.createCloudWatchAlarms === undefined || props.createCloudWatchAlarms) {
// Deploy best practices CW Alarms for Kinesis Stream
this.cloudwatchAlarms = defaults.buildKinesisStreamCWAlarms(this);
}
}

private getPutRecordTemplate(putRecordTemplate?: string): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-kinesis": "0.0.0",
"@aws-cdk/aws-logs": "0.0.0",
"@aws-cdk/aws-cloudwatch": "0.0.0",
"@aws-solutions-constructs/core": "0.0.0",
"constructs": "^3.2.0"
},
Expand Down Expand Up @@ -87,7 +88,8 @@
"@aws-cdk/aws-kinesis": "0.0.0",
"@aws-solutions-constructs/core": "0.0.0",
"constructs": "^3.2.0",
"@aws-cdk/aws-logs": "0.0.0"
"@aws-cdk/aws-logs": "0.0.0",
"@aws-cdk/aws-cloudwatch": "0.0.0"
},
"keywords": [
"aws",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ test('Test construct properties', () => {
expect(pattern.apiGatewayCloudWatchRole !== null);
expect(pattern.apiGatewayLogGroup !== null);
expect(pattern.kinesisStream !== null);
expect(pattern.cloudwatchAlarms !== null);
});

// --------------------------------------------------------------
Expand Down Expand Up @@ -80,24 +81,33 @@ test('Test deployment w/ overwritten properties', () => {
ShardCount: 1,
Name: 'my-stream'
});

// Test for Cloudwatch Alarms
expect(stack).toCountResources('AWS::CloudWatch::Alarm', 2);
});

// --------------------------------------------------------------
// Test deployment w/ existing stream
// Test deployment w/ existing stream without default cloudwatch alarms
// --------------------------------------------------------------
test('Test deployment w/ existing stream', () => {
const stack = new Stack();

new ApiGatewayToKinesisStreams(stack, 'api-gateway-kinesis', {
const construct = new ApiGatewayToKinesisStreams(stack, 'api-gateway-kinesis', {
apiGatewayProps: {},
existingStreamObj: new kinesis.Stream(stack, 'ExistingStream', {
shardCount: 5,
retentionPeriod: Duration.days(4)
})
}),
createCloudWatchAlarms: false
});

expect(stack).toHaveResource('AWS::Kinesis::Stream', {
ShardCount: 5,
RetentionPeriodHours: 96
});

expect(construct.cloudwatchAlarms == null);

// Since createCloudWatchAlars is set to false, no Alarm should exist
expect(stack).not.toHaveResource('AWS::CloudWatch::Alarm');
});
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,32 @@
]
}
},
"testapigatewaykinesisoverwriteKinesisStreamGetRecordsIteratorAgeAlarmAF0BEF52": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"EvaluationPeriods": 1,
"AlarmDescription": "Consumer Record Processing Falling Behind, there is risk for data loss due to record expiration.",
"MetricName": "GetRecords.IteratorAgeMilliseconds",
"Namespace": "AWS/Kinesis",
"Period": 300,
"Statistic": "Maximum",
"Threshold": 2592000
}
},
"testapigatewaykinesisoverwriteKinesisStreamReadProvisionedThroughputExceededAlarm5C0040FB": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"ComparisonOperator": "GreaterThanThreshold",
"EvaluationPeriods": 1,
"AlarmDescription": "Consumer Application is Reading at a Slower Rate Than Expected.",
"MetricName": "ReadProvisionedThroughputExceeded",
"Namespace": "AWS/Kinesis",
"Period": 300,
"Statistic": "Average",
"Threshold": 0
}
},
"KinesisStream46752A3E": {
"Type": "AWS::Kinesis::Stream",
"Properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,32 @@
]
}
},
"testapigatewaykinesisdefaultKinesisStreamGetRecordsIteratorAgeAlarm0638BB32": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"EvaluationPeriods": 1,
"AlarmDescription": "Consumer Record Processing Falling Behind, there is risk for data loss due to record expiration.",
"MetricName": "GetRecords.IteratorAgeMilliseconds",
"Namespace": "AWS/Kinesis",
"Period": 300,
"Statistic": "Maximum",
"Threshold": 2592000
}
},
"testapigatewaykinesisdefaultKinesisStreamReadProvisionedThroughputExceededAlarmE7251F6A": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"ComparisonOperator": "GreaterThanThreshold",
"EvaluationPeriods": 1,
"AlarmDescription": "Consumer Application is Reading at a Slower Rate Than Expected.",
"MetricName": "ReadProvisionedThroughputExceeded",
"Namespace": "AWS/Kinesis",
"Period": 300,
"Statistic": "Average",
"Threshold": 0
}
},
"KinesisStream46752A3E": {
"Type": "AWS::Kinesis::Stream",
"Properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,32 @@
]
}
},
"ApiGatwayToKinesisStreamsKinesisStreamGetRecordsIteratorAgeAlarm1705B52A": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"EvaluationPeriods": 1,
"AlarmDescription": "Consumer Record Processing Falling Behind, there is risk for data loss due to record expiration.",
"MetricName": "GetRecords.IteratorAgeMilliseconds",
"Namespace": "AWS/Kinesis",
"Period": 300,
"Statistic": "Maximum",
"Threshold": 2592000
}
},
"ApiGatwayToKinesisStreamsKinesisStreamReadProvisionedThroughputExceededAlarm69C57002": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"ComparisonOperator": "GreaterThanThreshold",
"EvaluationPeriods": 1,
"AlarmDescription": "Consumer Application is Reading at a Slower Rate Than Expected.",
"MetricName": "ReadProvisionedThroughputExceeded",
"Namespace": "AWS/Kinesis",
"Period": 300,
"Statistic": "Average",
"Threshold": 0
}
},
"KinesisStream46752A3E": {
"Type": "AWS::Kinesis::Stream",
"Properties": {
Expand Down

0 comments on commit 9dbec8a

Please sign in to comment.