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

Do not resolve IP on __init__ #9644

Merged
merged 1 commit into from
Jul 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 25 additions & 17 deletions tcp_check/datadog_checks/tcp_check/tcp_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from contextlib import closing

from datadog_checks.base import AgentCheck, ConfigurationError
from datadog_checks.base.errors import CheckException
from datadog_checks.base.utils.time import get_precise_time


Expand All @@ -18,24 +19,25 @@ def __init__(self, name, init_config, instances):
instance = self.instances[0]

self.instance_name = self.normalize_tag(instance['name'])
port = instance.get('port', None)
self.timeout = float(instance.get('timeout', 10))
self.collect_response_time = instance.get('collect_response_time', False)
custom_tags = instance.get('tags', [])
self.url = instance.get('host', None)
self.socket_type = None
self._addr = None

port = instance.get('port', None)
try:
self.port = int(port)
except Exception:
raise ConfigurationError("{} is not a correct port.".format(str(port)))
try:
self.url = instance.get('host', None)
split_url = self.url.split(":")
except Exception: # Would be raised if url is not a string
raise ConfigurationError("A valid url must be specified")

custom_tags = instance.get('tags', [])
self.tags = [
'url:{}:{}'.format(instance.get('host', None), self.port),
'url:{}:{}'.format(self.url, self.port),
'instance:{}'.format(instance.get('name')),
] + custom_tags

Expand All @@ -51,18 +53,26 @@ def __init__(self, name, init_config, instances):
if len(block) != 4:
raise ConfigurationError("{} is not a correct IPv6 address.".format(self.url))
# It's a correct IP V6 address
self.addr = self.url
self._addr = self.url
self.socket_type = socket.AF_INET6
else:
self.socket_type = socket.AF_INET
# IP will be resolved at check time

@property
def addr(self):
if self._addr is None:
try:
self.resolve_ip()
except Exception:
msg = "URL: {} is not a correct IPv4, IPv6 or hostname".format(self.url)
raise ConfigurationError(msg)
except Exception as e:
self.log.debug(str(e))
msg = "URL: {} could not be resolved".format(self.url)
raise CheckException(msg)
return self._addr

def resolve_ip(self):
self.addr = socket.gethostbyname(self.url)
self._addr = socket.gethostbyname(self.url)
self.log.debug("%s resolved to %s", self.url, self._addr)

def connect(self):
with closing(socket.socket(self.socket_type)) as sock:
Expand All @@ -74,10 +84,10 @@ def connect(self):

def check(self, instance):
start = get_precise_time() # Avoid initialisation warning
self.log.debug("Connecting to %s %d", self.addr, self.port)
self.log.debug("Connecting to %s %d", self.url, self.port)
try:
response_time = self.connect()
self.log.debug("%s:%d is UP", self.addr, self.port)
self.log.debug("%s:%d is UP", self.url, self.port)
self.report_as_service_check(AgentCheck.OK, 'UP')
if self.collect_response_time:
self.gauge(
Expand All @@ -104,16 +114,14 @@ def check(self, instance):
),
)
else:
self.log.info("%s:%d is DOWN (%s). Connection failed after %d ms", self.addr, self.port, str(e), length)
self.log.info("%s:%d is DOWN (%s). Connection failed after %d ms", self.url, self.port, str(e), length)
self.report_as_service_check(
AgentCheck.CRITICAL, "{}. Connection failed after {} ms".format(str(e), length)
)

if self.socket_type == socket.AF_INET:
self.log.debug("Attempting to re-resolve IP for %s:%d", self.addr, self.port)
try:
self.resolve_ip()
except Exception:
self.log.debug("Unable to re-resolve IP for %s:%d", self.addr, self.port)
self.log.debug("Will attempt to re-resolve IP for %s:%d on next run", self.url, self.port)
self._addr = None

def report_as_service_check(self, status, msg=None):
if status == AgentCheck.OK:
Expand Down
14 changes: 11 additions & 3 deletions tcp_check/tests/test_tcp_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,20 @@ def test_down(aggregator):
def test_reattempt_resolution():
instance = deepcopy(common.INSTANCE)
check = TCPCheck(common.CHECK_NAME, {}, [instance])
check.check(instance)

def failed_connection(self):
raise Exception()
# IP is not normally re-resolved after the first check run
with mock.patch.object(check, 'resolve_ip', wraps=check.resolve_ip) as resolve_ip:
check.check(instance)
assert not resolve_ip.called

check.connect = failed_connection
# Upon connection failure, cached resolved IP is cleared
with mock.patch.object(check, 'connect', wraps=check.connect) as connect:
connect.side_effect = lambda self: Exception()
check.check(instance)
assert check._addr is None

# On next check run IP is re-resolved
with mock.patch.object(check, 'resolve_ip', wraps=check.resolve_ip) as resolve_ip:
check.check(instance)
assert resolve_ip.called
Expand Down