Skip to content

Commit f8c9152

Browse files
authored
HDDS-14361. Implement bucket CRUD as BucketOperationHandler (#9679)
1 parent 2c35e45 commit f8c9152

File tree

3 files changed

+145
-59
lines changed

3 files changed

+145
-59
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.s3.endpoint;
19+
20+
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError;
21+
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import javax.ws.rs.core.Response;
25+
import org.apache.hadoop.ozone.audit.S3GAction;
26+
import org.apache.hadoop.ozone.client.OzoneBucket;
27+
import org.apache.hadoop.ozone.om.exceptions.OMException;
28+
import org.apache.hadoop.ozone.s3.exception.OS3Exception;
29+
import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
30+
import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams;
31+
import org.apache.hadoop.util.Time;
32+
import org.apache.http.HttpStatus;
33+
34+
/**
35+
* Handler for default bucket CRUD operations.
36+
* Implements PUT (create bucket) and DELETE operations when no
37+
* subresource query parameters are present.
38+
*
39+
* This handler processes bucket-level requests that do not target
40+
* specific subresources (such as {@code ?acl}, {@code ?uploads},
41+
* or {@code ?delete}), which are handled by dedicated handlers.
42+
*
43+
* This handler extends EndpointBase to inherit all required functionality
44+
* (configuration, headers, request context, audit logging, metrics, etc.).
45+
*/
46+
public class BucketCrudHandler extends EndpointBase implements BucketOperationHandler {
47+
48+
/**
49+
* Handle only plain PUT bucket (create bucket), not subresources.
50+
*/
51+
private boolean shouldHandle() {
52+
return queryParams().get(QueryParams.ACL) == null
53+
&& queryParams().get(QueryParams.UPLOADS) == null
54+
&& queryParams().get(QueryParams.DELETE) == null;
55+
}
56+
57+
/**
58+
* Handle PUT /{bucket} for bucket creation.
59+
*/
60+
@Override
61+
public Response handlePutRequest(String bucketName, InputStream body)
62+
throws IOException, OS3Exception {
63+
64+
if (!shouldHandle()) {
65+
return null;
66+
}
67+
68+
long startNanos = Time.monotonicNowNanos();
69+
S3GAction s3GAction = S3GAction.CREATE_BUCKET;
70+
71+
try {
72+
String location = createS3Bucket(bucketName);
73+
auditWriteSuccess(s3GAction);
74+
getMetrics().updateCreateBucketSuccessStats(startNanos);
75+
return Response.status(HttpStatus.SC_OK).header("Location", location)
76+
.build();
77+
} catch (OMException exception) {
78+
auditWriteFailure(s3GAction, exception);
79+
getMetrics().updateCreateBucketFailureStats(startNanos);
80+
if (exception.getResult() == OMException.ResultCodes.INVALID_BUCKET_NAME) {
81+
throw newError(S3ErrorTable.INVALID_BUCKET_NAME, bucketName, exception);
82+
}
83+
throw exception;
84+
} catch (Exception ex) {
85+
auditWriteFailure(s3GAction, ex);
86+
throw ex;
87+
}
88+
}
89+
90+
/**
91+
* Handle DELETE /{bucket} for bucket deletion.
92+
*/
93+
@Override
94+
public Response handleDeleteRequest(String bucketName)
95+
throws IOException, OS3Exception {
96+
97+
if (!shouldHandle()) {
98+
return null;
99+
}
100+
101+
long startNanos = Time.monotonicNowNanos();
102+
S3GAction s3GAction = S3GAction.DELETE_BUCKET;
103+
104+
try {
105+
if (S3Owner.hasBucketOwnershipVerificationConditions(getHeaders())) {
106+
OzoneBucket bucket = getBucket(bucketName);
107+
S3Owner.verifyBucketOwnerCondition(getHeaders(), bucketName, bucket.getOwner());
108+
}
109+
deleteS3Bucket(bucketName);
110+
} catch (OMException ex) {
111+
auditWriteFailure(s3GAction, ex);
112+
getMetrics().updateDeleteBucketFailureStats(startNanos);
113+
if (ex.getResult() == OMException.ResultCodes.BUCKET_NOT_EMPTY) {
114+
throw newError(S3ErrorTable.BUCKET_NOT_EMPTY, bucketName, ex);
115+
} else if (ex.getResult() == OMException.ResultCodes.BUCKET_NOT_FOUND) {
116+
throw newError(S3ErrorTable.NO_SUCH_BUCKET, bucketName, ex);
117+
} else if (isAccessDenied(ex)) {
118+
throw newError(S3ErrorTable.ACCESS_DENIED, bucketName, ex);
119+
} else {
120+
throw ex;
121+
}
122+
} catch (Exception ex) {
123+
auditWriteFailure(s3GAction, ex);
124+
throw ex;
125+
}
126+
127+
auditWriteSuccess(s3GAction);
128+
getMetrics().updateDeleteBucketSuccessStats(startNanos);
129+
return Response
130+
.status(HttpStatus.SC_NO_CONTENT)
131+
.build();
132+
}
133+
}

hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java

Lines changed: 7 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams;
6868
import org.apache.hadoop.ozone.s3.util.S3StorageType;
6969
import org.apache.hadoop.util.Time;
70-
import org.apache.http.HttpStatus;
7170
import org.slf4j.Logger;
7271
import org.slf4j.LoggerFactory;
7372

@@ -308,35 +307,7 @@ public Response put(
308307
}
309308
}
310309

311-
// No handler handled the request, execute default operation: create bucket
312-
return handleCreateBucket(bucketName);
313-
}
314-
315-
/**
316-
* Default PUT bucket operation (create bucket).
317-
*/
318-
private Response handleCreateBucket(String bucketName)
319-
throws IOException, OS3Exception {
320-
long startNanos = Time.monotonicNowNanos();
321-
S3GAction s3GAction = S3GAction.CREATE_BUCKET;
322-
323-
try {
324-
String location = createS3Bucket(bucketName);
325-
auditWriteSuccess(s3GAction);
326-
getMetrics().updateCreateBucketSuccessStats(startNanos);
327-
return Response.status(HttpStatus.SC_OK).header("Location", location)
328-
.build();
329-
} catch (OMException exception) {
330-
auditWriteFailure(s3GAction, exception);
331-
getMetrics().updateCreateBucketFailureStats(startNanos);
332-
if (exception.getResult() == ResultCodes.INVALID_BUCKET_NAME) {
333-
throw newError(S3ErrorTable.INVALID_BUCKET_NAME, bucketName, exception);
334-
}
335-
throw exception;
336-
} catch (Exception ex) {
337-
auditWriteFailure(s3GAction, ex);
338-
throw ex;
339-
}
310+
throw newError(S3ErrorTable.NOT_IMPLEMENTED, "PUT bucket");
340311
}
341312

342313
public Response listMultipartUploads(
@@ -429,38 +400,14 @@ public Response head(@PathParam(BUCKET) String bucketName)
429400
@DELETE
430401
public Response delete(@PathParam(BUCKET) String bucketName)
431402
throws IOException, OS3Exception {
432-
long startNanos = Time.monotonicNowNanos();
433-
S3GAction s3GAction = S3GAction.DELETE_BUCKET;
434-
435-
try {
436-
if (S3Owner.hasBucketOwnershipVerificationConditions(getHeaders())) {
437-
OzoneBucket bucket = getBucket(bucketName);
438-
S3Owner.verifyBucketOwnerCondition(getHeaders(), bucketName, bucket.getOwner());
439-
}
440-
deleteS3Bucket(bucketName);
441-
} catch (OMException ex) {
442-
auditWriteFailure(s3GAction, ex);
443-
getMetrics().updateDeleteBucketFailureStats(startNanos);
444-
if (ex.getResult() == ResultCodes.BUCKET_NOT_EMPTY) {
445-
throw newError(S3ErrorTable.BUCKET_NOT_EMPTY, bucketName, ex);
446-
} else if (ex.getResult() == ResultCodes.BUCKET_NOT_FOUND) {
447-
throw newError(S3ErrorTable.NO_SUCH_BUCKET, bucketName, ex);
448-
} else if (isAccessDenied(ex)) {
449-
throw newError(S3ErrorTable.ACCESS_DENIED, bucketName, ex);
450-
} else {
451-
throw ex;
403+
for (BucketOperationHandler handler : handlers) {
404+
Response response = handler.handleDeleteRequest(bucketName);
405+
if (response != null) {
406+
return response;
452407
}
453-
} catch (Exception ex) {
454-
auditWriteFailure(s3GAction, ex);
455-
throw ex;
456408
}
457409

458-
auditWriteSuccess(s3GAction);
459-
getMetrics().updateDeleteBucketSuccessStats(startNanos);
460-
return Response
461-
.status(HttpStatus.SC_NO_CONTENT)
462-
.build();
463-
410+
throw newError(S3ErrorTable.NOT_IMPLEMENTED, "DELETE bucket");
464411
}
465412

466413
/**
@@ -557,6 +504,7 @@ protected void init() {
557504
// initialize handlers
558505
handlers = new ArrayList<>();
559506
addHandler(new BucketAclHandler());
507+
addHandler(new BucketCrudHandler());
560508
}
561509

562510
private <T extends EndpointBase & BucketOperationHandler> void addHandler(T handler) {

hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketOperationHandler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,9 @@ default Response handleGetRequest(String bucketName)
6262
throws IOException, OS3Exception {
6363
return null;
6464
}
65+
66+
default Response handleDeleteRequest(String bucketName)
67+
throws IOException, OS3Exception {
68+
return null;
69+
}
6570
}

0 commit comments

Comments
 (0)