Skip to content
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(synthetics): enable auto delete lambdas via custom resource #26580

Merged
merged 29 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
54da8ea
add synthetics custom resource
kaizencc Jul 31, 2023
9f13e59
bring synthetics cr to module
kaizencc Jul 31, 2023
e016d41
use new custom resource
kaizencc Jul 31, 2023
c2f8456
rename lambdas into lambda
kaizencc Aug 1, 2023
0685168
add unit tests to custom resource
kaizencc Aug 1, 2023
7953311
Merge branch 'main' into conroy/synth-delete
kaizencc Aug 1, 2023
8064486
readme update
kaizencc Aug 1, 2023
afb18e1
Merge branch 'conroy/synth-delete' of https://github.com/kaizencc/aws…
kaizencc Aug 1, 2023
151e026
typo
kaizencc Aug 1, 2023
d18666d
update tests
kaizencc Aug 1, 2023
39a1fd6
new integ test
kaizencc Aug 2, 2023
e50a065
Merge branch 'main' into conroy/synth-delete
kaizencc Aug 2, 2023
c7a1661
Merge branch 'main' into conroy/synth-delete
kaizencc Aug 2, 2023
bc7cd00
update auto-delete-lambda-handler
kaizencc Aug 18, 2023
548c860
update aws-synthetics-alpha
kaizencc Aug 18, 2023
832c088
update integ test
kaizencc Aug 18, 2023
60ddf20
new cleanup property
kaizencc Aug 20, 2023
3aa5ef3
tests
kaizencc Aug 20, 2023
2832a41
integ tests
kaizencc Aug 20, 2023
2bbd553
Merge branch 'main' into conroy/synth-delete
kaizencc Aug 20, 2023
66d5625
yarnlock
kaizencc Aug 20, 2023
44de1fc
readme
kaizencc Aug 20, 2023
f878a63
rename for underlying resources
kaizencc Aug 20, 2023
e4f938c
Update packages/@aws-cdk/custom-resource-handlers/README.md
kaizencc Aug 20, 2023
42c95c5
Merge branch 'main' into conroy/synth-delete
kaizencc Aug 22, 2023
7b103fa
pr feedback
kaizencc Aug 22, 2023
0f39e57
fix typo and update snapshots
kaizencc Aug 22, 2023
2148463
one last typo ugh
kaizencc Aug 22, 2023
3f582cf
holy hell one more
kaizencc Aug 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions packages/@aws-cdk/aws-synthetics-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,35 @@ const schedule = synthetics.Schedule.cron({

If you want the canary to run just once upon deployment, you can use `Schedule.once()`.

### Canary DeleteLambdaResourcesOnCanaryDeletion
### Deleting underlying resources on canary deletion

You can specify whether the AWS CloudFormation is to also delete the Lambda functions and layers used by this canary, when the canary is deleted.
When you delete a lambda, the following underlying resources are isolated in your AWS account:

This can be provisioned by setting the `enableAutoDeleteLambdas` property to `true` when we define the canary.
- Lambda Function that runs your canary script
- S3 Bucket for artifact storage
- IAM roles and policies
- Log Groups in CloudWatch Logs.

```ts
const stack = new Stack();
To learn more about these underlying resources, see
[Synthetics Canaries Deletion](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/synthetics_canaries_deletion.html).

In the CDK, you can configure your canary to delete the underlying lambda function when the canary is deleted.
This can be provisioned by setting the `autoDeleteLambda` property to `true`. Note that this
will create a custom resource under the hood that takes care of the lambda deletion for you.

const canary = new synthetics.Canary(stack, 'Canary', {
```ts
const canary = new synthetics.Canary(this, 'Canary', {
test: synthetics.Test.custom({
handler: 'index.handler',
code: synthetics.Code.fromInline('/* Synthetics handler code'),
}),
enableAutoDeleteLambdas: true,
autoDeleteLambda: true,
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_4_0,
});
```

Synthetic Canaries create additional resources under the hood beyond Lambda functions. Setting `enableAutoDeleteLambdas: true` will take care of
cleaning up Lambda functions on deletion, but you still have to manually delete other resources like S3 buckets and CloudWatch logs.
Setting `autoDeleteLambda: true` will take care of cleaning up Lambda functions on deletion,
but you still have to manually delete other resources like S3 buckets and CloudWatch logs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ufff. Is that a good experience? I don't care so much about the log group, but the S3 bucket seems like a pain?

Alternative solution:

cleanup: Cleanup.CLEANUP_LAMBDA

Which we can extend in the future with Cleanup.CLEANUP_ALL ?


### Configuring the Canary Script

Expand Down
62 changes: 61 additions & 1 deletion packages/@aws-cdk/aws-synthetics-alpha/lib/canary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import { Runtime } from './runtime';
import { Schedule } from './schedule';
import { CloudWatchSyntheticsMetrics } from 'aws-cdk-lib/aws-synthetics/lib/synthetics-canned-metrics.generated';
import { CfnCanary } from 'aws-cdk-lib/aws-synthetics';
import { CustomResource, CustomResourceProvider, CustomResourceProviderRuntime } from 'aws-cdk-lib/core';
import * as path from 'path';

const AUTO_DELETE_LAMBDA_RESOURCE_TYPE = 'Custom::SyntheticsAutoDeleteLambda';
const AUTO_DELETE_LAMBDA_TAG = 'aws-cdk:auto-delete-lambda';

/**
* Specify a test that the canary should run
Expand Down Expand Up @@ -195,9 +200,17 @@ export interface CanaryProps {
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-synthetics-canary.html#cfn-synthetics-canary-deletelambdaresourcesoncanarydeletion
*
* @default false
* @deprecated use autoDeleteLambda instead
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
*/
readonly enableAutoDeleteLambdas?: boolean;

/**
* Whether or not to delete the lambda resource when the canary is deleted
*
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
* @default false
*/
readonly autoDeleteLambda?: boolean;

/**
* Lifecycle rules for the generated canary artifact bucket. Has no effect
* if a bucket is passed to `artifactsBucketLocation`. If you pass a bucket
Expand Down Expand Up @@ -248,6 +261,7 @@ export class Canary extends cdk.Resource implements ec2.IConnectable {
* @internal
*/
private readonly _connections?: ec2.Connections;
private readonly _resource: CfnCanary;

public constructor(scope: Construct, id: string, props: CanaryProps) {
if (props.canaryName && !cdk.Token.isUnresolved(props.canaryName)) {
Expand Down Expand Up @@ -285,12 +299,49 @@ export class Canary extends cdk.Resource implements ec2.IConnectable {
code: this.createCode(props),
runConfig: this.createRunConfig(props),
vpcConfig: this.createVpcConfig(props),
deleteLambdaResourcesOnCanaryDeletion: props.enableAutoDeleteLambdas,
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
});
this._resource = resource;

this.canaryId = resource.attrId;
this.canaryState = resource.attrState;
this.canaryName = this.getResourceNameAttribute(resource.ref);

if (props.autoDeleteLambda ?? props.enableAutoDeleteLambdas) {
this.enableAutoDeleteLambda();
}
}

private enableAutoDeleteLambda() {
const provider = CustomResourceProvider.getOrCreateProvider(this, AUTO_DELETE_LAMBDA_RESOURCE_TYPE, {
codeDirectory: path.join(__dirname, '..', 'custom-resource-handlers', 'dist', 'aws-synthetics-alpha', 'auto-delete-lambda-handler'),
useCfnResponseWrapper: false,
runtime: CustomResourceProviderRuntime.NODEJS_18_X,
description: `Lambda function for auto-deleting lambda created by ${this.canaryName}.`,
policyStatements: [{
Effect: 'Allow',
Action: ['lambda:DeleteFunction'],
Resource: this.lambdaArn(),
}, {
Effect: 'Allow',
Action: ['synthetics:GetCanary'],
Resource: '*',
}],
});

new CustomResource(this, 'AutoDeleteObjectsCustomResource', {
resourceType: AUTO_DELETE_LAMBDA_RESOURCE_TYPE,
serviceToken: provider.serviceToken,
properties: {
CanaryName: this.canaryName,
},
});

// We also tag the canary to record the fact that we want it autodeleted.
// The custom resource will check this tag before actually doing the delete.
// Because tagging and untagging will ALWAYS happen before the CR is deleted,
// we can set `autoDeleteLambda: false` without the removal of the CR emptying
// the lambda as a side effect.
cdk.Tags.of(this._resource).add(AUTO_DELETE_LAMBDA_TAG, 'true');
}

/**
Expand Down Expand Up @@ -402,6 +453,15 @@ export class Canary extends cdk.Resource implements ec2.IConnectable {
});
}

private lambdaArn() {
return cdk.Stack.of(this).formatArn({
service: 'lambda',
resource: 'function',
arnFormat: cdk.ArnFormat.COLON_RESOURCE_NAME,
resourceName: 'cwsyn-*',
});
}

/**
* Returns the code object taken in by the canary resource.
*/
Expand Down
6 changes: 5 additions & 1 deletion packages/@aws-cdk/aws-synthetics-alpha/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@
"cdk-build": {
"env": {
"AWSLINT_BASE_CONSTRUCT": true
}
},
"pre": [
"./scripts/airlift-custom-resource-handlers.sh"
]
},
"keywords": [
"aws",
Expand All @@ -84,6 +87,7 @@
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/cdk-build-tools": "0.0.0",
"@aws-cdk/custom-resource-handlers": "0.0.0",
"@aws-cdk/integ-runner": "0.0.0",
"@aws-cdk/pkglint": "0.0.0",
"@aws-cdk/integ-tests-alpha": "0.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

scriptdir=$(cd $(dirname $0) && pwd)
customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resource-handlers/package.json'))")
awscdklibdir=${scriptdir}/..

list_custom_resources() {
for file in $customresourcedir/dist/aws-synthetics-alpha/*/index.js; do
echo $file | rev | cut -d "/" -f 2-4 | rev
done
}

customresources=$(list_custom_resources)

echo $customresources

cd $awscdklibdir
mkdir -p $awscdklibdir/custom-resource-handlers

for cr in $customresources; do
mkdir -p $awscdklibdir/custom-resource-handlers/$cr
cp $customresourcedir/$cr/index.js $awscdklibdir/custom-resource-handlers/$cr
done
8 changes: 3 additions & 5 deletions packages/@aws-cdk/aws-synthetics-alpha/test/canary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ testDeprecated('Basic canary properties work', () => {
});
});

testDeprecated('Can set `DeleteLambdaResourceOnCanaryDeletion`', () => {
testDeprecated('autoDeleteLambda introduces custom resource to delete lambda', () => {
// GIVEN
const stack = new Stack();

Expand All @@ -46,14 +46,12 @@ testDeprecated('Can set `DeleteLambdaResourceOnCanaryDeletion`', () => {
handler: 'index.handler',
code: synthetics.Code.fromInline('/* Synthetics handler code'),
}),
enableAutoDeleteLambdas: true,
autoDeleteLambda: true,
runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_8,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Synthetics::Canary', {
DeleteLambdaResourcesOnCanaryDeletion: true,
});
Template.fromStack(stack).resourceCountIs('Custom::SyntheticsAutoDeleteLambda', 1);
});

testDeprecated('Canary can have generated name', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"version": "33.0.0",
"files": {
"35ce54dc52d67452e804a0c5cf852e07a4fe1202b8f44657a0b016fdd6037fde": {
"source": {
"path": "asset.35ce54dc52d67452e804a0c5cf852e07a4fe1202b8f44657a0b016fdd6037fde",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "35ce54dc52d67452e804a0c5cf852e07a4fe1202b8f44657a0b016fdd6037fde.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"7a1aa4038bfd6218fb1a3abf28d3007c71ea0e2b12db642b5a12434a928c14da": {
"source": {
"path": "asset.7a1aa4038bfd6218fb1a3abf28d3007c71ea0e2b12db642b5a12434a928c14da",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "7a1aa4038bfd6218fb1a3abf28d3007c71ea0e2b12db642b5a12434a928c14da.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"b8c7a10e4bcf6d8973de0803384857badfc9357895262fdfa1d9f8016d7fe3f7": {
"source": {
"path": "cdk-synthetics-canary-auto-delete-lambda.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "b8c7a10e4bcf6d8973de0803384857badfc9357895262fdfa1d9f8016d7fe3f7.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading