From 3822c855cf92ee0cd4539dee33e55f57d995bf89 Mon Sep 17 00:00:00 2001 From: Setu Shah Date: Mon, 10 Jan 2022 10:23:01 -0800 Subject: [PATCH] fix(lambda-python): bundle asset files correctly (#18335) Asset files are incorrectly being bundled under the `asset-input` directory instead of the root of the bundle. To also copy over hidden files (https://github.com/aws/aws-cdk/pull/18306#discussion_r780186564), I switched from using `-R` to `-a` based on what I found on [SO](https://stackoverflow.com/a/13020113) and the [man page](https://linux.die.net/man/1/cp). (`-a` is equivalent to `-dR`.) Fixes #18301 and @chrispykim's comment: https://github.com/aws/aws-cdk/pull/18082#issuecomment-1008442363. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-lambda-python/lib/bundling.ts | 2 +- .../aws-lambda-python/test/bundling.test.ts | 46 ++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts b/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts index 34dd54e3eee98..ddd3e167204c9 100644 --- a/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts +++ b/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts @@ -87,7 +87,7 @@ export class Bundling implements CdkBundlingOptions { if (packaging.dependenciesFile) { bundlingCommands.push(`python -m pip install -r ${DependenciesFile.PIP} -t ${options.outputDir}`); } - bundlingCommands.push(`cp -R ${options.inputDir}/ ${options.outputDir}`); + bundlingCommands.push(`cp -rT ${options.inputDir}/ ${options.outputDir}`); return bundlingCommands; } } diff --git a/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts index e5f91b2ea09b8..4af556b3b9a62 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts @@ -1,3 +1,4 @@ +import * as fs from 'fs'; import * as path from 'path'; import { Architecture, Code, Runtime } from '@aws-cdk/aws-lambda'; import { DockerImage } from '@aws-cdk/core'; @@ -25,7 +26,7 @@ beforeEach(() => { test('Bundling a function without dependencies', () => { const entry = path.join(__dirname, 'lambda-handler-nodeps'); - Bundling.bundle({ + const assetCode = Bundling.bundle({ entry: entry, runtime: Runtime.PYTHON_3_7, architecture: Architecture.X86_64, @@ -36,7 +37,7 @@ test('Bundling a function without dependencies', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'cp -R /asset-input/ /asset-output', + 'cp -rT /asset-input/ /asset-output', ], }), })); @@ -47,11 +48,14 @@ test('Bundling a function without dependencies', () => { }), platform: 'linux/amd64', })); + + const files = fs.readdirSync(assetCode.path); + expect(files).toContain('index.py'); }); test('Bundling a function with requirements.txt', () => { const entry = path.join(__dirname, 'lambda-handler'); - Bundling.bundle({ + const assetCode = Bundling.bundle({ entry: entry, runtime: Runtime.PYTHON_3_7, architecture: Architecture.X86_64, @@ -62,10 +66,14 @@ test('Bundling a function with requirements.txt', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'python -m pip install -r requirements.txt -t /asset-output && cp -R /asset-input/ /asset-output', + 'python -m pip install -r requirements.txt -t /asset-output && cp -rT /asset-input/ /asset-output', ], }), })); + + const files = fs.readdirSync(assetCode.path); + expect(files).toContain('index.py'); + expect(files).toContain('requirements.txt'); }); test('Bundling Python 2.7 with requirements.txt installed', () => { @@ -81,7 +89,7 @@ test('Bundling Python 2.7 with requirements.txt installed', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'python -m pip install -r requirements.txt -t /asset-output && cp -R /asset-input/ /asset-output', + 'python -m pip install -r requirements.txt -t /asset-output && cp -rT /asset-input/ /asset-output', ], }), })); @@ -101,7 +109,7 @@ test('Bundling a layer with dependencies', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'python -m pip install -r requirements.txt -t /asset-output/python && cp -R /asset-input/ /asset-output/python', + 'python -m pip install -r requirements.txt -t /asset-output/python && cp -rT /asset-input/ /asset-output/python', ], }), })); @@ -121,7 +129,7 @@ test('Bundling a python code layer', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'cp -R /asset-input/ /asset-output/python', + 'cp -rT /asset-input/ /asset-output/python', ], }), })); @@ -130,7 +138,7 @@ test('Bundling a python code layer', () => { test('Bundling a function with pipenv dependencies', () => { const entry = path.join(__dirname, 'lambda-handler-pipenv'); - Bundling.bundle({ + const assetCode = Bundling.bundle({ entry: path.join(entry, '.'), runtime: Runtime.PYTHON_3_9, architecture: Architecture.X86_64, @@ -141,16 +149,23 @@ test('Bundling a function with pipenv dependencies', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'PIPENV_VENV_IN_PROJECT=1 pipenv lock -r > requirements.txt && rm -rf .venv && python -m pip install -r requirements.txt -t /asset-output/python && cp -R /asset-input/ /asset-output/python', + 'PIPENV_VENV_IN_PROJECT=1 pipenv lock -r > requirements.txt && rm -rf .venv && python -m pip install -r requirements.txt -t /asset-output/python && cp -rT /asset-input/ /asset-output/python', ], }), })); + + const files = fs.readdirSync(assetCode.path); + expect(files).toContain('index.py'); + expect(files).toContain('Pipfile'); + expect(files).toContain('Pipfile.lock'); + // Contains hidden files. + expect(files).toContain('.gitignore'); }); test('Bundling a function with poetry dependencies', () => { const entry = path.join(__dirname, 'lambda-handler-poetry'); - Bundling.bundle({ + const assetCode = Bundling.bundle({ entry: path.join(entry, '.'), runtime: Runtime.PYTHON_3_9, architecture: Architecture.X86_64, @@ -161,10 +176,17 @@ test('Bundling a function with poetry dependencies', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'poetry export --with-credentials --format requirements.txt --output requirements.txt && python -m pip install -r requirements.txt -t /asset-output/python && cp -R /asset-input/ /asset-output/python', + 'poetry export --with-credentials --format requirements.txt --output requirements.txt && python -m pip install -r requirements.txt -t /asset-output/python && cp -rT /asset-input/ /asset-output/python', ], }), })); + + const files = fs.readdirSync(assetCode.path); + expect(files).toContain('index.py'); + expect(files).toContain('pyproject.toml'); + expect(files).toContain('poetry.lock'); + // Contains hidden files. + expect(files).toContain('.gitignore'); }); test('Bundling a function with custom bundling image', () => { @@ -184,7 +206,7 @@ test('Bundling a function with custom bundling image', () => { image, command: [ 'bash', '-c', - 'python -m pip install -r requirements.txt -t /asset-output/python && cp -R /asset-input/ /asset-output/python', + 'python -m pip install -r requirements.txt -t /asset-output/python && cp -rT /asset-input/ /asset-output/python', ], }), }));