Authenticator added for PrometheusHttpServer (#7225)

This commit is contained in:
Onur Kayabasi 2025-04-07 17:32:52 +02:00 committed by GitHub
parent f801a159f3
commit 3eb7ef00ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 3 deletions

View File

@ -10,6 +10,7 @@
package io.opentelemetry.exporter.prometheus;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.HttpHandler;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.export.MemoryMode;
@ -70,7 +71,8 @@ public final class PrometheusHttpServer implements MetricReader {
@Nullable Predicate<String> allowedResourceAttributesFilter,
MemoryMode memoryMode,
@Nullable HttpHandler defaultHandler,
DefaultAggregationSelector defaultAggregationSelector) {
DefaultAggregationSelector defaultAggregationSelector,
@Nullable Authenticator authenticator) {
this.builder = builder;
this.prometheusMetricReader =
new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter);
@ -99,6 +101,7 @@ public final class PrometheusHttpServer implements MetricReader {
.executorService(executor)
.registry(prometheusRegistry)
.defaultHandler(defaultHandler)
.authenticator(authenticator)
.buildAndStart();
} catch (IOException e) {
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);

View File

@ -8,6 +8,7 @@ package io.opentelemetry.exporter.prometheus;
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.HttpHandler;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.metrics.InstrumentType;
@ -36,6 +37,7 @@ public final class PrometheusHttpServerBuilder {
@Nullable private HttpHandler defaultHandler;
private DefaultAggregationSelector defaultAggregationSelector =
DefaultAggregationSelector.getDefault();
@Nullable private Authenticator authenticator;
PrometheusHttpServerBuilder() {}
@ -48,6 +50,7 @@ public final class PrometheusHttpServerBuilder {
this.executor = builder.executor;
this.memoryMode = builder.memoryMode;
this.defaultAggregationSelector = builder.defaultAggregationSelector;
this.authenticator = builder.authenticator;
}
/** Sets the host to bind to. If unset, defaults to {@value #DEFAULT_HOST}. */
@ -146,6 +149,17 @@ public final class PrometheusHttpServerBuilder {
return this;
}
/**
* Set the authenticator for {@link PrometheusHttpServer}.
*
* <p>If unset, no authentication will be performed.
*/
public PrometheusHttpServerBuilder setAuthenticator(Authenticator authenticator) {
requireNonNull(authenticator, "authenticator");
this.authenticator = authenticator;
return this;
}
/**
* Returns a new {@link PrometheusHttpServer} with the configuration of this builder which can be
* registered with a {@link io.opentelemetry.sdk.metrics.SdkMeterProvider}.
@ -166,6 +180,7 @@ public final class PrometheusHttpServerBuilder {
allowedResourceAttributesFilter,
memoryMode,
defaultHandler,
defaultAggregationSelector);
defaultAggregationSelector,
authenticator);
}
}

View File

@ -21,6 +21,9 @@ import com.linecorp.armeria.common.HttpHeaderNames;
import com.linecorp.armeria.common.HttpMethod;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.RequestHeaders;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
@ -528,6 +531,15 @@ class PrometheusHttpServerTest {
PrometheusRegistry prometheusRegistry = new PrometheusRegistry();
builder.setPrometheusRegistry(prometheusRegistry);
Authenticator authenticator =
new Authenticator() {
@Override
public Result authenticate(HttpExchange exchange) {
return new Success(new HttpPrincipal("anonymous", "public"));
}
};
builder.setAuthenticator(authenticator);
PrometheusHttpServer httpServer = builder.build();
PrometheusHttpServerBuilder fromOriginalBuilder = httpServer.toBuilder();
httpServer.close();
@ -538,7 +550,8 @@ class PrometheusHttpServerTest {
.hasFieldOrPropertyWithValue("otelScopeEnabled", false)
.hasFieldOrPropertyWithValue("allowedResourceAttributesFilter", resourceAttributesFilter)
.hasFieldOrPropertyWithValue("executor", executor)
.hasFieldOrPropertyWithValue("prometheusRegistry", prometheusRegistry);
.hasFieldOrPropertyWithValue("prometheusRegistry", prometheusRegistry)
.hasFieldOrPropertyWithValue("authenticator", authenticator);
}
/**
@ -611,4 +624,45 @@ class PrometheusHttpServerTest {
prometheusServer.shutdown();
}
}
@Test
void authenticator() {
// given
try (PrometheusHttpServer prometheusServer =
PrometheusHttpServer.builder()
.setHost("localhost")
.setPort(0)
.setAuthenticator(
new Authenticator() {
@Override
public Result authenticate(HttpExchange exchange) {
if ("/metrics".equals(exchange.getRequestURI().getPath())) {
return new Failure(401);
} else {
return new Success(new HttpPrincipal("anonymous", "public"));
}
}
})
.build()) {
prometheusServer.register(
new CollectionRegistration() {
@Override
public Collection<MetricData> collectAllMetrics() {
return metricData.get();
}
});
WebClient client =
WebClient.builder("http://localhost:" + prometheusServer.getAddress().getPort())
.decorator(RetryingClient.newDecorator(RetryRule.failsafe()))
.build();
// when
AggregatedHttpResponse metricsResponse = client.get("/metrics").aggregate().join();
AggregatedHttpResponse defaultResponse = client.get("/").aggregate().join();
// then
assertThat(metricsResponse.status()).isEqualTo(HttpStatus.UNAUTHORIZED);
assertThat(defaultResponse.status()).isEqualTo(HttpStatus.OK);
}
}
}