From cb7aae797d90aa26a7b2d62a09d96d298e2a8cee Mon Sep 17 00:00:00 2001 From: Paul Coignet Date: Thu, 7 May 2020 14:41:33 +0200 Subject: [PATCH 1/2] Get envoy version metadata --- envoy/datadog_checks/envoy/envoy.py | 34 +++++++++++++++++++ envoy/tests/common.py | 9 +++++ .../tests/docker/default/docker-compose.yaml | 2 +- envoy/tests/test_envoy.py | 23 ++++++++++++- envoy/tox.ini | 2 +- 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/envoy/datadog_checks/envoy/envoy.py b/envoy/datadog_checks/envoy/envoy.py index f205c5169e003..83d01d8437648 100644 --- a/envoy/datadog_checks/envoy/envoy.py +++ b/envoy/datadog_checks/envoy/envoy.py @@ -5,6 +5,7 @@ from collections import defaultdict import requests +from six.moves.urllib.parse import urljoin from datadog_checks.base import AgentCheck @@ -52,6 +53,8 @@ def check(self, instance): if self.caching_metrics is None: self.caching_metrics = instance.get('cache_metrics', True) + self._collect_metadata(stats_url) + try: response = self.http.get(stats_url) except requests.exceptions.Timeout: @@ -138,3 +141,34 @@ def whitelisted_metric(self, metric): return not blacklisted else: return True + + @AgentCheck.metadata_entrypoint + def _collect_metadata(self, stats_url): + # From http://domain/thing/stats to http://domain/thing/server_info + server_info_url = urljoin(stats_url, 'server_info') + + try: + response = self.http.get(server_info_url) + # { + # "version": "222aaacccfff888/1.14.1/Clean/RELEASE/BoringSSL", + # "state": "LIVE", + # ... + # } + raw_version = response.json()["version"].split('/')[1] + except requests.exceptions.Timeout: + msg = 'Envoy endpoint `{}` timed out after {} seconds'.format( + server_info_url, timeout=self.http.options['timeout'] + ) + self.log.info(msg) + return + except (requests.exceptions.RequestException, requests.exceptions.ConnectionError): + msg = 'Error accessing Envoy endpoint `{}`'.format(server_info_url) + self.log.info(msg) + return + + if response.status_code != 200: + msg = 'Envoy endpoint `{}` responded with HTTP status code {}'.format(server_info_url, response.status_code) + self.log.info(msg) + return + + self.set_metadata('version', raw_version) diff --git a/envoy/tests/common.py b/envoy/tests/common.py index 3d47d68478694..b44c6b39ddb4b 100644 --- a/envoy/tests/common.py +++ b/envoy/tests/common.py @@ -23,6 +23,11 @@ 'metric_blacklist': [r'envoy\.cluster\.out\.'], }, } +SERVER_INFO = { + "version": "222aaacccfff888/1.14.1/Clean/RELEASE/BoringSSL", + "state": "LIVE", +} +ENVOY_VERSION = os.getenv('ENVOY_VERSION') class MockResponse: @@ -30,6 +35,10 @@ def __init__(self, content, status_code): self.content = content self.status_code = status_code + def json(self): + # Metadata + return SERVER_INFO + @lru_cache(maxsize=None) def response(kind): diff --git a/envoy/tests/docker/default/docker-compose.yaml b/envoy/tests/docker/default/docker-compose.yaml index 3444f0aa22404..b0823bb60acb5 100644 --- a/envoy/tests/docker/default/docker-compose.yaml +++ b/envoy/tests/docker/default/docker-compose.yaml @@ -3,7 +3,7 @@ version: '3' services: front-envoy: - image: envoyproxy/envoy:${ENVOY_VERSION} + image: envoyproxy/envoy:v${ENVOY_VERSION} command: ["/usr/local/bin/envoy", "-c", "/etc/front-envoy.yaml", "--service-cluster", "front-proxy"] volumes: - ./front-envoy.yaml:/etc/front-envoy.yaml diff --git a/envoy/tests/test_envoy.py b/envoy/tests/test_envoy.py index 8efece4ea0994..52c0430bcd091 100644 --- a/envoy/tests/test_envoy.py +++ b/envoy/tests/test_envoy.py @@ -9,7 +9,7 @@ from datadog_checks.envoy import Envoy from datadog_checks.envoy.metrics import METRIC_PREFIX, METRICS -from .common import HOST, INSTANCES, response +from .common import ENVOY_VERSION, HOST, INSTANCES, response CHECK_NAME = 'envoy' @@ -120,3 +120,24 @@ def test_config(test_case, extra_config, expected_http_kwargs): ) http_wargs.update(expected_http_kwargs) r.get.assert_called_with('http://{}:8001/stats'.format(HOST), **http_wargs) + + +def test_metadata(datadog_agent): + instance = INSTANCES['main'] + check = Envoy(CHECK_NAME, {}, [instance]) + + with mock.patch('requests.get', return_value=response('multiple_services')): + check.check_id = 'test:123' + check._collect_metadata(instance['stats_url']) + + major, minor, patch = ENVOY_VERSION.split('.') + version_metadata = { + 'version.scheme': 'semver', + 'version.major': major, + 'version.minor': minor, + 'version.patch': patch, + 'version.raw': ENVOY_VERSION, + } + + datadog_agent.assert_metadata('test:123', version_metadata) + datadog_agent.assert_metadata_count(len(version_metadata)) diff --git a/envoy/tox.ini b/envoy/tox.ini index 7924da8ef2dc6..831e7d5ff5bec 100644 --- a/envoy/tox.ini +++ b/envoy/tox.ini @@ -20,7 +20,7 @@ passenv = COMPOSE* setenv = FLAVOR=default - ENVOY_VERSION=v1.14.1 + ENVOY_VERSION=1.14.1 commands = pip install -r requirements.in pytest -v {posargs} --benchmark-skip From 8cd5313889e9930aa8da2503bad4a6e9952a3363 Mon Sep 17 00:00:00 2001 From: Paul Coignet Date: Thu, 7 May 2020 16:58:32 +0200 Subject: [PATCH 2/2] Add metadata integration test --- envoy/datadog_checks/envoy/envoy.py | 15 +++++++-------- envoy/tests/test_envoy.py | 30 ++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/envoy/datadog_checks/envoy/envoy.py b/envoy/datadog_checks/envoy/envoy.py index 83d01d8437648..6add8e1ccc55c 100644 --- a/envoy/datadog_checks/envoy/envoy.py +++ b/envoy/datadog_checks/envoy/envoy.py @@ -149,6 +149,12 @@ def _collect_metadata(self, stats_url): try: response = self.http.get(server_info_url) + if response.status_code != 200: + msg = 'Envoy endpoint `{}` responded with HTTP status code {}'.format( + server_info_url, response.status_code + ) + self.log.info(msg) + return # { # "version": "222aaacccfff888/1.14.1/Clean/RELEASE/BoringSSL", # "state": "LIVE", @@ -156,9 +162,7 @@ def _collect_metadata(self, stats_url): # } raw_version = response.json()["version"].split('/')[1] except requests.exceptions.Timeout: - msg = 'Envoy endpoint `{}` timed out after {} seconds'.format( - server_info_url, timeout=self.http.options['timeout'] - ) + msg = 'Envoy endpoint `{}` timed out after {} seconds'.format(server_info_url, self.http.options['timeout']) self.log.info(msg) return except (requests.exceptions.RequestException, requests.exceptions.ConnectionError): @@ -166,9 +170,4 @@ def _collect_metadata(self, stats_url): self.log.info(msg) return - if response.status_code != 200: - msg = 'Envoy endpoint `{}` responded with HTTP status code {}'.format(server_info_url, response.status_code) - self.log.info(msg) - return - self.set_metadata('version', raw_version) diff --git a/envoy/tests/test_envoy.py b/envoy/tests/test_envoy.py index 52c0430bcd091..c5c1ac9bedfd9 100644 --- a/envoy/tests/test_envoy.py +++ b/envoy/tests/test_envoy.py @@ -5,6 +5,7 @@ import mock import pytest +import requests from datadog_checks.envoy import Envoy from datadog_checks.envoy.metrics import METRIC_PREFIX, METRICS @@ -125,11 +126,38 @@ def test_config(test_case, extra_config, expected_http_kwargs): def test_metadata(datadog_agent): instance = INSTANCES['main'] check = Envoy(CHECK_NAME, {}, [instance]) + check.check_id = 'test:123' + + with mock.patch('requests.get', side_effect=requests.exceptions.Timeout()): + check._collect_metadata(instance['stats_url']) + datadog_agent.assert_metadata_count(0) + with mock.patch('requests.get', side_effect=requests.exceptions.RequestException): + check._collect_metadata(instance['stats_url']) + datadog_agent.assert_metadata_count(0) with mock.patch('requests.get', return_value=response('multiple_services')): - check.check_id = 'test:123' check._collect_metadata(instance['stats_url']) + major, minor, patch = ENVOY_VERSION.split('.') + version_metadata = { + 'version.scheme': 'semver', + 'version.major': major, + 'version.minor': minor, + 'version.patch': patch, + 'version.raw': ENVOY_VERSION, + } + + datadog_agent.assert_metadata('test:123', version_metadata) + datadog_agent.assert_metadata_count(len(version_metadata)) + + +@pytest.mark.usefixtures('dd_environment') +def test_metadata_integration(aggregator, datadog_agent): + instance = INSTANCES['main'] + c = Envoy(CHECK_NAME, {}, [instance]) + c.check_id = 'test:123' + c.check(instance) + major, minor, patch = ENVOY_VERSION.split('.') version_metadata = { 'version.scheme': 'semver',