Skip to content

Commit

Permalink
Fix Sentry integration
Browse files Browse the repository at this point in the history
Why these changes are being introduced:
Lambda functions require a special sentry integration option which we
had not previously used so the integration was broken.

How this addresses that need:
* Adds Sentry lambda integration to webhook module and moves sentry init
  to top of module as required instead of within the lambda_handler
  function.
* Adds check for required WORKSPACE env in lambda_handler function.
* Adds tests for new functionality.
* Updates README to include local testing with Docker.
* Removes duplicate line in Dockerfile.

Side effects of this change:
Exceptions should now be sent to Sentry as expected in all AWS envs.

Relevant ticket(s):
* https://mitlibraries.atlassian.net/browse/IN-526
  • Loading branch information
hakbailey committed Aug 5, 2022
1 parent 0d26c83 commit 2bb39fd
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 16 deletions.
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ FROM public.ecr.aws/lambda/python:3.9

# Copy function code
COPY . ${LAMBDA_TASK_ROOT}/
COPY . ${LAMBDA_TASK_ROOT}/

# Install dependencies
RUN pip3 install pipenv
Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# alma-webhook-lambdas

Lambda functions to receive and handle Alma webhook requests

## Developing locally

### Installation and setup

- To install dependencies: `make install`
- To run tests: `make test`
- To update dependencies: `make update`

Required env variables:

- `ALMA_CHALLENGE_SECRET=itsasecret`: this value will work with the test fixtures, must
match Alma sandbox/prod configured challenge secrets in Dev1, stage, and prod
environments.
Expand All @@ -19,28 +22,35 @@ Required env variables:
- `SENTRY_DSN`: only needed in production.

### To verify local changes in Dev1

- Ensure your aws cli is configured with credentials for the Dev1 account.
- Ensure you have the above env variables set in your .env, matching those in our Dev1 environment.
- Add the following to your .env: `LAMBDA_FUNCTION_URL=<the Dev1 lambda function URL>`
- Publish the lambda function:

```bash
make publish-dev
make update-lambda-dev
```

#### GET request example

- Send a GET request with challenge phrase to the lambda function URL:

```bash
pipenv run python -c "from lambdas.helpers import send_get_to_lambda_function_url; print(send_get_to_lambda_function_url('your challenge phrase'))"
```

Observe output: `your challenge phrase`

#### POST request examples

- Send a POST request mimicking a webhook POST (not a POD export job)

```bash
pipenv run python -c "from lambdas.helpers import send_post_to_lambda_function_url, SAMPLE_WEBHOOK_POST_BODY; print(send_post_to_lambda_function_url(SAMPLE_WEBHOOK_POST_BODY))"
```

Observe output: `Webhook POST request received and validated, no action taken.`

- Send a POST request mimicking a POD export job webhook
Expand All @@ -52,4 +62,35 @@ Required env variables:
```bash
pipenv run python -c "from lambdas.helpers import send_post_to_lambda_function_url, SAMPLE_POD_EXPORT_JOB_END_WEBHOOK_POST_BODY; print(send_post_to_lambda_function_url(SAMPLE_POD_EXPORT_JOB_END_WEBHOOK_POST_BODY))"
```
Observe output: `Webhook POST request received and validated, POD upload initiated.` and then check the Dev1 ppod state machine logs to confirm the entire process ran!

Observe output: `Webhook POST request received and validated, POD upload initiated.` and then check the Dev1 ppod state machine logs to confirm the entire process ran!

## Running locally with Docker

Note: this is only useful for validating exceptions and error states, as success states require calling other AWS services for which we do not currently support local emulation.

<https://docs.aws.amazon.com/lambda/latest/dg/images-test.html>

### Build the container

```bash
make dist-dev
```

### Run the default handler for the container

```bash
docker run -p 9000:8080 alma-webhook-lambdas-dev:latest
```

Depending on what you're testing, you may need to pass `-e WORKSPACE=dev` and/or other environment variables as options to the `docker run` command.

### POST to the container

```bash
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d "{}"
```

### Observe output

Running the above with no env variables passed should result in an exception.
25 changes: 18 additions & 7 deletions lambdas/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,32 @@

import boto3
import sentry_sdk
from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

env = os.getenv("WORKSPACE")
if sentry_dsn := os.getenv("SENTRY_DSN"):
sentry = sentry_sdk.init(
dsn=sentry_dsn,
environment=env,
integrations=[
AwsLambdaIntegration(),
],
traces_sample_rate=1.0,
)
logger.info("Sentry DSN found, exceptions will be sent to Sentry with env=%s", env)
else:
logger.info("No Sentry DSN found, exceptions will not be sent to Sentry")

def lambda_handler(event: dict, context: object) -> dict[str, object]:
env = os.environ["WORKSPACE"]
if sentry_dsn := os.getenv("SENTRY_DSN"):
sentry_sdk.init(sentry_dsn, environment=env)
logger.info(
"Sentry DSN found, exceptions will be sent to Sentry with env=%s", env
)

def lambda_handler(event: dict, context: object) -> dict[str, object]:
logger.debug(json.dumps(event))

if not os.getenv("WORKSPACE"):
raise RuntimeError("Required env variable WORKSPACE is not set")

base_response = {
"headers": {"Content-Type": "text/plain"},
"isBase64Encoded": False,
Expand Down
20 changes: 13 additions & 7 deletions tests/test_webhook.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import datetime
import json
import logging
from importlib import reload
from unittest.mock import patch

import pytest

from lambdas import webhook
from lambdas.webhook import (
count_exported_records,
execute_state_machine,
Expand All @@ -13,10 +14,9 @@
)


def test_webhook_configures_sentry_if_dsn_present(caplog, get_request, monkeypatch):
def test_webhook_configures_sentry_if_dsn_present(caplog, monkeypatch):
monkeypatch.setenv("SENTRY_DSN", "https://1234567890@00000.ingest.sentry.io/123456")
caplog.set_level(logging.INFO)
lambda_handler(get_request, {})
reload(webhook)
assert (
"Sentry DSN found, exceptions will be sent to Sentry with env=test"
in caplog.text
Expand All @@ -27,9 +27,15 @@ def test_webhook_doesnt_configure_sentry_if_dsn_not_present(
caplog, get_request, monkeypatch
):
monkeypatch.delenv("SENTRY_DSN", raising=False)
caplog.set_level(logging.INFO)
lambda_handler(get_request, {})
assert "Sentry DSN found" not in caplog.text
reload(webhook)
assert "No Sentry DSN found, exceptions will not be sent to Sentry" in caplog.text


def test_webhook_missing_workspace_env_raises_error(monkeypatch):
monkeypatch.delenv("WORKSPACE", raising=False)
with pytest.raises(RuntimeError) as e:
lambda_handler({}, {})
assert "Required env variable WORKSPACE is not set" in str(e)


def test_webhook_missing_request_method_raises_error():
Expand Down

0 comments on commit 2bb39fd

Please sign in to comment.