Repository navigation
Conversation
…ach object Versioning is not implemented, so every bucket behaves like an AWS S3 bucket that never had versioning enabled. ListObjectVersions now lists each object with VersionId "null" instead of returning NotImplemented, and "null" is accepted as the version ID of the current object in DeleteObjects, DeleteObject, PutObjectTagging and DeleteObjectTagging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Before we have proper versioning implementation, we could have this. Thoughts, @fmorg-git ? |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
ListObjectVersions must reject version-id-marker when key-marker is absent.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Adds unversioned ListObjectVersions compatibility to S3 Gateway and permits operations targeting the current object via version ID null.
Changes:
- Returns each object as its latest null version with pagination support.
- Accepts null versions for deletion and tagging while rejecting unsupported versions.
- Adds unit, SDK integration, and interface documentation coverage.
| File | Description |
|---|---|
TestObjectTaggingPut.java |
Tests tagging the null version. |
TestObjectTaggingDelete.java |
Tests removing null-version tags. |
TestObjectNotImplemented.java |
Tests null-version object deletion. |
TestObjectMultiDelete.java |
Tests null-version bulk deletion responses. |
TestBucketNotImplemented.java |
Allows GET on ?versions. |
TestBucketList.java |
Tests version listing and pagination. |
S3Consts.java |
Defines version constants and parameters. |
ObjectTaggingHandler.java |
Accepts null-version tagging requests. |
ObjectOperationNotImplementedHandler.java |
Allows null-version deletion. |
MultiDeleteResponse.java |
Serializes response version IDs. |
ListVersionsResult.java |
Models ListObjectVersions responses. |
EndpointBase.java |
Adds version-ID validation helper. |
BucketOperationNotImplementedHandler.java |
Routes version listings to the endpoint. |
BucketEndpoint.java |
Implements listing and null-version bulk deletion. |
KeyMetadata.java |
Adds version metadata fields. |
AbstractS3SDKV2Tests.java |
Adds end-to-end SDK coverage. |
S3.md |
Documents ListObjectVersions support. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
hi @smengcl, yeah I was actually working on something very similar, but you got the PR up before me. FYI @Gargi-jais11 |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
hi @smengcl - fyi the change is bigger than this because the actions for STS differ between ListObjectVersions and ListObjects. Maybe I can handle the STS portions after this PR goes in? |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sounds good! Thx |
|
|
||
| StringWriter writer = new StringWriter(); | ||
| JAXB.marshal(result, writer); | ||
| assertThat(writer.toString()) |
There was a problem hiding this comment.
curious - for such a small set of files, would it be worth it to have an assertion on the actual expected output instead of a few contains and doesNotContain checks?
…ull version-id-marker Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gargi-jais11
left a comment
There was a problem hiding this comment.
Thanks @smengcl for the patch. Please find the inlined comments.
| int listType = queryParams().getInt(QueryParams.LIST_TYPE, 1); | ||
| boolean fetchOwner = queryParams().getBoolean(QueryParams.FETCH_OWNER, false); | ||
| return listType != 2 || fetchOwner; | ||
| return isListObjectVersions() || listType != 2 || fetchOwner; |
There was a problem hiding this comment.
here we always includes Owner on ListObjectVersions whenever isListObjectVersions() is true, even when fetch-owner=false. On AWS, owner is omitted by default and only included when fetch-owner=true (ListObjectVersions).
use the same rule as ListObjectsV2 here (fetchOwner only)
| static boolean isUnsupportedVersionId(@Nullable String versionId) { | ||
| return versionId != null && !NULL_VERSION_ID.equals(versionId); | ||
| } |
There was a problem hiding this comment.
versionId="" is treated as unsupported not the same as absent or "null", so DeleteObject / tagging etc. with an empty versionId returns NotImplemented.
we should be treating blank the same as absent and only reject non-null values.
| * @param listing result of listing the bucket with {@code key-marker} as the marker | ||
| * @param versionIdMarker {@code version-id-marker} of the request | ||
| */ | ||
| static ListVersionsResult of(ListObjectResponse listing, String versionIdMarker) { |
There was a problem hiding this comment.
AWS ListObjectVersions responses include KeyCount, but ListVersionsResult never maps it, so the XML omits KeyCount.

Generated-by: Claude Code (Fable 5.1, Opus 5.5)
What changes were proposed in this pull request?
Since HDDS-16591,
GET /bucket?versions(ListObjectVersions) returns 501 NotImplemented. Before that, the request fell through to ListObjects and clients parsed the response as an empty version list. Bucket cleanup tools commonly call ListObjectVersions and then delete by the returned version IDs. The ceph/s3-tests cleanup fixture does so before and after each test, so the nightly S3 compatibility report dropped from 37.0% to 0.4% for s3-tests.Ozone does not support bucket versioning yet, so every bucket behaves like an AWS S3 bucket that never had versioning enabled. This PR makes S3 Gateway respond the way AWS S3 does for such a bucket:
VersionwithVersionIdnullandIsLatesttrue, with noDeleteMarkerentries. It reuses the existing key listing inBucketEndpointand paginates withkey-marker,NextKeyMarkerandNextVersionIdMarker.PUTandDELETEon?versionsare still rejected.nullis accepted as the version ID of the current object in DeleteObjects, DeleteObject, PutObjectTagging and DeleteObjectTagging. Any other version ID is still rejected with NotImplemented.VersionIdinDeletedandErrorentries when the request specified it.Not changed: the request is still audited and authorized as a bucket listing (
GET_BUCKET), GetBucketVersioning still returns NotImplemented, and GetObject and HeadObject still ignoreversionId.This is the unversioned subset of the ListObjectVersions work planned in HDDS-15728, which can extend it with the merged listing for versioned buckets.
What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-16739
How was this patch tested?
TestBucketList,TestObjectMultiDelete,TestObjectNotImplemented,TestObjectTaggingPutandTestObjectTaggingDelete.TestBucketNotImplementednow expects onlyPUTandDELETEon?versionsto be rejected.testDeleteObjectsListedByListObjectVersions, which paginates ListObjectVersions, deletes the returned versions with DeleteObjects and checks that the bucket is empty.TestS3SDKlocally.🤖 Generated with Claude Code