From 9eff627eb292ec4c769f0c6a080fe2ab37d3d652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 17 Jul 2024 12:41:12 -0500 Subject: [PATCH] Add support for Grafana LGTM stack See gh-41551 --- ...nectionDetailsFactoryIntegrationTests.java | 38 ++++++ ...nectionDetailsFactoryIntegrationTests.java | 38 ++++++ ...DockerComposeConnectionDetailsFactory.java | 5 +- ...DockerComposeConnectionDetailsFactory.java | 5 +- .../pages/features/dev-services.adoc | 4 +- .../pages/testing/testcontainers.adoc | 4 +- .../spring-boot-testcontainers/build.gradle | 1 + ...nectionDetailsFactoryIntegrationTests.java | 129 ++++++++++++++++++ ...nectionDetailsFactoryIntegrationTests.java | 63 +++++++++ ...ricsContainerConnectionDetailsFactory.java | 63 +++++++++ ...cingContainerConnectionDetailsFactory.java | 62 +++++++++ .../main/resources/META-INF/spring.factories | 2 + .../build.gradle | 1 + .../boot/testsupport/container/TestImage.java | 7 + 14 files changed, 416 insertions(+), 6 deletions(-) create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelMetricsDockerComposeConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactory.java diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelMetricsDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelMetricsDockerComposeConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 00000000000..ca6b824e956 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelMetricsDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2024 the original author or 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 + * + * https://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 org.springframework.boot.docker.compose.service.connection.otlp; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsConnectionDetails; +import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.testsupport.container.TestImage; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for + * {@link OpenTelemetryMetricsDockerComposeConnectionDetailsFactory}. + * + * @author Eddú Meléndez + */ +class GrafanaOtelMetricsDockerComposeConnectionDetailsFactoryIntegrationTests { + + @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.GRAFANA_OTEL_LGTM) + void runCreatesConnectionDetails(OtlpMetricsConnectionDetails connectionDetails) { + assertThat(connectionDetails.getUrl()).startsWith("http://").endsWith("/v1/metrics"); + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 00000000000..ee9b63c5e89 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOtelTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2024 the original author or 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 + * + * https://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 org.springframework.boot.docker.compose.service.connection.otlp; + +import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; +import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.testsupport.container.TestImage; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for + * {@link OpenTelemetryTracingDockerComposeConnectionDetailsFactory}. + * + * @author Eddú Meléndez + */ +class GrafanaOtelTracingDockerComposeConnectionDetailsFactoryIntegrationTests { + + @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.GRAFANA_OTEL_LGTM) + void runCreatesConnectionDetails(OtlpTracingConnectionDetails connectionDetails) { + assertThat(connectionDetails.getUrl()).startsWith("http://").endsWith("/v1/traces"); + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryMetricsDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryMetricsDockerComposeConnectionDetailsFactory.java index 49913297040..5721ee20ddf 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryMetricsDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryMetricsDockerComposeConnectionDetailsFactory.java @@ -30,10 +30,13 @@ import org.springframework.boot.docker.compose.service.connection.DockerComposeC class OpenTelemetryMetricsDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory { + private static final String[] OPENTELEMETRY_IMAGE_NAMES = { "otel/opentelemetry-collector-contrib", + "grafana/otel-lgtm" }; + private static final int OTLP_PORT = 4318; OpenTelemetryMetricsDockerComposeConnectionDetailsFactory() { - super("otel/opentelemetry-collector-contrib", + super(OPENTELEMETRY_IMAGE_NAMES, "org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsExportAutoConfiguration"); } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java index 20e5b06b3da..6d93f3385d8 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java @@ -30,10 +30,13 @@ import org.springframework.boot.docker.compose.service.connection.DockerComposeC class OpenTelemetryTracingDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory { + private static final String[] OPENTELEMETRY_IMAGE_NAMES = { "otel/opentelemetry-collector-contrib", + "grafana/otel-lgtm" }; + private static final int OTLP_PORT = 4318; OpenTelemetryTracingDockerComposeConnectionDetailsFactory() { - super("otel/opentelemetry-collector-contrib", + super(OPENTELEMETRY_IMAGE_NAMES, "org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration"); } diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc index 7190c0ee320..fe59b9d944f 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc @@ -108,10 +108,10 @@ The following service connections are currently supported: | Containers named "neo4j" or "bitnami/neo4j" | `OtlpMetricsConnectionDetails` -| Containers named "otel/opentelemetry-collector-contrib" +| Containers named "otel/opentelemetry-collector-contrib", "grafana/otel-lgtm" | `OtlpTracingConnectionDetails` -| Containers named "otel/opentelemetry-collector-contrib" +| Containers named "otel/opentelemetry-collector-contrib", "grafana/otel-lgtm" | `PulsarConnectionDetails` | Containers named "apachepulsar/pulsar" diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc index 0ee1dcab5aa..ef7bcdd4cbc 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc @@ -72,10 +72,10 @@ The following service connection factories are provided in the `spring-boot-test | Containers of type `Neo4jContainer` | `OtlpMetricsConnectionDetails` -| Containers named "otel/opentelemetry-collector-contrib" +| Containers named "otel/opentelemetry-collector-contrib" or `LgtmStackContainer` | `OtlpTracingConnectionDetails` -| Containers named "otel/opentelemetry-collector-contrib" +| Containers named "otel/opentelemetry-collector-contrib" or `LgtmStackContainer` | `PulsarConnectionDetails` | Containers of type `PulsarContainer` diff --git a/spring-boot-project/spring-boot-testcontainers/build.gradle b/spring-boot-project/spring-boot-testcontainers/build.gradle index c78397114e9..6bf43ef0df0 100644 --- a/spring-boot-project/spring-boot-testcontainers/build.gradle +++ b/spring-boot-project/spring-boot-testcontainers/build.gradle @@ -66,6 +66,7 @@ dependencies { optional("org.testcontainers:cassandra") optional("org.testcontainers:couchbase") optional("org.testcontainers:elasticsearch") + optional("org.testcontainers:grafana") optional("org.testcontainers:jdbc") optional("org.testcontainers:kafka") optional("org.testcontainers:mariadb") diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 00000000000..7e2f62c2b7e --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,129 @@ +/* + * Copyright 2012-2024 the original author or 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 + * + * https://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 org.springframework.boot.testcontainers.service.connection.otlp; + +import java.time.Duration; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import io.restassured.RestAssured; +import io.restassured.response.Response; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.testcontainers.grafana.LgtmStackContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsExportAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory}. + * + * @author Eddú Meléndez + */ +@SpringJUnitConfig +@TestPropertySource(properties = { "management.otlp.metrics.export.resource-attributes.service.name=test", + "management.otlp.metrics.export.step=1s" }) +@Testcontainers(disabledWithoutDocker = true) +class GrafanaOtelLgtmMetricsContainerConnectionDetailsFactoryIntegrationTests { + + @Container + @ServiceConnection + static final LgtmStackContainer container = TestImage.container(LgtmStackContainer.class); + + @Autowired + private MeterRegistry meterRegistry; + + @Test + void connectionCanBeMadeToOpenTelemetryCollectorContainer() { + Counter.builder("test.counter").register(this.meterRegistry).increment(42); + Gauge.builder("test.gauge", () -> 12).register(this.meterRegistry); + Timer.builder("test.timer").register(this.meterRegistry).record(Duration.ofMillis(123)); + DistributionSummary.builder("test.distributionsummary").register(this.meterRegistry).record(24); + + Awaitility.given() + .pollInterval(Duration.ofSeconds(2)) + .atMost(Duration.ofSeconds(10)) + .ignoreExceptions() + .untilAsserted(() -> { + Response response = RestAssured.given() + .queryParam("query", "{job=\"test\"}") + .get("%s/api/v1/query".formatted(container.getPromehteusHttpUrl())) + .prettyPeek() + .thenReturn(); + assertThat(response.getStatusCode()).isEqualTo(200); + assertThat(response.body() + .jsonPath() + .getList("data.result.find { it.metric.__name__ == 'test_counter_total' }.value")).contains("42"); + assertThat(response.body() + .jsonPath() + .getList("data.result.find { it.metric.__name__ == 'test_gauge' }.value")).contains("12"); + assertThat(response.body() + .jsonPath() + .getList("data.result.find { it.metric.__name__ == 'test_timer_milliseconds_count' }.value")) + .contains("1"); + assertThat(response.body() + .jsonPath() + .getList("data.result.find { it.metric.__name__ == 'test_timer_milliseconds_sum' }.value")) + .contains("123"); + assertThat(response.body() + .jsonPath() + .getList( + "data.result.find { it.metric.__name__ == 'test_timer_milliseconds_bucket' & it.metric.le == '+Inf' }.value")) + .contains("1"); + assertThat(response.body() + .jsonPath() + .getList("data.result.find { it.metric.__name__ == 'test_distributionsummary_count' }.value")) + .contains("1"); + assertThat(response.body() + .jsonPath() + .getList("data.result.find { it.metric.__name__ == 'test_distributionsummary_sum' }.value")) + .contains("24"); + assertThat(response.body() + .jsonPath() + .getList( + "data.result.find { it.metric.__name__ == 'test_distributionsummary_bucket' & it.metric.le == '+Inf' }.value")) + .contains("1"); + }); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(OtlpMetricsExportAutoConfiguration.class) + static class TestConfiguration { + + @Bean + Clock customClock() { + return Clock.SYSTEM; + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 00000000000..e16fcbe5749 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2024 the original author or 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 + * + * https://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 org.springframework.boot.testcontainers.service.connection.otlp; + +import org.junit.jupiter.api.Test; +import org.testcontainers.grafana.LgtmStackContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for + * {@link GrafanaOtelLgtmTracingContainerConnectionDetailsFactoryIntegrationTests}. + * + * @author Eddú Meléndez + */ +@SpringJUnitConfig +@Testcontainers(disabledWithoutDocker = true) +class GrafanaOtelLgtmTracingContainerConnectionDetailsFactoryIntegrationTests { + + @Container + @ServiceConnection + static final LgtmStackContainer container = TestImage.container(LgtmStackContainer.class); + + @Autowired + private OtlpTracingConnectionDetails connectionDetails; + + @Test + void connectionCanBeMadeToOpenTelemetryContainer() { + assertThat(this.connectionDetails.getUrl()).isEqualTo("%s/v1/traces".formatted(container.getOtlpHttpUrl())); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(OtlpAutoConfiguration.class) + static class TestConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory.java new file mode 100644 index 00000000000..f4daf7daf04 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2023 the original author or 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 + * + * https://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 org.springframework.boot.testcontainers.service.connection.otlp; + +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.grafana.LgtmStackContainer; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} to create + * {@link OtlpMetricsConnectionDetails} from a + * {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using + * the {@code "otel/opentelemetry-collector-contrib"} image. + * + * @author Eddú Meléndez + */ +class GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory() { + super(ANY_CONNECTION_NAME, + "org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsExportAutoConfiguration"); + } + + @Override + protected OtlpMetricsConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new OpenTelemetryMetricsContainerConnectionDetails(source); + } + + private static final class OpenTelemetryMetricsContainerConnectionDetails + extends ContainerConnectionDetails implements OtlpMetricsConnectionDetails { + + private OpenTelemetryMetricsContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + } + + @Override + public String getUrl() { + return "%s/v1/metrics".formatted(getContainer().getOtlpHttpUrl()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactory.java new file mode 100644 index 00000000000..4433c7458d9 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOtelLgtmTracingContainerConnectionDetailsFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2023 the original author or 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 + * + * https://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 org.springframework.boot.testcontainers.service.connection.otlp; + +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.grafana.LgtmStackContainer; + +import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} to create + * {@link OtlpTracingConnectionDetails} from a + * {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using + * the {@code "otel/opentelemetry-collector-contrib"} image. + * + * @author Eddú Meléndez + */ +class GrafanaOtelLgtmTracingContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + GrafanaOtelLgtmTracingContainerConnectionDetailsFactory() { + super(ANY_CONNECTION_NAME, "org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration"); + } + + @Override + protected OtlpTracingConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new OpenTelemetryTracingContainerConnectionDetails(source); + } + + private static final class OpenTelemetryTracingContainerConnectionDetails + extends ContainerConnectionDetails implements OtlpTracingConnectionDetails { + + private OpenTelemetryTracingContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + } + + @Override + public String getUrl() { + return "%s/v1/traces".formatted(getContainer().getOtlpHttpUrl()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories index 813e438b31f..727f4d50f60 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories @@ -23,6 +23,8 @@ org.springframework.boot.testcontainers.service.connection.ldap.OpenLdapContaine org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.otlp.GrafanaOtelLgtmMetricsContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.otlp.GrafanaOtelLgtmTracingContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.OpenTelemetryMetricsContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.OpenTelemetryTracingContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.pulsar.PulsarContainerConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle index 41a47c555a6..967cc8dc958 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle @@ -20,6 +20,7 @@ dependencies { optional("org.testcontainers:cassandra") optional("org.testcontainers:couchbase") optional("org.testcontainers:elasticsearch") + optional("org.testcontainers:grafana") optional("org.testcontainers:junit-jupiter") optional("org.testcontainers:kafka") optional("org.testcontainers:mongodb") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java index 0c3c024a029..db26620b206 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java @@ -36,6 +36,7 @@ import org.testcontainers.containers.PulsarContainer; import org.testcontainers.containers.RabbitMQContainer; import org.testcontainers.couchbase.CouchbaseContainer; import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.grafana.LgtmStackContainer; import org.testcontainers.redpanda.RedpandaContainer; import org.testcontainers.utility.DockerImageName; @@ -158,6 +159,12 @@ public enum TestImage { */ OPENTELEMETRY("otel/opentelemetry-collector-contrib", "0.75.0"), + /** + * A container image suitable for testing Grafana Otel LGTM. + */ + GRAFANA_OTEL_LGTM("grafana/otel-lgtm", "0.6.0", () -> LgtmStackContainer.class, + (container) -> ((LgtmStackContainer) container).withStartupTimeout(Duration.ofMinutes(2))), + /** * A container image suitable for testing Postgres. */