HADOOP-19187: [ABFS][FNSOverBlob] AbfsClient Refactoring to Support Multiple Implementation of Clients. (#6879)
Refactor AbfsClient into DFS and Blob Client. Contributed by Anuj Modi
This commit is contained in:
parent
33c9ecb652
commit
b15ed27cfb
@ -44,6 +44,8 @@
|
|||||||
<suppressions>
|
<suppressions>
|
||||||
<suppress checks="ParameterNumber|MagicNumber"
|
<suppress checks="ParameterNumber|MagicNumber"
|
||||||
files="org[\\/]apache[\\/]hadoop[\\/]fs[\\/]azurebfs[\\/]AzureBlobFileSystemStore.java"/>
|
files="org[\\/]apache[\\/]hadoop[\\/]fs[\\/]azurebfs[\\/]AzureBlobFileSystemStore.java"/>
|
||||||
|
<suppress checks="ParameterNumber"
|
||||||
|
files="org[\\/]apache[\\/]hadoop[\\/]fs[\\/]azurebfs[\\/]services[\\/]AbfsClient.java"/>
|
||||||
<suppress checks="ParameterNumber|MagicNumber"
|
<suppress checks="ParameterNumber|MagicNumber"
|
||||||
files="org[\\/]apache[\\/]hadoop[\\/]fs[\\/]azurebfs[\\/]utils[\\/]Base64.java"/>
|
files="org[\\/]apache[\\/]hadoop[\\/]fs[\\/]azurebfs[\\/]utils[\\/]Base64.java"/>
|
||||||
<suppress checks="ParameterNumber|VisibilityModifier"
|
<suppress checks="ParameterNumber|VisibilityModifier"
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.VisibleForTesting;
|
import org.apache.hadoop.classification.VisibleForTesting;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.constants.AbfsServiceType;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.FixedSASTokenProvider;
|
import org.apache.hadoop.fs.azurebfs.services.FixedSASTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.HttpOperationType;
|
import org.apache.hadoop.fs.azurebfs.constants.HttpOperationType;
|
||||||
import org.apache.hadoop.fs.azurebfs.utils.MetricFormat;
|
import org.apache.hadoop.fs.azurebfs.utils.MetricFormat;
|
||||||
@ -74,6 +75,7 @@
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.EMPTY_STRING;
|
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.EMPTY_STRING;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.*;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.*;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.*;
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.*;
|
||||||
@ -87,6 +89,8 @@ public class AbfsConfiguration{
|
|||||||
|
|
||||||
private final Configuration rawConfig;
|
private final Configuration rawConfig;
|
||||||
private final String accountName;
|
private final String accountName;
|
||||||
|
// Service type identified from URL used to initialize FileSystem.
|
||||||
|
private final AbfsServiceType fsConfiguredServiceType;
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AbfsConfiguration.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AbfsConfiguration.class);
|
||||||
|
|
||||||
@ -94,6 +98,10 @@ public class AbfsConfiguration{
|
|||||||
DefaultValue = DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED)
|
DefaultValue = DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED)
|
||||||
private String isNamespaceEnabledAccount;
|
private String isNamespaceEnabledAccount;
|
||||||
|
|
||||||
|
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_ENABLE_DFSTOBLOB_FALLBACK,
|
||||||
|
DefaultValue = DEFAULT_FS_AZURE_ENABLE_DFSTOBLOB_FALLBACK)
|
||||||
|
private boolean isDfsToBlobFallbackEnabled;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_WRITE_MAX_CONCURRENT_REQUESTS,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_WRITE_MAX_CONCURRENT_REQUESTS,
|
||||||
DefaultValue = -1)
|
DefaultValue = -1)
|
||||||
private int writeMaxConcurrentRequestCount;
|
private int writeMaxConcurrentRequestCount;
|
||||||
@ -408,11 +416,14 @@ public class AbfsConfiguration{
|
|||||||
private String clientProvidedEncryptionKey;
|
private String clientProvidedEncryptionKey;
|
||||||
private String clientProvidedEncryptionKeySHA;
|
private String clientProvidedEncryptionKeySHA;
|
||||||
|
|
||||||
public AbfsConfiguration(final Configuration rawConfig, String accountName)
|
public AbfsConfiguration(final Configuration rawConfig,
|
||||||
throws IllegalAccessException, InvalidConfigurationValueException, IOException {
|
String accountName,
|
||||||
|
AbfsServiceType fsConfiguredServiceType)
|
||||||
|
throws IllegalAccessException, IOException {
|
||||||
this.rawConfig = ProviderUtils.excludeIncompatibleCredentialProviders(
|
this.rawConfig = ProviderUtils.excludeIncompatibleCredentialProviders(
|
||||||
rawConfig, AzureBlobFileSystem.class);
|
rawConfig, AzureBlobFileSystem.class);
|
||||||
this.accountName = accountName;
|
this.accountName = accountName;
|
||||||
|
this.fsConfiguredServiceType = fsConfiguredServiceType;
|
||||||
this.isSecure = getBoolean(FS_AZURE_SECURE_MODE, false);
|
this.isSecure = getBoolean(FS_AZURE_SECURE_MODE, false);
|
||||||
|
|
||||||
Field[] fields = this.getClass().getDeclaredFields();
|
Field[] fields = this.getClass().getDeclaredFields();
|
||||||
@ -434,10 +445,75 @@ public AbfsConfiguration(final Configuration rawConfig, String accountName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbfsConfiguration(final Configuration rawConfig, String accountName)
|
||||||
|
throws IllegalAccessException, IOException {
|
||||||
|
this(rawConfig, accountName, AbfsServiceType.DFS);
|
||||||
|
}
|
||||||
|
|
||||||
public Trilean getIsNamespaceEnabledAccount() {
|
public Trilean getIsNamespaceEnabledAccount() {
|
||||||
return Trilean.getTrilean(isNamespaceEnabledAccount);
|
return Trilean.getTrilean(isNamespaceEnabledAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the service type to be used based on the filesystem configuration.
|
||||||
|
* Precedence is given to service type configured for FNS Accounts using
|
||||||
|
* "fs.azure.fns.account.service.type". If not configured, then the service
|
||||||
|
* type identified from url used to initialize filesystem will be used.
|
||||||
|
* @return the service type.
|
||||||
|
*/
|
||||||
|
public AbfsServiceType getFsConfiguredServiceType() {
|
||||||
|
return getEnum(FS_AZURE_FNS_ACCOUNT_SERVICE_TYPE, fsConfiguredServiceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the service type configured for FNS Accounts to override the
|
||||||
|
* service type identified by URL used to initialize the filesystem.
|
||||||
|
* @return the service type.
|
||||||
|
*/
|
||||||
|
public AbfsServiceType getConfiguredServiceTypeForFNSAccounts() {
|
||||||
|
return getEnum(FS_AZURE_FNS_ACCOUNT_SERVICE_TYPE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the service type to be used for Ingress Operations irrespective of account type.
|
||||||
|
* Default value is the same as the service type configured for the file system.
|
||||||
|
* @return the service type.
|
||||||
|
*/
|
||||||
|
public AbfsServiceType getIngressServiceType() {
|
||||||
|
return getEnum(FS_AZURE_INGRESS_SERVICE_TYPE, getFsConfiguredServiceType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there is a need to move traffic from DFS to Blob.
|
||||||
|
* Needed when the service type is DFS and operations are experiencing compatibility issues.
|
||||||
|
* @return true if fallback enabled.
|
||||||
|
*/
|
||||||
|
public boolean isDfsToBlobFallbackEnabled() {
|
||||||
|
return isDfsToBlobFallbackEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the service type configured is valid for account type used.
|
||||||
|
* HNS Enabled accounts cannot have service type as BLOB.
|
||||||
|
* @param isHNSEnabled Flag to indicate if HNS is enabled for the account.
|
||||||
|
* @throws InvalidConfigurationValueException if the service type is invalid.
|
||||||
|
*/
|
||||||
|
public void validateConfiguredServiceType(boolean isHNSEnabled)
|
||||||
|
throws InvalidConfigurationValueException {
|
||||||
|
// Todo: [FnsOverBlob] - Remove this check, Failing FS Init with Blob Endpoint Until FNS over Blob is ready.
|
||||||
|
if (getFsConfiguredServiceType() == AbfsServiceType.BLOB) {
|
||||||
|
throw new InvalidConfigurationValueException(FS_DEFAULT_NAME_KEY,
|
||||||
|
"Blob Endpoint Support not yet available");
|
||||||
|
}
|
||||||
|
if (isHNSEnabled && getConfiguredServiceTypeForFNSAccounts() == AbfsServiceType.BLOB) {
|
||||||
|
throw new InvalidConfigurationValueException(
|
||||||
|
FS_AZURE_FNS_ACCOUNT_SERVICE_TYPE, "Cannot be BLOB for HNS Account");
|
||||||
|
} else if (isHNSEnabled && fsConfiguredServiceType == AbfsServiceType.BLOB) {
|
||||||
|
throw new InvalidConfigurationValueException(FS_DEFAULT_NAME_KEY,
|
||||||
|
"Blob Endpoint Url Cannot be used to initialize filesystem for HNS Account");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Azure Storage account name corresponding to this instance of configuration.
|
* Gets the Azure Storage account name corresponding to this instance of configuration.
|
||||||
* @return the Azure Storage account name
|
* @return the Azure Storage account name
|
||||||
@ -478,6 +554,7 @@ public String get(String key) {
|
|||||||
* Returns the account-specific value if it exists, then looks for an
|
* Returns the account-specific value if it exists, then looks for an
|
||||||
* account-agnostic value.
|
* account-agnostic value.
|
||||||
* @param key Account-agnostic configuration key
|
* @param key Account-agnostic configuration key
|
||||||
|
* @param defaultValue Value returned if none is configured
|
||||||
* @return value if one exists, else the default value
|
* @return value if one exists, else the default value
|
||||||
*/
|
*/
|
||||||
public String getString(String key, String defaultValue) {
|
public String getString(String key, String defaultValue) {
|
||||||
@ -522,7 +599,7 @@ public int getInt(String key, int defaultValue) {
|
|||||||
* looks for an account-agnostic value.
|
* looks for an account-agnostic value.
|
||||||
* @param key Account-agnostic configuration key
|
* @param key Account-agnostic configuration key
|
||||||
* @return value in String form if one exists, else null
|
* @return value in String form if one exists, else null
|
||||||
* @throws IOException
|
* @throws IOException if parsing fails.
|
||||||
*/
|
*/
|
||||||
public String getPasswordString(String key) throws IOException {
|
public String getPasswordString(String key) throws IOException {
|
||||||
char[] passchars = rawConfig.getPassword(accountConf(key));
|
char[] passchars = rawConfig.getPassword(accountConf(key));
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.hadoop.classification.VisibleForTesting;
|
import org.apache.hadoop.classification.VisibleForTesting;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException;
|
||||||
import org.apache.hadoop.fs.impl.BackReference;
|
import org.apache.hadoop.fs.impl.BackReference;
|
||||||
import org.apache.hadoop.security.ProviderUtils;
|
import org.apache.hadoop.security.ProviderUtils;
|
||||||
import org.apache.hadoop.util.Preconditions;
|
import org.apache.hadoop.util.Preconditions;
|
||||||
@ -109,13 +110,16 @@
|
|||||||
import org.apache.hadoop.util.LambdaUtils;
|
import org.apache.hadoop.util.LambdaUtils;
|
||||||
import org.apache.hadoop.util.Progressable;
|
import org.apache.hadoop.util.Progressable;
|
||||||
|
|
||||||
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
||||||
import static java.net.HttpURLConnection.HTTP_CONFLICT;
|
import static java.net.HttpURLConnection.HTTP_CONFLICT;
|
||||||
|
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
|
||||||
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL;
|
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL;
|
||||||
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL_DEFAULT;
|
import static org.apache.hadoop.fs.CommonConfigurationKeys.IOSTATISTICS_LOGGING_LEVEL_DEFAULT;
|
||||||
import static org.apache.hadoop.fs.Options.OpenFileOptions.FS_OPTION_OPENFILE_STANDARD_OPTIONS;
|
import static org.apache.hadoop.fs.Options.OpenFileOptions.FS_OPTION_OPENFILE_STANDARD_OPTIONS;
|
||||||
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.*;
|
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.*;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.CPK_IN_NON_HNS_ACCOUNT_ERROR_MESSAGE;
|
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.CPK_IN_NON_HNS_ACCOUNT_ERROR_MESSAGE;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.DATA_BLOCKS_BUFFER;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.DATA_BLOCKS_BUFFER;
|
||||||
|
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_BLOCK_UPLOAD_ACTIVE_BLOCKS;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BLOCK_UPLOAD_ACTIVE_BLOCKS;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BLOCK_UPLOAD_BUFFER_DIR;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BLOCK_UPLOAD_BUFFER_DIR;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.BLOCK_UPLOAD_ACTIVE_BLOCKS_DEFAULT;
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.BLOCK_UPLOAD_ACTIVE_BLOCKS_DEFAULT;
|
||||||
@ -213,6 +217,23 @@ public void initialize(URI uri, Configuration configuration)
|
|||||||
|
|
||||||
TracingContext tracingContext = new TracingContext(clientCorrelationId,
|
TracingContext tracingContext = new TracingContext(clientCorrelationId,
|
||||||
fileSystemId, FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat, listener);
|
fileSystemId, FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat, listener);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate the service type configured in the URI is valid for account type used.
|
||||||
|
* HNS Account Cannot have Blob Endpoint URI.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
abfsConfiguration.validateConfiguredServiceType(
|
||||||
|
tryGetIsNamespaceEnabled(new TracingContext(tracingContext)));
|
||||||
|
} catch (InvalidConfigurationValueException ex) {
|
||||||
|
LOG.debug("File system configured with Invalid Service Type", ex);
|
||||||
|
throw ex;
|
||||||
|
} catch (AzureBlobFileSystemException ex) {
|
||||||
|
LOG.debug("Failed to determine account type for service type validation", ex);
|
||||||
|
throw new InvalidConfigurationValueException(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file system if it does not exist.
|
||||||
if (abfsConfiguration.getCreateRemoteFileSystemDuringInitialization()) {
|
if (abfsConfiguration.getCreateRemoteFileSystemDuringInitialization()) {
|
||||||
if (this.tryGetFileStatus(new Path(AbfsHttpConstants.ROOT_PATH), tracingContext) == null) {
|
if (this.tryGetFileStatus(new Path(AbfsHttpConstants.ROOT_PATH), tracingContext) == null) {
|
||||||
try {
|
try {
|
||||||
@ -230,10 +251,7 @@ public void initialize(URI uri, Configuration configuration)
|
|||||||
*/
|
*/
|
||||||
if ((isEncryptionContextCPK(abfsConfiguration) || isGlobalKeyCPK(
|
if ((isEncryptionContextCPK(abfsConfiguration) || isGlobalKeyCPK(
|
||||||
abfsConfiguration))
|
abfsConfiguration))
|
||||||
&& !getIsNamespaceEnabled(
|
&& !getIsNamespaceEnabled(new TracingContext(tracingContext))) {
|
||||||
new TracingContext(clientCorrelationId, fileSystemId,
|
|
||||||
FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat,
|
|
||||||
listener))) {
|
|
||||||
/*
|
/*
|
||||||
* Close the filesystem gracefully before throwing exception. Graceful close
|
* Close the filesystem gracefully before throwing exception. Graceful close
|
||||||
* will ensure that all resources are released properly.
|
* will ensure that all resources are released properly.
|
||||||
@ -1400,6 +1418,34 @@ private FileStatus tryGetFileStatus(final Path f, TracingContext tracingContext)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function to check if the namespace is enabled on the storage account.
|
||||||
|
* If request fails with 4xx other than 400, it will be inferred as HNS.
|
||||||
|
* @param tracingContext tracing context
|
||||||
|
* @return true if namespace is enabled, false otherwise.
|
||||||
|
* @throws AzureBlobFileSystemException if any other error occurs.
|
||||||
|
*/
|
||||||
|
private boolean tryGetIsNamespaceEnabled(TracingContext tracingContext)
|
||||||
|
throws AzureBlobFileSystemException{
|
||||||
|
try {
|
||||||
|
return getIsNamespaceEnabled(tracingContext);
|
||||||
|
} catch (AbfsRestOperationException ex) {
|
||||||
|
/*
|
||||||
|
* Exception will be thrown for any non 400 error code.
|
||||||
|
* If status code is in 4xx range, it means it's an HNS account.
|
||||||
|
* If status code is in 5xx range, it means nothing can be inferred.
|
||||||
|
* In case of network errors status code will be -1.
|
||||||
|
*/
|
||||||
|
int statusCode = ex.getStatusCode();
|
||||||
|
if (statusCode > HTTP_BAD_REQUEST && statusCode < HTTP_INTERNAL_ERROR) {
|
||||||
|
LOG.debug("getNamespace failed with non 400 user error", ex);
|
||||||
|
statIncrement(ERROR_IGNORED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean fileSystemExists() throws IOException {
|
private boolean fileSystemExists() throws IOException {
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"AzureBlobFileSystem.fileSystemExists uri: {}", uri);
|
"AzureBlobFileSystem.fileSystemExists uri: {}", uri);
|
||||||
@ -1660,7 +1706,7 @@ AbfsDelegationTokenManager getDelegationTokenManager() {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean getIsNamespaceEnabled(TracingContext tracingContext)
|
boolean getIsNamespaceEnabled(TracingContext tracingContext)
|
||||||
throws AzureBlobFileSystemException {
|
throws AzureBlobFileSystemException {
|
||||||
return abfsStore.getIsNamespaceEnabled(tracingContext);
|
return getAbfsStore().getIsNamespaceEnabled(tracingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,11 +29,9 @@
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
|
||||||
import java.nio.charset.CharacterCodingException;
|
import java.nio.charset.CharacterCodingException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.CharsetDecoder;
|
import java.nio.charset.CharsetDecoder;
|
||||||
import java.nio.charset.CharsetEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -55,11 +53,13 @@
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.VisibleForTesting;
|
import org.apache.hadoop.classification.VisibleForTesting;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.constants.AbfsServiceType;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.EncryptionContextProvider;
|
import org.apache.hadoop.fs.azurebfs.extensions.EncryptionContextProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.security.ContextProviderEncryptionAdapter;
|
import org.apache.hadoop.fs.azurebfs.security.ContextProviderEncryptionAdapter;
|
||||||
import org.apache.hadoop.fs.azurebfs.security.ContextEncryptionAdapter;
|
import org.apache.hadoop.fs.azurebfs.security.ContextEncryptionAdapter;
|
||||||
import org.apache.hadoop.fs.azurebfs.security.NoContextEncryptionAdapter;
|
import org.apache.hadoop.fs.azurebfs.security.NoContextEncryptionAdapter;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AbfsClientHandler;
|
||||||
import org.apache.hadoop.fs.azurebfs.utils.EncryptionType;
|
import org.apache.hadoop.fs.azurebfs.utils.EncryptionType;
|
||||||
import org.apache.hadoop.fs.impl.BackReference;
|
import org.apache.hadoop.fs.impl.BackReference;
|
||||||
import org.apache.hadoop.fs.PathIOException;
|
import org.apache.hadoop.fs.PathIOException;
|
||||||
@ -158,6 +158,7 @@
|
|||||||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.AZURE_FOOTER_READ_BUFFER_SIZE;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.AZURE_FOOTER_READ_BUFFER_SIZE;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BUFFERED_PREAD_DISABLE;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_BUFFERED_PREAD_DISABLE;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_IDENTITY_TRANSFORM_CLASS;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_IDENTITY_TRANSFORM_CLASS;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes.ABFS_BLOB_DOMAIN_NAME;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.HttpHeaderConfigurations.X_MS_ENCRYPTION_CONTEXT;
|
import static org.apache.hadoop.fs.azurebfs.constants.HttpHeaderConfigurations.X_MS_ENCRYPTION_CONTEXT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,6 +170,13 @@ public class AzureBlobFileSystemStore implements Closeable, ListingSupport {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(AzureBlobFileSystemStore.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AzureBlobFileSystemStore.class);
|
||||||
|
|
||||||
private AbfsClient client;
|
private AbfsClient client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable to hold the client handler which will determine the operative
|
||||||
|
* client based on the service type configured.
|
||||||
|
* Initialized in the {@link #initializeClient(URI, String, String, boolean)}.
|
||||||
|
*/
|
||||||
|
private AbfsClientHandler clientHandler;
|
||||||
private URI uri;
|
private URI uri;
|
||||||
private String userName;
|
private String userName;
|
||||||
private String primaryUserGroup;
|
private String primaryUserGroup;
|
||||||
@ -221,7 +229,8 @@ public AzureBlobFileSystemStore(
|
|||||||
leaseRefs = Collections.synchronizedMap(new WeakHashMap<>());
|
leaseRefs = Collections.synchronizedMap(new WeakHashMap<>());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.abfsConfiguration = new AbfsConfiguration(abfsStoreBuilder.configuration, accountName);
|
this.abfsConfiguration = new AbfsConfiguration(abfsStoreBuilder.configuration,
|
||||||
|
accountName, getAbfsServiceTypeFromUrl());
|
||||||
} catch (IllegalAccessException exception) {
|
} catch (IllegalAccessException exception) {
|
||||||
throw new FileSystemOperationUnhandledException(exception);
|
throw new FileSystemOperationUnhandledException(exception);
|
||||||
}
|
}
|
||||||
@ -286,6 +295,8 @@ public AzureBlobFileSystemStore(
|
|||||||
/**
|
/**
|
||||||
* Checks if the given key in Azure Storage should be stored as a page
|
* Checks if the given key in Azure Storage should be stored as a page
|
||||||
* blob instead of block blob.
|
* blob instead of block blob.
|
||||||
|
* @param key The key to check.
|
||||||
|
* @return True if the key should be stored as a page blob, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isAppendBlobKey(String key) {
|
public boolean isAppendBlobKey(String key) {
|
||||||
return isKeyForDirectorySet(key, appendBlobDirSet);
|
return isKeyForDirectorySet(key, appendBlobDirSet);
|
||||||
@ -497,15 +508,9 @@ public void setFilesystemProperties(
|
|||||||
|
|
||||||
try (AbfsPerfInfo perfInfo = startTracking("setFilesystemProperties",
|
try (AbfsPerfInfo perfInfo = startTracking("setFilesystemProperties",
|
||||||
"setFilesystemProperties")) {
|
"setFilesystemProperties")) {
|
||||||
final String commaSeparatedProperties;
|
|
||||||
try {
|
|
||||||
commaSeparatedProperties = convertXmsPropertiesToCommaSeparatedString(properties);
|
|
||||||
} catch (CharacterCodingException ex) {
|
|
||||||
throw new InvalidAbfsRestOperationException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
final AbfsRestOperation op = client
|
final AbfsRestOperation op = getClient()
|
||||||
.setFilesystemProperties(commaSeparatedProperties, tracingContext);
|
.setFilesystemProperties(properties, tracingContext);
|
||||||
perfInfo.registerResult(op.getResult()).registerSuccess(true);
|
perfInfo.registerResult(op.getResult()).registerSuccess(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -590,18 +595,13 @@ public void setPathProperties(final Path path,
|
|||||||
path,
|
path,
|
||||||
properties);
|
properties);
|
||||||
|
|
||||||
final String commaSeparatedProperties;
|
|
||||||
try {
|
|
||||||
commaSeparatedProperties = convertXmsPropertiesToCommaSeparatedString(properties);
|
|
||||||
} catch (CharacterCodingException ex) {
|
|
||||||
throw new InvalidAbfsRestOperationException(ex);
|
|
||||||
}
|
|
||||||
final String relativePath = getRelativePath(path);
|
final String relativePath = getRelativePath(path);
|
||||||
final ContextEncryptionAdapter contextEncryptionAdapter
|
final ContextEncryptionAdapter contextEncryptionAdapter
|
||||||
= createEncryptionAdapterFromServerStoreContext(relativePath,
|
= createEncryptionAdapterFromServerStoreContext(relativePath,
|
||||||
tracingContext);
|
tracingContext);
|
||||||
final AbfsRestOperation op = client
|
final AbfsRestOperation op = client
|
||||||
.setPathProperties(getRelativePath(path), commaSeparatedProperties,
|
.setPathProperties(getRelativePath(path), properties,
|
||||||
tracingContext, contextEncryptionAdapter);
|
tracingContext, contextEncryptionAdapter);
|
||||||
contextEncryptionAdapter.destroy();
|
contextEncryptionAdapter.destroy();
|
||||||
perfInfo.registerResult(op.getResult()).registerSuccess(true);
|
perfInfo.registerResult(op.getResult()).registerSuccess(true);
|
||||||
@ -1810,18 +1810,29 @@ private void initializeClient(URI uri, String fileSystemName,
|
|||||||
|
|
||||||
LOG.trace("Initializing AbfsClient for {}", baseUrl);
|
LOG.trace("Initializing AbfsClient for {}", baseUrl);
|
||||||
if (tokenProvider != null) {
|
if (tokenProvider != null) {
|
||||||
this.client = new AbfsClient(baseUrl, creds, abfsConfiguration,
|
this.clientHandler = new AbfsClientHandler(baseUrl, creds, abfsConfiguration,
|
||||||
tokenProvider, encryptionContextProvider,
|
tokenProvider, encryptionContextProvider,
|
||||||
populateAbfsClientContext());
|
populateAbfsClientContext());
|
||||||
} else {
|
} else {
|
||||||
this.client = new AbfsClient(baseUrl, creds, abfsConfiguration,
|
this.clientHandler = new AbfsClientHandler(baseUrl, creds, abfsConfiguration,
|
||||||
sasTokenProvider, encryptionContextProvider,
|
sasTokenProvider, encryptionContextProvider,
|
||||||
populateAbfsClientContext());
|
populateAbfsClientContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.client = getClientHandler().getClient();
|
||||||
LOG.trace("AbfsClient init complete");
|
LOG.trace("AbfsClient init complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AbfsServiceType getAbfsServiceTypeFromUrl() {
|
||||||
|
if (uri.toString().contains(ABFS_BLOB_DOMAIN_NAME)) {
|
||||||
|
return AbfsServiceType.BLOB;
|
||||||
|
}
|
||||||
|
// In case of DFS Domain name or any other custom endpoint, the service
|
||||||
|
// type is to be identified as default DFS.
|
||||||
|
LOG.debug("Falling back to default service type DFS");
|
||||||
|
return AbfsServiceType.DFS;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate a new AbfsClientContext instance with the desired properties.
|
* Populate a new AbfsClientContext instance with the desired properties.
|
||||||
*
|
*
|
||||||
@ -1861,43 +1872,6 @@ private boolean parseIsDirectory(final String resourceType) {
|
|||||||
&& resourceType.equalsIgnoreCase(AbfsHttpConstants.DIRECTORY);
|
&& resourceType.equalsIgnoreCase(AbfsHttpConstants.DIRECTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert properties stored in a Map into a comma separated string. For map
|
|
||||||
* <key1:value1; key2:value2: keyN:valueN>, method would convert to:
|
|
||||||
* key1=value1,key2=value,...,keyN=valueN
|
|
||||||
* */
|
|
||||||
@VisibleForTesting
|
|
||||||
String convertXmsPropertiesToCommaSeparatedString(final Map<String,
|
|
||||||
String> properties) throws
|
|
||||||
CharacterCodingException {
|
|
||||||
StringBuilder commaSeparatedProperties = new StringBuilder();
|
|
||||||
|
|
||||||
final CharsetEncoder encoder = Charset.forName(XMS_PROPERTIES_ENCODING).newEncoder();
|
|
||||||
|
|
||||||
for (Map.Entry<String, String> propertyEntry : properties.entrySet()) {
|
|
||||||
String key = propertyEntry.getKey();
|
|
||||||
String value = propertyEntry.getValue();
|
|
||||||
|
|
||||||
Boolean canEncodeValue = encoder.canEncode(value);
|
|
||||||
if (!canEncodeValue) {
|
|
||||||
throw new CharacterCodingException();
|
|
||||||
}
|
|
||||||
|
|
||||||
String encodedPropertyValue = Base64.encode(encoder.encode(CharBuffer.wrap(value)).array());
|
|
||||||
commaSeparatedProperties.append(key)
|
|
||||||
.append(AbfsHttpConstants.EQUAL)
|
|
||||||
.append(encodedPropertyValue);
|
|
||||||
|
|
||||||
commaSeparatedProperties.append(AbfsHttpConstants.COMMA);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commaSeparatedProperties.length() != 0) {
|
|
||||||
commaSeparatedProperties.deleteCharAt(commaSeparatedProperties.length() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return commaSeparatedProperties.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Hashtable<String, String> parseCommaSeparatedXmsProperties(String xMsProperties) throws
|
private Hashtable<String, String> parseCommaSeparatedXmsProperties(String xMsProperties) throws
|
||||||
InvalidFileSystemPropertyException, InvalidAbfsRestOperationException {
|
InvalidFileSystemPropertyException, InvalidAbfsRestOperationException {
|
||||||
Hashtable<String, String> properties = new Hashtable<>();
|
Hashtable<String, String> properties = new Hashtable<>();
|
||||||
@ -2176,6 +2150,16 @@ public AbfsClient getClient() {
|
|||||||
return this.client;
|
return this.client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public AbfsClient getClient(AbfsServiceType serviceType) {
|
||||||
|
return getClientHandler().getClient(serviceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public AbfsClientHandler getClientHandler() {
|
||||||
|
return this.clientHandler;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setClient(AbfsClient client) {
|
void setClient(AbfsClient client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
@ -101,6 +101,8 @@ public final class AbfsHttpConstants {
|
|||||||
public static final String GMT_TIMEZONE = "GMT";
|
public static final String GMT_TIMEZONE = "GMT";
|
||||||
public static final String APPLICATION_JSON = "application/json";
|
public static final String APPLICATION_JSON = "application/json";
|
||||||
public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
|
public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
|
||||||
|
public static final String XMS_PROPERTIES_ENCODING_ASCII = "ISO-8859-1";
|
||||||
|
public static final String XMS_PROPERTIES_ENCODING_UNICODE = "UTF-8";
|
||||||
|
|
||||||
public static final String ROOT_PATH = "/";
|
public static final String ROOT_PATH = "/";
|
||||||
public static final String ACCESS_MASK = "mask:";
|
public static final String ACCESS_MASK = "mask:";
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.fs.azurebfs.constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure Storage Offers two sets of Rest APIs for interacting with the storage account.
|
||||||
|
* <ol>
|
||||||
|
* <li>Blob Rest API: <a href = https://learn.microsoft.com/en-us/rest/api/storageservices/blob-service-rest-api></a></li>
|
||||||
|
* <li>Data Lake Rest API: <a href = https://learn.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/operation-groups></a></li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
public enum AbfsServiceType {
|
||||||
|
/**
|
||||||
|
* Service type to set operative endpoint as Data Lake Rest API.
|
||||||
|
*/
|
||||||
|
DFS,
|
||||||
|
/**
|
||||||
|
* Service type to set operative endpoint as Blob Rest API.
|
||||||
|
*/
|
||||||
|
BLOB
|
||||||
|
}
|
@ -32,9 +32,35 @@ public final class ConfigurationKeys {
|
|||||||
/**
|
/**
|
||||||
* Config to specify if the configured account is HNS enabled or not. If
|
* 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
|
* this config is not set, getacl call is made on account filesystem root
|
||||||
* path to determine HNS status.
|
* path on DFS Endpoint 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_IS_HNS_ENABLED = "fs.azure.account.hns.enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config to specify which {@link AbfsServiceType} to use with HNS-Disabled Account type.
|
||||||
|
* Default value will be identified from URL used to initialize filesystem.
|
||||||
|
* This will allow an override to choose service endpoint in cases where any
|
||||||
|
* local DNS resolution is set for account and driver is unable to detect
|
||||||
|
* intended endpoint from the url used to initialize filesystem.
|
||||||
|
* If configured Blob for HNS-Enabled account, FS init will fail.
|
||||||
|
* Value {@value} case-insensitive "DFS" or "BLOB"
|
||||||
|
*/
|
||||||
|
public static final String FS_AZURE_FNS_ACCOUNT_SERVICE_TYPE = "fs.azure.fns.account.service.type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config to specify which {@link AbfsServiceType} to use only for Ingress Operations.
|
||||||
|
* Other operations will continue to move to the FS configured service endpoint.
|
||||||
|
* Value {@value} case-insensitive "DFS" or "BLOB"
|
||||||
|
*/
|
||||||
|
public static final String FS_AZURE_INGRESS_SERVICE_TYPE = "fs.azure.ingress.service.type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config to be set only for cases where traffic over dfs endpoint is
|
||||||
|
* experiencing compatibility issues and need to move to blob for mitigation.
|
||||||
|
* Value {@value} case-insensitive "True" or "False"
|
||||||
|
*/
|
||||||
|
public static final String FS_AZURE_ENABLE_DFSTOBLOB_FALLBACK = "fs.azure.enable.dfstoblob.fallback";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable expect hundred continue header.
|
* Enable or disable expect hundred continue header.
|
||||||
* Value: {@value}.
|
* Value: {@value}.
|
||||||
|
@ -45,7 +45,8 @@ public enum FSOperationType {
|
|||||||
SET_OWNER("SO"),
|
SET_OWNER("SO"),
|
||||||
SET_ACL("SA"),
|
SET_ACL("SA"),
|
||||||
TEST_OP("TS"),
|
TEST_OP("TS"),
|
||||||
WRITE("WR");
|
WRITE("WR"),
|
||||||
|
INIT("IN");
|
||||||
|
|
||||||
private final String opCode;
|
private final String opCode;
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
public final class FileSystemConfigurations {
|
public final class FileSystemConfigurations {
|
||||||
|
|
||||||
public static final String DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED = "";
|
public static final String DEFAULT_FS_AZURE_ACCOUNT_IS_HNS_ENABLED = "";
|
||||||
|
public static final boolean DEFAULT_FS_AZURE_ENABLE_DFSTOBLOB_FALLBACK = false;
|
||||||
public static final boolean DEFAULT_FS_AZURE_ACCOUNT_IS_EXPECT_HEADER_ENABLED = true;
|
public static final boolean DEFAULT_FS_AZURE_ACCOUNT_IS_EXPECT_HEADER_ENABLED = true;
|
||||||
public static final String USER_HOME_DIRECTORY_PREFIX = "/user";
|
public static final String USER_HOME_DIRECTORY_PREFIX = "/user";
|
||||||
|
|
||||||
|
@ -38,5 +38,8 @@ public final class FileSystemUriSchemes {
|
|||||||
public static final String WASB_SECURE_SCHEME = "wasbs";
|
public static final String WASB_SECURE_SCHEME = "wasbs";
|
||||||
public static final String WASB_DNS_PREFIX = "blob";
|
public static final String WASB_DNS_PREFIX = "blob";
|
||||||
|
|
||||||
|
public static final String ABFS_DFS_DOMAIN_NAME = "dfs.core.windows.net";
|
||||||
|
public static final String ABFS_BLOB_DOMAIN_NAME = "blob.core.windows.net";
|
||||||
|
|
||||||
private FileSystemUriSchemes() {}
|
private FileSystemUriSchemes() {}
|
||||||
}
|
}
|
@ -34,4 +34,8 @@ public InvalidConfigurationValueException(String configKey, Exception innerExcep
|
|||||||
public InvalidConfigurationValueException(String configKey) {
|
public InvalidConfigurationValueException(String configKey) {
|
||||||
super("Invalid configuration value detected for " + configKey);
|
super("Invalid configuration value detected for " + configKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InvalidConfigurationValueException(String configKey, String message) {
|
||||||
|
super(String.format("Invalid configuration value detected for \"%s\". %s ", configKey, message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.fs.azurebfs.services;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.constants.AbfsServiceType;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.extensions.EncryptionContextProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.extensions.SASTokenProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.utils.UriUtils.changeUrlFromBlobToDfs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbfsClientHandler is a class that provides a way to get the AbfsClient
|
||||||
|
* based on the service type.
|
||||||
|
*/
|
||||||
|
public class AbfsClientHandler {
|
||||||
|
public static final Logger LOG = LoggerFactory.getLogger(AbfsClientHandler.class);
|
||||||
|
|
||||||
|
private AbfsServiceType defaultServiceType;
|
||||||
|
private final AbfsDfsClient dfsAbfsClient;
|
||||||
|
|
||||||
|
public AbfsClientHandler(final URL baseUrl,
|
||||||
|
final SharedKeyCredentials sharedKeyCredentials,
|
||||||
|
final AbfsConfiguration abfsConfiguration,
|
||||||
|
final AccessTokenProvider tokenProvider,
|
||||||
|
final EncryptionContextProvider encryptionContextProvider,
|
||||||
|
final AbfsClientContext abfsClientContext) throws IOException {
|
||||||
|
this.dfsAbfsClient = createDfsClient(baseUrl, sharedKeyCredentials,
|
||||||
|
abfsConfiguration, tokenProvider, null, encryptionContextProvider,
|
||||||
|
abfsClientContext);
|
||||||
|
initServiceType(abfsConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbfsClientHandler(final URL baseUrl,
|
||||||
|
final SharedKeyCredentials sharedKeyCredentials,
|
||||||
|
final AbfsConfiguration abfsConfiguration,
|
||||||
|
final SASTokenProvider sasTokenProvider,
|
||||||
|
final EncryptionContextProvider encryptionContextProvider,
|
||||||
|
final AbfsClientContext abfsClientContext) throws IOException {
|
||||||
|
this.dfsAbfsClient = createDfsClient(baseUrl, sharedKeyCredentials,
|
||||||
|
abfsConfiguration, null, sasTokenProvider, encryptionContextProvider,
|
||||||
|
abfsClientContext);
|
||||||
|
initServiceType(abfsConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the default service type based on the user configuration.
|
||||||
|
* @param abfsConfiguration set by user.
|
||||||
|
*/
|
||||||
|
private void initServiceType(final AbfsConfiguration abfsConfiguration) {
|
||||||
|
this.defaultServiceType = abfsConfiguration.getFsConfiguredServiceType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the AbfsClient based on the default service type.
|
||||||
|
* @return AbfsClient
|
||||||
|
*/
|
||||||
|
public AbfsClient getClient() {
|
||||||
|
return getClient(defaultServiceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the AbfsClient based on the service type.
|
||||||
|
* @param serviceType AbfsServiceType
|
||||||
|
* @return AbfsClient
|
||||||
|
*/
|
||||||
|
public AbfsClient getClient(AbfsServiceType serviceType) {
|
||||||
|
return serviceType == AbfsServiceType.DFS ? dfsAbfsClient : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the AbfsDfsClient using the url used to configure file system.
|
||||||
|
* If URL is for Blob endpoint, it will be converted to DFS endpoint.
|
||||||
|
* @param baseUrl URL
|
||||||
|
* @param creds SharedKeyCredentials
|
||||||
|
* @param abfsConfiguration AbfsConfiguration
|
||||||
|
* @param tokenProvider AccessTokenProvider
|
||||||
|
* @param sasTokenProvider SASTokenProvider
|
||||||
|
* @param encryptionContextProvider EncryptionContextProvider
|
||||||
|
* @param abfsClientContext AbfsClientContext
|
||||||
|
* @return AbfsDfsClient with DFS endpoint URL
|
||||||
|
* @throws IOException if URL conversion fails.
|
||||||
|
*/
|
||||||
|
private AbfsDfsClient createDfsClient(final URL baseUrl,
|
||||||
|
final SharedKeyCredentials creds,
|
||||||
|
final AbfsConfiguration abfsConfiguration,
|
||||||
|
final AccessTokenProvider tokenProvider,
|
||||||
|
final SASTokenProvider sasTokenProvider,
|
||||||
|
final EncryptionContextProvider encryptionContextProvider,
|
||||||
|
final AbfsClientContext abfsClientContext) throws IOException {
|
||||||
|
URL dfsUrl = changeUrlFromBlobToDfs(baseUrl);
|
||||||
|
if (tokenProvider != null) {
|
||||||
|
LOG.debug("Creating AbfsDfsClient with access token provider using the URL: {}", dfsUrl);
|
||||||
|
return new AbfsDfsClient(dfsUrl, creds, abfsConfiguration,
|
||||||
|
tokenProvider, encryptionContextProvider,
|
||||||
|
abfsClientContext);
|
||||||
|
} else {
|
||||||
|
LOG.debug("Creating AbfsDfsClient with SAS token provider using the URL: {}", dfsUrl);
|
||||||
|
return new AbfsDfsClient(dfsUrl, creds, abfsConfiguration,
|
||||||
|
sasTokenProvider, encryptionContextProvider,
|
||||||
|
abfsClientContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,7 @@
|
|||||||
package org.apache.hadoop.fs.azurebfs.utils;
|
package org.apache.hadoop.fs.azurebfs.utils;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -30,11 +31,14 @@
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.apache.http.client.utils.URLEncodedUtils;
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
|
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.AND_MARK;
|
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.AND_MARK;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.EQUAL;
|
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.EQUAL;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes.ABFS_BLOB_DOMAIN_NAME;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes.ABFS_DFS_DOMAIN_NAME;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.QUERY_PARAM_SAOID;
|
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.QUERY_PARAM_SAOID;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.QUERY_PARAM_SIGNATURE;
|
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.QUERY_PARAM_SIGNATURE;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.QUERY_PARAM_SKOID;
|
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.QUERY_PARAM_SKOID;
|
||||||
@ -169,6 +173,38 @@ public static String getMaskedUrl(URL url) {
|
|||||||
return url.toString().replace(queryString, maskedQueryString);
|
return url.toString().replace(queryString, maskedQueryString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes Blob Endpoint URL to DFS Endpoint URL.
|
||||||
|
* If original url is not Blob Endpoint URL, it will return the original URL.
|
||||||
|
* @param url to be converted.
|
||||||
|
* @return updated URL
|
||||||
|
* @throws InvalidUriException in case of MalformedURLException.
|
||||||
|
*/
|
||||||
|
public static URL changeUrlFromBlobToDfs(URL url) throws InvalidUriException {
|
||||||
|
try {
|
||||||
|
url = new URL(url.toString().replace(ABFS_BLOB_DOMAIN_NAME, ABFS_DFS_DOMAIN_NAME));
|
||||||
|
} catch (MalformedURLException ex) {
|
||||||
|
throw new InvalidUriException(url.toString());
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes DFS Endpoint URL to Blob Endpoint URL.
|
||||||
|
* If original url is not DFS Endpoint URL, it will return the original URL.
|
||||||
|
* @param url to be converted.
|
||||||
|
* @return updated URL
|
||||||
|
* @throws InvalidUriException in case of MalformedURLException.
|
||||||
|
*/
|
||||||
|
public static URL changeUrlFromDfsToBlob(URL url) throws InvalidUriException {
|
||||||
|
try {
|
||||||
|
url = new URL(url.toString().replace(ABFS_DFS_DOMAIN_NAME, ABFS_BLOB_DOMAIN_NAME));
|
||||||
|
} catch (MalformedURLException ex) {
|
||||||
|
throw new InvalidUriException(url.toString());
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
private UriUtils() {
|
private UriUtils() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
82
hadoop-tools/hadoop-azure/src/site/markdown/fns_blob.md
Normal file
82
hadoop-tools/hadoop-azure/src/site/markdown/fns_blob.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<!---
|
||||||
|
Licensed 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. See accompanying LICENSE file.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# ABFS Driver for Namespace Disabled Accounts (FNS: Flat Namespace)
|
||||||
|
|
||||||
|
### Note: FNS-BLOB Support is being built and not yet ready for usage.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
The ABFS driver is recommended to be used only with HNS Enabled ADLS Gen-2 accounts
|
||||||
|
for big data analytics because of being more performant and scalable.
|
||||||
|
|
||||||
|
However, to enable users of legacy WASB Driver to migrate to ABFS driver without
|
||||||
|
needing them to upgrade their general purpose V2 accounts (HNS-Disabled), Support
|
||||||
|
for FNS accounts is being added to ABFS driver.
|
||||||
|
Refer to [WASB Deprication](./wasb.html) for more details.
|
||||||
|
|
||||||
|
## Azure Service Endpoints Used by ABFS Driver
|
||||||
|
Azure Services offers two set of endpoints for interacting with storage accounts:
|
||||||
|
1. [Azure Blob Storage](https://learn.microsoft.com/en-us/rest/api/storageservices/blob-service-rest-api) referred as Blob Endpoint
|
||||||
|
2. [Azure Data Lake Storage](https://learn.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/operation-groups) referred as DFS Endpoint
|
||||||
|
|
||||||
|
The ABFS Driver by default is designed to work with DFS Endpoint only which primarily
|
||||||
|
supports HNS Enabled Accounts only.
|
||||||
|
|
||||||
|
To enable ABFS Driver to work with FNS Accounts, Support for Blob Endpoint is being added.
|
||||||
|
This is because Azure services do not recommend using DFS Endpoint for FNS Accounts.
|
||||||
|
ABFS Driver will only allow FNS Accounts to be accessed using Blob Endpoint.
|
||||||
|
HNS Enabled accounts will still use DFS Endpoint which continues to be the
|
||||||
|
recommended stack based on performance and feature capabilities.
|
||||||
|
|
||||||
|
## Configuring ABFS Driver for FNS Accounts
|
||||||
|
Following configurations will be introduced to configure ABFS Driver for FNS Accounts:
|
||||||
|
1. Account Type: Must be set to `false` to indicate FNS Account
|
||||||
|
```xml
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.hns.enabled</name>
|
||||||
|
<value>false</value>
|
||||||
|
</property>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Account Url: It is the URL used to initialize the file system. It is either passed
|
||||||
|
directly to file system or configured as default uri using "fs.DefaultFS" configuration.
|
||||||
|
In both the cases the URL used must be the blob endpoint url of the account.
|
||||||
|
```xml
|
||||||
|
<property>
|
||||||
|
<name>fs.defaultFS</name>
|
||||||
|
<value>https://ACCOUNT_NAME.blob.core.windows.net</value>
|
||||||
|
</property>
|
||||||
|
```
|
||||||
|
3. Service Type for FNS Accounts: This will allow an override to choose service
|
||||||
|
type specially in cases where any local DNS resolution is set for the account and driver is
|
||||||
|
unable to detect the intended endpoint from above configured URL. If this is set
|
||||||
|
to blob for HNS Enabled Accounts, FS init will fail with InvalidConfiguration error.
|
||||||
|
```xml
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.fns.account.service.type</name>
|
||||||
|
<value>BLOB</value>
|
||||||
|
</property>
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Service Type for Ingress Operations: This will allow an override to choose service
|
||||||
|
type only for Ingress Related Operations like [Create](https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob?tabs=microsoft-entra-id),
|
||||||
|
[Append](https://learn.microsoft.com/en-us/rest/api/storageservices/put-block?tabs=microsoft-entra-id)
|
||||||
|
and [Flush](https://learn.microsoft.com/en-us/rest/api/storageservices/put-block-list?tabs=microsoft-entra-id). All other operations will still use the
|
||||||
|
configured service type.
|
||||||
|
```xml
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.fns.account.service.type</name>
|
||||||
|
<value>BLOB</value>
|
||||||
|
</property>
|
||||||
|
```
|
@ -20,6 +20,7 @@ See also:
|
|||||||
|
|
||||||
* [WASB](./wasb.html)
|
* [WASB](./wasb.html)
|
||||||
* [ABFS](./abfs.html)
|
* [ABFS](./abfs.html)
|
||||||
|
* [Namespace Disabled Accounts on ABFS](./fns_blob.html)
|
||||||
* [Testing](./testing_azure.html)
|
* [Testing](./testing_azure.html)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
@ -339,8 +339,7 @@ private AbfsRestOperation callOperation(AzureBlobFileSystem fs,
|
|||||||
case SET_ATTR:
|
case SET_ATTR:
|
||||||
Hashtable<String, String> properties = new Hashtable<>();
|
Hashtable<String, String> properties = new Hashtable<>();
|
||||||
properties.put("key", "{ value: valueTest }");
|
properties.put("key", "{ value: valueTest }");
|
||||||
return client.setPathProperties(path, fs.getAbfsStore()
|
return client.setPathProperties(path, properties,
|
||||||
.convertXmsPropertiesToCommaSeparatedString(properties),
|
|
||||||
getTestTracingContext(fs, false),
|
getTestTracingContext(fs, false),
|
||||||
createEncryptionAdapterFromServerStoreContext(path,
|
createEncryptionAdapterFromServerStoreContext(path,
|
||||||
getTestTracingContext(fs, false), client));
|
getTestTracingContext(fs, false), client));
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.enums.Trilean;
|
||||||
import org.apache.hadoop.util.Lists;
|
import org.apache.hadoop.util.Lists;
|
||||||
import org.junit.Assume;
|
import org.junit.Assume;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -97,8 +98,12 @@ private void setTestUserFs() throws Exception {
|
|||||||
+ getAccountName(), ClientCredsTokenProvider.class.getName());
|
+ getAccountName(), ClientCredsTokenProvider.class.getName());
|
||||||
conf.setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION,
|
conf.setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION,
|
||||||
false);
|
false);
|
||||||
conf.unset(FS_AZURE_ACCOUNT_IS_HNS_ENABLED);
|
// Since FS init now needs to know account type setting it before init to avoid that.
|
||||||
|
conf.setBoolean(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, isHNSEnabled);
|
||||||
this.testUserFs = FileSystem.newInstance(conf);
|
this.testUserFs = FileSystem.newInstance(conf);
|
||||||
|
// Resetting the namespace enabled flag to unknown after file system init.
|
||||||
|
((AzureBlobFileSystem) testUserFs).getAbfsStore().setNamespaceEnabled(
|
||||||
|
Trilean.UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTestFsConf(final String fsConfKey,
|
private void setTestFsConf(final String fsConfKey,
|
||||||
@ -306,11 +311,11 @@ public void testFsActionALL() throws Exception {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkPrerequisites() throws Exception {
|
private void checkPrerequisites() throws Exception {
|
||||||
setTestUserFs();
|
|
||||||
Assume.assumeTrue(FS_AZURE_TEST_NAMESPACE_ENABLED_ACCOUNT + " is false",
|
Assume.assumeTrue(FS_AZURE_TEST_NAMESPACE_ENABLED_ACCOUNT + " is false",
|
||||||
isHNSEnabled);
|
isHNSEnabled);
|
||||||
Assume.assumeTrue(FS_AZURE_ENABLE_CHECK_ACCESS + " is false",
|
Assume.assumeTrue(FS_AZURE_ENABLE_CHECK_ACCESS + " is false",
|
||||||
isCheckAccessEnabled);
|
isCheckAccessEnabled);
|
||||||
|
setTestUserFs();
|
||||||
checkIfConfigIsSet(FS_AZURE_BLOB_FS_CHECKACCESS_TEST_CLIENT_ID);
|
checkIfConfigIsSet(FS_AZURE_BLOB_FS_CHECKACCESS_TEST_CLIENT_ID);
|
||||||
checkIfConfigIsSet(FS_AZURE_BLOB_FS_CHECKACCESS_TEST_CLIENT_SECRET);
|
checkIfConfigIsSet(FS_AZURE_BLOB_FS_CHECKACCESS_TEST_CLIENT_SECRET);
|
||||||
checkIfConfigIsSet(FS_AZURE_BLOB_FS_CHECKACCESS_TEST_USER_GUID);
|
checkIfConfigIsSet(FS_AZURE_BLOB_FS_CHECKACCESS_TEST_USER_GUID);
|
||||||
|
@ -20,19 +20,32 @@
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.FileStatus;
|
import org.apache.hadoop.fs.FileStatus;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
||||||
|
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.InvalidConfigurationValueException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TrileanConversionException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TrileanConversionException;
|
||||||
import org.apache.hadoop.fs.azurebfs.enums.Trilean;
|
import org.apache.hadoop.fs.azurebfs.enums.Trilean;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
||||||
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
|
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
|
||||||
|
|
||||||
|
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
|
||||||
|
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ACCOUNT_IS_HNS_ENABLED;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes.ABFS_BLOB_DOMAIN_NAME;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes.ABFS_DFS_DOMAIN_NAME;
|
||||||
|
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test filesystem initialization and creation.
|
* Test filesystem initialization and creation.
|
||||||
*/
|
*/
|
||||||
@ -73,11 +86,11 @@ public void testGetAclCallOnHnsConfigAbsence() throws Exception {
|
|||||||
TracingContext tracingContext = getSampleTracingContext(fs, true);
|
TracingContext tracingContext = getSampleTracingContext(fs, true);
|
||||||
Mockito.doReturn(Mockito.mock(AbfsRestOperation.class))
|
Mockito.doReturn(Mockito.mock(AbfsRestOperation.class))
|
||||||
.when(client)
|
.when(client)
|
||||||
.getAclStatus(Mockito.anyString(), Mockito.any(TracingContext.class));
|
.getAclStatus(Mockito.anyString(), any(TracingContext.class));
|
||||||
store.getIsNamespaceEnabled(tracingContext);
|
store.getIsNamespaceEnabled(tracingContext);
|
||||||
|
|
||||||
Mockito.verify(client, Mockito.times(1))
|
Mockito.verify(client, Mockito.times(1))
|
||||||
.getAclStatus(Mockito.anyString(), Mockito.any(TracingContext.class));
|
.getAclStatus(Mockito.anyString(), any(TracingContext.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -96,6 +109,31 @@ public void testNoGetAclCallOnHnsConfigPresence() throws Exception {
|
|||||||
store.getIsNamespaceEnabled(tracingContext);
|
store.getIsNamespaceEnabled(tracingContext);
|
||||||
|
|
||||||
Mockito.verify(client, Mockito.times(0))
|
Mockito.verify(client, Mockito.times(0))
|
||||||
.getAclStatus(Mockito.anyString(), Mockito.any(TracingContext.class));
|
.getAclStatus(Mockito.anyString(), any(TracingContext.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: [FnsOverBlob] Remove this test case once Blob Endpoint Support is ready and enabled.
|
||||||
|
@Test
|
||||||
|
public void testFileSystemInitFailsWithBlobEndpoitUrl() throws Exception {
|
||||||
|
Configuration configuration = getRawConfiguration();
|
||||||
|
String defaultUri = configuration.get(FS_DEFAULT_NAME_KEY);
|
||||||
|
String blobUri = defaultUri.replace(ABFS_DFS_DOMAIN_NAME, ABFS_BLOB_DOMAIN_NAME);
|
||||||
|
intercept(InvalidConfigurationValueException.class,
|
||||||
|
"Blob Endpoint Support not yet available", () ->
|
||||||
|
FileSystem.newInstance(new Path(blobUri).toUri(), configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileSystemInitFailsIfNotAbleToDetermineAccountType() throws Exception {
|
||||||
|
AzureBlobFileSystem fs = ((AzureBlobFileSystem) FileSystem.newInstance(
|
||||||
|
getRawConfiguration()));
|
||||||
|
AzureBlobFileSystem mockedFs = Mockito.spy(fs);
|
||||||
|
Mockito.doThrow(
|
||||||
|
new AbfsRestOperationException(HTTP_UNAVAILABLE, "Throttled",
|
||||||
|
"Throttled", null)).when(mockedFs).getIsNamespaceEnabled(any());
|
||||||
|
|
||||||
|
intercept(AzureBlobFileSystemException.class,
|
||||||
|
FS_AZURE_ACCOUNT_IS_HNS_ENABLED, () ->
|
||||||
|
mockedFs.initialize(fs.getUri(), getRawConfiguration()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AbfsDfsClient;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
@ -140,12 +141,15 @@ private String getNonExistingUrl() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailedRequestWhenFSNotExist() throws Exception {
|
public void testFailedRequestWhenFSNotExist() throws Exception {
|
||||||
|
assumeValidTestConfigPresent(getRawConfiguration(), FS_AZURE_TEST_NAMESPACE_ENABLED_ACCOUNT);
|
||||||
AbfsConfiguration config = this.getConfiguration();
|
AbfsConfiguration config = this.getConfiguration();
|
||||||
config.setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION, false);
|
config.setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION, false);
|
||||||
String testUri = this.getTestUrl();
|
String testUri = this.getTestUrl();
|
||||||
String nonExistingFsUrl = getAbfsScheme() + "://" + UUID.randomUUID()
|
String nonExistingFsUrl = getAbfsScheme() + "://" + UUID.randomUUID()
|
||||||
+ testUri.substring(testUri.indexOf("@"));
|
+ testUri.substring(testUri.indexOf("@"));
|
||||||
|
config.setBoolean(FS_AZURE_ACCOUNT_IS_HNS_ENABLED, isUsingXNSAccount);
|
||||||
AzureBlobFileSystem fs = this.getFileSystem(nonExistingFsUrl);
|
AzureBlobFileSystem fs = this.getFileSystem(nonExistingFsUrl);
|
||||||
|
fs.getAbfsStore().setNamespaceEnabled(Trilean.UNKNOWN);
|
||||||
|
|
||||||
intercept(FileNotFoundException.class,
|
intercept(FileNotFoundException.class,
|
||||||
"\"The specified filesystem does not exist.\", 404",
|
"\"The specified filesystem does not exist.\", 404",
|
||||||
@ -214,12 +218,14 @@ private void unsetConfAndEnsureGetAclCallIsMadeOnce() throws IOException {
|
|||||||
|
|
||||||
private AbfsClient callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient()
|
private AbfsClient callAbfsGetIsNamespaceEnabledAndReturnMockAbfsClient()
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final AzureBlobFileSystem abfs = this.getFileSystem();
|
final AzureBlobFileSystem abfs = Mockito.spy(this.getFileSystem());
|
||||||
final AzureBlobFileSystemStore abfsStore = abfs.getAbfsStore();
|
final AzureBlobFileSystemStore abfsStore = Mockito.spy(abfs.getAbfsStore());
|
||||||
final AbfsClient mockClient = mock(AbfsClient.class);
|
final AbfsClient mockClient = mock(AbfsDfsClient.class);
|
||||||
|
doReturn(abfsStore).when(abfs).getAbfsStore();
|
||||||
|
doReturn(mockClient).when(abfsStore).getClient();
|
||||||
|
doReturn(mockClient).when(abfsStore).getClient(any());
|
||||||
doReturn(mock(AbfsRestOperation.class)).when(mockClient)
|
doReturn(mock(AbfsRestOperation.class)).when(mockClient)
|
||||||
.getAclStatus(anyString(), any(TracingContext.class));
|
.getAclStatus(anyString(), any(TracingContext.class));
|
||||||
abfsStore.setClient(mockClient);
|
|
||||||
getIsNamespaceEnabled(abfs);
|
getIsNamespaceEnabled(abfs);
|
||||||
return mockClient;
|
return mockClient;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,8 @@ private String getUserAgentString(AbfsConfiguration config,
|
|||||||
boolean includeSSLProvider) throws IOException, URISyntaxException {
|
boolean includeSSLProvider) throws IOException, URISyntaxException {
|
||||||
AbfsCounters abfsCounters = Mockito.spy(new AbfsCountersImpl(new URI("abcd")));
|
AbfsCounters abfsCounters = Mockito.spy(new AbfsCountersImpl(new URI("abcd")));
|
||||||
AbfsClientContext abfsClientContext = new AbfsClientContextBuilder().withAbfsCounters(abfsCounters).build();
|
AbfsClientContext abfsClientContext = new AbfsClientContextBuilder().withAbfsCounters(abfsCounters).build();
|
||||||
AbfsClient client = new AbfsClient(new URL("https://azure.com"), null,
|
// Todo : [FnsOverBlob] Update to work with Blob Endpoint as well when Fns Over Blob is ready.
|
||||||
|
AbfsClient client = new AbfsDfsClient(new URL("https://azure.com"), null,
|
||||||
config, (AccessTokenProvider) null, null, abfsClientContext);
|
config, (AccessTokenProvider) null, null, abfsClientContext);
|
||||||
String sslProviderName = null;
|
String sslProviderName = null;
|
||||||
if (includeSSLProvider) {
|
if (includeSSLProvider) {
|
||||||
@ -363,7 +364,8 @@ public static AbfsClient createTestClientFromCurrentContext(
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Create test AbfsClient
|
// Create test AbfsClient
|
||||||
AbfsClient testClient = new AbfsClient(
|
// Todo : [FnsOverBlob] Update to work with Blob Endpoint as well when Fns Over Blob is ready.
|
||||||
|
AbfsClient testClient = new AbfsDfsClient(
|
||||||
baseAbfsClientInstance.getBaseUrl(),
|
baseAbfsClientInstance.getBaseUrl(),
|
||||||
(currentAuthType == AuthType.SharedKey
|
(currentAuthType == AuthType.SharedKey
|
||||||
? new SharedKeyCredentials(
|
? new SharedKeyCredentials(
|
||||||
@ -391,7 +393,8 @@ public static AbfsClient getMockAbfsClient(AbfsClient baseAbfsClientInstance,
|
|||||||
(currentAuthType == AuthType.SharedKey)
|
(currentAuthType == AuthType.SharedKey)
|
||||||
|| (currentAuthType == AuthType.OAuth));
|
|| (currentAuthType == AuthType.OAuth));
|
||||||
|
|
||||||
AbfsClient client = mock(AbfsClient.class);
|
// Todo : [FnsOverBlob] Update to work with Blob Endpoint as well when Fns Over Blob is ready.
|
||||||
|
AbfsClient client = mock(AbfsDfsClient.class);
|
||||||
AbfsPerfTracker tracker = new AbfsPerfTracker(
|
AbfsPerfTracker tracker = new AbfsPerfTracker(
|
||||||
"test",
|
"test",
|
||||||
abfsConfig.getAccountName(),
|
abfsConfig.getAccountName(),
|
||||||
|
Loading…
Reference in New Issue
Block a user