This is an automated email from the ASF dual-hosted git repository. ivandika pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push: new ae9c53cfba7 HDDS-13663. Validate Presigned DeleteObject (#9019) ae9c53cfba7 is described below commit ae9c53cfba7a449455e4c031b838d05e8689b969 Author: Hsu Han Wen <hevin...@gmail.com> AuthorDate: Fri Sep 12 11:46:12 2025 +0800 HDDS-13663. Validate Presigned DeleteObject (#9019) --- .../ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java | 34 +++++++++ .../ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java | 87 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java index eed72db3c13..54bdddf6e1c 100644 --- a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java +++ b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java @@ -1378,6 +1378,40 @@ private GeneratePresignedUrlRequest createInitMPUPresignedUrlRequest(String buck return initMPUPresignUrlRequest; } + @Test + public void testPresignedUrlDelete() throws IOException { + final String bucketName = getBucketName(); + final String keyName = getKeyName(); + final String content = "bar"; + + s3Client.createBucket(bucketName); + try (InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) { + s3Client.putObject(bucketName, keyName, is, new ObjectMetadata()); + } + + // Set the presigned URL to expire after one hour. + Date expiration = Date.from(Instant.now().plusMillis(1000 * 60 * 60)); + + // Generate the presigned URL for DELETE + GeneratePresignedUrlRequest generatePresignedUrlRequest = + new GeneratePresignedUrlRequest(bucketName, keyName) + .withMethod(HttpMethod.DELETE) + .withExpiration(expiration); + URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest); + + // Execute the DELETE request using HttpUrlConnection + URL presignedUrl = new URL(url.toExternalForm()); + HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection(); + connection.setRequestMethod("DELETE"); + int responseCode = connection.getResponseCode(); + + // Verify the response code is 204 (No Content) + assertEquals(HttpURLConnection.HTTP_NO_CONTENT, responseCode); + + // Verify the object is deleted + assertFalse(s3Client.doesObjectExist(bucketName, keyName)); + } + /** * Tests the functionality to create a snapshot of an Ozone bucket and then read files * from the snapshot directory using the S3 SDK. diff --git a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java index ff54190d3f3..31f58c6d9a2 100644 --- a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java +++ b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java @@ -103,6 +103,7 @@ import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; import software.amazon.awssdk.services.s3.model.Delete; import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; import software.amazon.awssdk.services.s3.model.DeleteObjectTaggingRequest; import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest; import software.amazon.awssdk.services.s3.model.GetBucketAclRequest; @@ -119,6 +120,7 @@ import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; import software.amazon.awssdk.services.s3.model.ListPartsRequest; +import software.amazon.awssdk.services.s3.model.NoSuchKeyException; import software.amazon.awssdk.services.s3.model.ObjectIdentifier; import software.amazon.awssdk.services.s3.model.PutBucketAclRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; @@ -134,11 +136,13 @@ import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.CompleteMultipartUploadPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.CreateMultipartUploadPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.DeleteObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.HeadBucketPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.HeadObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedDeleteObjectRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedHeadBucketRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedHeadObjectRequest; @@ -1077,6 +1081,89 @@ private String buildCompleteMultipartUploadXml(List<CompletedPart> parts) { return xml.toString(); } + @Test + public void testPresignedUrlDelete() throws Exception { + final String bucketName = getBucketName(); + final String keyName = getKeyName(); + final String content = "bar"; + s3Client.createBucket(b -> b.bucket(bucketName)); + + s3Client.putObject(b -> b + .bucket(bucketName) + .key(keyName), + RequestBody.fromString(content)); + + try (S3Presigner presigner = createS3Presigner()) { + + DeleteObjectRequest objectRequest = DeleteObjectRequest.builder() + .bucket(bucketName) + .key(keyName) + .build(); + + DeleteObjectPresignRequest presignRequest = DeleteObjectPresignRequest.builder() + .signatureDuration(Duration.ofMinutes(10)) + .deleteObjectRequest(objectRequest) + .build(); + + PresignedDeleteObjectRequest presignedRequest = presigner.presignDeleteObject(presignRequest); + + // use http url connection + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) presignedRequest.url().openConnection(); + connection.setRequestMethod("DELETE"); + + int responseCode = connection.getResponseCode(); + assertEquals(204, responseCode, "DeleteObject presigned URL should return 204 No Content"); + + //verify the object was deleted + assertThrows(NoSuchKeyException.class, () -> s3Client.getObject(b -> b.bucket(bucketName).key(keyName))); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + + // use SdkHttpClient + s3Client.putObject(b -> b + .bucket(bucketName) + .key(keyName), + RequestBody.fromString(content)); + + try (S3Presigner presigner = createS3Presigner(); + SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) { + + DeleteObjectRequest objectRequest = DeleteObjectRequest.builder() + .bucket(bucketName) + .key(keyName) + .build(); + + DeleteObjectPresignRequest presignRequest = DeleteObjectPresignRequest.builder() + .signatureDuration(Duration.ofMinutes(10)) + .deleteObjectRequest(objectRequest) + .build(); + + PresignedDeleteObjectRequest presignedRequest = presigner.presignDeleteObject(presignRequest); + + SdkHttpRequest request = SdkHttpRequest.builder() + .method(SdkHttpMethod.DELETE) + .uri(presignedRequest.url().toURI()) + .build(); + + HttpExecuteRequest executeRequest = HttpExecuteRequest.builder() + .request(request) + .build(); + + HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call(); + assertEquals(204, response.httpResponse().statusCode(), + "DeleteObject presigned URL should return 204 No Content via SdkHttpClient"); + + //verify the object was deleted + assertThrows(NoSuchKeyException.class, () -> s3Client.getObject(b -> b.bucket(bucketName).key(keyName))); + } + } + private S3Presigner createS3Presigner() { return S3Presigner.builder() // TODO: Find a way to retrieve the path style configuration from S3Client instead --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@ozone.apache.org For additional commands, e-mail: commits-h...@ozone.apache.org