Polish "Add basic auth support for Prometheus pushgateway"

See gh-22548
pull/22918/head
Stephane Nicoll 4 years ago
parent 9ddc97f815
commit 14eb041086

@ -16,6 +16,11 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus; package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Map;
import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry; import io.micrometer.prometheus.PrometheusMeterRegistry;
@ -24,6 +29,7 @@ import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
import io.prometheus.client.exporter.PushGateway; import io.prometheus.client.exporter.PushGateway;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
@ -44,21 +50,17 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.log.LogMessage; import org.springframework.core.log.LogMessage;
import org.springframework.util.StringUtils;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Map;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Prometheus. * {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Prometheus.
* *
* @since 2.0.0
* @author Jon Schneider * @author Jon Schneider
* @author David J. M. Karlsen * @author David J. M. Karlsen
* @since 2.0.0
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@AutoConfigureBefore({CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class}) @AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class })
@AutoConfigureAfter(MetricsAutoConfiguration.class) @AutoConfigureAfter(MetricsAutoConfiguration.class)
@ConditionalOnBean(Clock.class) @ConditionalOnBean(Clock.class)
@ConditionalOnClass(PrometheusMeterRegistry.class) @ConditionalOnClass(PrometheusMeterRegistry.class)
@ -75,7 +77,7 @@ public class PrometheusMetricsExportAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PrometheusMeterRegistry prometheusMeterRegistry(PrometheusConfig prometheusConfig, public PrometheusMeterRegistry prometheusMeterRegistry(PrometheusConfig prometheusConfig,
CollectorRegistry collectorRegistry, Clock clock) { CollectorRegistry collectorRegistry, Clock clock) {
return new PrometheusMeterRegistry(prometheusConfig, collectorRegistry, clock); return new PrometheusMeterRegistry(prometheusConfig, collectorRegistry, clock);
} }
@ -118,30 +120,30 @@ public class PrometheusMetricsExportAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PrometheusPushGatewayManager prometheusPushGatewayManager(CollectorRegistry collectorRegistry, public PrometheusPushGatewayManager prometheusPushGatewayManager(CollectorRegistry collectorRegistry,
PrometheusProperties prometheusProperties, Environment environment) { PrometheusProperties prometheusProperties, Environment environment) {
PrometheusProperties.Pushgateway properties = prometheusProperties.getPushgateway(); PrometheusProperties.Pushgateway properties = prometheusProperties.getPushgateway();
Duration pushRate = properties.getPushRate(); Duration pushRate = properties.getPushRate();
String job = getJob(properties, environment); String job = getJob(properties, environment);
Map<String, String> groupingKey = properties.getGroupingKey(); Map<String, String> groupingKey = properties.getGroupingKey();
ShutdownOperation shutdownOperation = properties.getShutdownOperation(); ShutdownOperation shutdownOperation = properties.getShutdownOperation();
return new PrometheusPushGatewayManager(getPushGateway(properties.getBaseUrl()), collectorRegistry, PushGateway pushGateway = initializePushGateway(properties.getBaseUrl());
pushRate, job, groupingKey, shutdownOperation); if (StringUtils.hasText(properties.getUsername())) {
pushGateway.setConnectionFactory(
new BasicAuthHttpConnectionFactory(properties.getUsername(), properties.getPassword()));
}
return new PrometheusPushGatewayManager(pushGateway, collectorRegistry, pushRate, job, groupingKey,
shutdownOperation);
} }
private PushGateway getPushGateway(String url) { private PushGateway initializePushGateway(String url) {
PushGateway pushGateway = null;
try { try {
pushGateway = new PushGateway(new URL(url)); return new PushGateway(new URL(url));
} catch (MalformedURLException ex) { }
catch (MalformedURLException ex) {
logger.warn(LogMessage logger.warn(LogMessage
.format("Invalid PushGateway base url '%s': update your configuration to a valid URL", url)); .format("Invalid PushGateway base url '%s': update your configuration to a valid URL", url));
pushGateway = new PushGateway(url); return new PushGateway(url);
}
PrometheusProperties.Pushgateway properties = prometheusProperties.getPushgateway();
if (properties.getAuthEnabled()) {
pushgateway.setConnectionFactory(new BasicAuthHttpConnectionFactory(properties.getAuthusername(), properties.getAuthpassword()));
} }
return pushGateway;
} }
private String getJob(PrometheusProperties.Pushgateway properties, Environment environment) { private String getJob(PrometheusProperties.Pushgateway properties, Environment environment) {

@ -16,14 +16,15 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus; package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus;
import io.micrometer.prometheus.HistogramFlavor;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration; import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import io.micrometer.prometheus.HistogramFlavor;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* {@link ConfigurationProperties @ConfigurationProperties} for configuring metrics export * {@link ConfigurationProperties @ConfigurationProperties} for configuring metrics export
* to Prometheus. * to Prometheus.
@ -85,7 +86,6 @@ public class PrometheusProperties {
return this.pushgateway; return this.pushgateway;
} }
/** /**
* Configuration options for push-based interaction with Prometheus. * Configuration options for push-based interaction with Prometheus.
*/ */
@ -102,34 +102,29 @@ public class PrometheusProperties {
private String baseUrl = "http://localhost:9091"; private String baseUrl = "http://localhost:9091";
/** /**
* Frequency with which to push metrics. * Login user of the Prometheus Pushgateway.
*/
private Duration pushRate = Duration.ofMinutes(1);
/**
* Job identifier for this application instance.
*/ */
private String job; private String username;
/** /**
* Grouping key for the pushed metrics. * Login password of the Prometheus Pushgateway.
*/ */
private Map<String, String> groupingKey = new HashMap<>(); private String password;
/** /**
* Enable publishing via a Prometheus Pushgateway with Basic Auth. * Frequency with which to push metrics.
*/ */
private Boolean authEnabled = false; private Duration pushRate = Duration.ofMinutes(1);
/** /**
* Prometheus Pushgateway basic-auth username. * Job identifier for this application instance.
*/ */
private String authusername; private String job;
/** /**
* Prometheus Pushgateway basic-auth password. * Grouping key for the pushed metrics.
*/ */
private String authpassword; private Map<String, String> groupingKey = new HashMap<>();
/** /**
* Operation that should be performed on shutdown. * Operation that should be performed on shutdown.
@ -152,6 +147,22 @@ public class PrometheusProperties {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Duration getPushRate() { public Duration getPushRate() {
return this.pushRate; return this.pushRate;
} }
@ -184,29 +195,6 @@ public class PrometheusProperties {
this.shutdownOperation = shutdownOperation; this.shutdownOperation = shutdownOperation;
} }
public Boolean getAuthEnabled() {
return this.authEnabled;
}
public void setAuthEnabled(Boolean authEnabled) {
this.authEnabled = authEnabled;
}
public String getAuthusername() {
return authusername;
}
public void setAuthusername(String authusername) {
this.authusername = authusername;
}
public String getAuthpassword() {
return authpassword;
}
public void setAuthpassword(String authpassword) {
this.authpassword = authpassword;
}
} }
} }

@ -16,10 +16,16 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus; package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus;
import java.util.function.Consumer;
import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry; import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry; import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
import io.prometheus.client.exporter.DefaultHttpConnectionFactory;
import io.prometheus.client.exporter.HttpConnectionFactory;
import io.prometheus.client.exporter.PushGateway;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -29,6 +35,7 @@ import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScra
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -42,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link PrometheusMetricsExportAutoConfiguration}. * Tests for {@link PrometheusMetricsExportAutoConfiguration}.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll
*/ */
@ExtendWith(OutputCaptureExtension.class) @ExtendWith(OutputCaptureExtension.class)
class PrometheusMetricsExportAutoConfigurationTests { class PrometheusMetricsExportAutoConfigurationTests {
@ -142,6 +150,15 @@ class PrometheusMetricsExportAutoConfigurationTests {
}); });
} }
@Test
void withPushGatewayNoBasicAuth() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.metrics.export.prometheus.pushgateway.enabled=true")
.withUserConfiguration(BaseConfiguration.class)
.run(hasHttpConnectionFactory((httpConnectionFactory) -> assertThat(httpConnectionFactory)
.isInstanceOf(DefaultHttpConnectionFactory.class)));
}
@Test @Test
@Deprecated @Deprecated
void withCustomLegacyPushGatewayURL(CapturedOutput output) { void withCustomLegacyPushGatewayURL(CapturedOutput output) {
@ -163,11 +180,34 @@ class PrometheusMetricsExportAutoConfigurationTests {
.run((context) -> hasGatewayURL(context, "https://example.com:8080/metrics/")); .run((context) -> hasGatewayURL(context, "https://example.com:8080/metrics/"));
} }
@Test
void withPushGatewayBasicAuth() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.metrics.export.prometheus.pushgateway.enabled=true",
"management.metrics.export.prometheus.pushgateway.username=admin",
"management.metrics.export.prometheus.pushgateway.password=secret")
.withUserConfiguration(BaseConfiguration.class)
.run(hasHttpConnectionFactory((httpConnectionFactory) -> assertThat(httpConnectionFactory)
.isInstanceOf(BasicAuthHttpConnectionFactory.class)));
}
private void hasGatewayURL(AssertableApplicationContext context, String url) { private void hasGatewayURL(AssertableApplicationContext context, String url) {
assertThat(getPushGateway(context)).hasFieldOrPropertyWithValue("gatewayBaseURL", url);
}
private ContextConsumer<AssertableApplicationContext> hasHttpConnectionFactory(
Consumer<HttpConnectionFactory> httpConnectionFactory) {
return (context) -> {
PushGateway pushGateway = getPushGateway(context);
httpConnectionFactory
.accept((HttpConnectionFactory) ReflectionTestUtils.getField(pushGateway, "connectionFactory"));
};
}
private PushGateway getPushGateway(AssertableApplicationContext context) {
assertThat(context).hasSingleBean(PrometheusPushGatewayManager.class); assertThat(context).hasSingleBean(PrometheusPushGatewayManager.class);
PrometheusPushGatewayManager gatewayManager = context.getBean(PrometheusPushGatewayManager.class); PrometheusPushGatewayManager gatewayManager = context.getBean(PrometheusPushGatewayManager.class);
Object pushGateway = ReflectionTestUtils.getField(gatewayManager, "pushGateway"); return (PushGateway) ReflectionTestUtils.getField(gatewayManager, "pushGateway");
assertThat(pushGateway).hasFieldOrPropertyWithValue("gatewayBaseURL", url);
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)

Loading…
Cancel
Save