-
Notifications
You must be signed in to change notification settings - Fork 9.2k
HADOOP-19595. ABFS: AbfsConfiguration should store account type information (HNS or FNS) #7765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
37d6a1e
179a18e
8e13f7b
ad210ef
fd0d14e
fa95fcf
71c8148
c4703b2
67c1562
5eb0616
6032492
ef93413
0b909fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,6 +66,7 @@ | |
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidAbfsRestOperationException; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidFileSystemPropertyException; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.SASTokenProviderException; | ||
|
|
@@ -442,7 +443,7 @@ protected List<AbfsHttpHeader> createCommonHeaders(ApiVersion xMsVersion) { | |
| requestHeaders.add(new AbfsHttpHeader(X_MS_VERSION, xMsVersion.toString())); | ||
| requestHeaders.add(new AbfsHttpHeader(ACCEPT_CHARSET, UTF_8)); | ||
| requestHeaders.add(new AbfsHttpHeader(CONTENT_TYPE, EMPTY_STRING)); | ||
| requestHeaders.add(new AbfsHttpHeader(USER_AGENT, userAgent)); | ||
| requestHeaders.add(new AbfsHttpHeader(USER_AGENT, getUserAgent())); | ||
| return requestHeaders; | ||
| } | ||
|
|
||
|
|
@@ -1322,8 +1323,9 @@ String initializeUserAgent(final AbfsConfiguration abfsConfiguration, | |
| sb.append(abfsConfiguration.getClusterType()); | ||
|
|
||
| // Add a unique identifier in FNS-Blob user agent string | ||
| if (!getIsNamespaceEnabled() | ||
| && abfsConfiguration.getFsConfiguredServiceType() == BLOB) { | ||
| // Current filesystem init restricts HNS-Blob combination | ||
| // so namespace check not required. | ||
| if (BLOB == abfsConfiguration.getFsConfiguredServiceType()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was the FNS check removed ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, we only support FNS over Blob endpoint. HNS-Blob will fail during init only.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense |
||
| sb.append(SEMICOLON) | ||
| .append(SINGLE_WHITE_SPACE) | ||
| .append(FNS_BLOB_USER_AGENT_IDENTIFIER); | ||
|
|
@@ -1724,16 +1726,19 @@ protected String getUserAgent() { | |
|
|
||
| /** | ||
| * Checks if the namespace is enabled. | ||
| * Filesystem init will fail if namespace is not correctly configured, | ||
| * so instead of swallowing the exception, we should throw the exception | ||
| * in case namespace is not configured correctly. | ||
| * | ||
| * @return True if the namespace is enabled, false otherwise. | ||
| * @throws AbfsDriverException if the conversion fails. | ||
| * @throws AzureBlobFileSystemException if the conversion fails. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be AbfsDriverException?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AzureBlobFileSystemException is parent of AbfsDriverException, so fine to throw it like this, |
||
| */ | ||
| public boolean getIsNamespaceEnabled() throws AbfsDriverException { | ||
| public boolean getIsNamespaceEnabled() throws AzureBlobFileSystemException { | ||
| try { | ||
| return abfsConfiguration.getIsNamespaceEnabledAccount().toBoolean(); | ||
| return getAbfsConfiguration().getIsNamespaceEnabledAccount().toBoolean(); | ||
| } catch (TrileanConversionException ex) { | ||
| LOG.error("Failed to convert namespace enabled account property to boolean", ex); | ||
| throw new AbfsDriverException("Failed to determine if namespace is enabled", ex); | ||
| throw new InvalidConfigurationValueException("Failed to determine account type", ex); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,7 @@ | |
| import org.apache.hadoop.fs.FileSystem; | ||
| import org.apache.hadoop.fs.azurebfs.constants.AbfsServiceType; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException; | ||
| import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException; | ||
| import org.apache.hadoop.fs.azurebfs.services.AbfsClient; | ||
| import org.apache.hadoop.fs.azurebfs.services.AbfsDfsClient; | ||
| import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation; | ||
|
|
@@ -159,7 +160,8 @@ public void testFailedRequestWhenFSNotExist() throws Exception { | |
| + testUri.substring(testUri.indexOf("@")); | ||
| config.setBoolean(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, isUsingXNSAccount); | ||
| AzureBlobFileSystem fs = this.getFileSystem(nonExistingFsUrl); | ||
| fs.getAbfsStore().setNamespaceEnabled(Trilean.UNKNOWN); | ||
| fs.getAbfsStore().getAbfsConfiguration() | ||
| .setIsNamespaceEnabledAccountForTesting(Trilean.UNKNOWN); | ||
|
|
||
| FileNotFoundException ex = intercept(FileNotFoundException.class, ()-> { | ||
| fs.getFileStatus(new Path("/")); // Run a dummy FS call | ||
|
|
@@ -207,7 +209,8 @@ public void testEnsureGetAclCallIsMadeOnceWhenConfigIsNotPresent() | |
| private void ensureGetAclCallIsMadeOnceForInvalidConf(String invalidConf) | ||
| throws Exception { | ||
| this.getFileSystem().getAbfsStore() | ||
| .setNamespaceEnabled(Trilean.getTrilean(invalidConf)); | ||
| .getAbfsConfiguration() | ||
| .setIsNamespaceEnabledAccountForTesting(Trilean.getTrilean(invalidConf)); | ||
| AbfsClient mockClient = | ||
| callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient(); | ||
| verify(mockClient, times(1)) | ||
|
|
@@ -217,15 +220,17 @@ private void ensureGetAclCallIsMadeOnceForInvalidConf(String invalidConf) | |
| private void ensureGetAclCallIsNeverMadeForValidConf(String validConf) | ||
| throws Exception { | ||
| this.getFileSystem().getAbfsStore() | ||
| .setNamespaceEnabled(Trilean.getTrilean(validConf)); | ||
| .getAbfsConfiguration() | ||
| .setIsNamespaceEnabledAccountForTesting(Trilean.getTrilean(validConf)); | ||
| AbfsClient mockClient = | ||
| callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient(); | ||
| verify(mockClient, never()) | ||
| .getAclStatus(anyString(), any(TracingContext.class)); | ||
| } | ||
|
|
||
| private void unsetConfAndEnsureGetAclCallIsMadeOnce() throws IOException { | ||
| this.getFileSystem().getAbfsStore().setNamespaceEnabled(Trilean.UNKNOWN); | ||
| this.getFileSystem().getAbfsStore() | ||
| .getAbfsConfiguration().setIsNamespaceEnabledAccountForTesting(Trilean.UNKNOWN); | ||
| AbfsClient mockClient = | ||
| callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient(); | ||
| verify(mockClient, times(1)) | ||
|
|
@@ -262,7 +267,7 @@ private void ensureGetAclDetermineHnsStatusAccuratelyInternal(int statusCode, | |
| boolean expectedValue, boolean isExceptionExpected) throws Exception { | ||
| AzureBlobFileSystemStore store = Mockito.spy(getFileSystem().getAbfsStore()); | ||
| AbfsClient mockClient = mock(AbfsClient.class); | ||
| store.setNamespaceEnabled(Trilean.UNKNOWN); | ||
| store.getAbfsConfiguration().setIsNamespaceEnabledAccountForTesting(Trilean.UNKNOWN); | ||
| doReturn(mockClient).when(store).getClient(AbfsServiceType.DFS); | ||
| AbfsRestOperationException ex = new AbfsRestOperationException( | ||
| statusCode, null, Integer.toString(statusCode), null); | ||
|
|
@@ -354,30 +359,123 @@ public void testAccountSpecificConfig() throws Exception { | |
| */ | ||
| @Test | ||
| public void testNameSpaceConfig() throws Exception { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here also validate that client returns the same value as what is set in the config
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. |
||
| Configuration configuration = new Configuration(); | ||
| configuration.unset(FS_AZURE_ACCOUNT_IS_HNS_ENABLED); | ||
| Configuration configuration = getConfigurationWithoutHnsConfig(); | ||
| AzureBlobFileSystem abfs = (AzureBlobFileSystem) FileSystem.newInstance(configuration); | ||
| AbfsConfiguration abfsConfig = new AbfsConfiguration(configuration, "bogusAccountName"); | ||
|
|
||
| // Test that the namespace value when config is not set | ||
| Assertions.assertThat(abfsConfig.getIsNamespaceEnabledAccount()) | ||
| .describedAs("Namespace enabled should be unknown in case config is not set") | ||
| .isEqualTo(Trilean.UNKNOWN); | ||
|
|
||
| // In case no namespace config is present, file system init calls getAcl() to determine account type. | ||
| Assertions.assertThat(abfs.getIsNamespaceEnabled(getTestTracingContext(abfs, false))) | ||
| .describedAs("getIsNamespaceEnabled should return account type based on getAcl() call") | ||
| .isEqualTo(abfs.getAbfsClient().getIsNamespaceEnabled()); | ||
|
|
||
| // In case no namespace config is present, file system init calls getAcl() to determine account type. | ||
| Assertions.assertThat(abfs.getAbfsStore().getAbfsConfiguration().getIsNamespaceEnabledAccount()) | ||
| .describedAs("getIsNamespaceEnabled() should return updated account type based on getAcl() call") | ||
| .isNotEqualTo(Trilean.UNKNOWN); | ||
|
|
||
| configuration.set(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, TRUE_STR); | ||
| abfs = (AzureBlobFileSystem) FileSystem.newInstance(configuration); | ||
| abfsConfig = new AbfsConfiguration(configuration, "bogusAccountName"); | ||
|
|
||
| // Test that the namespace enabled config is set correctly | ||
| Assertions.assertThat(abfsConfig.getIsNamespaceEnabledAccount()) | ||
| .describedAs("Namespace enabled should be true in case config is set to true") | ||
| .isEqualTo(Trilean.TRUE); | ||
|
|
||
| // In case namespace config is present, same value will be return. | ||
| Assertions.assertThat(abfs.getIsNamespaceEnabled(getTestTracingContext(abfs, false))) | ||
| .describedAs("getIsNamespaceEnabled() should return true when config is set to true") | ||
| .isEqualTo(true); | ||
|
|
||
| // In case namespace config is present, same value will be return. | ||
| Assertions.assertThat(abfs.getAbfsClient().getIsNamespaceEnabled()) | ||
| .describedAs("Client's getIsNamespaceEnabled() should return true when config is set to true") | ||
| .isEqualTo(true); | ||
|
|
||
| configuration.set(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, FALSE_STR); | ||
| abfs = (AzureBlobFileSystem) FileSystem.newInstance(configuration); | ||
| abfsConfig = new AbfsConfiguration(configuration, "bogusAccountName"); | ||
|
|
||
| // Test that the namespace enabled config is set correctly | ||
| Assertions.assertThat(abfsConfig.getIsNamespaceEnabledAccount()) | ||
| .describedAs("Namespace enabled should be false in case config is set to false") | ||
| .isEqualTo(Trilean.FALSE); | ||
|
|
||
| // In case namespace config is present, same value will be return. | ||
| Assertions.assertThat(abfs.getIsNamespaceEnabled(getTestTracingContext(abfs, false))) | ||
| .describedAs("getIsNamespaceEnabled() should return false when config is set to false") | ||
| .isEqualTo(false); | ||
|
|
||
| // In case namespace config is present, same value will be return. | ||
| Assertions.assertThat(abfs.getAbfsClient().getIsNamespaceEnabled()) | ||
| .describedAs("Client's getIsNamespaceEnabled() should return false when config is set to false") | ||
| .isEqualTo(false); | ||
| } | ||
|
|
||
| /** | ||
| * Tests to check that the namespace configuration is set correctly | ||
| * during the initialization of the AzureBlobFileSystem. | ||
| * | ||
| * | ||
| * @throws Exception if any error occurs during configuration setup or evaluation | ||
| */ | ||
| @Test | ||
| public void testFsInitShouldSetNamespaceConfig() throws Exception { | ||
| // Mock the AzureBlobFileSystem and its dependencies | ||
| AzureBlobFileSystem mockFileSystem = Mockito.spy((AzureBlobFileSystem) | ||
| FileSystem.newInstance(getConfigurationWithoutHnsConfig())); | ||
| AzureBlobFileSystemStore mockStore = Mockito.spy(mockFileSystem.getAbfsStore()); | ||
| AbfsClient abfsClient = Mockito.spy(mockStore.getClient()); | ||
| Mockito.doReturn(abfsClient).when(mockStore).getClient(); | ||
| Mockito.doReturn(abfsClient).when(mockStore).getClient(any()); | ||
| abfsClient.getIsNamespaceEnabled(); | ||
| // Verify that getAclStatus is called once during initialization | ||
| Mockito.verify(abfsClient, times(0)) | ||
| .getAclStatus(anyString(), any(TracingContext.class)); | ||
|
|
||
| mockStore.getAbfsConfiguration().setIsNamespaceEnabledAccountForTesting(Trilean.UNKNOWN); | ||
| // In case someone wrongly configured the namespace in between the processing, | ||
| // abfsclient.getIsNamespaceEnabled() should throw an exception. | ||
| String errorMessage = intercept(InvalidConfigurationValueException.class, () -> { | ||
| abfsClient.getIsNamespaceEnabled(); | ||
| }).getMessage(); | ||
| Assertions.assertThat(errorMessage) | ||
| .describedAs("Client should throw exception when namespace is unknown") | ||
| .contains("Failed to determine account type"); | ||
|
|
||
| // In case of unknown namespace, store's getIsNamespaceEnabled should call getAclStatus | ||
| // to determine the namespace status. | ||
| mockStore.getIsNamespaceEnabled(getTestTracingContext(mockFileSystem, false)); | ||
| Mockito.verify(abfsClient, times(1)) | ||
| .getAclStatus(anyString(), any(TracingContext.class)); | ||
|
|
||
| abfsClient.getIsNamespaceEnabled(); | ||
| // Verify that client's getNamespaceEnabled will not call getAclStatus again | ||
| Mockito.verify(abfsClient, times(1)) | ||
| .getAclStatus(anyString(), any(TracingContext.class)); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the configuration without the HNS config set. | ||
| * | ||
| * @return Configuration without HNS config | ||
| */ | ||
| private Configuration getConfigurationWithoutHnsConfig() { | ||
| Configuration rawConfig = new Configuration(); | ||
| rawConfig.addResource(TEST_CONFIGURATION_FILE_NAME); | ||
| rawConfig.unset(FS_AZURE_ACCOUNT_IS_HNS_ENABLED); | ||
| rawConfig.unset(accountProperty(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, | ||
| this.getAccountName())); | ||
| String testAccountName = "testAccount.dfs.core.windows.net"; | ||
| String defaultUri = this.getTestUrl().replace(this.getAccountName(), testAccountName); | ||
| // Assert that account specific config takes precedence | ||
| rawConfig.set(CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY, defaultUri); | ||
| return rawConfig; | ||
| } | ||
|
|
||
| private void assertFileSystemInitWithExpectedHNSSettings( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use true false variable from AbfsHttpConstants
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the boolean value, don't think we need to get it from AbfsHttpConstants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is optional as you mentioned.