Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lambda-python): support for providing a custom bundling docker image #18082

Merged
merged 43 commits into from
Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
6bc24d1
feat: refactor bundling to use `cdk.BundlingOptions`
setu4993 Dec 18, 2021
1d018bb
fix: Use updated `Bundling` class
setu4993 Dec 18, 2021
4123034
test: Update tests to use updated `Bundling` class
setu4993 Dec 18, 2021
69ebdaa
feat: Add new `Packaging` class
setu4993 Dec 25, 2021
97f1509
refactor: Bundling to use `Packaging`, align with other Lambda functions
setu4993 Dec 25, 2021
d9b306e
test: Add tests for `Packaging`
setu4993 Dec 25, 2021
a4a2feb
test: Add, update bundling tests
setu4993 Dec 25, 2021
b0d9432
test: Add, update function, layer tests
setu4993 Dec 25, 2021
4e7868f
fix: Remove integ test that isn't applicable
setu4993 Dec 25, 2021
00f8bdb
feat: Split, export `BundlingOptions`
setu4993 Dec 25, 2021
bddeaa5
test: Fix function tests
setu4993 Dec 25, 2021
21ddb3b
test: Add bundling, integ test for custom Docker image
setu4993 Dec 25, 2021
f8e2a14
test: Ignore reqs.txt generated during test runs
setu4993 Dec 25, 2021
a6ddd7e
fix: Add default value for `Runtime`
setu4993 Dec 28, 2021
8fadde5
feat: Add bundling as an option for `PythonLayerVersion`
setu4993 Dec 28, 2021
4357764
test: Add tests for validating custom images are used for functions a…
setu4993 Dec 28, 2021
1c55a11
docs: Add documentation for bundling, update for packaging
setu4993 Dec 28, 2021
ef448b9
docs: Add docstrings for `Packaging`
setu4993 Dec 28, 2021
f92fb65
fix: Specify default `outputPathSuffix`
setu4993 Dec 28, 2021
5da5804
fix: Remove commented line
setu4993 Dec 28, 2021
90a8717
fix: docstring for `image`
setu4993 Dec 28, 2021
efceeb8
test: Verify bundling image was used
setu4993 Dec 28, 2021
a9f6d82
docs: Fix example to match fixture
setu4993 Dec 28, 2021
2ecff2c
fix: Add `DockerImage` to default fixture
setu4993 Dec 29, 2021
bf0dc2e
test: Add response to nodeps test
setu4993 Dec 29, 2021
8253cd9
test: Complete `custom_index.py` to match nodeps
setu4993 Dec 29, 2021
3c457be
test: Add integration test for no dependencies
setu4993 Dec 29, 2021
889fc19
test: Add integration test for sub-dir package
setu4993 Dec 29, 2021
eb4cb42
test: Update integration tests' expected output
setu4993 Dec 29, 2021
5bfaf9d
fix: Resolve handler to improve sub-dir handling
setu4993 Dec 29, 2021
7d64d53
test: Update integration tests' expected templates
setu4993 Dec 29, 2021
7a03fb3
fix: Make `runtime` required
setu4993 Dec 29, 2021
4c4b752
fix: Improve default docstring for `dependenciesFile`
setu4993 Dec 29, 2021
1c90313
feat: Support custom `buildArgs`
setu4993 Dec 29, 2021
518c347
test: Add custom build args test
setu4993 Dec 29, 2021
f0d882a
docs: Add details about build args
setu4993 Dec 30, 2021
bc17ce4
docs: Add example on using Code Artifact
setu4993 Dec 30, 2021
7376188
docs: Fix scope reference
setu4993 Dec 30, 2021
8b701d4
feat: Support customizing `HTTPS_PROXY`
setu4993 Dec 30, 2021
e9cd424
test: Update integ tests' JSON
setu4993 Dec 30, 2021
3f73b0a
docs: `PythonFunction` -> `lambda.PythonFunction`
setu4993 Dec 30, 2021
5e48214
fix: Set region explicitly
setu4993 Dec 30, 2021
b94dbac
Merge branch 'master' into feat/lambda-python/improve-bundling
mergify[bot] Dec 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 103 additions & 24 deletions packages/@aws-cdk/aws-lambda-python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,61 @@ Define a `PythonFunction`:
```ts
new lambda.PythonFunction(this, 'MyFunction', {
entry: '/path/to/my/function', // required
runtime: Runtime.PYTHON_3_6, // required
setu4993 marked this conversation as resolved.
Show resolved Hide resolved
runtime: Runtime.PYTHON_3_8, // required
index: 'my_index.py', // optional, defaults to 'index.py'
handler: 'my_exported_func', // optional, defaults to 'handler'
});
```

All other properties of `lambda.Function` are supported, see also the [AWS Lambda construct library](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-lambda).

## Module Dependencies
## Python Layer

If `requirements.txt` or `Pipfile` exists at the entry path, the construct will handle installing
all required modules in a [Lambda compatible Docker container](https://gallery.ecr.aws/sam/build-python3.7)
according to the `runtime` and with the Docker platform based on the target architecture of the Lambda function.
You may create a python-based lambda layer with `PythonLayerVersion`. If `PythonLayerVersion` detects a `requirements.txt`
or `Pipfile` or `poetry.lock` with the associated `pyproject.toml` at the entry path, then `PythonLayerVersion` will include the dependencies inline with your code in the
layer.

Define a `PythonLayerVersion`:

```ts
new lambda.PythonLayerVersion(this, 'MyLayer', {
entry: '/path/to/my/layer', // point this to your library's directory
})
Comment on lines +46 to +49
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this example.

```

A layer can also be used as a part of a `PythonFunction`:

```ts
new lambda.PythonFunction(this, 'MyFunction', {
entry: '/path/to/my/function',
runtime: Runtime.PYTHON_3_8,
layers: [
new lambda.PythonLayerVersion(this, 'MyLayer', {
entry: '/path/to/my/layer', // point this to your library's directory
}),
],
});
```

## Packaging

If `requirements.txt`, `Pipfile` or `poetry.lock` exists at the entry path, the construct will handle installing all required modules in a [Lambda compatible Docker container](https://gallery.ecr.aws/sam/build-python3.7) according to the `runtime` and with the Docker platform based on the target architecture of the Lambda function.

Python bundles are only recreated and published when a file in a source directory has changed.
Therefore (and as a general best-practice), it is highly recommended to commit a lockfile with a
list of all transitive dependencies and their exact versions.
This will ensure that when any dependency version is updated, the bundle asset is recreated and uploaded.
list of all transitive dependencies and their exact versions. This will ensure that when any dependency version is updated, the bundle asset is recreated and uploaded.

To that end, we recommend using [`pipenv`] or [`poetry`] which have lockfile support.

- [`pipenv`](https://pipenv-fork.readthedocs.io/en/latest/basics.html#example-pipfile-lock)
- [`poetry`](https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control)

To that end, we recommend using [`pipenv`] or [`poetry`] which has lockfile support.
Packaging is executed using the `Packaging` class, which:

[`pipenv`]: https://pipenv-fork.readthedocs.io/en/latest/basics.html#example-pipfile-lock
[`poetry`]: https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
1. Infers the packaging type based on the files present.
2. If it sees a `Pipfile` or a `poetry.lock` file, it exports it to a compatible `requirements.txt` file with credentials (if they're available in the source files or in the bundling container).
3. Installs dependencies using `pip`.
4. Copies the dependencies into an asset that is bundled for the Lambda package.

**Lambda with a requirements.txt**

Expand All @@ -73,24 +105,71 @@ To that end, we recommend using [`pipenv`] or [`poetry`] which has lockfile supp
```plaintext
.
├── lambda_function.py # exports a function named 'handler'
├── pyproject.toml # has to be present at the entry path
├── poetry.lock # your poetry lock file
├── pyproject.toml # your poetry project definition
├── poetry.lock # your poetry lock file has to be present at the entry path
```

**Lambda Layer Support**
## Custom Bundling

You may create a python-based lambda layer with `PythonLayerVersion`. If `PythonLayerVersion` detects a `requirements.txt`
or `Pipfile` or `poetry.lock` with the associated `pyproject.toml` at the entry path, then `PythonLayerVersion` will include the dependencies inline with your code in the
layer.
Custom bundling can be performed by passing in additional build arguments that point to index URLs to private repos, or by using an entirely custom Docker images for bundling dependencies. The build args currently supported are:

- `PIP_INDEX_URL`
- `PIP_EXTRA_INDEX_URL`
- `HTTPS_PROXY`

Additional build args for bundling that refer to PyPI indexes can be specified as:

```ts
new lambda.PythonFunction(this, 'MyFunction', {
entry: '/path/to/my/function',
runtime: Runtime.PYTHON_3_6,
layers: [
new lambda.PythonLayerVersion(this, 'MyLayer', {
entry: '/path/to/my/layer', // point this to your library's directory
}),
],
const entry = '/path/to/function';
const image = DockerImage.fromBuild(entry);

new lambda.PythonFunction(this, 'function', {
entry,
runtime: Runtime.PYTHON_3_8,
bundling: {
buildArgs: { PIP_INDEX_URL: "https://your.index.url/simple/", PIP_EXTRA_INDEX_URL: "https://your.extra-index.url/simple/" },
},
});
```

If using a custom Docker image for bundling, the dependencies are installed with `pip`, `pipenv` or `poetry` by using the `Packaging` class. A different bundling Docker image that is in the same directory as the function can be specified as:

```ts
const entry = '/path/to/function';
const image = DockerImage.fromBuild(entry);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be cool to address some of the open issues in this example. I know there are a couple of open issues that ask about providing custom build args that we could show.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an example for using Code Artifact below, which was also what brought me to contributing this. And added documentation for customizing other build args.


new lambda.PythonFunction(this, 'function', {
entry,
runtime: Runtime.PYTHON_3_8,
bundling: { image },
});
```

## Custom Bundling with Code Artifact

To use a Code Artifact PyPI repo, the `PIP_INDEX_URL` for bundling the function can be customized (requires AWS CLI in the build environment):

```ts
import { execSync } from 'child_process';

const entry = '/path/to/function';
const image = DockerImage.fromBuild(entry);

const domain = 'my-domain';
const domainOwner = '111122223333';
const repoName = 'my_repo';
const region = 'us-east-1';
const codeArtifactAuthToken = execSync(`aws codeartifact get-authorization-token --domain ${domain} --domain-owner ${domainOwner} --query authorizationToken --output text`).toString().trim();

const indexUrl = `https://aws:${codeArtifactAuthToken}@${domain}-${domainOwner}.d.codeartifact.${region}.amazonaws.com/pypi/${repoName}/simple/`;

new lambda.PythonFunction(this, 'function', {
entry,
runtime: Runtime.PYTHON_3_8,
bundling: {
buildArgs: { PIP_INDEX_URL: indexUrl },
},
});
```

This type of an example should work for `pip` and `poetry` based dependencies, but will not work for `pipenv`.
9 changes: 7 additions & 2 deletions packages/@aws-cdk/aws-lambda-python/lib/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
ARG IMAGE=public.ecr.aws/sam/build-python3.7
FROM $IMAGE

# Ensure rsync is installed
RUN yum -q list installed rsync &>/dev/null || yum install -y rsync
ARG PIP_INDEX_URL
ARG PIP_EXTRA_INDEX_URL
ARG HTTPS_PROXY

# Upgrade pip (required by cryptography v3.4 and above, which is a dependency of poetry)
RUN pip install --upgrade pip
RUN pip install pipenv poetry

CMD [ "python" ]
22 changes: 0 additions & 22 deletions packages/@aws-cdk/aws-lambda-python/lib/Dockerfile.dependencies

This file was deleted.

Loading