Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add version metadata #4874

Merged
merged 68 commits into from
Dec 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
42ccb88
Remove multi instance from code
hithwen Oct 18, 2019
ffe81db
Fix some tests
hithwen Oct 18, 2019
294602f
Remove known hosts
hithwen Oct 21, 2019
4e3c315
Do not modify constants
hithwen Oct 21, 2019
755c960
Do not iterate on instances on constructor
hithwen Oct 21, 2019
dd10d0e
DB is always cached, do not pass around
hithwen Oct 21, 2019
c600eb6
Extract version utils
hithwen Oct 21, 2019
9c7ce54
Fix unit tests
hithwen Oct 21, 2019
fb9784a
Add missing argument
hithwen Oct 21, 2019
962a8fa
Merge branch 'julia/postgres-refactor' into julia/pg-refactor-cached-…
hithwen Oct 21, 2019
ab8d4ea
Merge branch 'julia/pg-refactor-cached-connection' into julia/pg-refa…
hithwen Oct 21, 2019
5928819
Fix unit tests
hithwen Oct 21, 2019
f4022d3
Merge branch 'julia/postgres-refactor' into julia/pg-refactor-cached-…
hithwen Oct 21, 2019
7b0a249
Merge branch 'julia/pg-refactor-cached-connection' into julia/pg-refa…
hithwen Oct 21, 2019
81f9d5f
Remove unused argument
hithwen Oct 22, 2019
b7c9f74
Merge remote-tracking branch 'origin/master' into julia/pg-refactor-c…
hithwen Oct 23, 2019
0fe217d
Style fix
hithwen Oct 23, 2019
5398b24
Merge branch 'julia/pg-refactor-cached-connection' into julia/pg-refa…
hithwen Oct 23, 2019
768a356
Add semver dependency
hithwen Oct 23, 2019
d12d774
Add semver dependency to agent_requirements, create version constants
hithwen Oct 23, 2019
a0401c6
Style fix
hithwen Oct 23, 2019
6b3af0d
Fix tests
hithwen Oct 23, 2019
1cb0e14
Merge remote-tracking branch 'origin/master' into julia/pg-refactor-u…
hithwen Oct 24, 2019
2ce7e8a
Add inventories to postgres
hithwen Oct 24, 2019
c35f28a
fix metadata
hithwen Oct 24, 2019
837386d
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 24, 2019
0ddbb8a
Add missing import
hithwen Oct 24, 2019
ef4be98
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 24, 2019
e472798
Fix tests
hithwen Oct 24, 2019
4014058
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 24, 2019
6ab4df9
Remove unneded var
hithwen Oct 24, 2019
eafc603
Style fix
hithwen Oct 24, 2019
359b537
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 24, 2019
fb0cba8
Style fix
hithwen Oct 24, 2019
ea597b3
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 24, 2019
4240dc5
Merge remote-tracking branch 'origin/master' into julia/pg-refactor-u…
hithwen Oct 28, 2019
19f0d99
Update requirements when updating check
therve Oct 28, 2019
bcc3d40
Merge remote-tracking branch 'origin/therve/dev-requirements' into ju…
hithwen Oct 28, 2019
9e3b95a
Merge remote-tracking branch 'origin/master' into julia/pg-refactor-u…
hithwen Oct 28, 2019
faf3b65
Merge remote-tracking branch 'origin/master' into julia/pg-refactor-u…
hithwen Oct 29, 2019
1b8435e
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 29, 2019
0960c7a
Merge remote-tracking branch 'origin/master' into julia/pg-refactor-u…
hithwen Oct 29, 2019
6ce4997
Merge remote-tracking branch 'origin/master' into julia/pg-inventories
hithwen Oct 29, 2019
a217570
Add set metadata test
hithwen Oct 29, 2019
689f485
Log error when cannot determine version
hithwen Oct 29, 2019
6c53573
Remove custom comparison methods
hithwen Oct 29, 2019
eb0cdc2
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 30, 2019
143831d
Invalidate version on each agent run
hithwen Oct 30, 2019
1ddb84f
Fix typo
hithwen Oct 30, 2019
a4a31ac
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 30, 2019
d2efb46
Bump semver and throw exception if cannot determine version
hithwen Oct 30, 2019
7735c41
Add exception test
hithwen Oct 30, 2019
eb5611a
Merge branch 'julia/pg-refactor-use-semver' into julia/pg-inventories
hithwen Oct 30, 2019
66916d3
Fix tests
hithwen Oct 31, 2019
c8eabed
Merge remote-tracking branch 'origin/master' into julia/pg-inventories
hithwen Oct 31, 2019
33e2a00
Merge remote-tracking branch 'origin/master' into julia/pg-inventories
hithwen Oct 31, 2019
f6d7718
Add metadata integration test
hithwen Oct 31, 2019
e019e57
Fix metadata integration test
hithwen Oct 31, 2019
3673c05
Refactor
hithwen Oct 31, 2019
d13bba4
Simplify parse_version
hithwen Oct 31, 2019
7c08baf
Remove unused arg
hithwen Nov 25, 2019
a6a26e0
Merge remote-tracking branch 'origin/master' into julia/pg-inventories
hithwen Nov 25, 2019
c024d2f
Merge remote-tracking branch 'origin/master' into julia/pg-inventories
hithwen Dec 5, 2019
0626bef
Fix tests
hithwen Dec 5, 2019
91fe489
metadata are strings
hithwen Dec 5, 2019
2eb7c12
Style fix
hithwen Dec 6, 2019
0a751fa
Everything is strings, nones are not submitted. Better skip test message
hithwen Dec 9, 2019
f5dcb7c
Style fix
hithwen Dec 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions postgres/datadog_checks/postgres/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
STATIO_METRICS,
fmt,
)
from .version_utils import V8_3, V9, V9_1, V9_2, V9_4, V9_6, V10, get_version
from .version_utils import V8_3, V9, V9_1, V9_2, V9_4, V9_6, V10, get_raw_version, parse_version, transform_version

MAX_CUSTOM_RESULTS = 100
TABLE_COUNT_LIMIT = 200
Expand All @@ -59,6 +59,7 @@ class PostgreSql(AgentCheck):
GAUGE = AgentCheck.gauge
MONOTONIC = AgentCheck.monotonic_count
SERVICE_CHECK_NAME = 'postgres.can_connect'
METADATA_TRANSFORMERS = {'version': transform_version}

def __init__(self, name, init_config, instances):
AgentCheck.__init__(self, name, init_config, instances)
Expand Down Expand Up @@ -124,8 +125,9 @@ def _get_replication_role(self):
@property
def version(self):
if self._version is None:
self._version = get_version(self.db)
self.service_metadata('version', [self._version.major, self._version.minor, self._version.patch])
raw_version = get_raw_version(self.db)
self._version = parse_version(raw_version)
self.set_metadata('version', raw_version)
return self._version

def _get_instance_metrics(self, database_size_metrics, collect_default_db):
Expand Down Expand Up @@ -722,7 +724,7 @@ def check(self, instance):
self._connect(host, port, user, password, dbname, ssl, tags)
if tag_replication_role:
tags.extend(["replication_role:{}".format(self._get_replication_role())])
self.log.debug("Running check against version %s", self.version)
self.log.debug("Running check against version %s", str(self.version))
self._collect_stats(
user,
tags,
Expand All @@ -749,3 +751,4 @@ def check(self, instance):
self.db.commit()
except Exception as e:
self.log.warning("Unable to commit: %s", e)
self._version = None # We don't want to cache versions between runs to capture minor updates for metadata
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) In this case, maybe we should just pass down the version as method parameter instead of caching it on self._version. Seems easier to understand and less error prone.

43 changes: 33 additions & 10 deletions postgres/datadog_checks/postgres/version_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,48 @@
V10 = semver.parse("10.0.0")


def get_version(db):
def get_raw_version(db):
cursor = db.cursor()
cursor.execute('SHOW SERVER_VERSION;')
raw_version = cursor.fetchone()[0]
return raw_version


def parse_version(raw_version):
try:
# Only works for MAJOR.MINOR.PATCH(-PRE_RELEASE)
return semver.parse_version_info(raw_version)
except ValueError:
pass
try:
version_parts = raw_version.split(' ')[0].split('.')
version = [int(part) for part in version_parts]
# Version may be missing minor eg: 10.0
version = raw_version.split(' ')[0].split('.')
version = [int(part) for part in version]
while len(version) < 3:
version.append(0)
return semver.VersionInfo(*version)
except Exception:
except ValueError:
# Postgres might be in development, with format \d+[beta|rc]\d+
match = re.match(r'(\d+)([a-zA-Z]+)(\d+)', raw_version)
if match:
version = list(match.groups())
# We found a valid development version
if len(version) == 3:
# Replace development tag with a negative number to properly compare versions
version[1] = -1
version = [int(part) for part in version]
return semver.VersionInfo(*version)
return semver.parse_version_info('{}.0.0-{}.{}'.format(*version))
raise Exception("Cannot determine which version is {}".format(raw_version))


def transform_version(raw_version, options=None):
"""
:param raw_version: raw version in str format
:param options: keyword arguments to pass to any defined transformer
"""
version = parse_version(raw_version)
transformed = {
'version.major': str(version.major),
'version.minor': str(version.minor),
'version.patch': str(version.patch),
'version.raw': raw_version,
'version.scheme': 'semver',
}
if version.prerelease:
transformed['version.release'] = version.prerelease
return transformed
24 changes: 20 additions & 4 deletions postgres/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,28 @@ def test_wrong_version(aggregator, integration_check, pg_instance):
assert_state_clean(check)

check.check(pg_instance)
assert check._version.major == int(POSTGRES_VERSION)
assert_state_set(check)


@pytest.mark.integration
@pytest.mark.usefixtures('dd_environment')
def test_version_metadata(integration_check, pg_instance, datadog_agent):
check = integration_check(pg_instance)
check.check_id = 'test:123'
# Enforce to cache wrong version
check.check(pg_instance)
version = POSTGRES_VERSION.split('.')
version_metadata = {
'version.scheme': 'semver',
'version.major': version[0],
}
if len(version) == 2:
version_metadata['version.minor'] = version[1]

datadog_agent.assert_metadata('test:123', version_metadata)
datadog_agent.assert_metadata_count(5) # for raw and patch


@pytest.mark.integration
@pytest.mark.usefixtures('dd_environment')
def test_state_clears_on_connection_error(integration_check, pg_instance):
Expand All @@ -160,7 +178,6 @@ def throw_exception_first_time(*args, **kwargs):


def assert_state_clean(check):
assert check._version is None
assert check.instance_metrics is None
assert check.bgw_metrics is None
assert check.archiver_metrics is None
Expand All @@ -170,8 +187,7 @@ def assert_state_clean(check):
assert check.activity_metrics is None


def assert_state_set(check,):
assert check._version
def assert_state_set(check):
assert check.instance_metrics
assert check.bgw_metrics
if POSTGRES_VERSION != '9.3':
Expand Down
23 changes: 23 additions & 0 deletions postgres/tests/test_unit.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# (C) Datadog, Inc. 2018
# All rights reserved
# Licensed under Simplified BSD License (see LICENSE)
import mock
import psycopg2
import pytest
from mock import MagicMock
from semver import VersionInfo
from six import iteritems

from datadog_checks.postgres import util

Expand Down Expand Up @@ -172,3 +174,24 @@ def test_malformed_get_custom_queries(check):
malformed_custom_query_column['name'],
malformed_custom_query['metric_prefix'],
)


@pytest.mark.parametrize(
'test_case, params',
[
('9.6.2', {'version.major': '9', 'version.minor': '6', 'version.patch': '2'}),
('10.0', {'version.major': '10', 'version.minor': '0', 'version.patch': '0'}),
(
'11nightly3',
{'version.major': '11', 'version.minor': '0', 'version.patch': '0', 'version.release': 'nightly.3'},
),
],
)
def test_version_metadata(check, test_case, params):
check.check_id = 'test:123'
with mock.patch('datadog_checks.base.stubs.datadog_agent.set_check_metadata') as m:
check.set_metadata('version', test_case)
for name, value in iteritems(params):
m.assert_any_call('test:123', name, value)
m.assert_any_call('test:123', 'version.scheme', 'semver')
m.assert_any_call('test:123', 'version.raw', test_case)
71 changes: 52 additions & 19 deletions postgres/tests/test_version_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,77 @@
# All rights reserved
# Licensed under Simplified BSD License (see LICENSE)
import pytest
from mock import MagicMock
from semver import VersionInfo

from datadog_checks.postgres.version_utils import get_version
from datadog_checks.postgres.version_utils import parse_version, transform_version

pytestmark = pytest.mark.unit


def test_get_version():
def test_parse_version():
"""
Test _get_version() to make sure the check is properly parsing Postgres versions
"""
db = MagicMock()

# Test #.#.# style versions
db.cursor().fetchone.return_value = ['9.5.3']
assert get_version(db) == VersionInfo(9, 5, 3)
version = parse_version('9.5.3')
assert version == VersionInfo(9, 5, 3)

# Test #.# style versions
db.cursor().fetchone.return_value = ['10.2']
assert get_version(db) == VersionInfo(10, 2, 0)
v10_2 = parse_version('10.2')
assert v10_2 == VersionInfo(10, 2, 0)

v11 = parse_version('11')
assert v11 == VersionInfo(11, 0, 0)

# Test #beta# style versions
db.cursor().fetchone.return_value = ['11beta3']
assert get_version(db) == VersionInfo(11, -1, 3)
beta11 = parse_version('11beta3')
assert beta11 == VersionInfo(11, 0, 0, prerelease='beta.3')

assert v10_2 < beta11
assert v11 > beta11

# Test #rc# style versions
db.cursor().fetchone.return_value = ['11rc1']
assert get_version(db) == VersionInfo(11, -1, 1)
version = parse_version('11rc1')
assert version == VersionInfo(11, 0, 0, prerelease='rc.1')

# Test #nightly# style versions
db.cursor().fetchone.return_value = ['11nightly3']
assert get_version(db) == VersionInfo(11, -1, 3)
version = parse_version('11nightly3')
assert version == VersionInfo(11, 0, 0, 'nightly.3')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) This could be a parameterized test.

Currently if the firs case fail, the following cases are not run.



def test_throws_exception_for_unknown_version_format():
db = MagicMock()
db.cursor().fetchone.return_value = ['dontKnow']
with pytest.raises(Exception) as e:
get_version(db)
parse_version('dontKnow')
assert e.value.args[0] == "Cannot determine which version is dontKnow"


def test_transform_version():
version = transform_version('11beta4')
expected = {
'version.raw': '11beta4',
'version.major': '11',
'version.minor': '0',
'version.patch': '0',
'version.release': 'beta.4',
'version.scheme': 'semver',
}
assert expected == version

version = transform_version('10.0')
expected = {
'version.raw': '10.0',
'version.major': '10',
'version.minor': '0',
'version.patch': '0',
'version.scheme': 'semver',
}
assert expected == version

version = transform_version('10.5.4')
expected = {
'version.raw': '10.5.4',
'version.major': '10',
'version.minor': '5',
'version.patch': '4',
'version.scheme': 'semver',
}
assert expected == version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) This could be a parameterized test.

Currently if the firs case fail, the following cases are not run.

5 changes: 4 additions & 1 deletion postgres/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@

from .common import POSTGRES_VERSION

requires_over_10 = pytest.mark.skipif(float(POSTGRES_VERSION) < 10, reason='This test is for over 10 only')
requires_over_10 = pytest.mark.skipif(
POSTGRES_VERSION is None or float(POSTGRES_VERSION) < 10,
reason='This test is for over 10 only (make sure POSTGRES_VERSION is set)',
)