diff --git a/prometheus_client/exposition.py b/prometheus_client/exposition.py index cbdbb379..281fb1ea 100644 --- a/prometheus_client/exposition.py +++ b/prometheus_client/exposition.py @@ -822,9 +822,11 @@ def _escape_grouping_key(k, v): if v == "": # Per https://github.com/prometheus/pushgateway/pull/346. return k + "@base64", "=" - elif '/' in v or ' ' in v: + elif '/' in v or ' ' in v or v in ('.', '..'): # Added in Pushgateway 0.9.0. - # Use base64 encoding for values containing slashes or spaces + # Use base64 encoding for values containing slashes or spaces, and + # for "." and "..", which would otherwise be removed from the URL + # path as dot segments (the Pushgateway answers with a redirect). return k + "@base64", base64.urlsafe_b64encode(v.encode("utf-8")).decode("utf-8") else: return k, quote_plus(v) diff --git a/tests/test_exposition.py b/tests/test_exposition.py index b1acca44..c0fbde24 100644 --- a/tests/test_exposition.py +++ b/tests/test_exposition.py @@ -317,6 +317,17 @@ def test_push_with_complex_groupingkey(self): self.assertEqual(self.requests[0][0].headers.get('content-type'), CONTENT_TYPE_PLAIN_0_0_4) self.assertEqual(self.requests[0][1], b'# HELP g help\n# TYPE g gauge\ng 0.0\n') + def test_push_with_groupingkey_dot_segments(self): + push_to_gateway(self.address, "my_job", self.registry, {'a': '.', 'b': '..'}) + self.assertEqual(self.requests[0][0].command, 'PUT') + self.assertEqual(self.requests[0][0].path, '/metrics/job/my_job/a@base64/Lg==/b@base64/Li4=') + self.assertEqual(self.requests[0][1], b'# HELP g help\n# TYPE g gauge\ng 0.0\n') + + def test_push_with_dot_segment_job(self): + push_to_gateway(self.address, "..", self.registry) + self.assertEqual(self.requests[0][0].command, 'PUT') + self.assertEqual(self.requests[0][0].path, '/metrics/job@base64/Li4=') + def test_push_with_complex_job(self): push_to_gateway(self.address, "my/job", self.registry) self.assertEqual(self.requests[0][0].command, 'PUT')