@mapbox/cloudfriend
Version:
Helper functions for assembling CloudFormation templates in JavaScript
162 lines (154 loc) • 7.99 kB
JavaScript
;
const KinesisFirehoseBase = require('./kinesis-firehose-base');
const assert = require('assert');
;
/**
* Creates a Kinesis Firehouse that can receive records by direct put or by consuming a Kinesis Stream
* and writes out to the specific S3 destination. Creates a Kinesis Firehouse delivery stream,
* sets up logging, and creates a policy allowing records to be delivered to the delivery stream.
* Also creates a CloudWatch alarm on the `DeliveryToS3.DataFreshness` metric -- the age
* of the oldest record in Kinesis Data Firehose (from entering the Kinesis Data Firehose until now).
* By default, if that metric exceeds double the `BufferingIntervalInSeconds`, the
* alarm is triggered.
*
* @param {Object} options - Options.
* @param {String} options.LogicalName - The logical name of the Kinesis Firehouse delivery stream
* within the CloudFormation template. This is also used to construct the logical
* names of the other resources.
* @param {String} options.DestinationBucket - The name of the S3 bucket to write to.
* @param {String} [options.Prefix='raw/${logical name}/'] - The prefix path (folder) within the DestinationBucket to write to.
* @param {String|Object} [options.KinesisStreamARN=undefined] - The ARN of a source Kinesis Stream.
* @param {Number} [options.BufferingIntervalInSeconds=900] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisfirehose-deliverystream-bufferinghints.html#cfn-kinesisfirehose-deliverystream-bufferinghints-intervalinseconds).
* @param {Number} [options.BufferingSizeInMBs=128] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisfirehose-deliverystream-bufferinghints.html#cfn-kinesisfirehose-deliverystream-bufferinghints-sizeinmbs).
* @param {String} [options.AlarmName='${stack name}-${logical name}-Freshness-${region}'] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-alarmname).
* @param {String} [options.AlarmDescription='Freshness alarm for ${stack name}-${logical name} kinesis firehose in ${stack name} stack'] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-alarmdescription).
* @param {Array<String>} [options.AlarmActions=[]] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-alarmactions).
* @param {Number} [options.Period=60] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-period).
* @param {Number} [options.EvaluationPeriods=1] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-evaluationperiods).
* @param {String} [options.Statistic='Maximum'] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-statistic).
* @param {Number} [options.Threshold=(BufferingIntervalInSeconds * 2)] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-threshold).
* @param {String} [options.ComparisonOperator='GreaterThanThreshold'] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-comparisonoperator).
* @param {String} [options.TreatMissingData='notBreaching'] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-treatmissingdata).
* @param {String} [options.EvaluateLowSampleCountPercentile=undefined] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-evaluatelowsamplecountpercentile).
* @param {String} [options.ExtendedStatistic=undefined] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-extendedstatistic)]
* @param {Array<String>} [options.OKActions=undefined] - See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html#cfn-cloudwatch-alarms-okactions).
*
* @example
* const cf = require('@mapbox/cloudfriend');
*
* const myTemplate = { ... };
*
* const firehose = new cf.shortcuts.S3KinesisFirehose({
* LogicalName: 'MyKinesisFirehose',
* DestinationBucket: 'mah-bukkit'
* });
*
* module.exports = cf.merge(myTemplate, firehose);
*/
class S3KinesisFirehose extends KinesisFirehoseBase {
constructor(options) {
if (!options) throw new Error('Options required');
const {
LogicalName,
DestinationBucket,
Prefix = { 'Fn::Join': ['', ['raw/', LogicalName, '/']] },
BufferingIntervalInSeconds = 900,
BufferingSizeInMBs = 128,
AlarmName = { 'Fn::Sub': `\${AWS::StackName}-${LogicalName}-Freshness-\${AWS::Region}` },
AlarmDescription = {
'Fn::Sub': [
'Freshness alarm for ${AWS::StackName}-${name} kinesis firehose in ${AWS::StackName} stack',
{ name: LogicalName }
]
},
AlarmActions = [],
Period = 60,
EvaluationPeriods = 1,
Statistic = 'Maximum',
Threshold = BufferingIntervalInSeconds * 2,
ComparisonOperator = 'GreaterThanThreshold',
TreatMissingData = 'notBreaching',
EvaluateLowSampleCountPercentile,
ExtendedStatistic,
OKActions
} = options;
super(options);
assert(DestinationBucket, 'You must provide a DestinationBucket');
const BucketARN = { 'Fn::Sub': `arn:\${AWS::Partition}:s3:::${DestinationBucket}` };
const S3DestinationConfiguration = {
BucketARN,
Prefix,
BufferingHints: {
IntervalInSeconds: BufferingIntervalInSeconds,
SizeInMBs: BufferingSizeInMBs
},
CloudWatchLoggingOptions: {
Enabled: true,
LogGroupName: { 'Ref': `${LogicalName}LogGroup` },
LogStreamName: 'firehose'
},
CompressionFormat: 'GZIP',
RoleARN: { 'Fn::GetAtt': [`${LogicalName}Role`, 'Arn'] }
};
const Policy = {
PolicyName: 's3-destination',
PolicyDocument: {
Statement: [{
Effect: 'Allow',
Action: [
's3:PutObject',
's3:GetObject'
],
Resource: {
'Fn::Join': [
'/',
[
BucketARN,
Prefix,
'*'
]
]
}
},
{
Effect: 'Allow',
Action: [
's3:AbortMultipartUpload',
's3:GetBucketLocation',
's3:ListBucket',
's3:ListBucketMultipartUploads'
],
Resource: BucketARN
}]
}
};
this.Resources[`${LogicalName}ErrorAlarm`] = {
Type: 'AWS::CloudWatch::Alarm',
Properties: {
AlarmName,
AlarmDescription,
AlarmActions,
Period,
EvaluationPeriods,
Statistic,
Threshold,
ComparisonOperator,
TreatMissingData,
EvaluateLowSampleCountPercentile,
ExtendedStatistic,
OKActions,
Namespace: 'AWS/Firehose',
Dimensions: [
{
Name: 'FirehoseName',
Value: { 'Ref': LogicalName }
}
],
MetricName: 'DeliveryToS3.DataFreshness'
}
};
Object.assign(this.Resources[LogicalName].Properties, { S3DestinationConfiguration });
this.Resources[`${LogicalName}Role`].Properties.Policies.push(Policy);
}
}
module.exports = S3KinesisFirehose;