Skip to content

Commit 2c0c129

Browse files
HADOOP-19445: ABFS: [FnsOverBlob][Tests] Add Tests For Negative Scenarios Identified for Rename Operation (#7386) (#7525)
Contributed by Manish Bhatt Reviewed by Anmol Asrani, Anuj Modi Signed off by: Anuj Modi <[email protected]>
1 parent 7f2bb43 commit 2c0c129

File tree

7 files changed

+960
-76
lines changed

7 files changed

+960
-76
lines changed

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/services/AzureServiceErrorCode.java

-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ public enum AzureServiceErrorCode {
5656
OTHER_SERVER_THROTTLING("ServerBusy", HttpURLConnection.HTTP_UNAVAILABLE,
5757
"The server is currently unable to receive requests. Please retry your request."),
5858
INVALID_QUERY_PARAMETER_VALUE("InvalidQueryParameterValue", HttpURLConnection.HTTP_BAD_REQUEST, null),
59-
INVALID_RENAME_DESTINATION("InvalidRenameDestinationPath", HttpURLConnection.HTTP_BAD_REQUEST, null),
6059
AUTHORIZATION_PERMISSION_MISS_MATCH("AuthorizationPermissionMismatch", HttpURLConnection.HTTP_FORBIDDEN, null),
6160
ACCOUNT_REQUIRES_HTTPS("AccountRequiresHttps", HttpURLConnection.HTTP_BAD_REQUEST, null),
6261
MD5_MISMATCH("Md5Mismatch", HttpURLConnection.HTTP_BAD_REQUEST,

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsBlobClient.java

+31-26
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@
8383
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
8484
import static java.net.HttpURLConnection.HTTP_OK;
8585
import static java.net.HttpURLConnection.HTTP_PRECON_FAILED;
86-
import static org.apache.hadoop.fs.azurebfs.AzureBlobFileSystemStore.extractEtagHeader;
8786
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_GET_FILE_STATUS;
87+
import static org.apache.hadoop.fs.azurebfs.AzureBlobFileSystemStore.extractEtagHeader;
8888
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.ACQUIRE_LEASE_ACTION;
8989
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.AND_MARK;
9090
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.APPEND_BLOB_TYPE;
@@ -424,7 +424,7 @@ private void fixAtomicEntriesInListResults(final AbfsRestOperation op,
424424
}
425425
List<BlobListResultEntrySchema> filteredEntries = new ArrayList<>();
426426
for (BlobListResultEntrySchema entry : listResultSchema.paths()) {
427-
if (!takeListPathAtomicRenameKeyAction(entry.path(),
427+
if (!takeListPathAtomicRenameKeyAction(entry.path(), entry.isDirectory(),
428428
entry.contentLength().intValue(), tracingContext)) {
429429
filteredEntries.add(entry);
430430
}
@@ -442,17 +442,19 @@ public void createNonRecursivePreCheck(Path parentPath,
442442
if (isAtomicRenameKey(parentPath.toUri().getPath())) {
443443
takeGetPathStatusAtomicRenameKeyAction(parentPath, tracingContext);
444444
}
445-
getPathStatus(parentPath.toUri().getPath(), false,
446-
tracingContext, null);
445+
try {
446+
getPathStatus(parentPath.toUri().getPath(), false,
447+
tracingContext, null);
448+
} finally {
449+
getAbfsCounters().incrementCounter(CALL_GET_FILE_STATUS, 1);
450+
}
447451
} catch (AbfsRestOperationException ex) {
448452
if (ex.getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
449453
throw new FileNotFoundException("Cannot create file "
450454
+ parentPath.toUri().getPath()
451455
+ " because parent folder does not exist.");
452456
}
453457
throw ex;
454-
} finally {
455-
getAbfsCounters().incrementCounter(CALL_GET_FILE_STATUS, 1);
456458
}
457459
}
458460

@@ -807,23 +809,26 @@ public AbfsClientRenameResult renamePath(final String source,
807809
BlobRenameHandler blobRenameHandler = getBlobRenameHandler(source,
808810
destination, sourceEtag, isAtomicRenameKey(source), tracingContext
809811
);
810-
incrementAbfsRenamePath();
811-
if (blobRenameHandler.execute()) {
812-
final AbfsUriQueryBuilder abfsUriQueryBuilder
813-
= createDefaultUriQueryBuilder();
814-
final URL url = createRequestUrl(destination,
815-
abfsUriQueryBuilder.toString());
816-
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();
817-
final AbfsRestOperation successOp = getSuccessOp(
818-
AbfsRestOperationType.RenamePath, HTTP_METHOD_PUT,
819-
url, requestHeaders);
820-
return new AbfsClientRenameResult(successOp, true, false);
821-
} else {
822-
throw new AbfsRestOperationException(HTTP_INTERNAL_ERROR,
823-
AzureServiceErrorCode.UNKNOWN.getErrorCode(),
824-
ERR_RENAME_BLOB + source + SINGLE_WHITE_SPACE + AND_MARK
825-
+ SINGLE_WHITE_SPACE + destination,
826-
null);
812+
try {
813+
if (blobRenameHandler.execute()) {
814+
final AbfsUriQueryBuilder abfsUriQueryBuilder
815+
= createDefaultUriQueryBuilder();
816+
final URL url = createRequestUrl(destination,
817+
abfsUriQueryBuilder.toString());
818+
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();
819+
final AbfsRestOperation successOp = getSuccessOp(
820+
AbfsRestOperationType.RenamePath, HTTP_METHOD_PUT,
821+
url, requestHeaders);
822+
return new AbfsClientRenameResult(successOp, true, false);
823+
} else {
824+
throw new AbfsRestOperationException(HTTP_INTERNAL_ERROR,
825+
AzureServiceErrorCode.UNKNOWN.getErrorCode(),
826+
ERR_RENAME_BLOB + source + SINGLE_WHITE_SPACE + AND_MARK
827+
+ SINGLE_WHITE_SPACE + destination,
828+
null);
829+
}
830+
} finally {
831+
incrementAbfsRenamePath();
827832
}
828833
}
829834

@@ -1817,11 +1822,11 @@ public void takeGetPathStatusAtomicRenameKeyAction(final Path path,
18171822
* @throws AzureBlobFileSystemException server error
18181823
*/
18191824
private boolean takeListPathAtomicRenameKeyAction(final Path path,
1820-
final int renamePendingJsonLen,
1825+
final boolean isDirectory, final int renamePendingJsonLen,
18211826
final TracingContext tracingContext)
18221827
throws AzureBlobFileSystemException {
18231828
if (path == null || path.isRoot() || !isAtomicRenameKey(
1824-
path.toUri().getPath()) || !path.toUri()
1829+
path.toUri().getPath()) || isDirectory || !path.toUri()
18251830
.getPath()
18261831
.endsWith(RenameAtomicity.SUFFIX)) {
18271832
return false;
@@ -1849,7 +1854,7 @@ private boolean takeListPathAtomicRenameKeyAction(final Path path,
18491854
}
18501855

18511856
@VisibleForTesting
1852-
RenameAtomicity getRedoRenameAtomicity(final Path renamePendingJsonPath,
1857+
public RenameAtomicity getRedoRenameAtomicity(final Path renamePendingJsonPath,
18531858
int fileLen,
18541859
final TracingContext tracingContext) {
18551860
return new RenameAtomicity(renamePendingJsonPath,

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/BlobRenameHandler.java

+1-24
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
import org.apache.hadoop.classification.VisibleForTesting;
3333
import org.apache.hadoop.fs.Path;
34-
import org.apache.hadoop.fs.PathIOException;
3534
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
3635
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
3736
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TimeoutException;
@@ -257,36 +256,14 @@ private boolean containsColon(Path p) {
257256
private boolean preCheck(final Path src, final Path dst,
258257
final PathInformation pathInformation)
259258
throws AzureBlobFileSystemException {
260-
validateDestinationPath(src, dst);
259+
validateDestinationIsNotSubDir(src, dst);
261260
validateSourcePath(pathInformation);
262261
validateDestinationPathNotExist(src, dst, pathInformation);
263262
validateDestinationParentExist(src, dst, pathInformation);
264263

265264
return true;
266265
}
267266

268-
/**
269-
* Validate if the format of the destination path is correct and if the destination
270-
* path is not a sub-directory of the source path.
271-
*
272-
* @param src source path
273-
* @param dst destination path
274-
*
275-
* @throws AbfsRestOperationException if the destination path is invalid
276-
*/
277-
private void validateDestinationPath(final Path src, final Path dst)
278-
throws AbfsRestOperationException {
279-
if (containsColon(dst)) {
280-
throw new AbfsRestOperationException(
281-
HttpURLConnection.HTTP_BAD_REQUEST,
282-
AzureServiceErrorCode.INVALID_RENAME_DESTINATION.getErrorCode(), null,
283-
new PathIOException(dst.toUri().getPath(),
284-
"Destination path contains colon"));
285-
}
286-
287-
validateDestinationIsNotSubDir(src, dst);
288-
}
289-
290267
/**
291268
* Validate if the destination path is not a sub-directory of the source path.
292269
*

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ListActionTaker.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
3636
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
3737
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
38+
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.FileSystemOperationUnhandledException;
3839
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException;
3940
import org.apache.hadoop.fs.azurebfs.contracts.services.BlobListResultSchema;
4041
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultEntrySchema;
@@ -119,7 +120,14 @@ private boolean takeAction(List<Path> paths)
119120
LOG.debug("Thread interrupted while taking action on path: {}",
120121
path.toUri().getPath());
121122
} catch (ExecutionException e) {
122-
executionException = (AzureBlobFileSystemException) e.getCause();
123+
LOG.debug("Execution exception while taking action on path: {}",
124+
path.toUri().getPath());
125+
if (e.getCause() instanceof AzureBlobFileSystemException) {
126+
executionException = (AzureBlobFileSystemException) e.getCause();
127+
} else {
128+
executionException =
129+
new FileSystemOperationUnhandledException(executionException);
130+
}
123131
}
124132
}
125133
if (executionException != null) {
@@ -261,7 +269,7 @@ protected String listAndEnqueue(final ListBlobQueue listBlobQueue,
261269
protected void addPaths(final List<Path> paths,
262270
final ListResultSchema retrievedSchema) {
263271
for (ListResultEntrySchema entry : retrievedSchema.paths()) {
264-
Path entryPath = new Path(ROOT_PATH, entry.name());
272+
Path entryPath = new Path(ROOT_PATH + entry.name());
265273
if (!entryPath.equals(this.path)) {
266274
paths.add(entryPath);
267275
}

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/utils/UriUtils.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,13 @@ private static String replacedUrl(String baseUrl, String oldString, String newSt
248248
*/
249249
public static boolean isKeyForDirectorySet(String key, Set<String> dirSet) {
250250
for (String dir : dirSet) {
251-
if (dir.isEmpty() || key.startsWith(
252-
dir + AbfsHttpConstants.FORWARD_SLASH)) {
251+
// Ensure the directory ends with a forward slash
252+
if (StringUtils.isNotEmpty(dir)
253+
&& !dir.endsWith(AbfsHttpConstants.FORWARD_SLASH)) {
254+
dir += AbfsHttpConstants.FORWARD_SLASH;
255+
}
256+
// Return true if the directory is empty or the key starts with the directory
257+
if (dir.isEmpty() || key.startsWith(dir)) {
253258
return true;
254259
}
255260

0 commit comments

Comments
 (0)