Esempio n. 1
0
def test_flush_connection_timeout(endpoint_test_timeout_server):
    payload = mock.Mock()
    payload.get_payload.return_value = "foobar"
    payload.length = 12
    api = API(_HOST, _TIMEOUT_PORT)
    response = api._flush(payload)
    assert isinstance(response, socket.timeout)
Esempio n. 2
0
class TestRateByService(TestCase):
    """
    Check we get feedback from the agent and we're able to process it.
    """
    def setUp(self):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API('localhost', 8126, encoder=JSONEncoder())
        self.api_msgpack = API('localhost', 8126, encoder=MsgpackEncoder())

    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # [TODO:christian] when CI has an agent that is able to process the v0.4
        # endpoint, add a check to:
        # - make sure the output is a valid JSON
        # - make sure the priority sampler (if enabled) is updated

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)
Esempio n. 3
0
def test_flush_connection_uds(endpoint_uds_server):
    payload = mock.Mock()
    payload.get_payload.return_value = "foobar"
    payload.length = 12
    api = API(_HOST, 2019, uds_path=endpoint_uds_server.server_address)
    response = api._flush(payload)
    assert response.status == 200
Esempio n. 4
0
class TestRateByService(TestCase):
    """
    Check we get feedback from the agent and we're able to process it.
    """
    def setUp(self):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API('localhost', 8126, encoder=JSONEncoder())
        self.api_msgpack = API('localhost', 8126, encoder=MsgpackEncoder())

    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # [TODO:christian] when CI has an agent that is able to process the v0.4
        # endpoint, add a check to:
        # - make sure the output is a valid JSON
        # - make sure the priority sampler (if enabled) is updated

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)
Esempio n. 5
0
 def setUp(self):
     """
     Create a tracer without workers, while spying the ``send()`` method
     """
     # create a new API object to test the transport using synchronous calls
     self.tracer = get_dummy_tracer()
     self.api_json = API('localhost', 8126, encoder=JSONEncoder())
     self.api_msgpack = API('localhost', 8126, encoder=MsgpackEncoder())
 def setUp(self):
     """
     Create a tracer without workers, while spying the ``send()`` method
     """
     # create a new API object to test the transport using synchronous calls
     self.tracer = get_dummy_tracer()
     self.api_json = API('localhost', 7777, encoder=JSONEncoder())
     self.api_msgpack = API('localhost', 7777, encoder=MsgpackEncoder())
Esempio n. 7
0
def test_https():
    conn = mock.MagicMock(spec=httplib.HTTPSConnection)
    api = API('localhost', 8126, https=True)
    with mock.patch('ddtrace.compat.httplib.HTTPSConnection') as HTTPSConnection:
        HTTPSConnection.return_value = conn
        api._put('/test', '<test data>', 1)
    conn.request.assert_called_once()
    conn.close.assert_called_once()
Esempio n. 8
0
def test_flush_connection_reset(endpoint_test_reset_server):
    payload = mock.Mock()
    payload.get_payload.return_value = 'foobar'
    payload.length = 12
    api = API(_HOST, _RESET_PORT)
    response = api._flush(payload)
    if PY3:
        assert isinstance(response, (httplib.BadStatusLine, ConnectionResetError))  # noqa: F821
    else:
        assert isinstance(response, httplib.BadStatusLine)
Esempio n. 9
0
def test_flush_connection_timeout_connect():
    payload = mock.Mock()
    payload.get_payload.return_value = 'foobar'
    payload.length = 12
    api = API(_HOST, 2019)
    response = api._flush(payload)
    if PY3:
        assert isinstance(response, (OSError, ConnectionRefusedError))  # noqa: F821
    else:
        assert isinstance(response, socket.error)
    assert response.errno in (99, 111)
Esempio n. 10
0
    def setUp(self, get_container_info):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # Mock the container id we use for making requests
        get_container_info.return_value = CGroupInfo(container_id="test-container-id")

        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API("localhost", 8126, encoder=JSONEncoder())
        self.api_msgpack = API("localhost", 8126, encoder=MsgpackEncoder())
Esempio n. 11
0
def test_api_container_info(get_container_info):
    # When we have container information
    # DEV: `get_container_info` will return a `CGroupInfo` with a `container_id` or `None`
    info = CGroupInfo(container_id="test-container-id")
    get_container_info.return_value = info

    api = API(_HOST, 8126)
    assert api._container_info is info
    assert api._headers["Datadog-Container-Id"] == "test-container-id"

    # When we do not have container information
    get_container_info.return_value = None

    api = API(_HOST, 8126)
    assert api._container_info is None
    assert "Datadog-Container-Id" not in api._headers
Esempio n. 12
0
class TestRateByService(TestCase):
    """
    Check we get feedback from the agent and we're able to process it.
    """
    def setUp(self):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API("localhost",
                            8126,
                            encoder=JSONEncoder(),
                            priority_sampling=True)
        self.api_msgpack = API("localhost",
                               8126,
                               encoder=MsgpackEncoder(),
                               priority_sampling=True)

    @pytest.mark.skipif(AGENT_VERSION == "testagent",
                        reason="Test agent doesn't yet support rate sampling.")
    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace("client.testing").finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # [TODO:christian] when CI has an agent that is able to process the v0.4
        # endpoint, add a check to:
        # - make sure the output is a valid JSON
        # - make sure the priority sampler (if enabled) is updated

        # test JSON encoder
        responses = self.api_json.send_traces(traces)
        assert len(responses) == 1
        assert responses[0].status == 200
        resp = responses[0].get_json()
        assert isinstance(resp["rate_by_service"]["service:,env:"],
                          numeric_types)

        # test Msgpack encoder
        responses = self.api_msgpack.send_traces(traces)
        assert len(responses) == 1
        assert responses[0].status == 200
        resp = responses[0].get_json()
        assert isinstance(resp["rate_by_service"]["service:,env:"],
                          numeric_types)
Esempio n. 13
0
    def test_downgrade_api(self):
        # make a call to a not existing endpoint, downgrades
        # the current API to a stable one
        tracer = get_dummy_tracer()
        tracer.trace('client.testing').finish()
        trace = tracer.writer.pop()

        # the encoder is right but we're targeting an API
        # endpoint that is not available
        api = API('localhost', 8126)
        api._traces = '/v0.0/traces'
        assert isinstance(api._encoder, MsgpackEncoder)

        # after the call, we downgrade to a working endpoint
        response = api.send_traces([trace])
        assert response
        assert response.status == 200
        assert isinstance(api._encoder, JSONEncoder)
Esempio n. 14
0
    def test_downgrade_api(self):
        # make a call to a not existing endpoint, downgrades
        # the current API to a stable one
        tracer = get_dummy_tracer()
        tracer.trace('client.testing').finish()
        trace = tracer.writer.pop()

        # the encoder is right but we're targeting an API
        # endpoint that is not available
        api = API('localhost', 8126)
        api._traces = '/v0.0/traces'
        ok_(isinstance(api._encoder, MsgpackEncoder))

        # after the call, we downgrade to a working endpoint
        response = api.send_traces([trace])
        ok_(response)
        eq_(response.status, 200)
        ok_(isinstance(api._encoder, JSONEncoder))
Esempio n. 15
0
class APITests(TestCase):
    def setUp(self):
        # DEV: Mock here instead of in tests, before we have patched `httplib.HTTPConnection`
        self.conn = mock.MagicMock(spec=httplib.HTTPConnection)
        self.api = API('localhost', 8126)

    def tearDown(self):
        del self.api
        del self.conn

    @mock.patch('logging.Logger.debug')
    def test_parse_response_json(self, log):
        tracer = get_dummy_tracer()
        tracer.debug_logging = True

        test_cases = {
            'OK': {'js': None, 'log': "please make sure trace-agent is up to date"},
            'OK\n': {'js': None, 'log': "please make sure trace-agent is up to date"},
            'error:unsupported-endpoint': {'js': None, 'log': "unable to load JSON 'error:unsupported-endpoint'"},
            42: {'js': None, 'log': "unable to load JSON '42'"},  # int as key to trigger TypeError
            '{}': {'js': {}},
            '[]': {'js': []},
            '{"rate_by_service": {"service:,env:":0.5, "service:mcnulty,env:test":0.9, "service:postgres,env:test":0.6}}': {  # noqa
                'js': {
                    'rate_by_service': {
                        'service:,env:': 0.5,
                        'service:mcnulty,env:test': 0.9,
                        'service:postgres,env:test': 0.6,
                    },
                },
            },
            ' [4,2,1] ': {'js': [4, 2, 1]},
        }

        for k, v in iteritems(test_cases):
            r = ResponseMock(k)
            js = _parse_response_json(r)
            eq_(v['js'], js)
            if 'log' in v:
                ok_(
                    1 <= len(log.call_args_list),
                    'not enough elements in call_args_list: {}'.format(
                        log.call_args_list),
                )
                print(log.call_args_list)
                args = log.call_args_list[-1][0][0]
                ok_(v['log'] in args,
                    'unable to find {} in {}'.format(v['log'], args))

    @mock.patch('ddtrace.compat.httplib.HTTPConnection')
    def test_put_connection_close(self, HTTPConnection):
        """
        When calling API._put
            we close the HTTPConnection we create
        """
        HTTPConnection.return_value = self.conn

        with warnings.catch_warnings(record=True) as w:
            self.api._put('/test', '<test data>', 1)

            self.assertEqual(
                len(w), 0, 'Test raised unexpected warnings: {0!r}'.format(w))

        self.conn.request.assert_called_once()
        self.conn.close.assert_called_once()

    @mock.patch('ddtrace.compat.httplib.HTTPConnection')
    def test_put_connection_close_exception(self, HTTPConnection):
        """
        When calling API._put raises an exception
            we close the HTTPConnection we create
        """
        HTTPConnection.return_value = self.conn
        # Ensure calling `request` raises an exception
        self.conn.request.side_effect = Exception

        with warnings.catch_warnings(record=True) as w:
            with self.assertRaises(Exception):
                self.api._put('/test', '<test data>', 1)

            self.assertEqual(
                len(w), 0, 'Test raised unexpected warnings: {0!r}'.format(w))

        self.conn.request.assert_called_once()
        self.conn.close.assert_called_once()
Esempio n. 16
0
class TestAPITransport(TestCase):
    """
    Ensures that traces are properly sent to a local agent. These are part
    of integration tests so real calls are triggered and you have to execute
    a real trace-agent to let them pass.
    """
    @mock.patch('ddtrace.internal.runtime.container.get_container_info')
    def setUp(self, get_container_info):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # Mock the container id we use for making requests
        get_container_info.return_value = CGroupInfo(
            container_id='test-container-id')

        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API('localhost', 8126, encoder=JSONEncoder())
        self.api_msgpack = API('localhost', 8126, encoder=MsgpackEncoder())

    @mock.patch('ddtrace.api.httplib.HTTPConnection')
    def test_send_presampler_headers(self, mocked_http):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # make a call and retrieve the `conn` Mock object
        self.api_msgpack.send_traces(traces)
        request_call = mocked_http.return_value.request
        assert request_call.call_count == 1

        # retrieve the headers from the mocked request call
        expected_headers = {
            'Datadog-Container-Id': 'test-container-id',  # mocked in setUp()
            'Datadog-Meta-Lang': 'python',
            'Datadog-Meta-Lang-Interpreter': PYTHON_INTERPRETER,
            'Datadog-Meta-Lang-Version': PYTHON_VERSION,
            'Datadog-Meta-Tracer-Version': ddtrace.__version__,
            'X-Datadog-Trace-Count': '1',
            'Content-Type': 'application/msgpack',
        }
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        assert len(expected_headers) == len(headers)
        for k, v in expected_headers.items():
            assert v == headers[k]

    @mock.patch('ddtrace.api.httplib.HTTPConnection')
    def test_send_presampler_headers_not_in_services(self, mocked_http):
        # register some services and send them to the trace agent
        services = [{
            'client.service': {
                'app': 'django',
                'app_type': 'web',
            },
        }]

        # make a call and retrieve the `conn` Mock object
        self.api_msgpack.send_services(services)
        request_call = mocked_http.return_value.request
        assert request_call.call_count == 0

    def _send_traces_and_check(self, traces, nresponses=1):
        # test JSON encoder
        responses = self.api_json.send_traces(traces)
        assert len(responses) == nresponses
        for response in responses:
            assert response.status == 200

        # test Msgpack encoder
        responses = self.api_msgpack.send_traces(traces)
        assert len(responses) == nresponses
        for response in responses:
            assert response.status == 200

    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        self._send_traces_and_check(traces)

    def test_send_many_traces(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        # 30k is a right number to have both json and msgpack send 2 payload :)
        traces = [trace] * 30000

        self._send_traces_and_check(traces, 2)

    def test_send_single_with_wrong_errors(self):
        # if the error field is set to True, it must be cast as int so
        # that the agent decoder handles that properly without providing
        # a decoding error
        span = self.tracer.trace('client.testing')
        span.error = True
        span.finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        self._send_traces_and_check(traces)

    def test_send_multiple_traces(self):
        # register some traces and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()
        self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()
        traces = [trace_1, trace_2]

        self._send_traces_and_check(traces)

    def test_send_single_trace_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        self._send_traces_and_check(traces)

    def test_send_multiple_traces_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()

        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()

        traces = [trace_1, trace_2]

        self._send_traces_and_check(traces)

    def test_send_single_service(self):
        # register some services and send them to the trace agent
        services = [{
            'client.service': {
                'app': 'django',
                'app_type': 'web',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        assert response is None

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        assert response is None

    def test_send_service_called_multiple_times(self):
        # register some services and send them to the trace agent
        services = [{
            'backend': {
                'app': 'django',
                'app_type': 'web',
            },
            'database': {
                'app': 'postgres',
                'app_type': 'db',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        assert response is None

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        assert response is None
Esempio n. 17
0
def test_api_str():
    api = API('localhost', 8126)
    assert str(api) == 'localhost:8126'
    api = API('localhost', 8126, '/path/to/uds')
    assert str(api) == '/path/to/uds'
Esempio n. 18
0
class TestAPITransport(TestCase):
    """
    Ensures that traces are properly sent to a local agent. These are part
    of integration tests so real calls are triggered and you have to execute
    a real trace-agent to let them pass.
    """
    def setUp(self):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API('localhost', 7777, encoder=JSONEncoder())
        self.api_msgpack = API('localhost', 7777, encoder=MsgpackEncoder())

    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_multiple_traces(self):
        # register some traces and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()
        self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()
        traces = [trace_1, trace_2]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_trace_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_multiple_traces_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()

        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()

        traces = [trace_1, trace_2]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_service(self):
        # register some services and send them to the trace agent
        services = [{
            'client.service': {
                'app': 'django',
                'app_type': 'web',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        ok_(response)
        eq_(response.status, 200)

    def test_send_service_called_multiple_times(self):
        # register some services and send them to the trace agent
        services = [{
            'backend': {
                'app': 'django',
                'app_type': 'web',
            },
            'database': {
                'app': 'postgres',
                'app_type': 'db',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        ok_(response)
        eq_(response.status, 200)
Esempio n. 19
0
class TestAPITransport(TestCase):
    """
    Ensures that traces are properly sent to a local agent. These are part
    of integration tests so real calls are triggered and you have to execute
    a real trace-agent to let them pass.
    """
    @mock.patch("ddtrace.internal.runtime.container.get_container_info")
    def setUp(self, get_container_info):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # Mock the container id we use for making requests
        get_container_info.return_value = CGroupInfo(
            container_id="test-container-id")

        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API("localhost", 8126, encoder=JSONEncoder())
        self.api_msgpack = API("localhost", 8126, encoder=MsgpackEncoder())

    @mock.patch("ddtrace.api.httplib.HTTPConnection")
    def test_send_presampler_headers(self, mocked_http):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace("client.testing").finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # make a call and retrieve the `conn` Mock object
        self.api_msgpack.send_traces(traces)
        request_call = mocked_http.return_value.request
        assert request_call.call_count == 1

        # retrieve the headers from the mocked request call
        expected_headers = {
            "Datadog-Container-Id": "test-container-id",  # mocked in setUp()
            "Datadog-Meta-Lang": "python",
            "Datadog-Meta-Lang-Interpreter": PYTHON_INTERPRETER,
            "Datadog-Meta-Lang-Version": PYTHON_VERSION,
            "Datadog-Meta-Tracer-Version": ddtrace.__version__,
            "X-Datadog-Trace-Count": "1",
            "Content-Type": "application/msgpack",
        }
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        assert expected_headers == headers

    def _send_traces_and_check(self, traces, nresponses=1):
        # test JSON encoder
        responses = self.api_json.send_traces(traces)
        assert len(responses) == nresponses
        for response in responses:
            assert isinstance(response, Exception) or response.status == 200

        # test Msgpack encoder
        responses = self.api_msgpack.send_traces(traces)
        assert len(responses) == nresponses
        for response in responses:
            assert isinstance(response, Exception) or response.status == 200

    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace("client.testing").finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        self._send_traces_and_check(traces)

    def test_send_many_traces(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace("client.testing").finish()
        trace = self.tracer.writer.pop()
        # 20k is a right number to have both json and msgpack send 2 payload :)
        traces = [trace] * 20000

        self._send_traces_and_check(traces, 2)

    def test_send_single_trace_max_payload(self):
        payload = Payload()

        # compute number of spans to create to surpass max payload
        trace = [Span(self.tracer, "child.span")]
        trace_size = len(payload.encoder.encode_trace(trace))
        num_spans = int(math.floor(payload.max_payload_size / trace_size))

        # setup logging capture
        log = logging.getLogger("ddtrace.api")
        log_handler = MockedLogHandler(level="WARNING")
        log.addHandler(log_handler)

        with self.tracer.trace("client.testing"):
            for n in range(num_spans):
                self.tracer.trace("child.span").finish()

        trace = self.tracer.writer.pop()

        self._send_traces_and_check([trace], 1)

        logged_warnings = log_handler.messages["warning"]
        assert len(logged_warnings) == 1
        assert "Trace is larger than the max payload size, dropping it" in logged_warnings[
            0]

    def test_send_multiple_trace_max_payload(self):
        payload = Payload()

        # compute number of spans to create to surpass max payload
        trace = [Span(self.tracer, "child.span")]
        trace_size = len(payload.encoder.encode_trace(trace))
        num_spans = int(
            math.floor((payload.max_payload_size - trace_size) / trace_size))

        # setup logging capture
        log = logging.getLogger("ddtrace.api")
        log_handler = MockedLogHandler(level="WARNING")
        log.addHandler(log_handler)

        self.tracer.trace("client.testing").finish()
        traces = [self.tracer.writer.pop()]

        # create a trace larger than max payload size
        with self.tracer.trace("client.testing"):
            for n in range(num_spans):
                self.tracer.trace("child.span").finish()

        traces.append(self.tracer.writer.pop())

        self._send_traces_and_check(traces, 2)

        logged_warnings = log_handler.messages["warning"]
        assert len(logged_warnings) == 1
        assert "Trace is too big to fit in a payload, dropping it" in logged_warnings[
            0]

    def test_send_single_with_wrong_errors(self):
        # if the error field is set to True, it must be cast as int so
        # that the agent decoder handles that properly without providing
        # a decoding error
        span = self.tracer.trace("client.testing")
        span.error = True
        span.finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        self._send_traces_and_check(traces)

    def test_send_multiple_traces(self):
        # register some traces and send them to the trace agent
        self.tracer.trace("client.testing").finish()
        trace_1 = self.tracer.writer.pop()
        self.tracer.trace("client.testing").finish()
        trace_2 = self.tracer.writer.pop()
        traces = [trace_1, trace_2]

        self._send_traces_and_check(traces)

    def test_send_single_trace_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace("client.testing"):
            self.tracer.trace("client.testing").finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        self._send_traces_and_check(traces)

    def test_send_multiple_traces_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace("client.testing"):
            self.tracer.trace("client.testing").finish()
        trace_1 = self.tracer.writer.pop()

        with self.tracer.trace("client.testing"):
            self.tracer.trace("client.testing").finish()
        trace_2 = self.tracer.writer.pop()

        traces = [trace_1, trace_2]

        self._send_traces_and_check(traces)
Esempio n. 20
0
def test_api_str():
    api = API("localhost", 8126, https=True)
    assert str(api) == "https://localhost:8126"
    api = API("localhost", 8126, "/path/to/uds")
    assert str(api) == "unix:///path/to/uds"
Esempio n. 21
0
class TestAPITransport(TestCase):
    """
    Ensures that traces are properly sent to a local agent. These are part
    of integration tests so real calls are triggered and you have to execute
    a real trace-agent to let them pass.
    """
    def setUp(self):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API('localhost', 8126, encoder=JSONEncoder())
        self.api_msgpack = API('localhost', 8126, encoder=MsgpackEncoder())

    @mock.patch('ddtrace.api.httplib.HTTPConnection')
    def test_send_presampler_headers(self, mocked_http):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # make a call and retrieve the `conn` Mock object
        response = self.api_msgpack.send_traces(traces)
        request_call = mocked_http.return_value.request
        eq_(request_call.call_count, 1)

        # retrieve the headers from the mocked request call
        expected_headers = {
                'Datadog-Meta-Lang': 'python',
                'Datadog-Meta-Lang-Interpreter': PYTHON_INTERPRETER,
                'Datadog-Meta-Lang-Version': PYTHON_VERSION,
                'Datadog-Meta-Tracer-Version': ddtrace.__version__,
                'X-Datadog-Trace-Count': '1',
                'Content-Type': 'application/msgpack'
        }
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        eq_(len(expected_headers), len(headers))
        for k, v in expected_headers.items():
            eq_(v, headers[k])

    @mock.patch('ddtrace.api.httplib.HTTPConnection')
    def test_send_presampler_headers_not_in_services(self, mocked_http):
        # register some services and send them to the trace agent
        services = [{
            'client.service': {
                'app': 'django',
                'app_type': 'web',
            },
        }]

        # make a call and retrieve the `conn` Mock object
        response = self.api_msgpack.send_services(services)
        request_call = mocked_http.return_value.request
        eq_(request_call.call_count, 1)

        # retrieve the headers from the mocked request call
        expected_headers = {
                'Datadog-Meta-Lang': 'python',
                'Datadog-Meta-Lang-Interpreter': PYTHON_INTERPRETER,
                'Datadog-Meta-Lang-Version': PYTHON_VERSION,
                'Datadog-Meta-Tracer-Version': ddtrace.__version__,
                'Content-Type': 'application/msgpack'
        }
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        eq_(len(expected_headers), len(headers))
        for k, v in expected_headers.items():
            eq_(v, headers[k])

        # retrieve the headers from the mocked request call
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        ok_('X-Datadog-Trace-Count' not in headers.keys())

    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_with_wrong_errors(self):
        # if the error field is set to True, it must be cast as int so
        # that the agent decoder handles that properly without providing
        # a decoding error
        span = self.tracer.trace('client.testing')
        span.error = True
        span.finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_multiple_traces(self):
        # register some traces and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()
        self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()
        traces = [trace_1, trace_2]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_trace_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_multiple_traces_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()

        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()

        traces = [trace_1, trace_2]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_service(self):
        # register some services and send them to the trace agent
        services = [{
            'client.service': {
                'app': 'django',
                'app_type': 'web',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        ok_(response)
        eq_(response.status, 200)

    def test_send_service_called_multiple_times(self):
        # register some services and send them to the trace agent
        services = [{
            'backend': {
                'app': 'django',
                'app_type': 'web',
            },
            'database': {
                'app': 'postgres',
                'app_type': 'db',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        ok_(response)
        eq_(response.status, 200)
Esempio n. 22
0
class TestAPITransport(TestCase):
    """
    Ensures that traces are properly sent to a local agent. These are part
    of integration tests so real calls are triggered and you have to execute
    a real trace-agent to let them pass.
    """
    def setUp(self):
        """
        Create a tracer without workers, while spying the ``send()`` method
        """
        # create a new API object to test the transport using synchronous calls
        self.tracer = get_dummy_tracer()
        self.api_json = API('localhost', 8126, encoder=JSONEncoder())
        self.api_msgpack = API('localhost', 8126, encoder=MsgpackEncoder())

    @mock.patch('ddtrace.api.httplib.HTTPConnection')
    def test_send_presampler_headers(self, mocked_http):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # make a call and retrieve the `conn` Mock object
        response = self.api_msgpack.send_traces(traces)
        request_call = mocked_http.return_value.request
        eq_(request_call.call_count, 1)

        # retrieve the headers from the mocked request call
        expected_headers = {
            'Datadog-Meta-Lang': 'python',
            'Datadog-Meta-Lang-Interpreter': PYTHON_INTERPRETER,
            'Datadog-Meta-Lang-Version': PYTHON_VERSION,
            'Datadog-Meta-Tracer-Version': ddtrace.__version__,
            'X-Datadog-Trace-Count': '1',
            'Content-Type': 'application/msgpack'
        }
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        eq_(len(expected_headers), len(headers))
        for k, v in expected_headers.items():
            eq_(v, headers[k])

    @mock.patch('ddtrace.api.httplib.HTTPConnection')
    def test_send_presampler_headers_not_in_services(self, mocked_http):
        # register some services and send them to the trace agent
        services = [{
            'client.service': {
                'app': 'django',
                'app_type': 'web',
            },
        }]

        # make a call and retrieve the `conn` Mock object
        response = self.api_msgpack.send_services(services)
        request_call = mocked_http.return_value.request
        eq_(request_call.call_count, 1)

        # retrieve the headers from the mocked request call
        expected_headers = {
            'Datadog-Meta-Lang': 'python',
            'Datadog-Meta-Lang-Interpreter': PYTHON_INTERPRETER,
            'Datadog-Meta-Lang-Version': PYTHON_VERSION,
            'Datadog-Meta-Tracer-Version': ddtrace.__version__,
            'Content-Type': 'application/msgpack'
        }
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        eq_(len(expected_headers), len(headers))
        for k, v in expected_headers.items():
            eq_(v, headers[k])

        # retrieve the headers from the mocked request call
        params, _ = request_call.call_args_list[0]
        headers = params[3]
        ok_('X-Datadog-Trace-Count' not in headers.keys())

    def test_send_single_trace(self):
        # register a single trace with a span and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_with_wrong_errors(self):
        # if the error field is set to True, it must be cast as int so
        # that the agent decoder handles that properly without providing
        # a decoding error
        span = self.tracer.trace('client.testing')
        span.error = True
        span.finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_multiple_traces(self):
        # register some traces and send them to the trace agent
        self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()
        self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()
        traces = [trace_1, trace_2]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_trace_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace = self.tracer.writer.pop()
        traces = [trace]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_multiple_traces_multiple_spans(self):
        # register some traces and send them to the trace agent
        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_1 = self.tracer.writer.pop()

        with self.tracer.trace('client.testing'):
            self.tracer.trace('client.testing').finish()
        trace_2 = self.tracer.writer.pop()

        traces = [trace_1, trace_2]

        # test JSON encoder
        response = self.api_json.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_traces(traces)
        ok_(response)
        eq_(response.status, 200)

    def test_send_single_service(self):
        # register some services and send them to the trace agent
        services = [{
            'client.service': {
                'app': 'django',
                'app_type': 'web',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        ok_(response)
        eq_(response.status, 200)

    def test_send_service_called_multiple_times(self):
        # register some services and send them to the trace agent
        services = [{
            'backend': {
                'app': 'django',
                'app_type': 'web',
            },
            'database': {
                'app': 'postgres',
                'app_type': 'db',
            },
        }]

        # test JSON encoder
        response = self.api_json.send_services(services)
        ok_(response)
        eq_(response.status, 200)

        # test Msgpack encoder
        response = self.api_msgpack.send_services(services)
        ok_(response)
        eq_(response.status, 200)
Esempio n. 23
0
class APITests(TestCase):
    def setUp(self):
        # DEV: Mock here instead of in tests, before we have patched `httplib.HTTPConnection`
        self.conn = mock.MagicMock(spec=httplib.HTTPConnection)
        self.api = API("localhost", 8126)

    def tearDown(self):
        del self.api
        del self.conn

    def test_typecast_port(self):
        api = API("localhost", u"8126")
        self.assertEqual(api.port, 8126)

    @mock.patch("logging.Logger.debug")
    def test_parse_response_json(self, log):
        test_cases = {
            "OK":
            dict(
                js=None,
                log=
                "Cannot parse Datadog Agent response, please make sure your Datadog Agent is up to date",
            ),
            "OK\n":
            dict(
                js=None,
                log=
                "Cannot parse Datadog Agent response, please make sure your Datadog Agent is up to date",
            ),
            "error:unsupported-endpoint":
            dict(
                js=None,
                log=
                "Unable to parse Datadog Agent JSON response: 'error:unsupported-endpoint'",
            ),
            42:
            dict(  # int as key to trigger TypeError
                js=None,
                log="Unable to parse Datadog Agent JSON response: 42",
            ),
            "{}":
            dict(js={}),
            "[]":
            dict(js=[]),
            # Priority sampling "rate_by_service" response
            ('{"rate_by_service": '
             '{"service:,env:":0.5, "service:mcnulty,env:test":0.9, "service:postgres,env:test":0.6}}'):
            dict(js=dict(rate_by_service={
                "service:,env:": 0.5,
                "service:mcnulty,env:test": 0.9,
                "service:postgres,env:test": 0.6,
            }, ), ),
            " [4,2,1] ":
            dict(js=[4, 2, 1]),
        }

        for k, v in iteritems(test_cases):
            log.reset_mock()

            r = Response.from_http_response(ResponseMock(k))
            js = r.get_json()
            assert v["js"] == js
            if "log" in v:
                log.assert_called_once()
                msg = log.call_args[0][0] % log.call_args[0][1:]
                assert re.match(v["log"], msg), msg

    @mock.patch("ddtrace.compat.httplib.HTTPConnection")
    def test_put_connection_close(self, HTTPConnection):
        """
        When calling API._put
            we close the HTTPConnection we create
        """
        HTTPConnection.return_value = self.conn

        with warnings.catch_warnings(record=True) as w:
            self.api._put("/test", "<test data>", 1)

            self.assertEqual(
                len(w), 0, "Test raised unexpected warnings: {0!r}".format(w))

        self.conn.request.assert_called_once()
        self.conn.close.assert_called_once()

    @mock.patch("ddtrace.compat.httplib.HTTPConnection")
    def test_put_connection_close_exception(self, HTTPConnection):
        """
        When calling API._put raises an exception
            we close the HTTPConnection we create
        """
        HTTPConnection.return_value = self.conn
        # Ensure calling `request` raises an exception
        self.conn.request.side_effect = Exception

        with warnings.catch_warnings(record=True) as w:
            with self.assertRaises(Exception):
                self.api._put("/test", "<test data>", 1)

            self.assertEqual(
                len(w), 0, "Test raised unexpected warnings: {0!r}".format(w))

        self.conn.request.assert_called_once()
        self.conn.close.assert_called_once()
Esempio n. 24
0
 def test_typecast_port(self):
     api = API('localhost', u'8126')
     self.assertEqual(api.port, 8126)
Esempio n. 25
0
 def setUp(self):
     # DEV: Mock here instead of in tests, before we have patched `httplib.HTTPConnection`
     self.conn = mock.MagicMock(spec=httplib.HTTPConnection)
     self.api = API('localhost', 8126)
Esempio n. 26
0
class APITests(TestCase):
    def setUp(self):
        # DEV: Mock here instead of in tests, before we have patched `httplib.HTTPConnection`
        self.conn = mock.MagicMock(spec=httplib.HTTPConnection)
        self.api = API('localhost', 8126)

    def tearDown(self):
        del self.api
        del self.conn

    def test_typecast_port(self):
        api = API('localhost', u'8126')
        self.assertEqual(api.port, 8126)

    @mock.patch('logging.Logger.debug')
    def test_parse_response_json(self, log):
        tracer = get_dummy_tracer()
        tracer.debug_logging = True

        test_cases = {
            'OK':
            dict(
                js=None,
                log=
                'Cannot parse Datadog Agent response, please make sure your Datadog Agent is up to date',
            ),
            'OK\n':
            dict(
                js=None,
                log=
                'Cannot parse Datadog Agent response, please make sure your Datadog Agent is up to date',
            ),
            'error:unsupported-endpoint':
            dict(
                js=None,
                log=
                'Unable to parse Datadog Agent JSON response: .*? \'error:unsupported-endpoint\'',
            ),
            42:
            dict(  # int as key to trigger TypeError
                js=None,
                log='Unable to parse Datadog Agent JSON response: .*? 42',
            ),
            '{}':
            dict(js={}),
            '[]':
            dict(js=[]),

            # Priority sampling "rate_by_service" response
            ('{"rate_by_service": '
             '{"service:,env:":0.5, "service:mcnulty,env:test":0.9, "service:postgres,env:test":0.6}}'):
            dict(js=dict(rate_by_service={
                'service:,env:': 0.5,
                'service:mcnulty,env:test': 0.9,
                'service:postgres,env:test': 0.6,
            }, ), ),
            ' [4,2,1] ':
            dict(js=[4, 2, 1]),
        }

        for k, v in iteritems(test_cases):
            log.reset_mock()

            r = Response.from_http_response(ResponseMock(k))
            js = r.get_json()
            assert v['js'] == js
            if 'log' in v:
                log.assert_called_once()
                msg = log.call_args[0][0] % log.call_args[0][1:]
                assert re.match(v['log'], msg), msg

    @mock.patch('ddtrace.compat.httplib.HTTPConnection')
    def test_put_connection_close(self, HTTPConnection):
        """
        When calling API._put
            we close the HTTPConnection we create
        """
        HTTPConnection.return_value = self.conn

        with warnings.catch_warnings(record=True) as w:
            self.api._put('/test', '<test data>', 1)

            self.assertEqual(
                len(w), 0, 'Test raised unexpected warnings: {0!r}'.format(w))

        self.conn.request.assert_called_once()
        self.conn.close.assert_called_once()

    @mock.patch('ddtrace.compat.httplib.HTTPConnection')
    def test_put_connection_close_exception(self, HTTPConnection):
        """
        When calling API._put raises an exception
            we close the HTTPConnection we create
        """
        HTTPConnection.return_value = self.conn
        # Ensure calling `request` raises an exception
        self.conn.request.side_effect = Exception

        with warnings.catch_warnings(record=True) as w:
            with self.assertRaises(Exception):
                self.api._put('/test', '<test data>', 1)

            self.assertEqual(
                len(w), 0, 'Test raised unexpected warnings: {0!r}'.format(w))

        self.conn.request.assert_called_once()
        self.conn.close.assert_called_once()
Esempio n. 27
0
def test_api_str():
    api = API('localhost', 8126, https=True)
    assert str(api) == 'https://localhost:8126'
    api = API('localhost', 8126, '/path/to/uds')
    assert str(api) == 'unix:///path/to/uds'
Esempio n. 28
0
 def test_typecast_port(self):
     api = API("localhost", u"8126")
     self.assertEqual(api.port, 8126)