Skip to content

feat(layers): introduce new CDK Python constructor for Powertools Lambda Layer #5320

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

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 0 additions & 2 deletions .github/workflows/quality_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ on:
- "mypy.ini"
branches:
- develop
- v3
push:
paths:
- "aws_lambda_powertools/**"
Expand All @@ -37,7 +36,6 @@ on:
- "mypy.ini"
branches:
- develop
- v3

permissions:
contents: read
Expand Down
70 changes: 70 additions & 0 deletions .github/workflows/quality_code_cdk_constructor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Code quality - CDK constructor

# PROCESS
#
# 1. Install all dependencies and spin off containers for all supported Python versions
# 2. Run code formatters and linters (various checks) for code standard
# 3. Run static typing checker for potential bugs
# 4. Run tests

# USAGE
#
# Always triggered on new PRs, PR changes and PR merge.


on:
pull_request:
paths:
- "layer/layer_constructors/**"
branches:
- develop
push:
paths:
- "layer/layer_constructors/**"
branches:
- develop

permissions:
contents: read

jobs:
quality_check:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
env:
PYTHON: "${{ matrix.python-version }}"
permissions:
contents: read # checkout code only
defaults:
run:
working-directory: ./layer_v3/layer_constructors
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Install poetry
run: pipx install poetry
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
with:
python-version: ${{ matrix.python-version }}
cache: "poetry"
- name: Set up QEMU
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.0.0
with:
platforms: arm64
# NOTE: we need QEMU to build Layer against a different architecture (e.g., ARM)
- name: Set up Docker Buildx
id: builder
uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # v2.4.1
with:
install: true
driver: docker
platforms: linux/amd64,linux/arm64
- name: Install dependencies
run: |
pip install --upgrade pip pre-commit poetry
poetry install
- name: Test with pytest
run: poetry run pytest tests
Empty file added layer_v3/__init__.py
Empty file.
9 changes: 5 additions & 4 deletions layer_v3/layer/layer_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
)
from aws_cdk.aws_lambda import Architecture, CfnLayerVersionPermission, Runtime
from aws_cdk.aws_ssm import StringParameter
from cdk_aws_lambda_powertools_layer import LambdaPowertoolsLayerPythonV3
from constructs import Construct

from layer_v3.layer_constructors.layer_stack import LambdaPowertoolsLayerPythonV3


@jsii.implements(IAspect)
class ApplyCondition:
Expand Down Expand Up @@ -46,11 +47,11 @@ def __init__(
layer = LambdaPowertoolsLayerPythonV3(
self,
"Layer",
layer_version_name=layer_version_name,
version=powertools_version,
layer_name=layer_version_name,
powertools_version=powertools_version,
python_version=python_version,
include_extras=True,
compatible_architectures=[architecture] if architecture else [],
architecture=architecture or Architecture.X86_64,
)
layer.apply_removal_policy(RemovalPolicy.RETAIN)

Expand Down
Empty file.
45 changes: 45 additions & 0 deletions layer_v3/layer_constructors/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import annotations


def construct_build_args(include_extras: bool = True, version: str | None = None) -> str:
"""
This function creates a suffix string for the Powertools package based on
whether extra dependencies should be included and a specific version is required.

Params
------
include_extras: bool | None:
If True, include all extra dependencies in Powertools package
version: str | None
The version of Powertools to install. Can be a version number or a git reference.

Returns
-------
str
A string suffix to be appended to the Powertools package name during installation.
Examples:
- "" (empty string) if no extras or version specified
- "[all]" if include_extras is True
- "==1.2.3" if version is "1.2.3"
- "[all]==1.2.3" if include_extras is True and version is "1.2.3"
- " @ git+https://github.com/..." if version starts with "git"

Example
-------
>>> construct_build_args(True, "1.2.3")
'[all]==1.2.3'
>>> construct_build_args(False, "git+https://github.com/...")
' @ git+https://github.com/...'
"""

suffix = ""

if include_extras:
suffix = "[all]"
if version:
if version.startswith("git"):
suffix = f"{suffix} @ {version}"
else:
suffix = f"{suffix}=={version}"

return suffix
83 changes: 83 additions & 0 deletions layer_v3/layer_constructors/layer_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

from aws_cdk import aws_lambda as lambda_

if TYPE_CHECKING:
from constructs import Construct

from layer_v3.layer_constructors.helpers import construct_build_args


class LambdaPowertoolsLayerPythonV3(lambda_.LayerVersion):
"""
A CDK Stack that creates a Lambda Layer for Powertools for AWS Lambda (Python) V3.

This stack creates a Lambda Layer containing the Powertools for AWS Lambda (Python) V3 library.
It allows customization of the Python runtime version, inclusion of extra dependencies,
architecture, Powertools version, and layer name.

Attributes:
scope (Construct): The scope in which to define this construct.
construct_id (str): The scoped construct ID. Must be unique amongst siblings in the same scope.
python_version (lambda_.Runtime): The Python runtime version for the layer. Defaults to Python 3.12.
include_extras (bool): Whether to include extra dependencies. Defaults to True.
architecture (lambda_.Architecture): The compatible Lambda architecture. Defaults to x86_64.
powertools_version (str): The version of Powertools to use. If empty, uses the latest version.
layer_name (str): Custom name for the Lambda Layer. If empty, a default name will be used.

Example:
>>> app = cdk.App()
>>> LambdaPowertoolsLayerPythonV3(app, "PowertoolsLayer",
... python_version=lambda_.Runtime.PYTHON_3_11,
... include_extras=False,
... architecture=lambda_.Architecture.ARM_64,
... powertools_version="2.10.0",
... layer_name="MyCustomPowertoolsLayer")

"""

def __init__(
self,
scope: Construct,
construct_id: str,
python_version: lambda_.Runtime = lambda_.Runtime.PYTHON_3_12,
include_extras: bool = True,
architecture: lambda_.Architecture = lambda_.Architecture.X86_64,
powertools_version: str = "",
layer_name: str = "",
) -> None:

docker_file_path = str(Path(__file__).parent.parent / "docker")

python_normalized_version: str = python_version.to_string().replace("python", "")

if architecture.to_string() == "x86_64":
docker_architecture: str = "linux/amd64"
else:
docker_architecture: str = "linux/arm64"

super().__init__(
scope,
construct_id,
code=lambda_.Code.from_docker_build(
docker_file_path,
build_args={
"PACKAGE_SUFFIX": construct_build_args(
include_extras,
powertools_version,
),
"PYTHON_VERSION": python_normalized_version,
},
platform=docker_architecture,
),
layer_version_name=layer_name,
license="MIT-0",
compatible_runtimes=[python_version],
description=f"Powertools for AWS Lambda (Python) V3 [{architecture.to_string()} - Python {python_normalized_version}]" # noqa E501
+ (" with extra dependencies" if include_extras else "")
+ (f" version {powertools_version}" if powertools_version else " latest version"),
compatible_architectures=[architecture] if architecture else None,
)
Empty file.
Empty file.
Loading