diff --git a/.mypy.ini b/.mypy.ini index ee2f27fdd5..87d4b032d4 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -21,7 +21,6 @@ modules = azul.health, azul.hmac, azul.http, - azul.infra.lambda_layer, azul.lambdas, azul.modules, azul.oauth2, diff --git a/Makefile b/Makefile index 46a163e011..d56cee8f84 100644 --- a/Makefile +++ b/Makefile @@ -69,24 +69,10 @@ requirements_update: check_venv check_docker # been updated. Not using sed because Darwin's sed does not do -i. git restore requirements.trans.txt requirements.dev.trans.txt perl -i -p -e 's/^(?!#)/#/' requirements.trans.txt requirements.dev.trans.txt -# Since we're building for the x86_64 Lambda runtime, we should do so on an -# image for that architecture, hence the default platform override below. The -# override slows down this make target considerably on ARM hosts like Apple -# Silicon Macs. And for some reason, pip's --platform=…_x86_64 appears to do -# the right thing even on an ARM image, without the override, but I'd rather -# not play with fire at this time without a deeper understanding as to why. DOCKER_DEFAULT_PLATFORM=linux/amd64 $(MAKE) docker_deps docker_dev_deps python scripts/manage_requirements.py \ --image=$(azul_image)/deps:$(azul_image_tag) \ --build-image=$(azul_image)/dev-deps:$(azul_image_tag) -# Download wheels (source and binary) for the Lambda runtime - rm ${azul_chalice_bin}/* - pip download \ - --platform=manylinux2014_x86_64 \ - --only-binary=:all: \ - --no-deps \ - -r requirements.txt \ - --dest=${azul_chalice_bin} environment.boot: check_python python scripts/generate_environment_boot.py @@ -205,7 +191,7 @@ clean: check_env absolute_sources = $(shell echo $(project_root)/src \ $(project_root)/scripts \ $(project_root)/test \ - $(project_root)/lambdas/{layer,indexer,service}/app.py \ + $(project_root)/lambdas/{indexer,service}/app.py \ $(project_root)/.flake8/azul_flake8.py \ $(project_root)/environment.py \ $(project_root)/deployments/*/environment.py \ diff --git a/lambdas/layer/.chalice/config.json.template.py b/attic/lambdas/layer/.chalice/config.json.template.py similarity index 100% rename from lambdas/layer/.chalice/config.json.template.py rename to attic/lambdas/layer/.chalice/config.json.template.py diff --git a/lambdas/layer/.gitignore b/attic/lambdas/layer/.gitignore similarity index 100% rename from lambdas/layer/.gitignore rename to attic/lambdas/layer/.gitignore diff --git a/lambdas/layer/Makefile b/attic/lambdas/layer/Makefile similarity index 100% rename from lambdas/layer/Makefile rename to attic/lambdas/layer/Makefile diff --git a/lambdas/layer/app.py b/attic/lambdas/layer/app.py similarity index 100% rename from lambdas/layer/app.py rename to attic/lambdas/layer/app.py diff --git a/lambdas/layer/requirements.trans.txt b/attic/lambdas/layer/requirements.trans.txt similarity index 100% rename from lambdas/layer/requirements.trans.txt rename to attic/lambdas/layer/requirements.trans.txt diff --git a/lambdas/layer/requirements.txt b/attic/lambdas/layer/requirements.txt similarity index 100% rename from lambdas/layer/requirements.txt rename to attic/lambdas/layer/requirements.txt diff --git a/scripts/stage_layer.py b/attic/scripts/stage_layer.py similarity index 100% rename from scripts/stage_layer.py rename to attic/scripts/stage_layer.py diff --git a/src/azul/infra/lambda_layer.py b/attic/src/azul/infra/lambda_layer.py similarity index 100% rename from src/azul/infra/lambda_layer.py rename to attic/src/azul/infra/lambda_layer.py diff --git a/attic/terraform/lambda_layer.tf.json.template.py b/attic/terraform/lambda_layer.tf.json.template.py new file mode 100644 index 0000000000..c8789791ad --- /dev/null +++ b/attic/terraform/lambda_layer.tf.json.template.py @@ -0,0 +1,5 @@ +from azul.infra.terraform import ( + emit_tf, +) + +emit_tf(None) diff --git a/bin/wheels/runtime/Events-0.5-py3-none-any.whl b/bin/wheels/runtime/Events-0.5-py3-none-any.whl deleted file mode 100644 index b8bc52b2f3..0000000000 Binary files a/bin/wheels/runtime/Events-0.5-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/attrs-26.1.0-py3-none-any.whl b/bin/wheels/runtime/attrs-26.1.0-py3-none-any.whl deleted file mode 100644 index 3f0a9de772..0000000000 Binary files a/bin/wheels/runtime/attrs-26.1.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/boto3-1.43.63-py3-none-any.whl b/bin/wheels/runtime/boto3-1.43.63-py3-none-any.whl deleted file mode 100644 index 53dc632133..0000000000 Binary files a/bin/wheels/runtime/boto3-1.43.63-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/botocore-1.43.63-py3-none-any.whl b/bin/wheels/runtime/botocore-1.43.63-py3-none-any.whl deleted file mode 100644 index 89495a53b7..0000000000 Binary files a/bin/wheels/runtime/botocore-1.43.63-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/certifi-2026.7.22-py3-none-any.whl b/bin/wheels/runtime/certifi-2026.7.22-py3-none-any.whl deleted file mode 100644 index 7a4afb13ad..0000000000 Binary files a/bin/wheels/runtime/certifi-2026.7.22-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl b/bin/wheels/runtime/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl deleted file mode 100644 index a623d1ac86..0000000000 Binary files a/bin/wheels/runtime/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl b/bin/wheels/runtime/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl deleted file mode 100644 index 460ac338a1..0000000000 Binary files a/bin/wheels/runtime/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/chevron-0.14.0-py3-none-any.whl b/bin/wheels/runtime/chevron-0.14.0-py3-none-any.whl deleted file mode 100644 index 4a044ebfe8..0000000000 Binary files a/bin/wheels/runtime/chevron-0.14.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl b/bin/wheels/runtime/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl deleted file mode 100644 index 268fbb9708..0000000000 Binary files a/bin/wheels/runtime/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/fastavro-1.12.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl b/bin/wheels/runtime/fastavro-1.12.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl deleted file mode 100644 index 6bf41d1cc5..0000000000 Binary files a/bin/wheels/runtime/fastavro-1.12.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/furl-2.1.4-py2.py3-none-any.whl b/bin/wheels/runtime/furl-2.1.4-py2.py3-none-any.whl deleted file mode 100644 index 9f6528b6cf..0000000000 Binary files a/bin/wheels/runtime/furl-2.1.4-py2.py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/google_api_core-2.33.0-py3-none-any.whl b/bin/wheels/runtime/google_api_core-2.33.0-py3-none-any.whl deleted file mode 100644 index bd3bacfa93..0000000000 Binary files a/bin/wheels/runtime/google_api_core-2.33.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/google_auth-2.56.2-py3-none-any.whl b/bin/wheels/runtime/google_auth-2.56.2-py3-none-any.whl deleted file mode 100644 index c1fabfb3f6..0000000000 Binary files a/bin/wheels/runtime/google_auth-2.56.2-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/google_cloud_bigquery-3.42.3-py3-none-any.whl b/bin/wheels/runtime/google_cloud_bigquery-3.42.3-py3-none-any.whl deleted file mode 100644 index 07ee460c1c..0000000000 Binary files a/bin/wheels/runtime/google_cloud_bigquery-3.42.3-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/google_cloud_core-2.6.1-py3-none-any.whl b/bin/wheels/runtime/google_cloud_core-2.6.1-py3-none-any.whl deleted file mode 100644 index ee4fe1655e..0000000000 Binary files a/bin/wheels/runtime/google_cloud_core-2.6.1-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl b/bin/wheels/runtime/google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl deleted file mode 100644 index 77ba907272..0000000000 Binary files a/bin/wheels/runtime/google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/google_resumable_media-2.10.1-py3-none-any.whl b/bin/wheels/runtime/google_resumable_media-2.10.1-py3-none-any.whl deleted file mode 100644 index 7abaac3761..0000000000 Binary files a/bin/wheels/runtime/google_resumable_media-2.10.1-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/googleapis_common_protos-1.75.1-py3-none-any.whl b/bin/wheels/runtime/googleapis_common_protos-1.75.1-py3-none-any.whl deleted file mode 100644 index 2c695b5488..0000000000 Binary files a/bin/wheels/runtime/googleapis_common_protos-1.75.1-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/grpcio-1.83.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl b/bin/wheels/runtime/grpcio-1.83.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl deleted file mode 100644 index bf3346b153..0000000000 Binary files a/bin/wheels/runtime/grpcio-1.83.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/grpcio_status-1.83.0-py3-none-any.whl b/bin/wheels/runtime/grpcio_status-1.83.0-py3-none-any.whl deleted file mode 100644 index f16a55b802..0000000000 Binary files a/bin/wheels/runtime/grpcio_status-1.83.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/http_message_signatures-2.0.1-py3-none-any.whl b/bin/wheels/runtime/http_message_signatures-2.0.1-py3-none-any.whl deleted file mode 100644 index 3413c41f42..0000000000 Binary files a/bin/wheels/runtime/http_message_signatures-2.0.1-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/http_sfv-0.9.9-py3-none-any.whl b/bin/wheels/runtime/http_sfv-0.9.9-py3-none-any.whl deleted file mode 100644 index 1031ab7529..0000000000 Binary files a/bin/wheels/runtime/http_sfv-0.9.9-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/idna-3.18-py3-none-any.whl b/bin/wheels/runtime/idna-3.18-py3-none-any.whl deleted file mode 100644 index 57a9e8447f..0000000000 Binary files a/bin/wheels/runtime/idna-3.18-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/jmespath-1.1.0-py3-none-any.whl b/bin/wheels/runtime/jmespath-1.1.0-py3-none-any.whl deleted file mode 100644 index 777b40a9c8..0000000000 Binary files a/bin/wheels/runtime/jmespath-1.1.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/jsonschema-4.26.0-py3-none-any.whl b/bin/wheels/runtime/jsonschema-4.26.0-py3-none-any.whl deleted file mode 100644 index 2b6668dc00..0000000000 Binary files a/bin/wheels/runtime/jsonschema-4.26.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/jsonschema_specifications-2025.9.1-py3-none-any.whl b/bin/wheels/runtime/jsonschema_specifications-2025.9.1-py3-none-any.whl deleted file mode 100644 index e04d5d6197..0000000000 Binary files a/bin/wheels/runtime/jsonschema_specifications-2025.9.1-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl b/bin/wheels/runtime/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl deleted file mode 100644 index 0a25ca799c..0000000000 Binary files a/bin/wheels/runtime/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/more_itertools-11.1.0-py3-none-any.whl b/bin/wheels/runtime/more_itertools-11.1.0-py3-none-any.whl deleted file mode 100644 index 534b1c9e0e..0000000000 Binary files a/bin/wheels/runtime/more_itertools-11.1.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/msgpack-1.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl b/bin/wheels/runtime/msgpack-1.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl deleted file mode 100644 index 4c67792b87..0000000000 Binary files a/bin/wheels/runtime/msgpack-1.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/opensearch_py-2.8.0-py3-none-any.whl b/bin/wheels/runtime/opensearch_py-2.8.0-py3-none-any.whl deleted file mode 100644 index f5e16f3d93..0000000000 Binary files a/bin/wheels/runtime/opensearch_py-2.8.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/orderedmultidict-1.0.2-py2.py3-none-any.whl b/bin/wheels/runtime/orderedmultidict-1.0.2-py2.py3-none-any.whl deleted file mode 100644 index fe3d2cdc18..0000000000 Binary files a/bin/wheels/runtime/orderedmultidict-1.0.2-py2.py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/packaging-26.3-py3-none-any.whl b/bin/wheels/runtime/packaging-26.3-py3-none-any.whl deleted file mode 100644 index d54f571213..0000000000 Binary files a/bin/wheels/runtime/packaging-26.3-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/proto_plus-1.28.3-py3-none-any.whl b/bin/wheels/runtime/proto_plus-1.28.3-py3-none-any.whl deleted file mode 100644 index 72c16c6ca7..0000000000 Binary files a/bin/wheels/runtime/proto_plus-1.28.3-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl b/bin/wheels/runtime/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl deleted file mode 100644 index c404f428d8..0000000000 Binary files a/bin/wheels/runtime/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/pyasn1-0.6.4-py3-none-any.whl b/bin/wheels/runtime/pyasn1-0.6.4-py3-none-any.whl deleted file mode 100644 index da524f61e6..0000000000 Binary files a/bin/wheels/runtime/pyasn1-0.6.4-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/pyasn1_modules-0.4.2-py3-none-any.whl b/bin/wheels/runtime/pyasn1_modules-0.4.2-py3-none-any.whl deleted file mode 100644 index ecb42148e5..0000000000 Binary files a/bin/wheels/runtime/pyasn1_modules-0.4.2-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/pycparser-3.0-py3-none-any.whl b/bin/wheels/runtime/pycparser-3.0-py3-none-any.whl deleted file mode 100644 index 6293e4120d..0000000000 Binary files a/bin/wheels/runtime/pycparser-3.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/pyjwt-2.13.0-py3-none-any.whl b/bin/wheels/runtime/pyjwt-2.13.0-py3-none-any.whl deleted file mode 100644 index 32b2becd48..0000000000 Binary files a/bin/wheels/runtime/pyjwt-2.13.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/python_dateutil-2.9.0.post0-py2.py3-none-any.whl b/bin/wheels/runtime/python_dateutil-2.9.0.post0-py2.py3-none-any.whl deleted file mode 100644 index b9a14e1bf9..0000000000 Binary files a/bin/wheels/runtime/python_dateutil-2.9.0.post0-py2.py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/referencing-0.37.0-py3-none-any.whl b/bin/wheels/runtime/referencing-0.37.0-py3-none-any.whl deleted file mode 100644 index b2b482d60f..0000000000 Binary files a/bin/wheels/runtime/referencing-0.37.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/requests-2.33.1-py3-none-any.whl b/bin/wheels/runtime/requests-2.33.1-py3-none-any.whl deleted file mode 100644 index 3efd121507..0000000000 Binary files a/bin/wheels/runtime/requests-2.33.1-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/resumablehash-1.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl b/bin/wheels/runtime/resumablehash-1.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl deleted file mode 100644 index 2ae1f1be28..0000000000 Binary files a/bin/wheels/runtime/resumablehash-1.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/bin/wheels/runtime/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl deleted file mode 100644 index 37abe7b77b..0000000000 Binary files a/bin/wheels/runtime/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl and /dev/null differ diff --git a/bin/wheels/runtime/s3transfer-0.19.2-py3-none-any.whl b/bin/wheels/runtime/s3transfer-0.19.2-py3-none-any.whl deleted file mode 100644 index a92f501da3..0000000000 Binary files a/bin/wheels/runtime/s3transfer-0.19.2-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/setuptools-83.0.0-py3-none-any.whl b/bin/wheels/runtime/setuptools-83.0.0-py3-none-any.whl deleted file mode 100644 index 319e6896f2..0000000000 Binary files a/bin/wheels/runtime/setuptools-83.0.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/six-1.17.0-py2.py3-none-any.whl b/bin/wheels/runtime/six-1.17.0-py2.py3-none-any.whl deleted file mode 100644 index c506fd05b5..0000000000 Binary files a/bin/wheels/runtime/six-1.17.0-py2.py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/typing_extensions-4.16.0-py3-none-any.whl b/bin/wheels/runtime/typing_extensions-4.16.0-py3-none-any.whl deleted file mode 100644 index 9f9aae6adc..0000000000 Binary files a/bin/wheels/runtime/typing_extensions-4.16.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/urllib3-2.7.0-py3-none-any.whl b/bin/wheels/runtime/urllib3-2.7.0-py3-none-any.whl deleted file mode 100644 index 1400ec630f..0000000000 Binary files a/bin/wheels/runtime/urllib3-2.7.0-py3-none-any.whl and /dev/null differ diff --git a/bin/wheels/runtime/werkzeug-3.1.8-py3-none-any.whl b/bin/wheels/runtime/werkzeug-3.1.8-py3-none-any.whl deleted file mode 100644 index 6f47eba7b2..0000000000 Binary files a/bin/wheels/runtime/werkzeug-3.1.8-py3-none-any.whl and /dev/null differ diff --git a/environment.py b/environment.py index 9a18d9f4e1..84bd54cdec 100644 --- a/environment.py +++ b/environment.py @@ -202,6 +202,21 @@ def env() -> Mapping[str, str | None]: 'azul_docker_registry': '{AZUL_AWS_ACCOUNT_ID}.dkr.ecr.' '{AWS_DEFAULT_REGION}.amazonaws.com/', + # The platforms to build Lambda container images for, as a list of + # Docker platform specifications separated by space. The first platform + # in the list also determines the architecture of the deployed Lambda + # function. Images will be built for the other platforms listed, but + # those images will not be used by the deployment. A multi-platform + # image list consisting of the specified platform-specific images will + # also be pushed. + # + # On machines that support cross-platform image builds, this variable + # can be overridden locally to build images for additional platforms. On + # machines that don't support building the default platform, a different + # one can be specified, e.g., 'linux/arm64'. + # + 'azul_lambda_image_platforms': 'linux/amd64', + # The version of Docker used throughout the system. # # This variable is not intended to be overridden per deployment or @@ -702,7 +717,12 @@ def env() -> Mapping[str, str | None]: # from that directory. The wheels must be compatible with the AWS # Lambda platform. # - 'azul_chalice_bin': '{project_root}/bin/wheels/runtime', + # No longer actively used. Lambda runtime dependencies are now + # installed via pip inside the Docker image build. Our fork of + # Chalice still references this variable but handles it being + # unset. + # + 'azul_chalice_bin': None, # Stop `pip` from nagging us about updates. We update pip regularly like # any other dependency. There is nothing special about `pip` that would @@ -986,15 +1006,6 @@ def env() -> Mapping[str, str | None]: # 'AZUL_ENABLE_BUNDLE_NOTIFICATIONS': '0', - # A Lambda runtime version to pin to, which overrides the AWS-managed - # default. Pin the runtime to python:3.14.v35 to prevent OutOfMemory - # errors in the mirror Lambda function. - # - # FIXME: Remove pinned Lambda runtime version ARN - # https://github.com/DataBiosphere/azul/issues/7730 - # - 'azul_lambda_runtime_version': '6e4c2a8804e47c3599d89c0a896af2312961578a5546d2d9a288c0d93e6b1a2d', - # URL of Terra's external credentials manager (ECM) service used by the # Azul deployment. # diff --git a/lambdas/Dockerfile b/lambdas/Dockerfile new file mode 100644 index 0000000000..ea732d8a1d --- /dev/null +++ b/lambdas/Dockerfile @@ -0,0 +1,21 @@ +ARG azul_docker_registry +ARG azul_python_image +FROM ${azul_docker_registry}${azul_python_image} + +ARG APP + +RUN pip install --no-cache-dir awslambdaric + +COPY requirements.txt requirements.trans.txt /tmp/ +RUN pip install --no-cache-dir --no-deps -r /tmp/requirements.txt \ + && rm /tmp/requirements.txt /tmp/requirements.trans.txt + +COPY lambdas/${APP}/.chalice/terraform/deployment.zip /tmp/ +RUN python -m zipfile -e /tmp/deployment.zip /var/task \ + && rm /tmp/deployment.zip \ + && find /var/task -name __pycache__ -exec rm -rf {} + \ + && python -m compileall -q /var/task + +WORKDIR /var/task + +ENTRYPOINT ["/usr/local/bin/python", "-m", "awslambdaric"] diff --git a/lambdas/Dockerfile.dockerignore b/lambdas/Dockerfile.dockerignore new file mode 100644 index 0000000000..6c8016d726 --- /dev/null +++ b/lambdas/Dockerfile.dockerignore @@ -0,0 +1,5 @@ +* +!requirements.txt +!requirements.trans.txt +!lambdas/indexer/.chalice/terraform/deployment.zip +!lambdas/service/.chalice/terraform/deployment.zip diff --git a/lambdas/Makefile b/lambdas/Makefile index cf78d594d6..83851f1388 100644 --- a/lambdas/Makefile +++ b/lambdas/Makefile @@ -1,23 +1,19 @@ .PHONY: all -all: layer indexer service +all: indexer service include ../common.mk -.PHONY: layer -layer: check_env - $(MAKE) -C layer layer - .PHONY: indexer indexer: check_env - $(MAKE) -C indexer package + $(MAKE) -C indexer docker_push .PHONY: service service: check_env - $(MAKE) -C service package + $(MAKE) -C service docker_push .PHONY: clean clean: check_env - for d in indexer service layer; do $(MAKE) -C $$d clean || ! break; done + for d in indexer service; do $(MAKE) -C $$d clean || ! break; done .PHONY: openapi openapi: check_env diff --git a/lambdas/indexer/Makefile b/lambdas/indexer/Makefile index bd674c8176..dfdacb31f3 100644 --- a/lambdas/indexer/Makefile +++ b/lambdas/indexer/Makefile @@ -2,3 +2,5 @@ all: package include ../lambdas.mk + +$(eval $(call docker_image_macro,indexer)) diff --git a/lambdas/lambdas.mk b/lambdas/lambdas.mk index df1b1a4218..10b6ca698f 100644 --- a/lambdas/lambdas.mk +++ b/lambdas/lambdas.mk @@ -61,3 +61,37 @@ package: check_branch check_python check_aws config environ sources compile .PHONY: openapi openapi: check_python python $(project_root)/scripts/generate_openapi_document.py + + +docker_repo = $(azul_docker_registry)$(AZUL_DOMAIN_NAME)/azul/lambda + +define docker_image_macro +docker_tag := $$(docker_repo):$$(AZUL_DEPLOYMENT_STAGE)-$1 + +.PHONY: docker_image +docker_image: check_docker package + set -e; \ + for platform in $$(azul_lambda_image_platforms); do \ + docker buildx build \ + --platform $$$$platform \ + --build-arg azul_docker_registry=$$(azul_docker_registry) \ + --build-arg azul_python_image=$$(azul_python_image) \ + --build-arg APP=$1 \ + -f $$(project_root)/lambdas/Dockerfile \ + --tag $$(docker_tag)-$$$${platform//\//-} \ + --load \ + $$(project_root); \ + done + +.PHONY: docker_push +docker_push: docker_image + set -e; \ + tags=""; \ + for platform in $$(azul_lambda_image_platforms); do \ + tag=$$(docker_tag)-$$$${platform//\//-}; \ + docker push $$$$tag; \ + tags="$$$$tags $$$$tag"; \ + done; \ + docker manifest create --amend $$(docker_tag) $$$$tags; \ + docker manifest push --purge $$(docker_tag) +endef diff --git a/lambdas/service/Makefile b/lambdas/service/Makefile index fe71195c42..9b076bf7ee 100644 --- a/lambdas/service/Makefile +++ b/lambdas/service/Makefile @@ -7,3 +7,5 @@ include ../lambdas.mk apat_public_key: vendor/resources/apat_public_key.json package: apat_public_key + +$(eval $(call docker_image_macro,service)) diff --git a/requirements.all.txt b/requirements.all.txt index 6577c66114..2d19b25608 100644 --- a/requirements.all.txt +++ b/requirements.all.txt @@ -10,9 +10,9 @@ botocore-stubs==1.43.67 certifi==2026.7.22 cffi==2.1.1 chalice==1.32.0+21 -charset-normalizer==3.5.0 +charset-normalizer==3.5.1 chevron==0.14.0 -click==8.4.2 +click==8.5.0 coverage==7.15.3 cryptography==50.0.0 docker==7.2.0 @@ -28,14 +28,14 @@ gitpython==3.1.57 google-api-core==2.33.0 google-api-python-client==2.198.0 google-auth==2.56.2 -google-auth-httplib2==0.4.1 +google-auth-httplib2==0.4.2 google-cloud-bigquery==3.42.3 google-cloud-bigquery-reservation==1.25.0 -google-cloud-core==2.6.1 +google-cloud-core==2.7.0 google-cloud-storage==3.13.0 google-crc32c==1.8.0 -google-resumable-media==2.10.1 -googleapis-common-protos==1.75.1 +google-resumable-media==2.10.2 +googleapis-common-protos==1.75.2 greenlet==3.5.5 grpc-google-iam-v1==0.14.5 grpcio==1.83.0 @@ -43,7 +43,7 @@ grpcio-status==1.83.0 http-message-signatures==2.0.1 http_sfv==0.9.9 httplib2==0.32.0 -idna==3.18 +idna==3.19 inquirer==3.4.1 isort==8.0.1 jinxed==2.1.0 @@ -62,13 +62,13 @@ msgpack==1.2.1 msgpack-types==0.8.0 mypy==2.3.0 mypy-boto3-apigateway==1.43.0 -mypy-boto3-cloudwatch==1.43.54 +mypy-boto3-cloudwatch==1.43.78 mypy-boto3-dynamodb==1.43.64 -mypy-boto3-ec2==1.43.67 -mypy-boto3-ecr==1.43.0 +mypy-boto3-ec2==1.43.81 +mypy-boto3-ecr==1.43.73 mypy-boto3-iam==1.43.70 mypy-boto3-kms==1.43.12 -mypy-boto3-lambda==1.43.60 +mypy-boto3-lambda==1.43.76 mypy-boto3-opensearch==1.43.41 mypy-boto3-s3==1.43.66 mypy-boto3-secretsmanager==1.43.0 @@ -89,8 +89,8 @@ pathable==0.6.0 pathspec==1.1.1 pip==26.1.2 posix_ipc==1.3.2 -proto-plus==1.28.3 -protobuf==7.35.1 +proto-plus==1.28.4 +protobuf==7.36.0 py-partiql-parser==0.6.3 pyasn1==0.6.4 pyasn1_modules==0.4.2 @@ -105,7 +105,7 @@ pyjwt==2.13.0 pynacl==1.6.2 pyparsing==3.3.2 python-dateutil==2.9.0.post0 -python-dotenv==1.2.2 +python-dotenv==1.2.3 python-dxf==12.1.1 python-gitlab==8.5.0 pyyaml==6.0.3 @@ -140,4 +140,4 @@ www-authenticate==0.9.2 xmltodict==1.0.4 xmod==1.10.0 zope.event==6.2 -zope.interface==8.5 +zope.interface==8.6 diff --git a/requirements.dev.trans.txt b/requirements.dev.trans.txt index 85bbfeb2ec..f942d54c7a 100644 --- a/requirements.dev.trans.txt +++ b/requirements.dev.trans.txt @@ -2,11 +2,11 @@ annotated-types==0.8.0 ast_serialize==0.8.0 blessed==1.48.0 botocore-stubs==1.43.67 -click==8.4.2 +click==8.5.0 editor==1.8.0 et_xmlfile==2.0.0 gitdb==4.0.12 -google-auth-httplib2==0.4.1 +google-auth-httplib2==0.4.2 greenlet==3.5.5 grpc-google-iam-v1==0.14.5 httplib2==0.32.0 @@ -17,13 +17,13 @@ lazy-object-proxy==1.12.0 librt==0.15.0 mccabe==0.7.0 mypy-boto3-apigateway==1.43.0 -mypy-boto3-cloudwatch==1.43.54 +mypy-boto3-cloudwatch==1.43.78 mypy-boto3-dynamodb==1.43.64 -mypy-boto3-ec2==1.43.67 -mypy-boto3-ecr==1.43.0 +mypy-boto3-ec2==1.43.81 +mypy-boto3-ecr==1.43.73 mypy-boto3-iam==1.43.70 mypy-boto3-kms==1.43.12 -mypy-boto3-lambda==1.43.60 +mypy-boto3-lambda==1.43.76 mypy-boto3-opensearch==1.43.41 mypy-boto3-s3==1.43.66 mypy-boto3-secretsmanager==1.43.0 @@ -45,7 +45,7 @@ pydantic_core==2.46.4 pyflakes==3.4.0 pynacl==1.6.2 pyparsing==3.3.2 -python-dotenv==1.2.2 +python-dotenv==1.2.3 readchar==4.2.2 requests-toolbelt==1.0.0 rfc3339-validator==0.1.4 @@ -60,4 +60,4 @@ www-authenticate==0.9.2 xmltodict==1.0.4 xmod==1.10.0 zope.event==6.2 -zope.interface==8.5 +zope.interface==8.6 diff --git a/requirements.trans.txt b/requirements.trans.txt index f3dd59e4e0..e55669beec 100644 --- a/requirements.trans.txt +++ b/requirements.trans.txt @@ -1,20 +1,20 @@ certifi==2026.7.22 cffi==2.1.1 -charset-normalizer==3.5.0 +charset-normalizer==3.5.1 events==0.5 -google-cloud-core==2.6.1 +google-cloud-core==2.7.0 google-crc32c==1.8.0 -google-resumable-media==2.10.1 -googleapis-common-protos==1.75.1 +google-resumable-media==2.10.2 +googleapis-common-protos==1.75.2 grpcio==1.83.0 grpcio-status==1.83.0 -idna==3.18 +idna==3.19 jsonschema-specifications==2025.9.1 markupsafe==3.0.3 orderedmultidict==1.0.2 packaging==26.3 -proto-plus==1.28.3 -protobuf==7.35.1 +proto-plus==1.28.4 +protobuf==7.36.0 pyasn1==0.6.4 pyasn1_modules==0.4.2 pycparser==3.0 diff --git a/scripts/manage_images.py b/scripts/manage_images.py index fa2831f4de..1df8702737 100644 --- a/scripts/manage_images.py +++ b/scripts/manage_images.py @@ -132,15 +132,11 @@ def copy_multi_platform_image(src: TagImageRef, def make_platform_tag(tag, platform: Platform): assert not is_platform_tag(tag), R( 'Input already looks like a platform tag', tag) - return tag + platform_tag_suffix(platform) + return tag + platform.tag_suffix def is_platform_tag(tag): - return any(tag.endswith(platform_tag_suffix(p)) for p in platforms) - - -def platform_tag_suffix(platform): - return '-' + str(platform).replace('/', '-') + return any(tag.endswith(p.tag_suffix) for p in platforms) def delete_unused_images(repository): diff --git a/src/azul/__init__.py b/src/azul/__init__.py index b97b2c81ae..b1fee5a4dc 100644 --- a/src/azul/__init__.py +++ b/src/azul/__init__.py @@ -166,10 +166,6 @@ def aws_account_id(self) -> str: def project_root(self) -> str: return self.environ['project_root'] - @property - def chalice_bin(self) -> str: - return self.environ['azul_chalice_bin'] - @property def opensearch_domain(self) -> str: return self.environ['AZUL_OPENSEARCH_DOMAIN'] @@ -1296,10 +1292,6 @@ def _lambda_env(self, *, outsource: bool) -> dict[str, str]: and (len(v) > 128) == outsource) } - @cached_property - def lambda_runtime_version(self) -> str | None: - return self.environ.get('azul_lambda_runtime_version') - @cached_property def _outsourced_environ(self) -> dict[str, str]: try: @@ -1616,10 +1608,6 @@ def gitlab_access_token(self) -> str | None: def gitlab_data_volume_id(self) -> str | None: return self.environ.get('azul_gitlab_data_volume_id') - @property - def lambda_layer_key(self) -> str: - return 'lambda_layers' - @property def dynamo_sources_cache_table_name(self) -> str: return self.qualified_resource_name('sources_cache_by_auth') @@ -1739,6 +1727,10 @@ def docker_images(self) -> dict[str, ImageSpec]: 'linux/amd64' ] + @property + def lambda_image_platforms(self) -> list[str]: + return self.environ['azul_lambda_image_platforms'].split() + @property def docker_image_gists_path(self) -> Path: return Path(config.project_root) / 'docker_images.json' diff --git a/src/azul/docker.py b/src/azul/docker.py index 926b60c85c..15a092585c 100644 --- a/src/azul/docker.py +++ b/src/azul/docker.py @@ -358,6 +358,41 @@ def __str__(self) -> str: result.append(self.variant) return '/'.join(result) + @property + def tag_suffix(self) -> str: + """ + The string to append when encoding the platform of an image in a tag. + This is an Azul-specifc convention. + + >>> Platform.parse('linux/amd64').tag_suffix + '-linux-amd64' + """ + return '-' + str(self).replace('/', '-') + + +_docker_to_lambda_arch = { + 'amd64': 'x86_64', + 'arm64': 'arm64', +} + + +def lambda_image_platform() -> Platform: + return Platform.parse(config.lambda_image_platforms[0]) + + +def lambda_architecture() -> str: + return _docker_to_lambda_arch[lambda_image_platform().arch] + + +def lambda_image_uri(app_name: str) -> str: + platform = lambda_image_platform() + tag = config.deployment_stage + '-' + app_name + platform.tag_suffix + name = config.docker_registry + '/' + config.domain_name + '/azul/lambda' + tag_ref = TagImageRef.create(name, tag) + repo = Repository(tag_ref) + digest = repo.get_tag(tag) + return str(tag_ref.with_digest(digest)) + images_by_alias = { alias: TagImageRef.parse(spec['ref']) @@ -567,18 +602,22 @@ def _auth(self) -> tuple[str, str]: try: creds_store = config['credsStore'] except KeyError: - return self._decode_auth(config['auths'][auth_server_url]['auth']) - else: - command = [('docker-credential-' + creds_store), 'get'] - input = auth_server_url.encode('ascii') - log.info('Running %r with input %r', command, input) - process = subprocess.run(args=command, stdout=subprocess.PIPE, input=input) - output = process.stdout - assert process.returncode == 0, R( - f'Command {command} failed with status code {process.returncode}', - output, 'You may need to login into Docker Desktop') - credentials = json.loads(output) - return credentials['Username'], credentials['Secret'] + try: + cred_helpers = config['credHelpers'] + except KeyError: + return self._decode_auth(config['auths'][auth_server_url]['auth']) + else: + creds_store = cred_helpers[auth_server_url] + command = [('docker-credential-' + creds_store), 'get'] + input = auth_server_url.encode('ascii') + log.info('Running %r with input %r', command, input) + process = subprocess.run(args=command, stdout=subprocess.PIPE, input=input) + output = process.stdout + assert process.returncode == 0, R( + f'Command {command} failed with status code {process.returncode}', + output, 'You may need to login into Docker Desktop') + credentials = json.loads(output) + return credentials['Username'], credentials['Secret'] @property def encoded_auth(self) -> str: diff --git a/src/azul/infra/terraform.py b/src/azul/infra/terraform.py index 8f49085cec..2808d1ec0f 100644 --- a/src/azul/infra/terraform.py +++ b/src/azul/infra/terraform.py @@ -30,6 +30,10 @@ from azul.deployment import ( aws, ) +from azul.docker import ( + lambda_architecture, + lambda_image_uri, +) from azul.lib import ( R, cache, @@ -763,8 +767,6 @@ def resource_items(resource_type: str) -> Iterable[tuple[str, MutableJSON]]: ] for resource_name, resource in resource_items('aws_lambda_function'): - assert 'layers' not in resource - resource['layers'] = ['${aws_lambda_layer_version.dependencies.arn}'] # Publishing a new Lambda function version each time lets us perform # an atomic update of the function, avoiding a race condition # between the update of the function's configuration and its code. @@ -782,9 +784,21 @@ def resource_items(resource_type: str) -> Iterable[tuple[str, MutableJSON]]: ) ) json_dict(json_dict(resource['environment'])['variables']).update(env) - package_zip = str(self.package_zip_path(app_name)) - resource['source_code_hash'] = '${filebase64sha256("%s")}' % package_zip - resource['filename'] = package_zip + + # Chalice assumes that we deploy the app as source code. Patch the + # the resource definition to use the custom Docker image instead: + # + handler = resource.pop('handler') + del resource['runtime'] + del resource['source_code_hash'] + del resource['filename'] + resource['package_type'] = 'Image' + resource['image_uri'] = lambda_image_uri(app_name) + resource['architectures'] = [lambda_architecture()] + resource['image_config'] = { + 'command': [handler] + } + # Creating verbatim PFB manifests for large AnVIL datasets requires # more ephemeral storage than the default, so we raise it to the # maximum. We have to inject this here since Chalice doesn't support @@ -1018,29 +1032,6 @@ def alias_property(property_name: str, resource: MutableJSON): sqs_name, _ = config.unqualified_resource_name(resource_name, suffix) resource['event_source_arn'] = f'${{aws_sqs_queue.{sqs_name}.arn}}' - # Ensure that the Lambda permissions for the previous aliases aren't - # deleted until after the permissions for the new aliases have been - # created. - # - for resource_name, resource in resource_items('aws_lambda_permission'): - assert 'lifecycle' not in resource, (resource_name, resource) - resource['lifecycle'] = {'create_before_destroy': True} - - if config.lambda_runtime_version is not None: - resource_type = 'aws_lambda_runtime_management_config' - runtime_version = config.lambda_runtime_version - assert isinstance(runtime_version, str), runtime_version - runtime_version_configs: MutableJSON = {} - for resource_name, resource in resource_items('aws_lambda_function'): - runtime_version_configs[resource_name] = { - 'function_name': '${aws_lambda_function.%s.function_name}' % resource_name, - 'qualifier': '${aws_lambda_function.%s.version}' % resource_name, - 'update_runtime_on': 'Manual', - 'runtime_version_arn': 'arn:aws:lambda:us-east-1::runtime:' + runtime_version, - } - assert resource_type not in resources, resources - resources[resource_type] = runtime_version_configs - resource_type = 'aws_lambda_function_recursion_config' recursion_configs: MutableJSON = {} for resource_name, resource in resource_items('aws_lambda_function'): diff --git a/src/azul/logging.py b/src/azul/logging.py index 9413448524..3155696f35 100644 --- a/src/azul/logging.py +++ b/src/azul/logging.py @@ -78,6 +78,19 @@ def truncate(arg): lambda_log_date_format = '%Y-%m-%dT%H:%M:%S' +class LambdaLogFormatter(logging.Formatter): + """ + awslambdaric's StandardLogSink uses newlines to delimit CloudWatch log + events. To prevent multi-line log records (e.g., with tracebacks from + exc_info) from being split into separate events, this formatter replaces + newlines with carriage returns and appends a single newline at the end. + """ + + def format(self, record): + s = super().format(record) + return s.replace('\n', '\r') + '\n' + + def configure_app_logging(app: AzulChaliceApp, *loggers): _configure_log_levels(app.log, *loggers) if not app.loaded_dynamically: @@ -91,7 +104,7 @@ def configure_app_logging(app: AzulChaliceApp, *loggers): # for details. # handler = one(root_logger.handlers) - root_formatter = logging.Formatter(lambda_log_format, lambda_log_date_format) + root_formatter = LambdaLogFormatter(lambda_log_format, lambda_log_date_format) handler.setFormatter(root_formatter) else: # Otherwise, we're running `chalice local` diff --git a/terraform/api_gateway.tf.json.template.py b/terraform/api_gateway.tf.json.template.py index 80b2ec6bb5..b03c1bb93f 100644 --- a/terraform/api_gateway.tf.json.template.py +++ b/terraform/api_gateway.tf.json.template.py @@ -796,7 +796,12 @@ def add_waf_blocked_alarm(resources: JSON) -> JSON: resource_name: { 'name': config.active_function_alias_name, 'function_name': '${aws_lambda_function.%s.function_name}' % resource_name, - 'function_version': '${aws_lambda_function.%s.version}' % resource_name + 'function_version': '${aws_lambda_function.%s.version}' % resource_name, + 'lifecycle': { + 'replace_triggered_by': [ + 'aws_lambda_function.%s.package_type' % resource_name + ] + } } for app in apps for resource_name in app.chalice.tf_function_resource_names diff --git a/terraform/gitlab/gitlab.tf.json.template.py b/terraform/gitlab/gitlab.tf.json.template.py index fef023dcf9..eef1ef2709 100644 --- a/terraform/gitlab/gitlab.tf.json.template.py +++ b/terraform/gitlab/gitlab.tf.json.template.py @@ -602,8 +602,10 @@ def merge(sets: Iterable[Iterable[str]]) -> Iterable[str]: ], 'resources': [ f'arn:aws:lambda:{aws.region_name}:{aws.account}:event-source-mapping:*', - f'arn:aws:lambda:{aws.region_name}:{aws.account}:layer:azul-*', f'arn:aws:lambda:{aws.region_name}:{aws.account}:function:azul-*', + # FIXME: Remove the layer ARNs once the issue below landed + # https://github.com/DataBiosphere/azul/issues/7730 + f'arn:aws:lambda:{aws.region_name}:{aws.account}:layer:azul-*', f'arn:aws:lambda:{aws.region_name}:{aws.account}:layer:azul-*:*' ] }, @@ -968,6 +970,17 @@ def merge(sets: Iterable[Iterable[str]]) -> Iterable[str]: '*' ] }, + { + 'actions': [ + 'ecr:CompleteLayerUpload', + 'ecr:InitiateLayerUpload', + 'ecr:PutImage', + 'ecr:UploadLayerPart' + ], + 'resources': [ + f'arn:aws:ecr:{aws.region_name}:{aws.account}:repository/{config.domain_name}/azul/lambda' + ] + }, { 'actions': [ 'logs:CreateLogGroup', diff --git a/terraform/lambda_layer.tf.json.template.py b/terraform/lambda_layer.tf.json.template.py deleted file mode 100644 index 51e462f3ca..0000000000 --- a/terraform/lambda_layer.tf.json.template.py +++ /dev/null @@ -1,28 +0,0 @@ -from azul import ( - config, -) -from azul.deployment import ( - aws, -) -from azul.infra.lambda_layer import ( - DependenciesLayer, -) -from azul.infra.terraform import ( - emit_tf, -) - -layer = DependenciesLayer() - -emit_tf({ - "resource": [ - { - "aws_lambda_layer_version": { - "dependencies": { - "layer_name": config.qualified_resource_name("dependencies"), - "s3_bucket": aws.shared_bucket, - "s3_key": layer.object_key - } - } - } - ], -}) diff --git a/terraform/shared/shared.tf.json.template.py b/terraform/shared/shared.tf.json.template.py index 31b0ac72cd..aab73e476f 100644 --- a/terraform/shared/shared.tf.json.template.py +++ b/terraform/shared/shared.tf.json.template.py @@ -1113,12 +1113,46 @@ def conformance_pack(name: str) -> str: } }, 'aws_ecr_repository': { - tf_repository: { - 'name': name, + **{ + tf_repository: { + 'name': name, + 'force_delete': True + } + for name, tf_repository in docker.images_by_tf_repository.keys() + if config.docker_registry + }, + 'lambda': { + 'name': config.domain_name + '/azul/lambda', 'force_delete': True + }, + }, + 'aws_ecr_repository_policy': { + # Lambda pulls the app image on its own behalf, so the repository + # must permit it to do so. + 'lambda': { + 'repository': '${aws_ecr_repository.lambda.name}', + 'policy': json.dumps({ + 'Version': '2012-10-17', + 'Statement': [ + { + 'Effect': 'Allow', + 'Principal': { + 'Service': 'lambda.amazonaws.com' + }, + 'Action': [ + 'ecr:BatchGetImage', + 'ecr:GetDownloadUrlForLayer' + ], + 'Condition': { + 'ArnLike': { + 'aws:SourceArn': f'arn:aws:lambda:{config.region}' + f':{config.aws_account_id}:function:*' + } + } + } + ] + }) } - for name, tf_repository in docker.images_by_tf_repository.keys() - if config.docker_registry }, 'null_resource': { **{