Skip to content

Commit 2f48b77

Browse files
feat: Extract account/access key id and region for cross-account support (#1081)
**Description of changes:** Changes in ADOT package to support cross-account observability in Java V1 & V2 SDK. 1. We extract account id and region from remote resource arn. 2. We pass account access key id and region from span to metric when resource arn is not available. 3. Add instrumentation patch for AWS SDK Related changes in upstream package: yiyuan-he/opentelemetry-java-instrumentation#2 **Testing Plan:** 1. Unit tests for extracting account id and region from remote resource arn. 2. Unit tests for extracting access key and region from credential. 3. Unit test for the scenario when resource arn and credential information are both available. 5. Contract tests By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 93440a7 commit 2f48b77

File tree

13 files changed

+2148
-142
lines changed

13 files changed

+2148
-142
lines changed

.github/patches/opentelemetry-java-instrumentation.patch

Lines changed: 693 additions & 80 deletions
Large diffs are not rendered by default.

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java

Lines changed: 780 additions & 6 deletions
Large diffs are not rendered by default.

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ void testDynamoDbPutItem() {
181181
doTestDynamoDbPutItem();
182182
}
183183

184+
@Test
185+
void testDynamoDbDescribeTable() {
186+
doTestDynamoDbDescribeTable();
187+
}
188+
184189
@Test
185190
void testDynamoDbError() throws Exception {
186191
doTestDynamoDbError();
@@ -221,6 +226,11 @@ void testKinesisPutRecord() throws Exception {
221226
doTestKinesisPutRecord();
222227
}
223228

229+
@Test
230+
void testKinesisDescribeStream() {
231+
doTestKinesisDescribeStream();
232+
}
233+
224234
@Test
225235
void testKinsesisError() throws Exception {
226236
doTestKinesisError();
@@ -340,4 +350,9 @@ void testSnsError() throws Exception {
340350
void testSnsFault() throws Exception {
341351
doTestStepFunctionsFault();
342352
}
353+
354+
@Test
355+
void testCrossAccount() throws Exception {
356+
doTestCrossAccount(null);
357+
}
343358
}

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ void testDynamoDbPutItem() {
179179
doTestDynamoDbPutItem();
180180
}
181181

182+
@Test
183+
void testDynamoDbDescribeTable() {
184+
doTestDynamoDbDescribeTable();
185+
}
186+
182187
@Test
183188
void testDynamoDbError() throws Exception {
184189
doTestDynamoDbError();
@@ -224,6 +229,11 @@ void testKinesisPutRecord() throws Exception {
224229
doTestKinesisPutRecord();
225230
}
226231

232+
@Test
233+
void testKinesisDescribeStream() {
234+
doTestKinesisDescribeStream();
235+
}
236+
227237
@Test
228238
void testKinesisError() throws Exception {
229239
doTestKinesisError();
@@ -343,4 +353,9 @@ void testSnsError() throws Exception {
343353
void testSnsFault() throws Exception {
344354
doTestStepFunctionsFault();
345355
}
356+
357+
@Test
358+
void testCrossAccount() throws Exception {
359+
doTestCrossAccount("eu-central-1");
360+
}
346361
}

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ public class AppSignalsConstants {
3535
"aws.remote.resource.cfn.primary.identifier";
3636
public static final String AWS_SPAN_KIND = "aws.span.kind";
3737
public static final String AWS_REMOTE_DB_USER = "aws.remote.db.user";
38-
38+
public static final String AWS_REMOTE_RESOURCE_ACCESS_KEY =
39+
"aws.remote.resource.account.access_key";
40+
public static final String AWS_REMOTE_RESOURCE_ACCOUNT_ID = "aws.remote.resource.account.id";
41+
public static final String AWS_REMOTE_RESOURCE_REGION = "aws.remote.resource.region";
3942
// JVM Metrics
4043
public static final String JVM_GC_DURATION = "jvm.gc.collections.elapsed";
4144
public static final String JVM_GC_COUNT = "jvm.gc.collections.count";

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class SemanticConventionsConstants {
5656
public static final String AWS_TABLE_NAME = "aws.table.name";
5757
public static final String AWS_QUEUE_URL = "aws.queue.url";
5858
public static final String AWS_QUEUE_NAME = "aws.queue.name";
59+
public static final String AWS_STREAM_ARN = "aws.stream.arn";
5960
public static final String AWS_STREAM_NAME = "aws.stream.name";
6061
public static final String AWS_KNOWLEDGE_BASE_ID = "aws.bedrock.knowledge_base.id";
6162
public static final String AWS_DATA_SOURCE_ID = "aws.bedrock.data_source.id";

appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
import static spark.Spark.port;
2222
import static spark.Spark.post;
2323

24+
import com.amazonaws.auth.AWSCredentials;
25+
import com.amazonaws.auth.AWSCredentialsProvider;
2426
import com.amazonaws.auth.AWSStaticCredentialsProvider;
2527
import com.amazonaws.auth.BasicAWSCredentials;
28+
import com.amazonaws.auth.BasicSessionCredentials;
2629
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
2730
import com.amazonaws.regions.Regions;
2831
import com.amazonaws.services.bedrock.AmazonBedrockClient;
@@ -49,6 +52,7 @@
4952
import com.amazonaws.services.identitymanagement.model.PutRolePolicyRequest;
5053
import com.amazonaws.services.kinesis.AmazonKinesisClient;
5154
import com.amazonaws.services.kinesis.model.CreateStreamRequest;
55+
import com.amazonaws.services.kinesis.model.DescribeStreamRequest;
5256
import com.amazonaws.services.kinesis.model.PutRecordRequest;
5357
import com.amazonaws.services.s3.AmazonS3Client;
5458
import com.amazonaws.services.s3.model.CreateBucketRequest;
@@ -152,6 +156,7 @@ public static void main(String[] args) throws IOException, InterruptedException
152156
setupStepFunctions();
153157
setupSns();
154158
setupBedrock();
159+
setupCrossAccount();
155160

156161
// Add this log line so that we only start testing after all routes are configured.
157162
awaitInitialization();
@@ -320,6 +325,33 @@ private static void setupDynamoDb() {
320325
dynamoDbClient.putItem(putItemRequest);
321326
return "";
322327
});
328+
329+
get(
330+
"/ddb/describetable/:tablename",
331+
(req, res) -> {
332+
var tableName = req.params(":tablename");
333+
334+
var createTableRequest =
335+
new CreateTableRequest()
336+
.withTableName(tableName)
337+
.withAttributeDefinitions(
338+
new AttributeDefinition()
339+
.withAttributeName("partitionKey")
340+
.withAttributeType("S"))
341+
.withKeySchema(
342+
new KeySchemaElement()
343+
.withAttributeName("partitionKey")
344+
.withKeyType(KeyType.HASH))
345+
.withProvisionedThroughput(
346+
new ProvisionedThroughput()
347+
.withReadCapacityUnits(1L)
348+
.withWriteCapacityUnits(1L));
349+
dynamoDbClient.createTable(createTableRequest);
350+
351+
dynamoDbClient.describeTable(tableName);
352+
return "";
353+
});
354+
323355
get(
324356
"/ddb/error",
325357
(req, res) -> {
@@ -405,6 +437,30 @@ private static void setupKinesis() {
405437
return "";
406438
});
407439

440+
get(
441+
"/kinesis/describestream/:streamname",
442+
(req, res) -> {
443+
var streamName = req.params(":streamname");
444+
445+
var kinesisClient =
446+
AmazonKinesisClient.builder()
447+
.withEndpointConfiguration(endpointConfiguration)
448+
.withCredentials(CREDENTIALS_PROVIDER)
449+
.build();
450+
451+
var createStreamRequest = new CreateStreamRequest();
452+
createStreamRequest.setStreamName(streamName);
453+
454+
kinesisClient.createStream(createStreamRequest);
455+
456+
// Describe stream using ARN
457+
var streamArn = "arn:aws:kinesis:us-west-2:000000000000:stream/" + streamName;
458+
DescribeStreamRequest describeStreamRequest =
459+
new DescribeStreamRequest().withStreamARN(streamArn);
460+
kinesisClient.describeStream(describeStreamRequest);
461+
return "";
462+
});
463+
408464
get(
409465
"/kinesis/error",
410466
(req, res) -> {
@@ -1191,4 +1247,30 @@ private static void setupBedrock() {
11911247
return "";
11921248
});
11931249
}
1250+
1251+
private static void setupCrossAccount() {
1252+
// Create credentials provider with temporary credentials
1253+
AWSCredentials sessionCredentials =
1254+
new BasicSessionCredentials(
1255+
"account_b_access_key_id", "account_b_secret_access_key", "account_b_token");
1256+
AWSCredentialsProvider sessionCredentialsProvider =
1257+
new AWSStaticCredentialsProvider(sessionCredentials);
1258+
1259+
// Create S3 client with temporary credentials
1260+
var crossAccountS3Client =
1261+
AmazonS3Client.builder()
1262+
.withCredentials(sessionCredentialsProvider)
1263+
.withEndpointConfiguration(
1264+
new EndpointConfiguration(s3Endpoint, Regions.EU_CENTRAL_1.getName()))
1265+
.build();
1266+
1267+
get(
1268+
"/crossaccount/createbucket/accountb",
1269+
(req, res) -> {
1270+
CreateBucketRequest createBucketRequest =
1271+
new CreateBucketRequest("cross-account-bucket", Region.EU_Frankfurt);
1272+
crossAccountS3Client.createBucket(createBucketRequest);
1273+
return "";
1274+
});
1275+
}
11941276
}

appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.slf4j.Logger;
3636
import org.slf4j.LoggerFactory;
3737
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
38+
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
3839
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
3940
import software.amazon.awssdk.core.SdkBytes;
4041
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
@@ -68,6 +69,7 @@
6869
import software.amazon.awssdk.services.kinesis.model.DescribeStreamRequest;
6970
import software.amazon.awssdk.services.kinesis.model.PutRecordRequest;
7071
import software.amazon.awssdk.services.s3.S3Client;
72+
import software.amazon.awssdk.services.s3.model.CreateBucketConfiguration;
7173
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
7274
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
7375
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
@@ -148,6 +150,7 @@ public static void main(String[] args) throws IOException, InterruptedException
148150
setupSfn();
149151
setupBedrock();
150152
setupSns();
153+
setupCrossAccount();
151154
// Add this log line so that we only start testing after all routes are configured.
152155
awaitInitialization();
153156
logger.info("All routes initialized");
@@ -328,6 +331,36 @@ private static void setupDynamoDb() {
328331
dynamoDbClient.putItem(putItemRequest);
329332
return "";
330333
});
334+
335+
get(
336+
"/ddb/describetable/:tablename",
337+
(req, res) -> {
338+
var tableName = req.params(":tablename");
339+
340+
var createTableRequest =
341+
CreateTableRequest.builder()
342+
.tableName(tableName)
343+
.attributeDefinitions(
344+
AttributeDefinition.builder()
345+
.attributeName("partitionKey")
346+
.attributeType("S")
347+
.build())
348+
.keySchema(
349+
KeySchemaElement.builder()
350+
.attributeName("partitionKey")
351+
.keyType(KeyType.HASH)
352+
.build())
353+
.provisionedThroughput(
354+
ProvisionedThroughput.builder()
355+
.readCapacityUnits(1L)
356+
.writeCapacityUnits(1L)
357+
.build())
358+
.build();
359+
dynamoDbClient.createTable(createTableRequest);
360+
dynamoDbClient.describeTable(r -> r.tableName(tableName));
361+
return "";
362+
});
363+
331364
get(
332365
"/ddb/error",
333366
(req, res) -> {
@@ -413,6 +446,26 @@ private static void setupKinesis() {
413446
return "";
414447
});
415448

449+
get(
450+
"/kinesis/describestream/:streamname",
451+
(req, res) -> {
452+
var streamName = req.params(":streamname");
453+
454+
var kinesisClient =
455+
KinesisClient.builder()
456+
.endpointOverride(endpoint)
457+
.credentialsProvider(CREDENTIALS_PROVIDER)
458+
.build();
459+
460+
kinesisClient.createStream(CreateStreamRequest.builder().streamName(streamName).build());
461+
462+
// Describe stream using ARN
463+
var streamArn = "arn:aws:kinesis:us-west-2:000000000000:stream/" + streamName;
464+
var describeStreamRequest = DescribeStreamRequest.builder().streamARN(streamArn).build();
465+
kinesisClient.describeStream(describeStreamRequest);
466+
return "";
467+
});
468+
416469
get(
417470
"/kinesis/error",
418471
(req, res) -> {
@@ -1209,4 +1262,36 @@ private static void setupBedrock() {
12091262
return "";
12101263
});
12111264
}
1265+
1266+
private static void setupCrossAccount() {
1267+
// Create credentials provider with temporary credentials
1268+
AwsSessionCredentials sessionCredentials =
1269+
AwsSessionCredentials.create(
1270+
"account_b_access_key_id", "account_b_secret_access_key", "account_b_token");
1271+
StaticCredentialsProvider sessionCredentialsProvider =
1272+
StaticCredentialsProvider.create(sessionCredentials);
1273+
1274+
// Create S3 client with temporary credentials
1275+
var crossAccountS3Client =
1276+
S3Client.builder()
1277+
.credentialsProvider(sessionCredentialsProvider)
1278+
.endpointOverride(s3Endpoint)
1279+
.region(Region.EU_CENTRAL_1)
1280+
.build();
1281+
1282+
get(
1283+
"/crossaccount/createbucket/accountb",
1284+
(req, res) -> {
1285+
CreateBucketRequest createBucketRequest =
1286+
CreateBucketRequest.builder()
1287+
.bucket("cross-account-bucket")
1288+
.createBucketConfiguration(
1289+
CreateBucketConfiguration.builder()
1290+
.locationConstraint(Region.EU_CENTRAL_1.id())
1291+
.build())
1292+
.build();
1293+
crossAccountS3Client.createBucket(createBucketRequest);
1294+
return "";
1295+
});
1296+
}
12121297
}

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ final class AwsAttributeKeys {
2222

2323
private AwsAttributeKeys() {}
2424

25+
static final AttributeKey<String> AWS_AUTH_ACCESS_KEY =
26+
AttributeKey.stringKey("aws.auth.account.access_key");
27+
28+
static final AttributeKey<String> AWS_AUTH_REGION = AttributeKey.stringKey("aws.auth.region");
29+
2530
static final AttributeKey<String> AWS_SPAN_KIND = AttributeKey.stringKey("aws.span.kind");
2631

2732
static final AttributeKey<String> AWS_LOCAL_SERVICE = AttributeKey.stringKey("aws.local.service");
@@ -51,6 +56,15 @@ private AwsAttributeKeys() {}
5156
static final AttributeKey<String> AWS_REMOTE_OPERATION =
5257
AttributeKey.stringKey("aws.remote.operation");
5358

59+
static final AttributeKey<String> AWS_REMOTE_RESOURCE_ACCESS_KEY =
60+
AttributeKey.stringKey("aws.remote.resource.account.access_key");
61+
62+
static final AttributeKey<String> AWS_REMOTE_RESOURCE_ACCOUNT_ID =
63+
AttributeKey.stringKey("aws.remote.resource.account.id");
64+
65+
static final AttributeKey<String> AWS_REMOTE_RESOURCE_REGION =
66+
AttributeKey.stringKey("aws.remote.resource.region");
67+
5468
static final AttributeKey<String> AWS_REMOTE_RESOURCE_IDENTIFIER =
5569
AttributeKey.stringKey("aws.remote.resource.identifier");
5670

@@ -83,7 +97,7 @@ private AwsAttributeKeys() {}
8397
static final AttributeKey<String> AWS_LAMBDA_NAME =
8498
AttributeKey.stringKey("aws.lambda.function.name");
8599

86-
static final AttributeKey<String> AWS_LAMBDA_ARN =
100+
static final AttributeKey<String> AWS_LAMBDA_FUNCTION_ARN =
87101
AttributeKey.stringKey("aws.lambda.function.arn");
88102

89103
static final AttributeKey<String> AWS_LAMBDA_RESOURCE_ID =
@@ -100,7 +114,9 @@ private AwsAttributeKeys() {}
100114
static final AttributeKey<String> AWS_QUEUE_URL = AttributeKey.stringKey("aws.queue.url");
101115
static final AttributeKey<String> AWS_QUEUE_NAME = AttributeKey.stringKey("aws.queue.name");
102116
static final AttributeKey<String> AWS_STREAM_NAME = AttributeKey.stringKey("aws.stream.name");
117+
static final AttributeKey<String> AWS_STREAM_ARN = AttributeKey.stringKey("aws.stream.arn");
103118
static final AttributeKey<String> AWS_TABLE_NAME = AttributeKey.stringKey("aws.table.name");
119+
static final AttributeKey<String> AWS_TABLE_ARN = AttributeKey.stringKey("aws.table.arn");
104120
static final AttributeKey<String> AWS_AGENT_ID = AttributeKey.stringKey("aws.bedrock.agent.id");
105121
static final AttributeKey<String> AWS_KNOWLEDGE_BASE_ID =
106122
AttributeKey.stringKey("aws.bedrock.knowledge_base.id");

0 commit comments

Comments
 (0)