From d4474a82f49081802dfd09a19506eab9fd027e52 Mon Sep 17 00:00:00 2001 From: polamayster Date: Wed, 19 Feb 2020 22:39:02 +0200 Subject: [PATCH 1/3] [iss-202] extension support as Bottle plugin --- README.md | 14 +- aws_xray_sdk/ext/bottle/__init__.py | 0 aws_xray_sdk/ext/bottle/plugin.py | 105 ++++++++++ tests/ext/bottle/__init__.py | 0 tests/ext/bottle/test_bottle.py | 302 ++++++++++++++++++++++++++++ tests/ext/bottle/views/index.tpl | 7 + tox.ini | 2 + 7 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 aws_xray_sdk/ext/bottle/__init__.py create mode 100644 aws_xray_sdk/ext/bottle/plugin.py create mode 100644 tests/ext/bottle/__init__.py create mode 100644 tests/ext/bottle/test_bottle.py create mode 100644 tests/ext/bottle/views/index.tpl diff --git a/README.md b/README.md index 608a843b..c857f82d 100644 --- a/README.md +++ b/README.md @@ -386,7 +386,19 @@ xray_recorder.configure(service='fallback_name', dynamic_naming='*mysite.com*') XRayMiddleware(app, xray_recorder) ``` -### Serverless Support for Flask & Django Using X-Ray +### Add Bottle plugin + +```python +from aws_xray_sdk.core import xray_recorder +from aws_xray_sdk.ext.bottle.plugin import XRayPlugin + +app = Bottle() + +xray_recorder.configure(service='fallback_name', dynamic_naming='*mysite.com*') +app.install(XRayPlugin(xray_recorder)) +``` + +### Serverless Support for Flask & Django & Bottle Using X-Ray Serverless is an application model that enables you to shift more of your operational responsibilities to AWS. As a result, you can focus only on your applications and services, instead of the infrastructure management tasks such as server provisioning, patching, operating system maintenance, and capacity provisioning. With serverless, you can deploy your web application to [AWS Lambda](https://aws.amazon.com/lambda/) and have customers interact with it through a Lambda-invoking endpoint, such as [Amazon API Gateway](https://aws.amazon.com/api-gateway/). X-Ray supports the Serverless model out of the box and requires no extra configuration. The middlewares in Lambda generate `Subsegments` instead of `Segments` when an endpoint is reached. This is because `Segments` cannot be generated inside the Lambda function, but it is generated automatically by the Lambda container. Therefore, when using the middlewares with this model, it is important to make sure that your methods only generate `Subsegments`. diff --git a/aws_xray_sdk/ext/bottle/__init__.py b/aws_xray_sdk/ext/bottle/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aws_xray_sdk/ext/bottle/plugin.py b/aws_xray_sdk/ext/bottle/plugin.py new file mode 100644 index 00000000..9f09a8ab --- /dev/null +++ b/aws_xray_sdk/ext/bottle/plugin.py @@ -0,0 +1,105 @@ +from bottle import request, response, SimpleTemplate + +from aws_xray_sdk.core.lambda_launcher import check_in_lambda, LambdaContext +from aws_xray_sdk.core.models import http +from aws_xray_sdk.core.utils import stacktrace +from aws_xray_sdk.ext.util import calculate_sampling_decision, \ + calculate_segment_name, construct_xray_header, prepare_response_header + + +class XRayPlugin(object): + name = 'xray' + api = 2 + + def __init__(self, recorder): + self._recorder = recorder + self._in_lambda_ctx = False + + if check_in_lambda() and type(self._recorder.context) == LambdaContext: + self._in_lambda_ctx = True + + _patch_render(recorder) + + def apply(self, callback, route): + def wrapper(*a, **ka): + headers = request.headers + xray_header = construct_xray_header(headers) + name = calculate_segment_name(request.urlparts[1], self._recorder) + + sampling_req = { + 'host': request.urlparts[1], + 'method': request.method, + 'path': request.path, + 'service': name, + } + sampling_decision = calculate_sampling_decision( + trace_header=xray_header, + recorder=self._recorder, + sampling_req=sampling_req, + ) + + if self._in_lambda_ctx: + segment = self._recorder.begin_subsegment(name) + else: + segment = self._recorder.begin_segment( + name=name, + traceid=xray_header.root, + parent_id=xray_header.parent, + sampling=sampling_decision, + ) + + segment.save_origin_trace_header(xray_header) + segment.put_http_meta(http.URL, request.url) + segment.put_http_meta(http.METHOD, request.method) + segment.put_http_meta(http.USER_AGENT, headers.get('User-Agent')) + + client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR') + if client_ip: + segment.put_http_meta(http.CLIENT_IP, client_ip) + segment.put_http_meta(http.X_FORWARDED_FOR, True) + else: + segment.put_http_meta(http.CLIENT_IP, request.remote_addr) + + try: + rv = callback(*a, **ka) + except Exception as resp: + segment.put_http_meta(http.STATUS, getattr(resp, 'status_code', 500)) + stack = stacktrace.get_stacktrace(limit=self._recorder._max_trace_back) + segment.add_exception(resp, stack) + if self._in_lambda_ctx: + self._recorder.end_subsegment() + else: + self._recorder.end_segment() + + raise resp + + segment.put_http_meta(http.STATUS, response.status_code) + + origin_header = segment.get_origin_trace_header() + resp_header_str = prepare_response_header(origin_header, segment) + response.set_header(http.XRAY_HEADER, resp_header_str) + + cont_len = response.headers.get('Content-Length') + if cont_len: + segment.put_http_meta(http.CONTENT_LENGTH, int(cont_len)) + + if self._in_lambda_ctx: + self._recorder.end_subsegment() + else: + self._recorder.end_segment() + + return rv + + return wrapper + +def _patch_render(recorder): + + _render = SimpleTemplate.render + + @recorder.capture('template_render') + def _traced_render(self, *args, **kwargs): + if self.filename: + recorder.current_subsegment().name = self.filename + return _render(self, *args, **kwargs) + + SimpleTemplate.render = _traced_render diff --git a/tests/ext/bottle/__init__.py b/tests/ext/bottle/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/ext/bottle/test_bottle.py b/tests/ext/bottle/test_bottle.py new file mode 100644 index 00000000..60ff54f5 --- /dev/null +++ b/tests/ext/bottle/test_bottle.py @@ -0,0 +1,302 @@ +import pytest +from bottle import Bottle, request, response, template, view, HTTPError, TEMPLATE_PATH +from webtest import TestApp as WebApp + +from aws_xray_sdk import global_sdk_config +from aws_xray_sdk.ext.bottle.plugin import XRayPlugin +from aws_xray_sdk.core.context import Context +from aws_xray_sdk.core import lambda_launcher +from aws_xray_sdk.core.models import http, facade_segment, segment as segment_model +from tests.util import get_new_stubbed_recorder +import os + + +# define Bottle app for testing purpose +TEMPLATE_PATH.insert(0, os.path.dirname(__file__) + '/views') +app = Bottle() + +@app.route('/ok') +def ok(): + response_data = 'ok' + # Bottle not always set Content-Length header + response.content_length = len(response_data) + return response_data + + +@app.route('/error') +def error(): + response.status = 404 + return 'Not Found' + + +@app.route('/client_error') +def faulty_client(): + class CustomError(Exception): + def __init__(self, description=None, status_code=None): + self.description = description + self.status_code = status_code + + raise CustomError(description='Bad request', status_code=400) + + +@app.route('/server_error') +def faulty_server(): + raise HTTPError(status=503, body='Service Unavailable') + + +@app.route('/fault') +def fault(): + return {}['key'] + + +@app.route('/template') +def template_(): + return template('Hello {{name}}!', name='World') + + +@app.route('/view') +@view('index') +def view_(name='World'): + return dict(name=name) + + +# add X-Ray plugin to Bottle app +recorder = get_new_stubbed_recorder() +recorder.configure(service='test', sampling=False, context=Context()) +app.install(XRayPlugin(recorder)) + +app = WebApp(app) + +BASE_URL = 'http://localhost:80{}' + + +@pytest.fixture(autouse=True) +def cleanup(): + """ + Clean up context storage before and after each test run + """ + recorder.clear_trace_entities() + yield + recorder.clear_trace_entities() + global_sdk_config.set_sdk_enabled(True) + + +def test_ok(): + path = '/ok' + app.get(path, extra_environ={'REMOTE_ADDR': '127.0.0.1'}) + segment = recorder.emitter.pop() + assert not segment.in_progress + + request = segment.http['request'] + response = segment.http['response'] + + assert request['method'] == 'GET' + assert request['url'] == BASE_URL.format(path) + assert request['client_ip'] == '127.0.0.1' + assert response['status'] == 200 + assert response['content_length'] == 2 + + +def test_error(): + path = '/error' + try: + app.get(path, extra_environ={'HTTP_X_FORWARDED_FOR': '192.168.0.0'}) + except Exception: + pass + segment = recorder.emitter.pop() + assert not segment.in_progress + assert segment.error + + request = segment.http['request'] + response = segment.http['response'] + assert request['method'] == 'GET' + assert request['url'] == BASE_URL.format(path) + assert request['client_ip'] == '192.168.0.0' + assert response['status'] == 404 + + +def test_custom_client_error(): + path = '/client_error' + try: + app.get(path) + except Exception: + pass + segment = recorder.emitter.pop() + assert not segment.in_progress + assert segment.error + + response = segment.http['response'] + assert response['status'] == 400 + exception = segment.cause['exceptions'][0] + assert exception.type == 'CustomError' + + request = segment.http['request'] + assert request['method'] == 'GET' + assert request['url'] == BASE_URL.format(path) + + +def test_server_error(): + path = '/server_error' + try: + app.get(path) + except Exception as e: + pass + segment = recorder.emitter.pop() + assert not segment.in_progress + assert segment.fault + + response = segment.http['response'] + assert response['status'] == 503 + + exception = segment.cause['exceptions'][0] + assert exception.type == 'HTTPError' + + +def test_fault(): + path = '/fault' + try: + app.get(path) + except Exception: + pass + segment = recorder.emitter.pop() + assert not segment.in_progress + assert segment.fault + + response = segment.http['response'] + assert response['status'] == 500 + + exception = segment.cause['exceptions'][0] + assert exception.type == 'KeyError' + + +def test_render_template(): + path = '/template' + app.get(path) + segment = recorder.emitter.pop() + assert not segment.in_progress + # segment should contain a template render subsegment + assert segment.subsegments + + subsegment = segment.subsegments[0] + assert subsegment.name + assert subsegment.namespace == 'local' + assert not subsegment.in_progress + + +def test_render_view(): + path = '/view' + app.get(path) + segment = recorder.emitter.pop() + assert not segment.in_progress + # segment should contain a template render subsegment + assert segment.subsegments + + subsegment = segment.subsegments[0] + assert subsegment.name + assert subsegment.namespace == 'local' + assert not subsegment.in_progress + + +def test_incoming_sampling_decision_respected(): + path = '/ok' + # resp = app.get(path, headers={http.XRAY_HEADER: 'Sampled=0'}) + resp = app.get(path, headers={http.XRAY_HEADER: 'Sampled=0'}) + resp_header = resp.headers[http.XRAY_HEADER] + segment = recorder.emitter.pop() + + assert not segment + # The SDK should still send the headers back regardless of sampling decision + assert 'Root' in resp_header + + +def test_trace_header_data_perservation(): + path = '/ok' + app.get(path, headers={http.XRAY_HEADER: 'k1=v1'}) + segment = recorder.emitter.pop() + header = segment.get_origin_trace_header() + + assert header.data['k1'] == 'v1' + + +def test_sampled_response_header(): + path = '/ok' + app.get(path, headers={http.XRAY_HEADER: 'Sampled=?;k1=v1'}) + segment = recorder.emitter.pop() + + resp_header = response.headers.get(http.XRAY_HEADER) + assert segment.trace_id in resp_header + assert 'Sampled=1' in resp_header + + +def test_disabled_sdk(): + global_sdk_config.set_sdk_enabled(False) + path = '/ok' + app.get(path) + segment = recorder.emitter.pop() + assert not segment + + +def test_lambda_serverless(): + TRACE_ID = '1-5759e988-bd862e3fe1be46a994272793' + PARENT_ID = '53995c3f42cd8ad8' + HEADER_VAR = 'Root=%s;Parent=%s;Sampled=1' % (TRACE_ID, PARENT_ID) + + os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY] = HEADER_VAR + lambda_context = lambda_launcher.LambdaContext() + + new_recorder = get_new_stubbed_recorder() + new_recorder.configure(service='test', sampling=False, context=lambda_context) + new_app = Bottle() + + @new_app.route('/subsegment') + def subsegment_(): + # Test in between request and make sure Serverless creates a subsegment instead of a segment. + # Ensure that the parent segment is a facade segment. + assert new_recorder.current_subsegment() + assert type(new_recorder.current_segment()) == facade_segment.FacadeSegment + return 'ok' + + @new_app.route('/trace_header') + def trace_header(): + # Ensure trace header is preserved. + subsegment = new_recorder.current_subsegment() + header = subsegment.get_origin_trace_header() + assert header.data['k1'] == 'v1' + return 'ok' + + plugin = XRayPlugin(new_recorder) + plugin._in_lambda_ctx = True + new_app.install(plugin) + + app_client = WebApp(new_app) + + path = '/subsegment' + app_client.get(path) + new_app.get(path) + segment = recorder.emitter.pop() + assert not segment # Segment should be none because it's created and ended by the plugin + + path2 = '/trace_header' + app_client.get(path2, headers={http.XRAY_HEADER: 'k1=v1'}) + + +def test_lambda_default_ctx(): + # Track to make sure that Bottle will default to generating segments if context is not the lambda context + new_recorder = get_new_stubbed_recorder() + new_recorder.configure(service='test', sampling=False) + new_app = Bottle() + + @new_app.route('/segment') + def segment_(): + # Test in between request and make sure Lambda that uses default context generates a segment. + assert new_recorder.current_segment() + assert type(new_recorder.current_segment()) == segment_model.Segment + return 'ok' + + new_app.install(XRayPlugin(new_recorder)) + app_client = WebApp(new_app) + + path = '/segment' + app_client.get(path) + segment = recorder.emitter.pop() + assert not segment # Segment should be none because it's created and ended by the plugin diff --git a/tests/ext/bottle/views/index.tpl b/tests/ext/bottle/views/index.tpl new file mode 100644 index 00000000..46bbf4f8 --- /dev/null +++ b/tests/ext/bottle/views/index.tpl @@ -0,0 +1,7 @@ +%if name == 'World': +

Hello {{name}}!

+

This is a test.

+%else: +

Hello {{name.title()}}!

+

How are you?

+%end \ No newline at end of file diff --git a/tox.ini b/tox.ini index 39a5c678..b467d9c7 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,7 @@ deps = coverage==4.5.4 codecov requests + bottle >= 0.12.13 flask >= 0.10 sqlalchemy Flask-SQLAlchemy @@ -24,6 +25,7 @@ deps = psycopg2 pg8000 testing.postgresql + webtest # Python2 only deps py{27}: enum34 From 11b86870c60ffb42009230ef756c4b1f20f0312a Mon Sep 17 00:00:00 2001 From: polamayster Date: Wed, 4 Mar 2020 00:04:05 +0200 Subject: [PATCH 2/3] [iss-202] extension support as Bottle plugin --- README.md | 6 +- .../ext/bottle/{plugin.py => middleware.py} | 216 +++++++++--------- tests/ext/bottle/test_bottle.py | 14 +- tests/ext/bottle/views/index.tpl | 9 +- tox.ini | 2 +- 5 files changed, 125 insertions(+), 122 deletions(-) rename aws_xray_sdk/ext/bottle/{plugin.py => middleware.py} (95%) diff --git a/README.md b/README.md index c857f82d..4fa33d71 100644 --- a/README.md +++ b/README.md @@ -386,16 +386,16 @@ xray_recorder.configure(service='fallback_name', dynamic_naming='*mysite.com*') XRayMiddleware(app, xray_recorder) ``` -### Add Bottle plugin +### Add Bottle middleware(plugin) ```python from aws_xray_sdk.core import xray_recorder -from aws_xray_sdk.ext.bottle.plugin import XRayPlugin +from aws_xray_sdk.ext.bottle.middleware import XRayMiddleware app = Bottle() xray_recorder.configure(service='fallback_name', dynamic_naming='*mysite.com*') -app.install(XRayPlugin(xray_recorder)) +app.install(XRayMiddleware(xray_recorder)) ``` ### Serverless Support for Flask & Django & Bottle Using X-Ray diff --git a/aws_xray_sdk/ext/bottle/plugin.py b/aws_xray_sdk/ext/bottle/middleware.py similarity index 95% rename from aws_xray_sdk/ext/bottle/plugin.py rename to aws_xray_sdk/ext/bottle/middleware.py index 9f09a8ab..f51d9ca0 100644 --- a/aws_xray_sdk/ext/bottle/plugin.py +++ b/aws_xray_sdk/ext/bottle/middleware.py @@ -1,105 +1,111 @@ -from bottle import request, response, SimpleTemplate - -from aws_xray_sdk.core.lambda_launcher import check_in_lambda, LambdaContext -from aws_xray_sdk.core.models import http -from aws_xray_sdk.core.utils import stacktrace -from aws_xray_sdk.ext.util import calculate_sampling_decision, \ - calculate_segment_name, construct_xray_header, prepare_response_header - - -class XRayPlugin(object): - name = 'xray' - api = 2 - - def __init__(self, recorder): - self._recorder = recorder - self._in_lambda_ctx = False - - if check_in_lambda() and type(self._recorder.context) == LambdaContext: - self._in_lambda_ctx = True - - _patch_render(recorder) - - def apply(self, callback, route): - def wrapper(*a, **ka): - headers = request.headers - xray_header = construct_xray_header(headers) - name = calculate_segment_name(request.urlparts[1], self._recorder) - - sampling_req = { - 'host': request.urlparts[1], - 'method': request.method, - 'path': request.path, - 'service': name, - } - sampling_decision = calculate_sampling_decision( - trace_header=xray_header, - recorder=self._recorder, - sampling_req=sampling_req, - ) - - if self._in_lambda_ctx: - segment = self._recorder.begin_subsegment(name) - else: - segment = self._recorder.begin_segment( - name=name, - traceid=xray_header.root, - parent_id=xray_header.parent, - sampling=sampling_decision, - ) - - segment.save_origin_trace_header(xray_header) - segment.put_http_meta(http.URL, request.url) - segment.put_http_meta(http.METHOD, request.method) - segment.put_http_meta(http.USER_AGENT, headers.get('User-Agent')) - - client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR') - if client_ip: - segment.put_http_meta(http.CLIENT_IP, client_ip) - segment.put_http_meta(http.X_FORWARDED_FOR, True) - else: - segment.put_http_meta(http.CLIENT_IP, request.remote_addr) - - try: - rv = callback(*a, **ka) - except Exception as resp: - segment.put_http_meta(http.STATUS, getattr(resp, 'status_code', 500)) - stack = stacktrace.get_stacktrace(limit=self._recorder._max_trace_back) - segment.add_exception(resp, stack) - if self._in_lambda_ctx: - self._recorder.end_subsegment() - else: - self._recorder.end_segment() - - raise resp - - segment.put_http_meta(http.STATUS, response.status_code) - - origin_header = segment.get_origin_trace_header() - resp_header_str = prepare_response_header(origin_header, segment) - response.set_header(http.XRAY_HEADER, resp_header_str) - - cont_len = response.headers.get('Content-Length') - if cont_len: - segment.put_http_meta(http.CONTENT_LENGTH, int(cont_len)) - - if self._in_lambda_ctx: - self._recorder.end_subsegment() - else: - self._recorder.end_segment() - - return rv - - return wrapper - -def _patch_render(recorder): - - _render = SimpleTemplate.render - - @recorder.capture('template_render') - def _traced_render(self, *args, **kwargs): - if self.filename: - recorder.current_subsegment().name = self.filename - return _render(self, *args, **kwargs) - - SimpleTemplate.render = _traced_render +from bottle import request, response, SimpleTemplate + +from aws_xray_sdk.core.lambda_launcher import check_in_lambda, LambdaContext +from aws_xray_sdk.core.models import http +from aws_xray_sdk.core.utils import stacktrace +from aws_xray_sdk.ext.util import calculate_sampling_decision, \ + calculate_segment_name, construct_xray_header, prepare_response_header + + +class XRayMiddleware(object): + """ + Middleware that wraps each incoming request to a segment. + """ + name = 'xray' + api = 2 + + def __init__(self, recorder): + self._recorder = recorder + self._in_lambda_ctx = False + + if check_in_lambda() and type(self._recorder.context) == LambdaContext: + self._in_lambda_ctx = True + + _patch_render(recorder) + + def apply(self, callback, route): + """ + Apply middleware directly to each route callback. + """ + def wrapper(*a, **ka): + headers = request.headers + xray_header = construct_xray_header(headers) + name = calculate_segment_name(request.urlparts[1], self._recorder) + + sampling_req = { + 'host': request.urlparts[1], + 'method': request.method, + 'path': request.path, + 'service': name, + } + sampling_decision = calculate_sampling_decision( + trace_header=xray_header, + recorder=self._recorder, + sampling_req=sampling_req, + ) + + if self._in_lambda_ctx: + segment = self._recorder.begin_subsegment(name) + else: + segment = self._recorder.begin_segment( + name=name, + traceid=xray_header.root, + parent_id=xray_header.parent, + sampling=sampling_decision, + ) + + segment.save_origin_trace_header(xray_header) + segment.put_http_meta(http.URL, request.url) + segment.put_http_meta(http.METHOD, request.method) + segment.put_http_meta(http.USER_AGENT, headers.get('User-Agent')) + + client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR') + if client_ip: + segment.put_http_meta(http.CLIENT_IP, client_ip) + segment.put_http_meta(http.X_FORWARDED_FOR, True) + else: + segment.put_http_meta(http.CLIENT_IP, request.remote_addr) + + try: + rv = callback(*a, **ka) + except Exception as resp: + segment.put_http_meta(http.STATUS, getattr(resp, 'status_code', 500)) + stack = stacktrace.get_stacktrace(limit=self._recorder._max_trace_back) + segment.add_exception(resp, stack) + if self._in_lambda_ctx: + self._recorder.end_subsegment() + else: + self._recorder.end_segment() + + raise resp + + segment.put_http_meta(http.STATUS, response.status_code) + + origin_header = segment.get_origin_trace_header() + resp_header_str = prepare_response_header(origin_header, segment) + response.set_header(http.XRAY_HEADER, resp_header_str) + + cont_len = response.headers.get('Content-Length') + if cont_len: + segment.put_http_meta(http.CONTENT_LENGTH, int(cont_len)) + + if self._in_lambda_ctx: + self._recorder.end_subsegment() + else: + self._recorder.end_segment() + + return rv + + return wrapper + +def _patch_render(recorder): + + _render = SimpleTemplate.render + + @recorder.capture('template_render') + def _traced_render(self, *args, **kwargs): + if self.filename: + recorder.current_subsegment().name = self.filename + return _render(self, *args, **kwargs) + + SimpleTemplate.render = _traced_render diff --git a/tests/ext/bottle/test_bottle.py b/tests/ext/bottle/test_bottle.py index 60ff54f5..e5be6f75 100644 --- a/tests/ext/bottle/test_bottle.py +++ b/tests/ext/bottle/test_bottle.py @@ -3,7 +3,7 @@ from webtest import TestApp as WebApp from aws_xray_sdk import global_sdk_config -from aws_xray_sdk.ext.bottle.plugin import XRayPlugin +from aws_xray_sdk.ext.bottle.middleware import XRayMiddleware from aws_xray_sdk.core.context import Context from aws_xray_sdk.core import lambda_launcher from aws_xray_sdk.core.models import http, facade_segment, segment as segment_model @@ -15,6 +15,7 @@ TEMPLATE_PATH.insert(0, os.path.dirname(__file__) + '/views') app = Bottle() + @app.route('/ok') def ok(): response_data = 'ok' @@ -56,14 +57,14 @@ def template_(): @app.route('/view') @view('index') -def view_(name='World'): +def view_(name='bottle'): return dict(name=name) # add X-Ray plugin to Bottle app recorder = get_new_stubbed_recorder() recorder.configure(service='test', sampling=False, context=Context()) -app.install(XRayPlugin(recorder)) +app.install(XRayMiddleware(recorder)) app = WebApp(app) @@ -185,7 +186,8 @@ def test_render_template(): def test_render_view(): path = '/view' - app.get(path) + response = app.get(path) + assert response.text == "

Hello Bottle!

\r\n

How are you?

\r\n" segment = recorder.emitter.pop() assert not segment.in_progress # segment should contain a template render subsegment @@ -264,7 +266,7 @@ def trace_header(): assert header.data['k1'] == 'v1' return 'ok' - plugin = XRayPlugin(new_recorder) + plugin = XRayMiddleware(new_recorder) plugin._in_lambda_ctx = True new_app.install(plugin) @@ -293,7 +295,7 @@ def segment_(): assert type(new_recorder.current_segment()) == segment_model.Segment return 'ok' - new_app.install(XRayPlugin(new_recorder)) + new_app.install(XRayMiddleware(new_recorder)) app_client = WebApp(new_app) path = '/segment' diff --git a/tests/ext/bottle/views/index.tpl b/tests/ext/bottle/views/index.tpl index 46bbf4f8..a5a5c278 100644 --- a/tests/ext/bottle/views/index.tpl +++ b/tests/ext/bottle/views/index.tpl @@ -1,7 +1,2 @@ -%if name == 'World': -

Hello {{name}}!

-

This is a test.

-%else: -

Hello {{name.title()}}!

-

How are you?

-%end \ No newline at end of file +

Hello {{name.title()}}!

+

How are you?

diff --git a/tox.ini b/tox.ini index b467d9c7..d829182d 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ deps = coverage==4.5.4 codecov requests - bottle >= 0.12.13 + bottle >= 0.10 flask >= 0.10 sqlalchemy Flask-SQLAlchemy From ffbc93e06d10040d9fa09ff20f8991aa7e7080d5 Mon Sep 17 00:00:00 2001 From: polamayster Date: Wed, 4 Mar 2020 00:10:57 +0200 Subject: [PATCH 3/3] [iss-202] extension support as Bottle plugin --- tests/ext/bottle/test_bottle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ext/bottle/test_bottle.py b/tests/ext/bottle/test_bottle.py index e5be6f75..81e57970 100644 --- a/tests/ext/bottle/test_bottle.py +++ b/tests/ext/bottle/test_bottle.py @@ -187,7 +187,7 @@ def test_render_template(): def test_render_view(): path = '/view' response = app.get(path) - assert response.text == "

Hello Bottle!

\r\n

How are you?

\r\n" + assert response.text == "

Hello Bottle!

\n

How are you?

\n" segment = recorder.emitter.pop() assert not segment.in_progress # segment should contain a template render subsegment