Skip to content

Commit febe5c2

Browse files
feat: Add auto-instrumentation support for AWS Secrets Manager SDK v1 (#14027)
Co-authored-by: otelbot <[email protected]>
1 parent 7b5f56e commit febe5c2

File tree

6 files changed

+166
-9
lines changed

6 files changed

+166
-9
lines changed

instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ dependencies {
2323
latestDepTestLibrary("com.amazonaws:aws-java-sdk-sqs:1.12.583") // documented limitation
2424
}
2525

26-
if (!(findProperty("testLatestDeps") as Boolean)) {
26+
val testLatestDeps = findProperty("testLatestDeps") as Boolean
27+
if (!testLatestDeps) {
2728
configurations.testRuntimeClasspath {
2829
resolutionStrategy {
2930
eachDependency {
@@ -36,12 +37,26 @@ if (!(findProperty("testLatestDeps") as Boolean)) {
3637
}
3738
}
3839

40+
testing {
41+
suites {
42+
val testSecretsManager by registering(JvmTestSuite::class) {
43+
dependencies {
44+
implementation(project())
45+
implementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing"))
46+
version = if (testLatestDeps) "latest.release" else "1.12.80"
47+
implementation("com.amazonaws:aws-java-sdk-secretsmanager:$version")
48+
}
49+
}
50+
}
51+
}
52+
3953
tasks {
4054
val testStableSemconv by registering(Test::class) {
4155
jvmArgs("-Dotel.semconv-stability.opt-in=database")
4256
}
4357

4458
check {
59+
dependsOn(testing.suites)
4560
dependsOn(testStableSemconv)
4661
}
4762
}

instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkAttributesExtractor.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
2424
private static final AttributeKey<String> AWS_REQUEST_ID = stringKey("aws.request_id");
2525

2626
// Copied from AwsIncubatingAttributes
27+
private static final AttributeKey<String> AWS_SECRETSMANAGER_SECRET_ARN =
28+
stringKey("aws.secretsmanager.secret.arn");
2729
private static final AttributeKey<String> AWS_STEP_FUNCTIONS_ACTIVITY_ARN =
2830
stringKey("aws.step_functions.activity.arn");
2931
private static final AttributeKey<String> AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN =
@@ -62,8 +64,9 @@ public void onEnd(
6264
Request<?> request,
6365
@Nullable Response<?> response,
6466
@Nullable Throwable error) {
65-
if (response != null) {
66-
Object awsResp = response.getAwsResponse();
67+
Object awsResp = getAwsResponse(response);
68+
if (awsResp != null) {
69+
setAttribute(attributes, AWS_SECRETSMANAGER_SECRET_ARN, awsResp, RequestAccess::getSecretArn);
6770
setAttribute(
6871
attributes,
6972
AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN,
@@ -106,4 +109,11 @@ public static void setAttribute(
106109
attributes.put(key, value);
107110
}
108111
}
112+
113+
private static Object getAwsResponse(Response<?> response) {
114+
if (response == null) {
115+
return null;
116+
}
117+
return response.getAwsResponse();
118+
}
109119
}

instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import javax.annotation.Nullable;
1212

1313
final class RequestAccess {
14+
private static final String SECRETS_MANAGER_REQUEST_CLASS_PREFIX =
15+
"com.amazonaws.services.secretsmanager.model.";
1416
private static final String STEP_FUNCTIONS_REQUEST_CLASS_PREFIX =
1517
"com.amazonaws.services.stepfunctions.model.";
1618

@@ -22,20 +24,20 @@ protected RequestAccess computeValue(Class<?> type) {
2224
}
2325
};
2426

27+
@Nullable
28+
static String getSecretArn(Object request) {
29+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
30+
return invokeOrNull(access.getSecretArn, request);
31+
}
32+
2533
@Nullable
2634
static String getStepFunctionsActivityArn(Object request) {
27-
if (request == null) {
28-
return null;
29-
}
3035
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
3136
return invokeOrNull(access.getStepFunctionsActivityArn, request);
3237
}
3338

3439
@Nullable
3540
static String getStateMachineArn(Object request) {
36-
if (request == null) {
37-
return null;
38-
}
3941
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
4042
return invokeOrNull(access.getStateMachineArn, request);
4143
}
@@ -97,6 +99,7 @@ private static String invokeOrNull(@Nullable MethodHandle method, Object obj) {
9799
@Nullable private final MethodHandle getBucketName;
98100
@Nullable private final MethodHandle getQueueUrl;
99101
@Nullable private final MethodHandle getQueueName;
102+
@Nullable private final MethodHandle getSecretArn;
100103
@Nullable private final MethodHandle getStreamName;
101104
@Nullable private final MethodHandle getTableName;
102105
@Nullable private final MethodHandle getTopicArn;
@@ -112,6 +115,9 @@ private RequestAccess(Class<?> clz) {
112115
getTableName = findAccessorOrNull(clz, "getTableName");
113116
getTopicArn = findAccessorOrNull(clz, "getTopicArn");
114117
getTargetArn = findAccessorOrNull(clz, "getTargetArn");
118+
119+
boolean isSecretsManager = clz.getName().startsWith(SECRETS_MANAGER_REQUEST_CLASS_PREFIX);
120+
getSecretArn = isSecretsManager ? findAccessorOrNull(clz, "getARN") : null;
115121
boolean isStepFunction = clz.getName().startsWith(STEP_FUNCTIONS_REQUEST_CLASS_PREFIX);
116122
getStateMachineArn = isStepFunction ? findAccessorOrNull(clz, "getStateMachineArn") : null;
117123
getStepFunctionsActivityArn = isStepFunction ? findAccessorOrNull(clz, "getActivityArn") : null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.awssdk.v1_11;
7+
8+
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
9+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
10+
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
11+
import org.junit.jupiter.api.extension.RegisterExtension;
12+
13+
class SecretsManagerClientTest extends AbstractSecretsManagerClientTest {
14+
@RegisterExtension
15+
private static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
16+
17+
@Override
18+
protected InstrumentationExtension testing() {
19+
return testing;
20+
}
21+
22+
@Override
23+
public AWSSecretsManagerClientBuilder configureClient(
24+
AWSSecretsManagerClientBuilder clientBuilder) {
25+
26+
return clientBuilder.withRequestHandlers(
27+
AwsSdkTelemetry.builder(testing().getOpenTelemetry())
28+
.setCaptureExperimentalSpanAttributes(true)
29+
.build()
30+
.newRequestHandler());
31+
}
32+
}

instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies {
1212
compileOnly("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
1313
compileOnly("com.amazonaws:aws-java-sdk-rds:1.11.106")
1414
compileOnly("com.amazonaws:aws-java-sdk-s3:1.11.106")
15+
compileOnly("com.amazonaws:aws-java-sdk-secretsmanager:1.12.80")
1516
compileOnly("com.amazonaws:aws-java-sdk-sns:1.11.106")
1617
compileOnly("com.amazonaws:aws-java-sdk-sqs:1.11.106")
1718
compileOnly("com.amazonaws:aws-java-sdk-stepfunctions:1.11.106")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.awssdk.v1_11;
7+
8+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
9+
import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_SECRETSMANAGER_SECRET_ARN;
10+
import static java.util.Collections.singletonList;
11+
12+
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
13+
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
14+
import com.amazonaws.services.secretsmanager.model.CreateSecretRequest;
15+
import com.amazonaws.services.secretsmanager.model.DescribeSecretRequest;
16+
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
17+
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
18+
import io.opentelemetry.testing.internal.armeria.common.HttpStatus;
19+
import io.opentelemetry.testing.internal.armeria.common.MediaType;
20+
import java.util.List;
21+
import org.junit.jupiter.api.Test;
22+
23+
public abstract class AbstractSecretsManagerClientTest extends AbstractBaseAwsClientTest {
24+
25+
public abstract AWSSecretsManagerClientBuilder configureClient(
26+
AWSSecretsManagerClientBuilder client);
27+
28+
@Override
29+
protected boolean hasRequestId() {
30+
return false;
31+
}
32+
33+
@Test
34+
public void sendCreateSecretRequestWithMockedResponse() throws Exception {
35+
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
36+
AWSSecretsManager client =
37+
configureClient(clientBuilder)
38+
.withEndpointConfiguration(endpoint)
39+
.withCredentials(credentialsProvider)
40+
.build();
41+
42+
String body =
43+
"{"
44+
+ "\"ARN\": \"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3\","
45+
+ "\"Name\": \"MyTestDatabaseSecret\","
46+
+ "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\""
47+
+ "}";
48+
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body));
49+
50+
Object response =
51+
client.createSecret(
52+
new CreateSecretRequest().withName("secretName").withSecretString("secretValue"));
53+
54+
List<AttributeAssertion> additionalAttributes =
55+
singletonList(
56+
equalTo(
57+
AWS_SECRETSMANAGER_SECRET_ARN,
58+
"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3"));
59+
60+
assertRequestWithMockedResponse(
61+
response, client, "AWSSecretsManager", "CreateSecret", "POST", additionalAttributes);
62+
}
63+
64+
@Test
65+
public void sendDescribeSecretRequestWithMockedResponse() throws Exception {
66+
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
67+
AWSSecretsManager client =
68+
configureClient(clientBuilder)
69+
.withEndpointConfiguration(endpoint)
70+
.withCredentials(credentialsProvider)
71+
.build();
72+
73+
String body =
74+
"{"
75+
+ "\"ARN\": \"arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar\","
76+
+ "\"Name\": \"My-Secret-Id\","
77+
+ "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\""
78+
+ "}";
79+
80+
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body));
81+
Object response =
82+
client.describeSecret(new DescribeSecretRequest().withSecretId("My-Secret-Id"));
83+
84+
List<AttributeAssertion> additionalAttributes =
85+
singletonList(
86+
equalTo(
87+
AWS_SECRETSMANAGER_SECRET_ARN,
88+
"arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar"));
89+
90+
assertRequestWithMockedResponse(
91+
response, client, "AWSSecretsManager", "DescribeSecret", "POST", additionalAttributes);
92+
}
93+
}

0 commit comments

Comments
 (0)