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 3 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
54 changes: 53 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_LAMBDAS_RESOURCE_TYPE = 'Custom::SyntheticsAutoDeleteLambdas';
const AUTO_DELETE_LAMBDAS_TAG = 'aws-cdk:auto-delete-lambdas';

/**
* Specify a test that the canary should run
Expand Down Expand Up @@ -264,6 +269,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 @@ -301,12 +307,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.enableAutoDeleteLambdas) {
this.enableAutoDeleteLambdas();
}
}

private enableAutoDeleteLambdas() {
const provider = CustomResourceProvider.getOrCreateProvider(this, AUTO_DELETE_LAMBDAS_RESOURCE_TYPE, {
codeDirectory: path.join(__dirname, '..', 'custom-resource-handlers', 'dist', 'aws-synthetics-alpha', 'auto-delete-lambdas-handler'),
useCfnResponseWrapper: false,
runtime: CustomResourceProviderRuntime.NODEJS_18_X,
description: `Lambda function for auto-deleting lambdas 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_LAMBDAS_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 `enableAutoDeleteLambdas: false` without the removal of the CR emptying
// the lambda as a side effect.
cdk.Tags.of(this._resource).add(AUTO_DELETE_LAMBDAS_TAG, 'true');
}

/**
Expand Down Expand Up @@ -418,6 +461,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
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { LambdaClient, DeleteFunctionCommand } from '@aws-sdk/client-lambda';
import { SyntheticsClient, GetCanaryCommand } from '@aws-sdk/client-synthetics';
import { makeHandler } from '../../nodejs-entrypoint';

const AUTO_DELETE_LAMBDA_TAG = 'aws-cdk:auto-delete-lambdas';

const lambda = new LambdaClient({});
const synthetics = new SyntheticsClient({});

export const handler = makeHandler(autoDeleteHandler);

export async function autoDeleteHandler(event: AWSLambda.CloudFormationCustomResourceEvent) {
switch (event.RequestType) {
case 'Create':
break;
case 'Update':
return onUpdate(event);
case 'Delete':
return onDelete(event.ResourceProperties?.CanaryName);
}
};

async function onUpdate(event: AWSLambda.CloudFormationCustomResourceEvent) {
const updateEvent = event as AWSLambda.CloudFormationCustomResourceUpdateEvent;
const oldCanaryName = updateEvent.OldResourceProperties?.CanaryName;
const newCanaryName = updateEvent.ResourceProperties?.CanaryName;
const repositoryNameHasChanged = (newCanaryName && oldCanaryName)
&& (newCanaryName !== oldCanaryName);

// If the name of the canary has changed, CloudFormation will delete the canary
// and create a new one with the new name. So the old lambda will be isolated
// and we should try to delete the old lambdae. */
if (repositoryNameHasChanged) {
return onDelete(oldCanaryName);
}
}

async function onDelete(canaryName: string) {
if (!canaryName) {
throw new Error('No CanaryName was provided.');
}

try {
const response = (await synthetics.send(new GetCanaryCommand({
Name: canaryName,
})));

if (!isCanaryTaggedForDeletion(response.Canary?.Tags)) {
// eslint-disable-next-line no-console
console.log(`Canary does not have '${AUTO_DELETE_LAMBDA_TAG}' tag, skipping deletion.`);
return;
}

await lambda.send(new DeleteFunctionCommand({
FunctionName: `cwsyn-${canaryName}-${response.Canary?.Id}`,
}));
} catch (error: any) {
if (error.name !== 'ResourceNotFoundException') {
throw error;
}
// Canary or Lambda doesn't exist. Exiting.
}
}

/**
* The canary will only be tagged for deletion if it's being deleted in the same
* deployment as this Custom Resource.
*
* If the Custom Resource is ever deleted before the repository, it must be because
* `autoDeleteImages` has been switched to false, in which case the tag would have
* been removed before we get to this Delete event.
*/
function isCanaryTaggedForDeletion(tags?: Record<string, string>): boolean {
if (!tags) return false;
return Object.keys(tags).some((tag) => tag === AUTO_DELETE_LAMBDA_TAG && tags[tag] === 'true');
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/custom-resource-handlers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
},
"dependencies": {
"@aws-sdk/client-ecr": "^3.370.0",
"@aws-sdk/client-s3": "^3.370.0"
"@aws-sdk/client-s3": "^3.370.0",
"@aws-sdk/client-lambda": "^3.370.0",
"@aws-sdk/client-synthetics": "^3.370.0"
},
"repository": {
"url": "https://github.com/aws/aws-cdk.git",
Expand Down
Loading