diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/asset.d57a897c2081bda5ce88819548c8b944dff49e721e1f7f7e9e3dd8323e6ccb0d/index.js b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/asset.d57a897c2081bda5ce88819548c8b944dff49e721e1f7f7e9e3dd8323e6ccb0d/index.js new file mode 100644 index 0000000000000..f9cc630970c96 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/asset.d57a897c2081bda5ce88819548c8b944dff49e721e1f7f7e9e3dd8323e6ccb0d/index.js @@ -0,0 +1 @@ +"use strict";var I=Object.create;var i=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var w=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var P=(o,e)=>{for(var t in e)i(o,t,{get:e[t],enumerable:!0})},l=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of S(e))!A.call(o,s)&&s!==t&&i(o,s,{get:()=>e[s],enumerable:!(n=g(e,s))||n.enumerable});return o};var m=(o,e,t)=>(t=o!=null?I(w(o)):{},l(e||!o||!o.__esModule?i(t,"default",{value:o,enumerable:!0}):t,o)),L=o=>l(i({},"__esModule",{value:!0}),o);var W={};P(W,{autoDeleteHandler:()=>E,handler:()=>_});module.exports=L(W);var c=require("@aws-sdk/client-lambda"),u=require("@aws-sdk/client-synthetics");var y=m(require("https")),R=m(require("url")),a={sendHttpRequest:T,log:F,includeStackTraces:!0,userHandlerIndex:"./index"},p="AWSCDK::CustomResourceProviderFramework::CREATE_FAILED",D="AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";function C(o){return async(e,t)=>{let n={...e,ResponseURL:"..."};if(a.log(JSON.stringify(n,void 0,2)),e.RequestType==="Delete"&&e.PhysicalResourceId===p){a.log("ignoring DELETE event caused by a failed CREATE event"),await d("SUCCESS",e);return}try{let s=await o(n,t),r=b(e,s);await d("SUCCESS",r)}catch(s){let r={...e,Reason:a.includeStackTraces?s.stack:s.message};r.PhysicalResourceId||(e.RequestType==="Create"?(a.log("CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored"),r.PhysicalResourceId=p):a.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(e)}`)),await d("FAILED",r)}}}function b(o,e={}){let t=e.PhysicalResourceId??o.PhysicalResourceId??o.RequestId;if(o.RequestType==="Delete"&&t!==o.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from "${o.PhysicalResourceId}" to "${e.PhysicalResourceId}" during deletion`);return{...o,...e,PhysicalResourceId:t}}async function d(o,e){let t={Status:o,Reason:e.Reason??o,StackId:e.StackId,RequestId:e.RequestId,PhysicalResourceId:e.PhysicalResourceId||D,LogicalResourceId:e.LogicalResourceId,NoEcho:e.NoEcho,Data:e.Data},n=R.parse(e.ResponseURL),s=`${n.protocol}//${n.hostname}/${n.pathname}?***`;a.log("submit response to cloudformation",s,t);let r=JSON.stringify(t),f={hostname:n.hostname,path:n.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(r,"utf8")}};await x({attempts:5,sleep:1e3},a.sendHttpRequest)(f,r)}async function T(o,e){return new Promise((t,n)=>{try{let s=y.request(o,r=>{r.resume(),!r.statusCode||r.statusCode>=400?n(new Error(`Unsuccessful HTTP response: ${r.statusCode}`)):t()});s.on("error",n),s.write(e),s.end()}catch(s){n(s)}})}function F(o,...e){console.log(o,...e)}function x(o,e){return async(...t)=>{let n=o.attempts,s=o.sleep;for(;;)try{return await e(...t)}catch(r){if(n--<=0)throw r;await N(Math.floor(Math.random()*s)),s*=2}}}async function N(o){return new Promise(e=>setTimeout(e,o))}var h="aws-cdk:auto-delete-underlying-resources",H=new c.LambdaClient({}),U=new u.SyntheticsClient({}),_=C(E);async function E(o){switch(o.RequestType){case"Create":return{PhyscialResourceId:o.ResourceProperties?.CanaryName};case"Update":return{PhysicalResourceId:(await k(o)).PhysicalResourceId};case"Delete":return q(o.ResourceProperties?.CanaryName)}}async function k(o){return{PhysicalResourceId:o.ResourceProperties?.CanaryName}}async function q(o){if(console.log(`Deleting lambda function associated with ${o}`),!o)throw new Error("No CanaryName was provided.");try{let e=await U.send(new u.GetCanaryCommand({Name:o}));if(e.Canary===void 0||e.Canary.Id===void 0)return;if(e.Canary.EngineArn===void 0)return;if(!O(e.Canary.Tags)){console.log(`Canary does not have '${h}' tag, skipping deletion.`);return}let t=e.Canary.EngineArn.split(":");t.at(-1)?.includes(e.Canary.Id)||t.pop();let n=t.join(":");console.log(`Deleting lambda ${n}`),await H.send(new c.DeleteFunctionCommand({FunctionName:n}))}catch(e){if(e.name!=="ResourceNotFoundException")throw e}}function O(o){return o?Object.keys(o).some(e=>e===h&&o[e]==="true"):!1}0&&(module.exports={autoDeleteHandler,handler}); diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk-synthetics-canary-run-config.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk-synthetics-canary-run-config.assets.json new file mode 100644 index 0000000000000..e1052f472dacc --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk-synthetics-canary-run-config.assets.json @@ -0,0 +1,32 @@ +{ + "version": "36.0.0", + "files": { + "d57a897c2081bda5ce88819548c8b944dff49e721e1f7f7e9e3dd8323e6ccb0d": { + "source": { + "path": "asset.d57a897c2081bda5ce88819548c8b944dff49e721e1f7f7e9e3dd8323e6ccb0d", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d57a897c2081bda5ce88819548c8b944dff49e721e1f7f7e9e3dd8323e6ccb0d.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "fc29f8df930fff80137fdd7f660261c1880517b5b50f325ab7050b1acc40d27d": { + "source": { + "path": "cdk-synthetics-canary-run-config.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "fc29f8df930fff80137fdd7f660261c1880517b5b50f325ab7050b1acc40d27d.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk-synthetics-canary-run-config.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk-synthetics-canary-run-config.template.json new file mode 100644 index 0000000000000..1c4de34ba9ba6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk-synthetics-canary-run-config.template.json @@ -0,0 +1,477 @@ +{ + "Resources": { + "CanaryArtifactsBucket4A60D32B": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "aws:kms" + } + } + ] + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "CanaryArtifactsBucketPolicy63096C41": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "CanaryArtifactsBucket4A60D32B" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "CanaryServiceRoleD132250E": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:ListAllMyBuckets", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + }, + "/*" + ] + ] + } + }, + { + "Action": "cloudwatch:PutMetricData", + "Condition": { + "StringEquals": { + "cloudwatch:namespace": "CloudWatchSynthetics" + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/cwsyn-*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "canaryPolicy" + } + ] + } + }, + "Canary11957FE2": { + "Type": "AWS::Synthetics::Canary", + "Properties": { + "ArtifactS3Location": { + "Fn::Join": [ + "", + [ + "s3://", + { + "Ref": "CanaryArtifactsBucket4A60D32B" + } + ] + ] + }, + "Code": { + "Handler": "index.handler", + "Script": "\n exports.handler = async () => {\n console.log('hello world');\n };" + }, + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "CanaryServiceRoleD132250E", + "Arn" + ] + }, + "Name": "next", + "RunConfig": { + "ActiveTracing": true, + "MemoryInMB": 1024, + "TimeoutInSeconds": 240 + }, + "RuntimeVersion": "syn-nodejs-puppeteer-7.0", + "Schedule": { + "DurationInSeconds": "0", + "Expression": "rate(5 minutes)" + }, + "StartCanaryAfterCreation": true, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-underlying-resources", + "Value": "true" + } + ] + } + }, + "CanaryAutoDeleteUnderlyingResourcesCustomResource76464216": { + "Type": "Custom::SyntheticsAutoDeleteUnderlyingResources", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomSyntheticsAutoDeleteUnderlyingResourcesCustomResourceProviderHandler26776D4E", + "Arn" + ] + }, + "CanaryName": { + "Ref": "Canary11957FE2" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomSyntheticsAutoDeleteUnderlyingResourcesCustomResourceProviderRole2D11A112": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "lambda:DeleteFunction" + ], + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":lambda:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":function:cwsyn-*" + ] + ] + } + }, + { + "Effect": "Allow", + "Action": [ + "synthetics:GetCanary" + ], + "Resource": "*" + } + ] + } + } + ] + } + }, + "CustomSyntheticsAutoDeleteUnderlyingResourcesCustomResourceProviderHandler26776D4E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "d57a897c2081bda5ce88819548c8b944dff49e721e1f7f7e9e3dd8323e6ccb0d.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "CustomSyntheticsAutoDeleteUnderlyingResourcesCustomResourceProviderRole2D11A112", + "Arn" + ] + }, + "Runtime": { + "Fn::FindInMap": [ + "LatestNodeRuntimeMap", + { + "Ref": "AWS::Region" + }, + "value" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting underlying resources created by ", + { + "Ref": "Canary11957FE2" + }, + "." + ] + ] + } + }, + "DependsOn": [ + "CustomSyntheticsAutoDeleteUnderlyingResourcesCustomResourceProviderRole2D11A112" + ] + } + }, + "Mappings": { + "LatestNodeRuntimeMap": { + "af-south-1": { + "value": "nodejs20.x" + }, + "ap-east-1": { + "value": "nodejs20.x" + }, + "ap-northeast-1": { + "value": "nodejs20.x" + }, + "ap-northeast-2": { + "value": "nodejs20.x" + }, + "ap-northeast-3": { + "value": "nodejs20.x" + }, + "ap-south-1": { + "value": "nodejs20.x" + }, + "ap-south-2": { + "value": "nodejs20.x" + }, + "ap-southeast-1": { + "value": "nodejs20.x" + }, + "ap-southeast-2": { + "value": "nodejs20.x" + }, + "ap-southeast-3": { + "value": "nodejs20.x" + }, + "ap-southeast-4": { + "value": "nodejs20.x" + }, + "ca-central-1": { + "value": "nodejs20.x" + }, + "cn-north-1": { + "value": "nodejs18.x" + }, + "cn-northwest-1": { + "value": "nodejs18.x" + }, + "eu-central-1": { + "value": "nodejs20.x" + }, + "eu-central-2": { + "value": "nodejs20.x" + }, + "eu-north-1": { + "value": "nodejs20.x" + }, + "eu-south-1": { + "value": "nodejs20.x" + }, + "eu-south-2": { + "value": "nodejs20.x" + }, + "eu-west-1": { + "value": "nodejs20.x" + }, + "eu-west-2": { + "value": "nodejs20.x" + }, + "eu-west-3": { + "value": "nodejs20.x" + }, + "il-central-1": { + "value": "nodejs20.x" + }, + "me-central-1": { + "value": "nodejs20.x" + }, + "me-south-1": { + "value": "nodejs20.x" + }, + "sa-east-1": { + "value": "nodejs20.x" + }, + "us-east-1": { + "value": "nodejs20.x" + }, + "us-east-2": { + "value": "nodejs20.x" + }, + "us-gov-east-1": { + "value": "nodejs18.x" + }, + "us-gov-west-1": { + "value": "nodejs18.x" + }, + "us-iso-east-1": { + "value": "nodejs18.x" + }, + "us-iso-west-1": { + "value": "nodejs18.x" + }, + "us-isob-east-1": { + "value": "nodejs18.x" + }, + "us-west-1": { + "value": "nodejs20.x" + }, + "us-west-2": { + "value": "nodejs20.x" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.assets.json new file mode 100644 index 0000000000000..9f337c19f4367 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/integ.json new file mode 100644 index 0000000000000..3c68065f3464b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "cdk-integ-synthetics-canary-run-config/DefaultTest": { + "stacks": [ + "cdk-synthetics-canary-run-config" + ], + "assertionStack": "cdk-integ-synthetics-canary-run-config/DefaultTest/DeployAssert", + "assertionStackName": "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/manifest.json new file mode 100644 index 0000000000000..9d3e24f4006cf --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/manifest.json @@ -0,0 +1,155 @@ +{ + "version": "36.0.0", + "artifacts": { + "cdk-synthetics-canary-run-config.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cdk-synthetics-canary-run-config.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cdk-synthetics-canary-run-config": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdk-synthetics-canary-run-config.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/fc29f8df930fff80137fdd7f660261c1880517b5b50f325ab7050b1acc40d27d.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cdk-synthetics-canary-run-config.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cdk-synthetics-canary-run-config.assets" + ], + "metadata": { + "/cdk-synthetics-canary-run-config/Canary/ArtifactsBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CanaryArtifactsBucket4A60D32B" + } + ], + "/cdk-synthetics-canary-run-config/Canary/ArtifactsBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CanaryArtifactsBucketPolicy63096C41" + } + ], + "/cdk-synthetics-canary-run-config/Canary/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CanaryServiceRoleD132250E" + } + ], + "/cdk-synthetics-canary-run-config/Canary/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Canary11957FE2" + } + ], + "/cdk-synthetics-canary-run-config/Canary/AutoDeleteUnderlyingResourcesCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "CanaryAutoDeleteUnderlyingResourcesCustomResource76464216" + } + ], + "/cdk-synthetics-canary-run-config/LatestNodeRuntimeMap": [ + { + "type": "aws:cdk:logicalId", + "data": "LatestNodeRuntimeMap" + } + ], + "/cdk-synthetics-canary-run-config/Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomSyntheticsAutoDeleteUnderlyingResourcesCustomResourceProviderRole2D11A112" + } + ], + "/cdk-synthetics-canary-run-config/Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomSyntheticsAutoDeleteUnderlyingResourcesCustomResourceProviderHandler26776D4E" + } + ], + "/cdk-synthetics-canary-run-config/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cdk-synthetics-canary-run-config/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cdk-synthetics-canary-run-config" + }, + "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cdkintegsyntheticscanaryrunconfigDefaultTestDeployAssertDF592B72.assets" + ], + "metadata": { + "/cdk-integ-synthetics-canary-run-config/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cdk-integ-synthetics-canary-run-config/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cdk-integ-synthetics-canary-run-config/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/tree.json new file mode 100644 index 0000000000000..da08bc56463c9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.js.snapshot/tree.json @@ -0,0 +1,444 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "cdk-synthetics-canary-run-config": { + "id": "cdk-synthetics-canary-run-config", + "path": "cdk-synthetics-canary-run-config", + "children": { + "Canary": { + "id": "Canary", + "path": "cdk-synthetics-canary-run-config/Canary", + "children": { + "ArtifactsBucket": { + "id": "ArtifactsBucket", + "path": "cdk-synthetics-canary-run-config/Canary/ArtifactsBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-synthetics-canary-run-config/Canary/ArtifactsBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "bucketEncryption": { + "serverSideEncryptionConfiguration": [ + { + "serverSideEncryptionByDefault": { + "sseAlgorithm": "aws:kms" + } + } + ] + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Policy": { + "id": "Policy", + "path": "cdk-synthetics-canary-run-config/Canary/ArtifactsBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-synthetics-canary-run-config/Canary/ArtifactsBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "CanaryArtifactsBucket4A60D32B" + }, + "policyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "ServiceRole": { + "id": "ServiceRole", + "path": "cdk-synthetics-canary-run-config/Canary/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "cdk-synthetics-canary-run-config/Canary/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "cdk-synthetics-canary-run-config/Canary/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "canaryPolicy", + "policyDocument": { + "Statement": [ + { + "Action": "s3:ListAllMyBuckets", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CanaryArtifactsBucket4A60D32B", + "Arn" + ] + }, + "/*" + ] + ] + } + }, + { + "Action": "cloudwatch:PutMetricData", + "Condition": { + "StringEquals": { + "cloudwatch:namespace": "CloudWatchSynthetics" + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/cwsyn-*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "cdk-synthetics-canary-run-config/Canary/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Synthetics::Canary", + "aws:cdk:cloudformation:props": { + "artifactS3Location": { + "Fn::Join": [ + "", + [ + "s3://", + { + "Ref": "CanaryArtifactsBucket4A60D32B" + } + ] + ] + }, + "code": { + "handler": "index.handler", + "script": "\n exports.handler = async () => {\n console.log('hello world');\n };" + }, + "executionRoleArn": { + "Fn::GetAtt": [ + "CanaryServiceRoleD132250E", + "Arn" + ] + }, + "name": "next", + "runConfig": { + "activeTracing": true, + "memoryInMb": 1024, + "timeoutInSeconds": 240 + }, + "runtimeVersion": "syn-nodejs-puppeteer-7.0", + "schedule": { + "durationInSeconds": "0", + "expression": "rate(5 minutes)" + }, + "startCanaryAfterCreation": true, + "tags": [ + { + "key": "aws-cdk:auto-delete-underlying-resources", + "value": "true" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "AutoDeleteUnderlyingResourcesCustomResource": { + "id": "AutoDeleteUnderlyingResourcesCustomResource", + "path": "cdk-synthetics-canary-run-config/Canary/AutoDeleteUnderlyingResourcesCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "cdk-synthetics-canary-run-config/Canary/AutoDeleteUnderlyingResourcesCustomResource/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "LatestNodeRuntimeMap": { + "id": "LatestNodeRuntimeMap", + "path": "cdk-synthetics-canary-run-config/LatestNodeRuntimeMap", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider": { + "id": "Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider", + "path": "cdk-synthetics-canary-run-config/Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "cdk-synthetics-canary-run-config/Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Role": { + "id": "Role", + "path": "cdk-synthetics-canary-run-config/Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider/Role", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Handler": { + "id": "Handler", + "path": "cdk-synthetics-canary-run-config/Custom::SyntheticsAutoDeleteUnderlyingResourcesCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-synthetics-canary-run-config/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-synthetics-canary-run-config/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "cdk-integ-synthetics-canary-run-config": { + "id": "cdk-integ-synthetics-canary-run-config", + "path": "cdk-integ-synthetics-canary-run-config", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "cdk-integ-synthetics-canary-run-config/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "cdk-integ-synthetics-canary-run-config/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "cdk-integ-synthetics-canary-run-config/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-integ-synthetics-canary-run-config/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-integ-synthetics-canary-run-config/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.ts new file mode 100644 index 0000000000000..74e57cad758a7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-synthetics/test/integ.canary-run-config.ts @@ -0,0 +1,32 @@ +import { App, Duration, Size, Stack, StackProps } from 'aws-cdk-lib/core'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { Construct } from 'constructs'; +import * as synthetics from 'aws-cdk-lib/aws-synthetics'; + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + new synthetics.Canary(this, 'Canary', { + canaryName: 'next', + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline(` + exports.handler = async () => { + console.log(\'hello world\'); + };`), + }), + cleanup: synthetics.Cleanup.LAMBDA, + activeTracing: true, + memory: Size.mebibytes(1024), + timeout: Duration.minutes(4), + }); + } +} + +const app = new App(); + +new IntegTest(app, 'cdk-integ-synthetics-canary-run-config', { + testCases: [new TestStack(app, 'cdk-synthetics-canary-run-config')], +}); diff --git a/packages/aws-cdk-lib/aws-synthetics/README.md b/packages/aws-cdk-lib/aws-synthetics/README.md index c4ec669708416..e30ffc36e805b 100644 --- a/packages/aws-cdk-lib/aws-synthetics/README.md +++ b/packages/aws-cdk-lib/aws-synthetics/README.md @@ -87,6 +87,58 @@ const schedule = synthetics.Schedule.cron({ If you want the canary to run just once upon deployment, you can use `Schedule.once()`. +### Active Tracing + +You can choose to enable active AWS X-Ray tracing on canaries that use the syn-nodejs-2.0 or later runtime by setting `activeTracing` to `true`. + +With tracing enabled, traces are sent for all calls made by the canary that use the browser, the AWS SDK, or HTTP or HTTPS modules. + +For more information, see [Canaries and X-Ray tracing](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_tracing.html). + +```ts +const canary = new synthetics.Canary(this, 'MyCanary', { + schedule: synthetics.Schedule.rate(Duration.minutes(5)), + test: synthetics.Test.custom({ + code: synthetics.Code.fromAsset(path.join(__dirname, 'canary')), + handler: 'index.handler', + }), + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2, + activeTracing: true, // active tracing enabled +}); +``` + +### Memory + +You can set the maximum amount of memory that the canary can use while running by setting `memory`. + +```ts +const canary = new synthetics.Canary(this, 'MyCanary', { + schedule: synthetics.Schedule.rate(Duration.minutes(5)), + test: synthetics.Test.custom({ + code: synthetics.Code.fromAsset(path.join(__dirname, 'canary')), + handler: 'index.handler', + }), + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2, + memory: Size.mebibytes(1024), // 1024 MiB +}); +``` + +### Timeout + +You can set how long the canary is allowed to run before it must stop by setting `timeout`. + +```ts +const canary = new synthetics.Canary(this, 'MyCanary', { + schedule: synthetics.Schedule.rate(Duration.minutes(5)), + test: synthetics.Test.custom({ + code: synthetics.Code.fromAsset(path.join(__dirname, 'canary')), + handler: 'index.handler', + }), + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2, + timeout: Duration.seconds(60), // 60 seconds +}); +``` + ### Deleting underlying resources on canary deletion When you delete a lambda, the following underlying resources are isolated in your AWS account: @@ -114,7 +166,7 @@ const canary = new synthetics.Canary(this, 'Canary', { }); ``` -> Note: To properly clean up your canary on deletion, you still have to manually delete other resources +> 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 diff --git a/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts b/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts index 2c49036174bf2..192d0adb8cd9b 100644 --- a/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts +++ b/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts @@ -179,6 +179,20 @@ export interface CanaryProps { */ readonly test: Test; + /** + * Specifies whether this canary is to use active AWS X-Ray tracing when it runs. + * Active tracing enables this canary run to be displayed in the ServiceLens and X-Ray service maps even if the + * canary does not hit an endpoint that has X-Ray tracing enabled. Using X-Ray tracing incurs charges. + * + * You can enable active tracing only for canaries that use version syn-nodejs-2.0 or later for their canary runtime. + * + * @default false + * + * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_tracing.html + */ + + readonly activeTracing?: boolean; + /** * Key-value pairs that the Synthetics caches and makes available for your canary scripts. Use environment variables * to apply configuration changes, such as test and production environment configurations, without changing your @@ -188,6 +202,22 @@ export interface CanaryProps { */ readonly environmentVariables?: { [key: string]: string }; + /** + * The maximum amount of memory that the canary can use while running. + * This value must be a multiple of 64 Mib. + * + * @default Size.mebibytes(5) + */ + readonly memory?: cdk.Size; + + /** + * How long the canary is allowed to run before it must stop. + * You can't set this time to be longer than the frequency of the runs of this canary. + * + * @default - the frequency of the canary is used as this value, up to a maximum of 900 seconds. + */ + readonly timeout?: cdk.Duration; + /** * The VPC where this canary is run. * @@ -420,7 +450,7 @@ export class Canary extends cdk.Resource implements ec2.IConnectable { actions: ['s3:GetBucketLocation'], }), new iam.PolicyStatement({ - resources: [this.artifactsBucket.arnForObjects(`${prefix ? prefix+'/*' : '*'}`)], + resources: [this.artifactsBucket.arnForObjects(`${prefix ? prefix + '/*' : '*'}`)], actions: ['s3:PutObject'], }), new iam.PolicyStatement({ @@ -513,11 +543,45 @@ export class Canary extends cdk.Resource implements ec2.IConnectable { } private createRunConfig(props: CanaryProps): CfnCanary.RunConfigProperty | undefined { - if (!props.environmentVariables) { + if (props.activeTracing === undefined && + !props.environmentVariables && + !props.memory && + !props.timeout) { return undefined; } + const activeTracingNotSupportedRuntime = [ + Runtime.SYNTHETICS_PYTHON_SELENIUM_2_1, + Runtime.SYNTHETICS_PYTHON_SELENIUM_3_0, + ]; + + if (props.activeTracing && activeTracingNotSupportedRuntime.includes(props.runtime)) { + throw new Error('You can enable active tracing only for canaries that use version syn-nodejs-2.0 or later for their canary runtime.'); + } + + let memoryInMb = undefined; + if (!cdk.Token.isUnresolved(props.memory) && props.memory) { + memoryInMb = props.memory.toMebibytes(); + if (memoryInMb % 64 !== 0) { + throw new Error(`\`memory\` must be a multiple of 64 MiB, got ${memoryInMb} MiB.`); + } + if (memoryInMb < 960 || memoryInMb > 3008) { + throw new Error(`\`memory\` must be between 960 MiB and 3008 MiB, got ${memoryInMb} MiB.`); + } + } + + let timeoutInSeconds = undefined; + if (!cdk.Token.isUnresolved(props.timeout) && props.timeout) { + timeoutInSeconds = props.timeout.toSeconds(); + if (timeoutInSeconds < 3 || timeoutInSeconds > 840) { + throw new Error(`\`timeout\` must be between 3 seconds and 840 seconds, got ${timeoutInSeconds} seconds.`); + } + } + return { + activeTracing: props.activeTracing, environmentVariables: props.environmentVariables, + memoryInMb, + timeoutInSeconds, }; } diff --git a/packages/aws-cdk-lib/aws-synthetics/test/canary.test.ts b/packages/aws-cdk-lib/aws-synthetics/test/canary.test.ts index 5481002b9d579..757b02f2d7795 100644 --- a/packages/aws-cdk-lib/aws-synthetics/test/canary.test.ts +++ b/packages/aws-cdk-lib/aws-synthetics/test/canary.test.ts @@ -3,7 +3,7 @@ import { Match, Template } from '../../assertions'; import * as ec2 from '../../aws-ec2'; import * as iam from '../../aws-iam'; import * as s3 from '../../aws-s3'; -import { Duration, Lazy, Stack } from '../../core'; +import { Duration, Lazy, Size, Stack } from '../../core'; import * as synthetics from '../lib'; test('Basic canary properties work', () => { @@ -237,6 +237,44 @@ test('Python runtime can be specified', () => { }); }); +test.each([true, false])('activeTracing can be set to %s', (activeTracing: boolean) => { + // GIVEN + const stack = new Stack(); + + // WHEN + new synthetics.Canary(stack, 'Canary', { + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + activeTracing, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Synthetics::Canary', { + RunConfig: { + ActiveTracing: activeTracing, + }, + }); +}); + +test('throws when activeTracing is enabled with an unsupported runtime', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + expect(() => new synthetics.Canary(stack, 'Canary', { + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('# Synthetics handler code'), + }), + runtime: synthetics.Runtime.SYNTHETICS_PYTHON_SELENIUM_2_1, + activeTracing: true, + })) + .toThrow('You can enable active tracing only for canaries that use version syn-nodejs-2.0 or later for their canary runtime.'); +}); + test('environment variables can be specified', () => { // GIVEN const stack = new Stack(); @@ -282,6 +320,98 @@ test('environment variables are skipped if not provided', () => { }); }); +test('memory can be set', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new synthetics.Canary(stack, 'Canary', { + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + memory: Size.mebibytes(1024), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Synthetics::Canary', { + RunConfig: { + MemoryInMB: 1024, + }, + }); +}); + +test('throws when memory is not a multiple of 64 MiB', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + expect(() => new synthetics.Canary(stack, 'Canary', { + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + memory: Size.mebibytes(5), + })) + .toThrow('\`memory\` must be a multiple of 64 MiB, got 5 MiB.'); +}); + +test.each([64, 6400])('throws when memory is out of range, %d MiB', (memoryInMb: number) => { + // GIVEN + const stack = new Stack(); + + // WHEN + expect(() => new synthetics.Canary(stack, 'Canary', { + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + memory: Size.mebibytes(memoryInMb), + })) + .toThrow(`\`memory\` must be between 960 MiB and 3008 MiB, got ${memoryInMb} MiB.`); +}); + +test('timeout can be set', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new synthetics.Canary(stack, 'Canary', { + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + timeout: Duration.seconds(60), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Synthetics::Canary', { + RunConfig: { + TimeoutInSeconds: 60, + }, + }); +}); + +test.each([2, 900])('throws when timeout is out of range, %d seconds', (seconds: number) => { + // GIVEN + const stack = new Stack(); + + // WHEN + expect(() => new synthetics.Canary(stack, 'Canary', { + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + timeout: Duration.seconds(seconds), + })) + .toThrow(`\`timeout\` must be between 3 seconds and 840 seconds, got ${seconds} seconds.`); +}); + test('Runtime can be customized', () => { // GIVEN const stack = new Stack();