From 3b0868d135270f12f5a724457a10f6bd2599b781 Mon Sep 17 00:00:00 2001 From: atshaw43 <108552302+atshaw43@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:58:23 -0800 Subject: [PATCH] Fixing S3 bucket name extraction for AWS SDK V2 (#393) * Fixing S3 bucket name extraction for AWS SDK V2 --- .../build.gradle.kts | 1 + .../xray/interceptors/TracingInterceptor.java | 32 +++- .../DefaultOperationParameterWhitelist.json | 140 +++++++++--------- .../interceptors/TracingInterceptorTest.java | 125 ++++++++++++++++ 4 files changed, 222 insertions(+), 76 deletions(-) diff --git a/aws-xray-recorder-sdk-aws-sdk-v2/build.gradle.kts b/aws-xray-recorder-sdk-aws-sdk-v2/build.gradle.kts index e458a70a..bd3007d3 100644 --- a/aws-xray-recorder-sdk-aws-sdk-v2/build.gradle.kts +++ b/aws-xray-recorder-sdk-aws-sdk-v2/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { testImplementation("software.amazon.awssdk:lambda:2.15.20") testImplementation("software.amazon.awssdk:sqs:2.15.20") testImplementation("software.amazon.awssdk:sns:2.15.20") + testImplementation("software.amazon.awssdk:s3:2.15.20") } tasks.jar { diff --git a/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java b/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java index 85a7b607..de5a9ca5 100644 --- a/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java +++ b/aws-xray-recorder-sdk-aws-sdk-v2/src/main/java/com/amazonaws/xray/interceptors/TracingInterceptor.java @@ -149,7 +149,8 @@ private HashMap extractRequestParameters( SdkRequest request = context.request(); Optional parameterValue = request.getValueForField(parameterName, Object.class); if (parameterValue.isPresent()) { - parameters.put(SNAKE_CASE_NAMING_STRATEGY.translate(parameterName), parameterValue.get()); + parameters.put(normalizeParameterName( + SNAKE_CASE_NAMING_STRATEGY.translate(parameterName)), parameterValue.get()); } }); } @@ -161,14 +162,16 @@ private HashMap extractRequestParameters( Optional parameterValue = request.getValueForField(key, Map.class); if (parameterValue.isPresent()) { String renameTo = descriptor.getRenameTo() != null ? descriptor.getRenameTo() : key; - parameters.put(SNAKE_CASE_NAMING_STRATEGY.translate(renameTo), parameterValue.get().keySet()); + parameters.put(normalizeParameterName( + SNAKE_CASE_NAMING_STRATEGY.translate(renameTo)), parameterValue.get().keySet()); } } else if (descriptor.isList() && descriptor.shouldGetCount()) { SdkRequest request = context.request(); Optional parameterValue = request.getValueForField(key, List.class); if (parameterValue.isPresent()) { String renameTo = descriptor.getRenameTo() != null ? descriptor.getRenameTo() : key; - parameters.put(SNAKE_CASE_NAMING_STRATEGY.translate(renameTo), parameterValue.get().size()); + parameters.put(normalizeParameterName( + SNAKE_CASE_NAMING_STRATEGY.translate(renameTo)), parameterValue.get().size()); } } }); @@ -177,6 +180,20 @@ private HashMap extractRequestParameters( return parameters; } + /** + * @param parameterName + * The name of the parameter to normalize. + * @return + * The field key for a parameter that the X-Ray backend expects. + */ + private String normalizeParameterName(String parameterName) { + // AWS SDK V2 changed the field name from BucketName to Bucket. + if (parameterName.equals("bucket")) { + return "bucket_name"; + } + return parameterName; + } + private HashMap extractResponseParameters( Context.AfterExecution context, ExecutionAttributes executionAttributes) { HashMap parameters = new HashMap<>(); @@ -191,7 +208,8 @@ private HashMap extractResponseParameters( SdkResponse response = context.response(); Optional parameterValue = response.getValueForField(parameterName, Object.class); if (parameterValue.isPresent()) { - parameters.put(SNAKE_CASE_NAMING_STRATEGY.translate(parameterName), parameterValue.get()); + parameters.put(normalizeParameterName( + SNAKE_CASE_NAMING_STRATEGY.translate(parameterName)), parameterValue.get()); } }); } @@ -203,14 +221,16 @@ private HashMap extractResponseParameters( Optional parameterValue = response.getValueForField(key, Map.class); if (parameterValue.isPresent()) { String renameTo = descriptor.getRenameTo() != null ? descriptor.getRenameTo() : key; - parameters.put(SNAKE_CASE_NAMING_STRATEGY.translate(renameTo), parameterValue.get().keySet()); + parameters.put(normalizeParameterName( + SNAKE_CASE_NAMING_STRATEGY.translate(renameTo)), parameterValue.get().keySet()); } } else if (descriptor.isList() && descriptor.shouldGetCount()) { SdkResponse response = context.response(); Optional parameterValue = response.getValueForField(key, List.class); if (parameterValue.isPresent()) { String renameTo = descriptor.getRenameTo() != null ? descriptor.getRenameTo() : key; - parameters.put(SNAKE_CASE_NAMING_STRATEGY.translate(renameTo), parameterValue.get().size()); + parameters.put(normalizeParameterName( + SNAKE_CASE_NAMING_STRATEGY.translate(renameTo)), parameterValue.get().size()); } } }); diff --git a/aws-xray-recorder-sdk-aws-sdk-v2/src/main/resources/com/amazonaws/xray/interceptors/DefaultOperationParameterWhitelist.json b/aws-xray-recorder-sdk-aws-sdk-v2/src/main/resources/com/amazonaws/xray/interceptors/DefaultOperationParameterWhitelist.json index fc6d9196..28eeb79a 100644 --- a/aws-xray-recorder-sdk-aws-sdk-v2/src/main/resources/com/amazonaws/xray/interceptors/DefaultOperationParameterWhitelist.json +++ b/aws-xray-recorder-sdk-aws-sdk-v2/src/main/resources/com/amazonaws/xray/interceptors/DefaultOperationParameterWhitelist.json @@ -362,383 +362,383 @@ "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "PutObject": { "request_parameters": [ "Key", - "BucketName" + "Bucket" ] }, "GetObjectAcl": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "CreateBucket": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "ListObjectsV2": { "request_parameters": [ "Prefix", - "BucketName" + "Bucket" ] }, "ListObjects": { "request_parameters": [ "Prefix", - "BucketName" + "Bucket" ] }, "GetObjectTagging": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "SetObjectTagging": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "ListVersions": { "request_parameters": [ "Prefix", - "BucketName" + "Bucket" ] }, "SetObjectAcl": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "GetBucketAcl": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketAcl": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "HeadBucket": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "UploadPart": { "request_parameters": [ "Key", - "BucketName" + "Bucket" ] }, "DeleteObject": { "request_parameters": [ "Key", - "BucketName" + "Bucket" ] }, "DeleteBucket": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteObjects": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteVersion": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "GetBucketPolicy": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketPolicy": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "ListParts": { "request_parameters": [ "Key", - "BucketName" + "Bucket" ] }, "RestoreObject": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "RestoreObjectV2": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "SetBucketNotificationConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketLifecycleConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketNotificationConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketCrossOriginConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketCrossOriginConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketCrossOriginConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "ListBucketInventoryConfigurations": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketReplicationConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketReplicationConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketReplicationConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketAnalyticsConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketInventoryConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "ListBucketAnalyticsConfigurations": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteObjectTagging": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "SetBucketVersioningConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketVersioningConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketWebsiteConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketLifecycleConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketLifecycleConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketTaggingConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketTaggingConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetObjectMetadata": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "GetBucketLocation": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketLoggingConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "ListMultipartUploads": { "request_parameters": [ "Prefix", - "BucketName" + "Bucket" ] }, "DeleteBucketPolicy": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketEncryption": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketAccelerateConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketWebsiteConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "CompleteMultipartUpload": { "request_parameters": [ "Key", - "BucketName" + "Bucket" ] }, "InitiateMultipartUpload": { "request_parameters": [ "Key", - "BucketName" + "Bucket" ] }, "SetBucketEncryption": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketLoggingConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketWebsiteConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketEncryption": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "AbortMultipartUpload": { "request_parameters": [ "Key", - "BucketName" + "Bucket" ] }, "GeneratePresignedUrl": { "request_parameters": [ "Key", "VersionId", - "BucketName" + "Bucket" ] }, "DeleteBucketTaggingConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketAccelerateConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketMetricsConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "ListBucketMetricsConfigurations": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketInventoryConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketMetricsConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "SetBucketAnalyticsConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "DeleteBucketMetricsConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketAnalyticsConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] }, "GetBucketInventoryConfiguration": { "request_parameters": [ - "BucketName" + "Bucket" ] } } diff --git a/aws-xray-recorder-sdk-aws-sdk-v2/src/test/java/com/amazonaws/xray/interceptors/TracingInterceptorTest.java b/aws-xray-recorder-sdk-aws-sdk-v2/src/test/java/com/amazonaws/xray/interceptors/TracingInterceptorTest.java index c51760ff..e2bdf8ba 100644 --- a/aws-xray-recorder-sdk-aws-sdk-v2/src/test/java/com/amazonaws/xray/interceptors/TracingInterceptorTest.java +++ b/aws-xray-recorder-sdk-aws-sdk-v2/src/test/java/com/amazonaws/xray/interceptors/TracingInterceptorTest.java @@ -36,6 +36,7 @@ import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.mockito.Mockito; @@ -47,6 +48,7 @@ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.ExecutableHttpRequest; import software.amazon.awssdk.http.HttpExecuteResponse; @@ -62,6 +64,12 @@ import software.amazon.awssdk.services.lambda.LambdaAsyncClient; import software.amazon.awssdk.services.lambda.LambdaClient; import software.amazon.awssdk.services.lambda.model.InvokeRequest; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.sns.SnsClient; import software.amazon.awssdk.services.sns.model.PublishRequest; import software.amazon.awssdk.services.sqs.SqsClient; @@ -485,6 +493,108 @@ public void testNoHeaderAddedWhenPropagationOff() { verify(mockRequest.toBuilder(), never()).appendHeader(anyString(), anyString()); } + @Test + public void testS3() throws Exception { + SdkHttpClient mockClient = mockClientWithSuccessResponse(""); + + try (S3Client s3 = s3Client(mockClient)) { + String bucket = "test-bucket"; + String key = "test-key"; + + Segment segment = AWSXRay.beginSegment("test"); + + //Create a Bucket + CreateBucketRequest createBucketRequest = CreateBucketRequest.builder() + .bucket(bucket) + .build(); + + s3.createBucket(createBucketRequest); + + // Put an Object + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .bucket(bucket) + .key(key) + .build(); + + s3.putObject(putObjectRequest, RequestBody.fromString("This is a test from java")); + + // Get an Object + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .bucket(bucket) + .key(key) + .build(); + + s3.getObject(getObjectRequest); + + // Delete an Object + DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder() + .bucket(bucket) + .key(key) + .build(); + + s3.deleteObject(deleteObjectRequest); + + Assertions.assertEquals(4, segment.getSubsegments().size()); + + Assertions.assertEquals("CreateBucket", segment.getSubsegments().get(0).getAws().get("operation")); + Assertions.assertEquals(bucket, segment.getSubsegments().get(0).getAws().get("bucket_name")); + + Assertions.assertEquals("PutObject", segment.getSubsegments().get(1).getAws().get("operation")); + Assertions.assertEquals(bucket, segment.getSubsegments().get(1).getAws().get("bucket_name")); + Assertions.assertEquals(key, segment.getSubsegments().get(1).getAws().get("key")); + + Assertions.assertEquals("GetObject", segment.getSubsegments().get(2).getAws().get("operation")); + Assertions.assertEquals(bucket, segment.getSubsegments().get(2).getAws().get("bucket_name")); + Assertions.assertEquals(key, segment.getSubsegments().get(2).getAws().get("key")); + + Assertions.assertEquals("DeleteObject", segment.getSubsegments().get(3).getAws().get("operation")); + Assertions.assertEquals(bucket, segment.getSubsegments().get(3).getAws().get("bucket_name")); + Assertions.assertEquals(key, segment.getSubsegments().get(3).getAws().get("key")); + } + } + + @Test + public void testS3ListObjects() throws Exception { + SdkHttpClient mockClient = mockClientWithSuccessResponse( + "\n" + + " bucket\n" + + " \n" + + " \n" + + " 1000\n" + + " false\n" + + " \n" + + " my-image.jpg\n" + + " 2009-10-12T17:50:30.000Z\n" + + " \"fba9dede5f27731c9771645a39863328\"\n" + + " 434234\n" + + " STANDARD\n" + + " \n" + + " 75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a\n" + + " xray_test@amazon.com\n" + + " \n" + + " \n" + + ""); + + try (S3Client s3 = s3Client(mockClient)) { + String bucket = "test-bucket"; + + // List Objects + ListObjectsRequest listObjectsRequest = ListObjectsRequest.builder() + .bucket(bucket) + .build(); + + s3.listObjects(listObjectsRequest); + + Segment segment = AWSXRay.getCurrentSegment(); + + Assertions.assertEquals(1, segment.getSubsegments().size()); + + Assertions.assertEquals("ListObjects", segment.getSubsegments().get(0).getAws().get("operation")); + Assertions.assertEquals(bucket, segment.getSubsegments().get(0).getAws().get("bucket_name")); + + } + } + private SdkHttpClient mockSdkHttpClient(SdkHttpResponse response) throws Exception { return mockSdkHttpClient(response, "OK"); } @@ -610,5 +720,20 @@ private static SnsClient snsClient(SdkHttpClient mockClient) { ) .build(); } + + private static S3Client s3Client(SdkHttpClient mockClient) { + return S3Client.builder() + .httpClient(mockClient) + .endpointOverride(URI.create("http://example.com")) + .region(Region.of("us-west-42")) + .credentialsProvider(StaticCredentialsProvider.create( + AwsSessionCredentials.create("key", "secret", "session") + )) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .addExecutionInterceptor(new TracingInterceptor()) + .build() + ) + .build(); + } }