Skip to content

Commit 961e08a

Browse files
committed
fix(mixins-preview): vended logs multiple
1 parent cc491df commit 961e08a

File tree

17 files changed

+1879
-50
lines changed

17 files changed

+1879
-50
lines changed

packages/@aws-cdk/mixins-preview/lib/services/aws-logs/logs-delivery.ts

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Names, Stack, Tags } from 'aws-cdk-lib/core';
22
import { Effect, PolicyStatement, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
33
import * as logs from 'aws-cdk-lib/aws-logs';
44
import * as s3 from 'aws-cdk-lib/aws-s3';
5-
import type { IConstruct } from 'constructs';
5+
import { Construct, type IConstruct } from 'constructs';
66
import type { IDeliveryStreamRef } from 'aws-cdk-lib/aws-kinesisfirehose';
77
import { tryFindBucketPolicyForBucket } from '../../mixins/private/reflections';
88
import { ConstructSelector, Mixins } from '../../core';
@@ -63,7 +63,7 @@ export interface S3LogsDeliveryProps {
6363
* The permissions version ('V1' or 'V2') to be used for this delivery.
6464
* Depending on the source of the logs, different permissions are required.
6565
*
66-
* @default "V2
66+
* @default "V2"
6767
*/
6868
readonly permissionsVersion?: S3LogsDeliveryPermissionsVersion;
6969
}
@@ -73,37 +73,42 @@ export interface S3LogsDeliveryProps {
7373
*/
7474
export class S3LogsDelivery implements ILogsDelivery {
7575
private readonly bucket: s3.IBucketRef;
76-
private readonly permissions: 'V1' | 'V2';
76+
private readonly permissions: S3LogsDeliveryPermissionsVersion;
7777

7878
/**
7979
* Creates a new S3 Bucket delivery.
8080
*/
8181
constructor(bucket: s3.IBucketRef, props: S3LogsDeliveryProps = {}) {
8282
this.bucket = bucket;
83-
this.permissions = props.permissionsVersion ?? 'V2';
83+
this.permissions = props.permissionsVersion ?? S3LogsDeliveryPermissionsVersion.V2;
8484
}
8585

8686
/**
8787
* Binds the S3 destination to a delivery source and creates a delivery connection between them.
8888
*/
8989
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, _sourceResourceArn: string): ILogsDeliveryConfig {
90-
const bucketPolicy = this.getOrCreateBucketPolicy(scope);
90+
const container = new Construct(scope, deliveryId('S3', scope, this.bucket));
91+
92+
const bucketPolicy = this.getOrCreateBucketPolicy(container);
9193
this.grantLogsDelivery(bucketPolicy);
9294

93-
const destinationNamePrefix = 'cdk-s3-dest-';
94-
const deliveryDestination = new logs.CfnDeliveryDestination(scope, `CDKS3Dest${Names.uniqueId(this.bucket)}${Names.uniqueId(scope)}`, {
95+
const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
9596
destinationResourceArn: this.bucket.bucketRef.bucketArn,
96-
name: `${destinationNamePrefix}${Names.uniqueResourceName(scope, { maxLength: 60 - destinationNamePrefix.length })}`,
97+
name: deliveryDestName('s3', container),
9798
deliveryDestinationType: 'S3',
9899
});
99-
deliveryDestination.node.addDependency(bucketPolicy);
100100

101-
const delivery = new logs.CfnDelivery(scope, `CDKS3Delivery${Names.uniqueId(this.bucket)}${Names.uniqueId(scope)}`, {
101+
const delivery = new logs.CfnDelivery(container, 'Delivery', {
102102
deliveryDestinationArn: deliveryDestination.attrArn,
103103
deliverySourceName: deliverySource.deliverySourceRef.deliverySourceName,
104104
});
105-
delivery.addDependency(deliveryDestination);
105+
106+
delivery.node.addDependency(bucketPolicy);
107+
deliveryDestination.node.addDependency(bucketPolicy);
108+
deliverySource.node.addDependency(bucketPolicy);
109+
106110
delivery.node.addDependency(deliverySource);
111+
delivery.node.addDependency(deliveryDestination);
107112

108113
return {
109114
deliverySource,
@@ -120,7 +125,7 @@ export class S3LogsDelivery implements ILogsDelivery {
120125
private getOrCreateBucketPolicy(scope: IConstruct): s3.CfnBucketPolicy {
121126
const existingPolicy = tryFindBucketPolicyForBucket(this.bucket);
122127

123-
return existingPolicy ?? new s3.CfnBucketPolicy(scope, `CDKS3DestPolicy${Names.uniqueId(this.bucket)}`, {
128+
return existingPolicy ?? new s3.CfnBucketPolicy(scope, 'BucketPolicy', {
124129
bucket: this.bucket.bucketRef.bucketName,
125130
policyDocument: { // needed to create an empty policy document, otherwise a validation error is thrown
126131
Version: '2012-10-17',
@@ -195,23 +200,25 @@ export class FirehoseLogsDelivery implements ILogsDelivery {
195200
* Binds the Firehose destination to a delivery source and creates a delivery connection between them.
196201
*/
197202
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, _sourceResourceArn: string): ILogsDeliveryConfig {
203+
const container = new Construct(scope, deliveryId('Firehose', scope, this.deliveryStream));
204+
198205
// Firehose uses a service-linked role to deliver logs
199206
// This tag marks the destination stream as an allowed destination for the service-linked role
200207
Tags.of(this.deliveryStream).add('LogDeliveryEnabled', 'true');
201208

202-
const destinationNamePrefix = 'cdk-fh-dest-';
203-
const deliveryDestination = new logs.CfnDeliveryDestination(scope, `CDKFHDest${Names.uniqueId(this.deliveryStream)}${Names.uniqueId(scope)}`, {
209+
const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
204210
destinationResourceArn: this.deliveryStream.deliveryStreamRef.deliveryStreamArn,
205-
name: `${destinationNamePrefix}${Names.uniqueResourceName(scope, { maxLength: 60 - destinationNamePrefix.length })}`,
211+
name: deliveryDestName('fh', container),
206212
deliveryDestinationType: 'FH',
207213
});
208214

209-
const delivery = new logs.CfnDelivery(scope, `CDKFHDelivery${Names.uniqueId(this.deliveryStream)}${Names.uniqueId(scope)}`, {
215+
const delivery = new logs.CfnDelivery(container, 'Delivery', {
210216
deliveryDestinationArn: deliveryDestination.attrArn,
211217
deliverySourceName: deliverySource.deliverySourceRef.deliverySourceName,
212218
});
213-
delivery.addDependency(deliveryDestination);
219+
214220
delivery.node.addDependency(deliverySource);
221+
delivery.node.addDependency(deliveryDestination);
215222

216223
return {
217224
deliverySource,
@@ -239,23 +246,28 @@ export class LogGroupLogsDelivery implements ILogsDelivery {
239246
* Binds the log group destination to a delivery source and creates a delivery connection between them.
240247
*/
241248
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, _sourceResourceArn: string): ILogsDeliveryConfig {
242-
const logGroupPolicy = this.getOrCreateLogsResourcePolicy(scope);
249+
const container = new Construct(scope, deliveryId('LogGroup', scope, this.logGroup));
250+
251+
const logGroupPolicy = this.getOrCreateLogsResourcePolicy(container);
243252
this.grantLogsDelivery(logGroupPolicy);
244253

245-
const destinationNamePrefix = 'cdk-cwl-dest-';
246-
const deliveryDestination = new logs.CfnDeliveryDestination(scope, `CDKCWLDest${Names.uniqueId(this.logGroup)}${Names.uniqueId(scope)}`, {
254+
const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
247255
destinationResourceArn: this.logGroup.logGroupRef.logGroupArn,
248-
name: `${destinationNamePrefix}${Names.uniqueResourceName(scope, { maxLength: 60 - destinationNamePrefix.length })}`,
256+
name: deliveryDestName('cwl', container),
249257
deliveryDestinationType: 'CWL',
250258
});
251-
deliveryDestination.node.addDependency(logGroupPolicy);
252259

253-
const delivery = new logs.CfnDelivery(scope, `CDKCWLDelivery${Names.uniqueId(this.logGroup)}${Names.uniqueId(scope)}`, {
254-
deliveryDestinationArn: deliveryDestination.attrArn,
260+
const delivery = new logs.CfnDelivery(container, 'Delivery', {
261+
deliveryDestinationArn: deliveryDestination.deliveryDestinationRef.deliveryDestinationArn,
255262
deliverySourceName: deliverySource.deliverySourceRef.deliverySourceName,
256263
});
257-
delivery.addDependency(deliveryDestination);
264+
265+
delivery.node.addDependency(logGroupPolicy);
258266
delivery.node.addDependency(deliverySource);
267+
delivery.node.addDependency(deliveryDestination);
268+
269+
deliverySource.node.addDependency(logGroupPolicy);
270+
deliveryDestination.node.addDependency(logGroupPolicy);
259271

260272
return {
261273
deliverySource,
@@ -271,13 +283,13 @@ export class LogGroupLogsDelivery implements ILogsDelivery {
271283
*/
272284
private getOrCreateLogsResourcePolicy(scope: IConstruct): logs.ResourcePolicy {
273285
const stack = Stack.of(scope);
274-
const policyId = 'CDKCWLDestPolicy';
286+
const policyId = 'CdkLogGroupLogsDeliveryPolicy';
275287

276288
// Singleton policy per stack
277289
const existingPolicy = stack.node.tryFindChild(policyId) as logs.ResourcePolicy;
278290

279291
return existingPolicy ?? new logs.ResourcePolicy(stack, policyId, {
280-
resourcePolicyName: 'LogGroupLogsDeliveryPolicy',
292+
resourcePolicyName: Names.uniqueResourceName(scope, { maxLength: 255 }),
281293
});
282294
}
283295

@@ -318,21 +330,27 @@ export class XRayLogsDelivery implements ILogsDelivery {
318330
* Binds the X-Ray destination to a delivery source and creates a delivery connection between them.
319331
*/
320332
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, sourceResourceArn: string): ILogsDeliveryConfig {
321-
const xrayResourcePolicy = this.getOrCreateResourcePolicy(scope);
333+
const container = new Construct(scope, deliveryId('XRay', scope, deliverySource));
334+
335+
const xrayResourcePolicy = this.getOrCreateResourcePolicy(container);
322336
this.grantLogsDelivery(xrayResourcePolicy, sourceResourceArn);
323337

324-
const destinationNamePrefix = 'cdk-xray-dest-';
325-
const deliveryDestination = new logs.CfnDeliveryDestination(scope, `CDKXRayDest${Names.uniqueId(scope)}`, {
326-
name: `${destinationNamePrefix}${Names.uniqueResourceName(scope, { maxLength: 60 - destinationNamePrefix.length })}`,
338+
const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
339+
name: deliveryDestName('xray', container),
327340
deliveryDestinationType: 'XRAY',
328341
});
329342

330-
const delivery = new logs.CfnDelivery(scope, `CDKXRAYDelivery${Names.uniqueId(scope)}`, {
343+
const delivery = new logs.CfnDelivery(container, 'Delivery', {
331344
deliveryDestinationArn: deliveryDestination.attrArn,
332345
deliverySourceName: deliverySource.deliverySourceRef.deliverySourceName,
333346
});
334-
delivery.addDependency(deliveryDestination);
347+
348+
delivery.node.addDependency(xrayResourcePolicy);
349+
deliveryDestination.node.addDependency(xrayResourcePolicy);
350+
deliverySource.node.addDependency(xrayResourcePolicy);
351+
335352
delivery.node.addDependency(deliverySource);
353+
delivery.node.addDependency(deliveryDestination);
336354

337355
return {
338356
deliverySource,
@@ -349,14 +367,12 @@ export class XRayLogsDelivery implements ILogsDelivery {
349367
*/
350368
private getOrCreateResourcePolicy(scope: IConstruct): xray.ResourcePolicy {
351369
const stack = Stack.of(scope);
352-
const policyId = 'CDKXRAYDestPolicy';
370+
const policyId = 'CdkXRayLogsDeliveryPolicy';
353371

354372
// Singleton policy per stack
355373
const existingPolicy = stack.node.tryFindChild(policyId) as xray.ResourcePolicy;
356374

357-
return existingPolicy ?? new xray.ResourcePolicy(stack, policyId, {
358-
resourcePolicyName: 'XRayLogsDeliveryPolicy',
359-
});
375+
return existingPolicy ?? new xray.ResourcePolicy(stack, policyId);
360376
}
361377

362378
/**
@@ -386,3 +402,12 @@ export class XRayLogsDelivery implements ILogsDelivery {
386402
}));
387403
}
388404
}
405+
406+
function deliveryId(type: string, ...scopes: IConstruct[]) {
407+
return `Cdk${type}Delivery${scopes.map(s => Names.uniqueId(s)).join('')}`;
408+
}
409+
410+
function deliveryDestName(type: string, scope: IConstruct) {
411+
const prefix = `cdk-${type}-dest-`;
412+
return `${prefix}${Names.uniqueResourceName(scope, { maxLength: 60 - prefix.length })}`;
413+
}

packages/@aws-cdk/mixins-preview/lib/services/aws-xray/policy.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ export class ResourcePolicy extends Resource {
4949

5050
const l1 = new CfnResourcePolicy(this, 'ResourcePolicy', {
5151
policyName: Lazy.string({
52-
produce: () => props?.resourcePolicyName ?? Names.uniqueId(this),
52+
produce: () => props?.resourcePolicyName ?? Names.uniqueResourceName(this, {
53+
maxLength: 128,
54+
allowedSpecialCharacters: '+=,.@-',
55+
}),
5356
}),
5457
policyDocument: Lazy.string({
5558
produce: () => JSON.stringify(this.document),

packages/@aws-cdk/mixins-preview/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@
597597
"watch": "cdk-watch",
598598
"lint": "cdk-lint",
599599
"test": "cdk-test",
600+
"integ": "integ-runner --language typescript",
600601
"pkglint": "pkglint -f",
601602
"package": "cdk-package",
602603
"awslint": "cdk-awslint",
@@ -623,8 +624,10 @@
623624
"license": "Apache-2.0",
624625
"devDependencies": {
625626
"@aws-cdk/cdk-build-tools": "0.0.0",
626-
"@aws-cdk/pkglint": "0.0.0",
627627
"@aws-cdk/custom-resource-handlers": "0.0.0",
628+
"@aws-cdk/integ-runner": "^2.192.2",
629+
"@aws-cdk/integ-tests-alpha": "0.0.0",
630+
"@aws-cdk/pkglint": "0.0.0",
628631
"@aws-cdk/service-spec-types": "^0.0.189",
629632
"@aws-cdk/spec2cdk": "0.0.0",
630633
"@cdklabs/tskb": "^0.0.4",

packages/@aws-cdk/mixins-preview/test/mixin.test.ts renamed to packages/@aws-cdk/mixins-preview/test/core/mixin.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Construct } from 'constructs';
2-
import { Mixin } from '../lib/core';
2+
import { Mixin } from '../../lib/core';
33

44
class TestConstruct extends Construct {
55
constructor(scope: Construct, id: string) {

packages/@aws-cdk/mixins-preview/test/integration.test.ts renamed to packages/@aws-cdk/mixins-preview/test/mixins/full-example.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import * as logs from 'aws-cdk-lib/aws-logs';
55
import {
66
Mixins,
77
ConstructSelector,
8-
} from '../lib/core';
9-
import * as s3Mixins from '../lib/services/aws-s3/mixins';
8+
} from '../../lib/core';
9+
import * as s3Mixins from '../../lib/services/aws-s3/mixins';
1010

1111
describe('Integration Tests', () => {
1212
let app: App;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import * as cdk from 'aws-cdk-lib/core';
2+
import * as integ from '@aws-cdk/integ-tests-alpha';
3+
import * as s3 from 'aws-cdk-lib/aws-s3';
4+
import * as iam from 'aws-cdk-lib/aws-iam';
5+
import * as logs from 'aws-cdk-lib/aws-logs';
6+
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
7+
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
8+
import * as firehose from 'aws-cdk-lib/aws-kinesisfirehose';
9+
import { FirehoseLogsDelivery, LogGroupLogsDelivery, S3LogsDelivery } from '../../../lib/services/aws-logs/logs-delivery';
10+
11+
const app = new cdk.App();
12+
13+
const stack = new cdk.Stack(app, 'VendedLogsTest');
14+
15+
// Source Resource
16+
const cloudfrontBucket = new s3.Bucket(stack, 'OriginBucket', {
17+
removalPolicy: cdk.RemovalPolicy.DESTROY,
18+
autoDeleteObjects: true,
19+
});
20+
const distribution = new cloudfront.Distribution(stack, 'Distribution', {
21+
defaultBehavior: {
22+
origin: origins.S3BucketOrigin.withOriginAccessControl(cloudfrontBucket),
23+
},
24+
});
25+
26+
// Delivery Source
27+
const deliverySource = new logs.CfnDeliverySource(stack, 'CloudFrontSource', {
28+
name: `delivery-source-${distribution.distributionId}-ACCESS_LOGS`,
29+
resourceArn: distribution.distributionArn,
30+
logType: 'ACCESS_LOGS',
31+
});
32+
33+
// Destinations
34+
const destinationBucket = new s3.Bucket(stack, 'DeliveryBucket', {
35+
removalPolicy: cdk.RemovalPolicy.DESTROY,
36+
autoDeleteObjects: true,
37+
});
38+
const destinationLogGroup = new logs.LogGroup(stack, 'DeliveryLogGroup', {
39+
removalPolicy: cdk.RemovalPolicy.DESTROY,
40+
});
41+
42+
const firehoseBucket = new s3.Bucket(stack, 'FirehoseBucket', {
43+
removalPolicy: cdk.RemovalPolicy.DESTROY,
44+
autoDeleteObjects: true,
45+
});
46+
47+
const deliveryStream = new firehose.CfnDeliveryStream(stack, 'DeliveryStream', {
48+
s3DestinationConfiguration: {
49+
bucketArn: firehoseBucket.bucketArn,
50+
bufferingHints: {
51+
intervalInSeconds: 10,
52+
sizeInMBs: 1,
53+
},
54+
roleArn: new iam.Role(stack, 'FirehoseRole', {
55+
assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'),
56+
inlinePolicies: {
57+
s3: new iam.PolicyDocument({
58+
statements: [new iam.PolicyStatement({
59+
actions: ['s3:PutObject'],
60+
resources: [firehoseBucket.arnForObjects('*')],
61+
})],
62+
}),
63+
},
64+
}).roleArn,
65+
},
66+
});
67+
68+
// Setup deliveries
69+
new S3LogsDelivery(destinationBucket).bind(stack, deliverySource, distribution.distributionArn);
70+
new LogGroupLogsDelivery(destinationLogGroup).bind(stack, deliverySource, distribution.distributionArn);
71+
new FirehoseLogsDelivery(deliveryStream).bind(stack, deliverySource, distribution.distributionArn);
72+
73+
new integ.IntegTest(app, 'DeliveryTest', {
74+
testCases: [stack],
75+
});

packages/@aws-cdk/mixins-preview/test/services/aws-logs/integ.delivery.ts.snapshot/DeliveryTestDefaultTestDeployAssert48416031.assets.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)