Skip to content

Conversation

@echonesis
Copy link
Contributor

@echonesis echonesis commented Dec 17, 2025

What changes were proposed in this pull request?

Refactored the BucketEndpoint#put method by extracting ACL handling logic into dedicated handler classes, improving code modularity and maintainability through the Strategy pattern.

Changes

  • Created AclHandler class to handle bucket ACL PUT operations
  • Added BucketOperationHandler interface for pluggable operation handlers
  • Refactored BucketEndpoint#put() to delegate ACL operations to AclHandler

Testing

  • Added TestAclHandler
  • Updated existing TestBucketAcl tests

What is the link to the Apache JIRA

https://issues.apache.org/jira/browse/HDDS-14123

How was this patch tested?

GitHub Actions CI: https://github.com/echonesis/ozone/actions/runs/20752426565

Copy link
Contributor

@adoroszlai adoroszlai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @echonesis for working on this. I would like to simplify the implementation, but first need to do some refactoring in S3G to make it easier. (#9519 done, #9522 PR ready, HDDS-14204 in progress, HDDS-14198, maybe more)

Comment on lines 28 to 36
* Context object that provides access to BucketEndpoint resources.
* This allows handlers to access endpoint functionality without
* tight coupling to the BucketEndpoint class.
*
* Since BucketEndpoint extends EndpointBase, handlers can access:
* - Bucket and Volume operations
* - Methods inherited from EndpointBase
*/
public class BucketEndpointContext {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should introduce BucketEndpointContext. The context duplicates "random" functionality of BucketEndpoint. It would likely need to grow when extracting other PUT operations (tagging, etc.), GET operations. We will also want to extract handlers from ObjectEndpoint.

Handlers can be considered specialised "mini" endpoints. Thus they should simply extend EndpointBase to inherit all required functionality (configuration, headers, request context, audit logging, etc.).

Comment on lines 334 to 348
Map<String, String> queryParams = new HashMap<>();
queryParams.put("acl", aclMarker);
// Future handlers: queryParams.put("lifecycle", lifecycleMarker);

// Check for subresource operations using handlers
String queryParam = HANDLER_FACTORY.findFirstSupportedQueryParam(queryParams);

if (queryParam != null) {
BucketOperationHandler handler = HANDLER_FACTORY.getHandler(queryParam);
// Delegate to specific handler
s3GAction = getActionForQueryParam(queryParam);
Response response = handler.handlePutRequest(
bucketName, body, headers, getContext(), startNanos);
AUDIT.logWriteSuccess(
buildAuditMessageForSuccess(s3GAction, getAuditParameters()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too complex.

  1. Query params are already available in a map via ContainerRequestContext.
  2. Handlers should take care of audit logging and metrics. The logic to decide value of s3GAction should not be in centralized in BucketEndpoint.

It is better to let handlers inspect query params (and possibly other inputs, like headers) to decide if they should handle the request or not.

Something like (in AclHandler):

  private boolean shouldHandle() {
    return hasQueryParam(ACL);
  }

  public Response handlePutRequest(String bucketName, InputStream body) ... {
    if (!shouldHandle()) {
      return null;
    }
    ... handle bucket ACL put

Then we can simply try all handlers until one of them does the work:

for (BucketOperationHandler handler : handlers) {
  Response response = handler.handlePutRequest(bucketName, body);
  if (response != null) {
    return response;
  }
}

... continue handling in `BucketEndpoint`

@adoroszlai adoroszlai added the s3 S3 Gateway label Dec 18, 2025
@echonesis
Copy link
Contributor Author

Thanks @adoroszlai for the review.
I will keep up with those PRs and make this simpler.

@echonesis echonesis marked this pull request as draft December 18, 2025 15:39
echonesis and others added 23 commits December 24, 2025 11:22
@echonesis echonesis requested a review from adoroszlai December 24, 2025 17:08
@echonesis echonesis marked this pull request as ready for review December 25, 2025 14:48
Copy link
Contributor

@adoroszlai adoroszlai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @echonesis for updating the patch (and sorry for the late review after holidays).

* This handler extends EndpointBase to inherit all required functionality
* (configuration, headers, request context, audit logging, metrics, etc.).
*/
public class AclHandler extends EndpointBase implements BucketOperationHandler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename to BucketAclHandler. Object ACL is not implemented yet, but is planned (HDDS-9631).

Comment on lines 191 to 192
private List<OzoneAcl> getAndConvertAclOnBucket(String value,
String permission)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Please do not format method signature like this. Whenever visibility / return type / method name / other modifiers are changed, we would have to reindent all parameters.

I know it's the same as it was in BucketEndpoint, but we can improve formatting when moving code to a new file.

(Applies to few other similar methods.)

private boolean listKeysShallowEnabled;
private int maxKeysLimit = 1000;

private List<BucketOperationHandler> putHandlers;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BucketOperationHandler will eventually handle other operations (get, etc.), so this should be simply handlers.

Comment on lines 46 to 47
Response handlePutRequest(String bucketName, InputStream body)
throws IOException, OS3Exception;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some handlers may not implement PUT, so let's make this a default method that return null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing it out!
I will update it in the next commit.

Comment on lines 246 to 251
ozoneAclList.add(OzoneAcl.of(
IAccessAuthorizer.ACLIdentityType.USER,
userId,
OzoneAcl.AclScope.DEFAULT,
aclsOnBucket
));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: please import static for USER and DEFAULT, then this can be written in a single line.

(Also two other similar statements below.)

Comment on lines 296 to 300
// Create a spy of AclHandler to verify audit logging
AclHandler spyHandler = spy(EndpointBuilder.newAclHandlerBuilder()
.setClient(client)
.setHeaders(headers)
.build());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can spy(aclHandler), no need to create new instance.

Comment on lines 82 to 83
// Set up query parameter to indicate ACL operation
aclHandler.queryParamsForTest().set("acl", "");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be added in setup() to reduce duplication, then unset only in testHandlePutRequestWithoutAclQueryParam.

Comment on lines 111 to 114
Response response = aclHandler.handlePutRequest(BUCKET_NAME, null);

assertEquals(HTTP_OK, response.getStatus(),
"PUT ACL should return 200 OK");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can simplify a bit:

assertSucceeds(() -> aclHandler.handlePutRequest(BUCKET_NAME, null));

Comment on lines 120 to 121
when(headers.getHeaderString(S3Acl.GRANT_WRITE))
.thenReturn("id=\"testuser\"");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testHandlePutRequestWithReadHeader, testHandlePutRequestWithWriteHeader, testHandlePutRequestWithReadAcpHeader, testHandlePutRequestWithWriteAcpHeader, testHandlePutRequestWithFullControlHeader can be combined into a single @ParameterizedTest.

Comment on lines 185 to 187
OS3Exception exception = assertThrows(OS3Exception.class, () -> {
aclHandler.handlePutRequest(BUCKET_NAME, null);
}, "Should throw OS3Exception for unsupported grantee type");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: {} is unnecessary (in all assertThrows calls).

Comment on lines 199 to 204
OS3Exception exception = assertThrows(OS3Exception.class, () -> {
aclHandler.handlePutRequest(BUCKET_NAME, null);
}, "Should throw OS3Exception for email address grantee type");

assertEquals(HTTP_NOT_IMPLEMENTED, exception.getHttpCode(),
"Should return NOT_IMPLEMENTED for email address grantee type");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be simplified to:

assertErrorResponse(S3ErrorTable.NOT_IMPLEMENTED,
    () -> aclHandler.handlePutRequest(BUCKET_NAME, null));

@echonesis echonesis requested a review from adoroszlai January 6, 2026 16:19
Copy link
Contributor

@adoroszlai adoroszlai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @echonesis for updating the patch.

@adoroszlai adoroszlai merged commit 75aabae into apache:master Jan 6, 2026
44 checks passed
@echonesis
Copy link
Contributor Author

Thanks @adoroszlai for the review and merge.

@echonesis echonesis deleted the HDDS-14123 branch January 7, 2026 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

s3 S3 Gateway

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants