Skip to content

Commit 1cc43b3

Browse files
workeitelrix0rrr
authored andcommitted
feat(s3): add MetricsConfiguration Property to S3 Bucket (#2163)
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" });
1 parent 84fda45 commit 1cc43b3

File tree

2 files changed

+182
-10
lines changed

2 files changed

+182
-10
lines changed

packages/@aws-cdk/aws-s3/lib/bucket.ts

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,25 @@ export class BlockPublicAccess {
564564
}
565565
}
566566

567+
/**
568+
* Specifies a metrics configuration for the CloudWatch request metrics from an Amazon S3 bucket.
569+
*/
570+
export interface BucketMetrics {
571+
/**
572+
* The ID used to identify the metrics configuration.
573+
*/
574+
readonly id: string;
575+
/**
576+
* The prefix that an object must have to be included in the metrics results.
577+
*/
578+
readonly prefix?: string;
579+
/**
580+
* Specifies a list of tag filters to use as a metrics configuration filter.
581+
* The metrics configuration includes only objects that meet the filter's criteria.
582+
*/
583+
readonly tagFilters?: {[tag: string]: any};
584+
}
585+
567586
export interface BucketProps {
568587
/**
569588
* The kind of server-side encryption to apply to this bucket.
@@ -639,6 +658,13 @@ export interface BucketProps {
639658
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html
640659
*/
641660
readonly blockPublicAccess?: BlockPublicAccess;
661+
662+
/**
663+
* The metrics configuration of this bucket.
664+
*
665+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-metricsconfiguration.html
666+
*/
667+
readonly metrics?: BucketMetrics[];
642668
}
643669

644670
/**
@@ -720,6 +746,7 @@ export class Bucket extends BucketBase {
720746
private readonly lifecycleRules: LifecycleRule[] = [];
721747
private readonly versioned?: boolean;
722748
private readonly notifications: BucketNotifications;
749+
private readonly metrics: BucketMetrics[] = [];
723750

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

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

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

783+
// Add all bucket metric configurations rules
784+
(props.metrics || []).forEach(this.addMetric.bind(this));
785+
755786
// Add all lifecycle rules
756787
(props.lifecycleRules || []).forEach(this.addLifecycleRule.bind(this));
757788

@@ -791,6 +822,15 @@ export class Bucket extends BucketBase {
791822
this.lifecycleRules.push(rule);
792823
}
793824

825+
/**
826+
* Adds a metrics configuration for the CloudWatch request metrics from the bucket.
827+
*
828+
* @param metric The metric configuration to add
829+
*/
830+
public addMetric(metric: BucketMetrics) {
831+
this.metrics.push(metric);
832+
}
833+
794834
/**
795835
* Adds a bucket notification event destination.
796836
* @param event The event to trigger the notification
@@ -942,6 +982,8 @@ export class Bucket extends BucketBase {
942982
return undefined;
943983
}
944984

985+
const self = this;
986+
945987
return { rules: this.lifecycleRules.map(parseLifecycleRule) };
946988

947989
function parseLifecycleRule(rule: LifecycleRule): CfnBucket.RuleProperty {
@@ -958,22 +1000,40 @@ export class Bucket extends BucketBase {
9581000
prefix: rule.prefix,
9591001
status: enabled ? 'Enabled' : 'Disabled',
9601002
transitions: rule.transitions,
961-
tagFilters: parseTagFilters(rule.tagFilters)
1003+
tagFilters: self.parseTagFilters(rule.tagFilters)
9621004
};
9631005

9641006
return x;
9651007
}
1008+
}
9661009

967-
function parseTagFilters(tagFilters?: {[tag: string]: any}) {
968-
if (!tagFilters || tagFilters.length === 0) {
969-
return undefined;
970-
}
1010+
private parseMetricConfiguration(): CfnBucket.MetricsConfigurationProperty[] | undefined {
1011+
if (!this.metrics || this.metrics.length === 0) {
1012+
return undefined;
1013+
}
9711014

972-
return Object.keys(tagFilters).map(tag => ({
973-
key: tag,
974-
value: tagFilters[tag]
975-
}));
1015+
const self = this;
1016+
1017+
return this.metrics.map(parseMetric);
1018+
1019+
function parseMetric(metric: BucketMetrics): CfnBucket.MetricsConfigurationProperty {
1020+
return {
1021+
id: metric.id,
1022+
prefix: metric.prefix,
1023+
tagFilters: self.parseTagFilters(metric.tagFilters)
1024+
};
1025+
}
1026+
}
1027+
1028+
private parseTagFilters(tagFilters?: {[tag: string]: any}) {
1029+
if (!tagFilters || tagFilters.length === 0) {
1030+
return undefined;
9761031
}
1032+
1033+
return Object.keys(tagFilters).map(tag => ({
1034+
key: tag,
1035+
value: tagFilters[tag]
1036+
}));
9771037
}
9781038

9791039
private renderWebsiteConfiguration(props: BucketProps): CfnBucket.WebsiteConfigurationProperty | undefined {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { expect, haveResource } from '@aws-cdk/assert';
2+
import { Stack } from '@aws-cdk/cdk';
3+
import { Test } from 'nodeunit';
4+
import { Bucket } from '../lib';
5+
6+
export = {
7+
'Can use addMetrics() to add a metric configuration'(test: Test) {
8+
// GIVEN
9+
const stack = new Stack();
10+
11+
// WHEN
12+
const bucket = new Bucket(stack, 'Bucket');
13+
bucket.addMetric({
14+
id: "test"
15+
});
16+
17+
// THEN
18+
expect(stack).to(haveResource('AWS::S3::Bucket', {
19+
MetricsConfigurations: [{
20+
Id: "test"
21+
}]
22+
}));
23+
24+
test.done();
25+
},
26+
27+
'Bucket with metrics on prefix'(test: Test) {
28+
// GIVEN
29+
const stack = new Stack();
30+
31+
// WHEN
32+
new Bucket(stack, 'Bucket', {
33+
metrics: [{
34+
id: "test",
35+
prefix: "prefix"
36+
}]
37+
});
38+
39+
// THEN
40+
expect(stack).to(haveResource('AWS::S3::Bucket', {
41+
MetricsConfigurations: [{
42+
Id: "test",
43+
Prefix: "prefix"
44+
}]
45+
}));
46+
47+
test.done();
48+
},
49+
50+
'Bucket with metrics on tag filter'(test: Test) {
51+
// GIVEN
52+
const stack = new Stack();
53+
54+
// WHEN
55+
new Bucket(stack, 'Bucket', {
56+
metrics: [{
57+
id: "test",
58+
tagFilters: {tagname1: "tagvalue1", tagname2: "tagvalue2"}
59+
}]
60+
});
61+
62+
// THEN
63+
expect(stack).to(haveResource('AWS::S3::Bucket', {
64+
MetricsConfigurations: [{
65+
Id: "test",
66+
TagFilters: [
67+
{ Key: "tagname1", Value: "tagvalue1" },
68+
{ Key: "tagname2", Value: "tagvalue2" },
69+
]
70+
}]
71+
}));
72+
73+
test.done();
74+
},
75+
76+
'Bucket with multiple metric configurations'(test: Test) {
77+
// GIVEN
78+
const stack = new Stack();
79+
80+
// WHEN
81+
new Bucket(stack, 'Bucket', {
82+
metrics: [
83+
{
84+
id: "test",
85+
tagFilters: {tagname1: "tagvalue1", tagname2: "tagvalue2"}
86+
87+
},
88+
{
89+
id: "test2",
90+
prefix: "prefix"
91+
},
92+
]
93+
});
94+
95+
// THEN
96+
expect(stack).to(haveResource('AWS::S3::Bucket', {
97+
MetricsConfigurations: [{
98+
Id: "test",
99+
TagFilters: [
100+
{ Key: "tagname1", Value: "tagvalue1" },
101+
{ Key: "tagname2", Value: "tagvalue2" },
102+
]
103+
},
104+
{
105+
Id: "test2",
106+
Prefix: "prefix"
107+
}]
108+
}));
109+
110+
test.done();
111+
},
112+
};

0 commit comments

Comments
 (0)