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
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[report]
include = raven_python_lambda/*.py
omit = raven_python_lambda/__about__.py
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ var/
.installed.cfg
*.egg

venv/
venv

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
Expand Down
15 changes: 15 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
language: python

python:
- "2.7"
- "3.5"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lambda doesn't support 3.5

- "3.6"

install:
- pip install tox-travis coveralls

script:
- tox

after_success:
- coveralls
4 changes: 4 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Kevin Glisson <kevgliss>
Mike Grima <mikegrima> @THISisPLACEHLDR
Michal Przytulski <mprzytulski>
Björn Andersson <bjorn-spire>
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# ⚡️ Sentry/Raven SDK Integration For AWS Lambda (python) and Serverless

[![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com)

[![Build Status](https://travis-ci.org/Netflix-Skunkworks/raven-python-lambda.svg?branch=master)](https://travis-ci.org/Netflix-Skunkworks/raven-python-lambda)
[![Coverage Status](https://coveralls.io/repos/github/Netflix-Skunkworks/raven-python-lambda/badge.svg?branch=master)](https://coveralls.io/github/Netflix-Skunkworks/raven-python-lambda?branch=master)

## About
This library simplifies integration of Sentry's
Expand Down
14 changes: 9 additions & 5 deletions raven_python_lambda/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""
raven_python_lambda.lambda
~~~~~~~~~~~~~~~~~~~~~~~~~~

Raven wrapper for AWS Lambda handlers.
.. module: raven_python_lambda
:platform: Unix
:copyright: (c) 2017 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.

.. moduleauthor:: Kevin Glisson <kevgliss>
.. moduleauthor:: Mike Grima <mikegrima> @THISisPLACEHLDR
"""
import os
import math
Expand All @@ -18,6 +20,8 @@
from raven.transport.http import HTTPTransport
from raven.handlers.logging import SentryHandler

from raven_python_lambda.sqs_transport import SQSTransport

logging.basicConfig()
logger = logging.getLogger(__file__)

Expand Down Expand Up @@ -50,7 +54,7 @@ def configure_raven_client(config):
'alias': os.environ.get('SERVERLESS_ALIAS'),
'region': os.environ.get('SERVERLESS_REGION') or os.environ.get('AWS_REGION')
},
'transport': HTTPTransport,
'transport': SQSTransport if "sqs_name" in os.environ.get('SENTRY_DSN', "") else HTTPTransport,
'dsn': os.environ.get('SENTRY_DSN')
}

Expand Down
59 changes: 59 additions & 0 deletions raven_python_lambda/sqs_transport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
.. module: raven_python_lambda.sqs_transport
:platform: Unix
:copyright: (c) 2017 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.

.. moduleauthor:: Mike Grima <mikegrima> @THISisPLACEHLDR
"""
import base64
import json

import boto3

from raven.utils.compat import string_types
from raven.conf import defaults
from raven.transport.base import Transport


class SQSTransport(Transport):
scheme = ['sqs+https', 'sqs+http']

def __init__(self, sqs_region, sqs_account, sqs_name,
timeout=defaults.TIMEOUT, verify_ssl=True,
ca_certs=defaults.CA_BUNDLE):
# Stuff the docs require:
if isinstance(timeout, string_types):
timeout = int(timeout)
if isinstance(verify_ssl, string_types):
verify_ssl = bool(int(verify_ssl))

self.timeout = timeout
self.verify_ssl = verify_ssl
self.ca_certs = ca_certs
#####################

# Stuff SQS requires:
self.sqs_name = sqs_name
self.sqs_account = sqs_account
self.sqs_client = boto3.client("sqs", region_name=sqs_region)
self.queue_url = None

def send(self, url, data, headers):
"""
Sends a request to an SQS queue -- to be later popped off
later for submission into Sentry.
Note: This will simply raise any Boto ClientErrors that are encountered.
"""
if not self.queue_url:
self.queue_url = self.sqs_client.get_queue_url(QueueName=self.sqs_name,
QueueOwnerAWSAccountId=self.sqs_account)["QueueUrl"]

payload = {
"url": url,
"headers": headers,
"data": base64.b64encode(data).decode("utf-8")
}

self.sqs_client.send_message(QueueUrl=self.queue_url,
MessageBody=json.dumps(payload))
Empty file.
42 changes: 42 additions & 0 deletions raven_python_lambda/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
.. module: raven_python_lambda.tests.conftest
:platform: Unix
:copyright: (c) 2017 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.

.. moduleauthor:: Mike Grima <mikegrima> @THISisPLACEHLDR
"""

from moto import mock_sqs
import boto3
import pytest
import os


class DsnEnvVar:
def __init__(self):
self.old_dsn = os.environ.get("SENTRY_DSN")

def __enter__(self):
os.environ["SENTRY_DSN"] = "https://asdfasdfds:Lasdfasdfadfs@somesentry.com/project" \
"?sqs_name=sentry-queue&sqs_region=us-east-1&sqs_account=123456789012"

def __exit__(self, *args):
if self.old_dsn:
os.environ["SENTRY_DSN"] = self.old_dsn
else:
del os.environ["SENTRY_DSN"]


@pytest.fixture(scope="function")
def sqs():
with mock_sqs():
yield boto3.client("sqs", region_name="us-east-1")


@pytest.fixture(scope="function")
def sqs_queue(sqs):
with DsnEnvVar():
sqs.create_queue(QueueName="sentry-queue")
yield sqs.get_queue_url(QueueName="sentry-queue",
QueueOwnerAWSAccountId="123456789012")["QueueUrl"]
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import threading
from time import sleep
import os

import pytest

from raven_python_lambda import RavenLambdaWrapper


def test_raven_lambda_wrapper():
@RavenLambdaWrapper()
def test_func(event, context):
Expand Down Expand Up @@ -34,3 +36,16 @@ def f(event, context):

sleep(0.1) # A bit iffy. But if we don't wait a bit the threads will not have stopped
assert threading.active_count() == 1, 'expected all scheduled threads to have been removed'


def test_that_sqs_transport_is_used(sqs, sqs_queue):
@RavenLambdaWrapper()
def test_func(event, context):
raise Exception('There was an error.')

with pytest.raises(Exception):
test_func({}, FakeContext())

# Check that it sent to SQS:
messages = sqs.receive_message(QueueUrl=sqs_queue)["Messages"]
assert len(messages) == 1
54 changes: 54 additions & 0 deletions raven_python_lambda/tests/test_sqs_transport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
.. module: raven_python_lambda.tests.test_decorator
:platform: Unix
:copyright: (c) 2017 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.

.. moduleauthor:: Mike Grima <mikegrima> @THISisPLACEHLDR
"""
import boto3

from raven_python_lambda.sqs_transport import SQSTransport
from raven.base import Client

# Simplify comparing dicts with primitive values:
from raven.utils import json
import zlib

import base64


def test_sqs_transport(sqs_queue):
sqs = boto3.client("sqs", region_name="us-east-1")

c = Client(dsn="mock://some_username:some_password@localhost:8143/1"
"?sqs_region=us-east-1&sqs_account=123456789012&sqs_name=sentry-queue",
transport=SQSTransport)

data = dict(a=42, b=55, c=list(range(50)))
expected_message = zlib.decompress(c.encode(data))

c.send(**data)

transport = c._transport_cache["mock://some_username:some_password@localhost:8143/1"
"?sqs_region=us-east-1&sqs_account=123456789012"
"&sqs_name=sentry-queue"].get_transport()

assert transport.sqs_account == "123456789012"
assert transport.sqs_name == "sentry-queue"
assert type(transport.sqs_client).__name__ == type(sqs).__name__
assert transport.queue_url == "https://queue.amazonaws.com/123456789012/sentry-queue"

# Check SQS for the message that was sent over:
messages = sqs.receive_message(QueueUrl=transport.queue_url)["Messages"]
assert len(messages) == 1

body = json.loads(messages[0]["Body"])

assert body["url"] == "mock://localhost:8143/api/1/store/"
assert "sentry_secret=some_password" in body["headers"]["X-Sentry-Auth"]

decoded_data = base64.b64decode(body["data"])

assert json.dumps(json.loads(expected_message.decode('utf-8')), sort_keys=True) == \
json.dumps(c.decode(decoded_data), sort_keys=True) # noqa
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@

tests_require = [
'pytest',
'coverall'
'moto',
'coveralls',
'tox'
]

setup(
Expand Down
12 changes: 10 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,13 @@
envlist = py27,py36

[testenv]
deps=pytest
commands=pytest
passenv =
TRAVIS
TRAVIS_JOB_ID
TRAVIS_BRANCH
deps =
pytest
pytest-cov
moto
commands =
pytest --cov=raven_python_lambda