Skip to content

Commit 7939aba

Browse files
committed
Add grouping key escaping for URLs in Pushgateway exporter
Signed-off-by: Federico Torres <[email protected]>
1 parent 5445c1d commit 7939aba

File tree

3 files changed

+129
-5
lines changed

3 files changed

+129
-5
lines changed

prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.prometheus.metrics.exporter.pushgateway;
22

33
import static io.prometheus.metrics.exporter.pushgateway.Scheme.HTTP;
4-
import static io.prometheus.metrics.model.snapshots.PrometheusNaming.nameEscapingScheme;
4+
import static io.prometheus.metrics.model.snapshots.PrometheusNaming.*;
55

66
import io.prometheus.metrics.config.ExporterPushgatewayProperties;
77
import io.prometheus.metrics.config.PrometheusProperties;
@@ -434,11 +434,11 @@ private URL makeUrl(ExporterPushgatewayProperties properties)
434434
if (groupingKey != null) {
435435
for (Map.Entry<String, String> entry : groupingKey.entrySet()) {
436436
if (entry.getValue().isEmpty()) {
437-
url += "/" + entry.getKey() + "@base64/=";
437+
url += "/" + escapeName(entry.getKey(), EscapingScheme.VALUE_ENCODING_ESCAPING) + "@base64/=";
438438
} else if (entry.getValue().contains("/")) {
439-
url += "/" + entry.getKey() + "@base64/" + base64url(entry.getValue());
439+
url += "/" + escapeName(entry.getKey(), EscapingScheme.VALUE_ENCODING_ESCAPING) + "@base64/" + base64url(entry.getValue());
440440
} else {
441-
url += "/" + entry.getKey() + "/" + URLEncoder.encode(entry.getValue(), "UTF-8");
441+
url += "/" + escapeName(entry.getKey(), EscapingScheme.VALUE_ENCODING_ESCAPING) + "/" + URLEncoder.encode(entry.getValue(), "UTF-8");
442442
}
443443
}
444444
}

prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.prometheus.metrics.exporter.pushgateway;
22

3+
import static io.prometheus.metrics.model.snapshots.PrometheusNaming.nameValidationScheme;
34
import static org.assertj.core.api.Assertions.*;
45
import static org.assertj.core.api.Assertions.assertThat;
56
import static org.mockserver.model.HttpRequest.request;
@@ -11,6 +12,8 @@
1112
import java.lang.reflect.Field;
1213
import java.net.InetAddress;
1314
import java.net.URL;
15+
16+
import io.prometheus.metrics.model.snapshots.ValidationScheme;
1417
import org.junit.jupiter.api.AfterEach;
1518
import org.junit.jupiter.api.BeforeEach;
1619
import org.junit.jupiter.api.Test;
@@ -141,6 +144,23 @@ public void testPushWithGroupingKey() throws IOException {
141144
pg.push();
142145
}
143146

147+
@Test
148+
public void testPushWithEscapedGroupingKey() throws IOException {
149+
nameValidationScheme = ValidationScheme.UTF_8_VALIDATION;
150+
mockServerClient
151+
.when(request().withMethod("PUT").withPath("/metrics/job/j/U__l_2e_1/v1"))
152+
.respond(response().withStatusCode(202));
153+
PushGateway pg =
154+
PushGateway.builder()
155+
.address("localhost:" + mockServerClient.getPort())
156+
.registry(registry)
157+
.job("j")
158+
.groupingKey("l.1", "v1")
159+
.build();
160+
pg.push();
161+
nameValidationScheme = ValidationScheme.LEGACY_VALIDATION;
162+
}
163+
144164
@Test
145165
public void testPushWithMultiGroupingKey() throws IOException {
146166
mockServerClient
@@ -157,6 +177,24 @@ public void testPushWithMultiGroupingKey() throws IOException {
157177
pg.push();
158178
}
159179

180+
@Test
181+
public void testPushWithMultiEscapedGroupingKey() throws IOException {
182+
nameValidationScheme = ValidationScheme.UTF_8_VALIDATION;
183+
mockServerClient
184+
.when(request().withMethod("PUT").withPath("/metrics/job/j/U__l_2e_1/v1/U__l_2e_2/v2"))
185+
.respond(response().withStatusCode(202));
186+
PushGateway pg =
187+
PushGateway.builder()
188+
.address("localhost:" + mockServerClient.getPort())
189+
.registry(registry)
190+
.job("j")
191+
.groupingKey("l.1", "v1")
192+
.groupingKey("l.2", "v2")
193+
.build();
194+
pg.push();
195+
nameValidationScheme = ValidationScheme.LEGACY_VALIDATION;
196+
}
197+
160198
@Test
161199
public void testPushWithEmptyLabelGroupingKey() throws IOException {
162200
mockServerClient
@@ -205,6 +243,23 @@ public void testPushCollectorWithGroupingKey() throws IOException {
205243
pg.push(gauge);
206244
}
207245

246+
@Test
247+
public void testPushCollectorWithEscapedGroupingKey() throws IOException {
248+
nameValidationScheme = ValidationScheme.UTF_8_VALIDATION;
249+
mockServerClient
250+
.when(request().withMethod("PUT").withPath("/metrics/job/j/U__l_2e_1/v1"))
251+
.respond(response().withStatusCode(202));
252+
PushGateway pg =
253+
PushGateway.builder()
254+
.address("localhost:" + mockServerClient.getPort())
255+
.registry(registry)
256+
.job("j")
257+
.groupingKey("l.1", "v1")
258+
.build();
259+
pg.push(gauge);
260+
nameValidationScheme = ValidationScheme.LEGACY_VALIDATION;
261+
}
262+
208263
@Test
209264
public void testPushAdd() throws IOException {
210265
mockServerClient
@@ -244,6 +299,23 @@ public void testPushAddWithGroupingKey() throws IOException {
244299
pg.pushAdd();
245300
}
246301

302+
@Test
303+
public void testPushAddWithEscapedGroupingKey() throws IOException {
304+
nameValidationScheme = ValidationScheme.UTF_8_VALIDATION;
305+
mockServerClient
306+
.when(request().withMethod("POST").withPath("/metrics/job/j/U__l_2e_1/v1"))
307+
.respond(response().withStatusCode(202));
308+
PushGateway pg =
309+
PushGateway.builder()
310+
.address("localhost:" + mockServerClient.getPort())
311+
.registry(registry)
312+
.groupingKey("l.1", "v1")
313+
.job("j")
314+
.build();
315+
pg.pushAdd();
316+
nameValidationScheme = ValidationScheme.LEGACY_VALIDATION;
317+
}
318+
247319
@Test
248320
public void testPushAddCollectorWithGroupingKey() throws IOException {
249321
mockServerClient
@@ -259,6 +331,23 @@ public void testPushAddCollectorWithGroupingKey() throws IOException {
259331
pg.pushAdd(gauge);
260332
}
261333

334+
@Test
335+
public void testPushAddCollectorWithEscapedGroupingKey() throws IOException {
336+
nameValidationScheme = ValidationScheme.UTF_8_VALIDATION;
337+
mockServerClient
338+
.when(request().withMethod("POST").withPath("/metrics/job/j/U__l_2e_1/v1"))
339+
.respond(response().withStatusCode(202));
340+
PushGateway pg =
341+
PushGateway.builder()
342+
.address("localhost:" + mockServerClient.getPort())
343+
.registry(registry)
344+
.groupingKey("l.1", "v1")
345+
.job("j")
346+
.build();
347+
pg.pushAdd(gauge);
348+
nameValidationScheme = ValidationScheme.LEGACY_VALIDATION;
349+
}
350+
262351
@Test
263352
public void testDelete() throws IOException {
264353
mockServerClient
@@ -283,6 +372,22 @@ public void testDeleteWithGroupingKey() throws IOException {
283372
pg.delete();
284373
}
285374

375+
@Test
376+
public void testDeleteWithEscapedGroupingKey() throws IOException {
377+
nameValidationScheme = ValidationScheme.UTF_8_VALIDATION;
378+
mockServerClient
379+
.when(request().withMethod("DELETE").withPath("/metrics/job/j/U__l_2e_1/v1"))
380+
.respond(response().withStatusCode(202));
381+
PushGateway pg =
382+
PushGateway.builder()
383+
.address("localhost:" + mockServerClient.getPort())
384+
.job("j")
385+
.groupingKey("l.1", "v1")
386+
.build();
387+
pg.delete();
388+
nameValidationScheme = ValidationScheme.LEGACY_VALIDATION;
389+
}
390+
286391
@Test
287392
public void testInstanceIpGroupingKey() throws IOException {
288393
String ip = InetAddress.getLocalHost().getHostAddress();
@@ -299,4 +404,23 @@ public void testInstanceIpGroupingKey() throws IOException {
299404
.build();
300405
pg.delete();
301406
}
407+
408+
@Test
409+
public void testInstanceIpEscapedGroupingKey() throws IOException {
410+
nameValidationScheme = ValidationScheme.UTF_8_VALIDATION;
411+
String ip = InetAddress.getLocalHost().getHostAddress();
412+
assertThat(ip).isNotEmpty();
413+
mockServerClient
414+
.when(request().withMethod("DELETE").withPath("/metrics/job/j/instance/" + ip + "/U__l_2e_1/v1"))
415+
.respond(response().withStatusCode(202));
416+
PushGateway pg =
417+
PushGateway.builder()
418+
.address("localhost:" + mockServerClient.getPort())
419+
.job("j")
420+
.groupingKey("l.1", "v1")
421+
.instanceIpGroupingKey()
422+
.build();
423+
pg.delete();
424+
nameValidationScheme = ValidationScheme.LEGACY_VALIDATION;
425+
}
302426
}

prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNaming.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ static boolean metricNeedsEscaping(DataPointSnapshot d) {
574574
* string that is returned (especially NO_ESCAPING, which by definition is a
575575
* noop). This method does not do any validation of the name.
576576
*/
577-
static String escapeName(String name, EscapingScheme scheme) {
577+
public static String escapeName(String name, EscapingScheme scheme) {
578578
if (name.isEmpty()) {
579579
return name;
580580
}

0 commit comments

Comments
 (0)