Skip to content

Commit

Permalink
Add service connection from Testcontainers OpenTelemetry Collector
Browse files Browse the repository at this point in the history
  • Loading branch information
eddumelendez committed Apr 20, 2023
1 parent c55d398 commit 8563165
Show file tree
Hide file tree
Showing 13 changed files with 362 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* 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.
Expand Down Expand Up @@ -29,6 +29,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.otlp.OtlpConnectionDetails;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

Expand All @@ -53,10 +54,16 @@ public OtlpMetricsExportAutoConfiguration(OtlpProperties properties) {
this.properties = properties;
}

@Bean
@ConditionalOnMissingBean(OtlpConnectionDetails.class)
public OtlpConnectionDetails otlpConnectionDetails() {
return new PropertiesOtlpConnectionDetails(this.properties);
}

@Bean
@ConditionalOnMissingBean
public OtlpConfig otlpConfig() {
return new OtlpPropertiesConfigAdapter(this.properties);
public OtlpConfig otlpConfig(OtlpConnectionDetails connectionDetails) {
return new OtlpPropertiesConfigAdapter(this.properties, connectionDetails);
}

@Bean
Expand All @@ -65,4 +72,22 @@ public OtlpMeterRegistry otlpMeterRegistry(OtlpConfig otlpConfig, Clock clock) {
return new OtlpMeterRegistry(otlpConfig, clock);
}

/**
* Adapts {@link OtlpProperties} to {@link OtlpConnectionDetails}.
*/
static class PropertiesOtlpConnectionDetails implements OtlpConnectionDetails {

private final OtlpProperties properties;

PropertiesOtlpConnectionDetails(OtlpProperties properties) {
this.properties = properties;
}

@Override
public String getUrl() {
return this.properties.getUrl();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.micrometer.registry.otlp.OtlpConfig;

import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter;
import org.springframework.boot.autoconfigure.otlp.OtlpConnectionDetails;

/**
* Adapter to convert {@link OtlpProperties} to an {@link OtlpConfig}.
Expand All @@ -31,8 +32,11 @@
*/
class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<OtlpProperties> implements OtlpConfig {

OtlpPropertiesConfigAdapter(OtlpProperties properties) {
private final OtlpConnectionDetails connectionDetails;

OtlpPropertiesConfigAdapter(OtlpProperties properties, OtlpConnectionDetails connectionDetails) {
super(properties);
this.connectionDetails = connectionDetails;
}

@Override
Expand All @@ -42,7 +46,7 @@ public String prefix() {

@Override
public String url() {
return get(OtlpProperties::getUrl, OtlpConfig.super::url);
return get((properties) -> this.connectionDetails.getUrl(), OtlpConfig.super::url);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.otlp.OtlpConnectionDetails;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -83,6 +84,24 @@ void allowsRegistryToBeCustomized() {
.hasBean("customRegistry"));
}

@Test
void definesPropertiesBasedConnectionDetailsByDefault() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context)
.hasSingleBean(OtlpMetricsExportAutoConfiguration.PropertiesOtlpConnectionDetails.class));
}

@Test
void testConnectionFactoryWithOverridesWhenUsingCustomConnectionDetails() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class, ConnectionDetailsConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(OtlpConnectionDetails.class)
.doesNotHaveBean(OtlpMetricsExportAutoConfiguration.PropertiesOtlpConnectionDetails.class);
OtlpConfig config = context.getBean(OtlpConfig.class);
assertThat(config.url()).isEqualTo("http://localhost:12345/v1/metrics");
});
}

@Configuration(proxyBeanMethods = false)
static class BaseConfiguration {

Expand Down Expand Up @@ -115,4 +134,14 @@ OtlpMeterRegistry customRegistry(OtlpConfig config, Clock clock) {

}

@Configuration(proxyBeanMethods = false)
static class ConnectionDetailsConfiguration {

@Bean
OtlpConnectionDetails otlpConnectionDetails() {
return () -> "http://localhost:12345/v1/metrics";
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,37 +34,42 @@ class OtlpPropertiesConfigAdapterTests {
void whenPropertiesUrlIsSetAdapterUrlReturnsIt() {
OtlpProperties properties = new OtlpProperties();
properties.setUrl("http://another-url:4318/v1/metrics");
assertThat(new OtlpPropertiesConfigAdapter(properties).url()).isEqualTo("http://another-url:4318/v1/metrics");
assertThat(otlpPropertiesConfigAdapter(properties).url()).isEqualTo("http://another-url:4318/v1/metrics");
}

@Test
void whenPropertiesAggregationTemporalityIsNotSetAdapterAggregationTemporalityReturnsCumulative() {
OtlpProperties properties = new OtlpProperties();
assertThat(new OtlpPropertiesConfigAdapter(properties).aggregationTemporality())
assertThat(otlpPropertiesConfigAdapter(properties).aggregationTemporality())
.isSameAs(AggregationTemporality.CUMULATIVE);
}

@Test
void whenPropertiesAggregationTemporalityIsSetAdapterAggregationTemporalityReturnsIt() {
OtlpProperties properties = new OtlpProperties();
properties.setAggregationTemporality(AggregationTemporality.DELTA);
assertThat(new OtlpPropertiesConfigAdapter(properties).aggregationTemporality())
assertThat(otlpPropertiesConfigAdapter(properties).aggregationTemporality())
.isSameAs(AggregationTemporality.DELTA);
}

@Test
void whenPropertiesResourceAttributesIsSetAdapterResourceAttributesReturnsIt() {
OtlpProperties properties = new OtlpProperties();
properties.setResourceAttributes(Map.of("service.name", "boot-service"));
assertThat(new OtlpPropertiesConfigAdapter(properties).resourceAttributes()).containsEntry("service.name",
assertThat(otlpPropertiesConfigAdapter(properties).resourceAttributes()).containsEntry("service.name",
"boot-service");
}

@Test
void whenPropertiesHeadersIsSetAdapterHeadersReturnsIt() {
OtlpProperties properties = new OtlpProperties();
properties.setHeaders(Map.of("header", "value"));
assertThat(new OtlpPropertiesConfigAdapter(properties).headers()).containsEntry("header", "value");
assertThat(otlpPropertiesConfigAdapter(properties).headers()).containsEntry("header", "value");
}

private static OtlpPropertiesConfigAdapter otlpPropertiesConfigAdapter(OtlpProperties properties) {
return new OtlpPropertiesConfigAdapter(properties,
new OtlpMetricsExportAutoConfiguration.PropertiesOtlpConnectionDetails(properties));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.autoconfigure.otlp;

import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;

/**
* Details required to establish a connection to a OpenTelemetry Collector
* service.
*
* @author Eddú Meléndez
* @since 3.1.0
*/
public interface OtlpConnectionDetails extends ConnectionDetails {

String getUrl();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2012-2019 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.
*/

/**
* Auto-configuration for OpenTelemetry Collector.
*/
package org.springframework.boot.autoconfigure.otlp;
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,9 @@ The following service connection factories are provided in the `spring-boot-test
| `Neo4jConnectionDetails`
| Containers of type `Neo4jContainer`

| `OtlpConnectionDetails`
| Containers named "otel/opentelemetry-collector-contrib"

| `R2dbcConnectionDetails`
| Containers of type `MariaDBContainer`, `MSSQLServerContainer`, `MySQLContainer`, `OracleContainer`, or `PostgreSQLContainer`

Expand Down
4 changes: 4 additions & 0 deletions spring-boot-project/spring-boot-testcontainers/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ dependencies {
optional("org.testcontainers:redpanda")
optional("org.testcontainers:r2dbc")

testImplementation(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))
testImplementation(project(":spring-boot-project:spring-boot-testcontainers"))
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testImplementation("ch.qos.logback:logback-classic")
testImplementation("io.micrometer:micrometer-registry-otlp")
testImplementation("io.rest-assured:rest-assured")
testImplementation("org.assertj:assertj-core")
testImplementation("org.awaitility:awaitility")
testImplementation("org.influxdb:influxdb-java")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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.Container;
import org.testcontainers.containers.GenericContainer;

import org.springframework.boot.autoconfigure.otlp.OtlpConnectionDetails;
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 OtlpConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using
* the {@code "otel/opentelemetry-collector-contrib"} image.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class OpenTelemetryConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<OtlpConnectionDetails, Container<?>> {

OpenTelemetryConnectionDetailsFactory() {
super("otel/opentelemetry-collector-contrib");
}

@Override
protected OtlpConnectionDetails getContainerConnectionDetails(ContainerConnectionSource<Container<?>> source) {
return new OpenTelemetryContainerConnectionDetails(source);
}

private static final class OpenTelemetryContainerConnectionDetails extends ContainerConnectionDetails
implements OtlpConnectionDetails {

private final String url;

private OpenTelemetryContainerConnectionDetails(ContainerConnectionSource<Container<?>> source) {
super(source);
this.url = "http://" + source.getContainer().getHost() + ":" + source.getContainer().getMappedPort(4318)
+ "/v1/metrics";
}

@Override
public String getUrl() {
return this.url;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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.
*/

/**
* Support for testcontainers OpenTelemetry service connections.
*/
package org.springframework.boot.testcontainers.service.connection.otlp;
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ org.springframework.boot.testcontainers.service.connection.kafka.KafkaContainerC
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.OpenTelemetryConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.MariaDbR2dbcContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.MsSqlServerR2dbcContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.MySqlR2dbcContainerConnectionDetailsFactory,\
Expand Down

0 comments on commit 8563165

Please sign in to comment.