Skip to content

Commit 120feb6

Browse files
committed
Merge branch 'master' into sergio.prada/inject-trace-data-in-lambda-invocations
* master: ci: introduce riot, use it to run tracer tests (#1737) fix(django): add check for instrument view mro (#1744) fix: snapshot decorator for pytest fixtures (#1743) tests(logging): don't test warn (#1738) feat(flask): store request headers (#1735) ci: fix deploy_master (#1739)
2 parents cccd58f + bbc2343 commit 120feb6

27 files changed

+2344
-131
lines changed

.circleci/config.yml

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ commands:
5555
steps:
5656
- setup_tox
5757
- run: |
58-
apt-get update
59-
apt-get install -y --no-install-recommends libenchant-dev
58+
sudo apt-get update
59+
sudo apt-get install -y --no-install-recommends libenchant-dev
6060
- run: pip install awscli
6161
- run: tox -e docs
6262
- run:
@@ -106,6 +106,30 @@ commands:
106106
paths:
107107
- ".tox"
108108

109+
run_test:
110+
description: "Run tests matching a pattern"
111+
parameters:
112+
pattern:
113+
type: string
114+
default: ""
115+
wait:
116+
type: string
117+
default: ""
118+
steps:
119+
- attach_workspace:
120+
at: .
121+
- run: pip3 install riot
122+
- when:
123+
condition:
124+
<< parameters.wait >>
125+
steps:
126+
- run:
127+
name: "Waiting for << parameters.wait >>"
128+
command: pip install tox && tox -e 'wait' << parameters.wait >>
129+
- run:
130+
name: "Running tests matching '<< parameters.pattern >>'"
131+
command: "riot -v run -s '<< parameters.pattern >>'"
132+
109133
run_tox_scenario:
110134
description: "Run scripts/run-tox-scenario with setup, caching and persistence"
111135
parameters:
@@ -231,6 +255,19 @@ jobs:
231255
- setup_tox
232256
- run: tox -e flake8
233257

258+
build_base:
259+
executor: ddtrace_dev
260+
steps:
261+
- checkout
262+
- run: pip3 install riot
263+
- run:
264+
name: "Generate base virtual environments."
265+
command: "riot -v generate"
266+
- persist_to_workspace:
267+
root: .
268+
paths:
269+
- "."
270+
234271
build-docker-ci-image:
235272
executor: cimg_base
236273
steps:
@@ -292,8 +329,8 @@ jobs:
292329
tracer:
293330
executor: ddtrace_dev
294331
steps:
295-
- run_tox_scenario:
296-
pattern: '^py..-tracer'
332+
- run_test:
333+
pattern: "tracer"
297334

298335
opentracer:
299336
executor: ddtrace_dev
@@ -444,15 +481,15 @@ jobs:
444481
pattern: '^falcon_contrib'
445482

446483
django:
447-
executor: ddtrace_dev
448-
docker:
449-
- image: *ddtrace_dev_image
450-
- image: *redis_image
451-
- image: *memcached_image
452-
- *datadog_agent
484+
<<: *machine_executor
453485
steps:
454-
- run_tox_scenario:
455-
pattern: '^django_contrib'
486+
- checkout
487+
- run: SNAPSHOT_CI=1 docker-compose up -d memcached redis testagent
488+
- run:
489+
command: docker-compose logs -f
490+
background: true
491+
- run:
492+
command: ./scripts/ddtest scripts/run-tox-scenario '^django_contrib-'
456493

457494
djangorestframework:
458495
executor: ddtrace_dev
@@ -803,6 +840,7 @@ requires_pre_test: &requires_pre_test
803840
- black
804841
- flake8
805842
- ccheck
843+
- build_base
806844

807845
requires_tests: &requires_tests
808846
- build_docs
@@ -893,6 +931,7 @@ workflows:
893931
- black
894932
- flake8
895933
- ccheck
934+
- build_base
896935

897936
# Test building the package
898937
- test_build_alpine: *requires_pre_test
@@ -963,7 +1002,6 @@ workflows:
9631002
- sqlite3: *requires_pre_test
9641003
- test_logging: *requires_pre_test
9651004
- tornado: *requires_pre_test
966-
# tracer
9671005
- tracer: *requires_pre_test
9681006
- vertica: *requires_pre_test
9691007
- build-docker-ci-image: *requires_pre_test

.github/PULL_REQUEST_TEMPLATE/integration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ This PR adds support for [`<integration>`](<!--link to relevant integration docs
2727
- [ ] 500-level responses are tagged as errors
2828
- [ ] Async (if applicable)
2929
- [ ] Span parenting behaves as expected.
30+
- [ ] Request headers specified in the config are stored

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,6 @@ RUN \
6363
&& pyenv global 3.8.6 2.7.17 3.5.10 3.6.12 3.7.9 3.9.0 \
6464
&& pip install --upgrade pip
6565

66-
RUN pip install tox
66+
RUN pip install tox riot
6767

6868
CMD ["/bin/bash"]

ddtrace/contrib/django/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,10 @@
252252
with require_modules(required_modules) as missing_modules:
253253
if not missing_modules:
254254
from .middleware import TraceMiddleware
255+
from . import patch as _patch
255256
from .patch import patch, unpatch
256257

257-
__all__ = ["patch", "unpatch", "TraceMiddleware"]
258+
__all__ = ["patch", "unpatch", "TraceMiddleware", "_patch"]
258259

259260

260261
# define the Django app configuration

ddtrace/contrib/django/patch.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,13 +465,11 @@ def instrument_view(django, view):
465465
466466
We want to wrap all lifecycle/http method functions for every class in the MRO for this view
467467
"""
468-
if isfunction(view):
469-
return _instrument_view(django, view)
468+
if hasattr(view, "__mro__"):
469+
for cls in reversed(getmro(view)):
470+
_instrument_view(django, cls)
470471

471-
for cls in reversed(getmro(view)):
472-
_instrument_view(django, cls)
473-
474-
return view
472+
return _instrument_view(django, view)
475473

476474

477475
def _instrument_view(django, view):

ddtrace/contrib/flask/middleware.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from ...internal.logger import get_logger
44
from ...propagation.http import HTTPPropagator
55
from ...utils.deprecation import deprecated
6+
from ddtrace.http import store_request_headers
7+
from ddtrace import config
68

79
import flask.templating
810
from flask import g, request, signals
@@ -155,6 +157,7 @@ def _finish_span(self, span, exception=None):
155157
endpoint = ''
156158
url = ''
157159
if request:
160+
store_request_headers(request.headers, span, config.flask)
158161
method = request.method
159162
endpoint = request.endpoint or code
160163
url = request.base_url or ''
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
Check Django view before instrumenting MRO
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- |
4+
Store request headers in Flask integration.

riotfile.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from riot import Suite, Case
2+
3+
global_deps = [
4+
"mock",
5+
"pytest<4",
6+
"pytest-benchmark",
7+
]
8+
9+
global_env = [("PYTEST_ADDOPTS", "--color=yes")]
10+
11+
suites = [
12+
Suite(
13+
name="tracer",
14+
command="pytest tests/tracer/",
15+
cases=[
16+
Case(
17+
pys=[
18+
2.7,
19+
3.5,
20+
3.6,
21+
3.7,
22+
3.8,
23+
],
24+
pkgs=[("msgpack", [""])],
25+
),
26+
],
27+
),
28+
]

tests/__init__.py

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import contextlib
2-
import functools
32
import inspect
43
import os
54
import sys
65
from contextlib import contextmanager
76

87
import pytest
8+
from ddtrace.vendor import wrapt
99

1010
import ddtrace
1111
from ddtrace import Tracer, Span
12-
from ddtrace.compat import httplib
12+
from ddtrace.compat import httplib, to_unicode
1313
from ddtrace.constants import SPAN_MEASURED_KEY
1414
from ddtrace.encoding import JSONEncoder
1515
from ddtrace.ext import http
@@ -815,54 +815,55 @@ def snapshot(ignores=None, tracer=ddtrace.tracer):
815815
"""
816816
ignores = ignores or []
817817

818-
def dec(f):
819-
@functools.wraps(f)
820-
def wrapper(*args, **kwargs):
821-
if len(args) > 1:
822-
self = args[0]
823-
clsname = self.__class__.__name__
824-
else:
825-
clsname = ""
818+
@wrapt.decorator
819+
def wrapper(wrapped, instance, args, kwargs):
820+
if len(args) > 1:
821+
self = args[0]
822+
clsname = self.__class__.__name__
823+
else:
824+
clsname = ""
826825

827-
module = inspect.getmodule(f)
826+
module = inspect.getmodule(wrapped)
828827

829-
# Use the fully qualified function name as a unique test token to
830-
# identify the snapshot.
831-
token = "{}{}{}.{}".format(module.__name__, "." if clsname else "", clsname, f.__name__)
828+
# Use the fully qualified function name as a unique test token to
829+
# identify the snapshot.
830+
token = "{}{}{}.{}".format(module.__name__, "." if clsname else "", clsname, wrapped.__name__)
832831

833-
conn = httplib.HTTPConnection(tracer.writer.api.hostname, tracer.writer.api.port)
832+
conn = httplib.HTTPConnection(tracer.writer.api.hostname, tracer.writer.api.port)
833+
try:
834+
# clear queue in case traces have been generated before test case is
835+
# itself run
836+
tracer.writer.flush_queue()
837+
838+
# Signal the start of this test case to the test agent.
834839
try:
835-
# Signal the start of this test case to the test agent.
836-
try:
837-
conn.request("GET", "/test/start?token=%s" % token)
838-
except Exception as e:
839-
pytest.fail("Could not connect to test agent: %s" % str(e), pytrace=False)
840-
841-
r = conn.getresponse()
842-
if r.status != 200:
843-
# The test agent returns nice error messages we can forward to the user.
844-
raise SnapshotFailed(r.read().decode())
845-
846-
# Run the test.
847-
ret = f(*args, **kwargs)
848-
849-
# Flush out any remnant traces.
850-
tracer.writer.flush_queue()
851-
852-
# Query for the results of the test.
853-
conn = httplib.HTTPConnection(tracer.writer.api.hostname, tracer.writer.api.port)
854-
conn.request("GET", "/test/snapshot?ignores=%s&token=%s" % (",".join(ignores), token))
855-
r = conn.getresponse()
856-
if r.status != 200:
857-
raise SnapshotFailed(r.read().decode())
858-
return ret
859-
except SnapshotFailed as e:
860-
# Fail the test if a failure has occurred and print out the
861-
# message we got from the test agent.
862-
pytest.fail(str(e), pytrace=False)
863-
finally:
864-
conn.close()
865-
866-
return wrapper
867-
868-
return dec
840+
conn.request("GET", "/test/start?token=%s" % token)
841+
except Exception as e:
842+
pytest.fail("Could not connect to test agent: %s" % str(e), pytrace=False)
843+
844+
r = conn.getresponse()
845+
if r.status != 200:
846+
# The test agent returns nice error messages we can forward to the user.
847+
raise SnapshotFailed(r.read())
848+
849+
# Run the test.
850+
ret = wrapped(*args, **kwargs)
851+
852+
# Flush out any remnant traces.
853+
tracer.writer.flush_queue()
854+
855+
# Query for the results of the test.
856+
conn = httplib.HTTPConnection(tracer.writer.api.hostname, tracer.writer.api.port)
857+
conn.request("GET", "/test/snapshot?ignores=%s&token=%s" % (",".join(ignores), token))
858+
r = conn.getresponse()
859+
if r.status != 200:
860+
raise SnapshotFailed(r.read())
861+
return ret
862+
except SnapshotFailed as e:
863+
# Fail the test if a failure has occurred and print out the
864+
# message we got from the test agent.
865+
pytest.fail(to_unicode(e.args[0]), pytrace=False)
866+
finally:
867+
conn.close()
868+
869+
return wrapper

tests/contrib/django/conftest.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,24 @@ def pytest_configure():
2424
django.setup()
2525

2626

27-
@pytest.fixture(autouse=True)
28-
def patch_django(tracer):
27+
@pytest.fixture
28+
def tracer():
29+
tracer = DummyTracer()
2930
# Patch Django and override tracer to be our test tracer
3031
pin = Pin.get_from(django)
3132
original_tracer = pin.tracer
3233
Pin.override(django, tracer=tracer)
3334

3435
# Yield to our test
35-
yield
36+
yield tracer
37+
tracer.writer.pop()
3638

3739
# Reset the tracer pinned to Django and unpatch
3840
# DEV: unable to properly unpatch and reload django app with each test
3941
# unpatch()
4042
Pin.override(django, tracer=original_tracer)
4143

4244

43-
@pytest.fixture
44-
def tracer():
45-
tracer = DummyTracer()
46-
yield tracer
47-
tracer.writer.pop()
48-
49-
5045
@pytest.fixture
5146
def test_spans(tracer):
5247
container = TracerSpanContainer(tracer)

0 commit comments

Comments
 (0)