Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo;
import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
Expand Down Expand Up @@ -675,6 +676,16 @@ public void deleteKeys(List<String> keyList) throws IOException {
proxy.deleteKeys(volumeName, name, keyList);
}

/**
* Deletes the given list of keys from the bucket.
* @param keyList List of the key name to be deleted.
* @param quiet flag to not throw exception if delete fails
* @throws IOException
*/
public Map<String, ErrorInfo> deleteKeys(List<String> keyList, boolean quiet) throws IOException {
return proxy.deleteKeys(volumeName, name, keyList, quiet);
}

/**
* Rename the keyname from fromKeyName to toKeyName.
* @param fromKeyName The original key name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.DeleteTenantState;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo;
import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteInfo;
Expand Down Expand Up @@ -436,6 +437,18 @@ void deleteKeys(String volumeName, String bucketName,
List<String> keyNameList)
throws IOException;

/**
* Deletes keys through the list.
* @param volumeName Name of the Volume
* @param bucketName Name of the Bucket
* @param keyNameList List of the Key
* @param quiet flag to not throw exception if delete fails
* @throws IOException
*/
Map<String, ErrorInfo> deleteKeys(String volumeName, String bucketName,
List<String> keyNameList, boolean quiet)
throws IOException;

/**
* Renames an existing key within a bucket.
* @param volumeName Name of the Volume
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
import org.apache.hadoop.ozone.om.helpers.BucketEncryptionKeyInfo;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.DeleteTenantState;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.KeyInfoWithVolumeContext;
import org.apache.hadoop.ozone.om.helpers.OmBucketArgs;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
Expand Down Expand Up @@ -1642,6 +1643,18 @@ public void deleteKeys(
ozoneManagerClient.deleteKeys(omDeleteKeys);
}

@Override
public Map<String, ErrorInfo> deleteKeys(
String volumeName, String bucketName, List<String> keyNameList, boolean quiet)
throws IOException {
verifyVolumeName(volumeName);
verifyBucketName(bucketName);
Preconditions.checkNotNull(keyNameList);
OmDeleteKeys omDeleteKeys = new OmDeleteKeys(volumeName, bucketName,
keyNameList);
return ozoneManagerClient.deleteKeys(omDeleteKeys, quiet);
}

@Override
public void renameKey(String volumeName, String bucketName,
String fromKeyName, String toKeyName) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.hadoop.ozone.om.helpers;

/**
* Represent class which has info of error thrown for any operation.
*/
public class ErrorInfo {
private String code;
private String message;

public ErrorInfo(String errorCode, String errorMessage) {
this.code = errorCode;
this.message = errorMessage;
}

public String getCode() {
return code;
}

public void setCode(String errorCode) {
this.code = errorCode;
}

public String getMessage() {
return message;
}

public void setMessage(String errorMessage) {
this.message = errorMessage;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import jakarta.annotation.Nonnull;
Expand All @@ -32,6 +33,7 @@
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.DBUpdates;
import org.apache.hadoop.ozone.om.helpers.DeleteTenantState;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.KeyInfoWithVolumeContext;
import org.apache.hadoop.ozone.om.helpers.OmBucketArgs;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
Expand Down Expand Up @@ -359,6 +361,21 @@ default void deleteKeys(OmDeleteKeys deleteKeys) throws IOException {
"this to be implemented, as write requests use a new approach.");
}

/**
* Deletes existing key/keys. This interface supports delete
* multiple keys and a single key. Used by deleting files
* through OzoneFileSystem.
*
* @param deleteKeys
* @param quiet - flag to not throw exception if delete fails
* @throws IOException
*/
default Map<String, ErrorInfo> deleteKeys(OmDeleteKeys deleteKeys, boolean quiet)
throws IOException {
throw new UnsupportedOperationException("OzoneManager does not require " +
"this to be implemented, as write requests use a new approach.");
}


/**
* Deletes an existing empty bucket from volume.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
Expand All @@ -42,6 +43,7 @@
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.ListKeysLightResult;
import org.apache.hadoop.ozone.om.helpers.ListKeysResult;
import org.apache.hadoop.ozone.om.helpers.DBUpdates;
Expand Down Expand Up @@ -953,6 +955,12 @@ public void deleteKey(OmKeyArgs args) throws IOException {
*/
@Override
public void deleteKeys(OmDeleteKeys deleteKeys) throws IOException {
deleteKeys(deleteKeys, false);
}

@Override
public Map<String, ErrorInfo> deleteKeys(OmDeleteKeys deleteKeys, boolean quiet)
throws IOException {
DeleteKeysRequest.Builder req = DeleteKeysRequest.newBuilder();
DeleteKeyArgs deletedKeys = DeleteKeyArgs.newBuilder()
.setBucketName(deleteKeys.getBucket())
Expand All @@ -962,9 +970,20 @@ public void deleteKeys(OmDeleteKeys deleteKeys) throws IOException {
OMRequest omRequest = createOMRequest(Type.DeleteKeys)
.setDeleteKeysRequest(req)
.build();
OMResponse omResponse = submitRequest(omRequest);

handleError(submitRequest(omRequest));

Map<String, ErrorInfo> keyToErrors = new HashMap<>();
if (quiet) {
List<OzoneManagerProtocolProtos.DeleteKeyError> errors =
omResponse.getDeleteKeysResponse().getErrorsList();
for (OzoneManagerProtocolProtos.DeleteKeyError deleteKeyError : errors) {
keyToErrors.put(deleteKeyError.getKey(),
new ErrorInfo(deleteKeyError.getErrorCode(), deleteKeyError.getErrorMsg()));
}
} else {
handleError(omResponse);
}
return keyToErrors;
}

/**
Expand Down
3 changes: 1 addition & 2 deletions hadoop-ozone/dist/src/main/compose/common/s3a-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,9 @@ EOF
# Some tests are skipped due to known issues.
# - ITestS3AContractDistCp: HDDS-10616
# - ITestS3AContractGetFileStatusV1List: HDDS-10617
# - ITestS3AContractMkdir: HDDS-10572
# - ITestS3AContractRename: HDDS-10665
mvn -B -V --fail-never --no-transfer-progress \
-Dtest='ITestS3AContract*, ITestS3ACommitterMRJob, !ITestS3AContractDistCp, !ITestS3AContractGetFileStatusV1List, !ITestS3AContractMkdir, !ITestS3AContractRename' \
-Dtest='ITestS3AContract*, ITestS3ACommitterMRJob, !ITestS3AContractDistCp, !ITestS3AContractGetFileStatusV1List, !ITestS3AContractRename' \
clean test

local target="${RESULT_DIR}/junit/${bucket}/target"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1301,9 +1301,16 @@ message DeleteKeyArgs {
repeated string keys = 3;
}

message DeleteKeyError {
optional string key = 1;
optional string errorCode = 2;
optional string errorMsg = 3;
}

message DeleteKeysResponse {
optional DeleteKeyArgs unDeletedKeys = 1;
optional bool status = 2;
repeated DeleteKeyError errors = 3;
}

message DeleteKeyResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.hadoop.ozone.om.ResolvedBucket;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
Expand All @@ -57,6 +58,7 @@
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -95,6 +97,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
Exception exception = null;
OMClientResponse omClientResponse = null;
Result result = null;
Map<String, ErrorInfo> keyToError = new HashMap<>();

OMMetrics omMetrics = ozoneManager.getMetrics();
omMetrics.incNumKeyDeletes();
Expand Down Expand Up @@ -150,6 +153,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
objectKey);
deleteKeys.remove(keyName);
unDeletedKeys.addKeys(keyName);
keyToError.put(keyName, new ErrorInfo(OMException.ResultCodes.KEY_NOT_FOUND.name(), "Key does not exist"));
continue;
}

Expand All @@ -167,6 +171,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
LOG.error("Acl check failed for Key: {}", objectKey, ex);
deleteKeys.remove(keyName);
unDeletedKeys.addKeys(keyName);
keyToError.put(keyName, new ErrorInfo(OMException.ResultCodes.ACCESS_DENIED.name(), "ACL check failed"));
}
}

Expand All @@ -185,7 +190,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
final long volumeId = omMetadataManager.getVolumeId(volumeName);
omClientResponse =
getOmClientResponse(ozoneManager, omKeyInfoList, dirList, omResponse,
unDeletedKeys, deleteStatus, omBucketInfo, volumeId, dbOpenKeys);
unDeletedKeys, keyToError, deleteStatus, omBucketInfo, volumeId, dbOpenKeys);

result = Result.SUCCESS;

Expand All @@ -199,6 +204,8 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
// Add all keys which are failed due to any other exception .
for (int i = indexFailed; i < length; i++) {
unDeletedKeys.addKeys(deleteKeyArgs.getKeys(i));
keyToError.put(deleteKeyArgs.getKeys(i), new ErrorInfo(OMException.ResultCodes.INTERNAL_ERROR.name(),
ex.getMessage()));
}

omResponse.setDeleteKeysResponse(
Expand Down Expand Up @@ -260,12 +267,18 @@ protected OMClientResponse getOmClientResponse(OzoneManager ozoneManager,
List<OmKeyInfo> omKeyInfoList, List<OmKeyInfo> dirList,
OMResponse.Builder omResponse,
OzoneManagerProtocolProtos.DeleteKeyArgs.Builder unDeletedKeys,
Map<String, ErrorInfo> keyToErrors,
boolean deleteStatus, OmBucketInfo omBucketInfo, long volumeId, List<String> dbOpenKeys) {
OMClientResponse omClientResponse;
List<OzoneManagerProtocolProtos.DeleteKeyError> deleteKeyErrors = new ArrayList<>();
for (Map.Entry<String, ErrorInfo> key : keyToErrors.entrySet()) {
deleteKeyErrors.add(OzoneManagerProtocolProtos.DeleteKeyError.newBuilder().setKey(key.getKey())
.setErrorCode(key.getValue().getCode()).setErrorMsg(key.getValue().getMessage()).build());
}
omClientResponse = new OMKeysDeleteResponse(omResponse
.setDeleteKeysResponse(
DeleteKeysResponse.newBuilder().setStatus(deleteStatus)
.setUnDeletedKeys(unDeletedKeys))
.setUnDeletedKeys(unDeletedKeys).addAllErrors(deleteKeyErrors))
.setStatus(deleteStatus ? OK : PARTIAL_DELETE).setSuccess(deleteStatus)
.build(), omKeyInfoList, ozoneManager.isRatisEnabled(),
omBucketInfo.copyObject(), dbOpenKeys);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
Expand All @@ -36,7 +37,9 @@
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.OK;
import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.PARTIAL_DELETE;
Expand Down Expand Up @@ -146,12 +149,19 @@ protected OMClientResponse getOmClientResponse(OzoneManager ozoneManager,
List<OmKeyInfo> omKeyInfoList, List<OmKeyInfo> dirList,
OzoneManagerProtocolProtos.OMResponse.Builder omResponse,
OzoneManagerProtocolProtos.DeleteKeyArgs.Builder unDeletedKeys,
Map<String, ErrorInfo> keyToErrors,
boolean deleteStatus, OmBucketInfo omBucketInfo, long volumeId, List<String> dbOpenKeys) {
OMClientResponse omClientResponse;
List<OzoneManagerProtocolProtos.DeleteKeyError> deleteKeyErrors = new ArrayList<>();
for (Map.Entry<String, ErrorInfo> key : keyToErrors.entrySet()) {
deleteKeyErrors.add(OzoneManagerProtocolProtos.DeleteKeyError.newBuilder()
.setKey(key.getKey()).setErrorCode(key.getValue().getCode())
.setErrorMsg(key.getValue().getMessage()).build());
}
omClientResponse = new OMKeysDeleteResponseWithFSO(omResponse
.setDeleteKeysResponse(
OzoneManagerProtocolProtos.DeleteKeysResponse.newBuilder()
.setStatus(deleteStatus).setUnDeletedKeys(unDeletedKeys))
.setStatus(deleteStatus).setUnDeletedKeys(unDeletedKeys).addAllErrors(deleteKeyErrors))
.setStatus(deleteStatus ? OK : PARTIAL_DELETE).setSuccess(deleteStatus)
.build(), omKeyInfoList, dirList, ozoneManager.isRatisEnabled(),
omBucketInfo.copyObject(), volumeId, dbOpenKeys);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyArgs;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyError;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeysRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -73,6 +74,9 @@ protected void checkDeleteKeysResponse(
omClientResponse.getOMResponse().getDeleteKeysResponse()
.getUnDeletedKeys();
assertEquals(0, unDeletedKeys.getKeysCount());
List<DeleteKeyError> keyErrors = omClientResponse.getOMResponse().getDeleteKeysResponse()
.getErrorsList();
assertEquals(0, keyErrors.size());

// Check all keys are deleted.
for (String deleteKey : deleteKeyList) {
Expand Down Expand Up @@ -123,6 +127,9 @@ protected void checkDeleteKeysResponseForFailure(
.getDeleteKeysResponse().getUnDeletedKeys();
assertEquals(1,
unDeletedKeys.getKeysCount());
List<DeleteKeyError> keyErrors = omClientResponse.getOMResponse().getDeleteKeysResponse()
.getErrorsList();
assertEquals(1, keyErrors.size());
assertEquals("dummy", unDeletedKeys.getKeys(0));
}

Expand Down
Loading