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

Allow Lambda targets to point to python_aws_lambda_layers and exclude code automatically #19256

Open
huonw opened this issue Jun 6, 2023 · 3 comments
Labels
backend: Python Python backend-related issues enhancement

Comments

@huonw
Copy link
Contributor

huonw commented Jun 6, 2023

Is your feature request related to a problem? Please describe.

In #19123, Pants grows support for python_aws_lambda_layer to package layers for AWS Lambda. These are a bunch of code that's loaded into the Lambda environment, in addition to the core package. When packaging an in-repo layer for use with in-repo functions, typically the function doesn't need to include the code in the layer. Pants could automatically manage this, without needing to specify include_requirements=False and/or dependencies=["!!foo", "!!bar"].

Describe the solution you'd like

Potentially a layers field that takes a list of layer targets. To begin with, this can be just used for excluding the dependencies. This field can be used on both functions and other layers.

(NB. a package built like this is cannot be used in isolation, only with the right layers hooked into the deployed function in AWS.)

For instance, in this example:

  • boto3-layer includes the boto3 library and its dependencies, like requests
  • func depends on boto3, requests and numpy

The layers field means pants would notice that boto3 (and, preferably, its transitive dependencies like requests, which may require #12733) will already be provided by boto3-layer, and thus only include numpy and foo.py in the func package.

# BUILD
python_requirements(name="reqs") # includes boto3, requests, numpy, etc.

python_aws_lambda_layer(
    name="boto3-layer"
    dependencies=[":reqs#boto3"],
    ...
)

python_aws_lambda_function(
    name="func",
    handler="./foo.py:handler",
    layers=[":boto3-layer"], # NEW FEATURE
    ...
)
# foo.py
import boto3
import requests
import numpy

def handler(...): ...

Describe alternatives you've considered

The current approach of manually spelling out dependencies and exclusions probably works fine for many cases, e.g. a naive "requirements in a layer"/"sources in the function" split is trivial via include_requirements/include_sources.

Additional context
There's a long discussion in #19123 that touches on this.

@huonw huonw added the backend: Python Python backend-related issues label Jun 6, 2023
@huonw huonw changed the title Allow python_aws_lambda_function to point to python_aws_lambda_layers and exclude code automatically Allow Lambda targets to point to python_aws_lambda_layers and exclude code automatically Jun 6, 2023
@huonw
Copy link
Contributor Author

huonw commented Jun 8, 2023

and, preferably, its transitive dependencies like requests, which may require #12733

Doing this probably also requires pex-tool/pex#2097: e.g. pants would enumerate all the requirements in the parent layer(s), and tell PEX to specifically not include them (against it's better judgement 😅 ).

@huonw
Copy link
Contributor Author

huonw commented Aug 5, 2023

This would make it easy to have code implicitly use the packages that AWS automatically provides (e.g. boto3), or an external-provided layer (e.g. https://docs.powertools.aws.dev/lambda/python/latest/#lambda-layer) by reifying those layers, so pants can reason about them, but not using them when publishing:

# dependencies provided by the AWS Lambda runtime
python_aws_lambda_layer(
    name="aws-runtime-implicit-layer"
    dependencies=[":reqs#boto3", ...]
)
# dependencies provided by the AWS-published Powertools layer
python_aws_lambda_layer(
    name="aws-powertools-implicit-layer"
    dependencies=[":reqs#aws-lambda-powertools", ...],
    layers=[":aws-runtime-implicit-layer"]
)

python_aws_lambda_layer(
    name="func-deps"
    layers=[":aws-powertools-implicit-layer"], 
    dependencies=["./foo.py"]
)
python_aws_lambda_function(
    name="func",
    dependencies=["./foo.py"]
    layers=[":func-deps"], 
)

Then, to deploy:

  1. only func-deps is published as a layer (not aws-powertools-implicit-layer or aws-runtime-implicit-layer)
  2. func is published as a function, referencing the AWS-published Powertools layer and the func-deps layer

If this is a useful pattern, Pants can presumably grow automatic management of it (although if pants is managing it, it'll need to have a point of view on version skew between the layers and the runtime environment, e.g. I assume the AWS runtime updates boto3 semi-regularly, and thus may be on a different version to what the user's code specifies).

@huonw
Copy link
Contributor Author

huonw commented Nov 12, 2023

With pex 2.1.151 (https://github.com/pantsbuild/pex/releases/tag/v2.1.151), which Pants now includes (#20149), there's a flag --exclude that could be used as a building block here: if Pants knows which dependencies are provided by a target environment, they can be --exclude'd.

There's also future plans for --provided (pex-tool/pex#2097 (comment)) too, but I'm not sure it's useful for this, as these lambda artifacts have no PEX initialisation and thus there's little value in --provided, that's able to verify that they actually exist at runtime.

huonw added a commit that referenced this issue May 23, 2024
Similar to #20237, this allows passing arbitrary arguments to the first
pex invocation when building a FaaS (AWS Lambda or Google Cloud
Function) artifact.

This can serve as a stop-gap for #19256, because a user can write then
pass `--exclude` themselves when they really need that (or similar)
functionality.

---------

Co-authored-by: Huon Wilson <huon@exoflare.io>
cburroughs added a commit that referenced this issue Jul 23, 2024
Highlighted changelogs:
 * https://github.com/pex-tool/pex/releases/tag/v2.4.0
 * https://github.com/pex-tool/pex/releases/tag/v2.4.1
 * https://github.com/pex-tool/pex/releases/tag/v2.5.0
 * https://github.com/pex-tool/pex/releases/tag/v2.6.0
 * https://github.com/pex-tool/pex/releases/tag/v2.7.0
 * https://github.com/pex-tool/pex/releases/tag/v2.8.0
 * https://github.com/pex-tool/pex/releases/tag/v2.8.1
 * https://github.com/pex-tool/pex/releases/tag/v2.9.0
 * https://github.com/pex-tool/pex/releases/tag/v2.10.0
 * https://github.com/pex-tool/pex/releases/tag/v2.10.1
 * https://github.com/pex-tool/pex/releases/tag/v2.11.0 (!)

```
Lockfile diff: 3rdparty/python/user_reqs.lock [python-default]

==                    Upgraded dependencies                     ==

  certifi                        2024.6.2     -->   2024.7.4
  exceptiongroup                 1.2.1        -->   1.2.2
  pex                            2.3.3        -->   2.11.0
  pydantic                       1.10.16      -->   1.10.17
```

NOTE: This updates the scrupulously backwards compatible Pex, but does
not use any new features yet. The minimum version is unchanged.

Possibly relevant for: #15704 #21103 #20852 #15454 #21100 #11324 #19256
#19552 #19681
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend: Python Python backend-related issues enhancement
Projects
None yet
Development

No branches or pull requests

1 participant