Skip to content

Commit

Permalink
feat(s3): add MetricsConfiguration Property to S3 Bucket (#2163)
Browse files Browse the repository at this point in the history
Add metric Property to S3 Bucket for configuring bucket metrics.

You can either specify the metrics as properties:

    new Bucket(stack, 'Bucket', {
      metrics: [{
        id: "test",
        tagFilters: {tagname1: "tagvalue1", tagname2: "tagvalue2"}
      }]
    });

Or use the `addMetric` function:

    const bucket = new Bucket(stack, 'Bucket');
    bucket.addMetric({
      id: "test"
    });
  • Loading branch information
workeitel authored and rix0rrr committed May 8, 2019
1 parent 84fda45 commit 1cc43b3
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 10 deletions.
80 changes: 70 additions & 10 deletions packages/@aws-cdk/aws-s3/lib/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,25 @@ export class BlockPublicAccess {
}
}

/**
* Specifies a metrics configuration for the CloudWatch request metrics from an Amazon S3 bucket.
*/
export interface BucketMetrics {
/**
* The ID used to identify the metrics configuration.
*/
readonly id: string;
/**
* The prefix that an object must have to be included in the metrics results.
*/
readonly prefix?: string;
/**
* Specifies a list of tag filters to use as a metrics configuration filter.
* The metrics configuration includes only objects that meet the filter's criteria.
*/
readonly tagFilters?: {[tag: string]: any};
}

export interface BucketProps {
/**
* The kind of server-side encryption to apply to this bucket.
Expand Down Expand Up @@ -639,6 +658,13 @@ export interface BucketProps {
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html
*/
readonly blockPublicAccess?: BlockPublicAccess;

/**
* The metrics configuration of this bucket.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-metricsconfiguration.html
*/
readonly metrics?: BucketMetrics[];
}

/**
Expand Down Expand Up @@ -720,6 +746,7 @@ export class Bucket extends BucketBase {
private readonly lifecycleRules: LifecycleRule[] = [];
private readonly versioned?: boolean;
private readonly notifications: BucketNotifications;
private readonly metrics: BucketMetrics[] = [];

constructor(scope: Construct, id: string, props: BucketProps = {}) {
super(scope, id);
Expand All @@ -735,7 +762,8 @@ export class Bucket extends BucketBase {
versioningConfiguration: props.versioned ? { status: 'Enabled' } : undefined,
lifecycleConfiguration: new Token(() => this.parseLifecycleConfiguration()),
websiteConfiguration: this.renderWebsiteConfiguration(props),
publicAccessBlockConfiguration: props.blockPublicAccess
publicAccessBlockConfiguration: props.blockPublicAccess,
metricsConfigurations: new Token(() => this.parseMetricConfiguration())
});

applyRemovalPolicy(resource, props.removalPolicy !== undefined ? props.removalPolicy : RemovalPolicy.Orphan);
Expand All @@ -752,6 +780,9 @@ export class Bucket extends BucketBase {

this.disallowPublicAccess = props.blockPublicAccess && props.blockPublicAccess.blockPublicPolicy;

// Add all bucket metric configurations rules
(props.metrics || []).forEach(this.addMetric.bind(this));

// Add all lifecycle rules
(props.lifecycleRules || []).forEach(this.addLifecycleRule.bind(this));

Expand Down Expand Up @@ -791,6 +822,15 @@ export class Bucket extends BucketBase {
this.lifecycleRules.push(rule);
}

/**
* Adds a metrics configuration for the CloudWatch request metrics from the bucket.
*
* @param metric The metric configuration to add
*/
public addMetric(metric: BucketMetrics) {
this.metrics.push(metric);
}

/**
* Adds a bucket notification event destination.
* @param event The event to trigger the notification
Expand Down Expand Up @@ -942,6 +982,8 @@ export class Bucket extends BucketBase {
return undefined;
}

const self = this;

return { rules: this.lifecycleRules.map(parseLifecycleRule) };

function parseLifecycleRule(rule: LifecycleRule): CfnBucket.RuleProperty {
Expand All @@ -958,22 +1000,40 @@ export class Bucket extends BucketBase {
prefix: rule.prefix,
status: enabled ? 'Enabled' : 'Disabled',
transitions: rule.transitions,
tagFilters: parseTagFilters(rule.tagFilters)
tagFilters: self.parseTagFilters(rule.tagFilters)
};

return x;
}
}

function parseTagFilters(tagFilters?: {[tag: string]: any}) {
if (!tagFilters || tagFilters.length === 0) {
return undefined;
}
private parseMetricConfiguration(): CfnBucket.MetricsConfigurationProperty[] | undefined {
if (!this.metrics || this.metrics.length === 0) {
return undefined;
}

return Object.keys(tagFilters).map(tag => ({
key: tag,
value: tagFilters[tag]
}));
const self = this;

return this.metrics.map(parseMetric);

function parseMetric(metric: BucketMetrics): CfnBucket.MetricsConfigurationProperty {
return {
id: metric.id,
prefix: metric.prefix,
tagFilters: self.parseTagFilters(metric.tagFilters)
};
}
}

private parseTagFilters(tagFilters?: {[tag: string]: any}) {
if (!tagFilters || tagFilters.length === 0) {
return undefined;
}

return Object.keys(tagFilters).map(tag => ({
key: tag,
value: tagFilters[tag]
}));
}

private renderWebsiteConfiguration(props: BucketProps): CfnBucket.WebsiteConfigurationProperty | undefined {
Expand Down
112 changes: 112 additions & 0 deletions packages/@aws-cdk/aws-s3/test/test.metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { expect, haveResource } from '@aws-cdk/assert';
import { Stack } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';
import { Bucket } from '../lib';

export = {
'Can use addMetrics() to add a metric configuration'(test: Test) {
// GIVEN
const stack = new Stack();

// WHEN
const bucket = new Bucket(stack, 'Bucket');
bucket.addMetric({
id: "test"
});

// THEN
expect(stack).to(haveResource('AWS::S3::Bucket', {
MetricsConfigurations: [{
Id: "test"
}]
}));

test.done();
},

'Bucket with metrics on prefix'(test: Test) {
// GIVEN
const stack = new Stack();

// WHEN
new Bucket(stack, 'Bucket', {
metrics: [{
id: "test",
prefix: "prefix"
}]
});

// THEN
expect(stack).to(haveResource('AWS::S3::Bucket', {
MetricsConfigurations: [{
Id: "test",
Prefix: "prefix"
}]
}));

test.done();
},

'Bucket with metrics on tag filter'(test: Test) {
// GIVEN
const stack = new Stack();

// WHEN
new Bucket(stack, 'Bucket', {
metrics: [{
id: "test",
tagFilters: {tagname1: "tagvalue1", tagname2: "tagvalue2"}
}]
});

// THEN
expect(stack).to(haveResource('AWS::S3::Bucket', {
MetricsConfigurations: [{
Id: "test",
TagFilters: [
{ Key: "tagname1", Value: "tagvalue1" },
{ Key: "tagname2", Value: "tagvalue2" },
]
}]
}));

test.done();
},

'Bucket with multiple metric configurations'(test: Test) {
// GIVEN
const stack = new Stack();

// WHEN
new Bucket(stack, 'Bucket', {
metrics: [
{
id: "test",
tagFilters: {tagname1: "tagvalue1", tagname2: "tagvalue2"}

},
{
id: "test2",
prefix: "prefix"
},
]
});

// THEN
expect(stack).to(haveResource('AWS::S3::Bucket', {
MetricsConfigurations: [{
Id: "test",
TagFilters: [
{ Key: "tagname1", Value: "tagvalue1" },
{ Key: "tagname2", Value: "tagvalue2" },
]
},
{
Id: "test2",
Prefix: "prefix"
}]
}));

test.done();
},
};

0 comments on commit 1cc43b3

Please sign in to comment.