From 74d52289c044693ed3a0b7cac9b757499cff9517 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Thu, 15 Feb 2024 15:12:27 -0800 Subject: [PATCH] Support for HTTPServer's SampleNameFilter Signed-off-by: Davi Arnaut --- .../SampleNameFilterConfigurationTest.java | 99 +++++++++++++++++++ .../JavaAgent/application.sh | 6 ++ .../JavaAgent/exporter.yaml | 6 ++ .../Standalone/application.sh | 13 +++ .../Standalone/exporter.sh | 5 + .../Standalone/exporter.yaml | 7 ++ .../configuration/ConvertToStringList.java | 56 +++++++++++ .../jmx/common/http/HTTPServerFactory.java | 83 ++++++++++++++++ 8 files changed, 275 insertions(+) create mode 100644 integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest.java create mode 100644 integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/application.sh create mode 100644 integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/exporter.yaml create mode 100644 integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/application.sh create mode 100644 integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.sh create mode 100644 integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.yaml create mode 100644 jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/configuration/ConvertToStringList.java diff --git a/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest.java b/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest.java new file mode 100644 index 00000000..5c220bf8 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 The Prometheus jmx_exporter Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.prometheus.jmx.test.http.sampleNameFilter; + +import static io.prometheus.jmx.test.support.MetricsAssertions.assertThatMetricIn; +import static io.prometheus.jmx.test.support.RequestResponseAssertions.assertThatResponseForRequest; + +import io.prometheus.jmx.test.BaseTest; +import io.prometheus.jmx.test.Metric; +import io.prometheus.jmx.test.MetricsParser; +import io.prometheus.jmx.test.Mode; +import io.prometheus.jmx.test.support.ContentConsumer; +import io.prometheus.jmx.test.support.HealthyRequest; +import io.prometheus.jmx.test.support.HealthyResponse; +import io.prometheus.jmx.test.support.MetricsRequest; +import io.prometheus.jmx.test.support.MetricsResponse; +import io.prometheus.jmx.test.support.OpenMetricsRequest; +import io.prometheus.jmx.test.support.OpenMetricsResponse; +import io.prometheus.jmx.test.support.PrometheusMetricsRequest; +import io.prometheus.jmx.test.support.PrometheusMetricsResponse; +import java.util.Collection; +import org.antublue.test.engine.api.TestEngine; + +public class SampleNameFilterConfigurationTest extends BaseTest implements ContentConsumer { + + @TestEngine.Test + public void testHealthy() throws InterruptedException { + assertThatResponseForRequest(new HealthyRequest(testState.httpClient())) + .isSuperset(HealthyResponse.RESULT_200); + } + + @TestEngine.Test + public void testMetrics() { + assertThatResponseForRequest(new MetricsRequest(testState.httpClient())) + .isSuperset(MetricsResponse.RESULT_200) + .dispatch(this); + } + + @TestEngine.Test + public void testMetricsOpenMetricsFormat() { + assertThatResponseForRequest(new OpenMetricsRequest(testState.httpClient())) + .isSuperset(OpenMetricsResponse.RESULT_200) + .dispatch(this); + } + + @TestEngine.Test + public void testMetricsPrometheusFormat() { + assertThatResponseForRequest(new PrometheusMetricsRequest(testState.httpClient())) + .isSuperset(PrometheusMetricsResponse.RESULT_200) + .dispatch(this); + } + + @Override + public void accept(String content) { + Collection metrics = MetricsParser.parse(content); + + String buildInfoName = + testArgument.mode() == Mode.JavaAgent + ? "jmx_prometheus_javaagent" + : "jmx_prometheus_httpserver"; + + assertThatMetricIn(metrics) + .withName("jmx_exporter_build_info") + .withLabel("name", buildInfoName) + .exists(); + + assertThatMetricIn(metrics) + .withName("java_lang_Memory_NonHeapMemoryUsage_committed") + .exists(); + + assertThatMetricIn(metrics) + .withName("io_prometheus_jmx_tabularData_Server_1_Disk_Usage_Table_size") + .withLabel("source", "/dev/sda1") + .withValue(7.516192768E9) + .exists(); + + assertThatMetricIn(metrics) + .withName("io_prometheus_jmx_tabularData_Server_2_Disk_Usage_Table_pcent") + .withLabel("source", "/dev/sda2") + .withValue(0.8) + .exists(); + + assertThatMetricIn(metrics).withName("jvm_threads_state").doesNotExist(); + } +} diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/application.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/application.sh new file mode 100644 index 00000000..268f821e --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/application.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +java \ + -Xmx128M \ + -javaagent:jmx_prometheus_javaagent.jar=8888:exporter.yaml \ + -jar jmx_example_application.jar \ No newline at end of file diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/exporter.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/exporter.yaml new file mode 100644 index 00000000..675e132e --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/JavaAgent/exporter.yaml @@ -0,0 +1,6 @@ +httpServer: + sampleNameFilter: + name-must-not-be-equal-to: + - jvm_threads_state +rules: + - pattern: ".*" diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/application.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/application.sh new file mode 100644 index 00000000..c0fa948c --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/application.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +java \ + -Xmx128M \ + -Dcom.sun.management.jmxremote=true \ + -Dcom.sun.management.jmxremote.authenticate=false \ + -Dcom.sun.management.jmxremote.local.only=false \ + -Dcom.sun.management.jmxremote.port=9999 \ + -Dcom.sun.management.jmxremote.registry.ssl=false \ + -Dcom.sun.management.jmxremote.rmi.port=9999 \ + -Dcom.sun.management.jmxremote.ssl.need.client.auth=false \ + -Dcom.sun.management.jmxremote.ssl=false \ + -jar jmx_example_application.jar \ No newline at end of file diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.sh new file mode 100644 index 00000000..c35f1652 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +java \ + -Xmx128M \ + -jar jmx_prometheus_httpserver.jar 8888 exporter.yaml \ No newline at end of file diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.yaml new file mode 100644 index 00000000..2206d9a3 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/http/sampleNameFilter/SampleNameFilterConfigurationTest/Standalone/exporter.yaml @@ -0,0 +1,7 @@ +httpServer: + sampleNameFilter: + name-must-not-be-equal-to: + - jvm_threads_state +hostPort: application:9999 +rules: + - pattern: ".*" diff --git a/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/configuration/ConvertToStringList.java b/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/configuration/ConvertToStringList.java new file mode 100644 index 00000000..0ae344c5 --- /dev/null +++ b/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/configuration/ConvertToStringList.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Prometheus jmx_exporter Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.prometheus.jmx.common.configuration; + +import io.prometheus.jmx.common.util.Precondition; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ConvertToStringList implements Function> { + + private final Supplier supplier; + + /** + * Constructor + * + * @param supplier supplier + */ + public ConvertToStringList(Supplier supplier) { + Precondition.notNull(supplier); + this.supplier = supplier; + } + + /** + * Method to apply a function + * + * @param value value + * @return the return value + */ + @Override + public List apply(Object value) { + if (value == null) { + throw new IllegalArgumentException(); + } + + try { + return (List) value; + } catch (Throwable t) { + throw supplier.get(); + } + } +} diff --git a/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/http/HTTPServerFactory.java b/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/http/HTTPServerFactory.java index 0504c3b2..be3366cb 100644 --- a/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/http/HTTPServerFactory.java +++ b/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/http/HTTPServerFactory.java @@ -19,10 +19,12 @@ import com.sun.net.httpserver.Authenticator; import com.sun.net.httpserver.HttpsConfigurator; import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.SampleNameFilter; import io.prometheus.client.exporter.HTTPServer; import io.prometheus.jmx.common.configuration.ConvertToInteger; import io.prometheus.jmx.common.configuration.ConvertToMapAccessor; import io.prometheus.jmx.common.configuration.ConvertToString; +import io.prometheus.jmx.common.configuration.ConvertToStringList; import io.prometheus.jmx.common.configuration.ValidateIntegerInRange; import io.prometheus.jmx.common.configuration.ValidateStringIsNotBlank; import io.prometheus.jmx.common.http.authenticator.MessageDigestAuthenticator; @@ -38,7 +40,9 @@ import java.security.GeneralSecurityException; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionHandler; @@ -119,6 +123,7 @@ public HTTPServer createHTTPServer( configureThreads(httpServerBuilder); configureAuthentication(httpServerBuilder); configureSSL(httpServerBuilder); + configureSampleNameFilter(httpServerBuilder); return httpServerBuilder.build(); } @@ -591,6 +596,84 @@ public void configureSSL(HTTPServer.Builder httpServerBuilder) { } } + /** + * Method to configure sampleNameFilter + * + * @param httpServerBuilder httpServerBuilder + */ + public void configureSampleNameFilter(HTTPServer.Builder httpServerBuilder) { + Optional> allowedNames = Optional.empty(); + Optional> excludedNames = Optional.empty(); + Optional> allowedPrefixes = Optional.empty(); + Optional> excludedPrefixes = Optional.empty(); + + if (rootYamlMapAccessor.containsPath("/httpServer/sampleNameFilter")) { + YamlMapAccessor httpServerSampleNameFilterMapAccessor = + rootYamlMapAccessor + .get("/httpServer/sampleNameFilter") + .map( + new ConvertToMapAccessor( + ConfigurationException.supplier( + "Invalid configuration for" + + " /httpServer/sampleNameFilter"))) + .orElseThrow( + ConfigurationException.supplier( + "/httpServer/sampleNameFilter configuration values are" + + " required")); + allowedNames = + httpServerSampleNameFilterMapAccessor + .get("/name-must-be-equal-to") + .map( + new ConvertToStringList( + ConfigurationException.supplier( + "Invalid configuration for" + + " /httpServer/sampleNameFilter/name-must-be-equal-to" + + " must be a list of string"))); + + excludedNames = + httpServerSampleNameFilterMapAccessor + .get("/name-must-not-be-equal-to") + .map( + new ConvertToStringList( + ConfigurationException.supplier( + "Invalid configuration for" + + " /httpServer/sampleNameFilter/name-must-not-be-equal-to" + + " must be a list of string"))); + + allowedPrefixes = + httpServerSampleNameFilterMapAccessor + .get("/name-must-start-with") + .map( + new ConvertToStringList( + ConfigurationException.supplier( + "Invalid configuration for" + + " /httpServer/sampleNameFilter/name-must-start-with" + + " must be a list of string"))); + + excludedPrefixes = + httpServerSampleNameFilterMapAccessor + .get("/name-must-not-start-with") + .map( + new ConvertToStringList( + ConfigurationException.supplier( + "Invalid configuration for" + + " /httpServer/sampleNameFilter/name-must-not-start-with" + + " must be a list of string"))); + + SampleNameFilter.Builder sampleNameFilterBuilder = new SampleNameFilter.Builder(); + + allowedNames.ifPresent(names -> sampleNameFilterBuilder.nameMustBeEqualTo(names)); + excludedNames.ifPresent(names -> sampleNameFilterBuilder.nameMustNotBeEqualTo(names)); + + allowedPrefixes.ifPresent( + prefixes -> sampleNameFilterBuilder.nameMustStartWith(prefixes)); + excludedPrefixes.ifPresent( + prefixes -> sampleNameFilterBuilder.nameMustNotStartWith(prefixes)); + + httpServerBuilder.withSampleNameFilter(sampleNameFilterBuilder.build()); + } + } + /** * Class to implement a named thread factory *