diff --git a/Pipfile b/Pipfile index be16ef4..f3944f3 100644 --- a/Pipfile +++ b/Pipfile @@ -4,6 +4,7 @@ verify_ssl = true name = "pypi" [packages] +sentry-sdk = "*" [dev-packages] bandit = "*" @@ -11,6 +12,7 @@ black = "*" coverage = "*" coveralls = "*" mypy = "*" +pyflakes = "==2.4.0" pylama = {extras = ["all"], version = "*"} pytest = "*" diff --git a/README.md b/README.md index b68a520..52a9072 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ A template repository for creating Python lambda functions. - Dependabot alerts - Dependabot security updates - Secret scanning +7. Create a Sentry project for the app if needed (we want this for most apps): + - Send initial exceptions to Sentry project for dev, stage, and prod environments to create them. + - Create an alert for the prod environment only, with notifications sent to the appropriate team(s). + - If *not* using Sentry, delete Sentry configuration from my_function.py and test_my_function_.py, and remove sentry_sdk from project dependencies. # my_function @@ -29,6 +33,7 @@ Description of the function/functions. ## Required ENV +- `SENTRY_DSN` = If set to a valid Sentry DSN, enables Sentry exception monitoring. This is not needed for local development. - `WORKSPACE` = Set to `dev` for local development, this will be set to `stage` and `prod` in those environments by Terraform. ## Running locally @@ -44,7 +49,7 @@ Description of the function/functions. - Run the default handler for the container: ```bash - docker run -p 9000:8080 my_function:latest + docker run -e WORKSPACE=dev -p 9000:8080 my_function:latest ``` - Post to the container: diff --git a/lambdas/my_function.py b/lambdas/my_function.py index c399f35..96b28c7 100644 --- a/lambdas/my_function.py +++ b/lambdas/my_function.py @@ -1,10 +1,32 @@ import json import logging +import os + +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) -> str: # noqa + if not os.getenv("WORKSPACE"): + raise RuntimeError("Required env variable WORKSPACE is not set") + logger.debug(json.dumps(event)) + return "You have successfully called this lambda!" diff --git a/setup.cfg b/setup.cfg index 2763ff7..7145a37 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,5 +11,8 @@ max_line_length = 90 [pylama:isort] profile = black +[pylama:pydocstyle] +convention = pep257 + [tool:pytest] log_level = DEBUG diff --git a/tests/test_function.py b/tests/test_function.py deleted file mode 100644 index 749a1c4..0000000 --- a/tests/test_function.py +++ /dev/null @@ -1,5 +0,0 @@ -from lambdas.my_function import lambda_handler - - -def test_my_function(): - assert lambda_handler({}, {}) == "You have successfully called this lambda!" diff --git a/tests/test_my_function.py b/tests/test_my_function.py new file mode 100644 index 0000000..99540ef --- /dev/null +++ b/tests/test_my_function.py @@ -0,0 +1,34 @@ +from importlib import reload + +import pytest + +from lambdas import my_function + + +def test_my_function_configures_sentry_if_dsn_present(caplog, monkeypatch): + monkeypatch.setenv("SENTRY_DSN", "https://1234567890@00000.ingest.sentry.io/123456") + reload(my_function) + assert ( + "Sentry DSN found, exceptions will be sent to Sentry with env=test" + in caplog.text + ) + + +def test_my_function_doesnt_configure_sentry_if_dsn_not_present(caplog, monkeypatch): + monkeypatch.delenv("SENTRY_DSN", raising=False) + reload(my_function) + assert "No Sentry DSN found, exceptions will not be sent to Sentry" in caplog.text + + +def test_lambda_handler_missing_workspace_env_raises_error(monkeypatch): + monkeypatch.delenv("WORKSPACE", raising=False) + with pytest.raises(RuntimeError) as error: + my_function.lambda_handler({}, {}) + assert "Required env variable WORKSPACE is not set" in str(error) + + +def test_my_function(): + assert ( + my_function.lambda_handler({}, {}) + == "You have successfully called this lambda!" + )