Skip to content
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
56 changes: 24 additions & 32 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
# https://docs.astral.sh/uv/guides/integration/aws-lambda/#deploying-a-docker-image
FROM ghcr.io/astral-sh/uv:0.8.17 AS uv
from public.ecr.aws/lambda/python:3.13 as app

# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
# OS
env UV_COMPILE_BYTECODE=1
env UV_LINK_MODE=copy
## Configure for system level install
env UV_PROJECT_ENVIRONMENT=/var/lang/

# Enable bytecode compilation, to improve cold-start performance.
ENV UV_COMPILE_BYTECODE=1
## uv install
copy --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/

# Disable installer metadata, to create a deterministic layer.
ENV UV_NO_INSTALLER_METADATA=1
# App
workdir /app

# Enable copy mode to support bind mount caching.
ENV UV_LINK_MODE=copy
## Build / deps
copy pyproject.toml hatch.toml readme.md uv.lock .
copy src/critic/version.py src/critic/version.py
run --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --inexact

# Bundle the dependencies into the Lambda task root via `uv pip install --target`.
#
# Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`).
# This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock`
# files change, but remains robust to changes in the application code.
RUN --mount=from=uv,source=/uv,target=/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt && \
uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
## Source files after the build for better caching/speed when deps haven't changed
copy src/critic src/critic
copy src/lambda_handler.py src

FROM public.ecr.aws/lambda/python:3.13
## App Env
env FLASK_DEBUG=0

# Copy the runtime dependencies from the builder stage.
COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT}

# Now everything to do with the app
COPY src/critic/ ${LAMBDA_TASK_ROOT}/

# Uncomment this to get the CMD to be updated in the image.
# RUN echo 'd' > /tmp/annoying.txt

CMD ["app.lambda_handler"]
# Lambda Entry Point
# The lamba runtime environment will use this dotted path as the entry point to our app when
# the lambda function is invoked. It must be a function, it doesn't handle class methods.
cmd ["lambda_handler.entry"]
11 changes: 9 additions & 2 deletions mu-qa.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
project-org = 'Level 12'
image-name = 'critic'

[tool.mu.event-rules.run-due-checks]
policy-arns = [
'arn:aws:iam::089600762287:policy/CriticDDBAccessQA'
]

[deployed-env]
CRITIC_NAMESPACE = 'qa'

[event-rules.run-due-checks]
action='run_due_checks'
cron = '* * * * *' # Every minute on the minute
cron = '* * * * ? *' # Every minute on the minute
17 changes: 0 additions & 17 deletions src/critic/app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import logging

from flask import Flask
import mu

from critic.tasks import run_due_checks


log = logging.getLogger()
Expand All @@ -29,17 +26,3 @@ def logs_example():
@app.route('/error')
def error():
raise RuntimeError('Deliberate runtime error')


class ActionHandler(mu.ActionHandler):
wsgi_app = app

@staticmethod
def run_due_checks(event, context):
"""Triggered by EventBridge rule, invokes `run_due_checks` task."""
log.info('Invoking run_due_checks')
run_due_checks.invoke()


# The entry point for AWS lambda has to be a function
lambda_handler = ActionHandler.on_event
4 changes: 4 additions & 0 deletions src/critic/libs/ddb.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def namespace(table_name: str) -> str:

@classmethod
def name(cls) -> str:
namespace = os.environ.get('CRITIC_NAMESPACE', '')
if namespace in ('prod', 'qa'):
# Prod and QA envs don't have namespaced tables
return cls.base_name
return cls.namespace(cls.base_name)

@classmethod
Expand Down
23 changes: 23 additions & 0 deletions src/lambda_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import logging

import mu

from critic.app import app
from critic.tasks import run_due_checks


log = logging.getLogger()


class ActionHandler(mu.ActionHandler):
wsgi_app = app

@staticmethod
def run_due_checks(event, context):
"""Triggered by EventBridge rule, invokes `run_due_checks` task."""
log.info('Invoking run_due_checks')
run_due_checks.invoke()


# The entry point for AWS lambda has to be a function
entry = ActionHandler.on_event