diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy deleted file mode 100644 index 78baace09f5c..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import com.amazonaws.AmazonClientException -import com.amazonaws.ClientConfiguration -import com.amazonaws.Request -import com.amazonaws.SDKGlobalConfiguration -import com.amazonaws.auth.AWSCredentialsProviderChain -import com.amazonaws.auth.BasicAWSCredentials -import com.amazonaws.auth.EnvironmentVariableCredentialsProvider -import com.amazonaws.auth.InstanceProfileCredentialsProvider -import com.amazonaws.auth.NoOpSigner -import com.amazonaws.auth.SignerFactory -import com.amazonaws.auth.SystemPropertiesCredentialsProvider -import com.amazonaws.auth.profile.ProfileCredentialsProvider -import com.amazonaws.handlers.RequestHandler2 -import com.amazonaws.retry.PredefinedRetryPolicies -import com.amazonaws.services.ec2.AmazonEC2Client -import com.amazonaws.services.rds.AmazonRDSClient -import com.amazonaws.services.rds.model.DeleteOptionGroupRequest -import com.amazonaws.services.s3.AmazonS3Client -import com.amazonaws.services.s3.S3ClientOptions -import io.opentelemetry.api.trace.Span -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes -import io.opentelemetry.semconv.ServerAttributes -import io.opentelemetry.semconv.ErrorAttributes -import io.opentelemetry.semconv.HttpAttributes -import io.opentelemetry.semconv.NetworkAttributes -import io.opentelemetry.semconv.UrlAttributes -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 spock.lang.Shared - -import java.time.Duration - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.StatusCode.ERROR -import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT - -class Aws0ClientTest extends AgentInstrumentationSpecification { - - private static final CREDENTIALS_PROVIDER_CHAIN = new AWSCredentialsProviderChain( - new EnvironmentVariableCredentialsProvider(), - new SystemPropertiesCredentialsProvider(), - new ProfileCredentialsProvider(), - new InstanceProfileCredentialsProvider()) - - @Shared - def server = new MockWebServerExtension() - - def setupSpec() { - System.setProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY, "my-access-key") - System.setProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY, "my-secret-key") - server.start() - } - - def cleanupSpec() { - System.clearProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY) - System.clearProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY) - server.stop() - } - - def setup() { - server.beforeTestExecution(null) - } - - def "request handler is hooked up with constructor"() { - setup: - String accessKey = "asdf" - String secretKey = "qwerty" - def credentials = new BasicAWSCredentials(accessKey, secretKey) - def client = new AmazonS3Client(credentials) - if (addHandler) { - client.addRequestHandler(new RequestHandler2() {}) - } - - expect: - client.requestHandler2s != null - client.requestHandler2s.size() == size - client.requestHandler2s.get(0).getClass().getSimpleName() == "TracingRequestHandler" - - where: - addHandler | size - true | 2 - false | 1 - } - - def "send #operation request with mocked response"() { - setup: - server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)) - - when: - def response = call.call(client) - - then: - response != null - - client.requestHandler2s != null - client.requestHandler2s.size() == handlerCount - client.requestHandler2s.get(0).getClass().getSimpleName() == "TracingRequestHandler" - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "$service.$operation" - kind CLIENT - hasNoParent() - attributes { - "$UrlAttributes.URL_FULL" "${server.httpUri()}" - "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" { it.contains(service) } - "$RpcIncubatingAttributes.RPC_METHOD" "${operation}" - "aws.endpoint" "${server.httpUri()}" - "aws.agent" "java-aws-sdk" - for (def addedTag : additionalAttributes) { - "$addedTag.key" "$addedTag.value" - } - } - } - } - } - - def request = server.takeRequest() - request.request().headers().get("X-Amzn-Trace-Id") != null - request.request().headers().get("traceparent") == null - - where: - service | operation | method | path | handlerCount | client | additionalAttributes | call | body - "S3" | "CreateBucket" | "PUT" | "/testbucket/" | 1 | new AmazonS3Client().withEndpoint("${server.httpUri()}") | ["aws.bucket.name": "testbucket"] | { c -> c.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build()); c.createBucket("testbucket") } | "" - "S3" | "GetObject" | "GET" | "/someBucket/someKey" | 1 | new AmazonS3Client().withEndpoint("${server.httpUri()}") | ["aws.bucket.name": "someBucket"] | { c -> c.getObject("someBucket", "someKey") } | "" - "EC2" | "AllocateAddress" | "POST" | "/" | 4 | new AmazonEC2Client().withEndpoint("${server.httpUri()}") | [:] | { c -> c.allocateAddress() } | """ - - 59dbff89-35bd-4eac-99ed-be587EXAMPLE - 192.0.2.1 - standard - - """ - "RDS" | "DeleteOptionGroup" | "POST" | "/" | 1 | new AmazonRDSClient().withEndpoint("${server.httpUri()}") | [:] | { c -> c.deleteOptionGroup(new DeleteOptionGroupRequest()) } | """ - - - 0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99 - - - """ - } - - def "send #operation request to closed port"() { - setup: - server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)) - - when: - call.call(client) - - then: - thrown AmazonClientException - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "$service.$operation" - kind CLIENT - status ERROR - errorEvent AmazonClientException, ~/Unable to execute HTTP request/ - hasNoParent() - attributes { - "$UrlAttributes.URL_FULL" "http://localhost:${UNUSABLE_PORT}" - "$HttpAttributes.HTTP_REQUEST_METHOD" "$method" - "$ServerAttributes.SERVER_PORT" 61 - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" { it.contains(service) } - "$RpcIncubatingAttributes.RPC_METHOD" "${operation}" - "aws.endpoint" "http://localhost:${UNUSABLE_PORT}" - "aws.agent" "java-aws-sdk" - for (def addedTag : additionalAttributes) { - "$addedTag.key" "$addedTag.value" - } - "$ErrorAttributes.ERROR_TYPE" AmazonClientException.name - } - } - } - } - - where: - service | operation | method | url | call | additionalAttributes | body | client - "S3" | "GetObject" | "GET" | "someBucket/someKey" | { client -> client.getObject("someBucket", "someKey") } | ["aws.bucket.name": "someBucket"] | "" | new AmazonS3Client(CREDENTIALS_PROVIDER_CHAIN, new ClientConfiguration().withRetryPolicy(PredefinedRetryPolicies.getDefaultRetryPolicyWithCustomMaxRetries(0))).withEndpoint("http://localhost:${UNUSABLE_PORT}") - } - - def "naughty request handler doesn't break the trace"() { - setup: - def client = new AmazonS3Client(CREDENTIALS_PROVIDER_CHAIN) - client.addRequestHandler(new RequestHandler2() { - void beforeRequest(Request request) { - throw new IllegalStateException("bad handler") - } - }) - - when: - client.getObject("someBucket", "someKey") - - then: - !Span.current().getSpanContext().isValid() - thrown IllegalStateException - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "S3.GetObject" - kind CLIENT - status ERROR - errorEvent IllegalStateException, "bad handler" - hasNoParent() - attributes { - "$UrlAttributes.URL_FULL" "https://s3.amazonaws.com" - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$ServerAttributes.SERVER_ADDRESS" "s3.amazonaws.com" - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "Amazon S3" - "$RpcIncubatingAttributes.RPC_METHOD" "GetObject" - "aws.endpoint" "https://s3.amazonaws.com" - "aws.agent" "java-aws-sdk" - "aws.bucket.name" "someBucket" - "$ErrorAttributes.ERROR_TYPE" IllegalStateException.name - } - } - } - } - } - - // TODO(anuraaga): Add events for retries. - 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))) - AmazonS3Client client = new AmazonS3Client(new ClientConfiguration() - .withRequestTimeout(50 /* ms */) - .withRetryPolicy(PredefinedRetryPolicies.getDefaultRetryPolicyWithCustomMaxRetries(1))) - .withEndpoint("${server.httpUri()}") - - when: - client.getObject("someBucket", "someKey") - - then: - !Span.current().getSpanContext().isValid() - thrown AmazonClientException - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "S3.GetObject" - kind CLIENT - status ERROR - errorEvent AmazonClientException, ~/Unable to execute HTTP request/ - hasNoParent() - attributes { - "$UrlAttributes.URL_FULL" "${server.httpUri()}" - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$ServerAttributes.SERVER_PORT" server.httpPort() - "$ServerAttributes.SERVER_ADDRESS" "127.0.0.1" - "$RpcIncubatingAttributes.RPC_SYSTEM" "aws-api" - "$RpcIncubatingAttributes.RPC_SERVICE" "Amazon S3" - "$RpcIncubatingAttributes.RPC_METHOD" "GetObject" - "aws.endpoint" "${server.httpUri()}" - "aws.agent" "java-aws-sdk" - "aws.bucket.name" "someBucket" - "$ErrorAttributes.ERROR_TYPE" AmazonClientException.name - } - } - } - } - } - - def "calling generatePresignedUrl does not leak context"() { - setup: - SignerFactory.registerSigner("noop", NoOpSigner) - def client = new AmazonS3Client(new ClientConfiguration().withSignerOverride("noop")) - .withEndpoint("${server.httpUri()}") - - when: - client.generatePresignedUrl("someBucket", "someKey", new Date()) - - then: - // expecting no active span after call to generatePresignedUrl - !Span.current().getSpanContext().isValid() - } -} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java new file mode 100644 index 000000000000..dc524dbcc466 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java @@ -0,0 +1,371 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.api.trace.SpanKind.CLIENT; +import static io.opentelemetry.api.trace.SpanKind.PRODUCER; +import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PROTOCOL_VERSION; +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.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 java.util.Collections.emptyMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonWebServiceClient; +import com.amazonaws.ClientConfiguration; +import com.amazonaws.Request; +import com.amazonaws.SDKGlobalConfiguration; +import com.amazonaws.auth.AWSCredentialsProviderChain; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.EnvironmentVariableCredentialsProvider; +import com.amazonaws.auth.InstanceProfileCredentialsProvider; +import com.amazonaws.auth.NoOpSigner; +import com.amazonaws.auth.SignerFactory; +import com.amazonaws.auth.SystemPropertiesCredentialsProvider; +import com.amazonaws.auth.profile.ProfileCredentialsProvider; +import com.amazonaws.handlers.RequestHandler2; +import com.amazonaws.retry.PredefinedRetryPolicies; +import com.amazonaws.services.ec2.AmazonEC2Client; +import com.amazonaws.services.rds.AmazonRDSClient; +import com.amazonaws.services.rds.model.DeleteOptionGroupRequest; +import com.amazonaws.services.s3.AmazonS3Client; +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import io.opentelemetry.sdk.trace.data.StatusData; +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.Field; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +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.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class Aws0ClientTest { + + @RegisterExtension + private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private static final AWSCredentialsProviderChain credentialsProvider = + new AWSCredentialsProviderChain( + new EnvironmentVariableCredentialsProvider(), + new SystemPropertiesCredentialsProvider(), + new ProfileCredentialsProvider(), + new InstanceProfileCredentialsProvider()); + + private static final MockWebServerExtension server = new MockWebServerExtension(); + + @BeforeAll + static void setUp() { + System.setProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY, "my-access-key"); + System.setProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY, "my-secret-key"); + server.start(); + } + + @BeforeEach + void reset() { + server.beforeTestExecution(null); + } + + @AfterAll + static void cleanUp() { + System.clearProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY); + System.clearProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY); + server.stop(); + } + + private static Stream provideArguments() { + return Stream.of(Arguments.of(true, 2), Arguments.of(false, 1)); + } + + @ParameterizedTest + @SuppressWarnings("unchecked") + @MethodSource("provideArguments") + void testRequestHandlerIsHookedUpWithConstructor(boolean addHandler, int size) throws Exception { + BasicAWSCredentials credentials = new BasicAWSCredentials("asdf", "qwerty"); + AmazonS3Client client = new AmazonS3Client(credentials); + if (addHandler) { + client.addRequestHandler(new RequestHandler2() {}); + } + + List requestHandler2s = extractRequestHandlers(client); + + assertThat(requestHandler2s).isNotNull(); + assertThat(requestHandler2s.size()).isEqualTo(size); + assertThat(requestHandler2s.stream().findFirst().get().getClass().getSimpleName()) + .isEqualTo("TracingRequestHandler"); + } + + private static Stream provideSendRequestArguments() { + return Stream.of( + Arguments.of( + new AmazonS3Client().withEndpoint(server.httpUri().toString()), + "S3", + "CreateBucket", + "PUT", + 1, + (Function) c -> c.createBucket("testbucket"), + ImmutableMap.of("aws.bucket.name", "testbucket"), + ""), + Arguments.of( + new AmazonS3Client().withEndpoint(server.httpUri().toString()), + "S3", + "GetObject", + "GET", + 1, + (Function) c -> c.getObject("someBucket", "someKey"), + ImmutableMap.of("aws.bucket.name", "someBucket"), + ""), + Arguments.of( + new AmazonEC2Client().withEndpoint(server.httpUri().toString()), + "EC2", + "AllocateAddress", + "POST", + 4, + (Function) AmazonEC2Client::allocateAddress, + emptyMap(), + "" + + " 59dbff89-35bd-4eac-99ed-be587EXAMPLE" + + " 192.0.2.1" + + " standard" + + ""), + Arguments.of( + new AmazonRDSClient().withEndpoint(server.httpUri().toString()), + "RDS", + "DeleteOptionGroup", + "POST", + 1, + (Function) + c -> c.deleteOptionGroup(new DeleteOptionGroupRequest()), + emptyMap(), + "" + + " 59dbff89-35bd-4eac-99ed-be587EXAMPLE" + + " 192.0.2.1" + + " standard" + + "")); + } + + @ParameterizedTest + @MethodSource("provideSendRequestArguments") + @SuppressWarnings("unchecked") + void testSendRequestWithMockedResponse( + AmazonWebServiceClient client, + String service, + String operation, + String method, + int handlerCount, + Function call, + Map additionalAttributes, + String body) + throws Exception { + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)); + + Object response = call.apply(client); + assertThat(response).isNotNull(); + + List requestHandler2s = extractRequestHandlers(client); + + assertThat(requestHandler2s).isNotNull(); + assertThat(requestHandler2s.size()).isEqualTo(handlerCount); + assertThat(requestHandler2s.stream().findFirst().get().getClass().getSimpleName()) + .isEqualTo("TracingRequestHandler"); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> { + List attributes = + new ArrayList<>( + asList( + equalTo(URL_FULL, server.httpUri().toString()), + equalTo(HTTP_REQUEST_METHOD, method), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200), + equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(RPC_SYSTEM, "aws-api"), + satisfies(RPC_SERVICE, v -> v.contains(service)), + equalTo(RPC_METHOD, operation), + equalTo(stringKey("aws.endpoint"), server.httpUri().toString()), + equalTo(stringKey("aws.agent"), "java-aws-sdk"))); + + additionalAttributes.forEach((k, v) -> attributes.add(equalTo(stringKey(k), v))); + + span.hasName(service + "." + operation) + .hasKind(operation.equals("SendMessage") ? PRODUCER : CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + })); + + RecordedRequest request = server.takeRequest(); + assertThat(request.request().headers().get("X-Amzn-Trace-Id")).isNotNull(); + assertThat(request.request().headers().get("traceparent")).isNull(); + } + + @Test + void testSendS3RequestToClosedPort() { + AmazonS3Client client = + new AmazonS3Client( + credentialsProvider, + new ClientConfiguration() + .withRetryPolicy( + PredefinedRetryPolicies.getDefaultRetryPolicyWithCustomMaxRetries(0))) + .withEndpoint("http://localhost:" + UNUSABLE_PORT); + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); + + Throwable caught = catchThrowable(() -> client.getObject("someBucket", "someKey")); + assertThat(caught).isInstanceOf(AmazonClientException.class); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("S3.GetObject") + .hasKind(CLIENT) + .hasStatus(StatusData.error()) + .hasException(caught) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(URL_FULL, "http://localhost:" + UNUSABLE_PORT), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, 61), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "Amazon S3"), + equalTo(RPC_METHOD, "GetObject"), + equalTo(stringKey("aws.endpoint"), "http://localhost:" + UNUSABLE_PORT), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(stringKey("aws.bucket.name"), "someBucket"), + equalTo(ERROR_TYPE, AmazonClientException.class.getName())))); + } + + @Test + void testNaughtyRequestHandlerDoesntBreakTheTrace() { + AmazonS3Client client = new AmazonS3Client(credentialsProvider); + client.addRequestHandler( + new RequestHandler2() { + @Override + public void beforeRequest(Request request) { + throw new IllegalStateException("bad handler"); + } + }); + + Throwable caught = catchThrowable(() -> client.getObject("someBucket", "someKey")); + + assertThat(caught).isInstanceOf(IllegalStateException.class); + assertThat(Span.current().getSpanContext().isValid()).isFalse(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("S3.GetObject") + .hasKind(CLIENT) + .hasStatus(StatusData.error()) + .hasException(caught) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(URL_FULL, "https://s3.amazonaws.com"), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(SERVER_ADDRESS, "s3.amazonaws.com"), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "Amazon S3"), + equalTo(RPC_METHOD, "GetObject"), + equalTo(stringKey("aws.endpoint"), "https://s3.amazonaws.com"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(stringKey("aws.bucket.name"), "someBucket"), + equalTo(ERROR_TYPE, IllegalStateException.class.getName())))); + } + + @Test + void testTimeoutAndRetryErrorsAreNotCaptured() { + server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofSeconds(5))); + server.enqueue(HttpResponse.delayed(HttpResponse.of(HttpStatus.OK), Duration.ofSeconds(5))); + AmazonS3Client client = + new AmazonS3Client( + new ClientConfiguration() + .withRequestTimeout(50 /* ms */) + .withRetryPolicy( + PredefinedRetryPolicies.getDefaultRetryPolicyWithCustomMaxRetries(1))) + .withEndpoint(server.httpUri().toString()); + + Throwable caught = catchThrowable(() -> client.getObject("someBucket", "someKey")); + + assertThat(caught).isInstanceOf(AmazonClientException.class); + assertThat(Span.current().getSpanContext().isValid()).isFalse(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("S3.GetObject") + .hasKind(CLIENT) + .hasStatus(StatusData.error()) + .hasException(caught) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(URL_FULL, server.httpUri().toString()), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(SERVER_ADDRESS, "127.0.0.1"), + equalTo(RPC_SYSTEM, "aws-api"), + equalTo(RPC_SERVICE, "Amazon S3"), + equalTo(RPC_METHOD, "GetObject"), + equalTo(stringKey("aws.endpoint"), server.httpUri().toString()), + equalTo(stringKey("aws.agent"), "java-aws-sdk"), + equalTo(stringKey("aws.bucket.name"), "someBucket"), + equalTo(ERROR_TYPE, AmazonClientException.class.getName())))); + } + + @Test + void testCallingGeneratePresignedUrlDoesNotLeakContext() { + SignerFactory.registerSigner("noop", NoOpSigner.class); + AmazonS3Client client = + new AmazonS3Client(new ClientConfiguration().withSignerOverride("noop")) + .withEndpoint(server.httpUri().toString()); + + client.generatePresignedUrl("someBucket", "someKey", new Date()); + + assertThat(Span.current().getSpanContext().isValid()).isFalse(); + } + + @SuppressWarnings("unchecked") + private static List extractRequestHandlers(Object client) throws Exception { + Field requestHandler2sField = AmazonWebServiceClient.class.getDeclaredField("requestHandler2s"); + requestHandler2sField.setAccessible(true); + return (List) requestHandler2sField.get(client); + } +}