This is an automated email from the ASF dual-hosted git repository.
Gargi-jais11 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 1b3cb9122b1 HDDS-15258. Include x-amz-tagging-count header in HEAD
object tagging responses (#10265).
1b3cb9122b1 is described below
commit 1b3cb9122b19077d9e1e159dcb3820eb39df1ca8
Author: Gargi Jaiswal <[email protected]>
AuthorDate: Fri May 15 08:48:01 2026 +0530
HDDS-15258. Include x-amz-tagging-count header in HEAD object tagging
responses (#10265).
---
.../ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java | 24 ++++++++++++++++++++++
.../ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java | 19 +++++++++++++++++
.../hadoop/ozone/s3/endpoint/ObjectEndpoint.java | 1 +
.../hadoop/ozone/s3/endpoint/TestObjectHead.java | 21 +++++++++++++++++++
4 files changed, 65 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 8238ade3921..8e79e057c03 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
@@ -35,6 +35,7 @@
import com.amazonaws.AmazonServiceException.ErrorType;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.Headers;
import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.Bucket;
@@ -70,6 +71,7 @@
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.services.s3.model.SetObjectAclRequest;
+import com.amazonaws.services.s3.model.SetObjectTaggingRequest;
import com.amazonaws.services.s3.model.Tag;
import com.amazonaws.services.s3.model.UploadPartRequest;
import com.amazonaws.services.s3.model.UploadPartResult;
@@ -1101,6 +1103,28 @@ public void testGetObject() throws Exception {
}
}
+ @Test
+ public void testHeadObjectReturnsTaggingCount() {
+ final String bucketName = getBucketName();
+ final String keyName = getKeyName();
+ final String content = "head-object-tag-count";
+ s3Client.createBucket(bucketName);
+
+ s3Client.putObject(bucketName, keyName,
+ new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)),
new ObjectMetadata());
+
+ List<Tag> tags = Arrays.asList(new Tag("tag1", "v1"), new Tag("tag2",
"v2"));
+ s3Client.setObjectTagging(
+ new SetObjectTaggingRequest(bucketName, keyName, new
ObjectTagging(tags)));
+
+ ObjectMetadata head = s3Client.getObjectMetadata(bucketName, keyName);
+ // AWS SDK v1: getTaggingCount() exists on S3Object (GET), not on
ObjectMetadata (HEAD).
+ // x-amz-tagging-count is exposed via raw metadata.
+ Object tagCountHeader = head.getRawMetadataValue(Headers.S3_TAGGING_COUNT);
+ assertNotNull(tagCountHeader);
+ assertEquals(tags.size(), Integer.parseInt(tagCountHeader.toString()));
+ }
+
@Test
public void testGetObjectWithoutETag() throws Exception {
// Object uploaded using other protocols (e.g. ofs / ozone cli) will not
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 28d8cbf1f61..ea608437361 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
@@ -1249,6 +1249,25 @@ public void testLowLevelMultipartUpload(@TempDir Path
tempDir) throws Exception
assertEquals(userMetadata, headObjectResponse.metadata());
}
+ @Test
+ public void testHeadObjectReturnsTaggingCount() {
+ final String bucketName = getBucketName();
+ final String keyName = getKeyName();
+ s3Client.createBucket(b -> b.bucket(bucketName));
+ s3Client.putObject(b -> b.bucket(bucketName).key(keyName),
RequestBody.fromString("obj"));
+
+ List<Tag> tags = Arrays.asList(
+ Tag.builder().key("tag1").value("v1").build(),
+ Tag.builder().key("tag2").value("v2").build());
+
+ s3Client.putObjectTagging(b -> b.bucket(bucketName).key(keyName)
+ .tagging(Tagging.builder().tagSet(tags).build()));
+
+ HeadObjectResponse head = s3Client.headObject(b ->
b.bucket(bucketName).key(keyName));
+ assertNotNull(head.tagCount());
+ assertEquals(tags.size(), head.tagCount().intValue());
+ }
+
@Test
public void testResumableDownloadWithEtagMismatch() throws Exception {
// Arrange
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
index 2021790b332..38cd6965a22 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
@@ -580,6 +580,7 @@ public Response head(
addEntityTagHeader(response, key);
addLastModifiedDate(response, key);
+ addTagCountIfAny(response, key);
addCustomMetadataHeaders(response, key);
getMetrics().updateHeadKeySuccessStats(startNanos);
auditReadSuccess(s3GAction);
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
index 10259005d07..f6795ba8f56 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
@@ -27,9 +27,12 @@
import static org.apache.hadoop.ozone.s3.util.S3Consts.IF_MATCH_HEADER;
import static org.apache.hadoop.ozone.s3.util.S3Consts.IF_NONE_MATCH_HEADER;
import static
org.apache.hadoop.ozone.s3.util.S3Consts.IF_UNMODIFIED_SINCE_HEADER;
+import static org.apache.hadoop.ozone.s3.util.S3Consts.TAG_COUNT_HEADER;
+import static org.apache.hadoop.ozone.s3.util.S3Consts.TAG_HEADER;
import static org.apache.hadoop.ozone.s3.util.S3Consts.X_AMZ_CONTENT_SHA256;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -91,6 +94,9 @@ public void testHeadObject() throws Exception {
DateTimeFormatter.RFC_1123_DATE_TIME
.parse(response.getHeaderString("Last-Modified"));
+
+ assertNull(response.getHeaderString(TAG_COUNT_HEADER),
+ "HeadObject must omit x-amz-tagging-count (AWS TagCount) when object
has no tags");
}
@Test
@@ -210,6 +216,21 @@ public void
testHeadWhenKeyIsAFileAndKeyPathEndsWithASlash()
assertStatus(HttpStatus.SC_NOT_FOUND, () -> keyEndpoint.head(bucketName,
keyPath + "/"));
}
+ @Test
+ public void testHeadObjectIncludesTagCount()
+ throws Exception {
+ String keyName = "head-with-tags";
+
when(headers.getHeaderString(TAG_HEADER)).thenReturn("tag1=value1&tag2=value2");
+ assertSucceeds(() -> put(keyEndpoint, bucketName, keyName, "c"));
+
+ Response response = keyEndpoint.head(bucketName, keyName);
+ assertEquals(HttpStatus.SC_OK, response.getStatus());
+ // S3 HeadObject TagCount in the AWS API is driven by x-amz-tagging-count
+ assertNotNull(response.getHeaderString(TAG_COUNT_HEADER),
+ "HeadObject must include x-amz-tagging-count when object has tags (AWS
TagCount)");
+ assertEquals("2", response.getHeaderString(TAG_COUNT_HEADER));
+ }
+
private byte[] createKey(String keyPath) throws IOException {
byte[] bytes =
RandomStringUtils.secure().nextAlphanumeric(32).getBytes(UTF_8);
try (OutputStream out = bucket.createKey(keyPath, bytes.length)) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]