You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by tm...@apache.org on 2020/06/19 19:49:56 UTC

[hadoop] 01/06: HADOOP-17002. ABFS: Adding config to determine if the account is HNS enabled or not

This is an automated email from the ASF dual-hosted git repository.

tmarquardt pushed a commit to branch branch-3.3
in repository https://gitbox.apache.org/repos/asf/hadoop.git

commit 76ee7e5494579b6f8adf1d86b17e97a63a8576ad
Author: bilaharith <52...@users.noreply.github.com>
AuthorDate: Fri Apr 24 06:16:18 2020 +0530

    HADOOP-17002. ABFS: Adding config to determine if the account is HNS enabled or not
    
    Contributed by Bilahari T H.
---
 .../hadoop/fs/azurebfs/AbfsConfiguration.java      |  14 ++
 .../fs/azurebfs/AzureBlobFileSystemStore.java      |  59 ++++++---
 .../fs/azurebfs/constants/ConfigurationKeys.java   |   7 +
 .../constants/FileSystemConfigurations.java        |   3 +
 .../exceptions/TrileanConversionException.java     |  36 ++++++
 .../apache/hadoop/fs/azurebfs/enums/Trilean.java   |  80 ++++++++++++
 .../hadoop/fs/azurebfs/enums/package-info.java     |  22 ++++
 .../hadoop-azure/src/site/markdown/abfs.md         |   5 +
 .../fs/azurebfs/ITestGetNameSpaceEnabled.java      | 141 ++++++++++++++++++++-
 .../apache/hadoop/fs/azurebfs/TrileanTests.java    |  92 ++++++++++++++
 10 files changed, 438 insertions(+), 21 deletions(-)

diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AbfsConfiguration.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AbfsConfiguration.java
index 78d6260..d60bc37 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AbfsConfiguration.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AbfsConfiguration.java
@@ -47,6 +47,7 @@ import org.apache.hadoop.fs.azurebfs.diagnostics.BooleanConfigurationBasicValida
 import org.apache.hadoop.fs.azurebfs.diagnostics.IntegerConfigurationBasicValidator;
 import org.apache.hadoop.fs.azurebfs.diagnostics.LongConfigurationBasicValidator;
 import org.apache.hadoop.fs.azurebfs.diagnostics.StringConfigurationBasicValidator;
+import org.apache.hadoop.fs.azurebfs.enums.Trilean;
 import org.apache.hadoop.fs.azurebfs.extensions.CustomTokenProviderAdaptee;
 import org.apache.hadoop.fs.azurebfs.extensions.SASTokenProvider;
 import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
@@ -81,6 +82,10 @@ public class AbfsConfiguration{
   private final boolean isSecure;
   private static final Logger LOG = LoggerFactory.getLogger(AbfsConfiguration.class);
 
+  @StringConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_ACCOUNT_IS_HNS_ENABLED,
+      DefaultValue = DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED)
+  private String isNamespaceEnabledAccount;
+
   @IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_WRITE_BUFFER_SIZE,
       MinValue = MIN_BUFFER_SIZE,
       MaxValue = MAX_BUFFER_SIZE,
@@ -232,6 +237,10 @@ public class AbfsConfiguration{
     }
   }
 
+  public Trilean getIsNamespaceEnabledAccount() {
+    return Trilean.getTrilean(isNamespaceEnabledAccount);
+  }
+
   /**
    * Gets the Azure Storage account name corresponding to this instance of configuration.
    * @return the Azure Storage account name
@@ -746,6 +755,11 @@ public class AbfsConfiguration{
     this.maxIoRetries = maxIoRetries;
   }
 
+  @VisibleForTesting
+  void setIsNamespaceEnabledAccount(String isNamespaceEnabledAccount) {
+    this.isNamespaceEnabledAccount = isNamespaceEnabledAccount;
+  }
+
   private String getTrimmedPasswordString(String key, String defaultValue) throws IOException {
     String value = getPasswordString(key);
     if (StringUtils.isBlank(value)) {
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystemStore.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystemStore.java
index 62145e1..d37ceb3 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystemStore.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystemStore.java
@@ -73,6 +73,8 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
 import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
 import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultEntrySchema;
 import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultSchema;
+import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TrileanConversionException;
+import org.apache.hadoop.fs.azurebfs.enums.Trilean;
 import org.apache.hadoop.fs.azurebfs.extensions.SASTokenProvider;
 import org.apache.hadoop.fs.azurebfs.extensions.ExtensionHelper;
 import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
@@ -133,8 +135,7 @@ public class AzureBlobFileSystemStore implements Closeable {
 
   private final AbfsConfiguration abfsConfiguration;
   private final Set<String> azureAtomicRenameDirSet;
-  private boolean isNamespaceEnabledSet;
-  private boolean isNamespaceEnabled;
+  private Trilean isNamespaceEnabled;
   private final AuthType authType;
   private final UserGroupInformation userGroupInformation;
   private final IdentityTransformer identityTransformer;
@@ -155,6 +156,8 @@ public class AzureBlobFileSystemStore implements Closeable {
 
     LOG.trace("AbfsConfiguration init complete");
 
+    this.isNamespaceEnabled = abfsConfiguration.getIsNamespaceEnabledAccount();
+
     this.userGroupInformation = UserGroupInformation.getCurrentUser();
     this.userName = userGroupInformation.getShortUserName();
     LOG.trace("UGI init complete");
@@ -234,26 +237,33 @@ public class AzureBlobFileSystemStore implements Closeable {
   }
 
   public boolean getIsNamespaceEnabled() throws AzureBlobFileSystemException {
-    if (!isNamespaceEnabledSet) {
+    try {
+      return this.isNamespaceEnabled.toBoolean();
+    } catch (TrileanConversionException e) {
+      LOG.debug("isNamespaceEnabled is UNKNOWN; fall back and determine through"
+          + " getAcl server call", e);
+    }
 
-      LOG.debug("Get root ACL status");
-      try (AbfsPerfInfo perfInfo = startTracking("getIsNamespaceEnabled", "getAclStatus")) {
-        AbfsRestOperation op = client.getAclStatus(AbfsHttpConstants.FORWARD_SLASH + AbfsHttpConstants.ROOT_PATH);
-        perfInfo.registerResult(op.getResult());
-        isNamespaceEnabled = true;
-        perfInfo.registerSuccess(true);
-      } catch (AbfsRestOperationException ex) {
-        // Get ACL status is a HEAD request, its response doesn't contain errorCode
-        // So can only rely on its status code to determine its account type.
-        if (HttpURLConnection.HTTP_BAD_REQUEST != ex.getStatusCode()) {
-          throw ex;
-        }
-        isNamespaceEnabled = false;
+    LOG.debug("Get root ACL status");
+    try (AbfsPerfInfo perfInfo = startTracking("getIsNamespaceEnabled",
+        "getAclStatus")) {
+      AbfsRestOperation op = client.getAclStatus(
+          AbfsHttpConstants.FORWARD_SLASH + AbfsHttpConstants.ROOT_PATH);
+      perfInfo.registerResult(op.getResult());
+      isNamespaceEnabled = Trilean.getTrilean(true);
+      perfInfo.registerSuccess(true);
+    } catch (AbfsRestOperationException ex) {
+      // Get ACL status is a HEAD request, its response doesn't contain
+      // errorCode
+      // So can only rely on its status code to determine its account type.
+      if (HttpURLConnection.HTTP_BAD_REQUEST != ex.getStatusCode()) {
+        throw ex;
       }
-      isNamespaceEnabledSet = true;
+
+      isNamespaceEnabled = Trilean.getTrilean(false);
     }
 
-    return isNamespaceEnabled;
+    return isNamespaceEnabled.toBoolean();
   }
 
   @VisibleForTesting
@@ -1406,4 +1416,15 @@ public class AzureBlobFileSystemStore implements Closeable {
   AbfsClient getClient() {
     return this.client;
   }
-}
\ No newline at end of file
+
+  @VisibleForTesting
+  void setClient(AbfsClient client) {
+    this.client = client;
+  }
+
+  @VisibleForTesting
+  void setNamespaceEnabled(Trilean isNamespaceEnabled){
+    this.isNamespaceEnabled = isNamespaceEnabled;
+  }
+
+}
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/ConfigurationKeys.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/ConfigurationKeys.java
index 5db111b..597f93f 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/ConfigurationKeys.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/ConfigurationKeys.java
@@ -27,6 +27,13 @@ import org.apache.hadoop.classification.InterfaceStability;
 @InterfaceAudience.Public
 @InterfaceStability.Evolving
 public final class ConfigurationKeys {
+
+  /**
+   * Config to specify if the configured account is HNS enabled or not. If
+   * this config is not set, getacl call is made on account filesystem root
+   * path to determine HNS status.
+   */
+  public static final String FS_AZURE_ACCOUNT_IS_HNS_ENABLED = "fs.azure.account.hns.enabled";
   public static final String FS_AZURE_ACCOUNT_KEY_PROPERTY_NAME = "fs.azure.account.key";
   public static final String FS_AZURE_ACCOUNT_KEY_PROPERTY_NAME_REGX = "fs\\.azure\\.account\\.key\\.(.*)";
   public static final String FS_AZURE_SECURE_MODE = "fs.azure.secure.mode";
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/FileSystemConfigurations.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/FileSystemConfigurations.java
index 3add0ef..ef2e708 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/FileSystemConfigurations.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/constants/FileSystemConfigurations.java
@@ -30,6 +30,9 @@ import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.EMPTY_ST
 @InterfaceAudience.Public
 @InterfaceStability.Evolving
 public final class FileSystemConfigurations {
+
+  public static final String DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED = "";
+
   public static final String USER_HOME_DIRECTORY_PREFIX = "/user";
 
   // Retry parameter defaults.
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/exceptions/TrileanConversionException.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/exceptions/TrileanConversionException.java
new file mode 100644
index 0000000..87eb05c
--- /dev/null
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/exceptions/TrileanConversionException.java
@@ -0,0 +1,36 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.fs.azurebfs.contracts.exceptions;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Thrown when tried to convert Trilean.UNKNOWN to boolean. Only Trilean.TRUE
+ * and Trilean.FALSE can be converted to boolean.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Stable
+public final class TrileanConversionException
+    extends AzureBlobFileSystemException {
+  public TrileanConversionException() {
+    super("Cannot convert Trilean.UNKNOWN to boolean");
+  }
+
+}
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/enums/Trilean.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/enums/Trilean.java
new file mode 100644
index 0000000..dc5f439
--- /dev/null
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/enums/Trilean.java
@@ -0,0 +1,80 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.fs.azurebfs.enums;
+
+import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TrileanConversionException;
+
+/**
+ * Enum to represent 3 values, TRUE, FALSE and UNKNOWN. Can be used where
+ * boolean is not enough to hold the information.
+ */
+public enum Trilean {
+
+  FALSE, TRUE, UNKNOWN;
+
+  private static final String TRUE_STR = "true";
+  private static final String FALSE_STR = "false";
+
+  /**
+   * Converts boolean to Trilean.
+   *
+   * @param isTrue the boolean to convert.
+   * @return the corresponding Trilean for the passed boolean isTrue.
+   */
+  public static Trilean getTrilean(final boolean isTrue) {
+    if (isTrue) {
+      return Trilean.TRUE;
+    }
+
+    return Trilean.FALSE;
+  }
+
+  /**
+   * Converts String to Trilean.
+   *
+   * @param str the string to convert.
+   * @return the corresponding Trilean for the passed string str.
+   */
+  public static Trilean getTrilean(String str) {
+    if (TRUE_STR.equalsIgnoreCase(str)) {
+      return Trilean.TRUE;
+    }
+
+    if (FALSE_STR.equalsIgnoreCase(str)) {
+      return Trilean.FALSE;
+    }
+
+    return Trilean.UNKNOWN;
+  }
+
+  /**
+   * Converts the Trilean enum to boolean.
+   *
+   * @return the corresponding boolean.
+   * @throws TrileanConversionException when tried to convert Trilean.UNKNOWN.
+   */
+  public boolean toBoolean() throws TrileanConversionException {
+    if (this == Trilean.UNKNOWN) {
+      throw new TrileanConversionException();
+    }
+
+    return Boolean.valueOf(this.name());
+  }
+
+}
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/enums/package-info.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/enums/package-info.java
new file mode 100644
index 0000000..b2a9b0f
--- /dev/null
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/enums/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+package org.apache.hadoop.fs.azurebfs.enums;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
diff --git a/hadoop-tools/hadoop-azure/src/site/markdown/abfs.md b/hadoop-tools/hadoop-azure/src/site/markdown/abfs.md
index e1a2fca..2b4b14d 100644
--- a/hadoop-tools/hadoop-azure/src/site/markdown/abfs.md
+++ b/hadoop-tools/hadoop-azure/src/site/markdown/abfs.md
@@ -663,6 +663,11 @@ Hflush() being the only documented API that can provide persistent data
 transfer, Flush() also attempting to persist buffered data will lead to
 performance issues.
 
+### <a name="hnscheckconfigoptions"></a> HNS Check Options
+Config `fs.azure.account.hns.enabled` provides an option to specify whether
+ the storage account is HNS enabled or not. In case the config is not provided,
+  a server call is made to check the same.
+
 ### <a name="flushconfigoptions"></a> Access Options
 Config `fs.azure.enable.check.access` needs to be set true to enable
  the AzureBlobFileSystem.access().
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestGetNameSpaceEnabled.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestGetNameSpaceEnabled.java
index 74c8803..4268ff2 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestGetNameSpaceEnabled.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestGetNameSpaceEnabled.java
@@ -21,15 +21,31 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.UUID;
 
+import org.apache.hadoop.fs.azurebfs.enums.Trilean;
 import org.junit.Assume;
 import org.junit.Test;
+import org.assertj.core.api.Assertions;
 
+import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
+import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
 import org.apache.hadoop.fs.azurebfs.services.AuthType;
 
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED;
+import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.TEST_CONFIGURATION_FILE_NAME;
 import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION;
+import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ACCOUNT_IS_HNS_ENABLED;
 import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ACCOUNT_KEY_PROPERTY_NAME;
 import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.FS_AZURE_TEST_NAMESPACE_ENABLED_ACCOUNT;
 import static org.apache.hadoop.test.LambdaTestUtils.intercept;
@@ -39,6 +55,9 @@ import static org.apache.hadoop.test.LambdaTestUtils.intercept;
  */
 public class ITestGetNameSpaceEnabled extends AbstractAbfsIntegrationTest {
 
+  private static final String TRUE_STR = "true";
+  private static final String FALSE_STR = "false";
+
   private boolean isUsingXNSAccount;
   public ITestGetNameSpaceEnabled() throws Exception {
     isUsingXNSAccount = getConfiguration().getBoolean(FS_AZURE_TEST_NAMESPACE_ENABLED_ACCOUNT, false);
@@ -57,7 +76,57 @@ public class ITestGetNameSpaceEnabled extends AbstractAbfsIntegrationTest {
     Assume.assumeFalse("Skip this test because the account being used for test is a XNS account",
             isUsingXNSAccount);
     assertFalse("Expecting getIsNamespaceEnabled() return false",
-            getFileSystem().getIsNamespaceEnabled());
+        getFileSystem().getIsNamespaceEnabled());
+  }
+
+  @Test
+  public void testGetIsNamespaceEnabledWhenConfigIsTrue() throws Exception {
+    AzureBlobFileSystem fs = getNewFSWithHnsConf(TRUE_STR);
+    Assertions.assertThat(fs.getIsNamespaceEnabled()).describedAs(
+        "getIsNamespaceEnabled should return true when the "
+            + "config is set as true").isTrue();
+    fs.getAbfsStore().deleteFilesystem();
+    unsetAndAssert();
+  }
+
+  @Test
+  public void testGetIsNamespaceEnabledWhenConfigIsFalse() throws Exception {
+    AzureBlobFileSystem fs = getNewFSWithHnsConf(FALSE_STR);
+    Assertions.assertThat(fs.getIsNamespaceEnabled()).describedAs(
+        "getIsNamespaceEnabled should return false when the "
+            + "config is set as false").isFalse();
+    fs.getAbfsStore().deleteFilesystem();
+    unsetAndAssert();
+  }
+
+  private void unsetAndAssert() throws Exception {
+    AzureBlobFileSystem fs = getNewFSWithHnsConf(
+        DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED);
+    boolean expectedValue = this.getConfiguration()
+        .getBoolean(FS_AZURE_TEST_NAMESPACE_ENABLED_ACCOUNT, false);
+    Assertions.assertThat(fs.getIsNamespaceEnabled()).describedAs(
+        "getIsNamespaceEnabled should return the value "
+            + "configured for fs.azure.test.namespace.enabled")
+        .isEqualTo(expectedValue);
+    fs.getAbfsStore().deleteFilesystem();
+  }
+
+  private AzureBlobFileSystem getNewFSWithHnsConf(
+      String isNamespaceEnabledAccount) throws Exception {
+    Configuration rawConfig = new Configuration();
+    rawConfig.addResource(TEST_CONFIGURATION_FILE_NAME);
+    rawConfig.set(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, isNamespaceEnabledAccount);
+    rawConfig
+        .setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION, true);
+    rawConfig.set(CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY,
+        getNonExistingUrl());
+    return (AzureBlobFileSystem) FileSystem.get(rawConfig);
+  }
+
+  private String getNonExistingUrl() {
+    String testUri = this.getTestUrl();
+    return getAbfsScheme() + "://" + UUID.randomUUID() + testUri
+        .substring(testUri.indexOf("@"));
   }
 
   @Test
@@ -95,4 +164,72 @@ public class ITestGetNameSpaceEnabled extends AbstractAbfsIntegrationTest {
               fs.getIsNamespaceEnabled();
             });
   }
-}
\ No newline at end of file
+
+  @Test
+  public void testEnsureGetAclCallIsMadeOnceWhenConfigIsInvalid()
+      throws Exception {
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+    ensureGetAclCallIsMadeOnceForInvalidConf(" ");
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+    ensureGetAclCallIsMadeOnceForInvalidConf("Invalid conf");
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+  }
+
+  @Test
+  public void testEnsureGetAclCallIsNeverMadeWhenConfigIsValid()
+      throws Exception {
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+    ensureGetAclCallIsNeverMadeForValidConf(FALSE_STR.toLowerCase());
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+    ensureGetAclCallIsNeverMadeForValidConf(FALSE_STR.toUpperCase());
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+    ensureGetAclCallIsNeverMadeForValidConf(TRUE_STR.toLowerCase());
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+    ensureGetAclCallIsNeverMadeForValidConf(TRUE_STR.toUpperCase());
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+  }
+
+  @Test
+  public void testEnsureGetAclCallIsMadeOnceWhenConfigIsNotPresent()
+      throws IOException {
+    unsetConfAndEnsureGetAclCallIsMadeOnce();
+  }
+
+  private void ensureGetAclCallIsMadeOnceForInvalidConf(String invalidConf)
+      throws Exception {
+    this.getFileSystem().getAbfsStore()
+        .setNamespaceEnabled(Trilean.getTrilean(invalidConf));
+    AbfsClient mockClient =
+        callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient();
+    verify(mockClient, times(1)).getAclStatus(anyString());
+  }
+
+  private void ensureGetAclCallIsNeverMadeForValidConf(String validConf)
+      throws Exception {
+    this.getFileSystem().getAbfsStore()
+        .setNamespaceEnabled(Trilean.getTrilean(validConf));
+    AbfsClient mockClient =
+        callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient();
+    verify(mockClient, never()).getAclStatus(anyString());
+  }
+
+  private void unsetConfAndEnsureGetAclCallIsMadeOnce() throws IOException {
+    this.getFileSystem().getAbfsStore().setNamespaceEnabled(Trilean.UNKNOWN);
+    AbfsClient mockClient =
+        callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient();
+    verify(mockClient, times(1)).getAclStatus(anyString());
+  }
+
+  private AbfsClient callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient()
+      throws IOException {
+    final AzureBlobFileSystem abfs = this.getFileSystem();
+    final AzureBlobFileSystemStore abfsStore = abfs.getAbfsStore();
+    final AbfsClient mockClient = mock(AbfsClient.class);
+    doReturn(mock(AbfsRestOperation.class)).when(mockClient)
+        .getAclStatus(anyString());
+    abfsStore.setClient(mockClient);
+    abfs.getIsNamespaceEnabled();
+    return mockClient;
+  }
+
+}
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/TrileanTests.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/TrileanTests.java
new file mode 100644
index 0000000..45467d4
--- /dev/null
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/TrileanTests.java
@@ -0,0 +1,92 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.fs.azurebfs;
+
+import org.junit.Test;
+
+import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TrileanConversionException;
+import org.apache.hadoop.fs.azurebfs.enums.Trilean;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.catchThrowable;
+
+/**
+ * Tests for the enum Trilean.
+ */
+public class TrileanTests {
+
+  private static final String TRUE_STR = "true";
+  private static final String FALSE_STR = "false";
+
+  @Test
+  public void testGetTrileanForBoolean() {
+    assertThat(Trilean.getTrilean(true)).describedAs(
+        "getTrilean should return Trilean.TRUE when true is passed")
+        .isEqualTo(Trilean.TRUE);
+    assertThat(Trilean.getTrilean(false)).describedAs(
+        "getTrilean should return Trilean.FALSE when false is passed")
+        .isEqualTo(Trilean.FALSE);
+  }
+
+  @Test
+  public void testGetTrileanForString() {
+    assertThat(Trilean.getTrilean(TRUE_STR.toLowerCase())).describedAs(
+        "getTrilean should return Trilean.TRUE when true is passed")
+        .isEqualTo(Trilean.TRUE);
+    assertThat(Trilean.getTrilean(TRUE_STR.toUpperCase())).describedAs(
+        "getTrilean should return Trilean.TRUE when TRUE is passed")
+        .isEqualTo(Trilean.TRUE);
+
+    assertThat(Trilean.getTrilean(FALSE_STR.toLowerCase())).describedAs(
+        "getTrilean should return Trilean.FALSE when false is passed")
+        .isEqualTo(Trilean.FALSE);
+    assertThat(Trilean.getTrilean(FALSE_STR.toUpperCase())).describedAs(
+        "getTrilean should return Trilean.FALSE when FALSE is passed")
+        .isEqualTo(Trilean.FALSE);
+
+    testInvalidString(null);
+    testInvalidString(" ");
+    testInvalidString("invalid");
+    testInvalidString("truee");
+    testInvalidString("falsee");
+  }
+
+  private void testInvalidString(String invalidString) {
+    assertThat(Trilean.getTrilean(invalidString)).describedAs(
+        "getTrilean should return Trilean.UNKNOWN for anything not true/false")
+        .isEqualTo(Trilean.UNKNOWN);
+  }
+
+  @Test
+  public void testToBoolean() throws TrileanConversionException {
+    assertThat(Trilean.TRUE.toBoolean())
+        .describedAs("toBoolean should return true for Trilean.TRUE").isTrue();
+    assertThat(Trilean.FALSE.toBoolean())
+        .describedAs("toBoolean should return false for Trilean.FALSE")
+        .isFalse();
+
+    assertThat(catchThrowable(() -> Trilean.UNKNOWN.toBoolean())).describedAs(
+        "toBoolean on Trilean.UNKNOWN results in TrileanConversionException")
+        .isInstanceOf(TrileanConversionException.class).describedAs(
+        "Exception message should be: catchThrowable(()->Trilean.UNKNOWN"
+            + ".toBoolean())")
+        .hasMessage("Cannot convert Trilean.UNKNOWN to boolean");
+  }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org