From 3246075d86dbab486d2c2a4caefaeea54c05a8c0 Mon Sep 17 00:00:00 2001 From: John Watson Date: Tue, 30 Mar 2021 08:30:15 -0700 Subject: [PATCH] Create some integration tests for the various B3 propagator options. (#3073) * Create some integration tests for the various B3 propagator options. * update tracer/span names * use the right SDK in the right places * add some explanatory comments around the numbers of expected spans * clean up the test --- integration-tests/build.gradle.kts | 5 + .../B3PropagationIntegrationTest.java | 254 ++++++++++++++++++ .../JaegerExporterIntegrationTest.java | 16 +- 3 files changed, 261 insertions(+), 14 deletions(-) create mode 100644 integration-tests/src/test/java/io/opentelemetry/B3PropagationIntegrationTest.java diff --git a/integration-tests/build.gradle.kts b/integration-tests/build.gradle.kts index 637f0baf05c..7880d24fb8b 100644 --- a/integration-tests/build.gradle.kts +++ b/integration-tests/build.gradle.kts @@ -14,7 +14,12 @@ dependencies { implementation("com.google.protobuf:protobuf-java") implementation("io.grpc:grpc-netty-shaded") + testImplementation(project(":extensions:trace-propagators")) + testImplementation(project(":sdk:testing")) + testImplementation("org.junit.jupiter:junit-jupiter-params") testImplementation("com.fasterxml.jackson.core:jackson-databind") testImplementation("org.testcontainers:junit-jupiter") testImplementation("com.squareup.okhttp3:okhttp") + testImplementation("org.slf4j:slf4j-simple") + testImplementation("com.sparkjava:spark-core") } diff --git a/integration-tests/src/test/java/io/opentelemetry/B3PropagationIntegrationTest.java b/integration-tests/src/test/java/io/opentelemetry/B3PropagationIntegrationTest.java new file mode 100644 index 00000000000..2b6ab2266bd --- /dev/null +++ b/integration-tests/src/test/java/io/opentelemetry/B3PropagationIntegrationTest.java @@ -0,0 +1,254 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.extension.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import java.io.IOException; +import java.util.List; +import java.util.Random; +import java.util.stream.Stream; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.testcontainers.shaded.okhttp3.Call; +import org.testcontainers.shaded.okhttp3.OkHttpClient; +import org.testcontainers.shaded.okhttp3.Request; +import org.testcontainers.shaded.okhttp3.Response; +import org.testcontainers.shaded.okhttp3.ResponseBody; +import spark.Service; + +/** Integration tests for the B3 propagators, in various configurations. */ +public class B3PropagationIntegrationTest { + + private static final int server1Port = new Random().nextInt(40000) + 1024; + private static final int server2Port = new Random().nextInt(40000) + 1024; + private static final InMemorySpanExporter spanExporter = InMemorySpanExporter.create(); + + private Service server1; + private Service server2; + + private void setup(TextMapPropagator propagator) { + setupServer1(propagator); + setupServer2(propagator); + } + + @AfterEach + void shutdown() { + if (server2 != null) { + server2.stop(); + } + if (server1 != null) { + server1.stop(); + } + spanExporter.reset(); + } + + @ParameterizedTest + @ArgumentsSource(PropagatorArgumentSupplier.class) + void propagation(TextMapPropagator propagator) throws IOException { + setup(propagator); + OpenTelemetrySdk clientSdk = setupClient(propagator); + + OkHttpClient httpClient = createPropagatingClient(clientSdk); + + Span span = clientSdk.getTracer("testTracer").spanBuilder("clientSpan").startSpan(); + try (Scope ignored = span.makeCurrent()) { + Call call = + httpClient.newCall( + new Request.Builder().get().url("http://localhost:" + server1Port + "/test").build()); + + try (Response r = call.execute()) { + ResponseBody body = r.body(); + assertThat(body.string()).isEqualTo("OK"); + } + } finally { + span.end(); + } + + List finishedSpanItems = spanExporter.getFinishedSpanItems(); + // 3 spans, one from the client, and one from each of the servers. + assertThat(finishedSpanItems).hasSize(3); + String traceId = finishedSpanItems.get(0).getTraceId(); + + assertThat(finishedSpanItems) + .allSatisfy(spanData -> assertThat(spanData.getTraceId()).isEqualTo(traceId)); + } + + @ParameterizedTest + @ArgumentsSource(PropagatorArgumentSupplier.class) + void noClientTracing(TextMapPropagator propagator) throws IOException { + setup(propagator); + OkHttpClient httpClient = new OkHttpClient(); + + Call call = + httpClient.newCall( + new Request.Builder().get().url("http://localhost:" + server1Port + "/test").build()); + + try (Response r = call.execute()) { + ResponseBody body = r.body(); + assertThat(body.string()).isEqualTo("OK"); + } + + List finishedSpanItems = spanExporter.getFinishedSpanItems(); + // 2 spans, one from each of the servers + assertThat(finishedSpanItems).hasSize(2); + String traceId = finishedSpanItems.get(0).getTraceId(); + + assertThat(finishedSpanItems) + .allSatisfy(spanData -> assertThat(spanData.getTraceId()).isEqualTo(traceId)); + } + + private static OkHttpClient createPropagatingClient(OpenTelemetrySdk sdk) { + return new OkHttpClient.Builder() + .addNetworkInterceptor( + chain -> { + Request request = chain.request(); + + Request.Builder requestBuilder = request.newBuilder(); + + sdk.getPropagators() + .getTextMapPropagator() + .inject(Context.current(), requestBuilder, Request.Builder::header); + + request = requestBuilder.build(); + return chain.proceed(request); + }) + .build(); + } + + private static OpenTelemetrySdk setupClient(TextMapPropagator propagator) { + OpenTelemetrySdk clientSdk = + OpenTelemetrySdk.builder() + .setPropagators(ContextPropagators.create(propagator)) + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(spanExporter)) + .build()) + .build(); + return clientSdk; + } + + private void setupServer1(TextMapPropagator propagator) { + OpenTelemetrySdk serverSdk = + OpenTelemetrySdk.builder() + .setPropagators(ContextPropagators.create(propagator)) + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(spanExporter)) + .build()) + .build(); + + OkHttpClient serverClient = createPropagatingClient(serverSdk); + server1 = Service.ignite().port(server1Port); + server1.get( + "/test", + (request, response) -> { + Context incomingContext = extract(request, serverSdk); + + Span span = + serverSdk + .getTracer("server1Tracer") + .spanBuilder("server1Span") + .setParent(incomingContext) + .startSpan(); + try (Scope ignored = span.makeCurrent()) { + Call call = + serverClient.newCall( + new Request.Builder() + .get() + .url("http://localhost:" + server2Port + "/test2") + .build()); + + try (Response r = call.execute()) { + assertThat(r.code()).isEqualTo(200); + } + + return "OK"; + } finally { + span.end(); + } + }); + server1.awaitInitialization(); + } + + private void setupServer2(TextMapPropagator propagator) { + OpenTelemetrySdk serverSdk = + OpenTelemetrySdk.builder() + .setPropagators(ContextPropagators.create(propagator)) + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(spanExporter)) + .build()) + .build(); + + server2 = Service.ignite().port(server2Port); + server2.get( + "/test2", + (request, response) -> { + Context incomingContext = extract(request, serverSdk); + + Span span = + serverSdk + .getTracer("server2Tracer") + .spanBuilder("server2Span") + .setParent(incomingContext) + .startSpan(); + try (Scope ignored = span.makeCurrent()) { + return "OK"; + } finally { + span.end(); + } + }); + server2.awaitInitialization(); + } + + private static Context extract(spark.Request request, OpenTelemetrySdk sdk) { + return sdk.getPropagators() + .getTextMapPropagator() + .extract( + Context.root(), + request, + new TextMapGetter() { + @Override + public Iterable keys(spark.Request carrier) { + return carrier.headers(); + } + + @Nullable + @Override + public String get(@Nullable spark.Request carrier, String key) { + return carrier.headers(key); + } + }); + } + + public static class PropagatorArgumentSupplier implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of(B3Propagator.injectingMultiHeaders()), + Arguments.of(B3Propagator.injectingSingleHeader())); + } + } +} diff --git a/integration-tests/src/test/java/io/opentelemetry/JaegerExporterIntegrationTest.java b/integration-tests/src/test/java/io/opentelemetry/JaegerExporterIntegrationTest.java index fba6d8886a6..ad2ebdead64 100644 --- a/integration-tests/src/test/java/io/opentelemetry/JaegerExporterIntegrationTest.java +++ b/integration-tests/src/test/java/io/opentelemetry/JaegerExporterIntegrationTest.java @@ -25,22 +25,12 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -/** - * Integration test to verify that OpenTelemetry artefacts run in JRE 7. - * - *

An executable JAR with dependencies containing {@link SendTraceToJaeger} is built prior to - * executing the tests. - * - *

A Jaeger-all-in-one container is started, then an Alpine JRE 7 container is started with the - * executable JAR added to it and executed which will send a trace to the Jaeger instance. The test - * verifies that the trace is received by Jaeger. - */ +/** Integration test to verify that the Jaeger GRPC exporter works. */ @Testcontainers(disabledWithoutDocker = true) class JaegerExporterIntegrationTest { @@ -53,8 +43,6 @@ class JaegerExporterIntegrationTest { private static final String SERVICE_NAME = "integration test"; private static final String JAEGER_URL = "http://localhost"; - private static final Network network = Network.SHARED; - @Container public static GenericContainer jaegerContainer = new GenericContainer<>( @@ -101,7 +89,7 @@ private static Boolean assertJaegerHasTheTrace() { } private static OpenTelemetry initOpenTelemetry(String ip, int port) { - // Create a channel towards Jaeger end point + // Create a channel for the Jaeger endpoint ManagedChannel jaegerChannel = ManagedChannelBuilder.forAddress(ip, port).usePlaintext().build(); // Export traces to Jaeger