Exemplo n.º 1
0
def test_listen_different_ports():
    server = TChannel(name='test_server')
    server.listen()
    port = int(server.hostport.rsplit(":")[1])
    with pytest.raises(AlreadyListeningError):
        server.listen(port + 1)
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from __future__ import absolute_import

from tornado import gen, ioloop
from tchannel import TChannel, Response, thrift

tchannel = TChannel('thrift-server', hostport='localhost:54497')
service = thrift.load('tests/data/idls/ThriftTest.thrift')


@tchannel.thrift.register(service.ThriftTest)
@gen.coroutine
def testString(request):

    assert request.headers == {'req': 'header'}
    assert request.body.thing == 'req'

    return Response('resp', headers={'resp': 'header'})


tchannel.listen()
Exemplo n.º 3
0
def test_get_rank_no_connection():
    server = TChannel('server')
    server.listen()
    peer = Peer(TChannel('test'), '10.10.101.21:230')
    calculator = PreferIncomingCalculator()
    assert sys.maxint == calculator.get_rank(peer)
Exemplo n.º 4
0
def test_trace_propagation(
        endpoint, transport, encoding, enabled, expect_spans, expect_baggage,
        http_patchers, tracer, mock_server, thrift_service,
        app, http_server, base_url, http_client):
    """
    Main TChannel-OpenTracing integration test, using basictracer as
    implementation of OpenTracing API.

    The main logic of this test is as follows:
      1. Start a new trace with a root span
      2. Store a random value in the baggage
      3. Call the first service at the endpoint from `endpoint` parameter.
         The first service is either tchannel or http, depending on the value
         if `transport` parameter.
      4. The first service calls the second service using pre-defined logic
         that depends on the endpoint invoked on the first service.
      5. The second service accesses the tracing span and returns the value
         of the baggage item as the response.
      6. The first service responds with the value from the second service.
      7. The main test validates that the response is equal to the original
         random value of the baggage, proving trace & baggage propagation.
      8. The test also validates that all spans have been finished and
         recorded, and that they all have the same trace ID.

    We expect 5 spans to be created from each test run:
      *  top-level (root) span started in the test
      *  client span (calling service-1)
      *  service-1 server span
      *  service-1 client span (calling service-2)
      *  service-2 server span

    :param endpoint: name of the endpoint to call on the first service
    :param transport: type of the first service: tchannel or http
    :param enabled: if False, channels are instructed to disable tracing
    :param expect_spans: number of spans we expect to be generated
    :param http_patchers: monkey-patching of tornado AsyncHTTPClient
    :param tracer: a concrete implementation of OpenTracing Tracer
    :param mock_server: tchannel server (from conftest.py)
    :param thrift_service: fixture that creates a Thrift service from fake IDL
    :param app: tornado.web.Application fixture
    :param http_server: http server (provided by pytest-tornado)
    :param base_url: address of http server (provided by pytest-tornado)
    :param http_client: Tornado's AsyncHTTPClient (provided by pytest-tornado)
    """
    # mock_server is created as a fixture, so we need to set tracer on it
    mock_server.tchannel._dep_tchannel._tracer = tracer
    mock_server.tchannel._dep_tchannel._trace = enabled

    register(tchannel=mock_server.tchannel, thrift_service=thrift_service,
             http_client=http_client, base_url=base_url)

    tchannel = TChannel(name='test', tracer=tracer, trace=enabled)

    app.add_handlers(".*$", [
        (r"/", HttpHandler, {'client_channel': tchannel})
    ])

    with mock.patch('opentracing.tracer', tracer),\
            mock.patch.object(tracing.log, 'exception') as log_exception:
        assert opentracing.tracer == tracer  # sanity check that patch worked

        span = tracer.start_span('root')
        baggage = 'from handler3 %d' % time.time()
        span.set_baggage_item(BAGGAGE_KEY, baggage)
        if not enabled:
            span.set_tag('sampling.priority', 0)
        with span:  # use span as context manager so that it's always finished
            response_future = None
            with tchannel.context_provider.span_in_context(span):
                if transport == 'tchannel':
                    if encoding == 'json':
                        response_future = tchannel.json(
                            service='test-client',
                            endpoint=endpoint,
                            hostport=mock_server.hostport,
                            body=mock_server.hostport,
                        )
                    elif encoding == 'thrift':
                        if endpoint == 'thrift1':
                            response_future = tchannel.thrift(
                                thrift_service.X.thrift1(mock_server.hostport),
                                hostport=mock_server.hostport,
                            )
                        elif endpoint == 'thrift3':
                            response_future = tchannel.thrift(
                                thrift_service.X.thrift3(mock_server.hostport),
                                hostport=mock_server.hostport,
                            )
                        elif endpoint == 'thrift4':
                            response_future = tchannel.thrift(
                                thrift_service.X.thrift4(mock_server.hostport),
                                hostport=mock_server.hostport,
                            )
                        else:
                            raise ValueError('wrong endpoint %s' % endpoint)
                    else:
                        raise ValueError('wrong encoding %s' % encoding)
                elif transport == 'http':
                    response_future = http_client.fetch(
                        request=HTTPRequest(
                            url='%s%s' % (base_url, endpoint),
                            method='POST',
                            body=mock_server.hostport,
                        )
                    )
                else:
                    raise NotImplementedError(
                        'unknown transport %s' % transport)
            response = yield response_future
        assert log_exception.call_count == 0

    body = response.body
    if expect_baggage:
        assert body == baggage

    def get_sampled_spans():
        return [s for s in tracer.reporter.get_spans() if s.is_sampled]

    # Sometimes the test runs into weird race condition where the
    # after_send_response() hook is executed, but the span is not yet
    # recorded. To prevent flaky test runs we check and wait until
    # all spans are recorded, for up to 1 second.
    for i in range(0, 1000):
        spans = get_sampled_spans()
        if len(spans) >= expect_spans:
            break
        yield tornado.gen.sleep(0.001)  # yield execution and sleep for 1ms

    spans = get_sampled_spans()
    assert expect_spans == len(spans), 'Unexpected number of spans reported'
    # We expect all trace IDs in collected spans to be the same
    if expect_spans > 0:
        spans = tracer.reporter.get_spans()
        assert 1 == len(set([s.trace_id for s in spans])), \
            'all spans must have the same trace_id'
Exemplo n.º 5
0
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from tornado import ioloop
from tchannel import TChannel, thrift

tchannel = TChannel('keyvalue-server', hostport='localhost:12345')
service = thrift.load('examples/guide/keyvalue/service.thrift')

values = {'hello': 'world'}


@tchannel.thrift.register(service.KeyValue)
def getValue(request):
    key = request.body.key
    value = values.get(key)

    if value is None:
        raise service.NotFoundError(key)

    return value
Exemplo n.º 6
0
def test_listen_different_ports():
    server = TChannel(name='test_server')
    server.listen()
    with pytest.raises(AlreadyListeningError):
        server.listen(server.port + 1)
Exemplo n.º 7
0
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from __future__ import absolute_import
from __future__ import print_function
import json

from tornado import gen
from tornado import ioloop

from tchannel import TChannel, thrift

tchannel = TChannel('thrift-client')
service = thrift.load(
    path='tests/data/idls/ThriftTest.thrift',
    service='thrift-server',
    hostport='localhost:54497',
)


@gen.coroutine
def make_request():

    resp = yield tchannel.thrift(
        request=service.ThriftTest.testString(thing="req"),
        headers={
            'req': 'header',
        },
Exemplo n.º 8
0
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from tornado import gen, ioloop
from tchannel import TChannel, thrift

tchannel = TChannel('keyvalue-consumer')
service = thrift.load(
    path='examples/guide/keyvalue/service.thrift',
    service='keyvalue-server',
    hostport='localhost:8889',
)


@gen.coroutine
def run():

    yield tchannel.thrift(
        service.KeyValue.setValue("foo", "Hello, world!"),
    )

    response = yield tchannel.thrift(
Exemplo n.º 9
0
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from tornado import gen, ioloop

from tchannel import TChannel


tchannel = TChannel('raw-client')


@gen.coroutine
def make_request():

    resp = yield tchannel.raw(
        service='raw-server',
        endpoint='endpoint',
        body='req body',
        headers='req headers',
        hostport='localhost:54495',
    )

    raise gen.Return(resp)
Exemplo n.º 10
0
def test_span_tags(encoding, operation, tracer, thrift_service):
    server = TChannel('server', tracer=tracer)
    server.listen()

    def get_span_baggage():
        sp = server.context_provider.get_current_span()
        baggage = sp.get_baggage_item('bender') if sp else None
        return {'bender': baggage}

    @server.json.register('foo')
    def handler(_):
        return get_span_baggage()

    @server.thrift.register(thrift_service.X, method='thrift2')
    def thrift2(_):
        return json.dumps(get_span_baggage())

    client = TChannel('client', tracer=tracer, trace=True)

    span = tracer.start_span('root')
    span.set_baggage_item('bender', 'is great')
    with span:
        res = None
        with client.context_provider.span_in_context(span):
            if encoding == 'json':
                res = client.json(
                    service='test-service',  # match thrift_service name
                    endpoint='foo',
                    body={},
                    hostport=server.hostport,
                )
            elif encoding == 'thrift':
                res = client.thrift(
                    thrift_service.X.thrift2(),
                    hostport=server.hostport,
                )
            else:
                raise ValueError('Unknown encoding %s' % encoding)
        res = yield res  # cannot yield in StackContext
    res = res.body
    if isinstance(res, basestring):
        res = json.loads(res)
    assert res == {'bender': 'is great'}
    for i in range(1000):
        spans = tracer.reporter.get_spans()
        if len(spans) == 3:
            break
        yield tornado.gen.sleep(0.001)  # yield execution and sleep for 1ms
    spans = tracer.reporter.get_spans()
    assert len(spans) == 3
    assert 1 == len(set([s.trace_id for s in spans])), \
        'all spans must have the same trace_id'
    parent = child = None
    for s in spans:
        if s.tags is None:
            continue
        print('tags %s' % s.tags)
        # replace list with dictionary
        s.tags = {tag.key: tag.value for tag in s.tags}
        if s.kind == tags.SPAN_KIND_RPC_SERVER:
            child = s
        elif s.kind == tags.SPAN_KIND_RPC_CLIENT:
            parent = s
    assert parent is not None
    assert child is not None
    assert parent.operation_name == operation
    assert child.operation_name == operation
    assert parent.peer['service_name'] == 'test-service'
    assert child.peer['service_name'] == 'client'
    assert parent.peer['ipv4'] is not None
    assert child.peer['ipv4'] is not None
    assert parent.tags.get('as') == encoding
    assert child.tags.get('as') == encoding
Exemplo n.º 11
0
#!/usr/bin/env python

import sys

import tornado
from tchannel import TChannel

tchannel = TChannel('echo-server', hostport='127.0.0.1:0')


def close_callback(_data):
    stdin.close()
    tornado.ioloop.IOLoop.current().stop()
    sys.exit(1)


@tchannel.json.register('/echo')
def handler(request):
    return request.body


@tchannel.json.register('/exit')
def handler(request):
    tornado.ioloop.IOLoop.current().close()
    sys.exit()


if __name__ == '__main__':
    tchannel.listen()
    stdin = tornado.iostream.PipeIOStream(sys.stdin.fileno())
    stdin.read_until_close(callback=close_callback)
Exemplo n.º 12
0
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import os
import sys
import threading

from tornado import gen, ioloop
from tchannel import TChannel
from tchannel import thrift_request_builder
from service import KeyValue

tchannel = TChannel('thrift-benchmark-client')

kv = thrift_request_builder(service='thrift-benchmark',
                            thrift_module=KeyValue,
                            hostport='localhost:12345')

local = threading.local()
local.requests = 0


def report_work():
    print local.requests
    sys.stdout.flush()
    local.requests = 0

Exemplo n.º 13
0
def test_service_name_is_required(name, io_loop):
    with pytest.raises(AssertionError) as exc_info:
        TChannel(name)

    assert 'service name cannot be empty or None' in str(exc_info)
Exemplo n.º 14
0
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from __future__ import absolute_import
from __future__ import print_function
import json

from tornado import gen, ioloop

from tchannel import TChannel

tchannel = TChannel('json-client')


@gen.coroutine
def make_request():

    resp = yield tchannel.json(
        service='json-server',
        endpoint='endpoint',
        body={'req': 'body'},
        headers={'req': 'header'},
        hostport='localhost:54496',
    )

    raise gen.Return(resp)
Exemplo n.º 15
0
def peer():
    return Peer(
        tchannel=TChannel('peer'),
        hostport='127.0.0.1:21300',
    )
def server(io_loop):  # need io_loop fixture for listen() to work
    server = TChannel(name='server')
    server.listen()
    return server
Exemplo n.º 17
0
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from tornado import ioloop
from tchannel import TChannel, thrift

tchannel = TChannel('keyvalue-service', hostport='localhost:8889')
service = thrift.load('examples/guide/keyvalue/service.thrift')

values = {'hello': 'world'}


@tchannel.thrift.register(service.KeyValue)
def getValue(request):
    key = request.body.key
    value = values.get(key)

    if value is None:
        raise service.NotFoundError(key)

    return value
Exemplo n.º 18
0
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from __future__ import absolute_import

from tornado import ioloop
from tchannel import TChannel
from service import KeyValue

app = TChannel('thrift-benchmark', hostport='localhost:12345')

values = {'hello': 'world'}


@app.thrift.register(KeyValue)
def getValue(request):
    key = request.body.key
    value = values.get(key)

    if value is None:
        raise KeyValue.NotFoundError(key)

    return value

Exemplo n.º 19
0
def test_listen_duplicate_ports():
    server = TChannel(name='test_server')
    server.listen()
    server.listen()
    server.listen(server.port)
    server.listen()
Exemplo n.º 20
0
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from tornado import ioloop

from tchannel import TChannel, Response

tchannel = TChannel('json-server', hostport='localhost:54496')


@tchannel.json.register
def endpoint(request):

    assert request.headers == {'req': 'header'}
    assert request.body == {'req': 'body'}

    return Response({'resp': 'body'}, headers={'resp': 'header'})


tchannel.listen()

print tchannel.hostport