You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2020/06/05 12:57:58 UTC
[skywalking-python] 01/01: Test: set up plugin tests and add http
plugin test
This is an automated email from the ASF dual-hosted git repository.
kezhenxu94 pushed a commit to branch plugin-test
in repository https://gitbox.apache.org/repos/asf/skywalking-python.git
commit 5e02ed82205d41cdc45f942c633501db0f5a78bb
Author: kezhenxu94 <ke...@163.com>
AuthorDate: Fri Jun 5 20:57:35 2020 +0800
Test: set up plugin tests and add http plugin test
---
docs/FAQ.md | 2 +-
docs/PluginTest.md | 32 +++++++++++
setup.py | 3 +
tests/plugin/__init__.py | 71 ++++++++++++++++++++++++
setup.py => tests/plugin/docker/Dockerfile.agent | 28 ++--------
setup.py => tests/plugin/docker/Dockerfile.tool | 52 ++++++++---------
setup.py => tests/plugin/http/__init__.py | 25 ---------
setup.py => tests/plugin/http/docker-compose.yml | 56 +++++++++++--------
setup.py => tests/plugin/http/expected.data.yml | 45 +++++++--------
setup.py => tests/plugin/http/provider.py | 51 ++++++++---------
setup.py => tests/plugin/http/test_request.py | 56 ++++++++++---------
11 files changed, 250 insertions(+), 171 deletions(-)
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 55e7b0c..eaefae7 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -1,4 +1,4 @@
-## FAQ
+# FAQ
Q: How to disable some plugins?
A: You can find the plugin name in [the list](../README.md#supported-libraries) and disable one or more plugins by following methods.
diff --git a/docs/PluginTest.md b/docs/PluginTest.md
new file mode 100644
index 0000000..a10ea80
--- /dev/null
+++ b/docs/PluginTest.md
@@ -0,0 +1,32 @@
+# Plugin Test
+
+Plugin test is required and should pass before a new plugin is able to be merged into the master branch.
+
+## Mock Collector
+
+Mock Collector respects the same protocol as the SkyWalking backend, and thus receives the report data from the agent side,
+besides, it also exposes some http endpoints for verification.
+
+## Tested Service
+
+A tested service is a service involving the plugin that is to be tested, and exposes some endpoints to trigger the instrumentation
+and report data to the mock collector.
+
+## Docker Compose
+
+`docker-compose` is used to orchestrate the mock collector and the tested service(s), the `docker-compose.yml` should be
+able to run with `docker-compose -f docker-compose.yml up` in standalone mode, which can be used in debugging too.
+
+## Expected Data
+
+The `expected.data.yml` file contains the expected segment data after we have triggered the instrumentation and report to mock collector,
+since the mock collector received the segment data then, we can post the expected data to the mock collector and verify whether
+they match. This can be done through the `/dataValidate` of the mock collector, say `http://collector:12800/dataValidate`, for example.
+
+## Example
+
+If we want to test the plugin for the built-in library `http`, we will:
+
+1. Build a tested service, which sets up an http server by `http` library, and exposes an http endpoint to be triggered in the test codes, say `/trigger`, take [this provider service](../tests/plugin/http/provider.py) as example.
+1. Compose a `docker-compose.yml` file, orchestrating the service built in step 1 and the mock collector, take [this `docker-compose.yml`](../tests/plugin/http/docker-compose.yml) as example.
+1. Write test codes to trigger the endpoint int step 1, and send the expected data file to the mock collector to verify, take [this test](../tests/plugin/http/test_request.py) as example.
diff --git a/setup.py b/setup.py
index b5102f1..987b792 100644
--- a/setup.py
+++ b/setup.py
@@ -39,4 +39,7 @@ setup(
"grpcio",
"requests",
],
+ tests_require=[
+ "testcontainers"
+ ],
)
diff --git a/tests/plugin/__init__.py b/tests/plugin/__init__.py
new file mode 100644
index 0000000..5bb74c8
--- /dev/null
+++ b/tests/plugin/__init__.py
@@ -0,0 +1,71 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+import unittest
+from abc import ABC
+from collections import namedtuple
+
+import requests
+from requests import Response
+from testcontainers.compose import DockerCompose
+
+HostPort = namedtuple('HostPort', 'host port')
+ServicePort = namedtuple('ServicePort', 'service port')
+
+
+class BasePluginTest(unittest.TestCase, ABC):
+ compose = None # type: DockerCompose
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.compose.stop()
+
+ @classmethod
+ def host(cls, service_port):
+ # type: (ServicePort) -> str
+ service, port = service_port
+ return cls.compose.get_service_host(service_name=service, port=port)
+
+ @classmethod
+ def port(cls, service_port):
+ # type: (ServicePort) -> str
+ service, port = service_port
+ return cls.compose.get_service_port(service_name=service, port=port)
+
+ @classmethod
+ def url(cls, service_port, path=''):
+ # type: (ServicePort, str) -> str
+ return 'http://%s:%s/%s' % (cls.host(service_port), cls.port(service_port), path.lstrip('/'))
+
+ @classmethod
+ def collector_address(cls):
+ # type: () -> ServicePort
+ return ServicePort(service='collector', port='12800')
+
+ def validate(self, expected_file_name):
+ # type: (str) -> Response
+ with open(expected_file_name) as expected_data_file:
+ response = requests.post(
+ url=self.__class__.url(self.__class__.collector_address(), path='/dataValidate'),
+ data=os.linesep.join(expected_data_file.readlines()),
+ )
+ print('validate: ', response)
+
+ self.assertEqual(response.status_code, 200)
+
+ return response
diff --git a/setup.py b/tests/plugin/docker/Dockerfile.agent
similarity index 55%
copy from setup.py
copy to tests/plugin/docker/Dockerfile.agent
index b5102f1..7672618 100644
--- a/setup.py
+++ b/tests/plugin/docker/Dockerfile.agent
@@ -1,4 +1,3 @@
-#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
@@ -13,30 +12,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-import pathlib
+FROM python:3.7
-from setuptools import setup, find_packages
+ARG ROOT=.
-HERE = pathlib.Path(__file__).parent
+WORKDIR /agent
-README = (HERE / "README.md").read_text()
+ADD $ROOT /agent
-setup(
- name="skywalking-python",
- version="0.1.1",
- description="Python Agent for Apache SkyWalking",
- long_description=README,
- long_description_content_type="text/markdown",
- url="https://github.com/apache/skywalking-python/",
- author="Apache",
- author_email="dev@skywalking.apache.org",
- license="Apache 2.0",
- packages=find_packages(exclude=("tests",)),
- include_package_data=True,
- install_requires=[
- "grpcio",
- "requests",
- ],
-)
+RUN make setup install
diff --git a/setup.py b/tests/plugin/docker/Dockerfile.tool
similarity index 55%
copy from setup.py
copy to tests/plugin/docker/Dockerfile.tool
index b5102f1..f4283f4 100644
--- a/setup.py
+++ b/tests/plugin/docker/Dockerfile.tool
@@ -15,28 +15,30 @@
# limitations under the License.
#
-import pathlib
-
-from setuptools import setup, find_packages
-
-HERE = pathlib.Path(__file__).parent
-
-README = (HERE / "README.md").read_text()
-
-setup(
- name="skywalking-python",
- version="0.1.1",
- description="Python Agent for Apache SkyWalking",
- long_description=README,
- long_description_content_type="text/markdown",
- url="https://github.com/apache/skywalking-python/",
- author="Apache",
- author_email="dev@skywalking.apache.org",
- license="Apache 2.0",
- packages=find_packages(exclude=("tests",)),
- include_package_data=True,
- install_requires=[
- "grpcio",
- "requests",
- ],
-)
+FROM openjdk:8
+
+WORKDIR /tests
+
+ARG COMMIT_HASH=3c9d7099f05dc4a4b937c8a47506e56c130b6221
+
+ADD https://github.com/apache/skywalking-agent-test-tool/archive/${COMMIT_HASH}.tar.gz .
+
+RUN tar -xf ${COMMIT_HASH}.tar.gz --strip 1
+
+RUN rm ${COMMIT_HASH}.tar.gz
+
+RUN ./mvnw -B -DskipTests package
+
+FROM openjdk:8
+
+EXPOSE 19876 12800
+
+WORKDIR /tests
+
+COPY --from=0 /tests/dist/skywalking-mock-collector.tar.gz /tests
+
+RUN tar -xf skywalking-mock-collector.tar.gz --strip 1
+
+RUN chmod +x bin/collector-startup.sh
+
+ENTRYPOINT bin/collector-startup.sh
diff --git a/setup.py b/tests/plugin/http/__init__.py
similarity index 55%
copy from setup.py
copy to tests/plugin/http/__init__.py
index b5102f1..6222972 100644
--- a/setup.py
+++ b/tests/plugin/http/__init__.py
@@ -15,28 +15,3 @@
# limitations under the License.
#
-import pathlib
-
-from setuptools import setup, find_packages
-
-HERE = pathlib.Path(__file__).parent
-
-README = (HERE / "README.md").read_text()
-
-setup(
- name="skywalking-python",
- version="0.1.1",
- description="Python Agent for Apache SkyWalking",
- long_description=README,
- long_description_content_type="text/markdown",
- url="https://github.com/apache/skywalking-python/",
- author="Apache",
- author_email="dev@skywalking.apache.org",
- license="Apache 2.0",
- packages=find_packages(exclude=("tests",)),
- include_package_data=True,
- install_requires=[
- "grpcio",
- "requests",
- ],
-)
diff --git a/setup.py b/tests/plugin/http/docker-compose.yml
similarity index 50%
copy from setup.py
copy to tests/plugin/http/docker-compose.yml
index b5102f1..c43fee3 100644
--- a/setup.py
+++ b/tests/plugin/http/docker-compose.yml
@@ -15,28 +15,40 @@
# limitations under the License.
#
-import pathlib
+version: '2.1'
-from setuptools import setup, find_packages
+services:
+ collector:
+ build:
+ context: ../docker
+ dockerfile: Dockerfile.tool
+ ports:
+ - 19876:19876
+ - 12800:12800
+ networks:
+ - beyond
+ healthcheck:
+ test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/12800"]
+ interval: 5s
+ timeout: 60s
+ retries: 120
-HERE = pathlib.Path(__file__).parent
+ provider:
+ build:
+ context: ../../../
+ dockerfile: tests/plugin/docker/Dockerfile.agent
+ networks:
+ - beyond
+ ports:
+ - 9091:9091
+ volumes:
+ - ./provider.py:/app/provider.py
+ environment:
+ SW_AGENT_COLLECTOR_BACKEND_SERVICES: collector:19876
+ command: ['python3', '/app/provider.py']
+ depends_on:
+ collector:
+ condition: service_healthy
-README = (HERE / "README.md").read_text()
-
-setup(
- name="skywalking-python",
- version="0.1.1",
- description="Python Agent for Apache SkyWalking",
- long_description=README,
- long_description_content_type="text/markdown",
- url="https://github.com/apache/skywalking-python/",
- author="Apache",
- author_email="dev@skywalking.apache.org",
- license="Apache 2.0",
- packages=find_packages(exclude=("tests",)),
- include_package_data=True,
- install_requires=[
- "grpcio",
- "requests",
- ],
-)
+networks:
+ beyond:
diff --git a/setup.py b/tests/plugin/http/expected.data.yml
similarity index 55%
copy from setup.py
copy to tests/plugin/http/expected.data.yml
index b5102f1..644f42b 100644
--- a/setup.py
+++ b/tests/plugin/http/expected.data.yml
@@ -15,28 +15,23 @@
# limitations under the License.
#
-import pathlib
-
-from setuptools import setup, find_packages
-
-HERE = pathlib.Path(__file__).parent
-
-README = (HERE / "README.md").read_text()
-
-setup(
- name="skywalking-python",
- version="0.1.1",
- description="Python Agent for Apache SkyWalking",
- long_description=README,
- long_description_content_type="text/markdown",
- url="https://github.com/apache/skywalking-python/",
- author="Apache",
- author_email="dev@skywalking.apache.org",
- license="Apache 2.0",
- packages=find_packages(exclude=("tests",)),
- include_package_data=True,
- install_requires=[
- "grpcio",
- "requests",
- ],
-)
+segmentItems:
+ - serviceName: provider
+ segmentSize: 1
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: /
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 7000
+ isError: false
+ spanType: Entry
+ peer: not null
+ skipAnalysis: false
+ tags:
+ - {key: http.method, value: POST}
diff --git a/setup.py b/tests/plugin/http/provider.py
similarity index 51%
copy from setup.py
copy to tests/plugin/http/provider.py
index b5102f1..7af884a 100644
--- a/setup.py
+++ b/tests/plugin/http/provider.py
@@ -15,28 +15,29 @@
# limitations under the License.
#
-import pathlib
-
-from setuptools import setup, find_packages
-
-HERE = pathlib.Path(__file__).parent
-
-README = (HERE / "README.md").read_text()
-
-setup(
- name="skywalking-python",
- version="0.1.1",
- description="Python Agent for Apache SkyWalking",
- long_description=README,
- long_description_content_type="text/markdown",
- url="https://github.com/apache/skywalking-python/",
- author="Apache",
- author_email="dev@skywalking.apache.org",
- license="Apache 2.0",
- packages=find_packages(exclude=("tests",)),
- include_package_data=True,
- install_requires=[
- "grpcio",
- "requests",
- ],
-)
+import time
+
+from skywalking import agent, config
+
+if __name__ == '__main__':
+ config.service_name = 'provider'
+ config.logging_level = 'DEBUG'
+ agent.start()
+
+ import socketserver
+ from http.server import BaseHTTPRequestHandler
+
+ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
+
+ def do_POST(self):
+ time.sleep(0.5)
+ self.send_response(200)
+ self.send_header('Content-Type', 'application/json')
+ self.end_headers()
+ self.wfile.write('{"song": "Despacito", "artist": "Luis Fonsi"}'.encode('ascii'))
+
+ PORT = 9091
+ Handler = SimpleHTTPRequestHandler
+
+ with socketserver.TCPServer(("", PORT), Handler) as httpd:
+ httpd.serve_forever()
diff --git a/setup.py b/tests/plugin/http/test_request.py
similarity index 50%
copy from setup.py
copy to tests/plugin/http/test_request.py
index b5102f1..1619dd8 100644
--- a/setup.py
+++ b/tests/plugin/http/test_request.py
@@ -15,28 +15,34 @@
# limitations under the License.
#
-import pathlib
-
-from setuptools import setup, find_packages
-
-HERE = pathlib.Path(__file__).parent
-
-README = (HERE / "README.md").read_text()
-
-setup(
- name="skywalking-python",
- version="0.1.1",
- description="Python Agent for Apache SkyWalking",
- long_description=README,
- long_description_content_type="text/markdown",
- url="https://github.com/apache/skywalking-python/",
- author="Apache",
- author_email="dev@skywalking.apache.org",
- license="Apache 2.0",
- packages=find_packages(exclude=("tests",)),
- include_package_data=True,
- install_requires=[
- "grpcio",
- "requests",
- ],
-)
+import os
+import time
+import unittest
+from os.path import abspath, dirname
+
+import requests
+from testcontainers.compose import DockerCompose
+
+from tests.plugin import BasePluginTest
+
+
+class TestRequestPlugin(BasePluginTest):
+ @classmethod
+ def setUpClass(cls):
+ docker_dir = dirname(dirname(abspath(__file__)))
+
+ cls.compose = DockerCompose(filepath=os.path.join(docker_dir, 'http'))
+ cls.compose.start()
+
+ cls.compose.wait_for(cls.url(cls.collector_address()))
+
+ def test_request_plugin(self):
+ print('traffic: ', requests.post(url=self.url(('provider', '9091'))))
+
+ time.sleep(3)
+
+ self.validate(expected_file_name=os.path.join(dirname(abspath(__file__)), 'expected.data.yml'))
+
+
+if __name__ == '__main__':
+ unittest.main()