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 23 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 `cleanup: Cleanup.LAMBDA`. 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,
cleanup: synthetics.Cleanup.LAMBDA,
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.
> Note: To properly clean up your canary on deletion, you still have to manually delete other resources
> like S3 buckets and CloudWatch logs.

### Configuring the Canary Script

Expand Down
82 changes: 81 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_UNDERLYING_RESOURCES_RESOURCE_TYPE = 'Custom::SyntheticsAutoDeleteUnderlyingResources';
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
const AUTO_DELETE_UNDERLYING_RESOURCES_TAG = 'aws-cdk:auto-delete-underlying-resources';

/**
* Specify a test that the canary should run
Expand Down Expand Up @@ -50,6 +55,25 @@ export interface CustomTestOptions {
readonly handler: string,
}

/**
* Different ways to clean up underlying Canary resources
* when the Canary is deleted.
*/
export enum Cleanup {
/**
* Clean up nothing. The user is responsible for cleaning up
* all resources left behind by the Canary.
*/
NOTHING = 'nothing',

/**
* Clean up the underlying Lambda function only. The user is
* responsible for cleaning up all other resources left behind
* by the Canary.
*/
LAMBDA = 'lambda',
}

/**
* Options for specifying the s3 location that stores the data of each canary run. The artifacts bucket location **cannot**
* be updated once the canary is created.
Expand Down Expand Up @@ -195,9 +219,18 @@ export interface CanaryProps {
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-synthetics-canary.html#cfn-synthetics-canary-deletelambdaresourcesoncanarydeletion
*
* @default false
* @deprecated this feature has been deprecated by the service team, use `cleanup: Cleanup.LAMBDA` instead which will use a Custom Resource to achieve the same effect.
*/
readonly enableAutoDeleteLambdas?: boolean;

/**
* Specify the underlying resources to be cleaned up when the canary is deleted.
* Using `Cleanup.LAMBDA` will create a Custom Resource to achieve this.
*
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
* @default Cleanup.NOTHING
*/
readonly cleanup?: Cleanup;

/**
* 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 +281,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 +319,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.cleanup === Cleanup.LAMBDA ?? props.enableAutoDeleteLambdas) {
this.cleanupUnderlyingResources();
}
}

private cleanupUnderlyingResources() {
const provider = CustomResourceProvider.getOrCreateProvider(this, AUTO_DELETE_UNDERLYING_RESOURCES_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 underlying resources created by ${this.canaryName}.`,
policyStatements: [{
Effect: 'Allow',
Action: ['lambda:DeleteFunction'],
Resource: this.lambdaArn(),
}, {
Effect: 'Allow',
Action: ['synthetics:GetCanary'],
Resource: '*',
}],
});

new CustomResource(this, 'AutoDeleteUnderlyingResourcesCustomResource', {
resourceType: AUTO_DELETE_UNDERLYING_RESOURCES_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_UNDERLYING_RESOURCES_TAG, 'true');
}

/**
Expand Down Expand Up @@ -402,6 +473,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
Loading