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 9aaacb3abed3..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 AbstractAws2ClientCoreTest 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 c571c0aa9cf9..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 AbstractAws2ClientTest 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: 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
new file mode 100644
index 000000000000..343e19c0fb27
--- /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,355 @@
+/*
+ * 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;
+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 java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+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;
+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.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;
+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;
+
+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();
+
+ 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);
+ }
+
+ protected void configureSdkClient(SdkClientBuilder, ?> builder) {
+ builder.overrideConfiguration(createOverrideConfigurationBuilder().build());
+ }
+
+ @BeforeAll
+ static void setup() {
+ server.start();
+ }
+
+ @AfterAll
+ static void cleanup() {
+ server.stop();
+ }
+
+ @BeforeEach
+ void prepTest() {
+ server.beforeTestExecution(null);
+ }
+
+ private void validateOperationResponse(String operation, Object response) {
+ 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);
+ }
+ }));
+ }
+
+ private 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
+ private 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
+ private 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
+ private 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));
+ }
+
+ @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())),
+ Arguments.of(
+ "DeleteItem",
+ (Function)
+ c ->
+ c.deleteItem(
+ DeleteItemRequest.builder()
+ .tableName("sometable")
+ .key(
+ ImmutableMap.of(
+ "anotherKey", AttributeValue.builder().s("value").build(),
+ "key", AttributeValue.builder().s("value").build()))
+ .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(
+ ImmutableMap.of(
+ "keyOne", AttributeValue.builder().s("value").build(),
+ "keyTwo", AttributeValue.builder().s("differentValue").build()))
+ .attributesToGet("propertyOne", "propertyTwo")
+ .build())),
+ Arguments.of(
+ "PutItem",
+ (Function)
+ c ->
+ c.putItem(
+ PutItemRequest.builder()
+ .tableName("sometable")
+ .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(
+ "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 testSendDynamoDbRequestWithBuilderAndMockedResponse(
+ String operation, Function call) {
+ 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);
+ validateOperationResponse(operation, response);
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideArguments")
+ void testSendDynamoDbAsyncRequestWithBuilderAndMockedResponse(
+ String operation, Function call) {
+ 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(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
new file mode 100644
index 000000000000..12b0f79bc7bc
--- /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,699 @@
+/*
+ * 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.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;
+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;
+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 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;
+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";
+
+ // 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.
+ private final URI clientUri = URI.create("http://localhost:" + server.httpPort());
+
+ 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"
+ + "";
+
+ 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) {
+ 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();
+
+ 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(
+ // 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(
+ "CreateBucket",
+ "PUT",
+ (Function)
+ 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()),
+ (Function>)
+ c ->
+ c.getObject(
+ GetObjectRequest.builder().bucket("somebucket").key("somekey").build(),
+ AsyncResponseTransformer.toBytes()),
+ "1234567890"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideS3Arguments")
+ void testS3SendOperationRequestWithBuilder(
+ String operation, String method, Function call) throws Exception {
+ 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
+ .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");
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideS3Arguments")
+ void testS3AsyncSendOperationRequestWithBuilder(
+ String operation,
+ String method,
+ Function call,
+ Function> asyncCall,
+ String body)
+ 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
+ .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 = asyncCall.apply(client);
+ response.get();
+
+ clientAssertions("S3", operation, method, response, "UNKNOWN");
+ }
+
+ @Test
+ void testKinesisSendOperationRequestWithBuilder() {
+ 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",
+ (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())),
+ Arguments.of(
+ "SendMessage",
+ "27daac76-34dd-47df-bd01-1f6e873584a0",
+ (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())));
+ }
+
+ @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);
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideSqsArguments")
+ 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());
+ Object response = call.apply(wrapClient(SqsClient.class, SqsAsyncClient.class, client));
+
+ 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 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() {
+ Ec2ClientBuilder builder = Ec2Client.builder();
+ configureSdkClient(builder);
+ Ec2Client 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();
+
+ 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() {
+ 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() {
+ 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");
+ }
+
+ // 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() {
+ // 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);
+
+ 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"))));
+ }
+}