Skip to content

Commit e392420

Browse files
committed
HADOOP-19406. ABFS: [FNSOverBlob] Support User Delegation SAS for FNS Blob (apache#7523)
Contributed by Manika Joshi Reviewed by Anmol Asrani, Manish Bhatt, Anuj Modi Signed off by Anuj Modi<[email protected]>
1 parent a43b2c9 commit e392420

File tree

7 files changed

+228
-43
lines changed

7 files changed

+228
-43
lines changed

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

+23-5
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,8 +118,24 @@
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;
138+
import static org.apache.hadoop.fs.azurebfs.constants.AbfsServiceType.DFS;
123139
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.DATA_BLOCKS_BUFFER;
124140
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ACCOUNT_IS_HNS_ENABLED;
125141
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BLOCK_UPLOAD_ACTIVE_BLOCKS;
@@ -240,16 +256,18 @@ public void initialize(URI uri, Configuration configuration)
240256

241257
/*
242258
* Validates if the correct SAS Token provider is configured for non-HNS accounts.
243-
* For non-HNS accounts, if the authentication type is set to SAS, only a fixed SAS Token is supported as of now.
244-
* A custom SAS Token Provider should not be configured in such cases, as it will override the FixedSASTokenProvider and render it unused.
245-
* 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,
246263
* an InvalidConfigurationValueException will be thrown.
247264
*
248265
* @throws InvalidConfigurationValueException if account is not namespace enabled and FixedSASTokenProvider is not configured.
249266
*/
250267
try {
251268
if (abfsConfiguration.getAuthType(abfsConfiguration.getAccountName()) == AuthType.SAS && // Auth type is SAS
252269
!tryGetIsNamespaceEnabled(new TracingContext(initFSTracingContext)) && // Account is FNS
270+
abfsConfiguration.getFsConfiguredServiceType() == DFS && // Service type is DFS
253271
!abfsConfiguration.isFixedSASTokenProviderConfigured()) { // Fixed SAS Token Provider is not configured
254272
throw new InvalidConfigurationValueException(FS_AZURE_SAS_FIXED_TOKEN, UNAUTHORIZED_SAS);
255273
}

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@
3333
public interface SASTokenProvider {
3434

3535
String CHECK_ACCESS_OPERATION = "check-access";
36+
String COPY_BLOB_DST_OPERATION = "copy-blob-dst";
37+
String COPY_BLOB_SRC_OPERATION = "copy-blob-src";
3638
String CREATE_DIRECTORY_OPERATION = "create-directory";
3739
String CREATE_FILE_OPERATION = "create-file";
3840
String DELETE_OPERATION = "delete";
3941
String DELETE_RECURSIVE_OPERATION = "delete-recursive";
4042
String GET_ACL_OPERATION = "get-acl";
4143
String GET_STATUS_OPERATION = "get-status";
4244
String GET_PROPERTIES_OPERATION = "get-properties";
45+
String LEASE_BLOB_OPERATION = "lease-blob";
4346
String LIST_OPERATION = "list";
47+
String LIST_OPERATION_BLOB = "list-blob";
4448
String READ_OPERATION = "read";
4549
String RENAME_SOURCE_OPERATION = "rename-source";
4650
String RENAME_DESTINATION_OPERATION = "rename-destination";
@@ -49,8 +53,6 @@ public interface SASTokenProvider {
4953
String SET_PERMISSION_OPERATION = "set-permission";
5054
String SET_PROPERTIES_OPERATION = "set-properties";
5155
String WRITE_OPERATION = "write";
52-
// Generic HTTP operation can be used with FixedSASTokenProvider.
53-
String FIXED_SAS_STORE_OPERATION = "fixed-sas";
5456

5557
/**
5658
* Initialize authorizer for Azure Blob File System.

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

+20-18
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ public ListResponseData listPath(final String relativePath, final boolean recurs
367367
abfsUriQueryBuilder.addQuery(QUERY_PARAM_DELIMITER, FORWARD_SLASH);
368368
}
369369
abfsUriQueryBuilder.addQuery(QUERY_PARAM_MAX_RESULTS, String.valueOf(listMaxResults));
370-
appendSASTokenToQuery(relativePath, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
370+
appendSASTokenToQuery(relativePath, SASTokenProvider.LIST_OPERATION_BLOB, abfsUriQueryBuilder);
371371

372372
final URL url = createRequestUrl(abfsUriQueryBuilder.toString());
373373
final AbfsRestOperation op = getAbfsRestOperation(
@@ -555,11 +555,14 @@ public AbfsRestOperation createPathRestOp(final String path,
555555
final ContextEncryptionAdapter contextEncryptionAdapter,
556556
final TracingContext tracingContext) throws AzureBlobFileSystemException {
557557
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();
558+
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
558559
if (isFile) {
559560
addEncryptionKeyRequestHeaders(path, requestHeaders, true,
560561
contextEncryptionAdapter, tracingContext);
562+
appendSASTokenToQuery(path, SASTokenProvider.CREATE_FILE_OPERATION, abfsUriQueryBuilder);
561563
} else {
562564
requestHeaders.add(new AbfsHttpHeader(X_MS_META_HDI_ISFOLDER, TRUE));
565+
appendSASTokenToQuery(path, SASTokenProvider.CREATE_DIRECTORY_OPERATION, abfsUriQueryBuilder);
563566
}
564567
requestHeaders.add(new AbfsHttpHeader(CONTENT_LENGTH, ZERO));
565568
if (isAppendBlob) {
@@ -574,9 +577,6 @@ public AbfsRestOperation createPathRestOp(final String path,
574577
requestHeaders.add(new AbfsHttpHeader(HttpHeaderConfigurations.IF_MATCH, eTag));
575578
}
576579

577-
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
578-
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
579-
580580
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
581581
final AbfsRestOperation op = getAbfsRestOperation(
582582
AbfsRestOperationType.PutBlob,
@@ -698,7 +698,7 @@ public AbfsRestOperation acquireLease(final String path,
698698

699699
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
700700
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE);
701-
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
701+
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder);
702702

703703
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
704704
final AbfsRestOperation op = getAbfsRestOperation(
@@ -726,7 +726,7 @@ public AbfsRestOperation renewLease(final String path, final String leaseId,
726726

727727
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
728728
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE);
729-
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
729+
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder);
730730

731731
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
732732
final AbfsRestOperation op = getAbfsRestOperation(
@@ -754,7 +754,7 @@ public AbfsRestOperation releaseLease(final String path, final String leaseId,
754754

755755
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
756756
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE);
757-
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
757+
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder);
758758

759759
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
760760
final AbfsRestOperation op = getAbfsRestOperation(
@@ -781,7 +781,7 @@ public AbfsRestOperation breakLease(final String path,
781781

782782
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
783783
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, LEASE);
784-
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
784+
appendSASTokenToQuery(path, SASTokenProvider.LEASE_BLOB_OPERATION, abfsUriQueryBuilder);
785785

786786
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
787787
final AbfsRestOperation op = getAbfsRestOperation(
@@ -829,6 +829,8 @@ destination, sourceEtag, isAtomicRenameKey(source), tracingContext
829829
if (blobRenameHandler.execute(false)) {
830830
final AbfsUriQueryBuilder abfsUriQueryBuilder
831831
= createDefaultUriQueryBuilder();
832+
appendSASTokenToQuery(source, SASTokenProvider.RENAME_SOURCE_OPERATION,
833+
abfsUriQueryBuilder);
832834
final URL url = createRequestUrl(destination,
833835
abfsUriQueryBuilder.toString());
834836
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();
@@ -902,7 +904,7 @@ public AbfsRestOperation append(final String path,
902904
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, BLOCK);
903905
abfsUriQueryBuilder.addQuery(QUERY_PARAM_BLOCKID, reqParams.getBlockId());
904906

905-
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION,
907+
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.WRITE_OPERATION,
906908
abfsUriQueryBuilder, cachedSasToken);
907909

908910
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
@@ -975,7 +977,7 @@ public AbfsRestOperation appendBlock(final String path,
975977
}
976978
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
977979
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, APPEND_BLOCK);
978-
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
980+
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.WRITE_OPERATION, abfsUriQueryBuilder);
979981

980982
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
981983
final AbfsRestOperation op = getAbfsRestOperation(
@@ -1067,7 +1069,7 @@ public AbfsRestOperation flush(byte[] buffer,
10671069
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
10681070
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, BLOCKLIST);
10691071
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CLOSE, String.valueOf(isClose));
1070-
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION,
1072+
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.WRITE_OPERATION,
10711073
abfsUriQueryBuilder, cachedSasToken);
10721074

10731075
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
@@ -1129,7 +1131,7 @@ public AbfsRestOperation setPathProperties(final String path,
11291131

11301132
AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
11311133
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, METADATA);
1132-
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
1134+
appendSASTokenToQuery(path, SASTokenProvider.SET_PROPERTIES_OPERATION, abfsUriQueryBuilder);
11331135

11341136
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
11351137
final AbfsRestOperation op = getAbfsRestOperation(
@@ -1208,7 +1210,7 @@ public AbfsRestOperation getPathStatus(final String path,
12081210
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
12091211
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN,
12101212
String.valueOf(getAbfsConfiguration().isUpnUsed()));
1211-
appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION,
1213+
appendSASTokenToQuery(path, SASTokenProvider.GET_PROPERTIES_OPERATION,
12121214
abfsUriQueryBuilder);
12131215

12141216
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
@@ -1287,7 +1289,7 @@ public AbfsRestOperation read(final String path,
12871289
}
12881290

12891291
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
1290-
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.FIXED_SAS_STORE_OPERATION,
1292+
String sasTokenForReuse = appendSASTokenToQuery(path, SASTokenProvider.READ_OPERATION,
12911293
abfsUriQueryBuilder, cachedSasToken);
12921294

12931295
URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
@@ -1449,7 +1451,7 @@ public AbfsRestOperation getBlockList(final String path,
14491451
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();
14501452

14511453
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
1452-
String operation = SASTokenProvider.FIXED_SAS_STORE_OPERATION;
1454+
String operation = SASTokenProvider.READ_OPERATION;
14531455
appendSASTokenToQuery(path, operation, abfsUriQueryBuilder);
14541456

14551457
abfsUriQueryBuilder.addQuery(QUERY_PARAM_COMP, BLOCKLIST);
@@ -1487,9 +1489,9 @@ public AbfsRestOperation copyBlob(Path sourceBlobPath,
14871489
String dstBlobRelativePath = destinationBlobPath.toUri().getPath();
14881490
String srcBlobRelativePath = sourceBlobPath.toUri().getPath();
14891491
appendSASTokenToQuery(dstBlobRelativePath,
1490-
SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilderDst);
1492+
SASTokenProvider.COPY_BLOB_DST_OPERATION, abfsUriQueryBuilderDst);
14911493
appendSASTokenToQuery(srcBlobRelativePath,
1492-
SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilderSrc);
1494+
SASTokenProvider.COPY_BLOB_SRC_OPERATION, abfsUriQueryBuilderSrc);
14931495
final URL url = createRequestUrl(dstBlobRelativePath,
14941496
abfsUriQueryBuilderDst.toString());
14951497
final String sourcePathUrl = createRequestUrl(srcBlobRelativePath,
@@ -1523,7 +1525,7 @@ public AbfsRestOperation deleteBlobPath(final Path blobPath,
15231525
AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
15241526
String blobRelativePath = blobPath.toUri().getPath();
15251527
appendSASTokenToQuery(blobRelativePath,
1526-
SASTokenProvider.FIXED_SAS_STORE_OPERATION, abfsUriQueryBuilder);
1528+
SASTokenProvider.DELETE_OPERATION, abfsUriQueryBuilder);
15271529
final URL url = createRequestUrl(blobRelativePath,
15281530
abfsUriQueryBuilder.toString());
15291531
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();

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

+2-1
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.";
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/site/markdown/index.md

+13-4
Original file line numberDiff line numberDiff line change
@@ -652,13 +652,17 @@ To know more about how SAS Authentication works refer to
652652
[Grant limited access to Azure Storage resources using shared access signatures (SAS)](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview)
653653

654654
There are three types of SAS supported by Azure Storage:
655-
- [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. It is Identity based SAS that works at blob/directory level)
655+
- [User Delegation SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas):
656+
SAS-based authentication works with HNS-enabled ADLS Gen2 accounts
657+
(recommended for use with ABFS) and is also supported with non-HNS (FNS) Blob
658+
accounts. However, it is **NOT SUPPORTED** with FNS-DFS accounts.
656659
- [Service SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas): Global and works at container level.
657660
- [Account SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-account-sas): Global and works at account level.
658661

659662
#### Known Issues With SAS
660-
- SAS Based Authentication works only with HNS Enabled ADLS Gen2 Accounts which
661-
is a recommended account type to be used with ABFS.
663+
- SAS Based Authentication works with HNS Enabled ADLS Gen2 Accounts (which
664+
is a recommended account type to be used with ABFS). It is also supported with
665+
non-HNS (FNS) Blob accounts. It is **NOT SUPPORTED** with FNS-DFS accounts.
662666
- Certain root level operations are known to fail with SAS Based Authentication.
663667

664668
#### Using User Delegation SAS with ABFS
@@ -1465,7 +1469,12 @@ Once the above properties are configured, `hdfs dfs -ls abfs://container1@abfswa
14651469

14661470
Following failures are known and expected to fail as of now.
14671471
1. AzureBlobFileSystem.setXAttr() and AzureBlobFileSystem.getXAttr() will fail when attempted on root ("/") path with `Operation failed: "The request URI is invalid.", HTTP 400 Bad Request`
1468-
1472+
2. If you're using user-delegation SAS authentication:
1473+
- Listing operation for HNS accounts (on DFS endpoint) works with SAS token supporting either blob or directory
1474+
scopes (Signed Resource Type as Blob or Directory),
1475+
though it is intended to work only at the directory scope. It is a known bug.
1476+
- AzureBlobFileSystem.getFileStatus() is expected to fail at root ("/") path with
1477+
`Operation failed: "Server failed to authenticate the request.", HTTP 401 Unauthorized Error`
14691478
## <a name="testing"></a> Testing ABFS
14701479

14711480
See the relevant section in [Testing Azure](testing_azure.html).

0 commit comments

Comments
 (0)