-
Notifications
You must be signed in to change notification settings - Fork 11
Add Lambda function redirecting URIs to S3 file objects #7
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
negillett
merged 7 commits into
release-engineering:master
from
negillett:function-map-to-s3
Feb 19, 2020
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
55974ee
Add map_to_s3 Lambda function
negillett 978faba
Move map_to_s3 environment variables into function
negillett a5b1c37
Add tests for map_to_s3 function
negillett e6d209f
Change timestamp
negillett 6c40edd
Remove assert_called* calls in map_to_s3 tests
negillett 1221983
Broaden exceptions caught in map_to_s3
negillett 8bc1688
Document map_to_s3 Lambda function and schemas
negillett File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import json | ||
| import logging | ||
| import os | ||
| from datetime import datetime, timezone | ||
|
|
||
| import boto3 | ||
|
|
||
| LOG = logging.getLogger("map-to-s3-lambda") | ||
|
|
||
|
|
||
| def lambda_handler(event, context): | ||
| # pylint: disable=unused-argument | ||
|
|
||
| # This AWS Lambda function must be deployed with DB_TABLE_NAME and | ||
| # DB_TABLE_REGION environment variables | ||
| table_name = os.getenv("DB_TABLE_NAME") | ||
| table_region = os.getenv("DB_TABLE_REGION") | ||
|
|
||
| # Get uri from origin request event | ||
| request = event["Records"][0]["cf"]["request"] | ||
| web_uri = request["uri"] | ||
|
|
||
| # Query latest item with matching uri from DynamoDB table | ||
| LOG.info("Querying '%s' table for '%s'...", table_name, web_uri) | ||
|
|
||
| iso_now = datetime.now(timezone.utc).isoformat(timespec="milliseconds") | ||
|
|
||
| query_result = boto3.client("dynamodb", region_name=table_region).query( | ||
| TableName=table_name, | ||
| Limit=1, | ||
| ScanIndexForward=False, | ||
| KeyConditionExpression="web_uri = :u and from_date <= :d", | ||
| ExpressionAttributeValues={ | ||
| ":u": {"S": web_uri}, | ||
| ":d": {"S": str(iso_now)}, | ||
| }, | ||
| ) | ||
|
|
||
| if query_result["Items"]: | ||
| LOG.info("Item found for '%s'", web_uri) | ||
|
|
||
| try: | ||
| # Update request uri to point to S3 object key | ||
| request["uri"] = query_result["Items"][0]["object_key"]["S"] | ||
|
|
||
| return request | ||
| except Exception as err: | ||
| LOG.exception( | ||
| "Exception occurred while processing %s", | ||
| json.dumps(query_result["Items"][0]), | ||
| ) | ||
|
|
||
| raise err | ||
| else: | ||
| LOG.info("No item for '%s'", web_uri) | ||
|
|
||
| # Report 404 to prevent attempts on S3 | ||
| return { | ||
| "status": "404", | ||
| "statusDescription": "Not Found", | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| Function Reference | ||
| ================== | ||
|
|
||
| .. toctree:: | ||
| :maxdepth: 1 | ||
| :caption: Contents: | ||
|
|
||
| functions/map_to_s3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| map_to_s3 | ||
| ========= | ||
|
|
||
| This function provides mapping from a web URI, delivered by an AWS CloudFront | ||
| event, to the key of an object in an AWS S3 bucket. | ||
|
|
||
| The mapping between the URI and the object key is defined by the provided AWS | ||
| DynamoDB table this function is deployed with. | ||
|
|
||
| Required schemas for the DynamoDB table and S3 bucket can be found in the | ||
| :doc:`schema reference <../schema-reference>`. | ||
|
|
||
| Deployment | ||
| ---------- | ||
|
|
||
| This function may be deployed like any other AWS Lambda function using the | ||
| following event and environment variables. | ||
|
|
||
| Event | ||
| ^^^^^ | ||
| The event for this function must be a CloudFront distribution origin-request to | ||
| an S3 bucket. | ||
|
|
||
| Environment Variables | ||
| ^^^^^^^^^^^^^^^^^^^^^ | ||
| - DB_TABLE_NAME | ||
| (Required) | ||
|
|
||
| The name of the DynamoDB table from which to derive mapping. | ||
| - DB_TABLE_REGION | ||
| (Optional) | ||
|
|
||
| The AWS region in which the table resides. | ||
| If omitted, the region used is that of the function. | ||
|
|
||
| `AWS Lambda Environment Variables | ||
| <https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html>`_ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| Schema Reference | ||
| ================ | ||
|
|
||
| .. toctree:: | ||
| :maxdepth: 2 | ||
| :caption: Contents: | ||
|
|
||
| schemas/map_to_s3-schema |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| map_to_s3 Schemas | ||
| ================= | ||
|
|
||
| The following schemas are required of AWS S3 bucket objects and DynamoDB table | ||
| items for use with the map_to_s3 AWS Lambda function. | ||
|
|
||
| S3 Bucket Schema | ||
| ---------------- | ||
|
|
||
| Objects should be stored in the origin S3 bucket using the file’s sha256 | ||
| checksum as their key. | ||
|
|
||
| DynamoDB Table Schema | ||
| --------------------- | ||
|
|
||
| DynamoDB table items must possess the following keys and attributes. | ||
|
|
||
| Additional attributes are supported by the no-SQL model and may be used as | ||
| needed. | ||
|
|
||
| Keys | ||
| ^^^^ | ||
| - web_uri | ||
| (Primary) | ||
|
|
||
| A logical path to the desired content, excluding the hostname, | ||
| i.e., "/content/place/somepic.png". | ||
|
|
||
| - from_date | ||
| (Sort) | ||
|
|
||
| The datetime at which the content is made available, i.e., | ||
| "2020-02-17T20:48:13.037+00:00". | ||
|
|
||
| Only content sooner than or equal to the current date and time may be | ||
| retrieved from the origin. | ||
|
|
||
| Attributes | ||
| ^^^^^^^^^^ | ||
| - object_key | ||
| The key of the file object stored in the origin S3 bucket. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| boto3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| pytest | ||
| pytest | ||
| mock |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| from cdn_lambda.functions.map_to_s3 import lambda_handler | ||
| import os | ||
| import pytest | ||
| import mock | ||
| import json | ||
|
|
||
| TEST_PATH = "www.example.com/content/file.ext" | ||
| MOCKED_DT = "2020-02-17T15:38:05.864+00:00" | ||
|
|
||
|
|
||
| @mock.patch("boto3.client") | ||
| @mock.patch("cdn_lambda.functions.map_to_s3.datetime") | ||
| def test_map_to_s3(mocked_datetime, mocked_boto3_client): | ||
| mocked_datetime.now().isoformat.return_value = MOCKED_DT | ||
| mocked_boto3_client().query.return_value = { | ||
| "Items": [ | ||
| { | ||
| "web_uri": {"S": TEST_PATH}, | ||
| "from_date": {"S": "2020-02-17T00:00:00.000+00:00"}, | ||
| "object_key": {"S": "e4a3f2sum"}, | ||
| } | ||
| ] | ||
| } | ||
|
|
||
| env_vars = {"DB_TABLE_NAME": "test_table", "DB_TABLE_REGION": "us-east-1"} | ||
| event = {"Records": [{"cf": {"request": {"uri": TEST_PATH}}}]} | ||
|
|
||
| with mock.patch.dict(os.environ, env_vars): | ||
negillett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| request = lambda_handler(event, context=None) | ||
|
|
||
| assert request == {"uri": "e4a3f2sum"} | ||
|
|
||
|
|
||
| @mock.patch("boto3.client") | ||
| @mock.patch("cdn_lambda.functions.map_to_s3.datetime") | ||
| def test_map_to_s3_no_item(mocked_datetime, mocked_boto3_client): | ||
| mocked_datetime.now().isoformat.return_value = MOCKED_DT | ||
| mocked_boto3_client().query.return_value = {"Items": []} | ||
|
|
||
| env_vars = {"DB_TABLE_NAME": "test_table", "DB_TABLE_REGION": "us-east-1"} | ||
| event = {"Records": [{"cf": {"request": {"uri": TEST_PATH}}}]} | ||
|
|
||
| with mock.patch.dict(os.environ, env_vars): | ||
| request = lambda_handler(event, context=None) | ||
|
|
||
| assert request == {"status": "404", "statusDescription": "Not Found"} | ||
|
|
||
|
|
||
| @mock.patch("boto3.client") | ||
| @mock.patch("cdn_lambda.functions.map_to_s3.datetime") | ||
| def test_map_to_s3_invalid_item(mocked_datetime, mocked_boto3_client, caplog): | ||
| mocked_datetime.now().isoformat.return_value = MOCKED_DT | ||
| mocked_boto3_client().query.return_value = { | ||
| "Items": [ | ||
| { | ||
| "web_uri": {"S": TEST_PATH}, | ||
| "from_date": {"S": "2020-02-17T00:00:00.000+00:00"}, | ||
| } | ||
| ] | ||
| } | ||
|
|
||
| env_vars = {"DB_TABLE_NAME": "test_table", "DB_TABLE_REGION": "us-east-1"} | ||
| event = {"Records": [{"cf": {"request": {"uri": TEST_PATH}}}]} | ||
|
|
||
| with mock.patch.dict(os.environ, env_vars): | ||
| with pytest.raises(KeyError): | ||
| lambda_handler(event, context=None) | ||
|
|
||
| assert ( | ||
| "Exception occurred while processing %s" | ||
| % json.dumps( | ||
| { | ||
| "web_uri": {"S": TEST_PATH}, | ||
| "from_date": {"S": "2020-02-17T00:00:00.000+00:00"}, | ||
| } | ||
| ) | ||
| in caplog.text | ||
| ) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.