Skip to content

Commit

Permalink
mcache check supports IPv6 address (#19359)
Browse files Browse the repository at this point in the history
* mcache check supports IPv6 address

Signed-off-by: keisku <keisuke.umegaki@datadoghq.com>

* ddev dep freeze

---------

Signed-off-by: keisku <keisuke.umegaki@datadoghq.com>
  • Loading branch information
keisku authored Jan 10, 2025
1 parent a1583f3 commit 0ec4af3
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 2 deletions.
2 changes: 1 addition & 1 deletion agent_requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pysmi==1.2.1
pysnmp-mibs==0.1.6
pysnmp==5.1.0
pysocks==1.7.1
python-binary-memcached==0.31.2; sys_platform != 'win32'
python-binary-memcached==0.31.4; sys_platform != 'win32'
python-dateutil==2.9.0.post0
python3-gearman==0.1.0; sys_platform != 'win32'
pyvmomi==8.0.3.0.1
Expand Down
1 change: 1 addition & 0 deletions mcache/changelog.d/19359.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support IPv6.
15 changes: 15 additions & 0 deletions mcache/datadog_checks/mcache/mcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Licensed under Simplified BSD License (see LICENSE)
from __future__ import division

from ipaddress import IPv6Address

import bmemcached

from datadog_checks.base import AgentCheck, ConfigurationError
Expand Down Expand Up @@ -175,6 +177,13 @@ def _get_metrics(self, client, tags, service_check_tags=None):
except BadResponseError:
raise

def _is_ipv6(self, address):
try:
IPv6Address(address)
return True
except ValueError:
return False

def _get_optional_metrics(self, client, tags, options=None):
for arg, metrics_args in self.OPTIONAL_STATS.items():
if not options or options.get(arg, False):
Expand Down Expand Up @@ -257,6 +266,12 @@ def check(self, instance):
if not server and not socket:
raise InvalidConfigError('Either "url" or "socket" must be configured')

if self._is_ipv6(server):
# When it is already enclosed, this code path is not executed.
# bmemcached requires IPv6 addresses to be enclosed in brackets,
# because we set port with IP address at the client initialization.
server = "[{}]".format(server)

if socket:
server = 'unix'
port = socket
Expand Down
2 changes: 1 addition & 1 deletion mcache/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dynamic = [

[project.optional-dependencies]
deps = [
"python-binary-memcached==0.31.2; sys_platform != 'win32'",
"python-binary-memcached==0.31.4; sys_platform != 'win32'",
]

[project.urls]
Expand Down
17 changes: 17 additions & 0 deletions mcache/tests/compose/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,20 @@ services:
volumes:
- ${HOST_SOCKET_DIR}:${DOCKER_SOCKET_DIR}
command: memcached -S -s ${DOCKER_SOCKET_PATH} -a 777

memcached_ipv6:
image: datadog/docker-library:memcached_SASL
environment:
USERNAME: testuser
PASSWORD: testpass
networks:
ip6net:
ipv6_address: 2001:db8::2
command: "memcached -S -l::"

networks:
ip6net:
enable_ipv6: true
ipam:
config:
- subnet: 2001:db8::/64
17 changes: 17 additions & 0 deletions mcache/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ def dd_environment(e2e_instance):
yield e2e_instance


@pytest.fixture(scope='session')
def dd_environment_ipv6(instance_ipv6):
with docker_run(
os.path.join(HERE, 'compose', 'docker-compose.yaml'),
service_name='memcached_ipv6',
env_vars={'PWD': HERE},
conditions=[WaitFor(connect_to_mcache, args=(['{}:{}'.format('[2001:db8::2]', PORT)], USERNAME, PASSWORD))],
):
yield instance_ipv6


@pytest.fixture
def client():
return bmemcached.Client(["{}:{}".format(HOST, PORT)], USERNAME, PASSWORD)
Expand Down Expand Up @@ -93,3 +104,9 @@ def e2e_instance():
@pytest.fixture
def instance_socket():
return {'socket': get_host_socket_path(), 'tags': ['foo:bar'], 'username': USERNAME, 'password': PASSWORD}


@pytest.fixture(scope='session')
def instance_ipv6():
# This IPv6 address is defined in the docker-compose file.
return {'url': '2001:db8::2', 'port': PORT, 'tags': ['foo:bar'], 'username': USERNAME, 'password': PASSWORD}
15 changes: 15 additions & 0 deletions mcache/tests/test_integration_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,18 @@ def test_connections_leaks(check, instance):
check.check(instance)
# Verify that the count is still 0
assert count_connections(PORT) == 0


@pytest.mark.integration
@pytest.mark.usefixtures('dd_environment_ipv6')
def test_service_ok_ipv6(instance_ipv6, aggregator, dd_run_check):
"""
Service is up
"""
tags = ["host:[2001:db8::2]", "port:{}".format(PORT), "foo:bar"]
check = Memcache('mcache', {}, [instance_ipv6])
dd_run_check(check)
assert len(aggregator.service_checks(SERVICE_CHECK)) == 1
sc = aggregator.service_checks(SERVICE_CHECK)[0]
assert sc.status == check.OK
assert sc.tags == tags

0 comments on commit 0ec4af3

Please # to comment.