Skip to content

Commit 021abb2

Browse files
committed
tests addition
1 parent e1e66c8 commit 021abb2

File tree

7 files changed

+171
-27
lines changed

7 files changed

+171
-27
lines changed

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
package org.apache.hadoop.fs.azurebfs;
2020

21+
import javax.annotation.Nullable;
2122
import java.io.File;
2223
import java.io.FileNotFoundException;
2324
import java.io.IOException;
@@ -41,7 +42,6 @@
4142
import java.util.concurrent.ExecutorService;
4243
import java.util.concurrent.Executors;
4344
import java.util.concurrent.Future;
44-
import javax.annotation.Nullable;
4545

4646
import org.slf4j.Logger;
4747
import org.slf4j.LoggerFactory;
@@ -118,7 +118,22 @@
118118
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL;
119119
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL_DEFAULT;
120120
import static org.apache.hadoop.fs.Options.OpenFileOptions.FS_OPTION_OPENFILE_STANDARD_OPTIONS;
121-
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.*;
121+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_APPEND;
122+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_CREATE;
123+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_CREATE_NON_RECURSIVE;
124+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_DELETE;
125+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_EXIST;
126+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_GET_DELEGATION_TOKEN;
127+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_GET_FILE_STATUS;
128+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_LIST_STATUS;
129+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_MKDIRS;
130+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_OPEN;
131+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CALL_RENAME;
132+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.DIRECTORIES_CREATED;
133+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.DIRECTORIES_DELETED;
134+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.ERROR_IGNORED;
135+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.FILES_CREATED;
136+
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.FILES_DELETED;
122137
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.CPK_IN_NON_HNS_ACCOUNT_ERROR_MESSAGE;
123138
import static org.apache.hadoop.fs.azurebfs.constants.AbfsServiceType.DFS;
124139
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.DATA_BLOCKS_BUFFER;
@@ -241,9 +256,10 @@ public void initialize(URI uri, Configuration configuration)
241256

242257
/*
243258
* Validates if the correct SAS Token provider is configured for non-HNS accounts.
244-
* For non-HNS accounts, if the authentication type is set to SAS, only a fixed SAS Token is supported as of now.
245-
* A custom SAS Token Provider should not be configured in such cases, as it will override the FixedSASTokenProvider and render it unused.
246-
* If the namespace is not enabled and the FixedSASTokenProvider is not configured,
259+
* For non-HNS accounts with Blob endpoint, both fixed SAS Token and custom SAS Token provider are supported.
260+
* For non-HNS accounts with DFS endpoint, if the authentication type is set to SAS, only fixed SAS Token is supported as of now.
261+
* A custom SAS Token Provider should not be configured in this case as it will override the FixedSASTokenProvider and render it unused.
262+
* If the namespace is not enabled and the FixedSASTokenProvider is not configured for non-HNS accounts with DFS endpoint,
247263
* an InvalidConfigurationValueException will be thrown.
248264
*
249265
* @throws InvalidConfigurationValueException if account is not namespace enabled and FixedSASTokenProvider is not configured.

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,6 @@ destination, sourceEtag, isAtomicRenameKey(source), tracingContext
821821
final AbfsRestOperation successOp = getSuccessOp(
822822
AbfsRestOperationType.RenamePath, HTTP_METHOD_PUT,
823823
url, requestHeaders);
824-
successOp.setMask();
825824
return new AbfsClientRenameResult(successOp, true, false);
826825
} else {
827826
throw new AbfsRestOperationException(HTTP_INTERNAL_ERROR,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ public final class AbfsErrors {
6262
/**
6363
* Exception message on filesystem init if token-provider-auth-type configs are provided
6464
*/
65-
public static final String UNAUTHORIZED_SAS = "Incorrect SAS token provider configured for non-hierarchical namespace account with DFS service type.";
65+
public static final String UNAUTHORIZED_SAS
66+
= "Incorrect SAS token provider configured for non-hierarchical namespace account with DFS service type.";
6667
public static final String ERR_RENAME_BLOB =
6768
"FNS-Blob rename was not successful for source and destination path: ";
6869
public static final String ERR_DELETE_BLOB =

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,6 @@ public AbfsHttpOperation getResult() {
139139
return result;
140140
}
141141

142-
public void setMask() {
143-
result.setMaskForSAS();
144-
}
145-
146142
public void hardSetResult(int httpStatus) {
147143
result = AbfsHttpOperation.getAbfsHttpOperationWithFixedResult(this.url,
148144
this.method, httpStatus);

hadoop-tools/hadoop-azure/src/site/markdown/abfs.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -663,13 +663,17 @@ To know more about how SAS Authentication works refer to
663663
[Grant limited access to Azure Storage resources using shared access signatures (SAS)](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview)
664664

665665
There are three types of SAS supported by Azure Storage:
666-
- [User Delegation SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas): Recommended for use with ABFS Driver with HNS Enabled ADLS Gen2 accounts or HNS-Disabled Blob Storage accounts. It is Identity based SAS that works at blob/directory level)
666+
667+
- [User Delegation SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas):
668+
Recommended for use with ABFS Driver with HNS Enabled ADLS Gen2 accounts. It
669+
is Identity based SAS that works at blob/directory level)
667670
- [Service SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas): Global and works at container level.
668671
- [Account SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-account-sas): Global and works at account level.
669672

670673
#### Known Issues With SAS
671674
- SAS Based Authentication works with HNS Enabled ADLS Gen2 Accounts (which
672-
is a recommended account type to be used with ABFS) and HNS-Disabled Blob Storage accounts.
675+
is a recommended account type to be used with ABFS). It is also supported with
676+
non-HNS (FNS) Blob accounts. It is **NOT SUPPORTED** with FNS-DFS accounts.
673677
- Certain root level operations are known to fail with SAS Based Authentication.
674678

675679
#### Using User Delegation SAS with ABFS
@@ -737,7 +741,7 @@ the following configurations apart from above two:
737741

738742
- **Security**: More secure than Shared Key and allows granting limited access
739743
to data without exposing the access key. Recommended to be used only with HNS Enabled,
740-
ADLS Gen 2 storage accounts or HNS-Disabled Blob Storage accounts.
744+
ADLS Gen 2 storage accounts.
741745

742746
#### Using Account/Service SAS with ABFS
743747

hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java

Lines changed: 136 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
import java.nio.file.AccessDeniedException;
2424
import java.util.ArrayList;
2525
import java.util.Arrays;
26+
import java.util.Hashtable;
2627
import java.util.List;
2728
import java.util.UUID;
2829

2930
import org.assertj.core.api.Assertions;
3031
import org.junit.Assume;
3132
import org.junit.Test;
33+
import org.mockito.Mockito;
3234
import org.slf4j.Logger;
3335
import org.slf4j.LoggerFactory;
3436

@@ -40,10 +42,14 @@
4042
import org.apache.hadoop.fs.Path;
4143
import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants;
4244
import org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys;
45+
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultEntrySchema;
4346
import org.apache.hadoop.fs.azurebfs.extensions.MockDelegationSASTokenProvider;
47+
import org.apache.hadoop.fs.azurebfs.services.AbfsBlobClient;
48+
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
49+
import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
4450
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
4551
import org.apache.hadoop.fs.azurebfs.services.AuthType;
46-
import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
52+
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
4753
import org.apache.hadoop.fs.permission.AclEntry;
4854
import org.apache.hadoop.fs.permission.AclEntryScope;
4955
import org.apache.hadoop.fs.permission.AclStatus;
@@ -417,16 +423,75 @@ public void testProperties() throws Exception {
417423
}
418424

419425
@Test
420-
public void testSignatureMask() throws Exception {
426+
// FileSystemProperties are not supported by delegation SAS and should throw exception
427+
public void testSetFileSystemProperties() throws Exception {
421428
final AzureBlobFileSystem fs = getFileSystem();
422-
String src = String.format("/testABC/test%s.xt", UUID.randomUUID());
423-
fs.create(new Path(src)).close();
424-
AbfsRestOperation abfsHttpRestOperation = fs.getAbfsClient()
425-
.renamePath(src, "/testABC" + "/abc.txt", null,
426-
getTestTracingContext(fs, false), null,
427-
false)
428-
.getOp();
429-
AbfsHttpOperation result = abfsHttpRestOperation.getResult();
429+
final Hashtable<String, String>
430+
properties = new Hashtable<>();
431+
properties.put("FileSystemProperties", "true");
432+
TracingContext tracingContext = getTestTracingContext(fs, true);
433+
assertThrows(IOException.class, ()-> fs.getAbfsStore().setFilesystemProperties(properties, tracingContext));
434+
assertThrows(IOException.class, ()-> fs.getAbfsStore().getFilesystemProperties(tracingContext));
435+
}
436+
437+
@Test
438+
//Test list and delete operation on implicit paths
439+
public void testListAndDeleteImplicitPaths() throws Exception {
440+
AzureBlobFileSystem fs = getFileSystem();
441+
AbfsBlobClient client = ((AbfsBlobClient) getFileSystem().getAbfsClient());
442+
assumeBlobServiceType();
443+
444+
Path file1 = new Path("/testDir/dir1/file1");
445+
Path file2 = new Path("/testDir/dir1/file2");
446+
Path implicitDir = file1.getParent();
447+
448+
createAzCopyFolder(implicitDir);
449+
createAzCopyFile(file1);
450+
createAzCopyFile(file2);
451+
452+
AbfsRestOperation op = client.listPath(
453+
implicitDir.toString(), false, 2, null,
454+
getTestTracingContext(getFileSystem(), true));
455+
List<? extends ListResultEntrySchema> list = op.getResult()
456+
.getListResultSchema()
457+
.paths();
458+
Assertions.assertThat(list).hasSize(2);
459+
460+
client.deletePath(implicitDir.toString(), true, "",
461+
getTestTracingContext(fs, false));
462+
463+
Assertions.assertThat(fs.exists(file1))
464+
.describedAs("Deleted file1 should not exist.").isFalse();
465+
Assertions.assertThat(fs.exists(file2))
466+
.describedAs("Deleted file2 should not exist.").isFalse();
467+
Assertions.assertThat(fs.exists(implicitDir))
468+
.describedAs("The parent dir should not exist.")
469+
.isFalse();
470+
}
471+
472+
473+
/**
474+
* Spies on the AzureBlobFileSystem's store and client to enable mocking and verification
475+
* of client interactions in tests. It replaces the actual store and client with mocked versions.
476+
*
477+
* @param fs the AzureBlobFileSystem instance
478+
* @return the spied AbfsClient for interaction verification
479+
*/
480+
private AbfsClient addSpyHooksOnClient(final AzureBlobFileSystem fs) {
481+
AzureBlobFileSystemStore store = Mockito.spy(fs.getAbfsStore());
482+
Mockito.doReturn(store).when(fs).getAbfsStore();
483+
AbfsClient client = Mockito.spy(store.getClient());
484+
Mockito.doReturn(client).when(store).getClient();
485+
return client;
486+
}
487+
488+
/**
489+
* Asserts the signature masking in the URL and encoded URL of the AbfsRestOperation.
490+
*
491+
* @param op the AbfsRestOperation
492+
*/
493+
private void checkSignatureMaskAssertions(AbfsRestOperation op){
494+
AbfsHttpOperation result = op.getResult();
430495
String url = result.getMaskedUrl();
431496
String encodedUrl = result.getMaskedEncodedUrl();
432497
Assertions.assertThat(url.substring(url.indexOf("sig=")))
@@ -437,6 +502,67 @@ public void testSignatureMask() throws Exception {
437502
.startsWith("sig%3DXXXXX");
438503
}
439504

505+
@Test
506+
// Test masking of signature for rename operation for Blob
507+
public void testSignatureMaskforBlob() throws Exception {
508+
assumeBlobServiceType();
509+
final AzureBlobFileSystem fs = Mockito.spy(this.getFileSystem());
510+
AbfsBlobClient client = (AbfsBlobClient) addSpyHooksOnClient(fs);
511+
512+
fs.getAbfsStore().setClient(client);
513+
String src = String.format("/testABC/test%s.xt", UUID.randomUUID());
514+
String dest = "/testABC" + "/abc.txt";
515+
fs.create(new Path(src)).close();
516+
517+
Mockito.doAnswer(answer -> {
518+
Path srcCopy = answer.getArgument(0);
519+
Path dstCopy = answer.getArgument(1);
520+
String leaseId = answer.getArgument(2);
521+
TracingContext tracingContext = answer.getArgument(3);
522+
AbfsRestOperation op
523+
= ((AbfsBlobClient) getFileSystem().getAbfsClient()).copyBlob(srcCopy,
524+
dstCopy, leaseId, tracingContext);
525+
checkSignatureMaskAssertions(op);
526+
return answer.callRealMethod();
527+
})
528+
.when(client)
529+
.copyBlob(Mockito.any(Path.class), Mockito.any(Path.class),
530+
Mockito.any(String.class), Mockito.any(TracingContext.class));
531+
532+
Mockito.doAnswer(answer -> {
533+
Path blobPath = answer.getArgument(0);
534+
String leaseId = answer.getArgument(1);
535+
TracingContext tracingContext = answer.getArgument(2);
536+
AbfsRestOperation op
537+
= ((AbfsBlobClient) getFileSystem().getAbfsClient()).deleteBlobPath(blobPath,
538+
leaseId, tracingContext);
539+
checkSignatureMaskAssertions(op);
540+
return answer.callRealMethod();
541+
})
542+
.when(client)
543+
.deleteBlobPath(Mockito.any(Path.class), Mockito.any(String.class),
544+
Mockito.any(TracingContext.class));
545+
546+
client.renamePath(src, dest, null,
547+
getTestTracingContext(fs, false), null,
548+
false);
549+
}
550+
551+
// Test masking of signature for rename operation for DFS
552+
@Test
553+
public void testSignatureMask() throws Exception {
554+
assumeDfsServiceType();
555+
final AzureBlobFileSystem fs = getFileSystem();
556+
String src = String.format("/testABC/test%s.xt", UUID.randomUUID());
557+
fs.create(new Path(src)).close();
558+
AbfsRestOperation abfsHttpRestOperation = fs.getAbfsClient()
559+
.renamePath(src, "/testABC" + "/abc.txt", null,
560+
getTestTracingContext(fs, false), null,
561+
false)
562+
.getOp();
563+
checkSignatureMaskAssertions(abfsHttpRestOperation);
564+
}
565+
440566
@Test
441567
public void testSignatureMaskOnExceptionMessage() throws Exception {
442568
intercept(IOException.class, "sig=XXXX",

hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/utils/DelegationSASGenerator.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public String getDelegationSAS(String accountName, String containerName, String
7070
case SASTokenProvider.DELETE_RECURSIVE_OPERATION:
7171
sp = "d";
7272
sr = "d";
73-
sdd = Integer.toString(StringUtils.countMatches(path, "/"));
73+
sdd = path.equals("/")? "0": Integer.toString(StringUtils.countMatches(path, "/"));
7474
break;
7575
case SASTokenProvider.CHECK_ACCESS_OPERATION:
7676
case SASTokenProvider.GET_ACL_OPERATION:
@@ -79,10 +79,12 @@ public String getDelegationSAS(String accountName, String containerName, String
7979
break;
8080
case SASTokenProvider.LIST_OPERATION_BLOB:
8181
sp = "l";
82-
sr="c";
82+
sr = "c";
8383
break;
8484
case SASTokenProvider.LIST_OPERATION:
8585
sp = "l";
86+
sr = "d";
87+
sdd = path.equals("/")? "0": Integer.toString(StringUtils.countMatches(path, "/"));
8688
break;
8789
case SASTokenProvider.GET_PROPERTIES_OPERATION:
8890
case SASTokenProvider.READ_OPERATION:
@@ -196,4 +198,4 @@ private String computeSignatureForSAS(String sp, String st, String se, String sv
196198
LOG.debug("Delegation SAS stringToSign: " + stringToSign.replace("\n", "."));
197199
return computeHmac256(stringToSign);
198200
}
199-
}
201+
}

0 commit comments

Comments
 (0)