From 9515df1d606320abc542460732d6d99d3a339aea Mon Sep 17 00:00:00 2001 From: Jonathan Lee Date: Thu, 19 Oct 2023 16:43:22 -0700 Subject: [PATCH] add e2e tests for w3c and xray trace id values with xray exporter --- adot-testbed/app/build.gradle.kts | 10 + .../adot/testbed/TraceIdsWithXRayTests.java | 190 ++++++++++++++++++ .../configurations/config-xrayExporter.yaml | 14 ++ 3 files changed, 214 insertions(+) create mode 100644 adot-testbed/app/src/test/java/software/amazon/adot/testbed/TraceIdsWithXRayTests.java create mode 100644 adot-testbed/app/src/test/resources/configurations/config-xrayExporter.yaml diff --git a/adot-testbed/app/build.gradle.kts b/adot-testbed/app/build.gradle.kts index c4f55a89f..3ec0c106f 100644 --- a/adot-testbed/app/build.gradle.kts +++ b/adot-testbed/app/build.gradle.kts @@ -27,6 +27,16 @@ dependencies { testImplementation("com.github.rholder:guava-retrying:2.0.0") testImplementation("org.assertj:assertj-core:3.24.2") testImplementation("com.fasterxml.jackson.core:jackson-databind:2.13.0") + + // Trace ID (W3C & XRay) tests with XRay Exporter + api(platform("io.opentelemetry:opentelemetry-bom:1.30.0")) + testImplementation("io.opentelemetry:opentelemetry-exporter-otlp") + testImplementation("io.opentelemetry.contrib:opentelemetry-aws-xray:1.30.0") + testImplementation("io.opentelemetry:opentelemetry-sdk:1.30.0"); + testImplementation("io.opentelemetry:opentelemetry-sdk-metrics:1.30.0"); + testImplementation("io.opentelemetry:opentelemetry-exporter-logging:1.30.0"); + testImplementation("io.opentelemetry:opentelemetry-semconv:1.30.0-alpha"); + testImplementation("software.amazon.awssdk:xray") } // Apply a specific Java toolchain to ease working on different environments. diff --git a/adot-testbed/app/src/test/java/software/amazon/adot/testbed/TraceIdsWithXRayTests.java b/adot-testbed/app/src/test/java/software/amazon/adot/testbed/TraceIdsWithXRayTests.java new file mode 100644 index 000000000..1179d2c6f --- /dev/null +++ b/adot-testbed/app/src/test/java/software/amazon/adot/testbed/TraceIdsWithXRayTests.java @@ -0,0 +1,190 @@ +package software.amazon.adot.testbed; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Files; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.ArrayList; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.TestInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.InternetProtocol; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.MountableFile; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.contrib.awsxray.AwsXrayIdGenerator; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; + +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.xray.XRayClient; +import software.amazon.awssdk.services.xray.model.BatchGetTracesRequest; +import software.amazon.awssdk.services.xray.model.BatchGetTracesResponse; + +@Testcontainers(disabledWithoutDocker = true) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TraceIdsWithXRayTests { + private static final String TEST_IMAGE = System.getenv("TEST_IMAGE") != null && !System.getenv("TEST_IMAGE").isEmpty() + ? System.getenv("TEST_IMAGE") + : "public.ecr.aws/aws-observability/aws-otel-collector:latest"; + private final Logger collectorLogger = LoggerFactory.getLogger("collector"); + private GenericContainer collector; + + private GenericContainer createAndStartCollector(String configFilePath) throws IOException { + + // Create an environment variable map + Map envVariables = new HashMap<>(); + // Set credentials + envVariables.put("AWS_REGION", System.getenv("AWS_REGION")); + envVariables.put("AWS_ACCESS_KEY_ID", System.getenv("AWS_ACCESS_KEY_ID")); + envVariables.put("AWS_SECRET_ACCESS_KEY", System.getenv("AWS_SECRET_ACCESS_KEY")); + // Check if AWS_SESSION_TOKEN is not null before adding it + if (System.getenv("AWS_SESSION_TOKEN") != null) { + envVariables.put("AWS_SESSION_TOKEN", System.getenv("AWS_SESSION_TOKEN")); + } + + var collector = new FixedHostPortGenericContainer<>(TEST_IMAGE) + .withCopyFileToContainer(MountableFile.forClasspathResource(configFilePath), "/etc/collector/config.yaml") + .withFixedExposedPort(4317, 4317, InternetProtocol.TCP) + .withLogConsumer(new Slf4jLogConsumer(collectorLogger)) + .waitingFor(Wait.forLogMessage(".*Everything is ready. Begin running and processing data.*", 1)) + .withEnv(envVariables) + .withCommand("--config", "/etc/collector/config.yaml", "AWS_REGION=us-west-2"); + + collector.start(); + return collector; + } + + @Test + void testXRayTraceIdSendToXRay() throws Exception { + List traceIds = createTraces(true); + validateTracesInXRay(traceIds); + } + + @Test + void testW3CTraceIdSendToXRay() throws Exception { + List traceIds = createTraces(false); + validateTracesInXRay(traceIds); + } + + List createTraces(boolean useXRayIDGenerator) throws Exception { + collector = createAndStartCollector("/configurations/config-xrayExporter.yaml"); + + OpenTelemetry otel = openTelemetry(useXRayIDGenerator); + Tracer tracer = otel.getTracer("adot-trace-test"); + + Attributes attributes = Attributes.of( + AttributeKey.stringKey("http.method"), "GET", + AttributeKey.stringKey("http.url"), "http://localhost:8080/randomEndpoint" + ); + + int numOfTraces = 5; + List traceIds = new ArrayList(); + for (int count = 0; count < numOfTraces; count++) { + Span span = tracer.spanBuilder("trace-id-test") + .setSpanKind(SpanKind.SERVER) + .setAllAttributes(attributes) + .startSpan(); + //Format trace IDs to XRay format ({16 bytes} --> {1-<4-bytes>-<12-bytes>}) + String id = new StringBuilder(span.getSpanContext().getTraceId()).insert(8, "-").insert(0, "1-").toString(); + traceIds.add(id); + span.end(); + } + + // Takes a few seconds for traces to appear in XRay + Thread.sleep(15000); + + assertThat(traceIds).hasSize(numOfTraces); + return traceIds; + } + + void validateTracesInXRay(List traceIds) { + Region region = Region.of(System.getenv("AWS_REGION")); + XRayClient xray = XRayClient.builder() + .region(region) + .build(); + BatchGetTracesResponse tracesResponse = xray.batchGetTraces(BatchGetTracesRequest.builder() + .traceIds(traceIds) + .build()); + + // Assertions + Set traceIdsSet = new HashSet(traceIds); + assertThat(tracesResponse.traces()).hasSize(traceIds.size()); + tracesResponse.traces().forEach(trace -> { + assertThat(traceIdsSet.contains(trace.id())).isTrue(); + }); + } + + @AfterEach + public void resetGlobalOpenTelemetry() { + // Cleanup collector and otel sdk + collector.stop(); + GlobalOpenTelemetry.resetForTest(); + } + + public OpenTelemetry openTelemetry(boolean useXRayIDGenerator) { + Resource resource = Resource.getDefault().toBuilder() + .put(ResourceAttributes.SERVICE_NAME, "xray-test") + .put(ResourceAttributes.SERVICE_VERSION, "0.1.0") + .build(); + + SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() + .setSampler(Sampler.alwaysOn()) + .setResource(resource) + .build(); + + String exporter = System.getenv().getOrDefault("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"); + + SdkTracerProvider tracerProvider; + SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder() + .addSpanProcessor( + BatchSpanProcessor.builder( + OtlpGrpcSpanExporter.builder() + .setEndpoint(exporter) + .build()) + .build()) + .setResource(resource); + + if (useXRayIDGenerator) { + tracerProvider = tracerProviderBuilder + .setIdGenerator(AwsXrayIdGenerator.getInstance()) + .build(); + } else { + tracerProvider = tracerProviderBuilder.build(); + } + + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .buildAndRegisterGlobal(); + + return openTelemetry; + } +} diff --git a/adot-testbed/app/src/test/resources/configurations/config-xrayExporter.yaml b/adot-testbed/app/src/test/resources/configurations/config-xrayExporter.yaml new file mode 100644 index 000000000..88ff88152 --- /dev/null +++ b/adot-testbed/app/src/test/resources/configurations/config-xrayExporter.yaml @@ -0,0 +1,14 @@ +receivers: + otlp: + protocols: + grpc: + +exporters: + awsxray: + local_mode: true + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [awsxray] \ No newline at end of file