From dcf17f6099c899317e5f9d9b49238e2c9c8ea7a2 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Mon, 16 Dec 2024 06:50:19 -0500 Subject: [PATCH 01/14] start converting --- .../v2_2/AbstractAws2ClientCoreTest.groovy | 2 +- .../v2_2/AbstractAws2ClientCoreTest.java | 301 ++++++++++++++++++ 2 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy index 9aaacb3abed3..c75fb455ad02 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy @@ -35,7 +35,7 @@ import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable @Unroll -abstract class AbstractAws2ClientCoreTest extends InstrumentationSpecification { +abstract class AbstractAws2ClientCoreTest2 extends InstrumentationSpecification { static boolean isSqsAttributeInjectionEnabled() { // See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor return ConfigPropertiesUtil.getBoolean("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java new file mode 100644 index 000000000000..9536a022be9a --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -0,0 +1,301 @@ +package io.opentelemetry.instrumentation.awssdk.v2_2; + +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.testing.internal.armeria.common.HttpResponse; +import io.opentelemetry.testing.internal.armeria.common.HttpStatus; +import io.opentelemetry.testing.internal.armeria.common.MediaType; +import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension; +import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.RecordedRequest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.client.builder.SdkClientBuilder; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.model.QueryRequest; +import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Stream; + +import static com.google.common.collect.ImmutableMap.of; +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.SemanticAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; +import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; +import static org.assertj.core.api.Assertions.assertThat; + +public abstract class AbstractAws2ClientCoreTest { + protected abstract InstrumentationExtension getTesting(); + + abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); + + static boolean isSqsAttributeInjectionEnabled() { + // See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor + return ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false); + } + + static final StaticCredentialsProvider CREDENTIALS_PROVIDER = StaticCredentialsProvider + .create(AwsBasicCredentials.create("my-access-key", "my-secret-key")); + + static MockWebServerExtension server = new MockWebServerExtension(); + + @BeforeAll + static void setup() { + server.start(); + } + + @AfterAll + static void cleanup() { + server.stop(); + } + + @BeforeEach + void prepTest() { + server.beforeTestExecution(null); + } + + void configureSdkClient(SdkClientBuilder builder) { + builder.overrideConfiguration(createOverrideConfigurationBuilder().build()); + } + + static ImmutableMap createTableRequestKey = ImmutableMap.of( + "anotherKey", AttributeValue.builder().s("value").build(), + "key", AttributeValue.builder().s("value").build()); + + static ImmutableMap getItemRequestKey = ImmutableMap.of( + "keyOne", AttributeValue.builder().s("value").build(), + "keyTwo", AttributeValue.builder().s("differentValue").build()); + + static ImmutableMap putItemRequestKey = ImmutableMap.of( + "key", AttributeValue.builder().s("value").build(), + "attributeOne", AttributeValue.builder().s("one").build(), + "attributeTwo", AttributeValue.builder().s("two").build()); + + private static Stream provideArguments() { + return Stream.of( + Arguments.of("CreateTable", + (Function) c -> c.createTable(createTableRequest())), + Arguments.of("DeleteItem", + (Function) c -> c.deleteItem(DeleteItemRequest.builder() + .tableName("sometable") + .key(createTableRequestKey) + .conditionExpression("property in (:one, :two)") + .build())), + Arguments.of("DeleteItem", + (Function) c -> c.deleteTable( + DeleteTableRequest.builder().tableName("sometable").build())), + Arguments.of("GetItem", + (Function) c -> c.getItem( + GetItemRequest.builder() + .tableName("sometable") + .key(getItemRequestKey) + .attributesToGet("propertyOne", "propertyTwo") + .build())), + Arguments.of("PutItem", + (Function) c -> c.putItem( + PutItemRequest.builder() + .tableName("sometable") + .item(putItemRequestKey) + .conditionExpression("attributeOne <> :someVal") + .build())), + Arguments.of("Query", + (Function) c -> c.query( + QueryRequest.builder() + .tableName("sometable") + .select("ALL_ATTRIBUTES") + .keyConditionExpression("attribute = :aValue") + .filterExpression("anotherAttribute = :someVal") + .limit(10).build())), + Arguments.of("UpdateItem", + (Function) c -> c.updateItem( + UpdateItemRequest.builder() + .tableName("sometable") + .key( + of("keyOne", AttributeValue.builder().s("value").build(), + "keyTwo", AttributeValue.builder().s("differentValue").build())) + .conditionExpression("attributeOne <> :someVal") + .updateExpression("set attributeOne = :updateValue") + .build())) + ); + } + + @ParameterizedTest + @MethodSource("provideArguments") + void testSendDynamoDbRequestWithBuilderMockedResponse(String operation, + Function call) throws ExecutionException, InterruptedException { + DynamoDbClientBuilder builder = DynamoDbClient.builder(); + configureSdkClient(builder); + DynamoDbClient client = builder + .endpointOverride(server.httpUri()) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); + Object response = call.apply(client); + + if (response instanceof Future) { + response = ((Future) response).get(); + } + + assertThat(response).isNotNull(); + assertThat(response.getClass().getSimpleName()).startsWith(operation); + + RecordedRequest request = server.takeRequest(); + assertThat(request).isNotNull(); + assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); + assertThat(request.request().headers().get("traceparent")).isNotNull(); + + + getTesting().waitAndAssertTraces( + trace -> trace.hasSpansSatisfyingExactly( + span -> { + if (operation.equals("CreateTable")) { + assertCreateTableRequest(span); + } else if (operation.equals("Query")) { + assertQueryRequest(span); + } else { + assertDynamoDbRequest(span, operation); + } + } + ) + ); + } + + static CreateTableRequest createTableRequest() { + return CreateTableRequest.builder() + .tableName("sometable") + .globalSecondaryIndexes(Arrays.asList( + GlobalSecondaryIndex.builder() + .indexName("globalIndex") + .keySchema( + KeySchemaElement.builder() + .attributeName("attribute") + .build()) + .provisionedThroughput( + ProvisionedThroughput.builder() + .readCapacityUnits(10l) + .writeCapacityUnits(12l) + .build() + ) + .build(), + GlobalSecondaryIndex.builder() + .indexName("globalIndexSecondary") + .keySchema( + KeySchemaElement.builder() + .attributeName("attributeSecondary") + .build()) + .provisionedThroughput( + ProvisionedThroughput.builder() + .readCapacityUnits(7l) + .writeCapacityUnits(8l) + .build() + ) + .build())) + .provisionedThroughput( + ProvisionedThroughput.builder() + .readCapacityUnits(1l) + .writeCapacityUnits(1l) + .build() + ) + .build(); + } + + static SpanDataAssert assertCreateTableRequest(SpanDataAssert span) { + return span.hasName("DynamoDb.CreateTable") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(URL_FULL, server.httpUri().toString()), + equalTo(HTTP_REQUEST_METHOD, "POST"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "DynamoDb"), + equalTo(RPC_METHOD, "CreateTable"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, "UNKOWN"), + equalTo(stringKey("aws.table.name"), "sometable"), + equalTo(DB_SYSTEM, "dynamodb"), + equalTo(maybeStable(DB_OPERATION), "CreateTable"), + equalTo(stringKey("aws.dynamodb.global_secondary_indexes"), "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]"), + equalTo(stringKey("aws.dynamodb.provisioned_throughput.read_capacity_units"), "1"), + equalTo(stringKey("aws.dynamodb.provisioned_throughput.write_capacity_units"), "1")); + } + + static SpanDataAssert assertQueryRequest(SpanDataAssert span) { + return span.hasName("DynamoDb.Query") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(URL_FULL, server.httpUri().toString()), + equalTo(HTTP_REQUEST_METHOD, "POST"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "DynamoDb"), + equalTo(RPC_METHOD, "Query"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, "UNKOWN"), + equalTo(stringKey("aws.table.name"), "sometable"), + equalTo(DB_SYSTEM, "dynamodb"), + equalTo(maybeStable(DB_OPERATION), "Query"), + equalTo(stringKey("aws.dynamodb.limit"), "10"), + equalTo(stringKey("aws.dynamodb.select"), "ALL_ATTRIBUTES")); + } + + static SpanDataAssert assertDynamoDbRequest(SpanDataAssert span, String operation) { + return span.hasName("DynamoDb." + operation) + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(URL_FULL, server.httpUri().toString()), + equalTo(HTTP_REQUEST_METHOD, "POST"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "DynamoDb"), + equalTo(RPC_METHOD, operation), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, "UNKOWN"), + equalTo(stringKey("aws.table.name"), "sometable"), + equalTo(DB_SYSTEM, "dynamodb"), + equalTo(maybeStable(DB_OPERATION), operation)); + } +} From 5e9415dbc6133903c7ed773c85e5448b88def771 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Tue, 17 Dec 2024 17:25:55 -0500 Subject: [PATCH 02/14] start converting --- .../v2_2/AbstractAws2ClientCoreTest.groovy | 578 +++++++++--------- .../awssdk/v2_2/AbstractAws2ClientTest.groovy | 2 +- .../awssdk/v2_2/AbstractAws2ClientTest.java | 141 +++++ 3 files changed, 431 insertions(+), 290 deletions(-) create mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy index c75fb455ad02..52afdb74103a 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy @@ -1,289 +1,289 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awssdk.v2_2 - -import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.semconv.HttpAttributes -import io.opentelemetry.semconv.ServerAttributes -import io.opentelemetry.semconv.UrlAttributes -import io.opentelemetry.semconv.incubating.AwsIncubatingAttributes -import io.opentelemetry.semconv.incubating.DbIncubatingAttributes -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes -import io.opentelemetry.testing.internal.armeria.common.HttpResponse -import io.opentelemetry.testing.internal.armeria.common.HttpStatus -import io.opentelemetry.testing.internal.armeria.common.MediaType -import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider -import software.amazon.awssdk.core.client.builder.SdkClientBuilder -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration -import software.amazon.awssdk.regions.Region -import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient -import software.amazon.awssdk.services.dynamodb.DynamoDbClient -import software.amazon.awssdk.services.dynamodb.model.* -import spock.lang.Shared -import spock.lang.Unroll - -import java.util.concurrent.Future - -import static com.google.common.collect.ImmutableMap.of -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable - -@Unroll -abstract class AbstractAws2ClientCoreTest2 extends InstrumentationSpecification { - static boolean isSqsAttributeInjectionEnabled() { - // See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor - return ConfigPropertiesUtil.getBoolean("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false) - } - - static final StaticCredentialsProvider CREDENTIALS_PROVIDER = StaticCredentialsProvider - .create(AwsBasicCredentials.create("my-access-key", "my-secret-key")) - - @Shared - def server = new MockWebServerExtension() - - def setupSpec() { - server.start() - } - - def cleanupSpec() { - server.stop() - } - - def setup() { - server.beforeTestExecution(null) - } - - void configureSdkClient(SdkClientBuilder builder) { - builder.overrideConfiguration(createOverrideConfigurationBuilder().build()) - } - - abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); - - def "send DynamoDB #operation request with builder #builder.class.getName() mocked response"() { - setup: - configureSdkClient(builder) - def client = builder - .endpointOverride(server.httpUri()) - .region(Region.AP_NORTHEAST_1) - .credentialsProvider(CREDENTIALS_PROVIDER) - .build() - server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")) - def response = call.call(client) - - if (response instanceof Future) { - response = response.get() - } - - expect: - response != null - response.class.simpleName.startsWith(operation) - switch (operation) { - case "CreateTable": - assertCreateTableRequest(path, method, requestId) - break - case "Query": - assertQueryRequest(path, method, requestId) - break - default: - assertDynamoDbRequest(service, operation, path, method, requestId) - } - - where: - [service, operation, method, path, requestId, builder, call] << dynamoDbRequestDataTable(DynamoDbClient.builder()) - } - - def "send DynamoDB #operation async request with builder #builder.class.getName() mocked response"() { - setup: - configureSdkClient(builder) - def client = builder - .endpointOverride(server.httpUri()) - .region(Region.AP_NORTHEAST_1) - .credentialsProvider(CREDENTIALS_PROVIDER) - .build() - server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")) - def response = call.call(client) - - if (response instanceof Future) { - response = response.get() - } - - expect: - response != null - switch (operation) { - case "CreateTable": - assertCreateTableRequest(path, method, requestId) - break - case "Query": - assertQueryRequest(path, method, requestId) - break - default: - assertDynamoDbRequest(service, operation, path, method, requestId) - } - - where: - [service, operation, method, path, requestId, builder, call] << dynamoDbRequestDataTable(DynamoDbAsyncClient.builder()) - } - - def assertCreateTableRequest(path, method, requestId) { - assertTraces(1) { - trace(0, 1) { - span(0) { - name "DynamoDb.CreateTable" - kind CLIENT - hasNoParent() - attributes { - "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } - "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "DynamoDb" - "$RpcIncubatingAttributes.RPC_METHOD" "CreateTable" - "aws.agent" "java-aws-sdk" - "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" - "aws.table.name" "sometable" - "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" - "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "CreateTable" - "aws.dynamodb.global_secondary_indexes" "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]" - "aws.dynamodb.provisioned_throughput.read_capacity_units" "1" - "aws.dynamodb.provisioned_throughput.write_capacity_units" "1" - } - } - } - } - def request = server.takeRequest() - request.request().headers().get("X-Amzn-Trace-Id") != null - request.request().headers().get("traceparent") == null - } - - def assertQueryRequest(path, method, requestId) { - assertTraces(1) { - trace(0, 1) { - span(0) { - name "DynamoDb.Query" - kind CLIENT - hasNoParent() - attributes { - "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } - "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "DynamoDb" - "$RpcIncubatingAttributes.RPC_METHOD" "Query" - "aws.agent" "java-aws-sdk" - "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" - "aws.table.name" "sometable" - "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" - "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "Query" - "aws.dynamodb.limit" "10" - "aws.dynamodb.select" "ALL_ATTRIBUTES" - } - } - } - } - def request = server.takeRequest() - request.request().headers().get("X-Amzn-Trace-Id") != null - request.request().headers().get("traceparent") == null - } - - def assertDynamoDbRequest(service, operation, path, method, requestId) { - assertTraces(1) { - trace(0, 1) { - span(0) { - name "$service.$operation" - kind CLIENT - hasNoParent() - attributes { - "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } - "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "$service" - "$RpcIncubatingAttributes.RPC_METHOD" "${operation}" - "aws.agent" "java-aws-sdk" - "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" - "aws.table.name" "sometable" - "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" - "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "${operation}" - } - } - } - } - def request = server.takeRequest() - request.request().headers().get("X-Amzn-Trace-Id") != null - request.request().headers().get("traceparent") == null - } - - static dynamoDbRequestDataTable(client) { - [ - ["DynamoDb", "CreateTable", "POST", "/", "UNKNOWN", client, - { c -> c.createTable(createTableRequest()) }], - ["DynamoDb", "DeleteItem", "POST", "/", "UNKNOWN", client, - { c -> c.deleteItem(DeleteItemRequest.builder().tableName("sometable").key(of("anotherKey", val("value"), "key", val("value"))).conditionExpression("property in (:one :two)").build()) }], - ["DynamoDb", "DeleteTable", "POST", "/", "UNKNOWN", client, - { c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build()) }], - ["DynamoDb", "GetItem", "POST", "/", "UNKNOWN", client, - { c -> c.getItem(GetItemRequest.builder().tableName("sometable").key(of("keyOne", val("value"), "keyTwo", val("differentValue"))).attributesToGet("propertyOne", "propertyTwo").build()) }], - ["DynamoDb", "PutItem", "POST", "/", "UNKNOWN", client, - { c -> c.putItem(PutItemRequest.builder().tableName("sometable").item(of("key", val("value"), "attributeOne", val("one"), "attributeTwo", val("two"))).conditionExpression("attributeOne <> :someVal").build()) }], - ["DynamoDb", "Query", "POST", "/", "UNKNOWN", client, - { c -> c.query(QueryRequest.builder().tableName("sometable").select("ALL_ATTRIBUTES").keyConditionExpression("attribute = :aValue").filterExpression("anotherAttribute = :someVal").limit(10).build()) }], - ["DynamoDb", "UpdateItem", "POST", "/", "UNKNOWN", client, - { c -> c.updateItem(UpdateItemRequest.builder().tableName("sometable").key(of("keyOne", val("value"), "keyTwo", val("differentValue"))).conditionExpression("attributeOne <> :someVal").updateExpression("set attributeOne = :updateValue").build()) }] - ] - } - - static CreateTableRequest createTableRequest() { - return CreateTableRequest.builder() - .tableName("sometable") - .globalSecondaryIndexes(Arrays.asList( - GlobalSecondaryIndex.builder() - .indexName("globalIndex") - .keySchema( - KeySchemaElement.builder() - .attributeName("attribute") - .build()) - .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(10) - .writeCapacityUnits(12) - .build() - ) - .build(), - GlobalSecondaryIndex.builder() - .indexName("globalIndexSecondary") - .keySchema( - KeySchemaElement.builder() - .attributeName("attributeSecondary") - .build()) - .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(7) - .writeCapacityUnits(8) - .build() - ) - .build())) - .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(1) - .writeCapacityUnits(1) - .build() - ) - .build() - } - - static val(String value) { - return AttributeValue.builder().s(value).build() - } -} +///* +// * Copyright The OpenTelemetry Authors +// * SPDX-License-Identifier: Apache-2.0 +// */ +// +//package io.opentelemetry.instrumentation.awssdk.v2_2 +// +//import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil +//import io.opentelemetry.instrumentation.test.InstrumentationSpecification +//import io.opentelemetry.semconv.HttpAttributes +//import io.opentelemetry.semconv.ServerAttributes +//import io.opentelemetry.semconv.UrlAttributes +//import io.opentelemetry.semconv.incubating.AwsIncubatingAttributes +//import io.opentelemetry.semconv.incubating.DbIncubatingAttributes +//import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes +//import io.opentelemetry.testing.internal.armeria.common.HttpResponse +//import io.opentelemetry.testing.internal.armeria.common.HttpStatus +//import io.opentelemetry.testing.internal.armeria.common.MediaType +//import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension +//import software.amazon.awssdk.auth.credentials.AwsBasicCredentials +//import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider +//import software.amazon.awssdk.core.client.builder.SdkClientBuilder +//import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration +//import software.amazon.awssdk.regions.Region +//import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient +//import software.amazon.awssdk.services.dynamodb.DynamoDbClient +//import software.amazon.awssdk.services.dynamodb.model.* +//import spock.lang.Shared +//import spock.lang.Unroll +// +//import java.util.concurrent.Future +// +//import static com.google.common.collect.ImmutableMap.of +//import static io.opentelemetry.api.trace.SpanKind.CLIENT +//import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable +// +//@Unroll +//abstract class AbstractAws2ClientCoreTest2 extends InstrumentationSpecification { +// static boolean isSqsAttributeInjectionEnabled() { +// // See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor +// return ConfigPropertiesUtil.getBoolean("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false) +// } +// +// static final StaticCredentialsProvider CREDENTIALS_PROVIDER = StaticCredentialsProvider +// .create(AwsBasicCredentials.create("my-access-key", "my-secret-key")) +// +// @Shared +// def server = new MockWebServerExtension() +// +// def setupSpec() { +// server.start() +// } +// +// def cleanupSpec() { +// server.stop() +// } +// +// def setup() { +// server.beforeTestExecution(null) +// } +// +// void configureSdkClient(SdkClientBuilder builder) { +// builder.overrideConfiguration(createOverrideConfigurationBuilder().build()) +// } +// +// abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); +// +// def "send DynamoDB #operation request with builder #builder.class.getName() mocked response"() { +// setup: +// configureSdkClient(builder) +// def client = builder +// .endpointOverride(server.httpUri()) +// .region(Region.AP_NORTHEAST_1) +// .credentialsProvider(CREDENTIALS_PROVIDER) +// .build() +// server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")) +// def response = call.call(client) +// +// if (response instanceof Future) { +// response = response.get() +// } +// +// expect: +// response != null +// response.class.simpleName.startsWith(operation) +// switch (operation) { +// case "CreateTable": +// assertCreateTableRequest(path, method, requestId) +// break +// case "Query": +// assertQueryRequest(path, method, requestId) +// break +// default: +// assertDynamoDbRequest(service, operation, path, method, requestId) +// } +// +// where: +// [service, operation, method, path, requestId, builder, call] << dynamoDbRequestDataTable(DynamoDbClient.builder()) +// } +// +// def "send DynamoDB #operation async request with builder #builder.class.getName() mocked response"() { +// setup: +// configureSdkClient(builder) +// def client = builder +// .endpointOverride(server.httpUri()) +// .region(Region.AP_NORTHEAST_1) +// .credentialsProvider(CREDENTIALS_PROVIDER) +// .build() +// server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")) +// def response = call.call(client) +// +// if (response instanceof Future) { +// response = response.get() +// } +// +// expect: +// response != null +// switch (operation) { +// case "CreateTable": +// assertCreateTableRequest(path, method, requestId) +// break +// case "Query": +// assertQueryRequest(path, method, requestId) +// break +// default: +// assertDynamoDbRequest(service, operation, path, method, requestId) +// } +// +// where: +// [service, operation, method, path, requestId, builder, call] << dynamoDbRequestDataTable(DynamoDbAsyncClient.builder()) +// } +// +// def assertCreateTableRequest(path, method, requestId) { +// assertTraces(1) { +// trace(0, 1) { +// span(0) { +// name "DynamoDb.CreateTable" +// kind CLIENT +// hasNoParent() +// attributes { +// "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" +// "$ServerAttributes.SERVER_PORT" server.httpPort() +// "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } +// "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" +// "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 +// "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" +// "$RpcIncubatingAttributes.RPC_SERVICE" "DynamoDb" +// "$RpcIncubatingAttributes.RPC_METHOD" "CreateTable" +// "aws.agent" "java-aws-sdk" +// "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" +// "aws.table.name" "sometable" +// "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" +// "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "CreateTable" +// "aws.dynamodb.global_secondary_indexes" "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]" +// "aws.dynamodb.provisioned_throughput.read_capacity_units" "1" +// "aws.dynamodb.provisioned_throughput.write_capacity_units" "1" +// } +// } +// } +// } +// def request = server.takeRequest() +// request.request().headers().get("X-Amzn-Trace-Id") != null +// request.request().headers().get("traceparent") == null +// } +// +// def assertQueryRequest(path, method, requestId) { +// assertTraces(1) { +// trace(0, 1) { +// span(0) { +// name "DynamoDb.Query" +// kind CLIENT +// hasNoParent() +// attributes { +// "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" +// "$ServerAttributes.SERVER_PORT" server.httpPort() +// "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } +// "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" +// "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 +// "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" +// "$RpcIncubatingAttributes.RPC_SERVICE" "DynamoDb" +// "$RpcIncubatingAttributes.RPC_METHOD" "Query" +// "aws.agent" "java-aws-sdk" +// "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" +// "aws.table.name" "sometable" +// "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" +// "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "Query" +// "aws.dynamodb.limit" "10" +// "aws.dynamodb.select" "ALL_ATTRIBUTES" +// } +// } +// } +// } +// def request = server.takeRequest() +// request.request().headers().get("X-Amzn-Trace-Id") != null +// request.request().headers().get("traceparent") == null +// } +// +// def assertDynamoDbRequest(service, operation, path, method, requestId) { +// assertTraces(1) { +// trace(0, 1) { +// span(0) { +// name "$service.$operation" +// kind CLIENT +// hasNoParent() +// attributes { +// "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" +// "$ServerAttributes.SERVER_PORT" server.httpPort() +// "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } +// "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" +// "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 +// "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" +// "$RpcIncubatingAttributes.RPC_SERVICE" "$service" +// "$RpcIncubatingAttributes.RPC_METHOD" "${operation}" +// "aws.agent" "java-aws-sdk" +// "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" +// "aws.table.name" "sometable" +// "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" +// "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "${operation}" +// } +// } +// } +// } +// def request = server.takeRequest() +// request.request().headers().get("X-Amzn-Trace-Id") != null +// request.request().headers().get("traceparent") == null +// } +// +// static dynamoDbRequestDataTable(client) { +// [ +// ["DynamoDb", "CreateTable", "POST", "/", "UNKNOWN", client, +// { c -> c.createTable(createTableRequest()) }], +// ["DynamoDb", "DeleteItem", "POST", "/", "UNKNOWN", client, +// { c -> c.deleteItem(DeleteItemRequest.builder().tableName("sometable").key(of("anotherKey", val("value"), "key", val("value"))).conditionExpression("property in (:one :two)").build()) }], +// ["DynamoDb", "DeleteTable", "POST", "/", "UNKNOWN", client, +// { c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build()) }], +// ["DynamoDb", "GetItem", "POST", "/", "UNKNOWN", client, +// { c -> c.getItem(GetItemRequest.builder().tableName("sometable").key(of("keyOne", val("value"), "keyTwo", val("differentValue"))).attributesToGet("propertyOne", "propertyTwo").build()) }], +// ["DynamoDb", "PutItem", "POST", "/", "UNKNOWN", client, +// { c -> c.putItem(PutItemRequest.builder().tableName("sometable").item(of("key", val("value"), "attributeOne", val("one"), "attributeTwo", val("two"))).conditionExpression("attributeOne <> :someVal").build()) }], +// ["DynamoDb", "Query", "POST", "/", "UNKNOWN", client, +// { c -> c.query(QueryRequest.builder().tableName("sometable").select("ALL_ATTRIBUTES").keyConditionExpression("attribute = :aValue").filterExpression("anotherAttribute = :someVal").limit(10).build()) }], +// ["DynamoDb", "UpdateItem", "POST", "/", "UNKNOWN", client, +// { c -> c.updateItem(UpdateItemRequest.builder().tableName("sometable").key(of("keyOne", val("value"), "keyTwo", val("differentValue"))).conditionExpression("attributeOne <> :someVal").updateExpression("set attributeOne = :updateValue").build()) }] +// ] +// } +// +// static CreateTableRequest createTableRequest() { +// return CreateTableRequest.builder() +// .tableName("sometable") +// .globalSecondaryIndexes(Arrays.asList( +// GlobalSecondaryIndex.builder() +// .indexName("globalIndex") +// .keySchema( +// KeySchemaElement.builder() +// .attributeName("attribute") +// .build()) +// .provisionedThroughput( +// ProvisionedThroughput.builder() +// .readCapacityUnits(10) +// .writeCapacityUnits(12) +// .build() +// ) +// .build(), +// GlobalSecondaryIndex.builder() +// .indexName("globalIndexSecondary") +// .keySchema( +// KeySchemaElement.builder() +// .attributeName("attributeSecondary") +// .build()) +// .provisionedThroughput( +// ProvisionedThroughput.builder() +// .readCapacityUnits(7) +// .writeCapacityUnits(8) +// .build() +// ) +// .build())) +// .provisionedThroughput( +// ProvisionedThroughput.builder() +// .readCapacityUnits(1) +// .writeCapacityUnits(1) +// .build() +// ) +// .build() +// } +// +// static val(String value) { +// return AttributeValue.builder().s(value).build() +// } +//} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy index 2533a0202dac..b241603d22e7 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy @@ -52,7 +52,7 @@ import static io.opentelemetry.api.trace.SpanKind.PRODUCER import static io.opentelemetry.api.trace.StatusCode.ERROR @Unroll -abstract class AbstractAws2ClientTest extends AbstractAws2ClientCoreTest { +abstract class AbstractAws2ClientTest2 extends AbstractAws2ClientCoreTest { static final String QUEUE_URL = "http://xxx/somequeue" void assumeSupportedConfig(service, operation) { diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java new file mode 100644 index 000000000000..0834b29c6ca2 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -0,0 +1,141 @@ +package io.opentelemetry.instrumentation.awssdk.v2_2; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.testing.internal.armeria.common.HttpResponse; +import io.opentelemetry.testing.internal.armeria.common.HttpStatus; +import io.opentelemetry.testing.internal.armeria.common.MediaType; +import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.RecordedRequest; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.function.Function; +import java.util.stream.Stream; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; +import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; +import static org.assertj.core.api.Assertions.assertThat; + +public abstract class AbstractAws2ClientTest extends AbstractAws2ClientCoreTest { + private static final String QUEUE_URL = "http://xxx/somequeue"; + + private void assumeSupportedConfig(String service, String operation) { + Assumptions.assumeFalse( + service.equals("Sqs") + && operation.equals("SendMessage") + && isSqsAttributeInjectionEnabled(), + "Cannot check Sqs.SendMessage here due to hard-coded MD5."); + } + + // Force localhost instead of relying on mock server because using ip is yet another corner case of the virtual + // bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to prefix the hostname with the + // bucket name as label. + URI clientUri = URI.create("http://localhost:" + server.httpPort()); + + S3ClientBuilder s3ClientBuilder() throws Exception { + S3ClientBuilder builder = S3Client.builder(); + if (Boolean.getBoolean("testLatestDeps")) { + Method forcePathStyleMethod = S3ClientBuilder.class.getMethod("forcePathStyle", + Boolean.class); + forcePathStyleMethod.invoke(true); + } + return builder; + } + + S3AsyncClientBuilder s3AsyncClientBuilder() throws Exception { + S3AsyncClientBuilder builder = S3AsyncClient.builder(); + if (Boolean.getBoolean("testLatestDeps")) { + Method forcePathStyleMethod = S3ClientBuilder.class.getMethod("forcePathStyle", + Boolean.class); + forcePathStyleMethod.invoke(true); + } + return builder; + } + + private static Stream provideS3Arguments() { + return Stream.of( + Arguments.of( + "CreateBucket", + "PUT", + (Function) c -> c.createBucket( + CreateBucketRequest.builder().bucket("somebucket").build()) + ), + Arguments.of("GetObject", + "GET", + (Function) c -> c.getObject( + GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) + )); + } + + @ParameterizedTest + @MethodSource("provideS3Arguments") + void testS3SendOperationRequestWithBuilder(String operation, String method, + Function call) throws Exception { + + S3ClientBuilder builder = s3ClientBuilder(); + configureSdkClient(builder); + + S3Client client = builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); + + Object response = call.apply(client); + assertThat(response).isNotNull(); + assertThat(response.getClass().getSimpleName()).startsWith(operation); + + RecordedRequest request = server.takeRequest(); + assertThat(request).isNotNull(); + assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); + assertThat(request.request().headers().get("traceparent")).isNotNull(); + + getTesting().waitAndAssertTraces( + trace -> trace.hasSpansSatisfyingExactly( + span -> span.hasName("S3." + operation) + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + satisfies(SERVER_ADDRESS, + v -> v.matches("somebucket.localhost|localhost")), + satisfies(URL_FULL, + val -> val.satisfiesAnyOf( + v -> assertThat(v).startsWith("http://somebucket.localhost:" + server.httpPort()), + v -> assertThat(v).startsWith("http://localhost:" + server.httpPort() + "/somebucket") + )), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(HTTP_REQUEST_METHOD, method), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "S3"), + equalTo(RPC_METHOD, operation), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, "UNKOWN"), + equalTo(stringKey("aws.bucket.name"), "somebucket")))); + + + } + + +} From 636bf5a064ab719ba5d94f7d97c4cce91f80433d Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Sun, 22 Dec 2024 11:22:02 -0500 Subject: [PATCH 03/14] finish converting --- .../src/test/groovy/Aws2ClientTest.groovy | 15 - .../awssdk/v2_2/Aws2ClientTest.java | 27 + .../test/groovy/v2_2/Aws2ClientTest.groovy | 17 - .../awssdk/v2_2/Aws2ClientTest.java | 26 + .../awssdk/v2_2/Aws2ClientTest.groovy | 22 - .../awssdk/v2_2/Aws2ClientTest.java | 32 + .../awssdk/v2_2/Aws2ClientDynamodbTest.groovy | 24 - .../awssdk/v2_2/Aws2ClientDynamodbTest.java | 32 + .../v2_2/AbstractAws2ClientCoreTest.groovy | 289 -------- .../awssdk/v2_2/AbstractAws2ClientTest.groovy | 467 ------------ .../v2_2/AbstractAws2ClientCoreTest.java | 410 +++++++---- .../awssdk/v2_2/AbstractAws2ClientTest.java | 693 ++++++++++++++++-- 12 files changed, 1005 insertions(+), 1049 deletions(-) delete mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/groovy/Aws2ClientTest.groovy create mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/Aws2ClientTest.java delete mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/groovy/v2_2/Aws2ClientTest.groovy create mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java delete mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.groovy create mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java delete mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.groovy create mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.java delete mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy delete mode 100644 instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/groovy/Aws2ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/groovy/Aws2ClientTest.groovy deleted file mode 100644 index daaeeb63e589..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/groovy/Aws2ClientTest.groovy +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.awssdk.v2_2.AbstractAws2ClientTest -import io.opentelemetry.instrumentation.test.AgentTestTrait -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration - -class Aws2ClientTest extends AbstractAws2ClientTest implements AgentTestTrait { - @Override - ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { - return ClientOverrideConfiguration.builder() - } -} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/Aws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/Aws2ClientTest.java new file mode 100644 index 000000000000..db16e4ce3b81 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/Aws2ClientTest.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2; + +import io.opentelemetry.instrumentation.awssdk.v2_2.AbstractAws2ClientTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; + +class Aws2ClientTest extends AbstractAws2ClientTest { + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension getTesting() { + return testing; + } + + @Override + protected ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { + return ClientOverrideConfiguration.builder(); + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/groovy/v2_2/Aws2ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/groovy/v2_2/Aws2ClientTest.groovy deleted file mode 100644 index 071270a3f291..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/groovy/v2_2/Aws2ClientTest.groovy +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package v2_2 - -import io.opentelemetry.instrumentation.awssdk.v2_2.AbstractAws2ClientTest -import io.opentelemetry.instrumentation.test.LibraryTestTrait -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration - -class Aws2ClientTest extends AbstractAws2ClientTest implements LibraryTestTrait { - @Override - ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { - return ClientOverrideConfiguration.builder() - } -} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java new file mode 100644 index 000000000000..25ba2bb7243c --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v2_2; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; + +class Aws2ClientTest extends AbstractAws2ClientTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension getTesting() { + return testing; + } + + @Override + protected ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { + return ClientOverrideConfiguration.builder(); + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.groovy deleted file mode 100644 index 40a88e4c5863..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.groovy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awssdk.v2_2 - -import io.opentelemetry.instrumentation.test.LibraryTestTrait -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration - -class Aws2ClientTest extends AbstractAws2ClientTest implements LibraryTestTrait { - @Override - ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { - return ClientOverrideConfiguration.builder() - .addExecutionInterceptor( - AwsSdkTelemetry.builder(getOpenTelemetry()) - .setCaptureExperimentalSpanAttributes(true) - .setUseConfiguredPropagatorForMessaging(isSqsAttributeInjectionEnabled()) - .build() - .newExecutionInterceptor()) - } -} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java new file mode 100644 index 000000000000..c1a570ce65eb --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v2_2; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; + +class Aws2ClientTest extends AbstractAws2ClientTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension getTesting() { + return testing; + } + + @Override + protected ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { + return ClientOverrideConfiguration.builder() + .addExecutionInterceptor( + AwsSdkTelemetry.builder(getTesting().getOpenTelemetry()) + .setCaptureExperimentalSpanAttributes(true) + .setUseConfiguredPropagatorForMessaging(isSqsAttributeInjectionEnabled()) + .build() + .newExecutionInterceptor()); + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.groovy deleted file mode 100644 index a8c5a4aab1bf..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.groovy +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awssdk.v2_2 - -import groovy.transform.CompileStatic -import io.opentelemetry.instrumentation.test.LibraryTestTrait -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration - -@CompileStatic -class Aws2ClientDynamodbTest extends AbstractAws2ClientCoreTest implements LibraryTestTrait { - @Override - ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { - return ClientOverrideConfiguration.builder() - .addExecutionInterceptor( - AwsSdkTelemetry.builder(getOpenTelemetry()) - .setCaptureExperimentalSpanAttributes(true) - .setUseConfiguredPropagatorForMessaging(isSqsAttributeInjectionEnabled()) - .build() - .newExecutionInterceptor()) - } -} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.java new file mode 100644 index 000000000000..d7af4b42bfab --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testCoreOnly/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2ClientDynamodbTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v2_2; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; + +class Aws2ClientDynamodbTest extends AbstractAws2ClientCoreTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension getTesting() { + return testing; + } + + @Override + protected ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() { + return ClientOverrideConfiguration.builder() + .addExecutionInterceptor( + AwsSdkTelemetry.builder(getTesting().getOpenTelemetry()) + .setCaptureExperimentalSpanAttributes(true) + .setUseConfiguredPropagatorForMessaging(isSqsAttributeInjectionEnabled()) + .build() + .newExecutionInterceptor()); + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy deleted file mode 100644 index 52afdb74103a..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.groovy +++ /dev/null @@ -1,289 +0,0 @@ -///* -// * Copyright The OpenTelemetry Authors -// * SPDX-License-Identifier: Apache-2.0 -// */ -// -//package io.opentelemetry.instrumentation.awssdk.v2_2 -// -//import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil -//import io.opentelemetry.instrumentation.test.InstrumentationSpecification -//import io.opentelemetry.semconv.HttpAttributes -//import io.opentelemetry.semconv.ServerAttributes -//import io.opentelemetry.semconv.UrlAttributes -//import io.opentelemetry.semconv.incubating.AwsIncubatingAttributes -//import io.opentelemetry.semconv.incubating.DbIncubatingAttributes -//import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes -//import io.opentelemetry.testing.internal.armeria.common.HttpResponse -//import io.opentelemetry.testing.internal.armeria.common.HttpStatus -//import io.opentelemetry.testing.internal.armeria.common.MediaType -//import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension -//import software.amazon.awssdk.auth.credentials.AwsBasicCredentials -//import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider -//import software.amazon.awssdk.core.client.builder.SdkClientBuilder -//import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration -//import software.amazon.awssdk.regions.Region -//import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient -//import software.amazon.awssdk.services.dynamodb.DynamoDbClient -//import software.amazon.awssdk.services.dynamodb.model.* -//import spock.lang.Shared -//import spock.lang.Unroll -// -//import java.util.concurrent.Future -// -//import static com.google.common.collect.ImmutableMap.of -//import static io.opentelemetry.api.trace.SpanKind.CLIENT -//import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable -// -//@Unroll -//abstract class AbstractAws2ClientCoreTest2 extends InstrumentationSpecification { -// static boolean isSqsAttributeInjectionEnabled() { -// // See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor -// return ConfigPropertiesUtil.getBoolean("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false) -// } -// -// static final StaticCredentialsProvider CREDENTIALS_PROVIDER = StaticCredentialsProvider -// .create(AwsBasicCredentials.create("my-access-key", "my-secret-key")) -// -// @Shared -// def server = new MockWebServerExtension() -// -// def setupSpec() { -// server.start() -// } -// -// def cleanupSpec() { -// server.stop() -// } -// -// def setup() { -// server.beforeTestExecution(null) -// } -// -// void configureSdkClient(SdkClientBuilder builder) { -// builder.overrideConfiguration(createOverrideConfigurationBuilder().build()) -// } -// -// abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); -// -// def "send DynamoDB #operation request with builder #builder.class.getName() mocked response"() { -// setup: -// configureSdkClient(builder) -// def client = builder -// .endpointOverride(server.httpUri()) -// .region(Region.AP_NORTHEAST_1) -// .credentialsProvider(CREDENTIALS_PROVIDER) -// .build() -// server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")) -// def response = call.call(client) -// -// if (response instanceof Future) { -// response = response.get() -// } -// -// expect: -// response != null -// response.class.simpleName.startsWith(operation) -// switch (operation) { -// case "CreateTable": -// assertCreateTableRequest(path, method, requestId) -// break -// case "Query": -// assertQueryRequest(path, method, requestId) -// break -// default: -// assertDynamoDbRequest(service, operation, path, method, requestId) -// } -// -// where: -// [service, operation, method, path, requestId, builder, call] << dynamoDbRequestDataTable(DynamoDbClient.builder()) -// } -// -// def "send DynamoDB #operation async request with builder #builder.class.getName() mocked response"() { -// setup: -// configureSdkClient(builder) -// def client = builder -// .endpointOverride(server.httpUri()) -// .region(Region.AP_NORTHEAST_1) -// .credentialsProvider(CREDENTIALS_PROVIDER) -// .build() -// server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")) -// def response = call.call(client) -// -// if (response instanceof Future) { -// response = response.get() -// } -// -// expect: -// response != null -// switch (operation) { -// case "CreateTable": -// assertCreateTableRequest(path, method, requestId) -// break -// case "Query": -// assertQueryRequest(path, method, requestId) -// break -// default: -// assertDynamoDbRequest(service, operation, path, method, requestId) -// } -// -// where: -// [service, operation, method, path, requestId, builder, call] << dynamoDbRequestDataTable(DynamoDbAsyncClient.builder()) -// } -// -// def assertCreateTableRequest(path, method, requestId) { -// assertTraces(1) { -// trace(0, 1) { -// span(0) { -// name "DynamoDb.CreateTable" -// kind CLIENT -// hasNoParent() -// attributes { -// "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" -// "$ServerAttributes.SERVER_PORT" server.httpPort() -// "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } -// "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" -// "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 -// "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" -// "$RpcIncubatingAttributes.RPC_SERVICE" "DynamoDb" -// "$RpcIncubatingAttributes.RPC_METHOD" "CreateTable" -// "aws.agent" "java-aws-sdk" -// "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" -// "aws.table.name" "sometable" -// "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" -// "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "CreateTable" -// "aws.dynamodb.global_secondary_indexes" "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]" -// "aws.dynamodb.provisioned_throughput.read_capacity_units" "1" -// "aws.dynamodb.provisioned_throughput.write_capacity_units" "1" -// } -// } -// } -// } -// def request = server.takeRequest() -// request.request().headers().get("X-Amzn-Trace-Id") != null -// request.request().headers().get("traceparent") == null -// } -// -// def assertQueryRequest(path, method, requestId) { -// assertTraces(1) { -// trace(0, 1) { -// span(0) { -// name "DynamoDb.Query" -// kind CLIENT -// hasNoParent() -// attributes { -// "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" -// "$ServerAttributes.SERVER_PORT" server.httpPort() -// "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } -// "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" -// "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 -// "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" -// "$RpcIncubatingAttributes.RPC_SERVICE" "DynamoDb" -// "$RpcIncubatingAttributes.RPC_METHOD" "Query" -// "aws.agent" "java-aws-sdk" -// "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" -// "aws.table.name" "sometable" -// "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" -// "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "Query" -// "aws.dynamodb.limit" "10" -// "aws.dynamodb.select" "ALL_ATTRIBUTES" -// } -// } -// } -// } -// def request = server.takeRequest() -// request.request().headers().get("X-Amzn-Trace-Id") != null -// request.request().headers().get("traceparent") == null -// } -// -// def assertDynamoDbRequest(service, operation, path, method, requestId) { -// assertTraces(1) { -// trace(0, 1) { -// span(0) { -// name "$service.$operation" -// kind CLIENT -// hasNoParent() -// attributes { -// "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" -// "$ServerAttributes.SERVER_PORT" server.httpPort() -// "$UrlAttributes.URL_FULL" { it.startsWith("${server.httpUri()}${path}") } -// "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" -// "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 -// "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" -// "$RpcIncubatingAttributes.RPC_SERVICE" "$service" -// "$RpcIncubatingAttributes.RPC_METHOD" "${operation}" -// "aws.agent" "java-aws-sdk" -// "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" -// "aws.table.name" "sometable" -// "$DbIncubatingAttributes.DB_SYSTEM" "dynamodb" -// "${maybeStable(DbIncubatingAttributes.DB_OPERATION)}" "${operation}" -// } -// } -// } -// } -// def request = server.takeRequest() -// request.request().headers().get("X-Amzn-Trace-Id") != null -// request.request().headers().get("traceparent") == null -// } -// -// static dynamoDbRequestDataTable(client) { -// [ -// ["DynamoDb", "CreateTable", "POST", "/", "UNKNOWN", client, -// { c -> c.createTable(createTableRequest()) }], -// ["DynamoDb", "DeleteItem", "POST", "/", "UNKNOWN", client, -// { c -> c.deleteItem(DeleteItemRequest.builder().tableName("sometable").key(of("anotherKey", val("value"), "key", val("value"))).conditionExpression("property in (:one :two)").build()) }], -// ["DynamoDb", "DeleteTable", "POST", "/", "UNKNOWN", client, -// { c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build()) }], -// ["DynamoDb", "GetItem", "POST", "/", "UNKNOWN", client, -// { c -> c.getItem(GetItemRequest.builder().tableName("sometable").key(of("keyOne", val("value"), "keyTwo", val("differentValue"))).attributesToGet("propertyOne", "propertyTwo").build()) }], -// ["DynamoDb", "PutItem", "POST", "/", "UNKNOWN", client, -// { c -> c.putItem(PutItemRequest.builder().tableName("sometable").item(of("key", val("value"), "attributeOne", val("one"), "attributeTwo", val("two"))).conditionExpression("attributeOne <> :someVal").build()) }], -// ["DynamoDb", "Query", "POST", "/", "UNKNOWN", client, -// { c -> c.query(QueryRequest.builder().tableName("sometable").select("ALL_ATTRIBUTES").keyConditionExpression("attribute = :aValue").filterExpression("anotherAttribute = :someVal").limit(10).build()) }], -// ["DynamoDb", "UpdateItem", "POST", "/", "UNKNOWN", client, -// { c -> c.updateItem(UpdateItemRequest.builder().tableName("sometable").key(of("keyOne", val("value"), "keyTwo", val("differentValue"))).conditionExpression("attributeOne <> :someVal").updateExpression("set attributeOne = :updateValue").build()) }] -// ] -// } -// -// static CreateTableRequest createTableRequest() { -// return CreateTableRequest.builder() -// .tableName("sometable") -// .globalSecondaryIndexes(Arrays.asList( -// GlobalSecondaryIndex.builder() -// .indexName("globalIndex") -// .keySchema( -// KeySchemaElement.builder() -// .attributeName("attribute") -// .build()) -// .provisionedThroughput( -// ProvisionedThroughput.builder() -// .readCapacityUnits(10) -// .writeCapacityUnits(12) -// .build() -// ) -// .build(), -// GlobalSecondaryIndex.builder() -// .indexName("globalIndexSecondary") -// .keySchema( -// KeySchemaElement.builder() -// .attributeName("attributeSecondary") -// .build()) -// .provisionedThroughput( -// ProvisionedThroughput.builder() -// .readCapacityUnits(7) -// .writeCapacityUnits(8) -// .build() -// ) -// .build())) -// .provisionedThroughput( -// ProvisionedThroughput.builder() -// .readCapacityUnits(1) -// .writeCapacityUnits(1) -// .build() -// ) -// .build() -// } -// -// static val(String value) { -// return AttributeValue.builder().s(value).build() -// } -//} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy deleted file mode 100644 index b241603d22e7..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy +++ /dev/null @@ -1,467 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awssdk.v2_2 - -import io.opentelemetry.semconv.incubating.AwsIncubatingAttributes -import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes -import io.opentelemetry.semconv.ServerAttributes -import io.opentelemetry.semconv.HttpAttributes -import io.opentelemetry.semconv.UrlAttributes -import io.opentelemetry.testing.internal.armeria.common.HttpData -import io.opentelemetry.testing.internal.armeria.common.HttpResponse -import io.opentelemetry.testing.internal.armeria.common.HttpStatus -import io.opentelemetry.testing.internal.armeria.common.MediaType -import io.opentelemetry.testing.internal.armeria.common.ResponseHeaders -import org.junit.jupiter.api.Assumptions -import software.amazon.awssdk.core.ResponseInputStream -import software.amazon.awssdk.core.async.AsyncResponseTransformer -import software.amazon.awssdk.core.exception.SdkClientException -import software.amazon.awssdk.core.retry.RetryPolicy -import software.amazon.awssdk.http.apache.ApacheHttpClient -import software.amazon.awssdk.regions.Region -import software.amazon.awssdk.services.ec2.Ec2AsyncClient -import software.amazon.awssdk.services.ec2.Ec2Client -import software.amazon.awssdk.services.kinesis.KinesisClient -import software.amazon.awssdk.services.kinesis.model.DeleteStreamRequest -import software.amazon.awssdk.services.rds.RdsAsyncClient -import software.amazon.awssdk.services.rds.RdsClient -import software.amazon.awssdk.services.rds.model.DeleteOptionGroupRequest -import software.amazon.awssdk.services.s3.S3AsyncClient -import software.amazon.awssdk.services.s3.S3Client -import software.amazon.awssdk.services.s3.model.CreateBucketRequest -import software.amazon.awssdk.services.s3.model.GetObjectRequest -import software.amazon.awssdk.services.sns.SnsAsyncClient -import software.amazon.awssdk.services.sns.SnsClient -import software.amazon.awssdk.services.sns.model.PublishRequest -import software.amazon.awssdk.services.sqs.SqsAsyncClient -import software.amazon.awssdk.services.sqs.SqsClient -import software.amazon.awssdk.services.sqs.model.CreateQueueRequest -import software.amazon.awssdk.services.sqs.model.SendMessageRequest -import spock.lang.Unroll - -import java.nio.charset.StandardCharsets -import java.time.Duration -import java.util.concurrent.Future - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.PRODUCER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -@Unroll -abstract class AbstractAws2ClientTest2 extends AbstractAws2ClientCoreTest { - static final String QUEUE_URL = "http://xxx/somequeue" - - void assumeSupportedConfig(service, operation) { - Assumptions.assumeFalse( - service == "Sqs" - && operation == "SendMessage" - && isSqsAttributeInjectionEnabled(), - "Cannot check Sqs.SendMessage here due to hard-coded MD5.") - } - - // Force localhost instead of relying on mock server because using ip is yet another corner case of the virtual - // bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to prefix the hostname with the - // bucket name as label. - def clientUri = URI.create("http://localhost:${server.httpPort()}") - - def s3ClientBuilder() { - def builder = S3Client.builder() - if (Boolean.getBoolean("testLatestDeps")) { - builder.forcePathStyle(true) - } - return builder - } - - def s3AsyncClientBuilder() { - def builder = S3AsyncClient.builder() - if (Boolean.getBoolean("testLatestDeps")) { - builder.forcePathStyle(true) - } - return builder - } - - def "send #operation request with builder #builder.class.getName() mocked response"() { - assumeSupportedConfig(service, operation) - - setup: - configureSdkClient(builder) - def client = builder - .endpointOverride(clientUri) - .region(Region.AP_NORTHEAST_1) - .credentialsProvider(CREDENTIALS_PROVIDER) - .build() - - if (body instanceof Closure) { - server.enqueue(body.call()) - } else { - server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)) - } - - def response = call.call(client) - if (response instanceof Future) { - response = response.get() - } - - expect: - response != null - response.class.simpleName.startsWith(operation) || response instanceof ResponseInputStream - - assertTraces(1) { - trace(0, 1) { - span(0) { - name operation != "SendMessage" ? "$service.$operation" : "somequeue publish" - kind operation != "SendMessage" ? CLIENT : PRODUCER - hasNoParent() - attributes { - if (service == "S3") { - // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the hostname with the bucket name in case - // the bucket name is a valid DNS label, even in the case that we are using an endpoint override. - // Previously the sdk was only doing that if endpoint had "s3" as label in the FQDN. - // Our test assert both cases so that we don't need to know what version is being tested. - "$ServerAttributes.SERVER_ADDRESS" { it == "somebucket.localhost" || it == "localhost" } - "$UrlAttributes.URL_FULL" { it.startsWith("http://somebucket.localhost:${server.httpPort()}") || it.startsWith("http://localhost:${server.httpPort()}/somebucket") } - } else { - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$UrlAttributes.URL_FULL" { it.startsWith("http://localhost:${server.httpPort()}") } - } - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "$service" - "$RpcIncubatingAttributes.RPC_METHOD" "${operation}" - "aws.agent" "java-aws-sdk" - "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" - if (service == "S3") { - "aws.bucket.name" "somebucket" - } else if (service == "Sqs" && operation == "CreateQueue") { - "aws.queue.name" "somequeue" - } else if (service == "Sqs" && operation == "SendMessage") { - "aws.queue.url" QUEUE_URL - "$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "somequeue" - "$MessagingIncubatingAttributes.MESSAGING_OPERATION" "publish" - "$MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID" String - "$MessagingIncubatingAttributes.MESSAGING_SYSTEM" MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS - } else if (service == "Kinesis") { - "aws.stream.name" "somestream" - } else if (service == "Sns") { - "$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "somearn" - } - } - } - } - } - def request = server.takeRequest() - request.request().headers().get("X-Amzn-Trace-Id") != null - request.request().headers().get("traceparent") == null - - where: - service | operation | method | requestId | builder | call | body - "S3" | "CreateBucket" | "PUT" | "UNKNOWN" | s3ClientBuilder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | "" - "S3" | "GetObject" | "GET" | "UNKNOWN" | s3ClientBuilder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) } | "" - "Kinesis" | "DeleteStream" | "POST" | "UNKNOWN" | KinesisClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | "" - "Sns" | "Publish" | "POST" | "d74b8436-ae13-5ab4-a9ff-ce54dfea72a0" | SnsClient.builder() | { c -> c.publish(PublishRequest.builder().message("somemessage").topicArn("somearn").build()) } | """ - - - 567910cd-659e-55d4-8ccb-5aaf14679dc0 - - - d74b8436-ae13-5ab4-a9ff-ce54dfea72a0 - - - """ - "Sns" | "Publish" | "POST" | "d74b8436-ae13-5ab4-a9ff-ce54dfea72a0" | SnsClient.builder() | { c -> c.publish(PublishRequest.builder().message("somemessage").targetArn("somearn").build()) } | """ - - - 567910cd-659e-55d4-8ccb-5aaf14679dc0 - - - d74b8436-ae13-5ab4-a9ff-ce54dfea72a0 - - - """ - "Sqs" | "CreateQueue" | "POST" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | { - if (!Boolean.getBoolean("testLatestDeps")) { - def content = """ - - https://queue.amazonaws.com/123456789012/MyQueue - 7a62c49f-347e-4fc4-9331-6e8e7a96aa73 - - """ - return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content) - } - def content = """ - { - "QueueUrl":"https://queue.amazonaws.com/123456789012/MyQueue" - } - """ - ResponseHeaders headers = ResponseHeaders.builder(HttpStatus.OK) - .contentType(MediaType.PLAIN_TEXT_UTF_8) - .add("x-amzn-RequestId", "7a62c49f-347e-4fc4-9331-6e8e7a96aa73") - .build() - return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)) - } - "Sqs" | "SendMessage" | "POST" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()) } | { - if (!Boolean.getBoolean("testLatestDeps")) { - def content = """ - - - d41d8cd98f00b204e9800998ecf8427e - 3ae8f24a165a8cedc005670c81a27295 - 5fea7756-0ea4-451a-a703-a558b933e274 - - 27daac76-34dd-47df-bd01-1f6e873584a0 - - """ - return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content) - } - def content = """ - { - "MD5OfMessageBody":"d41d8cd98f00b204e9800998ecf8427e", - "MD5OfMessageAttributes":"3ae8f24a165a8cedc005670c81a27295", - "MessageId":"5fea7756-0ea4-451a-a703-a558b933e274" - } - """ - ResponseHeaders headers = ResponseHeaders.builder(HttpStatus.OK) - .contentType(MediaType.PLAIN_TEXT_UTF_8) - .add("x-amzn-RequestId", "27daac76-34dd-47df-bd01-1f6e873584a0") - .build() - return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)) - } - "Ec2" | "AllocateAddress" | "POST" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2Client.builder() | { c -> c.allocateAddress() } | """ - - 59dbff89-35bd-4eac-99ed-be587EXAMPLE - 192.0.2.1 - standard - - """ - "Rds" | "DeleteOptionGroup" | "POST" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """ - - 0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99 - - """ - } - - def "send #operation async request with builder #builder.class.getName() mocked response"() { - assumeSupportedConfig(service, operation) - setup: - configureSdkClient(builder) - def client = builder - .endpointOverride(clientUri) - .region(Region.AP_NORTHEAST_1) - .credentialsProvider(CREDENTIALS_PROVIDER) - .build() - - if (body instanceof Closure) { - server.enqueue(body.call()) - } else { - server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)) - } - - def response = call.call(client) - if (response instanceof Future) { - response = response.get() - } - - expect: - response != null - - assertTraces(1) { - trace(0, 1) { - span(0) { - name operation != "SendMessage" ? "$service.$operation" : "somequeue publish" - kind operation != "SendMessage" ? CLIENT : PRODUCER - hasNoParent() - attributes { - if (service == "S3") { - // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the hostname with the bucket name in case - // the bucket name is a valid DNS label, even in the case that we are using an endpoint override. - // Previously the sdk was only doing that if endpoint had "s3" as label in the FQDN. - // Our test assert both cases so that we don't need to know what version is being tested. - "$ServerAttributes.SERVER_ADDRESS" { it == "somebucket.localhost" || it == "localhost" } - "$UrlAttributes.URL_FULL" { it.startsWith("http://somebucket.localhost:${server.httpPort()}") || it.startsWith("http://localhost:${server.httpPort()}") } - } else { - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$UrlAttributes.URL_FULL" { it == "http://localhost:${server.httpPort()}" || it == "http://localhost:${server.httpPort()}/" } - } - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "$service" - "$RpcIncubatingAttributes.RPC_METHOD" "${operation}" - "aws.agent" "java-aws-sdk" - "$AwsIncubatingAttributes.AWS_REQUEST_ID" "$requestId" - if (service == "S3") { - "aws.bucket.name" "somebucket" - } else if (service == "Sqs" && operation == "CreateQueue") { - "aws.queue.name" "somequeue" - } else if (service == "Sqs" && operation == "SendMessage") { - "aws.queue.url" QUEUE_URL - "$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "somequeue" - "$MessagingIncubatingAttributes.MESSAGING_OPERATION" "publish" - "$MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID" String - "$MessagingIncubatingAttributes.MESSAGING_SYSTEM" MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS - } else if (service == "Kinesis") { - "aws.stream.name" "somestream" - } else if (service == "Sns") { - "$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "somearn" - } - } - } - } - } - def request = server.takeRequest() - request.request().headers().get("X-Amzn-Trace-Id") != null - request.request().headers().get("traceparent") == null - - if (service == "Sns" && operation == "Publish") { - def content = request.request().content().toStringUtf8() - def containsId = content.contains("${traces[0][0].traceId}-${traces[0][0].spanId}") - def containsTp = content.contains("=traceparent") - if (isSqsAttributeInjectionEnabled()) { - assert containsId && containsTp - } else { - assert !containsId && !containsTp - } - } - - where: - service | operation | method | requestId | builder | call | body - "S3" | "CreateBucket" | "PUT" | "UNKNOWN" | s3AsyncClientBuilder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | "" - "S3" | "GetObject" | "GET" | "UNKNOWN" | s3AsyncClientBuilder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), AsyncResponseTransformer.toBytes()) } | "1234567890" - // Kinesis seems to expect an http2 response which is incompatible with our test server. - // "Kinesis" | "DeleteStream" | "POST" | "/" | "UNKNOWN" | KinesisAsyncClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | "" - "Sqs" | "CreateQueue" | "POST" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsAsyncClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | { - if (!Boolean.getBoolean("testLatestDeps")) { - def content = """ - - https://queue.amazonaws.com/123456789012/MyQueue - 7a62c49f-347e-4fc4-9331-6e8e7a96aa73 - - """ - return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content) - } - def content = """ - { - "QueueUrl":"https://queue.amazonaws.com/123456789012/MyQueue" - } - """ - ResponseHeaders headers = ResponseHeaders.builder(HttpStatus.OK) - .contentType(MediaType.PLAIN_TEXT_UTF_8) - .add("x-amzn-RequestId", "7a62c49f-347e-4fc4-9331-6e8e7a96aa73") - .build() - return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)) - } - "Sqs" | "SendMessage" | "POST" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsAsyncClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()) } | { - if (!Boolean.getBoolean("testLatestDeps")) { - def content = """ - - - d41d8cd98f00b204e9800998ecf8427e - 3ae8f24a165a8cedc005670c81a27295 - 5fea7756-0ea4-451a-a703-a558b933e274 - - 27daac76-34dd-47df-bd01-1f6e873584a0 - - """ - return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content) - } - def content = """ - { - "MD5OfMessageBody":"d41d8cd98f00b204e9800998ecf8427e", - "MD5OfMessageAttributes":"3ae8f24a165a8cedc005670c81a27295", - "MessageId":"5fea7756-0ea4-451a-a703-a558b933e274" - } - """ - ResponseHeaders headers = ResponseHeaders.builder(HttpStatus.OK) - .contentType(MediaType.PLAIN_TEXT_UTF_8) - .add("x-amzn-RequestId", "27daac76-34dd-47df-bd01-1f6e873584a0") - .build() - return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)) - } - "Ec2" | "AllocateAddress" | "POST" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2AsyncClient.builder() | { c -> c.allocateAddress() } | """ - - 59dbff89-35bd-4eac-99ed-be587EXAMPLE - 192.0.2.1 - standard - - """ - "Rds" | "DeleteOptionGroup" | "POST" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsAsyncClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """ - - 0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99 - - """ - "Sns" | "Publish" | "POST" | "f187a3c1-376f-11df-8963-01868b7c937a" | SnsAsyncClient.builder() | { SnsAsyncClient c -> c.publish(r -> r.message("hello").topicArn("somearn")) } | """ - - - 94f20ce6-13c5-43a0-9a9e-ca52d816e90b - - - f187a3c1-376f-11df-8963-01868b7c937a - - - """ - } - - // TODO(anuraaga): Without AOP instrumentation of the HTTP client, we cannot model retries as - // spans because of https://github.com/aws/aws-sdk-java-v2/issues/1741. We should at least tweak - // the instrumentation to add Events for retries instead. - def "timeout and retry errors not captured"() { - setup: - // One retry so two requests. - server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofMillis(5000))) - server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofMillis(5000))) - def builder = S3Client.builder() - .overrideConfiguration(createOverrideConfigurationBuilder() - .retryPolicy(RetryPolicy.builder().numRetries(1).build()) - .build()) - .endpointOverride(clientUri) - .region(Region.AP_NORTHEAST_1) - .credentialsProvider(CREDENTIALS_PROVIDER) - .httpClientBuilder(ApacheHttpClient.builder().socketTimeout(Duration.ofMillis(50))) - - if (Boolean.getBoolean("testLatestDeps")) { - builder.forcePathStyle(true) - } - - def client = builder.build() - - when: - client.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) - - then: - thrown SdkClientException - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "S3.GetObject" - kind CLIENT - status ERROR - errorEvent SdkClientException, "Unable to execute HTTP request: Read timed out" - hasNoParent() - attributes { - // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the hostname with the bucket name in case - // the bucket name is a valid DNS label, even in the case that we are using an endpoint override. - // Previously the sdk was only doing that if endpoint had "s3" as label in the FQDN. - // Our test assert both cases so that we don't need to know what version is being tested. - "$ServerAttributes.SERVER_ADDRESS" { it == "somebucket.localhost" || it == "localhost" } - "$UrlAttributes.URL_FULL" { it == "http://somebucket.localhost:${server.httpPort()}/somekey" || it == "http://localhost:${server.httpPort()}/somebucket/somekey" } - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "S3" - "$RpcIncubatingAttributes.RPC_METHOD" "GetObject" - "aws.agent" "java-aws-sdk" - "aws.bucket.name" "somebucket" - } - } - } - } - } -} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 9536a022be9a..bdd7c83bce92 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -1,5 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + package io.opentelemetry.instrumentation.awssdk.v2_2; +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; +import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.common.collect.ImmutableMap; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; @@ -10,6 +31,11 @@ import io.opentelemetry.testing.internal.armeria.common.MediaType; import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension; import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.RecordedRequest; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -21,6 +47,8 @@ import software.amazon.awssdk.core.client.builder.SdkClientBuilder; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClientBuilder; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; @@ -34,33 +62,11 @@ import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.awssdk.services.dynamodb.model.QueryRequest; import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; -import java.util.Arrays; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.function.Function; -import java.util.stream.Stream; - -import static com.google.common.collect.ImmutableMap.of; -import static io.opentelemetry.api.common.AttributeKey.stringKey; -import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; -import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; -import static io.opentelemetry.semconv.SemanticAttributes.DB_OPERATION; -import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; -import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; -import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; -import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; -import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; -import static org.assertj.core.api.Assertions.assertThat; public abstract class AbstractAws2ClientCoreTest { protected abstract InstrumentationExtension getTesting(); - abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); + protected abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); static boolean isSqsAttributeInjectionEnabled() { // See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor @@ -68,8 +74,9 @@ static boolean isSqsAttributeInjectionEnabled() { "otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false); } - static final StaticCredentialsProvider CREDENTIALS_PROVIDER = StaticCredentialsProvider - .create(AwsBasicCredentials.create("my-access-key", "my-secret-key")); + static final StaticCredentialsProvider CREDENTIALS_PROVIDER = + StaticCredentialsProvider.create( + AwsBasicCredentials.create("my-access-key", "my-secret-key")); static MockWebServerExtension server = new MockWebServerExtension(); @@ -88,85 +95,202 @@ void prepTest() { server.beforeTestExecution(null); } - void configureSdkClient(SdkClientBuilder builder) { + void configureSdkClient(SdkClientBuilder builder) { builder.overrideConfiguration(createOverrideConfigurationBuilder().build()); } - static ImmutableMap createTableRequestKey = ImmutableMap.of( - "anotherKey", AttributeValue.builder().s("value").build(), - "key", AttributeValue.builder().s("value").build()); + static ImmutableMap createTableRequestKey = + ImmutableMap.of( + "anotherKey", AttributeValue.builder().s("value").build(), + "key", AttributeValue.builder().s("value").build()); - static ImmutableMap getItemRequestKey = ImmutableMap.of( - "keyOne", AttributeValue.builder().s("value").build(), - "keyTwo", AttributeValue.builder().s("differentValue").build()); + static ImmutableMap getItemRequestKey = + ImmutableMap.of( + "keyOne", AttributeValue.builder().s("value").build(), + "keyTwo", AttributeValue.builder().s("differentValue").build()); - static ImmutableMap putItemRequestKey = ImmutableMap.of( - "key", AttributeValue.builder().s("value").build(), - "attributeOne", AttributeValue.builder().s("one").build(), - "attributeTwo", AttributeValue.builder().s("two").build()); + static ImmutableMap putItemRequestKey = + ImmutableMap.of( + "key", AttributeValue.builder().s("value").build(), + "attributeOne", AttributeValue.builder().s("one").build(), + "attributeTwo", AttributeValue.builder().s("two").build()); private static Stream provideArguments() { return Stream.of( - Arguments.of("CreateTable", + Arguments.of( + "CreateTable", (Function) c -> c.createTable(createTableRequest())), - Arguments.of("DeleteItem", - (Function) c -> c.deleteItem(DeleteItemRequest.builder() - .tableName("sometable") - .key(createTableRequestKey) - .conditionExpression("property in (:one, :two)") - .build())), - Arguments.of("DeleteItem", - (Function) c -> c.deleteTable( - DeleteTableRequest.builder().tableName("sometable").build())), - Arguments.of("GetItem", - (Function) c -> c.getItem( - GetItemRequest.builder() - .tableName("sometable") - .key(getItemRequestKey) - .attributesToGet("propertyOne", "propertyTwo") - .build())), - Arguments.of("PutItem", - (Function) c -> c.putItem( - PutItemRequest.builder() - .tableName("sometable") - .item(putItemRequestKey) - .conditionExpression("attributeOne <> :someVal") - .build())), - Arguments.of("Query", - (Function) c -> c.query( - QueryRequest.builder() - .tableName("sometable") - .select("ALL_ATTRIBUTES") - .keyConditionExpression("attribute = :aValue") - .filterExpression("anotherAttribute = :someVal") - .limit(10).build())), - Arguments.of("UpdateItem", - (Function) c -> c.updateItem( - UpdateItemRequest.builder() - .tableName("sometable") - .key( - of("keyOne", AttributeValue.builder().s("value").build(), - "keyTwo", AttributeValue.builder().s("differentValue").build())) - .conditionExpression("attributeOne <> :someVal") - .updateExpression("set attributeOne = :updateValue") - .build())) - ); + Arguments.of( + "DeleteItem", + (Function) + c -> + c.deleteItem( + DeleteItemRequest.builder() + .tableName("sometable") + .key(createTableRequestKey) + .conditionExpression("property in (:one, :two)") + .build())), + Arguments.of( + "DeleteTable", + (Function) + c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build())), + Arguments.of( + "GetItem", + (Function) + c -> + c.getItem( + GetItemRequest.builder() + .tableName("sometable") + .key(getItemRequestKey) + .attributesToGet("propertyOne", "propertyTwo") + .build())), + Arguments.of( + "PutItem", + (Function) + c -> + c.putItem( + PutItemRequest.builder() + .tableName("sometable") + .item(putItemRequestKey) + .conditionExpression("attributeOne <> :someVal") + .build())), + Arguments.of( + "Query", + (Function) + c -> + c.query( + QueryRequest.builder() + .tableName("sometable") + .select("ALL_ATTRIBUTES") + .keyConditionExpression("attribute = :aValue") + .filterExpression("anotherAttribute = :someVal") + .limit(10) + .build())), + Arguments.of( + "UpdateItem", + (Function) + c -> + c.updateItem( + UpdateItemRequest.builder() + .tableName("sometable") + .key( + ImmutableMap.of( + "keyOne", + AttributeValue.builder().s("value").build(), + "keyTwo", + AttributeValue.builder().s("differentValue").build())) + .conditionExpression("attributeOne <> :someVal") + .updateExpression("set attributeOne = :updateValue") + .build()))); } @ParameterizedTest @MethodSource("provideArguments") - void testSendDynamoDbRequestWithBuilderMockedResponse(String operation, - Function call) throws ExecutionException, InterruptedException { + void testSendDynamoDbRequestWithBuilderAndMockedResponse( + String operation, Function call) + throws ExecutionException, InterruptedException { DynamoDbClientBuilder builder = DynamoDbClient.builder(); configureSdkClient(builder); - DynamoDbClient client = builder - .endpointOverride(server.httpUri()) - .region(Region.AP_NORTHEAST_1) - .credentialsProvider(CREDENTIALS_PROVIDER) - .build(); + DynamoDbClient client = + builder + .endpointOverride(server.httpUri()) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); Object response = call.apply(client); + executeCall(operation, response); + } + private static Stream provideAsyncArguments() { + return Stream.of( + Arguments.of( + "CreateTable", + (Function) c -> c.createTable(createTableRequest())), + Arguments.of( + "DeleteItem", + (Function) + c -> + c.deleteItem( + DeleteItemRequest.builder() + .tableName("sometable") + .key(createTableRequestKey) + .conditionExpression("property in (:one, :two)") + .build())), + Arguments.of( + "DeleteTable", + (Function) + c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build())), + Arguments.of( + "GetItem", + (Function) + c -> + c.getItem( + GetItemRequest.builder() + .tableName("sometable") + .key(getItemRequestKey) + .attributesToGet("propertyOne", "propertyTwo") + .build())), + Arguments.of( + "PutItem", + (Function) + c -> + c.putItem( + PutItemRequest.builder() + .tableName("sometable") + .item(putItemRequestKey) + .conditionExpression("attributeOne <> :someVal") + .build())), + Arguments.of( + "Query", + (Function) + c -> + c.query( + QueryRequest.builder() + .tableName("sometable") + .select("ALL_ATTRIBUTES") + .keyConditionExpression("attribute = :aValue") + .filterExpression("anotherAttribute = :someVal") + .limit(10) + .build())), + Arguments.of( + "UpdateItem", + (Function) + c -> + c.updateItem( + UpdateItemRequest.builder() + .tableName("sometable") + .key( + ImmutableMap.of( + "keyOne", + AttributeValue.builder().s("value").build(), + "keyTwo", + AttributeValue.builder().s("differentValue").build())) + .conditionExpression("attributeOne <> :someVal") + .updateExpression("set attributeOne = :updateValue") + .build()))); + } + + @ParameterizedTest + @MethodSource("provideAsyncArguments") + void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( + String operation, Function call) + throws ExecutionException, InterruptedException { + DynamoDbAsyncClientBuilder builder = DynamoDbAsyncClient.builder(); + configureSdkClient(builder); + DynamoDbAsyncClient client = + builder + .endpointOverride(server.httpUri()) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); + Object response = call.apply(client); + executeCall(operation, response); + } + + private void executeCall(String operation, Object response) + throws ExecutionException, InterruptedException { if (response instanceof Future) { response = ((Future) response).get(); } @@ -177,101 +301,94 @@ void testSendDynamoDbRequestWithBuilderMockedResponse(String operation, RecordedRequest request = server.takeRequest(); assertThat(request).isNotNull(); assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); - assertThat(request.request().headers().get("traceparent")).isNotNull(); - + assertThat(request.request().headers().get("traceparent")).isNull(); - getTesting().waitAndAssertTraces( - trace -> trace.hasSpansSatisfyingExactly( - span -> { - if (operation.equals("CreateTable")) { - assertCreateTableRequest(span); - } else if (operation.equals("Query")) { - assertQueryRequest(span); - } else { - assertDynamoDbRequest(span, operation); - } - } - ) - ); + getTesting() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> { + if (operation.equals("CreateTable")) { + assertCreateTableRequest(span); + } else if (operation.equals("Query")) { + assertQueryRequest(span); + } else { + assertDynamoDbRequest(span, operation); + } + })); } static CreateTableRequest createTableRequest() { return CreateTableRequest.builder() .tableName("sometable") - .globalSecondaryIndexes(Arrays.asList( - GlobalSecondaryIndex.builder() - .indexName("globalIndex") - .keySchema( - KeySchemaElement.builder() - .attributeName("attribute") - .build()) - .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(10l) - .writeCapacityUnits(12l) - .build() - ) - .build(), - GlobalSecondaryIndex.builder() - .indexName("globalIndexSecondary") - .keySchema( - KeySchemaElement.builder() - .attributeName("attributeSecondary") - .build()) - .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(7l) - .writeCapacityUnits(8l) - .build() - ) - .build())) + .globalSecondaryIndexes( + Arrays.asList( + GlobalSecondaryIndex.builder() + .indexName("globalIndex") + .keySchema(KeySchemaElement.builder().attributeName("attribute").build()) + .provisionedThroughput( + ProvisionedThroughput.builder() + .readCapacityUnits(10L) + .writeCapacityUnits(12L) + .build()) + .build(), + GlobalSecondaryIndex.builder() + .indexName("globalIndexSecondary") + .keySchema( + KeySchemaElement.builder().attributeName("attributeSecondary").build()) + .provisionedThroughput( + ProvisionedThroughput.builder() + .readCapacityUnits(7L) + .writeCapacityUnits(8L) + .build()) + .build())) .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(1l) - .writeCapacityUnits(1l) - .build() - ) + ProvisionedThroughput.builder().readCapacityUnits(1L).writeCapacityUnits(1L).build()) .build(); } - static SpanDataAssert assertCreateTableRequest(SpanDataAssert span) { - return span.hasName("DynamoDb.CreateTable") + @SuppressWarnings("deprecation") // uses deprecated semconv + static void assertCreateTableRequest(SpanDataAssert span) { + span.hasName("DynamoDb.CreateTable") .hasKind(SpanKind.CLIENT) .hasNoParent() .hasAttributesSatisfyingExactly( equalTo(SERVER_ADDRESS, "127.0.0.1"), equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri().toString()), + equalTo(URL_FULL, server.httpUri() + "/"), equalTo(HTTP_REQUEST_METHOD, "POST"), equalTo(HTTP_RESPONSE_STATUS_CODE, 200), equalTo(RPC_SYSTEM, "aws-api"), equalTo(RPC_SERVICE, "DynamoDb"), equalTo(RPC_METHOD, "CreateTable"), equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKOWN"), + equalTo(AWS_REQUEST_ID, "UNKNOWN"), equalTo(stringKey("aws.table.name"), "sometable"), equalTo(DB_SYSTEM, "dynamodb"), equalTo(maybeStable(DB_OPERATION), "CreateTable"), - equalTo(stringKey("aws.dynamodb.global_secondary_indexes"), "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]"), + equalTo( + stringKey("aws.dynamodb.global_secondary_indexes"), + "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]"), equalTo(stringKey("aws.dynamodb.provisioned_throughput.read_capacity_units"), "1"), equalTo(stringKey("aws.dynamodb.provisioned_throughput.write_capacity_units"), "1")); } - static SpanDataAssert assertQueryRequest(SpanDataAssert span) { - return span.hasName("DynamoDb.Query") + @SuppressWarnings("deprecation") // using deprecated semconv + static void assertQueryRequest(SpanDataAssert span) { + span.hasName("DynamoDb.Query") .hasKind(SpanKind.CLIENT) .hasNoParent() .hasAttributesSatisfyingExactly( equalTo(SERVER_ADDRESS, "127.0.0.1"), equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri().toString()), + equalTo(URL_FULL, server.httpUri() + "/"), equalTo(HTTP_REQUEST_METHOD, "POST"), equalTo(HTTP_RESPONSE_STATUS_CODE, 200), equalTo(RPC_SYSTEM, "aws-api"), equalTo(RPC_SERVICE, "DynamoDb"), equalTo(RPC_METHOD, "Query"), equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKOWN"), + equalTo(AWS_REQUEST_ID, "UNKNOWN"), equalTo(stringKey("aws.table.name"), "sometable"), equalTo(DB_SYSTEM, "dynamodb"), equalTo(maybeStable(DB_OPERATION), "Query"), @@ -279,21 +396,22 @@ static SpanDataAssert assertQueryRequest(SpanDataAssert span) { equalTo(stringKey("aws.dynamodb.select"), "ALL_ATTRIBUTES")); } - static SpanDataAssert assertDynamoDbRequest(SpanDataAssert span, String operation) { - return span.hasName("DynamoDb." + operation) + @SuppressWarnings("deprecation") // uses deprecated semconv + static void assertDynamoDbRequest(SpanDataAssert span, String operation) { + span.hasName("DynamoDb." + operation) .hasKind(SpanKind.CLIENT) .hasNoParent() .hasAttributesSatisfyingExactly( equalTo(SERVER_ADDRESS, "127.0.0.1"), equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri().toString()), + equalTo(URL_FULL, server.httpUri() + "/"), equalTo(HTTP_REQUEST_METHOD, "POST"), equalTo(HTTP_RESPONSE_STATUS_CODE, 200), equalTo(RPC_SYSTEM, "aws-api"), equalTo(RPC_SERVICE, "DynamoDb"), equalTo(RPC_METHOD, operation), equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKOWN"), + equalTo(AWS_REQUEST_ID, "UNKNOWN"), equalTo(stringKey("aws.table.name"), "sometable"), equalTo(DB_SYSTEM, "dynamodb"), equalTo(maybeStable(DB_OPERATION), operation)); diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index 0834b29c6ca2..560a3cd7bb3c 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -1,61 +1,171 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + package io.opentelemetry.instrumentation.awssdk.v2_2; +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; +import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.testing.internal.armeria.common.HttpData; import io.opentelemetry.testing.internal.armeria.common.HttpResponse; import io.opentelemetry.testing.internal.armeria.common.HttpStatus; import io.opentelemetry.testing.internal.armeria.common.MediaType; +import io.opentelemetry.testing.internal.armeria.common.ResponseHeaders; import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.RecordedRequest; +import java.lang.reflect.Method; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Stream; import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.ec2.Ec2AsyncClient; +import software.amazon.awssdk.services.ec2.Ec2AsyncClientBuilder; +import software.amazon.awssdk.services.ec2.Ec2Client; +import software.amazon.awssdk.services.ec2.Ec2ClientBuilder; +import software.amazon.awssdk.services.kinesis.KinesisClient; +import software.amazon.awssdk.services.kinesis.KinesisClientBuilder; +import software.amazon.awssdk.services.kinesis.model.DeleteStreamRequest; +import software.amazon.awssdk.services.rds.RdsAsyncClient; +import software.amazon.awssdk.services.rds.RdsAsyncClientBuilder; +import software.amazon.awssdk.services.rds.RdsClient; +import software.amazon.awssdk.services.rds.RdsClientBuilder; +import software.amazon.awssdk.services.rds.model.DeleteOptionGroupRequest; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.model.CreateBucketRequest; import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import java.lang.reflect.Method; -import java.net.URI; -import java.util.function.Function; -import java.util.stream.Stream; - -import static io.opentelemetry.api.common.AttributeKey.stringKey; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; -import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; -import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; -import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; -import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; -import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; -import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; -import static org.assertj.core.api.Assertions.assertThat; +import software.amazon.awssdk.services.sns.SnsClient; +import software.amazon.awssdk.services.sns.SnsClientBuilder; +import software.amazon.awssdk.services.sns.model.PublishRequest; +import software.amazon.awssdk.services.sqs.SqsAsyncClient; +import software.amazon.awssdk.services.sqs.SqsAsyncClientBuilder; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.SqsClientBuilder; +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; +import software.amazon.awssdk.services.sqs.model.SendMessageRequest; public abstract class AbstractAws2ClientTest extends AbstractAws2ClientCoreTest { private static final String QUEUE_URL = "http://xxx/somequeue"; - private void assumeSupportedConfig(String service, String operation) { + private static void assumeSupportedConfig(String operation) { Assumptions.assumeFalse( - service.equals("Sqs") - && operation.equals("SendMessage") - && isSqsAttributeInjectionEnabled(), + operation.equals("SendMessage") && isSqsAttributeInjectionEnabled(), "Cannot check Sqs.SendMessage here due to hard-coded MD5."); } - // Force localhost instead of relying on mock server because using ip is yet another corner case of the virtual - // bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to prefix the hostname with the + // Force localhost instead of relying on mock server because using ip is yet another corner case + // of the virtual + // bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to prefix the + // hostname with the // bucket name as label. URI clientUri = URI.create("http://localhost:" + server.httpPort()); + private static final Callable sqsCreateQueueResponse = + () -> { + String content; + if (!Boolean.getBoolean("testLatestDeps")) { + content = + "" + + " https://queue.amazonaws.com/123456789012/MyQueue" + + " 7a62c49f-347e-4fc4-9331-6e8e7a96aa73" + + " "; + return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content); + } + content = "{" + " \"QueueUrl\":\"https://queue.amazonaws.com/123456789012/MyQueue\"" + "}"; + ResponseHeaders headers = + ResponseHeaders.builder(HttpStatus.OK) + .contentType(MediaType.PLAIN_TEXT_UTF_8) + .add("x-amzn-RequestId", "7a62c49f-347e-4fc4-9331-6e8e7a96aa73") + .build(); + return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); + }; + + private static final Callable sqsSendMessageResponse = + () -> { + String content; + if (!Boolean.getBoolean("testLatestDeps")) { + content = + "" + + " " + + " d41d8cd98f00b204e9800998ecf8427e" + + " 3ae8f24a165a8cedc005670c81a27295" + + " 5fea7756-0ea4-451a-a703-a558b933e274" + + " " + + " 27daac76-34dd-47df-bd01-1f6e873584a0" + + ""; + return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content); + } + content = + "{" + + " \"MD5OfMessageBody\":\"d41d8cd98f00b204e9800998ecf8427e\"," + + " \"MD5OfMessageAttributes\":\"3ae8f24a165a8cedc005670c81a27295\"," + + " \"MessageId\":\"5fea7756-0ea4-451a-a703-a558b933e274\"" + + "}"; + ResponseHeaders headers = + ResponseHeaders.builder(HttpStatus.OK) + .contentType(MediaType.PLAIN_TEXT_UTF_8) + .add("x-amzn-RequestId", "27daac76-34dd-47df-bd01-1f6e873584a0") + .build(); + return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); + }; + + private static final String ec2BodyContent = + "" + + " 59dbff89-35bd-4eac-99ed-be587EXAMPLE" + + " 192.0.2.1" + + " standard" + + ""; + + private static final String rdsBodyContent = + "" + + " 0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" + + ""; + S3ClientBuilder s3ClientBuilder() throws Exception { S3ClientBuilder builder = S3Client.builder(); if (Boolean.getBoolean("testLatestDeps")) { - Method forcePathStyleMethod = S3ClientBuilder.class.getMethod("forcePathStyle", - Boolean.class); + Method forcePathStyleMethod = + S3ClientBuilder.class.getMethod("forcePathStyle", Boolean.class); forcePathStyleMethod.invoke(true); } return builder; @@ -64,8 +174,8 @@ S3ClientBuilder s3ClientBuilder() throws Exception { S3AsyncClientBuilder s3AsyncClientBuilder() throws Exception { S3AsyncClientBuilder builder = S3AsyncClient.builder(); if (Boolean.getBoolean("testLatestDeps")) { - Method forcePathStyleMethod = S3ClientBuilder.class.getMethod("forcePathStyle", - Boolean.class); + Method forcePathStyleMethod = + S3ClientBuilder.class.getMethod("forcePathStyle", Boolean.class); forcePathStyleMethod.invoke(true); } return builder; @@ -76,66 +186,511 @@ private static Stream provideS3Arguments() { Arguments.of( "CreateBucket", "PUT", - (Function) c -> c.createBucket( - CreateBucketRequest.builder().bucket("somebucket").build()) - ), - Arguments.of("GetObject", + (Function) + c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build())), + Arguments.of( + "GetObject", "GET", - (Function) c -> c.getObject( - GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) - )); + (Function) + c -> + c.getObject( + GetObjectRequest.builder().bucket("somebucket").key("somekey").build()))); } @ParameterizedTest @MethodSource("provideS3Arguments") - void testS3SendOperationRequestWithBuilder(String operation, String method, - Function call) throws Exception { - + void testS3SendOperationRequestWithBuilder( + String operation, String method, Function call) throws Exception { S3ClientBuilder builder = s3ClientBuilder(); configureSdkClient(builder); + S3Client client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); + + Object response = call.apply(client); + + assertThat(response.getClass().getSimpleName()) + .satisfiesAnyOf( + v -> assertThat(v).startsWith(operation), + v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + clientAssertions("S3", operation, method, response, "UNKNOWN"); + } + + private static Stream provideS3AsyncArguments() { + return Stream.of( + Arguments.of( + "CreateBucket", + "PUT", + (Function>) + c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()), + ""), + Arguments.of( + "GetObject", + "GET", + (Function>) + c -> + c.getObject( + GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), + AsyncResponseTransformer.toBytes()), + "1234567890")); + } + + @ParameterizedTest + @MethodSource("provideS3AsyncArguments") + void testS3AsyncSendOperationRequestWithBuilder( + String operation, String method, Function> call, String body) + throws Exception { + S3AsyncClientBuilder builder = s3AsyncClientBuilder(); + configureSdkClient(builder); + S3AsyncClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)); + + Future response = call.apply(client); + response.get(); + + clientAssertions("S3", operation, method, response, "UNKNOWN"); + } - S3Client client = builder - .endpointOverride(clientUri) - .region(Region.AP_NORTHEAST_1) - .credentialsProvider(CREDENTIALS_PROVIDER) - .build(); + @Test + void testKinesisSendOperationRequestWithBuilder() throws Exception { + KinesisClientBuilder builder = KinesisClient.builder(); + configureSdkClient(builder); + KinesisClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); + Object response = + client.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()); + + assertThat(response.getClass().getSimpleName()) + .satisfiesAnyOf( + v -> assertThat(v).startsWith("DeleteStream"), + v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + clientAssertions("Kinesis", "DeleteStream", "POST", response, "UNKNOWN"); + } + + private static Stream provideSqsArguments() { + return Stream.of( + Arguments.of( + "CreateQueue", + "7a62c49f-347e-4fc4-9331-6e8e7a96aa73", + sqsCreateQueueResponse, + (Function) + c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build())), + Arguments.of( + "SendMessage", + "27daac76-34dd-47df-bd01-1f6e873584a0", + sqsSendMessageResponse, + (Function) + c -> + c.sendMessage( + SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()))); + } + + @ParameterizedTest + @MethodSource("provideSqsArguments") + void testSqsSendOperationRequestWithBuilder( + String operation, + String requestId, + Callable serverResponse, + Function call) + throws Exception { + assumeSupportedConfig(operation); + + SqsClientBuilder builder = SqsClient.builder(); + configureSdkClient(builder); + SqsClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(serverResponse.call()); + Object response = call.apply(client); + + assertThat(response.getClass().getSimpleName()) + .satisfiesAnyOf( + v -> assertThat(v).startsWith(operation), + v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + clientAssertions("Sqs", operation, "POST", response, requestId); + } + + private static Stream provideSqsAsyncArguments() { + return Stream.of( + Arguments.of( + "CreateQueue", + "7a62c49f-347e-4fc4-9331-6e8e7a96aa73", + sqsCreateQueueResponse, + (Function>) + c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build())), + Arguments.of( + "SendMessage", + "27daac76-34dd-47df-bd01-1f6e873584a0", + sqsSendMessageResponse, + (Function>) + c -> + c.sendMessage( + SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()))); + } + + @ParameterizedTest + @MethodSource("provideSqsAsyncArguments") + void testSqsAsyncSendOperationRequestWithBuilder( + String operation, + String requestId, + Callable serverResponse, + Function> call) + throws Exception { + assumeSupportedConfig(operation); + + SqsAsyncClientBuilder builder = SqsAsyncClient.builder(); + configureSdkClient(builder); + SqsAsyncClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(serverResponse.call()); + Future response = call.apply(client); + response.get(); + + clientAssertions("Sqs", operation, "POST", response, requestId); + } + + private static Stream provideSnsArguments() { + return Stream.of( + Arguments.of( + (Function) + c -> + c.publish( + PublishRequest.builder() + .message("somemessage") + .topicArn("somearn") + .build()), + Arguments.of( + (Function) + c -> + c.publish( + PublishRequest.builder() + .message("somemessage") + .targetArn("somearn") + .build())))); + } + + @ParameterizedTest + @MethodSource("provideSnsArguments") + void testSnsSendOperationRequestWithBuilder(Function call) { + SnsClientBuilder builder = SnsClient.builder(); + configureSdkClient(builder); + SnsClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + String body = + "" + + " " + + " 567910cd-659e-55d4-8ccb-5aaf14679dc0" + + " " + + " " + + " d74b8436-ae13-5ab4-a9ff-ce54dfea72a0" + + " " + + ""; + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)); Object response = call.apply(client); + + assertThat(response.getClass().getSimpleName()) + .satisfiesAnyOf( + v -> assertThat(v).startsWith("Publish"), + v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + clientAssertions("Sns", "Publish", "POST", response, "d74b8436-ae13-5ab4-a9ff-ce54dfea72a0"); + } + + @Test + void testEc2SendOperationRequestWithBuilder() throws Exception { + Ec2ClientBuilder builder = Ec2Client.builder(); + configureSdkClient(builder); + Ec2Client client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + String content = + "" + + " 59dbff89-35bd-4eac-99ed-be587EXAMPLE" + + " 192.0.2.1" + + " standard" + + ""; + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content)); + Object response = client.allocateAddress(); + + assertThat(response.getClass().getSimpleName()) + .satisfiesAnyOf( + v -> assertThat(v).startsWith("AllocateAddress"), + v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + clientAssertions( + "Ec2", "AllocateAddress", "POST", response, "59dbff89-35bd-4eac-99ed-be587EXAMPLE"); + } + + @Test + void testEc2AsyncSendOperationRequestWithBuilder() throws Exception { + Ec2AsyncClientBuilder builder = Ec2AsyncClient.builder(); + configureSdkClient(builder); + Ec2AsyncClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, ec2BodyContent)); + Object response = client.allocateAddress(); + + clientAssertions( + "Ec2", "AllocateAddress", "POST", response, "59dbff89-35bd-4eac-99ed-be587EXAMPLE"); + } + + @Test + void testRdsSendOperationRequestWithBuilder() throws Exception { + RdsClientBuilder builder = RdsClient.builder(); + configureSdkClient(builder); + RdsClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, rdsBodyContent)); + Object response = client.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()); + + assertThat(response.getClass().getSimpleName()) + .satisfiesAnyOf( + v -> assertThat(v).startsWith("DeleteOptionGroup"), + v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + clientAssertions( + "Rds", "DeleteOptionGroup", "POST", response, "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99"); + } + + @Test + void testRdsAsyncSendOperationRequestWithBuilder() { + RdsAsyncClientBuilder builder = RdsAsyncClient.builder(); + configureSdkClient(builder); + RdsAsyncClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, rdsBodyContent)); + Object response = client.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()); + + clientAssertions( + "Rds", "DeleteOptionGroup", "POST", response, "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99"); + } + + @Test + void testTimeoutAndRetryErrorsAreNotCaptured() throws Exception { + // One retry so two requests. + server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofSeconds(5))); + server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofSeconds(5))); + S3ClientBuilder builder = + S3Client.builder() + .overrideConfiguration( + createOverrideConfigurationBuilder() + .retryPolicy(RetryPolicy.builder().numRetries(1).build()) + .build()) + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClientBuilder(ApacheHttpClient.builder().socketTimeout(Duration.ofMillis(50))); + + S3Client client = builder.build(); + + Throwable thrown = + catchThrowable( + () -> + client.getObject( + GetObjectRequest.builder().bucket("somebucket").key("somekey").build())); + + assertThat(thrown).isInstanceOf(SdkClientException.class); + assertThat(thrown).hasMessage("Unable to execute HTTP request: Read timed out"); + + getTesting() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("S3.GetObject") + .hasKind(SpanKind.CLIENT) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasNoParent() + .hasAttributesSatisfyingExactly( + // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the + // hostname with the bucket name + // in case the bucket name is a valid DNS label, even in the case + // that we are using an + // endpoint override. Previously the sdk was only doing that if + // endpoint had "s3" as label in + // the FQDN. Our test assert both cases so that we don't need to + // know what version is being + // tested. + satisfies( + SERVER_ADDRESS, + v -> v.matches("somebucket.localhost|localhost")), + satisfies( + URL_FULL, + val -> + val.satisfiesAnyOf( + v -> + assertThat(v) + .isEqualTo( + "http://somebucket.localhost:" + + server.httpPort() + + "/somekey"), + v -> + assertThat(v) + .isEqualTo( + "http://localhost:" + + server.httpPort() + + "/somebucket/somekey"))), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "S3"), + equalTo(RPC_METHOD, "GetObject"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(stringKey("aws.bucket.name"), "somebucket")))); + } + + @SuppressWarnings("deprecation") // uses deprecated semconv + private void clientAssertions( + String service, String operation, String method, Object response, String requestId) { assertThat(response).isNotNull(); - assertThat(response.getClass().getSimpleName()).startsWith(operation); RecordedRequest request = server.takeRequest(); assertThat(request).isNotNull(); assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); - assertThat(request.request().headers().get("traceparent")).isNotNull(); - - getTesting().waitAndAssertTraces( - trace -> trace.hasSpansSatisfyingExactly( - span -> span.hasName("S3." + operation) - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - satisfies(SERVER_ADDRESS, - v -> v.matches("somebucket.localhost|localhost")), - satisfies(URL_FULL, - val -> val.satisfiesAnyOf( - v -> assertThat(v).startsWith("http://somebucket.localhost:" + server.httpPort()), - v -> assertThat(v).startsWith("http://localhost:" + server.httpPort() + "/somebucket") - )), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(HTTP_REQUEST_METHOD, method), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "S3"), - equalTo(RPC_METHOD, operation), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKOWN"), - equalTo(stringKey("aws.bucket.name"), "somebucket")))); + assertThat(request.request().headers().get("traceparent")).isNull(); + List attributes = + new ArrayList<>( + asList( + // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the hostname with the + // bucket name + // in case the bucket name is a valid DNS label, even in the case that we are using + // an + // endpoint override. Previously the sdk was only doing that if endpoint had "s3" as + // label in + // the FQDN. Our test assert both cases so that we don't need to know what version + // is being + // tested. + satisfies(SERVER_ADDRESS, v -> v.matches("somebucket.localhost|localhost")), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(HTTP_REQUEST_METHOD, method), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, service), + equalTo(RPC_METHOD, operation), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, requestId))); - } + if (service.equals("S3")) { + attributes.addAll( + new ArrayList<>( + asList( + satisfies( + URL_FULL, + val -> + val.satisfiesAnyOf( + v -> + assertThat(v) + .startsWith( + "http://somebucket.localhost:" + server.httpPort()), + v -> + assertThat(v) + .startsWith( + "http://localhost:" + + server.httpPort() + + "/somebucket"))), + equalTo(stringKey("aws.bucket.name"), "somebucket")))); + } else { + attributes.addAll( + new ArrayList<>( + asList( + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + URL_FULL, val -> val.startsWith("http://localhost:" + server.httpPort()))))); + } + if (service.equals("Kinesis")) { + attributes.add(equalTo(stringKey("aws.stream.name"), "somestream")); + } + if (service.equals("Sns")) { + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "somearn")); + } + + if (service.equals("Sqs") && operation.equals("CreateQueue")) { + attributes.add(equalTo(stringKey("aws.queue.name"), "somequeue")); + } + + if (service.equals("Sqs") && operation.equals("SendMessage")) { + attributes.addAll( + new ArrayList<>( + asList( + equalTo(stringKey("aws.queue.url"), QUEUE_URL), + equalTo(MESSAGING_DESTINATION_NAME, "somequeue"), + equalTo(MESSAGING_OPERATION, "publish"), + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), + equalTo(MESSAGING_SYSTEM, AWS_SQS)))); + } + + String evaluatedOperation; + SpanKind operationKind; + if (operation.equals("SendMessage")) { + evaluatedOperation = "somequeue publish"; + operationKind = SpanKind.PRODUCER; + } else { + operationKind = SpanKind.CLIENT; + evaluatedOperation = service + "." + operation; + } + + getTesting() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName(evaluatedOperation) + .hasKind(operationKind) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes))); + } } From 4f0e617896b4dca4429f4feb43a73dbd8c34a416 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Sun, 22 Dec 2024 11:36:26 -0500 Subject: [PATCH 04/14] cleanup --- .../v2_2/AbstractAws2ClientCoreTest.java | 256 +++++++++--------- .../awssdk/v2_2/AbstractAws2ClientTest.java | 208 +++++++------- 2 files changed, 232 insertions(+), 232 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index bdd7c83bce92..9149c150bc90 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -115,6 +115,134 @@ void configureSdkClient(SdkClientBuilder builder) { "attributeOne", AttributeValue.builder().s("one").build(), "attributeTwo", AttributeValue.builder().s("two").build()); + private void executeCall(String operation, Object response) + throws ExecutionException, InterruptedException { + if (response instanceof Future) { + response = ((Future) response).get(); + } + + assertThat(response).isNotNull(); + assertThat(response.getClass().getSimpleName()).startsWith(operation); + + RecordedRequest request = server.takeRequest(); + assertThat(request).isNotNull(); + assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); + assertThat(request.request().headers().get("traceparent")).isNull(); + + getTesting() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> { + if (operation.equals("CreateTable")) { + assertCreateTableRequest(span); + } else if (operation.equals("Query")) { + assertQueryRequest(span); + } else { + assertDynamoDbRequest(span, operation); + } + })); + } + + static CreateTableRequest createTableRequest() { + return CreateTableRequest.builder() + .tableName("sometable") + .globalSecondaryIndexes( + Arrays.asList( + GlobalSecondaryIndex.builder() + .indexName("globalIndex") + .keySchema(KeySchemaElement.builder().attributeName("attribute").build()) + .provisionedThroughput( + ProvisionedThroughput.builder() + .readCapacityUnits(10L) + .writeCapacityUnits(12L) + .build()) + .build(), + GlobalSecondaryIndex.builder() + .indexName("globalIndexSecondary") + .keySchema( + KeySchemaElement.builder().attributeName("attributeSecondary").build()) + .provisionedThroughput( + ProvisionedThroughput.builder() + .readCapacityUnits(7L) + .writeCapacityUnits(8L) + .build()) + .build())) + .provisionedThroughput( + ProvisionedThroughput.builder().readCapacityUnits(1L).writeCapacityUnits(1L).build()) + .build(); + } + + @SuppressWarnings("deprecation") // uses deprecated semconv + static void assertCreateTableRequest(SpanDataAssert span) { + span.hasName("DynamoDb.CreateTable") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(URL_FULL, server.httpUri() + "/"), + equalTo(HTTP_REQUEST_METHOD, "POST"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "DynamoDb"), + equalTo(RPC_METHOD, "CreateTable"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, "UNKNOWN"), + equalTo(stringKey("aws.table.name"), "sometable"), + equalTo(DB_SYSTEM, "dynamodb"), + equalTo(maybeStable(DB_OPERATION), "CreateTable"), + equalTo( + stringKey("aws.dynamodb.global_secondary_indexes"), + "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]"), + equalTo(stringKey("aws.dynamodb.provisioned_throughput.read_capacity_units"), "1"), + equalTo(stringKey("aws.dynamodb.provisioned_throughput.write_capacity_units"), "1")); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + static void assertQueryRequest(SpanDataAssert span) { + span.hasName("DynamoDb.Query") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(URL_FULL, server.httpUri() + "/"), + equalTo(HTTP_REQUEST_METHOD, "POST"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "DynamoDb"), + equalTo(RPC_METHOD, "Query"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, "UNKNOWN"), + equalTo(stringKey("aws.table.name"), "sometable"), + equalTo(DB_SYSTEM, "dynamodb"), + equalTo(maybeStable(DB_OPERATION), "Query"), + equalTo(stringKey("aws.dynamodb.limit"), "10"), + equalTo(stringKey("aws.dynamodb.select"), "ALL_ATTRIBUTES")); + } + + @SuppressWarnings("deprecation") // uses deprecated semconv + static void assertDynamoDbRequest(SpanDataAssert span, String operation) { + span.hasName("DynamoDb." + operation) + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(URL_FULL, server.httpUri() + "/"), + equalTo(HTTP_REQUEST_METHOD, "POST"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "DynamoDb"), + equalTo(RPC_METHOD, operation), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, "UNKNOWN"), + equalTo(stringKey("aws.table.name"), "sometable"), + equalTo(DB_SYSTEM, "dynamodb"), + equalTo(maybeStable(DB_OPERATION), operation)); + } + private static Stream provideArguments() { return Stream.of( Arguments.of( @@ -288,132 +416,4 @@ void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( Object response = call.apply(client); executeCall(operation, response); } - - private void executeCall(String operation, Object response) - throws ExecutionException, InterruptedException { - if (response instanceof Future) { - response = ((Future) response).get(); - } - - assertThat(response).isNotNull(); - assertThat(response.getClass().getSimpleName()).startsWith(operation); - - RecordedRequest request = server.takeRequest(); - assertThat(request).isNotNull(); - assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); - assertThat(request.request().headers().get("traceparent")).isNull(); - - getTesting() - .waitAndAssertTraces( - trace -> - trace.hasSpansSatisfyingExactly( - span -> { - if (operation.equals("CreateTable")) { - assertCreateTableRequest(span); - } else if (operation.equals("Query")) { - assertQueryRequest(span); - } else { - assertDynamoDbRequest(span, operation); - } - })); - } - - static CreateTableRequest createTableRequest() { - return CreateTableRequest.builder() - .tableName("sometable") - .globalSecondaryIndexes( - Arrays.asList( - GlobalSecondaryIndex.builder() - .indexName("globalIndex") - .keySchema(KeySchemaElement.builder().attributeName("attribute").build()) - .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(10L) - .writeCapacityUnits(12L) - .build()) - .build(), - GlobalSecondaryIndex.builder() - .indexName("globalIndexSecondary") - .keySchema( - KeySchemaElement.builder().attributeName("attributeSecondary").build()) - .provisionedThroughput( - ProvisionedThroughput.builder() - .readCapacityUnits(7L) - .writeCapacityUnits(8L) - .build()) - .build())) - .provisionedThroughput( - ProvisionedThroughput.builder().readCapacityUnits(1L).writeCapacityUnits(1L).build()) - .build(); - } - - @SuppressWarnings("deprecation") // uses deprecated semconv - static void assertCreateTableRequest(SpanDataAssert span) { - span.hasName("DynamoDb.CreateTable") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri() + "/"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "DynamoDb"), - equalTo(RPC_METHOD, "CreateTable"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKNOWN"), - equalTo(stringKey("aws.table.name"), "sometable"), - equalTo(DB_SYSTEM, "dynamodb"), - equalTo(maybeStable(DB_OPERATION), "CreateTable"), - equalTo( - stringKey("aws.dynamodb.global_secondary_indexes"), - "[{\"IndexName\":\"globalIndex\",\"KeySchema\":[{\"AttributeName\":\"attribute\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":10,\"WriteCapacityUnits\":12}},{\"IndexName\":\"globalIndexSecondary\",\"KeySchema\":[{\"AttributeName\":\"attributeSecondary\"}],\"ProvisionedThroughput\":{\"ReadCapacityUnits\":7,\"WriteCapacityUnits\":8}}]"), - equalTo(stringKey("aws.dynamodb.provisioned_throughput.read_capacity_units"), "1"), - equalTo(stringKey("aws.dynamodb.provisioned_throughput.write_capacity_units"), "1")); - } - - @SuppressWarnings("deprecation") // using deprecated semconv - static void assertQueryRequest(SpanDataAssert span) { - span.hasName("DynamoDb.Query") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri() + "/"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "DynamoDb"), - equalTo(RPC_METHOD, "Query"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKNOWN"), - equalTo(stringKey("aws.table.name"), "sometable"), - equalTo(DB_SYSTEM, "dynamodb"), - equalTo(maybeStable(DB_OPERATION), "Query"), - equalTo(stringKey("aws.dynamodb.limit"), "10"), - equalTo(stringKey("aws.dynamodb.select"), "ALL_ATTRIBUTES")); - } - - @SuppressWarnings("deprecation") // uses deprecated semconv - static void assertDynamoDbRequest(SpanDataAssert span, String operation) { - span.hasName("DynamoDb." + operation) - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri() + "/"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "DynamoDb"), - equalTo(RPC_METHOD, operation), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKNOWN"), - equalTo(stringKey("aws.table.name"), "sometable"), - equalTo(DB_SYSTEM, "dynamodb"), - equalTo(maybeStable(DB_OPERATION), operation)); - } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index 560a3cd7bb3c..5337c9b831f5 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -181,6 +181,110 @@ S3AsyncClientBuilder s3AsyncClientBuilder() throws Exception { return builder; } + @SuppressWarnings("deprecation") // uses deprecated semconv + private void clientAssertions( + String service, String operation, String method, Object response, String requestId) { + assertThat(response).isNotNull(); + + RecordedRequest request = server.takeRequest(); + assertThat(request).isNotNull(); + assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); + assertThat(request.request().headers().get("traceparent")).isNull(); + + List attributes = + new ArrayList<>( + asList( + // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the hostname with the + // bucket name + // in case the bucket name is a valid DNS label, even in the case that we are using + // an + // endpoint override. Previously the sdk was only doing that if endpoint had "s3" as + // label in + // the FQDN. Our test assert both cases so that we don't need to know what version + // is being + // tested. + satisfies(SERVER_ADDRESS, v -> v.matches("somebucket.localhost|localhost")), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(HTTP_REQUEST_METHOD, method), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, service), + equalTo(RPC_METHOD, operation), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(AWS_REQUEST_ID, requestId))); + + if (service.equals("S3")) { + attributes.addAll( + new ArrayList<>( + asList( + satisfies( + URL_FULL, + val -> + val.satisfiesAnyOf( + v -> + assertThat(v) + .startsWith( + "http://somebucket.localhost:" + server.httpPort()), + v -> + assertThat(v) + .startsWith( + "http://localhost:" + + server.httpPort() + + "/somebucket"))), + equalTo(stringKey("aws.bucket.name"), "somebucket")))); + } else { + attributes.addAll( + new ArrayList<>( + asList( + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + URL_FULL, val -> val.startsWith("http://localhost:" + server.httpPort()))))); + } + + if (service.equals("Kinesis")) { + attributes.add(equalTo(stringKey("aws.stream.name"), "somestream")); + } + + if (service.equals("Sns")) { + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "somearn")); + } + + if (service.equals("Sqs") && operation.equals("CreateQueue")) { + attributes.add(equalTo(stringKey("aws.queue.name"), "somequeue")); + } + + if (service.equals("Sqs") && operation.equals("SendMessage")) { + attributes.addAll( + new ArrayList<>( + asList( + equalTo(stringKey("aws.queue.url"), QUEUE_URL), + equalTo(MESSAGING_DESTINATION_NAME, "somequeue"), + equalTo(MESSAGING_OPERATION, "publish"), + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), + equalTo(MESSAGING_SYSTEM, AWS_SQS)))); + } + + String evaluatedOperation; + SpanKind operationKind; + if (operation.equals("SendMessage")) { + evaluatedOperation = "somequeue publish"; + operationKind = SpanKind.PRODUCER; + } else { + operationKind = SpanKind.CLIENT; + evaluatedOperation = service + "." + operation; + } + + getTesting() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName(evaluatedOperation) + .hasKind(operationKind) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes))); + } + private static Stream provideS3Arguments() { return Stream.of( Arguments.of( @@ -589,108 +693,4 @@ void testTimeoutAndRetryErrorsAreNotCaptured() throws Exception { equalTo(stringKey("aws.agent"), "java-aws-sdk"), equalTo(stringKey("aws.bucket.name"), "somebucket")))); } - - @SuppressWarnings("deprecation") // uses deprecated semconv - private void clientAssertions( - String service, String operation, String method, Object response, String requestId) { - assertThat(response).isNotNull(); - - RecordedRequest request = server.takeRequest(); - assertThat(request).isNotNull(); - assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); - assertThat(request.request().headers().get("traceparent")).isNull(); - - List attributes = - new ArrayList<>( - asList( - // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the hostname with the - // bucket name - // in case the bucket name is a valid DNS label, even in the case that we are using - // an - // endpoint override. Previously the sdk was only doing that if endpoint had "s3" as - // label in - // the FQDN. Our test assert both cases so that we don't need to know what version - // is being - // tested. - satisfies(SERVER_ADDRESS, v -> v.matches("somebucket.localhost|localhost")), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(HTTP_REQUEST_METHOD, method), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, service), - equalTo(RPC_METHOD, operation), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, requestId))); - - if (service.equals("S3")) { - attributes.addAll( - new ArrayList<>( - asList( - satisfies( - URL_FULL, - val -> - val.satisfiesAnyOf( - v -> - assertThat(v) - .startsWith( - "http://somebucket.localhost:" + server.httpPort()), - v -> - assertThat(v) - .startsWith( - "http://localhost:" - + server.httpPort() - + "/somebucket"))), - equalTo(stringKey("aws.bucket.name"), "somebucket")))); - } else { - attributes.addAll( - new ArrayList<>( - asList( - equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - URL_FULL, val -> val.startsWith("http://localhost:" + server.httpPort()))))); - } - - if (service.equals("Kinesis")) { - attributes.add(equalTo(stringKey("aws.stream.name"), "somestream")); - } - - if (service.equals("Sns")) { - attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "somearn")); - } - - if (service.equals("Sqs") && operation.equals("CreateQueue")) { - attributes.add(equalTo(stringKey("aws.queue.name"), "somequeue")); - } - - if (service.equals("Sqs") && operation.equals("SendMessage")) { - attributes.addAll( - new ArrayList<>( - asList( - equalTo(stringKey("aws.queue.url"), QUEUE_URL), - equalTo(MESSAGING_DESTINATION_NAME, "somequeue"), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(MESSAGING_SYSTEM, AWS_SQS)))); - } - - String evaluatedOperation; - SpanKind operationKind; - if (operation.equals("SendMessage")) { - evaluatedOperation = "somequeue publish"; - operationKind = SpanKind.PRODUCER; - } else { - operationKind = SpanKind.CLIENT; - evaluatedOperation = service + "." + operation; - } - - getTesting() - .waitAndAssertTraces( - trace -> - trace.hasSpansSatisfyingExactly( - span -> - span.hasName(evaluatedOperation) - .hasKind(operationKind) - .hasNoParent() - .hasAttributesSatisfyingExactly(attributes))); - } } From 8c5074cc98e17b8b899b8c09e490b2c7851a999f Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Sun, 22 Dec 2024 11:40:20 -0500 Subject: [PATCH 05/14] cleanup --- .../awssdk/v2_2/AbstractAws2ClientTest.java | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index 5337c9b831f5..bd9391e9f1ea 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -94,10 +94,8 @@ private static void assumeSupportedConfig(String operation) { } // Force localhost instead of relying on mock server because using ip is yet another corner case - // of the virtual - // bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to prefix the - // hostname with the - // bucket name as label. + // of the virtual bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to + // prefix the hostname with the bucket name as label. URI clientUri = URI.create("http://localhost:" + server.httpPort()); private static final Callable sqsCreateQueueResponse = @@ -195,14 +193,10 @@ private void clientAssertions( new ArrayList<>( asList( // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the hostname with the - // bucket name - // in case the bucket name is a valid DNS label, even in the case that we are using - // an - // endpoint override. Previously the sdk was only doing that if endpoint had "s3" as - // label in - // the FQDN. Our test assert both cases so that we don't need to know what version - // is being - // tested. + // bucket name in case the bucket name is a valid DNS label, even in the case that + // we are using an endpoint override. Previously the sdk was only doing that if + // endpoint had "s3" as label in the FQDN. Our test assert both cases so that we + // don't need to know what version is being tested. satisfies(SERVER_ADDRESS, v -> v.matches("somebucket.localhost|localhost")), equalTo(SERVER_PORT, server.httpPort()), equalTo(HTTP_REQUEST_METHOD, method), @@ -658,14 +652,11 @@ void testTimeoutAndRetryErrorsAreNotCaptured() throws Exception { .hasNoParent() .hasAttributesSatisfyingExactly( // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the - // hostname with the bucket name - // in case the bucket name is a valid DNS label, even in the case - // that we are using an - // endpoint override. Previously the sdk was only doing that if - // endpoint had "s3" as label in - // the FQDN. Our test assert both cases so that we don't need to - // know what version is being - // tested. + // hostname with the bucket name in case the bucket name is a valid + // DNS label, even in the case that we are using an endpoint + // override. Previously the sdk was only doing that if endpoint had + // "s3" as label in the FQDN. Our test assert both cases so that we + // don't need to know what version is being tested. satisfies( SERVER_ADDRESS, v -> v.matches("somebucket.localhost|localhost")), From 5c69b358faa6775c82666c4d8726d62dda8df28e Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Sun, 22 Dec 2024 15:18:47 -0500 Subject: [PATCH 06/14] fix latestDep and refactor --- .../v2_2/AbstractAws2ClientCoreTest.java | 130 ++++++------- .../awssdk/v2_2/AbstractAws2ClientTest.java | 181 ++++++++---------- 2 files changed, 138 insertions(+), 173 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 9149c150bc90..f13d0a94c36b 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -247,10 +247,19 @@ private static Stream provideArguments() { return Stream.of( Arguments.of( "CreateTable", - (Function) c -> c.createTable(createTableRequest())), + (Function) c -> c.createTable(createTableRequest()), + (Function) c -> c.createTable(createTableRequest())), Arguments.of( "DeleteItem", (Function) + c -> + c.deleteItem( + DeleteItemRequest.builder() + .tableName("sometable") + .key(createTableRequestKey) + .conditionExpression("property in (:one, :two)") + .build()), + (Function) c -> c.deleteItem( DeleteItemRequest.builder() @@ -261,10 +270,20 @@ private static Stream provideArguments() { Arguments.of( "DeleteTable", (Function) + c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build()), + (Function) c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build())), Arguments.of( "GetItem", (Function) + c -> + c.getItem( + GetItemRequest.builder() + .tableName("sometable") + .key(getItemRequestKey) + .attributesToGet("propertyOne", "propertyTwo") + .build()), + (Function) c -> c.getItem( GetItemRequest.builder() @@ -275,6 +294,14 @@ private static Stream provideArguments() { Arguments.of( "PutItem", (Function) + c -> + c.putItem( + PutItemRequest.builder() + .tableName("sometable") + .item(putItemRequestKey) + .conditionExpression("attributeOne <> :someVal") + .build()), + (Function) c -> c.putItem( PutItemRequest.builder() @@ -285,6 +312,16 @@ private static Stream provideArguments() { Arguments.of( "Query", (Function) + c -> + c.query( + QueryRequest.builder() + .tableName("sometable") + .select("ALL_ATTRIBUTES") + .keyConditionExpression("attribute = :aValue") + .filterExpression("anotherAttribute = :someVal") + .limit(10) + .build()), + (Function) c -> c.query( QueryRequest.builder() @@ -297,6 +334,20 @@ private static Stream provideArguments() { Arguments.of( "UpdateItem", (Function) + c -> + c.updateItem( + UpdateItemRequest.builder() + .tableName("sometable") + .key( + ImmutableMap.of( + "keyOne", + AttributeValue.builder().s("value").build(), + "keyTwo", + AttributeValue.builder().s("differentValue").build())) + .conditionExpression("attributeOne <> :someVal") + .updateExpression("set attributeOne = :updateValue") + .build()), + (Function) c -> c.updateItem( UpdateItemRequest.builder() @@ -330,79 +381,12 @@ void testSendDynamoDbRequestWithBuilderAndMockedResponse( executeCall(operation, response); } - private static Stream provideAsyncArguments() { - return Stream.of( - Arguments.of( - "CreateTable", - (Function) c -> c.createTable(createTableRequest())), - Arguments.of( - "DeleteItem", - (Function) - c -> - c.deleteItem( - DeleteItemRequest.builder() - .tableName("sometable") - .key(createTableRequestKey) - .conditionExpression("property in (:one, :two)") - .build())), - Arguments.of( - "DeleteTable", - (Function) - c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build())), - Arguments.of( - "GetItem", - (Function) - c -> - c.getItem( - GetItemRequest.builder() - .tableName("sometable") - .key(getItemRequestKey) - .attributesToGet("propertyOne", "propertyTwo") - .build())), - Arguments.of( - "PutItem", - (Function) - c -> - c.putItem( - PutItemRequest.builder() - .tableName("sometable") - .item(putItemRequestKey) - .conditionExpression("attributeOne <> :someVal") - .build())), - Arguments.of( - "Query", - (Function) - c -> - c.query( - QueryRequest.builder() - .tableName("sometable") - .select("ALL_ATTRIBUTES") - .keyConditionExpression("attribute = :aValue") - .filterExpression("anotherAttribute = :someVal") - .limit(10) - .build())), - Arguments.of( - "UpdateItem", - (Function) - c -> - c.updateItem( - UpdateItemRequest.builder() - .tableName("sometable") - .key( - ImmutableMap.of( - "keyOne", - AttributeValue.builder().s("value").build(), - "keyTwo", - AttributeValue.builder().s("differentValue").build())) - .conditionExpression("attributeOne <> :someVal") - .updateExpression("set attributeOne = :updateValue") - .build()))); - } - @ParameterizedTest - @MethodSource("provideAsyncArguments") + @MethodSource("provideArguments") void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( - String operation, Function call) + String operation, + Function call, + Function asyncCall) throws ExecutionException, InterruptedException { DynamoDbAsyncClientBuilder builder = DynamoDbAsyncClient.builder(); configureSdkClient(builder); @@ -413,7 +397,7 @@ void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( .credentialsProvider(CREDENTIALS_PROVIDER) .build(); server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); - Object response = call.apply(client); + Object response = asyncCall.apply(client); executeCall(operation, response); } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index bd9391e9f1ea..fb56ab31290d 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -98,55 +98,6 @@ private static void assumeSupportedConfig(String operation) { // prefix the hostname with the bucket name as label. URI clientUri = URI.create("http://localhost:" + server.httpPort()); - private static final Callable sqsCreateQueueResponse = - () -> { - String content; - if (!Boolean.getBoolean("testLatestDeps")) { - content = - "" - + " https://queue.amazonaws.com/123456789012/MyQueue" - + " 7a62c49f-347e-4fc4-9331-6e8e7a96aa73" - + " "; - return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content); - } - content = "{" + " \"QueueUrl\":\"https://queue.amazonaws.com/123456789012/MyQueue\"" + "}"; - ResponseHeaders headers = - ResponseHeaders.builder(HttpStatus.OK) - .contentType(MediaType.PLAIN_TEXT_UTF_8) - .add("x-amzn-RequestId", "7a62c49f-347e-4fc4-9331-6e8e7a96aa73") - .build(); - return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); - }; - - private static final Callable sqsSendMessageResponse = - () -> { - String content; - if (!Boolean.getBoolean("testLatestDeps")) { - content = - "" - + " " - + " d41d8cd98f00b204e9800998ecf8427e" - + " 3ae8f24a165a8cedc005670c81a27295" - + " 5fea7756-0ea4-451a-a703-a558b933e274" - + " " - + " 27daac76-34dd-47df-bd01-1f6e873584a0" - + ""; - return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content); - } - content = - "{" - + " \"MD5OfMessageBody\":\"d41d8cd98f00b204e9800998ecf8427e\"," - + " \"MD5OfMessageAttributes\":\"3ae8f24a165a8cedc005670c81a27295\"," - + " \"MessageId\":\"5fea7756-0ea4-451a-a703-a558b933e274\"" - + "}"; - ResponseHeaders headers = - ResponseHeaders.builder(HttpStatus.OK) - .contentType(MediaType.PLAIN_TEXT_UTF_8) - .add("x-amzn-RequestId", "27daac76-34dd-47df-bd01-1f6e873584a0") - .build(); - return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); - }; - private static final String ec2BodyContent = "" + " 59dbff89-35bd-4eac-99ed-be587EXAMPLE" @@ -164,7 +115,7 @@ S3ClientBuilder s3ClientBuilder() throws Exception { if (Boolean.getBoolean("testLatestDeps")) { Method forcePathStyleMethod = S3ClientBuilder.class.getMethod("forcePathStyle", Boolean.class); - forcePathStyleMethod.invoke(true); + forcePathStyleMethod.invoke(builder, true); } return builder; } @@ -173,8 +124,8 @@ S3AsyncClientBuilder s3AsyncClientBuilder() throws Exception { S3AsyncClientBuilder builder = S3AsyncClient.builder(); if (Boolean.getBoolean("testLatestDeps")) { Method forcePathStyleMethod = - S3ClientBuilder.class.getMethod("forcePathStyle", Boolean.class); - forcePathStyleMethod.invoke(true); + S3AsyncClientBuilder.class.getMethod("forcePathStyle", Boolean.class); + forcePathStyleMethod.invoke(builder, true); } return builder; } @@ -285,14 +236,23 @@ private static Stream provideS3Arguments() { "CreateBucket", "PUT", (Function) - c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build())), + c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()), + (Function>) + c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()), + ""), Arguments.of( "GetObject", "GET", (Function) c -> c.getObject( - GetObjectRequest.builder().bucket("somebucket").key("somekey").build()))); + GetObjectRequest.builder().bucket("somebucket").key("somekey").build()), + (Function>) + c -> + c.getObject( + GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), + AsyncResponseTransformer.toBytes()), + "1234567890")); } @ParameterizedTest @@ -319,29 +279,14 @@ void testS3SendOperationRequestWithBuilder( clientAssertions("S3", operation, method, response, "UNKNOWN"); } - private static Stream provideS3AsyncArguments() { - return Stream.of( - Arguments.of( - "CreateBucket", - "PUT", - (Function>) - c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()), - ""), - Arguments.of( - "GetObject", - "GET", - (Function>) - c -> - c.getObject( - GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), - AsyncResponseTransformer.toBytes()), - "1234567890")); - } - @ParameterizedTest - @MethodSource("provideS3AsyncArguments") + @MethodSource("provideS3Arguments") void testS3AsyncSendOperationRequestWithBuilder( - String operation, String method, Function> call, String body) + String operation, + String method, + Function call, + Function> asyncCall, + String body) throws Exception { S3AsyncClientBuilder builder = s3AsyncClientBuilder(); configureSdkClient(builder); @@ -354,7 +299,7 @@ void testS3AsyncSendOperationRequestWithBuilder( server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)); - Future response = call.apply(client); + Future response = asyncCall.apply(client); response.get(); clientAssertions("S3", operation, method, response, "UNKNOWN"); @@ -388,14 +333,68 @@ private static Stream provideSqsArguments() { Arguments.of( "CreateQueue", "7a62c49f-347e-4fc4-9331-6e8e7a96aa73", - sqsCreateQueueResponse, + (Callable) + () -> { + String content; + if (!Boolean.getBoolean("testLatestDeps")) { + content = + "" + + " https://queue.amazonaws.com/123456789012/MyQueue" + + " 7a62c49f-347e-4fc4-9331-6e8e7a96aa73" + + " "; + return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content); + } + content = + "{" + + " \"QueueUrl\":\"https://queue.amazonaws.com/123456789012/MyQueue\"" + + "}"; + ResponseHeaders headers = + ResponseHeaders.builder(HttpStatus.OK) + .contentType(MediaType.PLAIN_TEXT_UTF_8) + .add("x-amzn-RequestId", "7a62c49f-347e-4fc4-9331-6e8e7a96aa73") + .build(); + return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); + }, (Function) + c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()), + (Function>) c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build())), Arguments.of( "SendMessage", "27daac76-34dd-47df-bd01-1f6e873584a0", - sqsSendMessageResponse, + (Callable) + () -> { + String content; + if (!Boolean.getBoolean("testLatestDeps")) { + content = + "" + + " " + + " d41d8cd98f00b204e9800998ecf8427e" + + " 3ae8f24a165a8cedc005670c81a27295" + + " 5fea7756-0ea4-451a-a703-a558b933e274" + + " " + + " 27daac76-34dd-47df-bd01-1f6e873584a0" + + ""; + return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content); + } + content = + "{" + + " \"MD5OfMessageBody\":\"d41d8cd98f00b204e9800998ecf8427e\"," + + " \"MD5OfMessageAttributes\":\"3ae8f24a165a8cedc005670c81a27295\"," + + " \"MessageId\":\"5fea7756-0ea4-451a-a703-a558b933e274\"" + + "}"; + ResponseHeaders headers = + ResponseHeaders.builder(HttpStatus.OK) + .contentType(MediaType.PLAIN_TEXT_UTF_8) + .add("x-amzn-RequestId", "27daac76-34dd-47df-bd01-1f6e873584a0") + .build(); + return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); + }, (Function) + c -> + c.sendMessage( + SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()), + (Function>) c -> c.sendMessage( SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()))); @@ -430,31 +429,14 @@ void testSqsSendOperationRequestWithBuilder( clientAssertions("Sqs", operation, "POST", response, requestId); } - private static Stream provideSqsAsyncArguments() { - return Stream.of( - Arguments.of( - "CreateQueue", - "7a62c49f-347e-4fc4-9331-6e8e7a96aa73", - sqsCreateQueueResponse, - (Function>) - c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build())), - Arguments.of( - "SendMessage", - "27daac76-34dd-47df-bd01-1f6e873584a0", - sqsSendMessageResponse, - (Function>) - c -> - c.sendMessage( - SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()))); - } - @ParameterizedTest - @MethodSource("provideSqsAsyncArguments") + @MethodSource("provideSqsArguments") void testSqsAsyncSendOperationRequestWithBuilder( String operation, String requestId, Callable serverResponse, - Function> call) + Function call, + Function> asyncCall) throws Exception { assumeSupportedConfig(operation); @@ -468,7 +450,7 @@ void testSqsAsyncSendOperationRequestWithBuilder( .build(); server.enqueue(serverResponse.call()); - Future response = call.apply(client); + Future response = asyncCall.apply(client); response.get(); clientAssertions("Sqs", operation, "POST", response, requestId); @@ -638,7 +620,6 @@ void testTimeoutAndRetryErrorsAreNotCaptured() throws Exception { GetObjectRequest.builder().bucket("somebucket").key("somekey").build())); assertThat(thrown).isInstanceOf(SdkClientException.class); - assertThat(thrown).hasMessage("Unable to execute HTTP request: Read timed out"); getTesting() .waitAndAssertTraces( From 9dd164529a77baee7bd6998b97b0157f566f75a2 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Sun, 22 Dec 2024 15:26:32 -0500 Subject: [PATCH 07/14] more cleanups --- .../v2_2/AbstractAws2ClientCoreTest.java | 22 ++++---- .../awssdk/v2_2/AbstractAws2ClientTest.java | 53 ++++++++----------- 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index f13d0a94c36b..3baa15b90ad0 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -68,17 +68,17 @@ public abstract class AbstractAws2ClientCoreTest { protected abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); - static boolean isSqsAttributeInjectionEnabled() { + protected static boolean isSqsAttributeInjectionEnabled() { // See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor return ConfigPropertiesUtil.getBoolean( "otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false); } - static final StaticCredentialsProvider CREDENTIALS_PROVIDER = + protected static final StaticCredentialsProvider CREDENTIALS_PROVIDER = StaticCredentialsProvider.create( AwsBasicCredentials.create("my-access-key", "my-secret-key")); - static MockWebServerExtension server = new MockWebServerExtension(); + protected static MockWebServerExtension server = new MockWebServerExtension(); @BeforeAll static void setup() { @@ -95,21 +95,21 @@ void prepTest() { server.beforeTestExecution(null); } - void configureSdkClient(SdkClientBuilder builder) { + protected void configureSdkClient(SdkClientBuilder builder) { builder.overrideConfiguration(createOverrideConfigurationBuilder().build()); } - static ImmutableMap createTableRequestKey = + private static final ImmutableMap createTableRequestKey = ImmutableMap.of( "anotherKey", AttributeValue.builder().s("value").build(), "key", AttributeValue.builder().s("value").build()); - static ImmutableMap getItemRequestKey = + private static final ImmutableMap getItemRequestKey = ImmutableMap.of( "keyOne", AttributeValue.builder().s("value").build(), "keyTwo", AttributeValue.builder().s("differentValue").build()); - static ImmutableMap putItemRequestKey = + private static final ImmutableMap putItemRequestKey = ImmutableMap.of( "key", AttributeValue.builder().s("value").build(), "attributeOne", AttributeValue.builder().s("one").build(), @@ -144,7 +144,7 @@ private void executeCall(String operation, Object response) })); } - static CreateTableRequest createTableRequest() { + private static CreateTableRequest createTableRequest() { return CreateTableRequest.builder() .tableName("sometable") .globalSecondaryIndexes( @@ -174,7 +174,7 @@ static CreateTableRequest createTableRequest() { } @SuppressWarnings("deprecation") // uses deprecated semconv - static void assertCreateTableRequest(SpanDataAssert span) { + private static void assertCreateTableRequest(SpanDataAssert span) { span.hasName("DynamoDb.CreateTable") .hasKind(SpanKind.CLIENT) .hasNoParent() @@ -200,7 +200,7 @@ static void assertCreateTableRequest(SpanDataAssert span) { } @SuppressWarnings("deprecation") // using deprecated semconv - static void assertQueryRequest(SpanDataAssert span) { + private static void assertQueryRequest(SpanDataAssert span) { span.hasName("DynamoDb.Query") .hasKind(SpanKind.CLIENT) .hasNoParent() @@ -223,7 +223,7 @@ static void assertQueryRequest(SpanDataAssert span) { } @SuppressWarnings("deprecation") // uses deprecated semconv - static void assertDynamoDbRequest(SpanDataAssert span, String operation) { + private static void assertDynamoDbRequest(SpanDataAssert span, String operation) { span.hasName("DynamoDb." + operation) .hasKind(SpanKind.CLIENT) .hasNoParent() diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index fb56ab31290d..7cb9987aa84f 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -35,6 +35,7 @@ import io.opentelemetry.testing.internal.armeria.common.MediaType; import io.opentelemetry.testing.internal.armeria.common.ResponseHeaders; import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.RecordedRequest; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -42,6 +43,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.function.Function; import java.util.stream.Stream; @@ -96,7 +98,7 @@ private static void assumeSupportedConfig(String operation) { // Force localhost instead of relying on mock server because using ip is yet another corner case // of the virtual bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to // prefix the hostname with the bucket name as label. - URI clientUri = URI.create("http://localhost:" + server.httpPort()); + private final URI clientUri = URI.create("http://localhost:" + server.httpPort()); private static final String ec2BodyContent = "" @@ -110,26 +112,6 @@ private static void assumeSupportedConfig(String operation) { + " 0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" + ""; - S3ClientBuilder s3ClientBuilder() throws Exception { - S3ClientBuilder builder = S3Client.builder(); - if (Boolean.getBoolean("testLatestDeps")) { - Method forcePathStyleMethod = - S3ClientBuilder.class.getMethod("forcePathStyle", Boolean.class); - forcePathStyleMethod.invoke(builder, true); - } - return builder; - } - - S3AsyncClientBuilder s3AsyncClientBuilder() throws Exception { - S3AsyncClientBuilder builder = S3AsyncClient.builder(); - if (Boolean.getBoolean("testLatestDeps")) { - Method forcePathStyleMethod = - S3AsyncClientBuilder.class.getMethod("forcePathStyle", Boolean.class); - forcePathStyleMethod.invoke(builder, true); - } - return builder; - } - @SuppressWarnings("deprecation") // uses deprecated semconv private void clientAssertions( String service, String operation, String method, Object response, String requestId) { @@ -259,7 +241,12 @@ private static Stream provideS3Arguments() { @MethodSource("provideS3Arguments") void testS3SendOperationRequestWithBuilder( String operation, String method, Function call) throws Exception { - S3ClientBuilder builder = s3ClientBuilder(); + S3ClientBuilder builder = S3Client.builder(); + if (Boolean.getBoolean("testLatestDeps")) { + Method forcePathStyleMethod = + S3ClientBuilder.class.getMethod("forcePathStyle", Boolean.class); + forcePathStyleMethod.invoke(builder, true); + } configureSdkClient(builder); S3Client client = builder @@ -287,8 +274,17 @@ void testS3AsyncSendOperationRequestWithBuilder( Function call, Function> asyncCall, String body) - throws Exception { - S3AsyncClientBuilder builder = s3AsyncClientBuilder(); + throws NoSuchMethodException, + InvocationTargetException, + IllegalAccessException, + ExecutionException, + InterruptedException { + S3AsyncClientBuilder builder = S3AsyncClient.builder(); + if (Boolean.getBoolean("testLatestDeps")) { + Method forcePathStyleMethod = + S3AsyncClientBuilder.class.getMethod("forcePathStyle", Boolean.class); + forcePathStyleMethod.invoke(builder, true); + } configureSdkClient(builder); S3AsyncClient client = builder @@ -519,14 +515,7 @@ void testEc2SendOperationRequestWithBuilder() throws Exception { .credentialsProvider(CREDENTIALS_PROVIDER) .build(); - String content = - "" - + " 59dbff89-35bd-4eac-99ed-be587EXAMPLE" - + " 192.0.2.1" - + " standard" - + ""; - - server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, content)); + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, ec2BodyContent)); Object response = client.allocateAddress(); assertThat(response.getClass().getSimpleName()) From 17efec73d9263bf09a72c5fbfab478bbe541b3b2 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Sun, 22 Dec 2024 15:30:06 -0500 Subject: [PATCH 08/14] rename --- .../awssdk/v2_2/AbstractAws2ClientCoreTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 3baa15b90ad0..1e88f7c6c920 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -115,7 +115,7 @@ protected void configureSdkClient(SdkClientBuilder builder) { "attributeOne", AttributeValue.builder().s("one").build(), "attributeTwo", AttributeValue.builder().s("two").build()); - private void executeCall(String operation, Object response) + private void validateOperationResponse(String operation, Object response) throws ExecutionException, InterruptedException { if (response instanceof Future) { response = ((Future) response).get(); @@ -378,7 +378,7 @@ void testSendDynamoDbRequestWithBuilderAndMockedResponse( .build(); server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); Object response = call.apply(client); - executeCall(operation, response); + validateOperationResponse(operation, response); } @ParameterizedTest @@ -398,6 +398,6 @@ void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( .build(); server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); Object response = asyncCall.apply(client); - executeCall(operation, response); + validateOperationResponse(operation, response); } } From 639fc25d64c74f7e2876c3092b7fe648c19f5c22 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Sun, 22 Dec 2024 15:42:14 -0500 Subject: [PATCH 09/14] forgot sns async --- .../awssdk/v2_2/AbstractAws2ClientTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index 7cb9987aa84f..2f08881fd7a6 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -38,6 +38,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; @@ -76,6 +77,8 @@ import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.model.CreateBucketRequest; import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.sns.SnsAsyncClient; +import software.amazon.awssdk.services.sns.SnsAsyncClientBuilder; import software.amazon.awssdk.services.sns.SnsClient; import software.amazon.awssdk.services.sns.SnsClientBuilder; import software.amazon.awssdk.services.sns.model.PublishRequest; @@ -122,6 +125,23 @@ private void clientAssertions( assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); assertThat(request.request().headers().get("traceparent")).isNull(); + if (service.equals("SNS") && operation.equals("Publish")) { + String content = request.request().content(Charset.defaultCharset()); + boolean containsId = + content.contains( + getTesting().spans().get(0).getTraceId() + + "-" + + getTesting().spans().get(0).getSpanId()); + boolean containsTp = content.contains("=traceparent"); + if (isSqsAttributeInjectionEnabled()) { + assertThat(containsId).isTrue(); + assertThat(containsTp).isTrue(); + } else { + assertThat(containsId).isFalse(); + assertThat(containsTp).isFalse(); + } + } + List attributes = new ArrayList<>( asList( @@ -504,6 +524,34 @@ void testSnsSendOperationRequestWithBuilder(Function call) { clientAssertions("Sns", "Publish", "POST", response, "d74b8436-ae13-5ab4-a9ff-ce54dfea72a0"); } + @ParameterizedTest + @MethodSource("provideSnsArguments") + void testSnsAsyncSendOperationRequestWithBuilder() { + SnsAsyncClientBuilder builder = SnsAsyncClient.builder(); + configureSdkClient(builder); + SnsAsyncClient client = + builder + .endpointOverride(clientUri) + .region(Region.AP_NORTHEAST_1) + .credentialsProvider(CREDENTIALS_PROVIDER) + .build(); + + String body = + "" + + " " + + " 94f20ce6-13c5-43a0-9a9e-ca52d816e90b" + + " " + + " " + + " f187a3c1-376f-11df-8963-01868b7c937a" + + " " + + ""; + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)); + Object response = client.publish(r -> r.message("hello").topicArn("somearn")); + + clientAssertions("Sns", "Publish", "POST", response, "f187a3c1-376f-11df-8963-01868b7c937a"); + } + @Test void testEc2SendOperationRequestWithBuilder() throws Exception { Ec2ClientBuilder builder = Ec2Client.builder(); @@ -584,6 +632,9 @@ void testRdsAsyncSendOperationRequestWithBuilder() { "Rds", "DeleteOptionGroup", "POST", response, "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99"); } + // TODO: Without AOP instrumentation of the HTTP client, we cannot model retries as + // spans because of https://github.com/aws/aws-sdk-java-v2/issues/1741. We should at least tweak + // the instrumentation to add Events for retries instead. @Test void testTimeoutAndRetryErrorsAreNotCaptured() throws Exception { // One retry so two requests. From 57ef25cbe601ad4620ef057ed7cebf6002736caa Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Mon, 23 Dec 2024 05:00:05 -0500 Subject: [PATCH 10/14] move things --- .../awssdk/v2_2/AbstractAws2ClientCoreTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 1e88f7c6c920..65acca6b229c 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -64,6 +64,12 @@ import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; public abstract class AbstractAws2ClientCoreTest { + protected static final StaticCredentialsProvider CREDENTIALS_PROVIDER = + StaticCredentialsProvider.create( + AwsBasicCredentials.create("my-access-key", "my-secret-key")); + + protected static final MockWebServerExtension server = new MockWebServerExtension(); + protected abstract InstrumentationExtension getTesting(); protected abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); @@ -74,12 +80,6 @@ protected static boolean isSqsAttributeInjectionEnabled() { "otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false); } - protected static final StaticCredentialsProvider CREDENTIALS_PROVIDER = - StaticCredentialsProvider.create( - AwsBasicCredentials.create("my-access-key", "my-secret-key")); - - protected static MockWebServerExtension server = new MockWebServerExtension(); - @BeforeAll static void setup() { server.start(); From d2dc050e7cd9169b073863776e16f78608f6db7f Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Mon, 23 Dec 2024 05:01:24 -0500 Subject: [PATCH 11/14] move things --- .../v2_2/AbstractAws2ClientCoreTest.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 65acca6b229c..44722d5d445b 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -70,6 +70,22 @@ public abstract class AbstractAws2ClientCoreTest { protected static final MockWebServerExtension server = new MockWebServerExtension(); + private static final ImmutableMap createTableRequestKey = + ImmutableMap.of( + "anotherKey", AttributeValue.builder().s("value").build(), + "key", AttributeValue.builder().s("value").build()); + + private static final ImmutableMap getItemRequestKey = + ImmutableMap.of( + "keyOne", AttributeValue.builder().s("value").build(), + "keyTwo", AttributeValue.builder().s("differentValue").build()); + + private static final ImmutableMap putItemRequestKey = + ImmutableMap.of( + "key", AttributeValue.builder().s("value").build(), + "attributeOne", AttributeValue.builder().s("one").build(), + "attributeTwo", AttributeValue.builder().s("two").build()); + protected abstract InstrumentationExtension getTesting(); protected abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); @@ -80,6 +96,10 @@ protected static boolean isSqsAttributeInjectionEnabled() { "otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false); } + protected void configureSdkClient(SdkClientBuilder builder) { + builder.overrideConfiguration(createOverrideConfigurationBuilder().build()); + } + @BeforeAll static void setup() { server.start(); @@ -95,26 +115,6 @@ void prepTest() { server.beforeTestExecution(null); } - protected void configureSdkClient(SdkClientBuilder builder) { - builder.overrideConfiguration(createOverrideConfigurationBuilder().build()); - } - - private static final ImmutableMap createTableRequestKey = - ImmutableMap.of( - "anotherKey", AttributeValue.builder().s("value").build(), - "key", AttributeValue.builder().s("value").build()); - - private static final ImmutableMap getItemRequestKey = - ImmutableMap.of( - "keyOne", AttributeValue.builder().s("value").build(), - "keyTwo", AttributeValue.builder().s("differentValue").build()); - - private static final ImmutableMap putItemRequestKey = - ImmutableMap.of( - "key", AttributeValue.builder().s("value").build(), - "attributeOne", AttributeValue.builder().s("one").build(), - "attributeTwo", AttributeValue.builder().s("two").build()); - private void validateOperationResponse(String operation, Object response) throws ExecutionException, InterruptedException { if (response instanceof Future) { From 9cf6eedeb103a8b5ec2cfcefba9c7c3635dd496a Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Mon, 23 Dec 2024 16:58:58 -0500 Subject: [PATCH 12/14] add proxy wrapper to remove excess code --- .../v2_2/AbstractAws2ClientCoreTest.java | 79 ++++++------------- .../awssdk/v2_2/AbstractAws2ClientTest.java | 37 ++++----- 2 files changed, 37 insertions(+), 79 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 44722d5d445b..33a6ee2798e6 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -31,7 +31,10 @@ import io.opentelemetry.testing.internal.armeria.common.MediaType; import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension; import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.RecordedRequest; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.function.Function; @@ -243,23 +246,30 @@ private static void assertDynamoDbRequest(SpanDataAssert span, String operation) equalTo(maybeStable(DB_OPERATION), operation)); } + @SuppressWarnings("unchecked") + protected static T wrapClient( + Class syncClientClass, Class asyncClientClass, U asyncClient) { + return (T) + Proxy.newProxyInstance( + AbstractAws2ClientCoreTest.class.getClassLoader(), + new Class[] {syncClientClass}, + (proxy, method, args) -> { + Method asyncMethod = + asyncClientClass.getMethod(method.getName(), method.getParameterTypes()); + CompletableFuture future = + (CompletableFuture) asyncMethod.invoke(asyncClient, args); + return future.get(); + }); + } + private static Stream provideArguments() { return Stream.of( Arguments.of( "CreateTable", - (Function) c -> c.createTable(createTableRequest()), - (Function) c -> c.createTable(createTableRequest())), + (Function) c -> c.createTable(createTableRequest())), Arguments.of( "DeleteItem", (Function) - c -> - c.deleteItem( - DeleteItemRequest.builder() - .tableName("sometable") - .key(createTableRequestKey) - .conditionExpression("property in (:one, :two)") - .build()), - (Function) c -> c.deleteItem( DeleteItemRequest.builder() @@ -270,20 +280,10 @@ private static Stream provideArguments() { Arguments.of( "DeleteTable", (Function) - c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build()), - (Function) c -> c.deleteTable(DeleteTableRequest.builder().tableName("sometable").build())), Arguments.of( "GetItem", (Function) - c -> - c.getItem( - GetItemRequest.builder() - .tableName("sometable") - .key(getItemRequestKey) - .attributesToGet("propertyOne", "propertyTwo") - .build()), - (Function) c -> c.getItem( GetItemRequest.builder() @@ -294,14 +294,6 @@ private static Stream provideArguments() { Arguments.of( "PutItem", (Function) - c -> - c.putItem( - PutItemRequest.builder() - .tableName("sometable") - .item(putItemRequestKey) - .conditionExpression("attributeOne <> :someVal") - .build()), - (Function) c -> c.putItem( PutItemRequest.builder() @@ -312,16 +304,6 @@ private static Stream provideArguments() { Arguments.of( "Query", (Function) - c -> - c.query( - QueryRequest.builder() - .tableName("sometable") - .select("ALL_ATTRIBUTES") - .keyConditionExpression("attribute = :aValue") - .filterExpression("anotherAttribute = :someVal") - .limit(10) - .build()), - (Function) c -> c.query( QueryRequest.builder() @@ -334,20 +316,6 @@ private static Stream provideArguments() { Arguments.of( "UpdateItem", (Function) - c -> - c.updateItem( - UpdateItemRequest.builder() - .tableName("sometable") - .key( - ImmutableMap.of( - "keyOne", - AttributeValue.builder().s("value").build(), - "keyTwo", - AttributeValue.builder().s("differentValue").build())) - .conditionExpression("attributeOne <> :someVal") - .updateExpression("set attributeOne = :updateValue") - .build()), - (Function) c -> c.updateItem( UpdateItemRequest.builder() @@ -384,9 +352,7 @@ void testSendDynamoDbRequestWithBuilderAndMockedResponse( @ParameterizedTest @MethodSource("provideArguments") void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( - String operation, - Function call, - Function asyncCall) + String operation, Function call) throws ExecutionException, InterruptedException { DynamoDbAsyncClientBuilder builder = DynamoDbAsyncClient.builder(); configureSdkClient(builder); @@ -397,7 +363,8 @@ void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( .credentialsProvider(CREDENTIALS_PROVIDER) .build(); server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); - Object response = asyncCall.apply(client); + Object response = + call.apply(wrapClient(DynamoDbClient.class, DynamoDbAsyncClient.class, client)); validateOperationResponse(operation, response); } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index 2f08881fd7a6..12b0f79bc7bc 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -92,12 +92,6 @@ public abstract class AbstractAws2ClientTest extends AbstractAws2ClientCoreTest { private static final String QUEUE_URL = "http://xxx/somequeue"; - private static void assumeSupportedConfig(String operation) { - Assumptions.assumeFalse( - operation.equals("SendMessage") && isSqsAttributeInjectionEnabled(), - "Cannot check Sqs.SendMessage here due to hard-coded MD5."); - } - // Force localhost instead of relying on mock server because using ip is yet another corner case // of the virtual bucket changes introduced by aws sdk v2.18.0. When using IP, there is no way to // prefix the hostname with the bucket name as label. @@ -115,6 +109,12 @@ private static void assumeSupportedConfig(String operation) { + " 0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" + ""; + private static void assumeSupportedConfig(String operation) { + Assumptions.assumeFalse( + operation.equals("SendMessage") && isSqsAttributeInjectionEnabled(), + "Cannot check Sqs.SendMessage here due to hard-coded MD5."); + } + @SuppressWarnings("deprecation") // uses deprecated semconv private void clientAssertions( String service, String operation, String method, Object response, String requestId) { @@ -322,7 +322,7 @@ void testS3AsyncSendOperationRequestWithBuilder( } @Test - void testKinesisSendOperationRequestWithBuilder() throws Exception { + void testKinesisSendOperationRequestWithBuilder() { KinesisClientBuilder builder = KinesisClient.builder(); configureSdkClient(builder); KinesisClient client = @@ -372,8 +372,6 @@ private static Stream provideSqsArguments() { return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); }, (Function) - c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()), - (Function>) c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build())), Arguments.of( "SendMessage", @@ -407,10 +405,6 @@ private static Stream provideSqsArguments() { return HttpResponse.of(headers, HttpData.of(StandardCharsets.UTF_8, content)); }, (Function) - c -> - c.sendMessage( - SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()), - (Function>) c -> c.sendMessage( SendMessageRequest.builder().queueUrl(QUEUE_URL).messageBody("").build()))); @@ -451,8 +445,7 @@ void testSqsAsyncSendOperationRequestWithBuilder( String operation, String requestId, Callable serverResponse, - Function call, - Function> asyncCall) + Function call) throws Exception { assumeSupportedConfig(operation); @@ -466,8 +459,7 @@ void testSqsAsyncSendOperationRequestWithBuilder( .build(); server.enqueue(serverResponse.call()); - Future response = asyncCall.apply(client); - response.get(); + Object response = call.apply(wrapClient(SqsClient.class, SqsAsyncClient.class, client)); clientAssertions("Sqs", operation, "POST", response, requestId); } @@ -524,8 +516,7 @@ void testSnsSendOperationRequestWithBuilder(Function call) { clientAssertions("Sns", "Publish", "POST", response, "d74b8436-ae13-5ab4-a9ff-ce54dfea72a0"); } - @ParameterizedTest - @MethodSource("provideSnsArguments") + @Test void testSnsAsyncSendOperationRequestWithBuilder() { SnsAsyncClientBuilder builder = SnsAsyncClient.builder(); configureSdkClient(builder); @@ -553,7 +544,7 @@ void testSnsAsyncSendOperationRequestWithBuilder() { } @Test - void testEc2SendOperationRequestWithBuilder() throws Exception { + void testEc2SendOperationRequestWithBuilder() { Ec2ClientBuilder builder = Ec2Client.builder(); configureSdkClient(builder); Ec2Client client = @@ -575,7 +566,7 @@ void testEc2SendOperationRequestWithBuilder() throws Exception { } @Test - void testEc2AsyncSendOperationRequestWithBuilder() throws Exception { + void testEc2AsyncSendOperationRequestWithBuilder() { Ec2AsyncClientBuilder builder = Ec2AsyncClient.builder(); configureSdkClient(builder); Ec2AsyncClient client = @@ -593,7 +584,7 @@ void testEc2AsyncSendOperationRequestWithBuilder() throws Exception { } @Test - void testRdsSendOperationRequestWithBuilder() throws Exception { + void testRdsSendOperationRequestWithBuilder() { RdsClientBuilder builder = RdsClient.builder(); configureSdkClient(builder); RdsClient client = @@ -636,7 +627,7 @@ void testRdsAsyncSendOperationRequestWithBuilder() { // spans because of https://github.com/aws/aws-sdk-java-v2/issues/1741. We should at least tweak // the instrumentation to add Events for retries instead. @Test - void testTimeoutAndRetryErrorsAreNotCaptured() throws Exception { + void testTimeoutAndRetryErrorsAreNotCaptured() { // One retry so two requests. server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofSeconds(5))); server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofSeconds(5))); From db38d71b64060e0f4b4aedbd53915a35e108e7b6 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Mon, 23 Dec 2024 17:20:39 -0500 Subject: [PATCH 13/14] remove unneeded future checking with new wrapper --- .../awssdk/v2_2/AbstractAws2ClientCoreTest.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 33a6ee2798e6..57a8421f812c 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -35,8 +35,6 @@ import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.function.Function; import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; @@ -118,12 +116,7 @@ void prepTest() { server.beforeTestExecution(null); } - private void validateOperationResponse(String operation, Object response) - throws ExecutionException, InterruptedException { - if (response instanceof Future) { - response = ((Future) response).get(); - } - + private void validateOperationResponse(String operation, Object response) { assertThat(response).isNotNull(); assertThat(response.getClass().getSimpleName()).startsWith(operation); @@ -334,8 +327,7 @@ private static Stream provideArguments() { @ParameterizedTest @MethodSource("provideArguments") void testSendDynamoDbRequestWithBuilderAndMockedResponse( - String operation, Function call) - throws ExecutionException, InterruptedException { + String operation, Function call) { DynamoDbClientBuilder builder = DynamoDbClient.builder(); configureSdkClient(builder); DynamoDbClient client = @@ -352,8 +344,7 @@ void testSendDynamoDbRequestWithBuilderAndMockedResponse( @ParameterizedTest @MethodSource("provideArguments") void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse( - String operation, Function call) - throws ExecutionException, InterruptedException { + String operation, Function call) { DynamoDbAsyncClientBuilder builder = DynamoDbAsyncClient.builder(); configureSdkClient(builder); DynamoDbAsyncClient client = From 2d1ff0e9097de62c9914ba3b195ce2026ae25f49 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Mon, 30 Dec 2024 13:16:53 +0200 Subject: [PATCH 14/14] inline fields --- .../v2_2/AbstractAws2ClientCoreTest.java | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index 57a8421f812c..343e19c0fb27 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -71,22 +71,6 @@ public abstract class AbstractAws2ClientCoreTest { protected static final MockWebServerExtension server = new MockWebServerExtension(); - private static final ImmutableMap createTableRequestKey = - ImmutableMap.of( - "anotherKey", AttributeValue.builder().s("value").build(), - "key", AttributeValue.builder().s("value").build()); - - private static final ImmutableMap getItemRequestKey = - ImmutableMap.of( - "keyOne", AttributeValue.builder().s("value").build(), - "keyTwo", AttributeValue.builder().s("differentValue").build()); - - private static final ImmutableMap putItemRequestKey = - ImmutableMap.of( - "key", AttributeValue.builder().s("value").build(), - "attributeOne", AttributeValue.builder().s("one").build(), - "attributeTwo", AttributeValue.builder().s("two").build()); - protected abstract InstrumentationExtension getTesting(); protected abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder(); @@ -267,7 +251,10 @@ private static Stream provideArguments() { c.deleteItem( DeleteItemRequest.builder() .tableName("sometable") - .key(createTableRequestKey) + .key( + ImmutableMap.of( + "anotherKey", AttributeValue.builder().s("value").build(), + "key", AttributeValue.builder().s("value").build())) .conditionExpression("property in (:one, :two)") .build())), Arguments.of( @@ -281,7 +268,10 @@ private static Stream provideArguments() { c.getItem( GetItemRequest.builder() .tableName("sometable") - .key(getItemRequestKey) + .key( + ImmutableMap.of( + "keyOne", AttributeValue.builder().s("value").build(), + "keyTwo", AttributeValue.builder().s("differentValue").build())) .attributesToGet("propertyOne", "propertyTwo") .build())), Arguments.of( @@ -291,7 +281,11 @@ private static Stream provideArguments() { c.putItem( PutItemRequest.builder() .tableName("sometable") - .item(putItemRequestKey) + .item( + ImmutableMap.of( + "key", AttributeValue.builder().s("value").build(), + "attributeOne", AttributeValue.builder().s("one").build(), + "attributeTwo", AttributeValue.builder().s("two").build())) .conditionExpression("attributeOne <> :someVal") .build())), Arguments.of(