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
37 changes: 34 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ authors = ["Edward Q. Bridges <github@eqbridges.com>"]
python = "^3.7"
pillow = "^7.1.2"
python-magic = "^0.4.18"
sentry_sdk = "^0.16.3"

[tool.poetry.dev-dependencies]
pytest = "^5.2"
Expand All @@ -30,4 +31,3 @@ changelog_file = "CHANGELOG.md"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

102 changes: 66 additions & 36 deletions src/thumbnailer/lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from botocore.errorfactory import ClientError

from thumbnailer import version
from thumbnailer.image import resize
from thumbnailer.responses import (
generate_binary_response,
Expand All @@ -14,10 +15,14 @@
)
from thumbnailer.s3 import KeyNotFound, download_file_from_s3, upload_file_to_s3
from thumbnailer.util import configure_logging, DEFAULT_WIDTH, DEFAULT_HEIGHT
from sentry_sdk import init, configure_scope
from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration


MEDIA_BUCKET_ENV_KEY = 'MEDIA_UPLOAD_BUCKET_NAME'
THUMBS_BUCKET_ENV_KEY = 'MEDIA_THUMBS_BUCKET_NAME'
MONITORING_DSN = 'SENTRY_DSN'
OPERATING_ENV = 'OPERATING_ENV'


def parse_path(path):
Expand Down Expand Up @@ -65,53 +70,78 @@ def setup_verbose_logging(evt):
configure_logging(verboseLogging)


def init_monitoring():
dsn = environ.get(MONITORING_DSN)
env = environ.get(OPERATING_ENV)

if not dsn:
return

init(
dsn=dsn,
integrations=[AwsLambdaIntegration()],
release=f'v{version}',
send_default_pii=False,
traces_sample_rate=0.50,
environment=env,
_experiments={'auto_enabling_integrations': True},
)


def handler(event, context):
setup_verbose_logging(event)

init_monitoring()

debug(event)

url_path = event.get('path')
if not url_path:
return generate_json_respone(400, f'url path not set')

if url_path.endswith('favicon.ico'):
return generate_favicon_response()
with configure_scope() as scope:
scope.set_tag("image_url_path", url_path)
scope.set_extra("thumbnail_event", event)

if url_path.endswith('version'):
return generate_version_response()
if environ.get('TRIGGER_ERROR'):
1 / 0

source_bucket = environ.get(MEDIA_BUCKET_ENV_KEY)
if not source_bucket:
return generate_json_respone(
400, f'source bucket not configured via "{MEDIA_BUCKET_ENV_KEY}"'
)
if not url_path:
return generate_json_respone(400, f'url path not set')

thumbs_bucket = environ.get(THUMBS_BUCKET_ENV_KEY)
if not thumbs_bucket:
return generate_json_respone(
400, f'thumbs bucket not configured via "{THUMBS_BUCKET_ENV_KEY}"'
)
if url_path.endswith('favicon.ico'):
return generate_favicon_response()

try:
width, height, key = parse_path(url_path)
except ValueError as e:
return generate_json_respone(400, str(e))
if url_path.endswith('version'):
return generate_version_response()

keyname, ext = splitext(key)
thumbnail_key = format_thumbnail_key(keyname, width, height, ext)

# info(f'retrieving s3://{source_bucket}:{key} ({ext}) to s3://{thumbs_bucket}:{thumbnail_key} at dims {width}x{height}')
source_bucket = environ.get(MEDIA_BUCKET_ENV_KEY)
if not source_bucket:
return generate_json_respone(
400, f'source bucket not configured via "{MEDIA_BUCKET_ENV_KEY}"'
)

with NamedTemporaryFile(suffix=ext) as temp:
try:
download_file_from_s3(thumbs_bucket, thumbnail_key, temp.name)
except KeyNotFound:
info(
f'{thumbs_bucket}/{thumbnail_key} not found. creating it from {source_bucket}/{key}'
thumbs_bucket = environ.get(THUMBS_BUCKET_ENV_KEY)
if not thumbs_bucket:
return generate_json_respone(
400, f'thumbs bucket not configured via "{THUMBS_BUCKET_ENV_KEY}"'
)
source_stream = download_file_from_s3(source_bucket, key, temp.name)
resize(temp.name, width, height)
upload_file_to_s3(thumbs_bucket, thumbnail_key, temp.name)

info(f'returning thumbnail from file {temp.name}')
return generate_binary_response(200, temp.name)
try:
width, height, key = parse_path(url_path)
except ValueError as e:
return generate_json_respone(400, str(e))

keyname, ext = splitext(key)
thumbnail_key = format_thumbnail_key(keyname, width, height, ext)

info(f'retrieving s3://{source_bucket}:{key} ({ext}) to s3://{thumbs_bucket}:{thumbnail_key} at dims {width}x{height}')

with NamedTemporaryFile(suffix=ext) as temp:
try:
download_file_from_s3(thumbs_bucket, thumbnail_key, temp.name)
except KeyNotFound:
info(f'{thumbs_bucket}/{thumbnail_key} not found. creating from {source_bucket}/{key}')
source_stream = download_file_from_s3(source_bucket, key, temp.name)
resize(temp.name, width, height)
upload_file_to_s3(thumbs_bucket, thumbnail_key, temp.name)

info(f'returning thumbnail from file {temp.name}')
return generate_binary_response(200, temp.name)