Skip to content

Commit

Permalink
fix(synthetics): updated handler validation (#26569)
Browse files Browse the repository at this point in the history
This fix updates handler validation based on Synthetic rules for [Node](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_WritingCanary_Nodejs.html#:~:text=and%20files.-,Handler%20name,-Be%20sure%20to) and [Python](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_WritingCanary_Python.html#:~:text=Packaging%20your%20canary%20files) runtimes.

Closes #26540.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
lpizzinidev authored Aug 2, 2023
1 parent 6a70b4f commit 1eaec92
Show file tree
Hide file tree
Showing 18 changed files with 15,510 additions and 12,583 deletions.
46 changes: 30 additions & 16 deletions packages/@aws-cdk/aws-synthetics-alpha/lib/canary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,9 @@ export class Test {
* @param options The configuration options
*/
public static custom(options: CustomTestOptions): Test {
Test.validateHandler(options.handler);
return new Test(options.code, options.handler);
}

/**
* Verifies that the given handler ends in '.handler'. Returns the handler if successful and
* throws an error if not.
*
* @param handler - the handler given by the user
*/
private static validateHandler(handler: string) {
if (!handler.endsWith('.handler')) {
throw new Error(`Canary Handler must end in '.handler' (${handler})`);
}
if (handler.length > 21) {
throw new Error(`Canary Handler must be less than 21 characters (${handler})`);
}
}

/**
* Construct a Test property
*
Expand Down Expand Up @@ -422,6 +406,7 @@ export class Canary extends cdk.Resource implements ec2.IConnectable {
* Returns the code object taken in by the canary resource.
*/
private createCode(props: CanaryProps): CfnCanary.CodeProperty {
this.validateHandler(props.test.handler, props.runtime);
const codeConfig = {
handler: props.test.handler,
...props.test.code.bind(this, props.test.handler, props.runtime.family),
Expand All @@ -435,6 +420,35 @@ export class Canary extends cdk.Resource implements ec2.IConnectable {
};
}

/**
* Verifies that the handler name matches the conventions given a certain runtime.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-code.html#cfn-synthetics-canary-code-handler
* @param handler - the name of the handler
* @param runtime - the runtime version
*/
private validateHandler(handler: string, runtime: Runtime) {
const oldRuntimes = [
Runtime.SYNTHETICS_PYTHON_SELENIUM_1_0,
Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_0,
Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_1,
Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_2,
Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_3,
];
if (oldRuntimes.includes(runtime)) {
if (!handler.match(/^[0-9A-Za-z_\\-]+\.handler*$/)) {
throw new Error(`Canary Handler must be specified as \'fileName.handler\' for legacy runtimes, received ${handler}`);
}
} else {
if (!handler.match(/^([0-9a-zA-Z_-]+\/)*[0-9A-Za-z_\\-]+\.[A-Za-z_][A-Za-z0-9_]*$/)) {
throw new Error(`Canary Handler must be specified either as \'fileName.handler\', \'fileName.functionName\', or \'folder/fileName.functionName\', received ${handler}`);
}
}
if (handler.length < 1 || handler.length > 128) {
throw new Error(`Canary Handler length must be between 1 and 128, received ${handler.length}`);
}
}

private createRunConfig(props: CanaryProps): CfnCanary.RunConfigProperty | undefined {
if (!props.environmentVariables) {
return undefined;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 87 additions & 0 deletions packages/@aws-cdk/aws-synthetics-alpha/test/canary.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as path from 'path';
import { testDeprecated } from '@aws-cdk/cdk-build-tools';
import { Match, Template } from 'aws-cdk-lib/assertions';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
Expand Down Expand Up @@ -703,3 +704,89 @@ testDeprecated('Role policy generated as expected', () => {
}],
});
});

testDeprecated('Should create handler with path for recent runtimes', () => {
// GIVEN
const stack = new Stack();

// WHEN
new synthetics.Canary(stack, 'Canary', {
canaryName: 'mycanary',
test: synthetics.Test.custom({
handler: 'folder/canary.functionName',
code: synthetics.Code.fromAsset(path.join(__dirname, 'canaries')),
}),
runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_8,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Synthetics::Canary', {
Name: 'mycanary',
Code: {
Handler: 'folder/canary.functionName',
},
RuntimeVersion: 'syn-nodejs-puppeteer-3.8',
});
});

describe('handler validation', () => {
testDeprecated('legacy runtimes', () => {
const stack = new Stack();
expect(() => {
new synthetics.Canary(stack, 'Canary', {
test: synthetics.Test.custom({
handler: 'index.functionName',
code: synthetics.Code.fromAsset(path.join(__dirname, 'canaries')),
}),
runtime: synthetics.Runtime.SYNTHETICS_PYTHON_SELENIUM_1_0,
});
}).toThrow(/Canary Handler must be specified as 'fileName.handler' for legacy runtimes/);
});

testDeprecated('recent runtimes', () => {
const stack = new Stack();

expect(() => {
new synthetics.Canary(stack, 'Canary', {
test: synthetics.Test.custom({
handler: 'invalidHandler',
code: synthetics.Code.fromAsset(path.join(__dirname, 'canaries')),
}),
runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_9,
});
}).toThrow(/Canary Handler must be specified either as 'fileName.handler', 'fileName.functionName', or 'folder\/fileName.functionName'/);

expect(() => {
new synthetics.Canary(stack, 'Canary1', {
test: synthetics.Test.custom({
handler: 'canary.functionName',
code: synthetics.Code.fromAsset(path.join(__dirname, 'canaries')),
}),
runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_9,
});
}).not.toThrow();

expect(() => {
new synthetics.Canary(stack, 'Canary2', {
test: synthetics.Test.custom({
handler: 'folder/canary.functionName',
code: synthetics.Code.fromAsset(path.join(__dirname, 'canaries')),
}),
runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_9,
});
}).not.toThrow();
});

testDeprecated('handler length', () => {
const stack = new Stack();
expect(() => {
new synthetics.Canary(stack, 'Canary1', {
test: synthetics.Test.custom({
handler: 'longHandlerName'.repeat(10) + '.handler',
code: synthetics.Code.fromAsset(path.join(__dirname, 'canaries')),
}),
runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_9,
});
}).toThrow(/Canary Handler length must be between 1 and 128/);
});
});
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
{
"version": "32.0.0",
"version": "33.0.0",
"files": {
"fe2373df587bda7e4bf68910b7be30b2042493413a6f6f2c43efab4f184d3bad": {
"d5b88712f80b7d8c96ddbffc883a569f5e5681cf14a985d4d0933ca3712f5110": {
"source": {
"path": "asset.fe2373df587bda7e4bf68910b7be30b2042493413a6f6f2c43efab4f184d3bad.bundle",
"path": "asset.d5b88712f80b7d8c96ddbffc883a569f5e5681cf14a985d4d0933ca3712f5110.bundle",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "fe2373df587bda7e4bf68910b7be30b2042493413a6f6f2c43efab4f184d3bad.zip",
"objectKey": "d5b88712f80b7d8c96ddbffc883a569f5e5681cf14a985d4d0933ca3712f5110.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"5c382b4e425866a69d59a5cffba84fb128c5482ea37a4da5b0ddb7a5a803d15e": {
"df5ea21aeb8a2b4d703bce966a4cf197c3a558b9b1bd275879fa58d12447002e": {
"source": {
"path": "IntegCanaryTestDefaultTestDeployAssert3AD5A094.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "5c382b4e425866a69d59a5cffba84fb128c5482ea37a4da5b0ddb7a5a803d15e.json",
"objectKey": "df5ea21aeb8a2b4d703bce966a4cf197c3a558b9b1bd275879fa58d12447002e.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Loading

0 comments on commit 1eaec92

Please sign in to comment.