diff --git a/smoke-tests/src/main/java/io/opentelemetry/smoketest/GroovyTestTelemetryRetriever.java b/smoke-tests/src/main/java/io/opentelemetry/smoketest/GroovyTestTelemetryRetriever.java new file mode 100644 index 000000000000..ac6d8b1d8498 --- /dev/null +++ b/smoke-tests/src/main/java/io/opentelemetry/smoketest/GroovyTestTelemetryRetriever.java @@ -0,0 +1,88 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.util.JsonFormat; +import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest; +import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; +import io.opentelemetry.testing.internal.armeria.client.WebClient; +import java.util.Collection; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class GroovyTestTelemetryRetriever { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + final WebClient client; + + GroovyTestTelemetryRetriever(int backendPort) { + client = WebClient.of("http://localhost:" + backendPort); + } + + void clearTelemetry() { + client.get("/clear").aggregate().join(); + } + + Collection waitForTraces() { + return waitForTelemetry("get-traces", ExportTraceServiceRequest::newBuilder); + } + + Collection waitForMetrics() { + return waitForTelemetry("get-metrics", ExportMetricsServiceRequest::newBuilder); + } + + Collection waitForLogs() { + return waitForTelemetry("get-logs", ExportLogsServiceRequest::newBuilder); + } + + @SuppressWarnings({"unchecked", "InterruptedExceptionSwallowed"}) + private > + Collection waitForTelemetry(String path, Supplier builderConstructor) { + try { + String content = waitForContent(path); + + return StreamSupport.stream(OBJECT_MAPPER.readTree(content).spliterator(), false) + .map( + jsonNode -> { + B builder = builderConstructor.get(); + // TODO: Register parser into object mapper to avoid de -> re -> deserialize. + try { + String json = OBJECT_MAPPER.writeValueAsString(jsonNode); + JsonFormat.parser().merge(json, builder); + } catch (Exception e) { + throw new IllegalStateException(e); + } + return (T) builder.build(); + }) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @SuppressWarnings("SystemOut") + private String waitForContent(String path) throws InterruptedException { + long previousSize = 0; + long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30); + String content = "[]"; + while (System.currentTimeMillis() < deadline) { + content = client.get(path).aggregate().join().contentUtf8(); + if (content.length() > 2 && content.length() == previousSize) { + break; + } + previousSize = content.length(); + System.out.println("Current content size $previousSize"); + TimeUnit.MILLISECONDS.sleep(500); + } + + return content; + } +} diff --git a/smoke-tests/src/main/java/io/opentelemetry/smoketest/JavaTelemetryRetriever.java b/smoke-tests/src/main/java/io/opentelemetry/smoketest/JavaTelemetryRetriever.java deleted file mode 100644 index 3670c089c70f..000000000000 --- a/smoke-tests/src/main/java/io/opentelemetry/smoketest/JavaTelemetryRetriever.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest; - -import io.opentelemetry.instrumentation.testing.internal.TelemetryConverter; -import io.opentelemetry.sdk.logs.data.LogRecordData; -import io.opentelemetry.sdk.metrics.data.MetricData; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.testing.internal.armeria.client.WebClient; -import io.opentelemetry.testing.internal.jackson.core.JsonProcessingException; -import io.opentelemetry.testing.internal.jackson.databind.ObjectMapper; -import io.opentelemetry.testing.internal.proto.collector.logs.v1.ExportLogsServiceRequest; -import io.opentelemetry.testing.internal.proto.collector.metrics.v1.ExportMetricsServiceRequest; -import io.opentelemetry.testing.internal.proto.collector.trace.v1.ExportTraceServiceRequest; -import io.opentelemetry.testing.internal.protobuf.GeneratedMessage; -import io.opentelemetry.testing.internal.protobuf.InvalidProtocolBufferException; -import io.opentelemetry.testing.internal.protobuf.util.JsonFormat; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -public class JavaTelemetryRetriever { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private final WebClient client; - - public JavaTelemetryRetriever(int backendPort) { - client = WebClient.of("http://localhost:" + backendPort); - } - - public void clearTelemetry() { - client.get("/clear").aggregate().join(); - } - - public List waitForTraces() { - Collection requests = - waitForTelemetry("get-traces", ExportTraceServiceRequest::newBuilder); - return TelemetryConverter.getSpanData( - convert(requests, ExportTraceServiceRequest::getResourceSpansList)); - } - - public Collection waitForMetrics() { - Collection requests = - waitForTelemetry("get-metrics", ExportMetricsServiceRequest::newBuilder); - return TelemetryConverter.getMetricsData( - convert(requests, ExportMetricsServiceRequest::getResourceMetricsList)); - } - - public Collection waitForLogs() { - Collection requests = - waitForTelemetry("get-logs", ExportLogsServiceRequest::newBuilder); - return TelemetryConverter.getLogRecordData( - convert(requests, ExportLogsServiceRequest::getResourceLogsList)); - } - - private static List convert(Collection items, Function> converter) { - return items.stream() - .flatMap(item -> converter.apply(item).stream()) - .collect(Collectors.toList()); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private - Collection waitForTelemetry(String path, Supplier builderConstructor) { - try { - return OBJECT_MAPPER - .readTree(waitForContent(path)) - .valueStream() - .map( - jsonNode -> { - B builder = builderConstructor.get(); - // TODO: Register parser into object mapper to avoid de -> re -> deserialize. - try { - JsonFormat.parser().merge(OBJECT_MAPPER.writeValueAsString(jsonNode), builder); - return (T) builder.build(); - } catch (InvalidProtocolBufferException | JsonProcessingException e) { - throw new IllegalStateException(e); - } - }) - .collect(Collectors.toList()); - } catch (InterruptedException | JsonProcessingException e) { - throw new IllegalStateException(e); - } - } - - @SuppressWarnings("SystemOut") - private String waitForContent(String path) throws InterruptedException { - long previousSize = 0; - long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30); - String content = "[]"; - while (System.currentTimeMillis() < deadline) { - content = client.get(path).aggregate().join().contentUtf8(); - if (content.length() > 2 && content.length() == previousSize) { - break; - } - - previousSize = content.length(); - System.out.println("Current content size " + previousSize); - TimeUnit.MILLISECONDS.sleep(500); - } - - return content; - } - - public final WebClient getClient() { - return client; - } -} diff --git a/smoke-tests/src/main/java/io/opentelemetry/smoketest/SmokeTestInstrumentationExtension.java b/smoke-tests/src/main/java/io/opentelemetry/smoketest/SmokeTestInstrumentationExtension.java new file mode 100644 index 000000000000..8002f7775560 --- /dev/null +++ b/smoke-tests/src/main/java/io/opentelemetry/smoketest/SmokeTestInstrumentationExtension.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * JUnit 5 extension for writing smoke tests that use a {@link TelemetryRetriever} to retrieve + * telemetry data from the fake backend. + * + *

Example usage: + * + *

+ *   class MySmokeTest implements TelemetryRetrieverProvider {
+ *     {@literal @}RegisterExtension
+ *     static final InstrumentationExtension testing = SmokeTestInstrumentationExtension.create();
+ *
+ *     {@literal @}Test
+ *     void test() {
+ *       // test code ...
+ *
+ *       var spans = testing.spans();
+ *       // assertions on collected spans ...
+ *     }
+ *   }
+ * 
+ */ +public class SmokeTestInstrumentationExtension extends InstrumentationExtension { + private SmokeTestInstrumentationExtension() { + super(SmokeTestRunner.instance()); + } + + public static SmokeTestInstrumentationExtension create() { + return new SmokeTestInstrumentationExtension(); + } + + @Override + public void beforeEach(ExtensionContext extensionContext) { + Object testInstance = extensionContext.getRequiredTestInstance(); + + if (!(testInstance instanceof TelemetryRetrieverProvider)) { + throw new AssertionError( + "SmokeTestInstrumentationExtension can only be applied to a subclass of " + + "TelemetryRetrieverProvider"); + } + + SmokeTestRunner smokeTestRunner = (SmokeTestRunner) getTestRunner(); + smokeTestRunner.setTelemetryRetriever( + ((TelemetryRetrieverProvider) testInstance).getTelemetryRetriever()); + + super.beforeEach(extensionContext); + } +} diff --git a/smoke-tests/src/main/java/io/opentelemetry/smoketest/SmokeTestRunner.java b/smoke-tests/src/main/java/io/opentelemetry/smoketest/SmokeTestRunner.java new file mode 100644 index 000000000000..935866770cba --- /dev/null +++ b/smoke-tests/src/main/java/io/opentelemetry/smoketest/SmokeTestRunner.java @@ -0,0 +1,72 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.trace.data.SpanData; +import java.util.List; + +/** + * An implementation of {@link InstrumentationTestRunner} that uses {@link TelemetryRetriever} to + * fetch traces, metrics and logs from the fake backend. + */ +public class SmokeTestRunner extends InstrumentationTestRunner { + + private static final SmokeTestRunner INSTANCE = new SmokeTestRunner(); + + private TelemetryRetriever telemetryRetriever; + + public static SmokeTestRunner instance() { + return INSTANCE; + } + + private SmokeTestRunner() { + super(OpenTelemetry.noop()); + } + + void setTelemetryRetriever(TelemetryRetriever telemetryRetriever) { + this.telemetryRetriever = telemetryRetriever; + } + + @Override + public void beforeTestClass() {} + + @Override + public void afterTestClass() {} + + @Override + public void clearAllExportedData() { + telemetryRetriever.clearTelemetry(); + } + + @Override + public OpenTelemetry getOpenTelemetry() { + throw new UnsupportedOperationException(); + } + + @Override + public List getExportedSpans() { + return telemetryRetriever.waitForTraces(); + } + + @Override + public List getExportedMetrics() { + return telemetryRetriever.waitForMetrics(); + } + + @Override + public List getExportedLogRecords() { + return telemetryRetriever.waitForLogs(); + } + + @Override + public boolean forceFlushCalled() { + throw new UnsupportedOperationException(); + } +} diff --git a/smoke-tests/src/main/java/io/opentelemetry/smoketest/TargetRunner.java b/smoke-tests/src/main/java/io/opentelemetry/smoketest/TargetRunner.java deleted file mode 100644 index 57ff7c14bf97..000000000000 --- a/smoke-tests/src/main/java/io/opentelemetry/smoketest/TargetRunner.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest; - -@FunctionalInterface -public interface TargetRunner { - void runInTarget() throws Exception; -} diff --git a/smoke-tests/src/main/java/io/opentelemetry/smoketest/TelemetryRetriever.java b/smoke-tests/src/main/java/io/opentelemetry/smoketest/TelemetryRetriever.java index b737d27c5b04..32ad80ca2763 100644 --- a/smoke-tests/src/main/java/io/opentelemetry/smoketest/TelemetryRetriever.java +++ b/smoke-tests/src/main/java/io/opentelemetry/smoketest/TelemetryRetriever.java @@ -5,65 +5,85 @@ package io.opentelemetry.smoketest; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.protobuf.GeneratedMessage; -import com.google.protobuf.util.JsonFormat; -import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest; -import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; -import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; +import io.opentelemetry.instrumentation.testing.internal.TelemetryConverter; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.testing.internal.armeria.client.WebClient; +import io.opentelemetry.testing.internal.jackson.core.JsonProcessingException; +import io.opentelemetry.testing.internal.jackson.databind.ObjectMapper; +import io.opentelemetry.testing.internal.proto.collector.logs.v1.ExportLogsServiceRequest; +import io.opentelemetry.testing.internal.proto.collector.metrics.v1.ExportMetricsServiceRequest; +import io.opentelemetry.testing.internal.proto.collector.trace.v1.ExportTraceServiceRequest; +import io.opentelemetry.testing.internal.protobuf.GeneratedMessage; +import io.opentelemetry.testing.internal.protobuf.InvalidProtocolBufferException; +import io.opentelemetry.testing.internal.protobuf.util.JsonFormat; import java.util.Collection; +import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; public class TelemetryRetriever { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private final WebClient client; - final WebClient client; - - TelemetryRetriever(int backendPort) { + public TelemetryRetriever(int backendPort) { client = WebClient.of("http://localhost:" + backendPort); } - void clearTelemetry() { + public void clearTelemetry() { client.get("/clear").aggregate().join(); } - Collection waitForTraces() { - return waitForTelemetry("get-traces", ExportTraceServiceRequest::newBuilder); + public List waitForTraces() { + Collection requests = + waitForTelemetry("get-traces", ExportTraceServiceRequest::newBuilder); + return TelemetryConverter.getSpanData( + convert(requests, ExportTraceServiceRequest::getResourceSpansList)); + } + + public List waitForMetrics() { + Collection requests = + waitForTelemetry("get-metrics", ExportMetricsServiceRequest::newBuilder); + return TelemetryConverter.getMetricsData( + convert(requests, ExportMetricsServiceRequest::getResourceMetricsList)); } - Collection waitForMetrics() { - return waitForTelemetry("get-metrics", ExportMetricsServiceRequest::newBuilder); + public List waitForLogs() { + Collection requests = + waitForTelemetry("get-logs", ExportLogsServiceRequest::newBuilder); + return TelemetryConverter.getLogRecordData( + convert(requests, ExportLogsServiceRequest::getResourceLogsList)); } - Collection waitForLogs() { - return waitForTelemetry("get-logs", ExportLogsServiceRequest::newBuilder); + private static List convert(Collection items, Function> converter) { + return items.stream() + .flatMap(item -> converter.apply(item).stream()) + .collect(Collectors.toList()); } - @SuppressWarnings({"unchecked", "InterruptedExceptionSwallowed"}) - private > + @SuppressWarnings({"unchecked", "rawtypes"}) + private Collection waitForTelemetry(String path, Supplier builderConstructor) { try { - String content = waitForContent(path); - - return StreamSupport.stream(OBJECT_MAPPER.readTree(content).spliterator(), false) + return OBJECT_MAPPER + .readTree(waitForContent(path)) + .valueStream() .map( jsonNode -> { B builder = builderConstructor.get(); // TODO: Register parser into object mapper to avoid de -> re -> deserialize. try { - String json = OBJECT_MAPPER.writeValueAsString(jsonNode); - JsonFormat.parser().merge(json, builder); - } catch (Exception e) { + JsonFormat.parser().merge(OBJECT_MAPPER.writeValueAsString(jsonNode), builder); + return (T) builder.build(); + } catch (InvalidProtocolBufferException | JsonProcessingException e) { throw new IllegalStateException(e); } - return (T) builder.build(); }) .collect(Collectors.toList()); - } catch (Exception e) { + } catch (InterruptedException | JsonProcessingException e) { throw new IllegalStateException(e); } } @@ -78,11 +98,20 @@ private String waitForContent(String path) throws InterruptedException { if (content.length() > 2 && content.length() == previousSize) { break; } + previousSize = content.length(); - System.out.println("Current content size $previousSize"); + System.out.println("Current content size " + previousSize); TimeUnit.MILLISECONDS.sleep(500); } + if ("true".equals(System.getenv("debug"))) { + System.out.println(content); + } + return content; } + + public final WebClient getClient() { + return client; + } } diff --git a/smoke-tests/src/main/java/io/opentelemetry/smoketest/TelemetryRetrieverProvider.java b/smoke-tests/src/main/java/io/opentelemetry/smoketest/TelemetryRetrieverProvider.java new file mode 100644 index 000000000000..ba9a9715db08 --- /dev/null +++ b/smoke-tests/src/main/java/io/opentelemetry/smoketest/TelemetryRetrieverProvider.java @@ -0,0 +1,14 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest; + +/** + * Provides a {@link TelemetryRetriever} instance. Tests using {@link + * SmokeTestInstrumentationExtension} must implement this interface. + */ +public interface TelemetryRetrieverProvider { + TelemetryRetriever getTelemetryRetriever(); +} diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy deleted file mode 100644 index 0e111ceaa442..000000000000 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest - - -import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest -import spock.lang.IgnoreIf -import spock.lang.Unroll - -import java.time.Duration - -import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers - -@IgnoreIf({ useWindowsContainers() }) -class DeclarativeConfigurationSmokeTest extends SmokeTest { - - protected String getTargetImage(String jdk) { - "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20241021.11448062567" - } - - @Override - protected List getExtraResources() { - [ - ResourceMapping.of("declarative-config.yaml", "/declarative-config.yaml"), - ] - } - - @Override - protected Map getExtraEnv() { - return ["OTEL_EXPERIMENTAL_CONFIG_FILE": "declarative-config.yaml"] - } - - @Override - protected TargetWaitStrategy getWaitStrategy() { - return new TargetWaitStrategy.Log(Duration.ofMinutes(1), ".*Started SpringbootApplication in.*") - } - - @Unroll - def "spring boot smoke test on JDK #jdk"(int jdk) { - setup: - startTarget(jdk) - - when: - client().get("/greeting").aggregate().join() - Collection traces = waitForTraces() - - then: "There is one trace" - traces.size() == 1 - - then: "There is one span (io.opentelemetry.opentelemetry-instrumentation-annotations-1.16 " + - "is not used, because instrumentation_mode=none)" - getSpanStream(traces).count() == 1 - - then: "explicitly set attribute is present" - hasResourceAttribute(traces, "service.name", "declarative-config-smoke-test") - - then: "explicitly set container detector is used" - findResourceAttribute(traces, "container.id").findAny().isPresent() - - then: "explicitly set container process detector is used" - findResourceAttribute(traces, "process.executable.path").findAny().isPresent() - - then: "explicitly set container host detector is used" - findResourceAttribute(traces, "host.name").findAny().isPresent() - - then: "distro detector is added by customizer" - hasResourceAttribute(traces, "telemetry.distro.name", "opentelemetry-javaagent") - - cleanup: - stopTarget() - - where: - jdk << [8, 11, 17] - } -} diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/IbmHttpsUrlConnectionTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/IbmHttpsUrlConnectionTest.groovy index e9e5f2c89f8a..cfba01a7d654 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/IbmHttpsUrlConnectionTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/IbmHttpsUrlConnectionTest.groovy @@ -47,7 +47,7 @@ class IbmHttpsUrlConnectionTest extends Specification { .withLogConsumer(new Slf4jLogConsumer(logger)) backend.start() - def telemetryRetriever = new TelemetryRetriever(backend.getMappedPort(BACKEND_PORT)) + def telemetryRetriever = new GroovyTestTelemetryRetriever(backend.getMappedPort(BACKEND_PORT)) GenericContainer target = new GenericContainer<>(DockerImageName.parse("ibmjava:8-sdk")) diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PropagationTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PropagationTest.groovy deleted file mode 100644 index 690fdc556c39..000000000000 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PropagationTest.groovy +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest - -import io.opentelemetry.api.trace.TraceId -import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest -import spock.lang.IgnoreIf - -import java.time.Duration - -import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers -import static java.util.stream.Collectors.toSet - -abstract class PropagationTest extends SmokeTest { - - @Override - protected String getTargetImage(String jdk) { - "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20211213.1570880324" - } - - @Override - protected TargetWaitStrategy getWaitStrategy() { - return new TargetWaitStrategy.Log(Duration.ofMinutes(1), ".*Started SpringbootApplication in.*") - } - - def "Should propagate test"() { - setup: - startTarget(11) - when: - def response = client().get("/front").aggregate().join() - Collection traces = waitForTraces() - def traceIds = getSpanStream(traces) - .map({ TraceId.fromBytes(it.getTraceId().toByteArray()) }) - .collect(toSet()) - - then: - traceIds.size() == 1 - - def traceId = traceIds.first() - - response.contentUtf8() == "${traceId};${traceId}" - - cleanup: - stopTarget() - - } - -} - -@IgnoreIf({ useWindowsContainers() }) -class DefaultPropagationTest extends PropagationTest { -} - -@IgnoreIf({ useWindowsContainers() }) -class W3CPropagationTest extends PropagationTest { - @Override - protected Map getExtraEnv() { - return ["otel.propagators": "tracecontext"] - } -} - -@IgnoreIf({ useWindowsContainers() }) -class B3PropagationTest extends PropagationTest { - @Override - protected Map getExtraEnv() { - return ["otel.propagators": "b3"] - } -} - -@IgnoreIf({ useWindowsContainers() }) -class B3MultiPropagationTest extends PropagationTest { - @Override - protected Map getExtraEnv() { - return ["otel.propagators": "b3multi"] - } -} - -@IgnoreIf({ useWindowsContainers() }) -class OtTracePropagationTest extends SmokeTest { - @Override - protected String getTargetImage(String jdk) { - "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20211213.1570880324" - } - - @Override - protected TargetWaitStrategy getWaitStrategy() { - return new TargetWaitStrategy.Log(Duration.ofMinutes(1), ".*Started SpringbootApplication in.*") - } - - // OtTracer only propagates lower half of trace ID so we have to mangle the trace IDs similar to - // the Lightstep backend. - def "Should propagate test"() { - setup: - startTarget(11) - when: - def response = client().get("/front").aggregate().join() - Collection traces = waitForTraces() - def traceIds = getSpanStream(traces) - .map({ TraceId.fromBytes(it.getTraceId().toByteArray()).substring(16) }) - .collect(toSet()) - - then: - traceIds.size() == 1 - - def traceId = traceIds.first() - - response.contentUtf8().matches(/[0-9a-f]{16}${traceId};[0]{16}${traceId}/) - - cleanup: - stopTarget() - } - - @Override - protected Map getExtraEnv() { - return ["otel.propagators": "ottrace"] - } -} - -@IgnoreIf({ useWindowsContainers() }) -class XRayPropagationTest extends PropagationTest { - @Override - protected Map getExtraEnv() { - return ["otel.propagators": "xray"] - } -} diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SdkDisabledSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SdkDisabledSmokeTest.groovy deleted file mode 100644 index 0177a6355c96..000000000000 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SdkDisabledSmokeTest.groovy +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest - -import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest -import spock.lang.IgnoreIf -import spock.lang.Unroll - -import java.util.jar.Attributes -import java.util.jar.JarFile - -import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers - -@IgnoreIf({ useWindowsContainers() }) -class SdkDisabledSmokeTest extends SmokeTest { - - protected String getTargetImage(String jdk) { - "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20211213.1570880324" - } - - @Override - protected Map getExtraEnv() { - return [ - "OTEL_SDK_DISABLED": "true" - ] - } - - @Unroll - def "noop sdk smoke test on JDK #jdk"(int jdk) { - setup: - def output = startTarget(jdk) - def currentAgentVersion = new JarFile(agentPath).getManifest().getMainAttributes().get(Attributes.Name.IMPLEMENTATION_VERSION).toString() - - when: - def response = client().get("/greeting").aggregate().join() - Collection traces = waitForTraces() - - then: "no spans are exported" - response.contentUtf8() == "Hi!" - traces.isEmpty() - - then: "javaagent logs its version on startup" - isVersionLogged(output, currentAgentVersion) - - - then: "no metrics are exported" - def metrics = new MetricsInspector(waitForMetrics()) - metrics.requests.isEmpty() - - cleanup: - stopTarget() - - where: - jdk << [8, 11, 17] - } -} diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy index d2f6f53e613f..931d7998ce71 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy @@ -28,7 +28,7 @@ abstract class SmokeTest extends Specification { protected static final TestContainerManager containerManager = createContainerManager() @Shared - private TelemetryRetriever telemetryRetriever + private GroovyTestTelemetryRetriever telemetryRetriever @Shared protected String agentPath = System.getProperty("io.opentelemetry.smoketest.agent.shadowJar.path") @@ -75,7 +75,7 @@ abstract class SmokeTest extends Specification { def setupSpec() { containerManager.startEnvironmentOnce() - telemetryRetriever = new TelemetryRetriever(containerManager.getBackendMappedPort()) + telemetryRetriever = new GroovyTestTelemetryRetriever(containerManager.getBackendMappedPort()) } def startTarget(int jdk) { diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy deleted file mode 100644 index fcff6b54ae5d..000000000000 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest - -import io.opentelemetry.api.trace.TraceId -import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest -import spock.lang.IgnoreIf -import spock.lang.Unroll - -import java.time.Duration -import java.util.jar.Attributes -import java.util.jar.JarFile - -import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers -import static java.util.stream.Collectors.toSet - -@IgnoreIf({ useWindowsContainers() }) -class SpringBootSmokeTest extends SmokeTest { - - protected String getTargetImage(String jdk) { - "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20241021.11448062567" - } - - @Override - protected boolean getSetServiceName() { - return false - } - - @Override - protected Map getExtraEnv() { - return ["OTEL_METRICS_EXPORTER": "otlp", "OTEL_RESOURCE_ATTRIBUTES": "foo=bar"] - } - - @Override - protected TargetWaitStrategy getWaitStrategy() { - return new TargetWaitStrategy.Log(Duration.ofMinutes(1), ".*Started SpringbootApplication in.*") - } - - @Unroll - def "spring boot smoke test on JDK #jdk"(int jdk) { - setup: - def output = startTarget(jdk) - def currentAgentVersion = new JarFile(agentPath).getManifest().getMainAttributes().get(Attributes.Name.IMPLEMENTATION_VERSION).toString() - - when: - def response = client().get("/greeting").aggregate().join() - Collection traces = waitForTraces() - - then: "spans are exported" - response.contentUtf8() == "Hi!" - countSpansByName(traces, 'GET /greeting') == 1 - countSpansByName(traces, 'WebController.withSpan') == 1 - - then: "thread details are recorded" - getSpanStream(traces) - .allMatch { it.attributesList.stream().map { it.key }.collect(toSet()).containsAll(["thread.id", "thread.name"]) } - - then: "correct agent version is captured in the resource" - [currentAgentVersion] as Set == findResourceAttribute(traces, "telemetry.distro.version") - .map { it.stringValue } - .collect(toSet()) - - then: "OS is captured in the resource" - findResourceAttribute(traces, "os.type") - .map { it.stringValue } - .findAny() - .isPresent() - - then: "javaagent logs its version on startup" - isVersionLogged(output, currentAgentVersion) - - then: "correct traceIds are logged via MDC instrumentation" - def loggedTraceIds = getLoggedTraceIds(output) - def spanTraceIds = getSpanStream(traces) - .map({ TraceId.fromBytes(it.getTraceId().toByteArray()) }) - .collect(toSet()) - loggedTraceIds == spanTraceIds - - then: "JVM metrics are exported" - def metrics = new MetricsInspector(waitForMetrics()) - metrics.hasMetricsNamed("jvm.memory.used") - metrics.hasMetricsNamed("jvm.memory.committed") - metrics.hasMetricsNamed("jvm.memory.limit") - metrics.hasMetricsNamed("jvm.memory.used_after_last_gc") - - then: "resource attributes are read from the environment" - def foo = findResourceAttribute(traces, "foo") - .map { it.stringValue } - .findAny() - foo.isPresent() - foo.get() == "bar" - - then: "service name is autodetected" - def serviceName = findResourceAttribute(traces, "service.name") - .map { it.stringValue } - .findAny() - serviceName.isPresent() - serviceName.get() == "otel-spring-test-app" - - then: "service version is autodetected" - def serviceVersion = findResourceAttribute(traces, "service.version") - .map { it.stringValue } - .findAny() - serviceVersion.isPresent() - serviceVersion.get() == "2.10.0-alpha-SNAPSHOT" - - cleanup: - stopTarget() - - where: - jdk << [8, 11, 17, 21, 23] - } -} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/AgentDebugLoggingTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/AgentDebugLoggingTest.java index 3e881f6fb41c..3e77810bfb1e 100644 --- a/smoke-tests/src/test/java/io/opentelemetry/smoketest/AgentDebugLoggingTest.java +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/AgentDebugLoggingTest.java @@ -27,7 +27,7 @@ protected TargetWaitStrategy getWaitStrategy() { @DisplayName("verifies that debug logging is working by checking for a debug log on startup") @Test - void verifyLogging() throws Exception { - withTarget(8, () -> {}); + void verifyLogging() { + startTarget(8); } } diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.java new file mode 100644 index 000000000000..608ab18d2763 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.java @@ -0,0 +1,80 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.semconv.ServiceAttributes; +import io.opentelemetry.semconv.incubating.ContainerIncubatingAttributes; +import io.opentelemetry.semconv.incubating.HostIncubatingAttributes; +import io.opentelemetry.semconv.incubating.ProcessIncubatingAttributes; +import io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class DeclarativeConfigurationSmokeTest extends JavaSmokeTest { + @Override + protected String getTargetImage(String jdk) { + return "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk" + + jdk + + "-20241021.11448062567"; + } + + @Override + protected List getExtraResources() { + return List.of(ResourceMapping.of("declarative-config.yaml", "/declarative-config.yaml")); + } + + @Override + protected Map getExtraEnv() { + return Map.of("OTEL_EXPERIMENTAL_CONFIG_FILE", "declarative-config.yaml"); + } + + @Override + protected TargetWaitStrategy getWaitStrategy() { + return new TargetWaitStrategy.Log( + Duration.ofMinutes(1), ".*Started SpringbootApplication in.*"); + } + + @ParameterizedTest + @ValueSource(ints = {8, 11, 17}) + void springBootSmokeTest(int jdk) { + startTarget(jdk); + + client().get("/greeting").aggregate().join(); + + // There is one span (io.opentelemetry.opentelemetry-instrumentation-annotations-1.16 is + // not used, because instrumentation_mode=none) + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasResourceSatisfying( + resource -> + resource + .hasAttribute( + ServiceAttributes.SERVICE_NAME, "declarative-config-smoke-test") + .hasAttribute( + satisfies( + ContainerIncubatingAttributes.CONTAINER_ID, + v -> v.isNotBlank())) + .hasAttribute( + satisfies( + ProcessIncubatingAttributes.PROCESS_EXECUTABLE_PATH, + v -> v.isNotBlank())) + .hasAttribute( + satisfies( + HostIncubatingAttributes.HOST_NAME, v -> v.isNotBlank())) + .hasAttribute( + TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME, + "opentelemetry-javaagent")))); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/JavaSmokeTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/JavaSmokeTest.java index 9c4f23053447..88ebff9a2e72 100644 --- a/smoke-tests/src/test/java/io/opentelemetry/smoketest/JavaSmokeTest.java +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/JavaSmokeTest.java @@ -7,26 +7,39 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static java.util.stream.Collectors.toSet; +import static org.assertj.core.api.Assertions.assertThat; -import io.opentelemetry.sdk.logs.data.LogRecordData; -import io.opentelemetry.sdk.metrics.data.MetricData; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.smoketest.windows.WindowsTestContainerManager; import io.opentelemetry.testing.internal.armeria.client.WebClient; -import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Consumer; -import org.junit.jupiter.api.BeforeEach; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.ToStringConsumer; -public abstract class JavaSmokeTest { +public abstract class JavaSmokeTest implements TelemetryRetrieverProvider { + + @RegisterExtension + public static final InstrumentationExtension testing = SmokeTestInstrumentationExtension.create(); + + private static final Pattern TRACE_ID_PATTERN = + Pattern.compile(".*trace_id=(?[a-zA-Z0-9]+).*"); protected static final TestContainerManager containerManager = createContainerManager(); - private JavaTelemetryRetriever telemetryRetriever; + private static TelemetryRetriever telemetryRetriever; protected String agentPath = System.getProperty("io.opentelemetry.smoketest.agent.shadowJar.path"); + @RegisterExtension static final AutoCleanupExtension autoCleanup = AutoCleanupExtension.create(); + protected WebClient client() { return WebClient.of("h1c://localhost:" + containerManager.getTargetMappedPort(8080)); } @@ -59,27 +72,20 @@ protected List getExtraPorts() { return emptyList(); } - @BeforeEach - void setUp() { + @BeforeAll + static void setUp() { containerManager.startEnvironmentOnce(); - telemetryRetriever = new JavaTelemetryRetriever(containerManager.getBackendMappedPort()); - } - - public void withTarget(int jdk, TargetRunner runner) throws Exception { - startTarget(jdk); - try { - runner.runInTarget(); - } finally { - stopTarget(); - } + telemetryRetriever = new TelemetryRetriever(containerManager.getBackendMappedPort()); } - public Consumer startTarget(int jdk) { + protected Consumer startTarget(int jdk) { return startTarget(String.valueOf(jdk), null, false); } - public Consumer startTarget(String jdk, String serverVersion, boolean windows) { + protected Consumer startTarget(String jdk, String serverVersion, boolean windows) { String targetImage = getTargetImage(jdk, serverVersion, windows); + autoCleanup.deferCleanup(() -> containerManager.stopTarget()); + return containerManager.startTarget( targetImage, agentPath, @@ -106,25 +112,24 @@ protected String[] getCommand() { return null; } - public void cleanup() { - telemetryRetriever.clearTelemetry(); - } - - public void stopTarget() { - containerManager.stopTarget(); - cleanup(); + protected static void assertVersionLogged(Consumer output, String version) { + assertThat( + logLines(output) + .anyMatch(l -> l.contains("opentelemetry-javaagent - version: " + version))) + .isTrue(); } - protected List waitForTraces() { - return telemetryRetriever.waitForTraces(); + private static Stream logLines(Consumer output) { + return ((ToStringConsumer) output).toUtf8String().lines(); } - protected Collection waitForMetrics() { - return telemetryRetriever.waitForMetrics(); + protected static Set getLoggedTraceIds(Consumer output) { + return logLines(output).flatMap(JavaSmokeTest::findTraceId).collect(toSet()); } - protected Collection waitForLogs() { - return telemetryRetriever.waitForLogs(); + private static Stream findTraceId(String log) { + var m = TRACE_ID_PATTERN.matcher(log); + return m.matches() ? Stream.of(m.group("traceId")) : Stream.empty(); } private static TestContainerManager createContainerManager() { @@ -132,4 +137,9 @@ private static TestContainerManager createContainerManager() { ? new WindowsTestContainerManager() : new LinuxTestContainerManager(); } + + @Override + public TelemetryRetriever getTelemetryRetriever() { + return telemetryRetriever; + } } diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/LogsSmokeTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/LogsSmokeTest.java index c49fb4311bf8..57233a9a8d0c 100644 --- a/smoke-tests/src/test/java/io/opentelemetry/smoketest/LogsSmokeTest.java +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/LogsSmokeTest.java @@ -31,14 +31,11 @@ protected TargetWaitStrategy getWaitStrategy() { @ParameterizedTest @ValueSource(ints = {8, 11, 17}) - void shouldExportLogs(int jdk) throws Exception { - withTarget( - jdk, - () -> { - client().get("/greeting").aggregate().join(); - Collection logs = waitForLogs(); + void shouldExportLogs(int jdk) { + startTarget(jdk); + client().get("/greeting").aggregate().join(); + Collection logs = testing.logRecords(); - assertThat(logs).isNotEmpty(); - }); + assertThat(logs).isNotEmpty(); } } diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/QuarkusSmokeTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/QuarkusSmokeTest.java index c2ab2a37f693..9f7d3ea00946 100644 --- a/smoke-tests/src/test/java/io/opentelemetry/smoketest/QuarkusSmokeTest.java +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/QuarkusSmokeTest.java @@ -5,8 +5,6 @@ package io.opentelemetry.smoketest; -import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat; - import io.opentelemetry.semconv.ServiceAttributes; import io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes; import java.time.Duration; @@ -38,36 +36,30 @@ protected boolean getSetServiceName() { @ParameterizedTest @ValueSource(ints = {17, 21, 23}) // Quarkus 3.7+ requires Java 17+ void quarkusSmokeTest(int jdk) throws Exception { - withTarget( - jdk, - () -> { - String currentAgentVersion; - try (JarFile agentJar = new JarFile(agentPath)) { - currentAgentVersion = - agentJar - .getManifest() - .getMainAttributes() - .getValue(Attributes.Name.IMPLEMENTATION_VERSION); - } + startTarget(jdk); + String currentAgentVersion; + try (JarFile agentJar = new JarFile(agentPath)) { + currentAgentVersion = + agentJar + .getManifest() + .getMainAttributes() + .getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } - client().get("/hello").aggregate().join(); + client().get("/hello").aggregate().join(); - assertThat(waitForTraces()) - .hasTracesSatisfyingExactly( - trace -> - trace.hasSpansSatisfyingExactly( - span -> - span.hasName("GET /hello") - .hasResourceSatisfying( - resource -> { - resource - .hasAttribute( - TelemetryIncubatingAttributes - .TELEMETRY_DISTRO_VERSION, - currentAgentVersion) - .hasAttribute( - ServiceAttributes.SERVICE_NAME, "quarkus"); - }))); - }); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET /hello") + .hasResourceSatisfying( + resource -> { + resource + .hasAttribute( + TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION, + currentAgentVersion) + .hasAttribute(ServiceAttributes.SERVICE_NAME, "quarkus"); + }))); } } diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/SdkDisabledSmokeTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/SdkDisabledSmokeTest.java new file mode 100644 index 000000000000..41b66d60f333 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/SdkDisabledSmokeTest.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import java.util.Map; +import java.util.function.Consumer; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.output.OutputFrame; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class SdkDisabledSmokeTest extends JavaSmokeTest { + @Override + protected String getTargetImage(String jdk) { + return "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk" + + jdk + + "-20211213.1570880324"; + } + + @Override + protected Map getExtraEnv() { + return Map.of("OTEL_SDK_DISABLED", "true"); + } + + @ParameterizedTest + @ValueSource(ints = {8, 11, 17}) + void noopSdkSmokeTest(int jdk) throws Exception { + Consumer output = startTarget(jdk); + String currentAgentVersion = + new JarFile(agentPath) + .getManifest() + .getMainAttributes() + .get(Attributes.Name.IMPLEMENTATION_VERSION) + .toString(); + + assertThat(client().get("/greeting").aggregate().join().contentUtf8()).isEqualTo("Hi!"); + assertThat(testing.spans()).isEmpty(); + assertVersionLogged(output, currentAgentVersion); + assertThat(testing.spans()).isEmpty(); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/SecurityManagerSmokeTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/SecurityManagerSmokeTest.java index 216255628f65..1b9a0070775b 100644 --- a/smoke-tests/src/test/java/io/opentelemetry/smoketest/SecurityManagerSmokeTest.java +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/SecurityManagerSmokeTest.java @@ -5,8 +5,6 @@ package io.opentelemetry.smoketest; -import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat; - import java.util.Collections; import java.util.Map; import org.junit.jupiter.api.condition.DisabledIf; @@ -15,6 +13,7 @@ @DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") class SecurityManagerSmokeTest extends JavaSmokeTest { + @Override protected String getTargetImage(String jdk) { return "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-security-manager:jdk" @@ -30,12 +29,9 @@ protected Map getExtraEnv() { @ParameterizedTest @ValueSource(ints = {8, 11, 17, 21, 23}) - void securityManagerSmokeTest(int jdk) throws Exception { - withTarget( - jdk, - () -> - assertThat(waitForTraces()) - .hasTracesSatisfyingExactly( - trace -> trace.hasSpansSatisfyingExactly(span -> span.hasName("test")))); + void securityManagerSmokeTest(int jdk) { + startTarget(jdk); + testing.waitAndAssertTraces( + trace -> trace.hasSpansSatisfyingExactly(span -> span.hasName("test"))); } } diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/SpringBootSmokeTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/SpringBootSmokeTest.java new file mode 100644 index 000000000000..0029c10d0765 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/SpringBootSmokeTest.java @@ -0,0 +1,114 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.semconv.ServiceAttributes; +import io.opentelemetry.semconv.incubating.OsIncubatingAttributes; +import io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes; +import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.stream.Collectors; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.output.OutputFrame; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class SpringBootSmokeTest extends JavaSmokeTest { + @Override + protected String getTargetImage(String jdk) { + return "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk" + + jdk + + "-20241021.11448062567"; + } + + @Override + protected boolean getSetServiceName() { + return false; + } + + @Override + protected Map getExtraEnv() { + return Map.of("OTEL_METRICS_EXPORTER", "otlp", "OTEL_RESOURCE_ATTRIBUTES", "foo=bar"); + } + + @Override + protected TargetWaitStrategy getWaitStrategy() { + return new TargetWaitStrategy.Log( + Duration.ofMinutes(1), ".*Started SpringbootApplication in.*"); + } + + @ParameterizedTest + @ValueSource(ints = {8, 11, 17, 21, 23}) + void springBootSmokeTest(int jdk) throws Exception { + Consumer output = startTarget(jdk); + String currentAgentVersion; + try (JarFile agentJar = new JarFile(agentPath)) { + currentAgentVersion = + agentJar + .getManifest() + .getMainAttributes() + .getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } + + var response = client().get("/greeting").aggregate().join(); + assertThat(response.contentUtf8()).isEqualTo("Hi!"); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET /greeting") + .hasAttribute( + satisfies(ThreadIncubatingAttributes.THREAD_ID, a -> a.isNotNull())) + .hasAttribute( + satisfies(ThreadIncubatingAttributes.THREAD_NAME, a -> a.isNotBlank())) + .hasResourceSatisfying( + resource -> + resource + .hasAttribute( + TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION, + currentAgentVersion) + .hasAttribute( + satisfies( + OsIncubatingAttributes.OS_TYPE, a -> a.isNotNull())) + .hasAttribute(AttributeKey.stringKey("foo"), "bar") + .hasAttribute( + ServiceAttributes.SERVICE_NAME, "otel-spring-test-app") + .hasAttribute( + ServiceAttributes.SERVICE_VERSION, + "2.10.0-alpha-SNAPSHOT")), + span -> span.hasName("WebController.withSpan"))); + + // Check agent version is logged on startup + assertVersionLogged(output, currentAgentVersion); + + // Check trace IDs are logged via MDC instrumentation + Set loggedTraceIds = getLoggedTraceIds(output); + List spans = testing.spans(); + Set spanTraceIds = spans.stream().map(SpanData::getTraceId).collect(Collectors.toSet()); + assertThat(loggedTraceIds).isEqualTo(spanTraceIds); + + // Check JVM metrics are exported + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-telemetry-java8", + metric -> metric.hasName("jvm.memory.used"), + metric -> metric.hasName("jvm.memory.committed"), + metric -> metric.hasName("jvm.memory.limit"), + metric -> metric.hasName("jvm.memory.used_after_last_gc")); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/B3MultiPropagationTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/B3MultiPropagationTest.java new file mode 100644 index 000000000000..db814482335c --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/B3MultiPropagationTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest.propagation; + +import java.util.Map; +import org.junit.jupiter.api.condition.DisabledIf; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class B3MultiPropagationTest extends PropagationTest { + @Override + protected Map getExtraEnv() { + return Map.of("otel.propagators", "b3multi"); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/B3PropagationTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/B3PropagationTest.java new file mode 100644 index 000000000000..73fc15912935 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/B3PropagationTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest.propagation; + +import java.util.Map; +import org.junit.jupiter.api.condition.DisabledIf; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class B3PropagationTest extends PropagationTest { + @Override + protected Map getExtraEnv() { + return Map.of("otel.propagators", "b3"); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/DefaultPropagationTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/DefaultPropagationTest.java new file mode 100644 index 000000000000..a765f0a43006 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/DefaultPropagationTest.java @@ -0,0 +1,11 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest.propagation; + +import org.junit.jupiter.api.condition.DisabledIf; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class DefaultPropagationTest extends PropagationTest {} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/OtTracePropagationTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/OtTracePropagationTest.java new file mode 100644 index 000000000000..698f5980b0a6 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/OtTracePropagationTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest.propagation; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.smoketest.JavaSmokeTest; +import io.opentelemetry.smoketest.TargetWaitStrategy; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import java.time.Duration; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class OtTracePropagationTest extends JavaSmokeTest { + @Override + protected String getTargetImage(String jdk) { + return "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk" + + jdk + + "-20211213.1570880324"; + } + + @Override + protected TargetWaitStrategy getWaitStrategy() { + return new TargetWaitStrategy.Log( + Duration.ofMinutes(1), ".*Started SpringbootApplication in.*"); + } + + @Test + public void shouldPropagate() { + startTarget(11); + AggregatedHttpResponse response = client().get("/front").aggregate().join(); + var traceId = testing.waitForTraces(1).get(0).get(0).getTraceId().substring(16); + + assertThat(response.contentUtf8()).matches("[0-9a-f]{16}" + traceId + ";[0]{16}" + traceId); + } + + @Override + protected Map getExtraEnv() { + return Map.of("otel.propagators", "ottrace"); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/PropagationTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/PropagationTest.java new file mode 100644 index 000000000000..ab4ef67aa6e9 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/PropagationTest.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest.propagation; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.smoketest.JavaSmokeTest; +import io.opentelemetry.smoketest.TargetWaitStrategy; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import java.time.Duration; +import org.junit.jupiter.api.Test; + +public abstract class PropagationTest extends JavaSmokeTest { + + @Override + protected String getTargetImage(String jdk) { + return "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk" + + jdk + + "-20211213.1570880324"; + } + + @Override + protected TargetWaitStrategy getWaitStrategy() { + return new TargetWaitStrategy.Log( + Duration.ofMinutes(1), ".*Started SpringbootApplication in.*"); + } + + @Test + public void shouldPropagate() { + startTarget(11); + AggregatedHttpResponse response = client().get("/front").aggregate().join(); + + var traceId = testing.waitForTraces(1).get(0).get(0).getTraceId(); + assertThat(response.contentUtf8()).isEqualTo(traceId + ";" + traceId); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/W3CPropagationTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/W3CPropagationTest.java new file mode 100644 index 000000000000..5bb4b70da593 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/W3CPropagationTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest.propagation; + +import java.util.Map; +import org.junit.jupiter.api.condition.DisabledIf; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class W3CPropagationTest extends PropagationTest { + @Override + protected Map getExtraEnv() { + return Map.of("otel.propagators", "tracecontext"); + } +} diff --git a/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/XRayPropagationTest.java b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/XRayPropagationTest.java new file mode 100644 index 000000000000..71d2968457d5 --- /dev/null +++ b/smoke-tests/src/test/java/io/opentelemetry/smoketest/propagation/XRayPropagationTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest.propagation; + +import java.util.Map; +import org.junit.jupiter.api.condition.DisabledIf; + +@DisabledIf("io.opentelemetry.smoketest.TestContainerManager#useWindowsContainers") +class XRayPropagationTest extends PropagationTest { + @Override + protected Map getExtraEnv() { + return Map.of("otel.propagators", "xray"); + } +} diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java index 830b3fcfdcee..9780924c7e6a 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java @@ -93,7 +93,7 @@ public static void assertScopeVersion(List> traces) { for (List trace : traces) { for (SpanData span : trace) { InstrumentationScopeInfo scopeInfo = span.getInstrumentationScopeInfo(); - if (!scopeInfo.getName().equals("test")) { + if (!scopeInfo.getName().startsWith("test")) { assertThat(scopeInfo.getVersion()) .as( "Instrumentation version of module %s was empty; make sure that the "