diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/__entrypoint__.js b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/__entrypoint__.js rename to packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/__entrypoint__.js diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/index.js b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/index.js similarity index 100% rename from packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/index.js rename to packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.cross-account-zone-delegation.js.snapshot/asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6/index.js diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b/__entrypoint__.js b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b/__entrypoint__.js new file mode 100644 index 0000000000000..02033f55cf612 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b/__entrypoint__.js @@ -0,0 +1,155 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + const parsedUrl = url.parse(event.ResponseURL); + const loggingSafeUrl = `${parsedUrl.protocol}//${parsedUrl.hostname}/${parsedUrl.pathname}?***`; + exports.external.log('submit response to cloudformation', loggingSafeUrl, json); + const responseBody = JSON.stringify(json); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { + 'content-type': '', + 'content-length': Buffer.byteLength(responseBody, 'utf8'), + }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, exports.external.sendHttpRequest)(req, responseBody); +} +async function defaultSendHttpRequest(options, requestBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, (response) => { + response.resume(); // Consume the response but don't care about it + if (!response.statusCode || response.statusCode >= 400) { + reject(new Error(`Unsuccessful HTTP response: ${response.statusCode}`)); + } + else { + resolve(); + } + }); + request.on('error', reject); + request.write(requestBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b/index.js b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/asset.392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b/index.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.assets.json index 1ab7979945d89..e8809b4fe0801 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.assets.json @@ -1,15 +1,15 @@ { "version": "36.0.0", "files": { - "df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6": { + "392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b": { "source": { - "path": "asset.df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6", + "path": "asset.392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b", "packaging": "zip" }, "destinations": { "234567890123-us-east-1": { "bucketName": "cdk-hnb659fds-assets-234567890123-us-east-1", - "objectKey": "df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6.zip", + "objectKey": "392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::234567890123:role/cdk-hnb659fds-file-publishing-role-234567890123-us-east-1" } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.template.json index b21853e7cfa70..f33c3f3d2046d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.rename-cross-account-zone-delegation.js.snapshot/child-stack.template.json @@ -103,7 +103,7 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-234567890123-us-east-1", - "S3Key": "df3b0c6a1a1c298cd483caec10a008f70e053a49a8472aa907dfa3021fed2bd6.zip" + "S3Key": "392b3833f117607db3f72dc30f3acf726cd6b17317a3da1598e52003fd21f86b.zip" }, "Timeout": 900, "MemorySize": 128,