diff --git a/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest.java b/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest.java new file mode 100644 index 00000000..3ee68427 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest.java @@ -0,0 +1,179 @@ +package io.prometheus.jmx.test.opentelemetry; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.prometheus.jmx.test.support.JmxExporterMode; +import io.prometheus.jmx.test.support.OpenTelemetryTestEnvironment; +import io.prometheus.jmx.test.support.OpenTelemetryTestEnvironmentFactory; +import io.prometheus.jmx.test.support.TestSupport; +import io.prometheus.jmx.test.support.http.HttpClient; +import io.prometheus.jmx.test.support.http.HttpResponse; +import io.prometheus.jmx.test.support.throttle.ExponentialBackoffThrottle; +import io.prometheus.jmx.test.support.throttle.Throttle; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.testcontainers.containers.Network; +import org.verifyica.api.ArgumentContext; +import org.verifyica.api.ClassContext; +import org.verifyica.api.Trap; +import org.verifyica.api.Verifyica; +import org.yaml.snakeyaml.Yaml; + +/** Class to implement OpenTelemetryTest */ +public class OpenTelemetryEnvironmentVariableTest { + + @Verifyica.ArgumentSupplier(parallelism = Integer.MAX_VALUE) + public static Stream arguments() { + return OpenTelemetryTestEnvironmentFactory.createOpenTelemetryTestEnvironments(); + } + + @Verifyica.Prepare + public static void prepare(ClassContext classContext) { + TestSupport.getOrCreateNetwork(classContext); + } + + @Verifyica.BeforeAll + public void beforeAll(ArgumentContext argumentContext) { + Class testClass = argumentContext.classContext().testClass(); + Network network = TestSupport.getOrCreateNetwork(argumentContext); + TestSupport.initializeOpenTelemetryTestEnvironment(argumentContext, network, testClass); + } + + /** Method to test that Prometheus is up */ + @Verifyica.Test + @Verifyica.Order(1) + public void testPrometheusIsUp(OpenTelemetryTestEnvironment openTelemetryTestEnvironment) + throws IOException { + Throttle throttle = new ExponentialBackoffThrottle(100, 5000); + boolean isUp = false; + + for (int i = 0; i < 10; i++) { + HttpResponse httpResponse = sendPrometheusQuery(openTelemetryTestEnvironment, "up"); + + assertThat(httpResponse).isNotNull(); + + if (httpResponse.statusCode() == 200) { + assertThat(httpResponse.body()).isNotNull(); + assertThat(httpResponse.body().string()).isNotNull(); + + Map map = new Yaml().load(httpResponse.body().string()); + + String status = (String) map.get("status"); + assertThat(status).isEqualTo("success"); + + isUp = true; + break; + } else { + throttle.throttle(); + } + } + + assertThat(isUp).withFailMessage("Prometheus is down").isTrue(); + } + + /** Method to test that metrics exist in Prometheus */ + @Verifyica.Test + @Verifyica.Order(2) + public void testPrometheusHasMetrics(OpenTelemetryTestEnvironment openTelemetryTestEnvironment) + throws IOException { + boolean isJmxExporterModeJavaStandalone = + openTelemetryTestEnvironment.getJmxExporterMode() == JmxExporterMode.Standalone; + + for (String metricName : + ExpectedMetricsNames.getMetricsNames().stream() + .filter( + metricName -> + !isJmxExporterModeJavaStandalone + || (!metricName.startsWith("jvm_") + && !metricName.startsWith("process_"))) + .collect(Collectors.toList())) { + Double value = getPrometheusMetric(openTelemetryTestEnvironment, metricName); + + assertThat(value).as("metricName [%s]", metricName).isNotNull(); + assertThat(value).as("metricName [%s]", metricName).isEqualTo(1); + } + } + + @Verifyica.AfterAll + public void afterAll(ArgumentContext argumentContext) throws Throwable { + List traps = new ArrayList<>(); + + traps.add(new Trap(() -> TestSupport.destroyOpenTelemetryTestEnvironment(argumentContext))); + traps.add(new Trap(() -> TestSupport.destroyNetwork(argumentContext))); + + Trap.assertEmpty(traps); + } + + @Verifyica.Conclude + public static void conclude(ClassContext classContext) throws Throwable { + new Trap(() -> TestSupport.destroyNetwork(classContext)).assertEmpty(); + } + + /** + * Method to get a Prometheus metric + * + * @param openTelemetryTestEnvironment openTelemetryTestEnvironment + * @param metricName metricName + * @return the metric value, or null if it doesn't exist + */ + protected Double getPrometheusMetric( + OpenTelemetryTestEnvironment openTelemetryTestEnvironment, String metricName) + throws IOException { + Throttle throttle = new ExponentialBackoffThrottle(100, 5000); + Double value = null; + + for (int i = 0; i < 10; i++) { + HttpResponse httpResponse = + sendPrometheusQuery(openTelemetryTestEnvironment, metricName); + + assertThat(httpResponse).isNotNull(); + assertThat(httpResponse.statusCode()).isEqualTo(200); + assertThat(httpResponse.body()).isNotNull(); + assertThat(httpResponse.body().string()).isNotNull(); + + // TODO parse response and return value + if (httpResponse.body().string().contains(metricName)) { + value = 1.0; + break; + } + + throttle.throttle(); + } + + return value; + } + + /** + * Method to send a Prometheus query + * + * @param openTelemetryTestEnvironment openTelemetryTestEnvironment + * @param query query + * @return an HttpResponse + */ + protected HttpResponse sendPrometheusQuery( + OpenTelemetryTestEnvironment openTelemetryTestEnvironment, String query) + throws IOException { + return sendRequest( + openTelemetryTestEnvironment, + "/api/v1/query?query=" + URLEncoder.encode(query, StandardCharsets.UTF_8)); + } + + /** + * Method to send a Http GET request + * + * @param openTelemetryTestEnvironment openTelemetryTestEnvironment + * @param path path + * @return an HttpResponse + */ + protected HttpResponse sendRequest( + OpenTelemetryTestEnvironment openTelemetryTestEnvironment, String path) + throws IOException { + return HttpClient.sendRequest(openTelemetryTestEnvironment.getPrometheusUrl(path)); + } +} diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/application.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/application.sh new file mode 100755 index 00000000..0767f5f5 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/application.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +export OTEL_EXPORTER_OTLP_ENDPOINT="http://prometheus:9090/api/v1/otlp" +export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" +export OTEL_METRIC_EXPORT_INTERVAL="1" + +java \ + -Xmx512M \ + -javaagent:jmx_prometheus_javaagent.jar=exporter.yaml \ + -jar jmx_example_application.jar diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/exporter.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/exporter.yaml new file mode 100644 index 00000000..a5b51834 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/exporter.yaml @@ -0,0 +1,4 @@ +openTelemetry: +# Configuration via environment variables +rules: + - pattern: ".*" diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/prometheus.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/prometheus.yaml new file mode 100644 index 00000000..5b174f8e --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/JavaAgent/prometheus.yaml @@ -0,0 +1,4 @@ +global: + scrape_interval: 15s # Default scrape interval, not used since there are no scrape targets + +scrape_configs: [] # Empty scrape_configs means no targets to scrape \ No newline at end of file diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/application.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/application.sh new file mode 100755 index 00000000..9efc7364 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/application.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +java \ + -Xmx512M \ + -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 diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/exporter.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/exporter.sh new file mode 100755 index 00000000..97ca65ea --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/exporter.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +export OTEL_EXPORTER_OTLP_ENDPOINT="http://prometheus:9090/api/v1/otlp" +export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" +export OTEL_METRIC_EXPORT_INTERVAL="1" + +java \ + -Xmx512M \ + -jar jmx_prometheus_standalone.jar exporter.yaml diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/exporter.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/exporter.yaml new file mode 100644 index 00000000..d8067726 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/exporter.yaml @@ -0,0 +1,5 @@ +openTelemetry: +# Configuration via environment variables +hostPort: application:9999 +rules: + - pattern: ".*" diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/prometheus.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/prometheus.yaml new file mode 100644 index 00000000..5b174f8e --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/opentelemetry/OpenTelemetryEnvironmentVariableTest/Standalone/prometheus.yaml @@ -0,0 +1,4 @@ +global: + scrape_interval: 15s # Default scrape interval, not used since there are no scrape targets + +scrape_configs: [] # Empty scrape_configs means no targets to scrape \ No newline at end of file diff --git a/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/opentelemetry/OpenTelemetryExporterFactory.java b/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/opentelemetry/OpenTelemetryExporterFactory.java index eb0586bc..78ca3bf3 100644 --- a/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/opentelemetry/OpenTelemetryExporterFactory.java +++ b/jmx_prometheus_common/src/main/java/io/prometheus/jmx/common/opentelemetry/OpenTelemetryExporterFactory.java @@ -59,10 +59,8 @@ public OpenTelemetryExporter createOpenTelemetryExporter( new ConvertToMapAccessor( ConfigurationException.supplier( "Invalid configuration for" - + " /openTelemetry"))) - .orElseThrow( - ConfigurationException.supplier( - "Missing /openTelemetry configuration")); + + " /openTelemetry must be a map"))) + .orElse(new YamlMapAccessor()); String endpoint = openTelemetryYamlMapAccessor