From 0096f47a8cf9ee0e84f72d1a3d69a344279a29cc Mon Sep 17 00:00:00 2001 From: Alex Varju Date: Wed, 19 Oct 2022 06:54:44 -0700 Subject: [PATCH] fix(lambda-python): root-owned cache items not cleaned up after install (#22512) This is another attempt at fixing #22012. #22398 intended to fix it, but the issue still occurs when building on arm64 machines with both `compatible_runtimes=[lambdas.Runtime.PYTHON_3_9]` and `compatible_architectures=[lambdas.Architecture.ARM_64]` The core problem is that the build leaves root-owned files under `/tmp/pip-cache`, which then cause permission problems when subsequent layers calling `pip install` are unable to create temporary directories. Example error: ``` WARNING: Building wheel for alembic-utils failed: [Errno 13] Permission denied: '/tmp/pip-cache/wheels/5c' ``` Expanding on the idea within #22398, this PR adds one more step to the chain of operations, removing any temporary files from the cache that are no longer needed (because the packages are now installed), and ensuring that subsequent layers are able to create temporary files inside the cache directories. Comparing the contents of the Docker image before and after this change... Before: ``` $ ls -aFl /tmp/*cache /tmp/pip-cache: total 16 drwxrwxrwx 4 root root 4096 Oct 15 00:57 ./ drwxrwxrwt 1 root root 4096 Oct 15 00:58 ../ drwxr-xr-x 18 root root 4096 Oct 15 00:57 http/ drwxr-xr-x 2 root root 4096 Oct 15 00:57 selfcheck/ /tmp/poetry-cache: total 8 drwxrwxrwx 2 root root 4096 Oct 15 00:57 ./ drwxrwxrwt 1 root root 4096 Oct 15 00:58 ../ ``` After: ``` $ ls -aFl /tmp/*cache /tmp/pip-cache: total 8 drwxrwxrwx 2 root root 4096 Oct 15 01:00 ./ drwxrwxrwt 1 root root 4096 Oct 15 01:00 ../ /tmp/poetry-cache: total 8 drwxrwxrwx 2 root root 4096 Oct 15 00:59 ./ drwxrwxrwt 1 root root 4096 Oct 15 01:00 ../ ``` ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-lambda-python/lib/Dockerfile | 4 +- .../index.js | 767 ++++++++++++++++++ .../function.arm64.integ.snapshot/cdk.out | 1 + .../integ-lambda-python-arm64.assets.json | 45 + .../integ-lambda-python-arm64.template.json | 134 +++ .../function.arm64.integ.snapshot/integ.json | 13 + ...efaultTestDeployAssertAB537F34.assets.json | 32 + ...aultTestDeployAssertAB537F34.template.json | 178 ++++ .../manifest.json | 166 ++++ .../function.arm64.integ.snapshot/tree.json | 372 +++++++++ .../test/integ.function.arm64.ts | 53 ++ .../lambda-handler-arm64/handler/index.py | 16 + .../layer/requirements.txt | 4 + .../aws-lambda-python/test/packaging.test.ts | 8 +- 14 files changed, 1788 insertions(+), 5 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/asset.3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac.bundle/index.js create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.assets.json create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.template.json create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.assets.json create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.template.json create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-lambda-python/test/integ.function.arm64.ts create mode 100644 packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/handler/index.py create mode 100644 packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/layer/requirements.txt diff --git a/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile b/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile index 28bde8baf87a7..e61f15fcb0fa8 100644 --- a/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile +++ b/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile @@ -31,6 +31,8 @@ RUN \ # Ensure all users can write to poetry cache chmod -R 777 /tmp/poetry-cache && \ # pipenv 2022.4.8 is the last version with Python 3.6 support - pip install pipenv==2022.4.8 poetry + pip install pipenv==2022.4.8 poetry && \ +# Ensure no temporary files remain in the caches + rm -rf /tmp/pip-cache/* /tmp/poetry-cache/* CMD [ "python" ] diff --git a/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/asset.3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac.bundle/index.js b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/asset.3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac.bundle/index.js new file mode 100644 index 0000000000000..6bee1ced2a9b7 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/asset.3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac.bundle/index.js @@ -0,0 +1,767 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler, + isComplete: () => isComplete, + onTimeout: () => onTimeout +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// ../assertions/lib/matcher.ts +var Matcher = class { + static isMatcher(x) { + return x && x instanceof Matcher; + } +}; +var MatchResult = class { + constructor(target) { + this.failures = []; + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.target = target; + } + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + recordFailure(failure) { + this.failures.push(failure); + return this; + } + hasFailed() { + return this.failures.length !== 0; + } + get failCount() { + return this.failures.length; + } + compose(id, inner) { + const innerF = inner.failures; + this.failures.push(...innerF.map((f) => { + return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; + })); + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + toHumanStrings() { + return this.failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + } + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } +}; + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } +}; + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} + +// ../assertions/lib/match.ts +var Match = class { + static absent() { + return new AbsentMatch("absent"); + } + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + static not(pattern) { + return new NotMatch("not", pattern); + } + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + static anyValue() { + return new AnyMatch("anyValue"); + } + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } +}; +var LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } +}; +var ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + if (!this.subsequence && this.pattern.length !== actual.length) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected array of length ${this.pattern.length} but received ${actual.length}` + }); + } + let patternIdx = 0; + let actualIdx = 0; + const result = new MatchResult(actual); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + if (!this.subsequence || !innerResult.hasFailed()) { + result.compose(`[${actualIdx}]`, innerResult); + patternIdx++; + actualIdx++; + } else { + actualIdx++; + } + } + for (; patternIdx < this.pattern.length; patternIdx++) { + const pattern = this.pattern[patternIdx]; + const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; + result.recordFailure({ + matcher: this, + path: [], + message: `Missing element${element}at pattern index ${patternIdx}` + }); + } + return result; + } +}; +var ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [`/${a}`], + message: "Unexpected key" + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [`/${patternKey}`], + message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(`/${patternKey}`, inner); + } + return result; + } +}; +var SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + if (getType(actual) !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + return result; + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + result.recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + return result; + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + result.compose(`(${this.name})`, innerResult); + return result; + } +}; +var NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } +}; +var AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } +}; +var StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } +}; + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var AWS = __toESM(require("aws-sdk")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + async handle() { + try { + if ("stateMachineArn" in this.event.ResourceProperties) { + const req = { + stateMachineArn: this.event.ResourceProperties.stateMachineArn, + name: this.event.RequestId, + input: JSON.stringify(this.event) + }; + await this.startExecution(req); + return; + } else { + const response = await this.processEvent(this.event.ResourceProperties); + return response; + } + } catch (e) { + console.log(e); + throw e; + } finally { + clearTimeout(this.timeout); + } + } + async handleIsComplete() { + try { + const result = await this.processEvent(this.event.ResourceProperties); + return result; + } catch (e) { + console.log(e); + return; + } finally { + clearTimeout(this.timeout); + } + } + async startExecution(req) { + try { + const sfn = new AWS.StepFunctions(); + await sfn.startExecution(req).promise(); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } finally { + clearTimeout(this.timeout); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = decodeCall(request2.actual); + const expected = decodeCall(request2.expected); + let result; + const matcher = new MatchCreator(expected).getMatcher(); + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + failed: true, + assertion: JSON.stringify({ + status: "fail", + message: [ + ...matchResult.toHumanStrings(), + JSON.stringify(matchResult.target, void 0, 2) + ].join("\n") + }) + }; + if (request2.failDeployment) { + throw new Error(result.assertion); + } + } else { + result = { + assertion: JSON.stringify({ + status: "success" + }) + }; + } + return result; + } +}; +var MatchCreator = class { + constructor(obj) { + this.parsedObj = { + matcher: obj + }; + } + getMatcher() { + try { + const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { + const nested = Object.keys(v)[0]; + switch (nested) { + case "$ArrayWith": + return Match.arrayWith(v[nested]); + case "$ObjectLike": + return Match.objectLike(v[nested]); + case "$StringLike": + return Match.stringLikeRegexp(v[nested]); + default: + return v; + } + }); + if (Matcher.isMatcher(final.matcher)) { + return final.matcher; + } + return Match.exact(final.matcher); + } catch { + return Match.exact(this.parsedObj.matcher); + } + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + try { + const parsed = JSON.parse(call); + return parsed; + } catch (e) { + return call; + } +} + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign( + {}, + ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + let childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + if (typeof childKey === "string") { + childKey = isJsonString(childKey); + } + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object) + ); +} +var AwsApiCallHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS2 = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS2.VERSION}`); + if (!Object.prototype.hasOwnProperty.call(AWS2, request2.service)) { + throw Error(`Service ${request2.service} does not exist in AWS SDK version ${AWS2.VERSION}.`); + } + const service = new AWS2[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = { + ...flatten(respond) + }; + const resp = request2.flattenResponse === "true" ? flatData : respond; + console.log(`Returning result ${JSON.stringify(resp)}`); + return resp; + } +}; +function isJsonString(value) { + try { + return JSON.parse(value); + } catch { + return value; + } +} + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + if (event.RequestType === "Delete") { + await provider.respond({ + status: "SUCCESS", + reason: "OK" + }); + return; + } + const result = await provider.handle(); + if ("stateMachineArn" in event.ResourceProperties) { + console.info('Found "stateMachineArn", waiter statemachine started'); + return; + } else if ("expected" in event.ResourceProperties) { + console.info('Found "expected", testing assertions'); + const actualPath = event.ResourceProperties.actualPath; + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + try { + const assertionResult = await assertion.handle(); + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: { + ...assertionResult, + ...result + } + }); + return; + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + return; +} +async function onTimeout(timeoutEvent) { + const isCompleteRequest = JSON.parse(JSON.parse(timeoutEvent.Cause).errorMessage); + const provider = createResourceHandler(isCompleteRequest, standardContext); + await provider.respond({ + status: "FAILED", + reason: "Operation timed out: " + JSON.stringify(isCompleteRequest) + }); +} +async function isComplete(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + const result = await provider.handleIsComplete(); + const actualPath = event.ResourceProperties.actualPath; + if (result) { + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + if ("expected" in event.ResourceProperties) { + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + const assertionResult = await assertion.handleIsComplete(); + if (!(assertionResult == null ? void 0 : assertionResult.failed)) { + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: { + ...assertionResult, + ...result + } + }); + return; + } else { + console.log(`Assertion Failed: ${JSON.stringify(assertionResult)}`); + throw new Error(JSON.stringify(event)); + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } else { + console.log("No result"); + throw new Error(JSON.stringify(event)); + } + return; + } catch (e) { + console.log(e); + throw new Error(JSON.stringify(event)); + } +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new AwsApiCallHandler(event, context); + } else if (event.ResourceType.startsWith(ASSERT_RESOURCE_TYPE)) { + return new AssertionHandler(event, context); + } else { + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +var standardContext = { + getRemainingTimeInMillis: () => 9e4 +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler, + isComplete, + onTimeout +}); diff --git a/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.assets.json b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.assets.json new file mode 100644 index 0000000000000..05942c5715367 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.assets.json @@ -0,0 +1,45 @@ +{ + "version": "21.0.0", + "files": { + "8c04935671db7da18580bb01371198f54ead836061bcbac6b8bab4c2e4d1cf35": { + "source": { + "path": "asset.8c04935671db7da18580bb01371198f54ead836061bcbac6b8bab4c2e4d1cf35", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "8c04935671db7da18580bb01371198f54ead836061bcbac6b8bab4c2e4d1cf35.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "db453ad95b1e0fd185aa7ea88192ad521ced57f7bd163bcd0f002f20b441e424": { + "source": { + "path": "asset.db453ad95b1e0fd185aa7ea88192ad521ced57f7bd163bcd0f002f20b441e424", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "db453ad95b1e0fd185aa7ea88192ad521ced57f7bd163bcd0f002f20b441e424.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "d541f2115f9333bde1ef9d780fe0176c421e684cc754401b5de78b9a1d133b62": { + "source": { + "path": "integ-lambda-python-arm64.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d541f2115f9333bde1ef9d780fe0176c421e684cc754401b5de78b9a1d133b62.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/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.template.json b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.template.json new file mode 100644 index 0000000000000..cf22196370fe0 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ-lambda-python-arm64.template.json @@ -0,0 +1,134 @@ +{ + "Resources": { + "LayerB20D2F06": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "8c04935671db7da18580bb01371198f54ead836061bcbac6b8bab4c2e4d1cf35.zip" + }, + "CompatibleArchitectures": [ + "arm64" + ], + "CompatibleRuntimes": [ + "python3.9" + ] + } + }, + "HandlerServiceRoleFCDC14AE": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "Handler886CB40B": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "db453ad95b1e0fd185aa7ea88192ad521ced57f7bd163bcd0f002f20b441e424.zip" + }, + "Role": { + "Fn::GetAtt": [ + "HandlerServiceRoleFCDC14AE", + "Arn" + ] + }, + "Architectures": [ + "arm64" + ], + "Handler": "index.handler", + "Layers": [ + { + "Ref": "LayerB20D2F06" + } + ], + "Runtime": "python3.9" + }, + "DependsOn": [ + "HandlerServiceRoleFCDC14AE" + ] + } + }, + "Outputs": { + "FunctionArn": { + "Value": { + "Fn::GetAtt": [ + "Handler886CB40B", + "Arn" + ] + } + }, + "ExportsOutputRefHandler886CB40BD176DC16": { + "Value": { + "Ref": "Handler886CB40B" + }, + "Export": { + "Name": "integ-lambda-python-arm64:ExportsOutputRefHandler886CB40BD176DC16" + } + } + }, + "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/aws-lambda-python/test/function.arm64.integ.snapshot/integ.json b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ.json new file mode 100644 index 0000000000000..b8905e06295aa --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "21.0.0", + "testCases": { + "lambda-python-arm64/DefaultTest": { + "stacks": [ + "integ-lambda-python-arm64" + ], + "stackUpdateWorkflow": false, + "assertionStack": "lambda-python-arm64/DefaultTest/DeployAssert", + "assertionStackName": "lambdapythonarm64DefaultTestDeployAssertAB537F34" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.assets.json b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.assets.json new file mode 100644 index 0000000000000..34facd6820e94 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.assets.json @@ -0,0 +1,32 @@ +{ + "version": "21.0.0", + "files": { + "3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac": { + "source": { + "path": "asset.3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac.bundle", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "42975a3b062a8d0733fa13d5857f7aee684ba7fdbf7b4518fd0f50e039bdf92f": { + "source": { + "path": "lambdapythonarm64DefaultTestDeployAssertAB537F34.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "42975a3b062a8d0733fa13d5857f7aee684ba7fdbf7b4518fd0f50e039bdf92f.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/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.template.json b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.template.json new file mode 100644 index 0000000000000..634c49f636e24 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/lambdapythonarm64DefaultTestDeployAssertAB537F34.template.json @@ -0,0 +1,178 @@ +{ + "Resources": { + "LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5": { + "Type": "Custom::DeployAssert@SdkCallLambdainvoke", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "Lambda", + "api": "invoke", + "expected": "{\"$ObjectLike\":{\"Payload\":\"200\"}}", + "parameters": { + "FunctionName": { + "Fn::ImportValue": "integ-lambda-python-arm64:ExportsOutputRefHandler886CB40BD176DC16" + } + }, + "flattenResponse": "false", + "salt": "1666045944925" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5Invoke30065BC0": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::ImportValue": "integ-lambda-python-arm64:ExportsOutputRefHandler886CB40BD176DC16" + }, + "Principal": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { + "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": [ + { + "Action": [ + "lambda:Invoke" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "lambda:InvokeFunction" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":lambda:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":function:", + { + "Fn::ImportValue": "integ-lambda-python-arm64:ExportsOutputRefHandler886CB40BD176DC16" + } + ] + ] + } + ] + } + ] + } + } + ] + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "3074b79e05e7b98930b6449e01baa3e68b32ecff86328933c2542f7b7fe6fdac.zip" + }, + "Timeout": 120, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + } + }, + "Outputs": { + "AssertionResultsLambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5": { + "Value": { + "Fn::GetAtt": [ + "LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5", + "assertion" + ] + } + } + }, + "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/aws-lambda-python/test/function.arm64.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..2452d22b89c04 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/manifest.json @@ -0,0 +1,166 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "integ-lambda-python-arm64.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-lambda-python-arm64.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-lambda-python-arm64": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-lambda-python-arm64.template.json", + "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}/d541f2115f9333bde1ef9d780fe0176c421e684cc754401b5de78b9a1d133b62.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-lambda-python-arm64.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": [ + "integ-lambda-python-arm64.assets" + ], + "metadata": { + "/integ-lambda-python-arm64/Layer/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LayerB20D2F06" + } + ], + "/integ-lambda-python-arm64/Handler/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HandlerServiceRoleFCDC14AE" + } + ], + "/integ-lambda-python-arm64/Handler/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Handler886CB40B" + } + ], + "/integ-lambda-python-arm64/FunctionArn": [ + { + "type": "aws:cdk:logicalId", + "data": "FunctionArn" + } + ], + "/integ-lambda-python-arm64/Exports/Output{\"Ref\":\"Handler886CB40B\"}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputRefHandler886CB40BD176DC16" + } + ], + "/integ-lambda-python-arm64/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-lambda-python-arm64/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-lambda-python-arm64" + }, + "lambdapythonarm64DefaultTestDeployAssertAB537F34.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "lambdapythonarm64DefaultTestDeployAssertAB537F34.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "lambdapythonarm64DefaultTestDeployAssertAB537F34": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "lambdapythonarm64DefaultTestDeployAssertAB537F34.template.json", + "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}/42975a3b062a8d0733fa13d5857f7aee684ba7fdbf7b4518fd0f50e039bdf92f.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "lambdapythonarm64DefaultTestDeployAssertAB537F34.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": [ + "integ-lambda-python-arm64", + "lambdapythonarm64DefaultTestDeployAssertAB537F34.assets" + ], + "metadata": { + "/lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5" + } + ], + "/lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5Invoke30065BC0" + } + ], + "/lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsLambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5" + } + ], + "/lambda-python-arm64/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" + } + ], + "/lambda-python-arm64/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" + } + ], + "/lambda-python-arm64/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/lambda-python-arm64/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "lambda-python-arm64/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/tree.json new file mode 100644 index 0000000000000..9f3daef0226f3 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/function.arm64.integ.snapshot/tree.json @@ -0,0 +1,372 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.129" + } + }, + "integ-lambda-python-arm64": { + "id": "integ-lambda-python-arm64", + "path": "integ-lambda-python-arm64", + "children": { + "Layer": { + "id": "Layer", + "path": "integ-lambda-python-arm64/Layer", + "children": { + "Code": { + "id": "Code", + "path": "integ-lambda-python-arm64/Layer/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "integ-lambda-python-arm64/Layer/Code/Stage", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "integ-lambda-python-arm64/Layer/Code/AssetBucket", + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3-assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-lambda-python-arm64/Layer/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::LayerVersion", + "aws:cdk:cloudformation:props": { + "content": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "8c04935671db7da18580bb01371198f54ead836061bcbac6b8bab4c2e4d1cf35.zip" + }, + "compatibleArchitectures": [ + "arm64" + ], + "compatibleRuntimes": [ + "python3.9" + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.CfnLayerVersion", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda-python.PythonLayerVersion", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "integ-lambda-python-arm64/Handler", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "integ-lambda-python-arm64/Handler/ServiceRole", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-lambda-python-arm64/Handler/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" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Code": { + "id": "Code", + "path": "integ-lambda-python-arm64/Handler/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "integ-lambda-python-arm64/Handler/Code/Stage", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "integ-lambda-python-arm64/Handler/Code/AssetBucket", + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3-assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-lambda-python-arm64/Handler/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "db453ad95b1e0fd185aa7ea88192ad521ced57f7bd163bcd0f002f20b441e424.zip" + }, + "role": { + "Fn::GetAtt": [ + "HandlerServiceRoleFCDC14AE", + "Arn" + ] + }, + "architectures": [ + "arm64" + ], + "handler": "index.handler", + "layers": [ + { + "Ref": "LayerB20D2F06" + } + ], + "runtime": "python3.9" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda-python.PythonFunction", + "version": "0.0.0" + } + }, + "FunctionArn": { + "id": "FunctionArn", + "path": "integ-lambda-python-arm64/FunctionArn", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, + "Exports": { + "id": "Exports", + "path": "integ-lambda-python-arm64/Exports", + "children": { + "Output{\"Ref\":\"Handler886CB40B\"}": { + "id": "Output{\"Ref\":\"Handler886CB40B\"}", + "path": "integ-lambda-python-arm64/Exports/Output{\"Ref\":\"Handler886CB40B\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.129" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "lambda-python-arm64": { + "id": "lambda-python-arm64", + "path": "lambda-python-arm64", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "lambda-python-arm64/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "lambda-python-arm64/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.129" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "lambda-python-arm64/DefaultTest/DeployAssert", + "children": { + "LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5": { + "id": "LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.129" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/Default", + "children": { + "Default": { + "id": "Default", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "Invoke": { + "id": "Invoke", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/Invoke", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "AssertionResults": { + "id": "AssertionResults", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/LambdaInvoke35ae3a38b93bc81e7b95e90f43eeaec5/AssertionResults", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.LambdaInvokeFunction", + "version": "0.0.0" + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81": { + "id": "SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "children": { + "Staging": { + "id": "Staging", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "lambda-python-arm64/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.129" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.arm64.ts b/packages/@aws-cdk/aws-lambda-python/test/integ.function.arm64.ts new file mode 100644 index 0000000000000..bc82d66404e9e --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.arm64.ts @@ -0,0 +1,53 @@ +import * as path from 'path'; +import { Architecture, Runtime } from '@aws-cdk/aws-lambda'; +import { App, CfnOutput, Stack, StackProps } from '@aws-cdk/core'; +import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as lambda from '../lib'; + +/* + * Stack verification steps: + * * aws lambda invoke --function-name --invocation-type Event --payload '"OK"' response.json + */ + +class TestStack extends Stack { + public readonly functionName: string; + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const layer = new lambda.PythonLayerVersion(this, 'Layer', { + entry: path.join(__dirname, 'lambda-handler-arm64/layer'), + compatibleRuntimes: [Runtime.PYTHON_3_9], + compatibleArchitectures: [Architecture.ARM_64], + }); + + const fn = new lambda.PythonFunction(this, 'Handler', { + entry: path.join(__dirname, 'lambda-handler-arm64/handler'), + runtime: Runtime.PYTHON_3_9, + architecture: Architecture.ARM_64, + layers: [layer], + }); + this.functionName = fn.functionName; + + new CfnOutput(this, 'FunctionArn', { + value: fn.functionArn, + }); + } +} + +const app = new App(); +const testCase = new TestStack(app, 'integ-lambda-python-arm64'); +const integ = new IntegTest(app, 'lambda-python-arm64', { + testCases: [testCase], + stackUpdateWorkflow: false, +}); + +const invoke = integ.assertions.invokeFunction({ + functionName: testCase.functionName, +}); + +invoke.expect(ExpectedResult.objectLike({ + Payload: '200', +})); + +app.synth(); diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/handler/index.py b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/handler/index.py new file mode 100644 index 0000000000000..5fbb2425351f2 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/handler/index.py @@ -0,0 +1,16 @@ +from itertools import count +from flupy import flu + +def handler(event, context): + # Example flupy processing an infinite sequence + pipeline = ( + flu(count()) + .map(lambda x: x**2) + .filter(lambda x: x % 517 == 0) + .chunk(5) + .take(3) + ) + for item in pipeline: + print(item) + + return 200 diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/layer/requirements.txt b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/layer/requirements.txt new file mode 100644 index 0000000000000..483953ca1676f --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-arm64/layer/requirements.txt @@ -0,0 +1,4 @@ +# Lock versions of pip packages +typing-extensions==4.4.0 +# Used by this lambda +flupy==1.1.9 diff --git a/packages/@aws-cdk/aws-lambda-python/test/packaging.test.ts b/packages/@aws-cdk/aws-lambda-python/test/packaging.test.ts index 4054bd082f131..ac9d26f7db6ce 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/packaging.test.ts +++ b/packages/@aws-cdk/aws-lambda-python/test/packaging.test.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import { Packaging } from '../lib/packaging'; -test('Packging with no dependencies', () => { +test('Packaging with no dependencies', () => { const entry = path.join(__dirname, 'lambda-handler-nodeps'); const packaging = Packaging.fromEntry(entry); @@ -9,7 +9,7 @@ test('Packging with no dependencies', () => { expect(packaging).toEqual(Packaging.withNoPackaging()); }); -test('Packging with requirements.txt', () => { +test('Packaging with requirements.txt', () => { const entry = path.join(__dirname, 'lambda-handler'); const packaging = Packaging.fromEntry(entry); @@ -17,7 +17,7 @@ test('Packging with requirements.txt', () => { expect(packaging).toEqual(Packaging.withPip()); }); -test('Packging with pipenv', () => { +test('Packaging with pipenv', () => { const entry = path.join(__dirname, 'lambda-handler-pipenv'); const packaging = Packaging.fromEntry(entry); @@ -25,7 +25,7 @@ test('Packging with pipenv', () => { expect(packaging).toEqual(Packaging.withPipenv()); }); -test('Packging with poetry', () => { +test('Packaging with poetry', () => { const entry = path.join(__dirname, 'lambda-handler-poetry'); const packaging = Packaging.fromEntry(entry);