diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts index 1c2654b32547..042757e6aa73 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts @@ -23,7 +23,8 @@ dependencies { latestDepTestLibrary("com.amazonaws:aws-java-sdk-sqs:1.12.583") // documented limitation } -if (!(findProperty("testLatestDeps") as Boolean)) { +val testLatestDeps = findProperty("testLatestDeps") as Boolean +if (!testLatestDeps) { configurations.testRuntimeClasspath { resolutionStrategy { eachDependency { @@ -36,12 +37,26 @@ if (!(findProperty("testLatestDeps") as Boolean)) { } } +testing { + suites { + val testSecretsManager by registering(JvmTestSuite::class) { + dependencies { + implementation(project()) + implementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing")) + version = if (testLatestDeps) "latest.release" else "1.12.80" + implementation("com.amazonaws:aws-java-sdk-secretsmanager:$version") + } + } + } +} + tasks { val testStableSemconv by registering(Test::class) { jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { + dependsOn(testing.suites) dependsOn(testStableSemconv) } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkAttributesExtractor.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkAttributesExtractor.java index 531cbd4b82dd..57bbd87105c8 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkAttributesExtractor.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkAttributesExtractor.java @@ -24,6 +24,8 @@ class AwsSdkAttributesExtractor implements AttributesExtractor, Respo private static final AttributeKey AWS_REQUEST_ID = stringKey("aws.request_id"); // Copied from AwsIncubatingAttributes + private static final AttributeKey AWS_SECRETSMANAGER_SECRET_ARN = + stringKey("aws.secretsmanager.secret.arn"); private static final AttributeKey AWS_STEP_FUNCTIONS_ACTIVITY_ARN = stringKey("aws.step_functions.activity.arn"); private static final AttributeKey AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN = @@ -62,8 +64,9 @@ public void onEnd( Request request, @Nullable Response response, @Nullable Throwable error) { - if (response != null) { - Object awsResp = response.getAwsResponse(); + Object awsResp = getAwsResponse(response); + if (awsResp != null) { + setAttribute(attributes, AWS_SECRETSMANAGER_SECRET_ARN, awsResp, RequestAccess::getSecretArn); setAttribute( attributes, AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN, @@ -106,4 +109,11 @@ public static void setAttribute( attributes.put(key, value); } } + + private static Object getAwsResponse(Response response) { + if (response == null) { + return null; + } + return response.getAwsResponse(); + } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java index 8480b02de6ac..2658cd12448f 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java @@ -11,6 +11,8 @@ import javax.annotation.Nullable; final class RequestAccess { + private static final String SECRETS_MANAGER_REQUEST_CLASS_PREFIX = + "com.amazonaws.services.secretsmanager.model."; private static final String STEP_FUNCTIONS_REQUEST_CLASS_PREFIX = "com.amazonaws.services.stepfunctions.model."; @@ -22,20 +24,20 @@ protected RequestAccess computeValue(Class type) { } }; + @Nullable + static String getSecretArn(Object request) { + RequestAccess access = REQUEST_ACCESSORS.get(request.getClass()); + return invokeOrNull(access.getSecretArn, request); + } + @Nullable static String getStepFunctionsActivityArn(Object request) { - if (request == null) { - return null; - } RequestAccess access = REQUEST_ACCESSORS.get(request.getClass()); return invokeOrNull(access.getStepFunctionsActivityArn, request); } @Nullable static String getStateMachineArn(Object request) { - if (request == null) { - return null; - } RequestAccess access = REQUEST_ACCESSORS.get(request.getClass()); return invokeOrNull(access.getStateMachineArn, request); } @@ -97,6 +99,7 @@ private static String invokeOrNull(@Nullable MethodHandle method, Object obj) { @Nullable private final MethodHandle getBucketName; @Nullable private final MethodHandle getQueueUrl; @Nullable private final MethodHandle getQueueName; + @Nullable private final MethodHandle getSecretArn; @Nullable private final MethodHandle getStreamName; @Nullable private final MethodHandle getTableName; @Nullable private final MethodHandle getTopicArn; @@ -112,6 +115,9 @@ private RequestAccess(Class clz) { getTableName = findAccessorOrNull(clz, "getTableName"); getTopicArn = findAccessorOrNull(clz, "getTopicArn"); getTargetArn = findAccessorOrNull(clz, "getTargetArn"); + + boolean isSecretsManager = clz.getName().startsWith(SECRETS_MANAGER_REQUEST_CLASS_PREFIX); + getSecretArn = isSecretsManager ? findAccessorOrNull(clz, "getARN") : null; boolean isStepFunction = clz.getName().startsWith(STEP_FUNCTIONS_REQUEST_CLASS_PREFIX); getStateMachineArn = isStepFunction ? findAccessorOrNull(clz, "getStateMachineArn") : null; getStepFunctionsActivityArn = isStepFunction ? findAccessorOrNull(clz, "getActivityArn") : null; diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/testSecretsManager/java/io/opentelemetry/instrumentation/awssdk/v1_11/SecretsManagerClientTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/testSecretsManager/java/io/opentelemetry/instrumentation/awssdk/v1_11/SecretsManagerClientTest.java new file mode 100644 index 000000000000..fa214b2afad0 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/testSecretsManager/java/io/opentelemetry/instrumentation/awssdk/v1_11/SecretsManagerClientTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v1_11; + +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SecretsManagerClientTest extends AbstractSecretsManagerClientTest { + @RegisterExtension + private static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @Override + public AWSSecretsManagerClientBuilder configureClient( + AWSSecretsManagerClientBuilder clientBuilder) { + + return clientBuilder.withRequestHandlers( + AwsSdkTelemetry.builder(testing().getOpenTelemetry()) + .setCaptureExperimentalSpanAttributes(true) + .build() + .newRequestHandler()); + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts index 7a88d3ca7c63..793c6603ddba 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { compileOnly("com.amazonaws:aws-java-sdk-kinesis:1.11.106") compileOnly("com.amazonaws:aws-java-sdk-rds:1.11.106") compileOnly("com.amazonaws:aws-java-sdk-s3:1.11.106") + compileOnly("com.amazonaws:aws-java-sdk-secretsmanager:1.12.80") compileOnly("com.amazonaws:aws-java-sdk-sns:1.11.106") compileOnly("com.amazonaws:aws-java-sdk-sqs:1.11.106") compileOnly("com.amazonaws:aws-java-sdk-stepfunctions:1.11.106") diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSecretsManagerClientTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSecretsManagerClientTest.java new file mode 100644 index 000000000000..1267037d1d3f --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSecretsManagerClientTest.java @@ -0,0 +1,93 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v1_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_SECRETSMANAGER_SECRET_ARN; +import static java.util.Collections.singletonList; + +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.amazonaws.services.secretsmanager.model.CreateSecretRequest; +import com.amazonaws.services.secretsmanager.model.DescribeSecretRequest; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +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 java.util.List; +import org.junit.jupiter.api.Test; + +public abstract class AbstractSecretsManagerClientTest extends AbstractBaseAwsClientTest { + + public abstract AWSSecretsManagerClientBuilder configureClient( + AWSSecretsManagerClientBuilder client); + + @Override + protected boolean hasRequestId() { + return false; + } + + @Test + public void sendCreateSecretRequestWithMockedResponse() throws Exception { + AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard(); + AWSSecretsManager client = + configureClient(clientBuilder) + .withEndpointConfiguration(endpoint) + .withCredentials(credentialsProvider) + .build(); + + String body = + "{" + + "\"ARN\": \"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3\"," + + "\"Name\": \"MyTestDatabaseSecret\"," + + "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\"" + + "}"; + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)); + + Object response = + client.createSecret( + new CreateSecretRequest().withName("secretName").withSecretString("secretValue")); + + List additionalAttributes = + singletonList( + equalTo( + AWS_SECRETSMANAGER_SECRET_ARN, + "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3")); + + assertRequestWithMockedResponse( + response, client, "AWSSecretsManager", "CreateSecret", "POST", additionalAttributes); + } + + @Test + public void sendDescribeSecretRequestWithMockedResponse() throws Exception { + AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard(); + AWSSecretsManager client = + configureClient(clientBuilder) + .withEndpointConfiguration(endpoint) + .withCredentials(credentialsProvider) + .build(); + + String body = + "{" + + "\"ARN\": \"arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar\"," + + "\"Name\": \"My-Secret-Id\"," + + "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\"" + + "}"; + + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body)); + Object response = + client.describeSecret(new DescribeSecretRequest().withSecretId("My-Secret-Id")); + + List additionalAttributes = + singletonList( + equalTo( + AWS_SECRETSMANAGER_SECRET_ARN, + "arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar")); + + assertRequestWithMockedResponse( + response, client, "AWSSecretsManager", "DescribeSecret", "POST", additionalAttributes); + } +}