-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
feat(lambda): allow to delete log group of a lambda function when it is removed #21820
Conversation
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.
Thank you for your contribution. Please see my comments inline.
* | ||
* @default false | ||
*/ | ||
readonly autoDeleteLogGroup?: boolean; |
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 inconsistent with our contract elsewhere when the user sets the Removal Policy. Please see other places where we have done this and follow that model. You use the enum below.
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.
Hi @TheRealAmazonKendra thank you for the review!
This API is actually inspired by s3.Bucket.autoDeleteObjects
. In this case the only concern for developers is whether the log group is automatically deleted or not. It is only a binary choice and there is no need to use RemovalPolicy enum to specify that. One of the main advantages of CDK constructs is abstraction, and here we abstract away RemovalPolicy to provide better developer experience.
Also internally we do not use CloudFormation RemovalPolicy here but using a feature implemented in the LogRetention custom resource below. I'm not sure if it is inconsistent to use the different contract from other CFn removal policies. Isn't it natural that different things will have different contracts?
RemovalPolicy: props.removalPolicy, |
I'm open to discussion about this :) currently I don't see much benefits to use enum here though.
Pull request has been modified.
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.
#21113 is the experience this should be consistent with. In fact, we should be using LogRetentionProps here instead of the individual props. As a CDK team member, I understand that abstraction is a key advantage of constructs. Consistent user experience is also an important.
Hi @TheRealAmazonKendra I really appreciate your review and effort you expended on my PR! So let me summarize the discussion so far. I assume there are currently three possible options to be considered: // #1
// Pros: Less typing. Directly expressing developer's intent.
// Cons: Due to more abstraction, the API becomes different from LogRetention construct.
new lambda.Function(stack, 'AutoDeleteLogGroup', {
code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_14_X,
autoDeleteLogGroup: true,
});
// #2
// Pros: Similar API to LogRetention construct.
// Cons: * Requires more typing. We have to import aws-cdk-lib.RemovalPolicy, and the property name seems a little redundant.
// * We can also pass RemovalPolicy.SNAPSHOT although it means nothing here, which is unnecessarily confusing.
// * It may look like removal policy of Lambda function itself, which is also misleading.
new lambda.Function(stack, 'AutoDeleteLogGroup', {
code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_14_X,
logRetentionRemovalPolicy: RemovalPolicy.DESTROY,
});
// #3
// If we go with this approach we need much refactor work. I guess we can ignore this approach for now.
new lambda.Function(stack, 'AutoDeleteLogGroup', {
code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_14_X,
logRetentionProps: {
// also move other logRetention* props here
removalPolicy: RemovalPolicy.DESTROY,
},
}); I agree with API consistency is important, and I somewhat agree with that we need to be consistent with props of internal constructs. But What we also need to be care about is the consistency between constructs that are using To be honest after the release of this feature I’ll be specifying this option every time I create a Lambda function. And that's why I’m inclined to keep this API as simple as possible... I'd like to avoid to specify cdk.RemovalPolicy repetitively where a single boolean is enough. As a side note, I asked my colleagues about this today and most of them prefer #1 over #2 due to its simplicity. That being said I'm willing to follow your final decision. Considering all of the above, how do you think about #1 and #2? And sorry for writing such a long message with my poor English. Let me know if there are any sentences that does not make sense! Thanks. |
Pull request has been modified.
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.
#3 is the direction I think is the right one, though I wouldn't necessarily call the field logRetentionProps
, it just needs to be of the type LogRetentionProps
. This way, the user gets access to all the functionality there within. The only change in logs
is that logGroupName
now needs to be optional. This is the contract that should have existed from the beginning so we're not rewriting the same stuff over and over again. I'm not sure why we deviated here, but I think that was a mistake. If we're changing the contract now, it should be to fix that and not further deviate.
@TheRealAmazonKendra If we are to fully replace the existing contract design, and if the contract consistency is a top priority, how about adding a new interface to set log retention? e.g. interface ILoggable {
setLogRetention(props: LogRetentionProps): LogRetention;
logGroup: ILogGroup;
}
class Function implements ILoggable {
setLogRetention(props: LogRetentionProps) {
// ...
}
}
const func = new lambda.Function(stack, 'AutoDeleteLogGroup', {
code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_14_X,
});
func.setLogRetention({ removalPolicy: RemovalPolicy.DESTROY }) As long as they implement this interface, the API will be ensured to be consistent between constructs. However I'm not sure if the above approach will work well with some constructs such as AppSync.GraphqlApi, which seemingly want to provide optimized experience by defining their own props (e.g. LogConfig). I guess we also need to discuss with people who are developing the constructs? Also I think we should, ideally, define a dedicated props interface for |
Hi @TheRealAmazonKendra |
This PR has been in the CHANGES REQUESTED state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week. |
Pull request has been modified.
@tmokmss Please could you advise on the current state of this PR (e.g. active, blocked or abandoned)? |
Ping - We're hoping to avoid implementing a hacky workaround for the most obvious non-working solution. |
This PR has been in the MERGE CONFLICTS state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week. |
This PR has been in the MERGE CONFLICTS state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week. |
This PR has been in the MERGE CONFLICTS state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week. |
This PR cannot be merged because it has conflicts. Please resolve them. The PR will be considered stale and closed if it remains in an unmergeable state. |
@tmokmss Apologies for the delay. Let's wrap up the open questions:
The API for
Yes, but that's already implemented in the CR anyway. I also have one more thing I'd like to clarify:
I don't see how this is happening with the code you have on the PR. Is this still the case, if so can you help me understand how? |
Hi @mrgrain, thanks for the review!
Sure. Let's say we have the following template deployed. new lambda.Function(stack, 'Function', {
code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_14_X,
autoDeleteLogGroup: true,
}); The CFn template for this code looks like this: "FunctionF74A7E0D": {
"Type": "AWS::Lambda::Function",
"Properties": { ...
}
},
"FunctionLogRetentionAC501FFB": {
"Type": "Custom::LogRetention",
"Properties": {
...
"RemovalPolicy": "destroy"
}
} Now if we remove the autoDeleteLogGroup property: new lambda.Function(stack, 'Function', {
code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_14_X,
- autoDeleteLogGroup: true,
}); the CFn template will look like this: "FunctionF74A7E0D": {
"Type": "AWS::Lambda::Function",
"Properties": { ...
}
},
- "FunctionLogRetentionAC501FFB": {
- "Type": "Custom::LogRetention",
- "Properties": {
- ...
- "RemovalPolicy": "destroy"
- }
- } Note that only the log retention resource is deleted, and the function remains as-is. Here is where the unexpected behavior comes in. When a aws-cdk/packages/aws-cdk-lib/aws-logs/lib/log-retention-provider/index.ts Lines 156 to 160 in 6c588da
Actually there is no way for the custom resource to know if the corresponding function is removed or not. I thought we could query the Lambda service if the function exists or not, but that is also difficult, because of the dependency between a LogRetention and a function. A LogRetention depends on a corresponding function (to get the function name), so it must be deleted before the function. aws-cdk/packages/aws-cdk-lib/aws-lambda/lib/function.ts Lines 868 to 869 in 7c7697e
I still has not found a good solution for this problem, but any suggestion is welcome! :) I think the easiest approach is just to accept this behavior as expected, and document details about this. |
@tmokmss Thanks for the explanation. I understand the problem now. I think the issue comes down to creating a false equality between the Not quite sure what the solution would be for all of these... 🤔 |
@mrgrain Another solution would be to always define a LogRetention custom resource regardless of a function's properties. Then even if we remove those properties, the log retention resource and the log group will still be retained. I hesitated to do this because it would require updates to many many existing integ tests. But If other features are also facing this problem, it might be reasonable to change the current behavior. |
Been thinking about this all morning. It's fairly easy to implement a variation of the fix that S3's |
@mrgrain ok, thanks! I'm looking forward to that:) |
I'm planning to create a generic |
Nice! I just found issue #16756 and the discussions there appear to cover most related considerations. After reading them, I'm now convinced that tagging a parent resource is (maybe only) a viable solution. (I didn't know the special ordering of tags.) Looking up the current deployed template (reverted in d220b94) is interesting but I guess it will also requires special handling for each parent resource type anyway. |
This PR has been in the MERGE CONFLICTS state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week. |
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
No more orphaned log groups after your trial and error!
closes #21804
Now we can set
autoDeleteLogGroup: true
to delete an associated Lambda log automatically.Consideration on property removal
If a user remove
autoDeleteLogGroup
property as below, the logRetention resource will be removed from a synthesized template, and it will result in the removal of log group itself. This implicit behavior should be clearly warned in the doc.new lambda.Function(stack, 'AutoDeleteLogGroup', { code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_14_X, - autoDeleteLogGroup: true, });
There was an issue regarding s3 Bucket's
autoDeleteObjects
feature: #16603. We might need to implement a similar approach. It might be difficult since we are not directly managing LogGroup in CFn though.All Submissions:
Adding new Unconventional Dependencies:
New Features
yarn integ
to deploy the infrastructure and generate the snapshot (i.e.yarn integ
without--dry-run
)?By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license