Skip to content
Open
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
1 change: 1 addition & 0 deletions http_check/changelog.d/21381.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix instance tag normalization to preserve minuses (-) in tag values according to Datadog tag rules
29 changes: 27 additions & 2 deletions http_check/datadog_checks/http_check/http_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ def __init__(self, name, init_config, instances):
if is_affirmative(self.instance.get('use_cert_from_response', False)):
self.HTTP_CONFIG_REMAPPER['disable_ssl_validation']['default'] = False

def normalize_instance_tag(self, tag):
# type: (Union[str, bytes]) -> str
"""Normalize instance tag values according to Datadog tag rules.

Datadog tags may contain alphanumerics, underscores, minuses, colons, periods, and slashes.
Other characters are converted to underscores.

This is different from the base normalize_tag which incorrectly converts minuses to underscores.
"""
if isinstance(tag, str):
tag = tag.encode('utf-8', 'ignore')

# Only replace characters that are NOT allowed in Datadog tags
# Allowed: alphanumerics, underscores, minuses, colons, periods, slashes
# Pattern matches: commas, pluses, asterisks, parentheses, brackets, braces, whitespaces
tag = re.sub(rb'[,\+\*()\[\]{}\s]', rb'_', tag)

# Clean up multiple underscores
tag = re.sub(rb'__+', rb'_', tag)

# Clean up underscores around dots
tag = re.sub(rb'_*\._*', rb'.', tag).strip(b'_')

return tag.decode('utf-8')

def check(self, instance):
(
addr,
Expand Down Expand Up @@ -106,7 +131,7 @@ def send_status_down(loginfo, down_msg):
# Store tags in a temporary list so that we don't modify the global tags data structure
tags_list = list(tags)
tags_list.append("url:{}".format(addr))
instance_name = self.normalize_tag(instance["name"])
instance_name = self.normalize_instance_tag(instance["name"])
tags_list.append("instance:{}".format(instance_name))
service_checks = []
service_checks_tags = self._get_service_checks_tags(instance)
Expand Down Expand Up @@ -284,7 +309,7 @@ def send_status_down(loginfo, down_msg):
self.report_as_service_check(sc_name, status, service_checks_tags, msg)

def _get_service_checks_tags(self, instance):
instance_name = self.normalize_tag(instance["name"])
instance_name = self.normalize_instance_tag(instance["name"])
url = instance.get("url", None)
if url is not None:
url = ensure_unicode(url)
Expand Down
3 changes: 2 additions & 1 deletion http_check/tests/test_http_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,8 @@ def test_service_check_instance_name_normalization(aggregator, http_check):
http_check.check(CONFIG_UNORMALIZED_INSTANCE_NAME['instances'][0])

# Assess instance name normalization
normalized_tags = ['url:https://valid.mock', 'instance:need_to_be_normalized']
# With the fix, minuses are preserved: "_need-to__be_normalized-" -> "need-to_be_normalized-"
normalized_tags = ['url:https://valid.mock', 'instance:need-to_be_normalized-']
aggregator.assert_service_check(HTTPCheck.SC_STATUS, status=HTTPCheck.OK, tags=normalized_tags, count=1)
aggregator.assert_service_check(HTTPCheck.SC_SSL_CERT, status=HTTPCheck.OK, tags=normalized_tags, count=1)

Expand Down
Loading