Skip to content

Commit f5b2f75

Browse files
HDDS-1795. Implement S3 Delete Bucket request to use Cache and DoubleBuffer. (#1097)
1 parent 69a46a9 commit f5b2f75

12 files changed

Lines changed: 569 additions & 39 deletions

File tree

hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public enum OMAction implements AuditAction {
4141

4242
// S3 Bucket
4343
CREATE_S3_BUCKET,
44+
DELETE_S3_BUCKET,
4445

4546
// READ Actions
4647
CHECK_VOLUME_ACCESS,

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMetrics.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ public class OMMetrics {
129129

130130
private @Metric MutableCounterLong numS3BucketCreates;
131131
private @Metric MutableCounterLong numS3BucketCreateFails;
132+
private @Metric MutableCounterLong numS3BucketDeletes;
133+
private @Metric MutableCounterLong numS3BucketDeleteFails;
132134

133135

134136
public OMMetrics() {
@@ -150,6 +152,17 @@ public void incNumS3BucketCreateFails() {
150152
numS3BucketCreateFails.incr();
151153
}
152154

155+
public void incNumS3BucketDeletes() {
156+
numBucketOps.incr();
157+
numS3BucketDeletes.incr();
158+
}
159+
160+
public void incNumS3BucketDeleteFails() {
161+
numBucketOps.incr();
162+
numS3BucketDeleteFails.incr();
163+
}
164+
165+
153166
public void incNumS3Buckets() {
154167
numS3Buckets.incr();
155168
}

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.hadoop.ozone.om.request.key.OMKeyPurgeRequest;
3333
import org.apache.hadoop.ozone.om.request.key.OMKeyRenameRequest;
3434
import org.apache.hadoop.ozone.om.request.s3.bucket.S3BucketCreateRequest;
35+
import org.apache.hadoop.ozone.om.request.s3.bucket.S3BucketDeleteRequest;
3536
import org.apache.hadoop.ozone.om.request.volume.OMVolumeCreateRequest;
3637
import org.apache.hadoop.ozone.om.request.volume.OMVolumeDeleteRequest;
3738
import org.apache.hadoop.ozone.om.request.volume.OMVolumeSetOwnerRequest;
@@ -102,6 +103,8 @@ public static OMClientRequest createClientRequest(OMRequest omRequest) {
102103
return new OMKeyPurgeRequest(omRequest);
103104
case CreateS3Bucket:
104105
return new S3BucketCreateRequest(omRequest);
106+
case DeleteS3Bucket:
107+
return new S3BucketDeleteRequest(omRequest);
105108
default:
106109
// TODO: will update once all request types are implemented.
107110
return null;
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.ozone.om.request.s3.bucket;
20+
21+
import java.io.IOException;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
25+
import com.google.common.base.Optional;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
import org.apache.hadoop.ozone.OzoneConsts;
30+
import org.apache.hadoop.ozone.audit.OMAction;
31+
import org.apache.hadoop.ozone.om.OMMetadataManager;
32+
import org.apache.hadoop.ozone.om.OMMetrics;
33+
import org.apache.hadoop.ozone.om.OzoneManager;
34+
import org.apache.hadoop.ozone.om.exceptions.OMException;
35+
import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
36+
import org.apache.hadoop.ozone.om.response.OMClientResponse;
37+
import org.apache.hadoop.ozone.om.response.s3.bucket.S3BucketDeleteResponse;
38+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
39+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
40+
.OMRequest;
41+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
42+
.OMResponse;
43+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
44+
.S3DeleteBucketRequest;
45+
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
46+
import org.apache.hadoop.ozone.security.acl.OzoneObj;
47+
import org.apache.hadoop.utils.db.cache.CacheKey;
48+
import org.apache.hadoop.utils.db.cache.CacheValue;
49+
50+
import static org.apache.hadoop.ozone.OzoneConsts.S3_BUCKET_MAX_LENGTH;
51+
import static org.apache.hadoop.ozone.OzoneConsts.S3_BUCKET_MIN_LENGTH;
52+
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.BUCKET_LOCK;
53+
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.S3_BUCKET_LOCK;
54+
55+
/**
56+
* Handle Create S3Bucket request.
57+
*/
58+
public class S3BucketDeleteRequest extends OMVolumeRequest {
59+
60+
private static final Logger LOG =
61+
LoggerFactory.getLogger(S3BucketDeleteRequest.class);
62+
63+
public S3BucketDeleteRequest(OMRequest omRequest) {
64+
super(omRequest);
65+
}
66+
67+
public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
68+
S3DeleteBucketRequest s3DeleteBucketRequest =
69+
getOmRequest().getDeleteS3BucketRequest();
70+
71+
// TODO: Do we need to enforce the bucket rules in this code path?
72+
// https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
73+
74+
// For now only checked the length.
75+
int bucketLength = s3DeleteBucketRequest.getS3BucketName().length();
76+
if (bucketLength < S3_BUCKET_MIN_LENGTH ||
77+
bucketLength >= S3_BUCKET_MAX_LENGTH) {
78+
throw new OMException("S3BucketName must be at least 3 and not more " +
79+
"than 63 characters long",
80+
OMException.ResultCodes.S3_BUCKET_INVALID_LENGTH);
81+
}
82+
83+
return getOmRequest().toBuilder().setUserInfo(getUserInfo()).build();
84+
85+
}
86+
87+
@Override
88+
public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,
89+
long transactionLogIndex) {
90+
S3DeleteBucketRequest s3DeleteBucketRequest =
91+
getOmRequest().getDeleteS3BucketRequest();
92+
93+
String s3BucketName = s3DeleteBucketRequest.getS3BucketName();
94+
95+
OMResponse.Builder omResponse = OMResponse.newBuilder().setCmdType(
96+
OzoneManagerProtocolProtos.Type.DeleteS3Bucket).setStatus(
97+
OzoneManagerProtocolProtos.Status.OK).setSuccess(true);
98+
99+
OMMetrics omMetrics = ozoneManager.getMetrics();
100+
omMetrics.incNumS3BucketDeletes();
101+
IOException exception = null;
102+
boolean acquiredS3Lock = false;
103+
boolean acquiredBucketLock = false;
104+
String volumeName = null;
105+
OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
106+
try {
107+
// check Acl
108+
if (ozoneManager.getAclsEnabled()) {
109+
checkAcls(ozoneManager, OzoneObj.ResourceType.BUCKET,
110+
OzoneObj.StoreType.S3, IAccessAuthorizer.ACLType.DELETE, null,
111+
s3BucketName, null);
112+
}
113+
114+
acquiredS3Lock = omMetadataManager.getLock().acquireLock(S3_BUCKET_LOCK,
115+
s3BucketName);
116+
117+
String s3Mapping = omMetadataManager.getS3Table().get(s3BucketName);
118+
119+
if (s3Mapping == null) {
120+
throw new OMException("S3Bucket " + s3BucketName + " not found",
121+
OMException.ResultCodes.S3_BUCKET_NOT_FOUND);
122+
} else {
123+
volumeName = getOzoneVolumeName(s3Mapping);
124+
125+
acquiredBucketLock =
126+
omMetadataManager.getLock().acquireLock(BUCKET_LOCK, volumeName,
127+
s3BucketName);
128+
129+
String bucketKey = omMetadataManager.getBucketKey(volumeName,
130+
s3BucketName);
131+
132+
// Update bucket table cache and s3 table cache.
133+
omMetadataManager.getBucketTable().addCacheEntry(
134+
new CacheKey<>(bucketKey),
135+
new CacheValue<>(Optional.absent(), transactionLogIndex));
136+
omMetadataManager.getS3Table().addCacheEntry(
137+
new CacheKey<>(s3BucketName),
138+
new CacheValue<>(Optional.absent(), transactionLogIndex));
139+
}
140+
} catch (IOException ex) {
141+
exception = ex;
142+
} finally {
143+
if (acquiredBucketLock) {
144+
omMetadataManager.getLock().releaseLock(BUCKET_LOCK, volumeName,
145+
s3BucketName);
146+
}
147+
if (acquiredS3Lock) {
148+
omMetadataManager.getLock().releaseLock(S3_BUCKET_LOCK, s3BucketName);
149+
}
150+
}
151+
152+
// Performing audit logging outside of the lock.
153+
auditLog(ozoneManager.getAuditLogger(),
154+
buildAuditMessage(OMAction.DELETE_S3_BUCKET,
155+
buildAuditMap(s3BucketName), exception,
156+
getOmRequest().getUserInfo()));
157+
158+
if (exception == null) {
159+
// Decrement s3 bucket and ozone bucket count. As S3 bucket is mapped to
160+
// ozonevolume/ozone bucket.
161+
LOG.debug("S3Bucket {} successfully deleted", s3BucketName);
162+
omMetrics.decNumS3Buckets();
163+
omMetrics.decNumBuckets();
164+
omResponse.setDeleteS3BucketResponse(
165+
OzoneManagerProtocolProtos.S3DeleteBucketResponse.newBuilder());
166+
return new S3BucketDeleteResponse(s3BucketName, volumeName,
167+
omResponse.build());
168+
} else {
169+
LOG.error("S3Bucket Deletion failed for S3Bucket:{}", s3BucketName,
170+
exception);
171+
omMetrics.incNumS3BucketDeleteFails();
172+
return new S3BucketDeleteResponse(null, null,
173+
createErrorOMResponse(omResponse, exception));
174+
}
175+
}
176+
177+
/**
178+
* Extract volumeName from s3Mapping.
179+
* @param s3Mapping
180+
* @return volumeName
181+
* @throws IOException
182+
*/
183+
private String getOzoneVolumeName(String s3Mapping) throws IOException {
184+
return s3Mapping.split("/")[0];
185+
}
186+
187+
private Map<String, String> buildAuditMap(String s3BucketName) {
188+
Map<String, String> auditMap = new HashMap<>();
189+
auditMap.put(s3BucketName, OzoneConsts.S3_BUCKET);
190+
return auditMap;
191+
}
192+
193+
}

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/bucket/S3BucketCreateResponse.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.io.IOException;
2323

2424
import com.google.common.base.Preconditions;
25+
import com.google.common.annotations.VisibleForTesting;
26+
2527
import org.apache.hadoop.ozone.om.OMMetadataManager;
2628
import org.apache.hadoop.ozone.om.response.OMClientResponse;
2729
import org.apache.hadoop.ozone.om.response.bucket.OMBucketCreateResponse;
@@ -69,5 +71,9 @@ public void addToDBBatch(OMMetadataManager omMetadataManager,
6971
s3Mapping);
7072
}
7173
}
72-
}
7374

75+
@VisibleForTesting
76+
public String getS3Mapping() {
77+
return s3Mapping;
78+
}
79+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with this
4+
* work for additional information regarding copyright ownership. The ASF
5+
* licenses this file to you under the Apache License, Version 2.0 (the
6+
* "License"); you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* <p>
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
* <p>
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations under
15+
* the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.om.response.s3.bucket;
19+
20+
import org.apache.hadoop.ozone.om.OMMetadataManager;
21+
import org.apache.hadoop.ozone.om.response.OMClientResponse;
22+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
23+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
24+
import org.apache.hadoop.utils.db.BatchOperation;
25+
26+
import javax.annotation.Nonnull;
27+
import javax.annotation.Nullable;
28+
import java.io.IOException;
29+
30+
/**
31+
* Response for S3Bucket Delete request.
32+
*/
33+
public class S3BucketDeleteResponse extends OMClientResponse {
34+
35+
private String s3BucketName;
36+
private String volumeName;
37+
public S3BucketDeleteResponse(@Nullable String s3BucketName,
38+
@Nullable String volumeName, @Nonnull OMResponse omResponse) {
39+
super(omResponse);
40+
this.s3BucketName = s3BucketName;
41+
this.volumeName = volumeName;
42+
}
43+
44+
@Override
45+
public void addToDBBatch(OMMetadataManager omMetadataManager,
46+
BatchOperation batchOperation) throws IOException {
47+
48+
if (getOMResponse().getStatus() == OzoneManagerProtocolProtos.Status.OK) {
49+
omMetadataManager.getBucketTable().deleteWithBatch(batchOperation,
50+
omMetadataManager.getBucketKey(volumeName, s3BucketName));
51+
omMetadataManager.getS3Table().deleteWithBatch(batchOperation,
52+
s3BucketName);
53+
}
54+
}
55+
}

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerHARequestHandlerImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public OMResponse handleApplyTransaction(OMRequest omRequest,
6868
case CreateFile:
6969
case PurgeKeys:
7070
case CreateS3Bucket:
71+
case DeleteS3Bucket:
7172
//TODO: We don't need to pass transactionID, this will be removed when
7273
// complete write requests is changed to new model. And also we can
7374
// return OMClientResponse, then adding to doubleBuffer can be taken

hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMRequestUtils.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,17 @@ public static OzoneManagerProtocolProtos.OMRequest createS3BucketRequest(
203203
.setClientId(UUID.randomUUID().toString()).build();
204204
}
205205

206+
public static OzoneManagerProtocolProtos.OMRequest deleteS3BucketRequest(
207+
String s3BucketName) {
208+
OzoneManagerProtocolProtos.S3DeleteBucketRequest request =
209+
OzoneManagerProtocolProtos.S3DeleteBucketRequest.newBuilder()
210+
.setS3BucketName(s3BucketName).build();
211+
return OzoneManagerProtocolProtos.OMRequest.newBuilder()
212+
.setDeleteS3BucketRequest(request)
213+
.setCmdType(OzoneManagerProtocolProtos.Type.DeleteS3Bucket)
214+
.setClientId(UUID.randomUUID().toString()).build();
215+
}
216+
206217
public static List< HddsProtos.KeyValue> getMetadataList() {
207218
List<HddsProtos.KeyValue> metadataList = new ArrayList<>();
208219
metadataList.add(HddsProtos.KeyValue.newBuilder().setKey("key1").setValue(

0 commit comments

Comments
 (0)