-
Notifications
You must be signed in to change notification settings - Fork 4.3k
chore(mixins-preview): delivery destinations for vended logs #36087
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| interface IDeliveryDestination extends IResource { | ||
| readonly destinationArn: string; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this, we can use the existing IDeliveryDestinationRef
| interface IDeliveryDestination extends IResource { | |
| readonly destinationArn: string; | |
| } | |
| interface IDeliveryDestination extends IResource { | |
| readonly destinationArn: string; | |
| } |
| readonly destinationArn: string; | ||
| } | ||
|
|
||
| abstract class DeliveryDestinationBase extends Resource implements IDeliveryDestination { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| abstract class DeliveryDestinationBase extends Resource implements IDeliveryDestination { | |
| abstract class DeliveryDestinationBase extends Resource implements IDeliveryDestinationRef { |
|
|
||
| export class S3DeliveryDestination extends DeliveryDestinationBase { | ||
| public readonly destinationArn: string; | ||
| constructor(scope: Construct, id: string, props: DeliveryDestinationProps & Required<Pick<DeliveryDestinationProps, 's3Bucket'>>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't compile in jsii. Really we need 3 separate Props objects.
| const bucketPolicy = new CfnBucketPolicy(scope, 'S3Policy', { | ||
| bucket: props.s3Bucket.bucketName, | ||
| policyDocument: bucketPolicyDoc.toJSON(), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a fun side quest: We need a helper function, that for a given Stack and IBucketRef goes through the whole stack and tries to find an existing CfnBucketPolicy for that IBucketRef.
If we find one, we use that. Otherwise we create a new one.
| const destinationNamePrefix = 's3-delivery-destination-'; | ||
| const deliveryDestination = new CfnDeliveryDestination(scope, 'S3DeliveryDestination', { | ||
| destinationResourceArn: props.s3Bucket.bucketArn, | ||
| name: `${destinationNamePrefix}${Names.uniqueResourceName(props.s3Bucket, { maxLength: 60 - destinationNamePrefix.length })}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
| policyDocument: bucketPolicyDoc.toJSON(), | ||
| }); | ||
|
|
||
| const destinationNamePrefix = 's3-delivery-destination-'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's find a shorter prefix. Maybe something with cdk
| constructor(scope: Construct, id: string, props: DeliveryDestinationProps & Required<Pick<DeliveryDestinationProps, 'logGroup'>>) { | ||
| super(scope, id); | ||
|
|
||
| const logGroupPolicy = new CfnResourcePolicy(scope, 'CloudwatchDeliveryPolicy', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this one I'd like us to create a new singleton resource policy for delivery destination. We will getOrCreate that policy and than add statements to it.
This is so that when a user has multiple CloudwatchDeliveryDestinations in their stack, we only ever add a single policy. Because they are limited to max 10 per account.
| } | ||
| } | ||
|
|
||
| export class CloudwatchDeliveryDestination extends DeliveryDestinationBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one I think would be this though if we follow the service name plus stuff pattern.
| export class CloudwatchDeliveryDestination extends DeliveryDestinationBase { | |
| export class LogsDeliveryDestination extends DeliveryDestinationBase { |
| bucketPolicyDoc.addStatements(v1BucketPolicy); | ||
| } | ||
|
|
||
| const bucketPolicy = new CfnBucketPolicy(scope, 'S3Policy', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are allowed to use a L2 BucketPolicy here if it helps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think addDependency works with L2s, since currently I am using addDependency to ensure we don't create our deliveryDestination before it has sufficient permissions. However, considering we want our policies to largely be singletons, I may have to rethink the use of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tangent: Curious about the problem here, this might be another L1/L2 paper-cut we should be fixing.
mrgrain
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also needs unit tests ;)
|
|
||
| interface DeliveryDestinationProps { | ||
| readonly permissionsVersion: 'V1' | 'V2'; | ||
| readonly destinationService: 'S3' | 'CWL' | 'FH' | 'XRAY'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is very redundant we know which service based on the class
| } | ||
|
|
||
| interface DeliveryDestinationProps { | ||
| readonly permissionsVersion: 'V1' | 'V2'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is only used for S3, right?
| this.deliveryDestinationRef = deliveryDestination.deliveryDestinationRef; | ||
| } | ||
|
|
||
| getOrCreateBucketPolicy(stack: Construct, bucketProps: S3DestinationProps): BucketPolicy { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you move this search part into a helper function in a separate file please and than make sure you have good test coverage.
|
|
||
| getOrCreateBucketPolicy(stack: Construct, bucketProps: S3DestinationProps): BucketPolicy { | ||
| const allConstructs = stack.node.findAll(); | ||
| const bucketPolicies = allConstructs.filter(construct => construct instanceof BucketPolicy) as BucketPolicy[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't assume there's an L2. Look for the L1 instead.
| const allConstructs = stack.node.findAll(); | ||
| const resourcePolicies = allConstructs.filter( | ||
| construct => construct instanceof ResourcePolicy && | ||
| CfnResource.isCfnResource(construct.node.defaultChild) && | ||
| construct.node.defaultChild.cfnResourceType === 'AWS::Logs::ResourcePolicy', | ||
| ) as ResourcePolicy[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because Log Resource Policies are not attached to a resource we can use a normal singleton pattern. have a look how this is done elsewhere in the CDK
|
|
||
| export class XRayDeliveryDestination extends DeliveryDestinationBase { | ||
| public readonly deliveryDestinationRef: DeliveryDestinationReference; | ||
| constructor(scope: Construct, id: string, props: DeliveryDestinationProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| constructor(scope: Construct, id: string, props: DeliveryDestinationProps) { | |
| constructor(scope: IConstruct, id: string, props: DeliveryDestinationProps) { |
|
|
||
| export class LogsDeliveryDestination extends DeliveryDestinationBase { | ||
| public readonly deliveryDestinationRef: DeliveryDestinationReference; | ||
| constructor(scope: Construct, id: string, props: LogsDestinationProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| constructor(scope: Construct, id: string, props: LogsDestinationProps) { | |
| constructor(scope: IConstruct, id: string, props: LogsDestinationProps) { |
|
|
||
| export class S3DeliveryDestination extends DeliveryDestinationBase { | ||
| public readonly deliveryDestinationRef: DeliveryDestinationReference; | ||
| constructor(scope: Construct, id: string, props: S3DestinationProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure we want IConstruct where ever possible. I might be wrong though :D
| constructor(scope: Construct, id: string, props: S3DestinationProps) { | |
| constructor(scope: IConstruct, id: string, props: S3DestinationProps) { |
|
|
||
| const destinationNamePrefix = 'cdk-xray-dest-'; | ||
| const deliveryDestination = new CfnDeliveryDestination(scope, 'CDKXRayDeliveryDestination', { | ||
| name: `${destinationNamePrefix}${Names.uniqueResourceName(scope, { maxLength: 60 - destinationNamePrefix.length })}`, // there is no resource for XRays |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it work if you do it like that?
| name: `${destinationNamePrefix}${Names.uniqueResourceName(scope, { maxLength: 60 - destinationNamePrefix.length })}`, // there is no resource for XRays | |
| name: `${destinationNamePrefix}${Names.uniqueResourceName(this, { maxLength: 60 - destinationNamePrefix.length })}`, // there is no resource for XRays |
Reason for this change
Adds ground work for vended logs mixin.
Description of changes
Sets up mini-constructs to be used by the mixin that assists in setting up vended logs.
Max length of name for delivery destination is 60 characters (see: https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutDeliveryDestination.html).
Describe any new or updated permissions being added
Description of how you validated changes
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license