HADOOP-15660. ABFS: Add support for OAuth
Contributed by Da Zhou, Rajeev Bansal, and Junhua Gu.
This commit is contained in:
parent
d6a4f39bd5
commit
9149b9703e
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs;
|
package org.apache.hadoop.fs.azurebfs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -26,7 +27,6 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations;
|
import org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.annotations.ConfigurationValidationAnnotations.IntegerConfigurationValidatorAnnotation;
|
import org.apache.hadoop.fs.azurebfs.contracts.annotations.ConfigurationValidationAnnotations.IntegerConfigurationValidatorAnnotation;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.annotations.ConfigurationValidationAnnotations.LongConfigurationValidatorAnnotation;
|
import org.apache.hadoop.fs.azurebfs.contracts.annotations.ConfigurationValidationAnnotations.LongConfigurationValidatorAnnotation;
|
||||||
|
@ -37,16 +37,26 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemExc
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.ConfigurationPropertyNotFoundException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.ConfigurationPropertyNotFoundException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.KeyProviderException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.KeyProviderException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TokenAccessProviderException;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.Base64StringConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.Base64StringConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.BooleanConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.BooleanConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.IntegerConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.IntegerConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.LongConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.LongConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.StringConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.StringConfigurationBasicValidator;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.CustomTokenProviderAdaptee;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.CustomTokenProviderAdapter;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.MsiTokenProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.RefreshTokenBasedTokenProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.UserPasswordTokenProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.KeyProvider;
|
import org.apache.hadoop.fs.azurebfs.services.KeyProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.SimpleKeyProvider;
|
import org.apache.hadoop.fs.azurebfs.services.SimpleKeyProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.utils.SSLSocketFactoryEx;
|
import org.apache.hadoop.fs.azurebfs.utils.SSLSocketFactoryEx;
|
||||||
|
import org.apache.hadoop.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_SSL_CHANNEL_MODE_KEY;
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.*;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.DEFAULT_FS_AZURE_SSL_CHANNEL_MODE;
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.DEFAULT_FS_AZURE_SSL_CHANNEL_MODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,81 +68,81 @@ public class AbfsConfiguration{
|
||||||
private final Configuration configuration;
|
private final Configuration configuration;
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_WRITE_BUFFER_SIZE,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_WRITE_BUFFER_SIZE,
|
||||||
MinValue = FileSystemConfigurations.MIN_BUFFER_SIZE,
|
MinValue = FileSystemConfigurations.MIN_BUFFER_SIZE,
|
||||||
MaxValue = FileSystemConfigurations.MAX_BUFFER_SIZE,
|
MaxValue = FileSystemConfigurations.MAX_BUFFER_SIZE,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_WRITE_BUFFER_SIZE)
|
DefaultValue = FileSystemConfigurations.DEFAULT_WRITE_BUFFER_SIZE)
|
||||||
private int writeBufferSize;
|
private int writeBufferSize;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_READ_BUFFER_SIZE,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_READ_BUFFER_SIZE,
|
||||||
MinValue = FileSystemConfigurations.MIN_BUFFER_SIZE,
|
MinValue = FileSystemConfigurations.MIN_BUFFER_SIZE,
|
||||||
MaxValue = FileSystemConfigurations.MAX_BUFFER_SIZE,
|
MaxValue = FileSystemConfigurations.MAX_BUFFER_SIZE,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_READ_BUFFER_SIZE)
|
DefaultValue = FileSystemConfigurations.DEFAULT_READ_BUFFER_SIZE)
|
||||||
private int readBufferSize;
|
private int readBufferSize;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_MIN_BACKOFF_INTERVAL,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_MIN_BACKOFF_INTERVAL,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_MIN_BACKOFF_INTERVAL)
|
DefaultValue = FileSystemConfigurations.DEFAULT_MIN_BACKOFF_INTERVAL)
|
||||||
private int minBackoffInterval;
|
private int minBackoffInterval;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_MAX_BACKOFF_INTERVAL,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_MAX_BACKOFF_INTERVAL,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_MAX_BACKOFF_INTERVAL)
|
DefaultValue = FileSystemConfigurations.DEFAULT_MAX_BACKOFF_INTERVAL)
|
||||||
private int maxBackoffInterval;
|
private int maxBackoffInterval;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_BACKOFF_INTERVAL,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_BACKOFF_INTERVAL,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_BACKOFF_INTERVAL)
|
DefaultValue = FileSystemConfigurations.DEFAULT_BACKOFF_INTERVAL)
|
||||||
private int backoffInterval;
|
private int backoffInterval;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_MAX_IO_RETRIES,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_MAX_IO_RETRIES,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_MAX_RETRY_ATTEMPTS)
|
DefaultValue = FileSystemConfigurations.DEFAULT_MAX_RETRY_ATTEMPTS)
|
||||||
private int maxIoRetries;
|
private int maxIoRetries;
|
||||||
|
|
||||||
@LongConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_BLOCK_SIZE_PROPERTY_NAME,
|
@LongConfigurationValidatorAnnotation(ConfigurationKey = AZURE_BLOCK_SIZE_PROPERTY_NAME,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
MaxValue = FileSystemConfigurations.MAX_AZURE_BLOCK_SIZE,
|
MaxValue = FileSystemConfigurations.MAX_AZURE_BLOCK_SIZE,
|
||||||
DefaultValue = FileSystemConfigurations.MAX_AZURE_BLOCK_SIZE)
|
DefaultValue = FileSystemConfigurations.MAX_AZURE_BLOCK_SIZE)
|
||||||
private long azureBlockSize;
|
private long azureBlockSize;
|
||||||
|
|
||||||
@StringConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_BLOCK_LOCATION_HOST_PROPERTY_NAME,
|
@StringConfigurationValidatorAnnotation(ConfigurationKey = AZURE_BLOCK_LOCATION_HOST_PROPERTY_NAME,
|
||||||
DefaultValue = FileSystemConfigurations.AZURE_BLOCK_LOCATION_HOST_DEFAULT)
|
DefaultValue = FileSystemConfigurations.AZURE_BLOCK_LOCATION_HOST_DEFAULT)
|
||||||
private String azureBlockLocationHost;
|
private String azureBlockLocationHost;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_CONCURRENT_CONNECTION_VALUE_OUT,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_CONCURRENT_CONNECTION_VALUE_OUT,
|
||||||
MinValue = 1,
|
MinValue = 1,
|
||||||
DefaultValue = FileSystemConfigurations.MAX_CONCURRENT_WRITE_THREADS)
|
DefaultValue = FileSystemConfigurations.MAX_CONCURRENT_WRITE_THREADS)
|
||||||
private int maxConcurrentWriteThreads;
|
private int maxConcurrentWriteThreads;
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_CONCURRENT_CONNECTION_VALUE_IN,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = AZURE_CONCURRENT_CONNECTION_VALUE_IN,
|
||||||
MinValue = 1,
|
MinValue = 1,
|
||||||
DefaultValue = FileSystemConfigurations.MAX_CONCURRENT_READ_THREADS)
|
DefaultValue = FileSystemConfigurations.MAX_CONCURRENT_READ_THREADS)
|
||||||
private int maxConcurrentReadThreads;
|
private int maxConcurrentReadThreads;
|
||||||
|
|
||||||
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_TOLERATE_CONCURRENT_APPEND,
|
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = AZURE_TOLERATE_CONCURRENT_APPEND,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_READ_TOLERATE_CONCURRENT_APPEND)
|
DefaultValue = FileSystemConfigurations.DEFAULT_READ_TOLERATE_CONCURRENT_APPEND)
|
||||||
private boolean tolerateOobAppends;
|
private boolean tolerateOobAppends;
|
||||||
|
|
||||||
@StringConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.FS_AZURE_ATOMIC_RENAME_KEY,
|
@StringConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_ATOMIC_RENAME_KEY,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_FS_AZURE_ATOMIC_RENAME_DIRECTORIES)
|
DefaultValue = FileSystemConfigurations.DEFAULT_FS_AZURE_ATOMIC_RENAME_DIRECTORIES)
|
||||||
private String azureAtomicDirs;
|
private String azureAtomicDirs;
|
||||||
|
|
||||||
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION,
|
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION)
|
DefaultValue = FileSystemConfigurations.DEFAULT_AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION)
|
||||||
private boolean createRemoteFileSystemDuringInitialization;
|
private boolean createRemoteFileSystemDuringInitialization;
|
||||||
|
|
||||||
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.AZURE_SKIP_USER_GROUP_METADATA_DURING_INITIALIZATION,
|
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = AZURE_SKIP_USER_GROUP_METADATA_DURING_INITIALIZATION,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_AZURE_SKIP_USER_GROUP_METADATA_DURING_INITIALIZATION)
|
DefaultValue = FileSystemConfigurations.DEFAULT_AZURE_SKIP_USER_GROUP_METADATA_DURING_INITIALIZATION)
|
||||||
private boolean skipUserGroupMetadataDuringInitialization;
|
private boolean skipUserGroupMetadataDuringInitialization;
|
||||||
|
|
||||||
|
|
||||||
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.FS_AZURE_READ_AHEAD_QUEUE_DEPTH,
|
@IntegerConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_READ_AHEAD_QUEUE_DEPTH,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_READ_AHEAD_QUEUE_DEPTH)
|
DefaultValue = FileSystemConfigurations.DEFAULT_READ_AHEAD_QUEUE_DEPTH)
|
||||||
private int readAheadQueueDepth;
|
private int readAheadQueueDepth;
|
||||||
|
|
||||||
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.FS_AZURE_ENABLE_FLUSH,
|
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_ENABLE_FLUSH,
|
||||||
DefaultValue = FileSystemConfigurations.DEFAULT_ENABLE_FLUSH)
|
DefaultValue = FileSystemConfigurations.DEFAULT_ENABLE_FLUSH)
|
||||||
private boolean enableFlush;
|
private boolean enableFlush;
|
||||||
|
|
||||||
@StringConfigurationValidatorAnnotation(ConfigurationKey = ConfigurationKeys.FS_AZURE_USER_AGENT_PREFIX_KEY,
|
@StringConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_USER_AGENT_PREFIX_KEY,
|
||||||
DefaultValue = "")
|
DefaultValue = "")
|
||||||
private String userAgentId;
|
private String userAgentId;
|
||||||
|
|
||||||
|
@ -140,7 +150,7 @@ public class AbfsConfiguration{
|
||||||
|
|
||||||
public AbfsConfiguration(final Configuration configuration) throws IllegalAccessException, InvalidConfigurationValueException {
|
public AbfsConfiguration(final Configuration configuration) throws IllegalAccessException, InvalidConfigurationValueException {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.isSecure = this.configuration.getBoolean(ConfigurationKeys.FS_AZURE_SECURE_MODE, false);
|
this.isSecure = this.configuration.getBoolean(FS_AZURE_SECURE_MODE, false);
|
||||||
|
|
||||||
validateStorageAccountKeys();
|
validateStorageAccountKeys();
|
||||||
Field[] fields = this.getClass().getDeclaredFields();
|
Field[] fields = this.getClass().getDeclaredFields();
|
||||||
|
@ -161,17 +171,17 @@ public class AbfsConfiguration{
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmulator() {
|
public boolean isEmulator() {
|
||||||
return this.getConfiguration().getBoolean(ConfigurationKeys.FS_AZURE_EMULATOR_ENABLED, false);
|
return this.getConfiguration().getBoolean(FS_AZURE_EMULATOR_ENABLED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSecureMode() {
|
public boolean isSecureMode() {
|
||||||
return this.isSecure;
|
return isSecure;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStorageAccountKey(final String accountName) throws AzureBlobFileSystemException {
|
public String getStorageAccountKey(final String accountName) throws AzureBlobFileSystemException {
|
||||||
String key;
|
String key;
|
||||||
String keyProviderClass =
|
String keyProviderClass =
|
||||||
configuration.get(ConfigurationKeys.AZURE_KEY_ACCOUNT_KEYPROVIDER_PREFIX + accountName);
|
configuration.get(AZURE_KEY_ACCOUNT_KEYPROVIDER_PREFIX + accountName);
|
||||||
KeyProvider keyProvider;
|
KeyProvider keyProvider;
|
||||||
|
|
||||||
if (keyProviderClass == null) {
|
if (keyProviderClass == null) {
|
||||||
|
@ -278,19 +288,88 @@ public class AbfsConfiguration{
|
||||||
return configuration.getEnum(FS_AZURE_SSL_CHANNEL_MODE_KEY, DEFAULT_FS_AZURE_SSL_CHANNEL_MODE);
|
return configuration.getEnum(FS_AZURE_SSL_CHANNEL_MODE_KEY, DEFAULT_FS_AZURE_SSL_CHANNEL_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthType getAuthType(final String accountName) {
|
||||||
|
return configuration.getEnum(FS_AZURE_ACCOUNT_AUTH_TYPE_PROPERTY_NAME + accountName, AuthType.SharedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessTokenProvider getTokenProvider(final String accountName) throws TokenAccessProviderException {
|
||||||
|
AuthType authType = configuration.getEnum(FS_AZURE_ACCOUNT_AUTH_TYPE_PROPERTY_NAME + accountName, AuthType.SharedKey);
|
||||||
|
if (authType == AuthType.OAuth) {
|
||||||
|
try {
|
||||||
|
Class<? extends AccessTokenProvider> tokenProviderClass =
|
||||||
|
configuration.getClass(FS_AZURE_ACCOUNT_TOKEN_PROVIDER_TYPE_PROPERTY_NAME + accountName, null,
|
||||||
|
AccessTokenProvider.class);
|
||||||
|
AccessTokenProvider tokenProvider = null;
|
||||||
|
if (tokenProviderClass == ClientCredsTokenProvider.class) {
|
||||||
|
String authEndpoint = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_CLIENT_ENDPOINT + accountName);
|
||||||
|
String clientId = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_CLIENT_ID + accountName);
|
||||||
|
String clientSecret = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_CLIENT_SECRET + accountName);
|
||||||
|
tokenProvider = new ClientCredsTokenProvider(authEndpoint, clientId, clientSecret);
|
||||||
|
} else if (tokenProviderClass == UserPasswordTokenProvider.class) {
|
||||||
|
String authEndpoint = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_CLIENT_ENDPOINT + accountName);
|
||||||
|
String username = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_USER_NAME + accountName);
|
||||||
|
String password = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_USER_PASSWORD + accountName);
|
||||||
|
tokenProvider = new UserPasswordTokenProvider(authEndpoint, username, password);
|
||||||
|
} else if (tokenProviderClass == MsiTokenProvider.class) {
|
||||||
|
String tenantGuid = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_MSI_TENANT + accountName);
|
||||||
|
String clientId = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_CLIENT_ID + accountName);
|
||||||
|
tokenProvider = new MsiTokenProvider(tenantGuid, clientId);
|
||||||
|
} else if (tokenProviderClass == RefreshTokenBasedTokenProvider.class) {
|
||||||
|
String refreshToken = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_REFRESH_TOKEN + accountName);
|
||||||
|
String clientId = getPasswordString(FS_AZURE_ACCOUNT_OAUTH_CLIENT_ID + accountName);
|
||||||
|
tokenProvider = new RefreshTokenBasedTokenProvider(clientId, refreshToken);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Failed to initialize " + tokenProviderClass);
|
||||||
|
}
|
||||||
|
return tokenProvider;
|
||||||
|
} catch(IllegalArgumentException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new TokenAccessProviderException("Unable to load key provider class.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (authType == AuthType.Custom) {
|
||||||
|
try {
|
||||||
|
String configKey = FS_AZURE_ACCOUNT_TOKEN_PROVIDER_TYPE_PROPERTY_NAME + accountName;
|
||||||
|
Class<? extends CustomTokenProviderAdaptee> customTokenProviderClass =
|
||||||
|
configuration.getClass(configKey, null,
|
||||||
|
CustomTokenProviderAdaptee.class);
|
||||||
|
if (customTokenProviderClass == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("The configuration value for \"%s\" is invalid.", configKey));
|
||||||
|
}
|
||||||
|
CustomTokenProviderAdaptee azureTokenProvider = ReflectionUtils
|
||||||
|
.newInstance(customTokenProviderClass, configuration);
|
||||||
|
if (azureTokenProvider == null) {
|
||||||
|
throw new IllegalArgumentException("Failed to initialize " + customTokenProviderClass);
|
||||||
|
}
|
||||||
|
azureTokenProvider.initialize(configuration, accountName);
|
||||||
|
return new CustomTokenProviderAdapter(azureTokenProvider);
|
||||||
|
} catch(IllegalArgumentException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new TokenAccessProviderException("Unable to load custom token provider class.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new TokenAccessProviderException(String.format(
|
||||||
|
"Invalid auth type: %s is being used, expecting OAuth", authType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void validateStorageAccountKeys() throws InvalidConfigurationValueException {
|
void validateStorageAccountKeys() throws InvalidConfigurationValueException {
|
||||||
Base64StringConfigurationBasicValidator validator = new Base64StringConfigurationBasicValidator(
|
Base64StringConfigurationBasicValidator validator = new Base64StringConfigurationBasicValidator(
|
||||||
ConfigurationKeys.FS_AZURE_ACCOUNT_KEY_PROPERTY_NAME, "", true);
|
FS_AZURE_ACCOUNT_KEY_PROPERTY_NAME, "", true);
|
||||||
this.storageAccountKeys = this.configuration.getValByRegex(ConfigurationKeys.FS_AZURE_ACCOUNT_KEY_PROPERTY_NAME_REGX);
|
this.storageAccountKeys = configuration.getValByRegex(FS_AZURE_ACCOUNT_KEY_PROPERTY_NAME_REGX);
|
||||||
|
|
||||||
for (Map.Entry<String, String> account : this.storageAccountKeys.entrySet()) {
|
for (Map.Entry<String, String> account : storageAccountKeys.entrySet()) {
|
||||||
validator.validate(account.getValue());
|
validator.validate(account.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int validateInt(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
int validateInt(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
||||||
IntegerConfigurationValidatorAnnotation validator = field.getAnnotation(IntegerConfigurationValidatorAnnotation.class);
|
IntegerConfigurationValidatorAnnotation validator = field.getAnnotation(IntegerConfigurationValidatorAnnotation.class);
|
||||||
String value = this.configuration.get(validator.ConfigurationKey());
|
String value = configuration.get(validator.ConfigurationKey());
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
return new IntegerConfigurationBasicValidator(
|
return new IntegerConfigurationBasicValidator(
|
||||||
|
@ -303,7 +382,7 @@ public class AbfsConfiguration{
|
||||||
|
|
||||||
long validateLong(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
long validateLong(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
||||||
LongConfigurationValidatorAnnotation validator = field.getAnnotation(LongConfigurationValidatorAnnotation.class);
|
LongConfigurationValidatorAnnotation validator = field.getAnnotation(LongConfigurationValidatorAnnotation.class);
|
||||||
String value = this.configuration.get(validator.ConfigurationKey());
|
String value = configuration.get(validator.ConfigurationKey());
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
return new LongConfigurationBasicValidator(
|
return new LongConfigurationBasicValidator(
|
||||||
|
@ -316,7 +395,7 @@ public class AbfsConfiguration{
|
||||||
|
|
||||||
String validateString(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
String validateString(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
||||||
StringConfigurationValidatorAnnotation validator = field.getAnnotation(StringConfigurationValidatorAnnotation.class);
|
StringConfigurationValidatorAnnotation validator = field.getAnnotation(StringConfigurationValidatorAnnotation.class);
|
||||||
String value = this.configuration.get(validator.ConfigurationKey());
|
String value = configuration.get(validator.ConfigurationKey());
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
return new StringConfigurationBasicValidator(
|
return new StringConfigurationBasicValidator(
|
||||||
|
@ -327,7 +406,7 @@ public class AbfsConfiguration{
|
||||||
|
|
||||||
String validateBase64String(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
String validateBase64String(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
||||||
Base64StringConfigurationValidatorAnnotation validator = field.getAnnotation((Base64StringConfigurationValidatorAnnotation.class));
|
Base64StringConfigurationValidatorAnnotation validator = field.getAnnotation((Base64StringConfigurationValidatorAnnotation.class));
|
||||||
String value = this.configuration.get(validator.ConfigurationKey());
|
String value = configuration.get(validator.ConfigurationKey());
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
return new Base64StringConfigurationBasicValidator(
|
return new Base64StringConfigurationBasicValidator(
|
||||||
|
@ -338,7 +417,7 @@ public class AbfsConfiguration{
|
||||||
|
|
||||||
boolean validateBoolean(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
boolean validateBoolean(Field field) throws IllegalAccessException, InvalidConfigurationValueException {
|
||||||
BooleanConfigurationValidatorAnnotation validator = field.getAnnotation(BooleanConfigurationValidatorAnnotation.class);
|
BooleanConfigurationValidatorAnnotation validator = field.getAnnotation(BooleanConfigurationValidatorAnnotation.class);
|
||||||
String value = this.configuration.get(validator.ConfigurationKey());
|
String value = configuration.get(validator.ConfigurationKey());
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
return new BooleanConfigurationBasicValidator(
|
return new BooleanConfigurationBasicValidator(
|
||||||
|
@ -347,6 +426,14 @@ public class AbfsConfiguration{
|
||||||
validator.ThrowIfInvalid()).validate(value);
|
validator.ThrowIfInvalid()).validate(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getPasswordString(String key) throws IOException {
|
||||||
|
char[] passchars = configuration.getPassword(key);
|
||||||
|
if (passchars != null) {
|
||||||
|
return new String(passchars);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setReadBufferSize(int bufferSize) {
|
void setReadBufferSize(int bufferSize) {
|
||||||
this.readBufferSize = bufferSize;
|
this.readBufferSize = bufferSize;
|
||||||
|
|
|
@ -67,10 +67,12 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TimeoutException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
|
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.ListResultEntrySchema;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultSchema;
|
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultSchema;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsInputStream;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsInputStream;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsOutputStream;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsOutputStream;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.ExponentialRetryPolicy;
|
import org.apache.hadoop.fs.azurebfs.services.ExponentialRetryPolicy;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.SharedKeyCredentials;
|
import org.apache.hadoop.fs.azurebfs.services.SharedKeyCredentials;
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
import org.apache.hadoop.fs.permission.FsAction;
|
||||||
|
@ -487,16 +489,22 @@ public class AzureBlobFileSystemStore {
|
||||||
throw new InvalidUriException(uri.toString());
|
throw new InvalidUriException(uri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
int dotIndex = accountName.indexOf(AbfsHttpConstants.DOT);
|
SharedKeyCredentials creds = null;
|
||||||
if (dotIndex <= 0) {
|
AccessTokenProvider tokenProvider = null;
|
||||||
throw new InvalidUriException(
|
|
||||||
uri.toString() + " - account name is not fully qualified.");
|
|
||||||
}
|
|
||||||
SharedKeyCredentials creds =
|
|
||||||
new SharedKeyCredentials(accountName.substring(0, dotIndex),
|
|
||||||
this.abfsConfiguration.getStorageAccountKey(accountName));
|
|
||||||
|
|
||||||
this.client = new AbfsClient(baseUrl, creds, abfsConfiguration, new ExponentialRetryPolicy());
|
if (abfsConfiguration.getAuthType(accountName) == AuthType.SharedKey) {
|
||||||
|
int dotIndex = accountName.indexOf(AbfsHttpConstants.DOT);
|
||||||
|
if (dotIndex <= 0) {
|
||||||
|
throw new InvalidUriException(
|
||||||
|
uri.toString() + " - account name is not fully qualified.");
|
||||||
|
}
|
||||||
|
creds = new SharedKeyCredentials(accountName.substring(0, dotIndex),
|
||||||
|
abfsConfiguration.getStorageAccountKey(accountName));
|
||||||
|
} else {
|
||||||
|
tokenProvider = abfsConfiguration.getTokenProvider(accountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client = new AbfsClient(baseUrl, creds, abfsConfiguration, new ExponentialRetryPolicy(), tokenProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRelativePath(final Path path) {
|
private String getRelativePath(final Path path) {
|
||||||
|
@ -537,7 +545,7 @@ public class AzureBlobFileSystemStore {
|
||||||
Date utcDate = new SimpleDateFormat(DATE_TIME_PATTERN).parse(lastModifiedTime);
|
Date utcDate = new SimpleDateFormat(DATE_TIME_PATTERN).parse(lastModifiedTime);
|
||||||
parsedTime = utcDate.getTime();
|
parsedTime = utcDate.getTime();
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
LOG.error("Failed to parse the date {0}", lastModifiedTime);
|
LOG.error("Failed to parse the date {}", lastModifiedTime);
|
||||||
} finally {
|
} finally {
|
||||||
return parsedTime;
|
return parsedTime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,5 +60,24 @@ public final class ConfigurationKeys {
|
||||||
public static final String AZURE_KEY_ACCOUNT_KEYPROVIDER_PREFIX = "fs.azure.account.keyprovider.";
|
public static final String AZURE_KEY_ACCOUNT_KEYPROVIDER_PREFIX = "fs.azure.account.keyprovider.";
|
||||||
public static final String AZURE_KEY_ACCOUNT_SHELLKEYPROVIDER_SCRIPT = "fs.azure.shellkeyprovider.script";
|
public static final String AZURE_KEY_ACCOUNT_SHELLKEYPROVIDER_SCRIPT = "fs.azure.shellkeyprovider.script";
|
||||||
|
|
||||||
|
/** Prefix for auth type properties: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_AUTH_TYPE_PROPERTY_NAME = "fs.azure.account.auth.type.";
|
||||||
|
/** Prefix for oauth token provider type: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_TOKEN_PROVIDER_TYPE_PROPERTY_NAME = "fs.azure.account.oauth.provider.type.";
|
||||||
|
/** Prefix for oauth AAD client id: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_OAUTH_CLIENT_ID = "fs.azure.account.oauth2.client.id.";
|
||||||
|
/** Prefix for oauth AAD client secret: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_OAUTH_CLIENT_SECRET = "fs.azure.account.oauth2.client.secret.";
|
||||||
|
/** Prefix for oauth AAD client endpoint: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_OAUTH_CLIENT_ENDPOINT = "fs.azure.account.oauth2.client.endpoint.";
|
||||||
|
/** Prefix for oauth msi tenant id: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_OAUTH_MSI_TENANT = "fs.azure.account.oauth2.msi.tenant.";
|
||||||
|
/** Prefix for oauth user name: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_OAUTH_USER_NAME = "fs.azure.account.oauth2.user.name.";
|
||||||
|
/** Prefix for oauth user password: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_OAUTH_USER_PASSWORD = "fs.azure.account.oauth2.user.password.";
|
||||||
|
/** Prefix for oauth refresh token: {@value}. */
|
||||||
|
public static final String FS_AZURE_ACCOUNT_OAUTH_REFRESH_TOKEN = "fs.azure.account.oauth2.refresh.token.";
|
||||||
|
|
||||||
private ConfigurationKeys() {}
|
private ConfigurationKeys() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
*
|
||||||
|
* 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.contracts.exceptions;
|
||||||
|
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if there is a problem instantiating a TokenAccessProvider or retrieving a configuration
|
||||||
|
* using a TokenAccessProvider object.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class TokenAccessProviderException extends AzureBlobFileSystemException {
|
||||||
|
|
||||||
|
public TokenAccessProviderException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenAccessProviderException(String message, Throwable cause) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ public enum AzureServiceErrorCode {
|
||||||
INGRESS_OVER_ACCOUNT_LIMIT(null, HttpURLConnection.HTTP_UNAVAILABLE, "Ingress is over the account limit."),
|
INGRESS_OVER_ACCOUNT_LIMIT(null, HttpURLConnection.HTTP_UNAVAILABLE, "Ingress is over the account limit."),
|
||||||
EGRESS_OVER_ACCOUNT_LIMIT(null, HttpURLConnection.HTTP_UNAVAILABLE, "Egress is over the account limit."),
|
EGRESS_OVER_ACCOUNT_LIMIT(null, HttpURLConnection.HTTP_UNAVAILABLE, "Egress is over the account limit."),
|
||||||
INVALID_QUERY_PARAMETER_VALUE("InvalidQueryParameterValue", HttpURLConnection.HTTP_BAD_REQUEST, null),
|
INVALID_QUERY_PARAMETER_VALUE("InvalidQueryParameterValue", HttpURLConnection.HTTP_BAD_REQUEST, null),
|
||||||
|
AUTHORIZATION_PERMISSION_MISS_MATCH("AuthorizationPermissionMismatch", HttpURLConnection.HTTP_FORBIDDEN, null),
|
||||||
UNKNOWN(null, -1, null);
|
UNKNOWN(null, -1, null);
|
||||||
|
|
||||||
private final String errorCode;
|
private final String errorCode;
|
||||||
|
|
|
@ -57,13 +57,31 @@ public class ListResultEntrySchema {
|
||||||
@JsonProperty(value = "contentLength")
|
@JsonProperty(value = "contentLength")
|
||||||
private Long contentLength;
|
private Long contentLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The owner property.
|
||||||
|
*/
|
||||||
|
@JsonProperty(value = "owner")
|
||||||
|
private String owner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The group property.
|
||||||
|
*/
|
||||||
|
@JsonProperty(value = "group")
|
||||||
|
private String group;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The permissions property.
|
||||||
|
*/
|
||||||
|
@JsonProperty(value = "permissions")
|
||||||
|
private String permissions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name value.
|
* Get the name value.
|
||||||
*
|
*
|
||||||
* @return the name value
|
* @return the name value
|
||||||
*/
|
*/
|
||||||
public String name() {
|
public String name() {
|
||||||
return this.name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,7 +101,7 @@ public class ListResultEntrySchema {
|
||||||
* @return the isDirectory value
|
* @return the isDirectory value
|
||||||
*/
|
*/
|
||||||
public Boolean isDirectory() {
|
public Boolean isDirectory() {
|
||||||
return this.isDirectory;
|
return isDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,7 +121,7 @@ public class ListResultEntrySchema {
|
||||||
* @return the lastModified value
|
* @return the lastModified value
|
||||||
*/
|
*/
|
||||||
public String lastModified() {
|
public String lastModified() {
|
||||||
return this.lastModified;
|
return lastModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,7 +141,7 @@ public class ListResultEntrySchema {
|
||||||
* @return the etag value
|
* @return the etag value
|
||||||
*/
|
*/
|
||||||
public String eTag() {
|
public String eTag() {
|
||||||
return this.eTag;
|
return eTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,7 +161,7 @@ public class ListResultEntrySchema {
|
||||||
* @return the contentLength value
|
* @return the contentLength value
|
||||||
*/
|
*/
|
||||||
public Long contentLength() {
|
public Long contentLength() {
|
||||||
return this.contentLength;
|
return contentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,4 +175,65 @@ public class ListResultEntrySchema {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
Get the owner value.
|
||||||
|
*
|
||||||
|
* @return the owner value
|
||||||
|
*/
|
||||||
|
public String owner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the owner value.
|
||||||
|
*
|
||||||
|
* @param owner the owner value to set
|
||||||
|
* @return the ListEntrySchema object itself.
|
||||||
|
*/
|
||||||
|
public ListResultEntrySchema withOwner(final String owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the group value.
|
||||||
|
*
|
||||||
|
* @return the group value
|
||||||
|
*/
|
||||||
|
public String group() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the group value.
|
||||||
|
*
|
||||||
|
* @param group the group value to set
|
||||||
|
* @return the ListEntrySchema object itself.
|
||||||
|
*/
|
||||||
|
public ListResultEntrySchema withGroup(final String group) {
|
||||||
|
this.group = group;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the permissions value.
|
||||||
|
*
|
||||||
|
* @return the permissions value
|
||||||
|
*/
|
||||||
|
public String permissions() {
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the permissions value.
|
||||||
|
*
|
||||||
|
* @param permissions the permissions value to set
|
||||||
|
* @return the ListEntrySchema object itself.
|
||||||
|
*/
|
||||||
|
public ListResultEntrySchema withPermissions(final String permissions) {
|
||||||
|
this.permissions = permissions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Azure Active Directory token when requested. The provider can
|
||||||
|
* cache the token if it has already retrieved one. If it does, then the
|
||||||
|
* provider is responsible for checking expiry and refreshing as needed.
|
||||||
|
*
|
||||||
|
* In other words, this is is a token cache that fetches tokens when
|
||||||
|
* requested, if the cached token has expired.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AccessTokenProvider {
|
||||||
|
|
||||||
|
private AzureADToken token;
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AccessTokenProvider.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the {@link AzureADToken} cached (or retrieved) by this instance.
|
||||||
|
*
|
||||||
|
* @return {@link AzureADToken} containing the access token
|
||||||
|
* @throws IOException if there is an error fetching the token
|
||||||
|
*/
|
||||||
|
public synchronized AzureADToken getToken() throws IOException {
|
||||||
|
if (isTokenAboutToExpire()) {
|
||||||
|
LOG.debug("AAD Token is missing or expired:"
|
||||||
|
+ " Calling refresh-token from abstract base class");
|
||||||
|
token = refreshToken();
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the method to fetch the access token. Derived classes should override
|
||||||
|
* this method to actually get the token from Azure Active Directory.
|
||||||
|
*
|
||||||
|
* This method will be called initially, and then once when the token
|
||||||
|
* is about to expire.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return {@link AzureADToken} containing the access token
|
||||||
|
* @throws IOException if there is an error fetching the token
|
||||||
|
*/
|
||||||
|
protected abstract AzureADToken refreshToken() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the token is about to expire in the next 5 minutes.
|
||||||
|
* The 5 minute allowance is to allow for clock skew and also to
|
||||||
|
* allow for token to be refreshed in that much time.
|
||||||
|
*
|
||||||
|
* @return true if the token is expiring in next 5 minutes
|
||||||
|
*/
|
||||||
|
private boolean isTokenAboutToExpire() {
|
||||||
|
if (token == null) {
|
||||||
|
LOG.debug("AADToken: no token. Returning expiring=true");
|
||||||
|
return true; // no token should have same response as expired token
|
||||||
|
}
|
||||||
|
boolean expiring = false;
|
||||||
|
// allow 5 minutes for clock skew
|
||||||
|
long approximatelyNow = System.currentTimeMillis() + FIVE_MINUTES;
|
||||||
|
if (token.getExpiry().getTime() < approximatelyNow) {
|
||||||
|
expiring = true;
|
||||||
|
}
|
||||||
|
if (expiring) {
|
||||||
|
LOG.debug("AADToken: token expiring: "
|
||||||
|
+ token.getExpiry().toString()
|
||||||
|
+ " : Five-minute window: "
|
||||||
|
+ new Date(approximatelyNow).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return expiring;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 minutes in milliseconds
|
||||||
|
private static final long FIVE_MINUTES = 300 * 1000;
|
||||||
|
}
|
|
@ -0,0 +1,344 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.codehaus.jackson.JsonFactory;
|
||||||
|
import org.codehaus.jackson.JsonParser;
|
||||||
|
import org.codehaus.jackson.JsonToken;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.ExponentialRetryPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides convenience methods to obtain AAD tokens.
|
||||||
|
* While convenient, it is not necessary to use these methods to
|
||||||
|
* obtain the tokens. Customers can use any other method
|
||||||
|
* (e.g., using the adal4j client) to obtain tokens.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
@InterfaceStability.Evolving
|
||||||
|
public final class AzureADAuthenticator {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AzureADAuthenticator.class);
|
||||||
|
private static final String RESOURCE_NAME = "https://storage.azure.com/";
|
||||||
|
private static final int CONNECT_TIMEOUT = 30 * 1000;
|
||||||
|
private static final int READ_TIMEOUT = 30 * 1000;
|
||||||
|
|
||||||
|
private AzureADAuthenticator() {
|
||||||
|
// no operation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets Azure Active Directory token using the user ID and password of
|
||||||
|
* a service principal (that is, Web App in Azure Active Directory).
|
||||||
|
*
|
||||||
|
* Azure Active Directory allows users to set up a web app as a
|
||||||
|
* service principal. Users can optionally obtain service principal keys
|
||||||
|
* from AAD. This method gets a token using a service principal's client ID
|
||||||
|
* and keys. In addition, it needs the token endpoint associated with the
|
||||||
|
* user's directory.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param authEndpoint the OAuth 2.0 token endpoint associated
|
||||||
|
* with the user's directory (obtain from
|
||||||
|
* Active Directory configuration)
|
||||||
|
* @param clientId the client ID (GUID) of the client web app
|
||||||
|
* btained from Azure Active Directory configuration
|
||||||
|
* @param clientSecret the secret key of the client web app
|
||||||
|
* @return {@link AzureADToken} obtained using the creds
|
||||||
|
* @throws IOException throws IOException if there is a failure in connecting to Azure AD
|
||||||
|
*/
|
||||||
|
public static AzureADToken getTokenUsingClientCreds(String authEndpoint,
|
||||||
|
String clientId, String clientSecret)
|
||||||
|
throws IOException {
|
||||||
|
Preconditions.checkNotNull(authEndpoint, "authEndpoint");
|
||||||
|
Preconditions.checkNotNull(clientId, "clientId");
|
||||||
|
Preconditions.checkNotNull(clientSecret, "clientSecret");
|
||||||
|
|
||||||
|
QueryParams qp = new QueryParams();
|
||||||
|
qp.add("resource", RESOURCE_NAME);
|
||||||
|
qp.add("grant_type", "client_credentials");
|
||||||
|
qp.add("client_id", clientId);
|
||||||
|
qp.add("client_secret", clientSecret);
|
||||||
|
LOG.debug("AADToken: starting to fetch token using client creds for client ID " + clientId);
|
||||||
|
|
||||||
|
return getTokenCall(authEndpoint, qp.serialize(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets AAD token from the local virtual machine's VM extension. This only works on
|
||||||
|
* an Azure VM with MSI extension
|
||||||
|
* enabled.
|
||||||
|
*
|
||||||
|
* @param tenantGuid (optional) The guid of the AAD tenant. Can be {@code null}.
|
||||||
|
* @param clientId (optional) The clientId guid of the MSI service
|
||||||
|
* principal to use. Can be {@code null}.
|
||||||
|
* @param bypassCache {@code boolean} specifying whether a cached token is acceptable or a fresh token
|
||||||
|
* request should me made to AAD
|
||||||
|
* @return {@link AzureADToken} obtained using the creds
|
||||||
|
* @throws IOException throws IOException if there is a failure in obtaining the token
|
||||||
|
*/
|
||||||
|
public static AzureADToken getTokenFromMsi(String tenantGuid, String clientId,
|
||||||
|
boolean bypassCache) throws IOException {
|
||||||
|
Preconditions.checkNotNull(tenantGuid, "tenantGuid");
|
||||||
|
Preconditions.checkNotNull(clientId, "clientId");
|
||||||
|
|
||||||
|
String authEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token";
|
||||||
|
|
||||||
|
QueryParams qp = new QueryParams();
|
||||||
|
qp.add("api-version", "2018-02-01");
|
||||||
|
qp.add("resource", RESOURCE_NAME);
|
||||||
|
|
||||||
|
|
||||||
|
if (tenantGuid.length() > 0) {
|
||||||
|
String authority = "https://login.microsoftonline.com/" + tenantGuid;
|
||||||
|
qp.add("authority", authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientId.length() > 0) {
|
||||||
|
qp.add("client_id", clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bypassCache) {
|
||||||
|
qp.add("bypass_cache", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
Hashtable<String, String> headers = new Hashtable<>();
|
||||||
|
headers.put("Metadata", "true");
|
||||||
|
|
||||||
|
LOG.debug("AADToken: starting to fetch token using MSI");
|
||||||
|
return getTokenCall(authEndpoint, qp.serialize(), headers, "GET");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets Azure Active Directory token using refresh token.
|
||||||
|
*
|
||||||
|
* @param clientId the client ID (GUID) of the client web app obtained from Azure Active Directory configuration
|
||||||
|
* @param refreshToken the refresh token
|
||||||
|
* @return {@link AzureADToken} obtained using the refresh token
|
||||||
|
* @throws IOException throws IOException if there is a failure in connecting to Azure AD
|
||||||
|
*/
|
||||||
|
public static AzureADToken getTokenUsingRefreshToken(String clientId,
|
||||||
|
String refreshToken) throws IOException {
|
||||||
|
String authEndpoint = "https://login.microsoftonline.com/Common/oauth2/token";
|
||||||
|
QueryParams qp = new QueryParams();
|
||||||
|
qp.add("grant_type", "refresh_token");
|
||||||
|
qp.add("refresh_token", refreshToken);
|
||||||
|
if (clientId != null) {
|
||||||
|
qp.add("client_id", clientId);
|
||||||
|
}
|
||||||
|
LOG.debug("AADToken: starting to fetch token using refresh token for client ID " + clientId);
|
||||||
|
return getTokenCall(authEndpoint, qp.serialize(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HttpException extends IOException {
|
||||||
|
private int httpErrorCode;
|
||||||
|
private String requestId;
|
||||||
|
|
||||||
|
public int getHttpErrorCode() {
|
||||||
|
return this.httpErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequestId() {
|
||||||
|
return this.requestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpException(int httpErrorCode, String requestId, String message) {
|
||||||
|
super(message);
|
||||||
|
this.httpErrorCode = httpErrorCode;
|
||||||
|
this.requestId = requestId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AzureADToken getTokenCall(String authEndpoint, String body,
|
||||||
|
Hashtable<String, String> headers, String httpMethod)
|
||||||
|
throws IOException {
|
||||||
|
AzureADToken token = null;
|
||||||
|
ExponentialRetryPolicy retryPolicy
|
||||||
|
= new ExponentialRetryPolicy(3, 0, 1000, 2);
|
||||||
|
|
||||||
|
int httperror = 0;
|
||||||
|
String requestId;
|
||||||
|
String httpExceptionMessage = null;
|
||||||
|
IOException ex = null;
|
||||||
|
boolean succeeded = false;
|
||||||
|
int retryCount = 0;
|
||||||
|
do {
|
||||||
|
httperror = 0;
|
||||||
|
requestId = "";
|
||||||
|
ex = null;
|
||||||
|
try {
|
||||||
|
token = getTokenSingleCall(authEndpoint, body, headers, httpMethod);
|
||||||
|
} catch (HttpException e) {
|
||||||
|
httperror = e.httpErrorCode;
|
||||||
|
requestId = e.requestId;
|
||||||
|
httpExceptionMessage = e.getMessage();
|
||||||
|
} catch (IOException e) {
|
||||||
|
ex = e;
|
||||||
|
}
|
||||||
|
succeeded = ((httperror == 0) && (ex == null));
|
||||||
|
retryCount++;
|
||||||
|
} while (!succeeded && retryPolicy.shouldRetry(retryCount, httperror));
|
||||||
|
if (!succeeded) {
|
||||||
|
if (ex != null) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
if (httperror != 0) {
|
||||||
|
throw new IOException(httpExceptionMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AzureADToken getTokenSingleCall(
|
||||||
|
String authEndpoint, String payload, Hashtable<String, String> headers, String httpMethod)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
AzureADToken token = null;
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
String urlString = authEndpoint;
|
||||||
|
|
||||||
|
httpMethod = (httpMethod == null) ? "POST" : httpMethod;
|
||||||
|
if (httpMethod.equals("GET")) {
|
||||||
|
urlString = urlString + "?" + payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(urlString);
|
||||||
|
conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod(httpMethod);
|
||||||
|
conn.setReadTimeout(READ_TIMEOUT);
|
||||||
|
conn.setConnectTimeout(CONNECT_TIMEOUT);
|
||||||
|
|
||||||
|
if (headers != null && headers.size() > 0) {
|
||||||
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
conn.setRequestProperty(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.setRequestProperty("Connection", "close");
|
||||||
|
|
||||||
|
if (httpMethod.equals("POST")) {
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.getOutputStream().write(payload.getBytes("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int httpResponseCode = conn.getResponseCode();
|
||||||
|
String requestId = conn.getHeaderField("x-ms-request-id");
|
||||||
|
String responseContentType = conn.getHeaderField("Content-Type");
|
||||||
|
long responseContentLength = conn.getHeaderFieldLong("Content-Length", 0);
|
||||||
|
|
||||||
|
requestId = requestId == null ? "" : requestId;
|
||||||
|
if (httpResponseCode == HttpURLConnection.HTTP_OK
|
||||||
|
&& responseContentType.startsWith("application/json") && responseContentLength > 0) {
|
||||||
|
InputStream httpResponseStream = conn.getInputStream();
|
||||||
|
token = parseTokenFromStream(httpResponseStream);
|
||||||
|
} else {
|
||||||
|
String responseBody = consumeInputStream(conn.getInputStream(), 1024);
|
||||||
|
String proxies = "none";
|
||||||
|
String httpProxy = System.getProperty("http.proxy");
|
||||||
|
String httpsProxy = System.getProperty("https.proxy");
|
||||||
|
if (httpProxy != null || httpsProxy != null) {
|
||||||
|
proxies = "http:" + httpProxy + "; https:" + httpsProxy;
|
||||||
|
}
|
||||||
|
String logMessage =
|
||||||
|
"AADToken: HTTP connection failed for getting token from AzureAD. Http response: "
|
||||||
|
+ httpResponseCode + " " + conn.getResponseMessage()
|
||||||
|
+ " Content-Type: " + responseContentType
|
||||||
|
+ " Content-Length: " + responseContentLength
|
||||||
|
+ " Request ID: " + requestId.toString()
|
||||||
|
+ " Proxies: " + proxies
|
||||||
|
+ " First 1K of Body: " + responseBody;
|
||||||
|
LOG.debug(logMessage);
|
||||||
|
throw new HttpException(httpResponseCode, requestId, logMessage);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AzureADToken parseTokenFromStream(InputStream httpResponseStream) throws IOException {
|
||||||
|
AzureADToken token = new AzureADToken();
|
||||||
|
try {
|
||||||
|
int expiryPeriod = 0;
|
||||||
|
|
||||||
|
JsonFactory jf = new JsonFactory();
|
||||||
|
JsonParser jp = jf.createJsonParser(httpResponseStream);
|
||||||
|
String fieldName, fieldValue;
|
||||||
|
jp.nextToken();
|
||||||
|
while (jp.hasCurrentToken()) {
|
||||||
|
if (jp.getCurrentToken() == JsonToken.FIELD_NAME) {
|
||||||
|
fieldName = jp.getCurrentName();
|
||||||
|
jp.nextToken(); // field value
|
||||||
|
fieldValue = jp.getText();
|
||||||
|
|
||||||
|
if (fieldName.equals("access_token")) {
|
||||||
|
token.setAccessToken(fieldValue);
|
||||||
|
}
|
||||||
|
if (fieldName.equals("expires_in")) {
|
||||||
|
expiryPeriod = Integer.parseInt(fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jp.nextToken();
|
||||||
|
}
|
||||||
|
jp.close();
|
||||||
|
long expiry = System.currentTimeMillis();
|
||||||
|
expiry = expiry + expiryPeriod * 1000L; // convert expiryPeriod to milliseconds and add
|
||||||
|
token.setExpiry(new Date(expiry));
|
||||||
|
LOG.debug("AADToken: fetched token with expiry " + token.getExpiry().toString());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOG.debug("AADToken: got exception when parsing json token " + ex.toString());
|
||||||
|
throw ex;
|
||||||
|
} finally {
|
||||||
|
httpResponseStream.close();
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String consumeInputStream(InputStream inStream, int length) throws IOException {
|
||||||
|
byte[] b = new byte[length];
|
||||||
|
int totalBytesRead = 0;
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bytesRead = inStream.read(b, totalBytesRead, length - totalBytesRead);
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
}
|
||||||
|
} while (bytesRead >= 0 && totalBytesRead < length);
|
||||||
|
|
||||||
|
return new String(b, 0, totalBytesRead, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object representing the AAD access token to use when making HTTP requests to Azure Data Lake Storage.
|
||||||
|
*/
|
||||||
|
public class AzureADToken {
|
||||||
|
private String accessToken;
|
||||||
|
private Date expiry;
|
||||||
|
|
||||||
|
public String getAccessToken() {
|
||||||
|
return this.accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessToken(String accessToken) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getExpiry() {
|
||||||
|
return new Date(this.expiry.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiry(Date expiry) {
|
||||||
|
this.expiry = new Date(expiry.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides tokens based on client credentials.
|
||||||
|
*/
|
||||||
|
public class ClientCredsTokenProvider extends AccessTokenProvider {
|
||||||
|
|
||||||
|
private final String authEndpoint;
|
||||||
|
|
||||||
|
private final String clientId;
|
||||||
|
|
||||||
|
private final String clientSecret;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AccessTokenProvider.class);
|
||||||
|
|
||||||
|
|
||||||
|
public ClientCredsTokenProvider(final String authEndpoint,
|
||||||
|
final String clientId, final String clientSecret) {
|
||||||
|
|
||||||
|
Preconditions.checkNotNull(authEndpoint, "authEndpoint");
|
||||||
|
Preconditions.checkNotNull(clientId, "clientId");
|
||||||
|
Preconditions.checkNotNull(clientSecret, "clientSecret");
|
||||||
|
|
||||||
|
this.authEndpoint = authEndpoint;
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.clientSecret = clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AzureADToken refreshToken() throws IOException {
|
||||||
|
LOG.debug("AADToken: refreshing client-credential based token");
|
||||||
|
return AzureADAuthenticator.getTokenUsingClientCreds(authEndpoint, clientId, clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface provides an extensibility model for customizing the acquisition
|
||||||
|
* of Azure Active Directory Access Tokens. When "fs.azure.account.auth.type" is
|
||||||
|
* set to "Custom", implementors may use the
|
||||||
|
* "fs.azure.account.oauth.provider.type.{accountName}" configuration property
|
||||||
|
* to specify a class with a custom implementation of CustomTokenProviderAdaptee.
|
||||||
|
* This class will be dynamically loaded, initialized, and invoked to provide
|
||||||
|
* AAD Access Tokens and their Expiry.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Public
|
||||||
|
@InterfaceStability.Evolving
|
||||||
|
public interface CustomTokenProviderAdaptee {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize with supported configuration. This method is invoked when the
|
||||||
|
* (URI, Configuration)} method is invoked.
|
||||||
|
*
|
||||||
|
* @param configuration Configuration object
|
||||||
|
* @param accountName Account Name
|
||||||
|
* @throws IOException if instance can not be configured.
|
||||||
|
*/
|
||||||
|
void initialize(Configuration configuration, String accountName)
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the access token that should be added to https connection's header.
|
||||||
|
* Will be called depending upon {@link #getExpiryTime()} expiry time is set,
|
||||||
|
* so implementations should be performant. Implementations are responsible
|
||||||
|
* for any refreshing of the token.
|
||||||
|
*
|
||||||
|
* @return String containing the access token
|
||||||
|
* @throws IOException if there is an error fetching the token
|
||||||
|
*/
|
||||||
|
String getAccessToken() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain expiry time of the token. If implementation is performant enough to
|
||||||
|
* maintain expiry and expect {@link #getAccessToken()} call for every
|
||||||
|
* connection then safe to return current or past time.
|
||||||
|
*
|
||||||
|
* However recommended to use the token expiry time received from Azure Active
|
||||||
|
* Directory.
|
||||||
|
*
|
||||||
|
* @return Date to expire access token retrieved from AAD.
|
||||||
|
*/
|
||||||
|
Date getExpiryTime();
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides tokens based on custom implementation, following the Adapter Design
|
||||||
|
* Pattern.
|
||||||
|
*/
|
||||||
|
public final class CustomTokenProviderAdapter extends AccessTokenProvider {
|
||||||
|
|
||||||
|
private CustomTokenProviderAdaptee adaptee;
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AccessTokenProvider.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a token provider based on the custom token provider.
|
||||||
|
*
|
||||||
|
* @param adaptee the custom token provider
|
||||||
|
*/
|
||||||
|
public CustomTokenProviderAdapter(CustomTokenProviderAdaptee adaptee) {
|
||||||
|
Preconditions.checkNotNull(adaptee, "adaptee");
|
||||||
|
this.adaptee = adaptee;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AzureADToken refreshToken() throws IOException {
|
||||||
|
LOG.debug("AADToken: refreshing custom based token");
|
||||||
|
|
||||||
|
AzureADToken azureADToken = new AzureADToken();
|
||||||
|
azureADToken.setAccessToken(adaptee.getAccessToken());
|
||||||
|
azureADToken.setExpiry(adaptee.getExpiryTime());
|
||||||
|
|
||||||
|
return azureADToken;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides tokens based on Azure VM's Managed Service Identity.
|
||||||
|
*/
|
||||||
|
public class MsiTokenProvider extends AccessTokenProvider {
|
||||||
|
|
||||||
|
private final String tenantGuid;
|
||||||
|
|
||||||
|
private final String clientId;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AccessTokenProvider.class);
|
||||||
|
|
||||||
|
public MsiTokenProvider(final String tenantGuid, final String clientId) {
|
||||||
|
this.tenantGuid = tenantGuid;
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AzureADToken refreshToken() throws IOException {
|
||||||
|
LOG.debug("AADToken: refreshing token from MSI");
|
||||||
|
AzureADToken token = AzureADAuthenticator.getTokenFromMsi(tenantGuid, clientId, false);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities class http query parameters.
|
||||||
|
*/
|
||||||
|
public class QueryParams {
|
||||||
|
private Map<String, String> params = new HashMap<>();
|
||||||
|
private String apiVersion = null;
|
||||||
|
private String separator = "";
|
||||||
|
private String serializedString = null;
|
||||||
|
|
||||||
|
public void add(String name, String value) {
|
||||||
|
params.put(name, value);
|
||||||
|
serializedString = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApiVersion(String apiVersion) {
|
||||||
|
this.apiVersion = apiVersion;
|
||||||
|
serializedString = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String serialize() {
|
||||||
|
if (serializedString == null) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
try {
|
||||||
|
sb.append(separator);
|
||||||
|
sb.append(URLEncoder.encode(name, "UTF-8"));
|
||||||
|
sb.append('=');
|
||||||
|
sb.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
|
||||||
|
separator = "&";
|
||||||
|
} catch (UnsupportedEncodingException ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiVersion != null) {
|
||||||
|
sb.append(separator);
|
||||||
|
sb.append("api-version=");
|
||||||
|
sb.append(apiVersion);
|
||||||
|
separator = "&";
|
||||||
|
}
|
||||||
|
serializedString = sb.toString();
|
||||||
|
}
|
||||||
|
return serializedString;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides tokens based on refresh token.
|
||||||
|
*/
|
||||||
|
public class RefreshTokenBasedTokenProvider extends AccessTokenProvider {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AccessTokenProvider.class);
|
||||||
|
|
||||||
|
private final String clientId;
|
||||||
|
|
||||||
|
private final String refreshToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a token provider based on the refresh token provided.
|
||||||
|
*
|
||||||
|
* @param clientId the client ID (GUID) of the client web app obtained from Azure Active Directory configuration
|
||||||
|
* @param refreshToken the refresh token
|
||||||
|
*/
|
||||||
|
public RefreshTokenBasedTokenProvider(String clientId, String refreshToken) {
|
||||||
|
Preconditions.checkNotNull(clientId, "clientId");
|
||||||
|
Preconditions.checkNotNull(refreshToken, "refreshToken");
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.refreshToken = refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AzureADToken refreshToken() throws IOException {
|
||||||
|
LOG.debug("AADToken: refreshing refresh-token based token");
|
||||||
|
return AzureADAuthenticator.getTokenUsingRefreshToken(clientId, refreshToken);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
/**
|
||||||
|
* Provides tokens based on username and password.
|
||||||
|
*/
|
||||||
|
public class UserPasswordTokenProvider extends AccessTokenProvider {
|
||||||
|
|
||||||
|
private final String authEndpoint;
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AccessTokenProvider.class);
|
||||||
|
|
||||||
|
public UserPasswordTokenProvider(final String authEndpoint,
|
||||||
|
final String username, final String password) {
|
||||||
|
Preconditions.checkNotNull(authEndpoint, "authEndpoint");
|
||||||
|
Preconditions.checkNotNull(username, "username");
|
||||||
|
Preconditions.checkNotNull(password, "password");
|
||||||
|
|
||||||
|
this.authEndpoint = authEndpoint;
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AzureADToken refreshToken() throws IOException {
|
||||||
|
LOG.debug("AADToken: refreshing user-password based token");
|
||||||
|
return AzureADAuthenticator.getTokenUsingClientCreds(authEndpoint, username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPasswordString(Configuration conf, String key)
|
||||||
|
throws IOException {
|
||||||
|
char[] passchars = conf.getPassword(key);
|
||||||
|
if (passchars == null) {
|
||||||
|
throw new IOException("Password " + key + " not found");
|
||||||
|
}
|
||||||
|
return new String(passchars);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* 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.oauth2;
|
|
@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
||||||
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
|
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.*;
|
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.*;
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes.HTTPS_SCHEME;
|
import static org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes.HTTPS_SCHEME;
|
||||||
|
@ -42,7 +43,7 @@ import static org.apache.hadoop.fs.azurebfs.constants.HttpHeaderConfigurations.*
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.*;
|
import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbfsClient
|
* AbfsClient.
|
||||||
*/
|
*/
|
||||||
public class AbfsClient {
|
public class AbfsClient {
|
||||||
public static final Logger LOG = LoggerFactory.getLogger(AbfsClient.class);
|
public static final Logger LOG = LoggerFactory.getLogger(AbfsClient.class);
|
||||||
|
@ -54,9 +55,13 @@ public class AbfsClient {
|
||||||
private final AbfsConfiguration abfsConfiguration;
|
private final AbfsConfiguration abfsConfiguration;
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
|
|
||||||
|
private final AccessTokenProvider tokenProvider;
|
||||||
|
|
||||||
|
|
||||||
public AbfsClient(final URL baseUrl, final SharedKeyCredentials sharedKeyCredentials,
|
public AbfsClient(final URL baseUrl, final SharedKeyCredentials sharedKeyCredentials,
|
||||||
final AbfsConfiguration abfsConfiguration,
|
final AbfsConfiguration abfsConfiguration,
|
||||||
final ExponentialRetryPolicy exponentialRetryPolicy) {
|
final ExponentialRetryPolicy exponentialRetryPolicy,
|
||||||
|
final AccessTokenProvider tokenProvider) {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.sharedKeyCredentials = sharedKeyCredentials;
|
this.sharedKeyCredentials = sharedKeyCredentials;
|
||||||
String baseUrlString = baseUrl.toString();
|
String baseUrlString = baseUrl.toString();
|
||||||
|
@ -76,6 +81,7 @@ public class AbfsClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userAgent = initializeUserAgent(abfsConfiguration, sslProviderName);
|
this.userAgent = initializeUserAgent(abfsConfiguration, sslProviderName);
|
||||||
|
this.tokenProvider = tokenProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFileSystem() {
|
public String getFileSystem() {
|
||||||
|
@ -409,6 +415,14 @@ public class AbfsClient {
|
||||||
return encodedString;
|
return encodedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized String getAccessToken() throws IOException {
|
||||||
|
if (tokenProvider != null) {
|
||||||
|
return "Bearer " + tokenProvider.getToken().getAccessToken();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String initializeUserAgent(final AbfsConfiguration abfsConfiguration,
|
String initializeUserAgent(final AbfsConfiguration abfsConfiguration,
|
||||||
final String sslProviderName) {
|
final String sslProviderName) {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.apache.hadoop.fs.azurebfs.services;
|
package org.apache.hadoop.fs.azurebfs.services;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Http Request / Response Headers for Rest AbfsClient
|
* The Http Request / Response Headers for Rest AbfsClient.
|
||||||
*/
|
*/
|
||||||
public class AbfsHttpHeader {
|
public class AbfsHttpHeader {
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
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.AzureBlobFileSystemException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidAbfsRestOperationException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidAbfsRestOperationException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.constants.HttpHeaderConfigurations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The AbfsRestOperation for Rest AbfsClient.
|
* The AbfsRestOperation for Rest AbfsClient.
|
||||||
|
@ -48,7 +49,7 @@ public class AbfsRestOperation {
|
||||||
// request body and all the download methods have a response body.
|
// request body and all the download methods have a response body.
|
||||||
private final boolean hasRequestBody;
|
private final boolean hasRequestBody;
|
||||||
|
|
||||||
private final Logger LOG = LoggerFactory.getLogger(AbfsClient.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AbfsClient.class);
|
||||||
|
|
||||||
// For uploads, this is the request entity body. For downloads,
|
// For uploads, this is the request entity body. For downloads,
|
||||||
// this will hold the response entity body.
|
// this will hold the response entity body.
|
||||||
|
@ -139,9 +140,15 @@ public class AbfsRestOperation {
|
||||||
httpOperation = new AbfsHttpOperation(url, method, requestHeaders);
|
httpOperation = new AbfsHttpOperation(url, method, requestHeaders);
|
||||||
|
|
||||||
// sign the HTTP request
|
// sign the HTTP request
|
||||||
client.getSharedKeyCredentials().signRequest(
|
if (client.getAccessToken() == null) {
|
||||||
httpOperation.getConnection(),
|
// sign the HTTP request
|
||||||
hasRequestBody ? bufferLength : 0);
|
client.getSharedKeyCredentials().signRequest(
|
||||||
|
httpOperation.getConnection(),
|
||||||
|
hasRequestBody ? bufferLength : 0);
|
||||||
|
} else {
|
||||||
|
httpOperation.getConnection().setRequestProperty(HttpHeaderConfigurations.AUTHORIZATION,
|
||||||
|
client.getAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
if (hasRequestBody) {
|
if (hasRequestBody) {
|
||||||
// HttpUrlConnection requires
|
// HttpUrlConnection requires
|
||||||
|
@ -163,9 +170,7 @@ public class AbfsRestOperation {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG.isDebugEnabled()) {
|
LOG.debug("HttpRequest: " + httpOperation.toString());
|
||||||
LOG.debug("HttpRequest: " + httpOperation.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client.getRetryPolicy().shouldRetry(retryCount, httpOperation.getStatusCode())) {
|
if (client.getRetryPolicy().shouldRetry(retryCount, httpOperation.getStatusCode())) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth Type Enum.
|
||||||
|
*/
|
||||||
|
public enum AuthType {
|
||||||
|
SharedKey,
|
||||||
|
OAuth,
|
||||||
|
Custom
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
||||||
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.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
import org.apache.hadoop.fs.azure.AbstractWasbTestWithTimeout;
|
import org.apache.hadoop.fs.azure.AbstractWasbTestWithTimeout;
|
||||||
import org.apache.hadoop.fs.azure.AzureNativeFileSystemStore;
|
import org.apache.hadoop.fs.azure.AzureNativeFileSystemStore;
|
||||||
import org.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
import org.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
||||||
|
@ -62,7 +63,7 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
LoggerFactory.getLogger(AbstractAbfsIntegrationTest.class);
|
LoggerFactory.getLogger(AbstractAbfsIntegrationTest.class);
|
||||||
|
|
||||||
private final boolean isEmulator;
|
private boolean isEmulator;
|
||||||
private NativeAzureFileSystem wasb;
|
private NativeAzureFileSystem wasb;
|
||||||
private AzureBlobFileSystem abfs;
|
private AzureBlobFileSystem abfs;
|
||||||
private String abfsScheme;
|
private String abfsScheme;
|
||||||
|
@ -71,20 +72,18 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
private String fileSystemName;
|
private String fileSystemName;
|
||||||
private String accountName;
|
private String accountName;
|
||||||
private String testUrl;
|
private String testUrl;
|
||||||
|
private AuthType authType;
|
||||||
protected AbstractAbfsIntegrationTest(final boolean secure) {
|
|
||||||
this(secure ? FileSystemUriSchemes.ABFS_SECURE_SCHEME : FileSystemUriSchemes.ABFS_SCHEME);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AbstractAbfsIntegrationTest() {
|
protected AbstractAbfsIntegrationTest() {
|
||||||
this(FileSystemUriSchemes.ABFS_SCHEME);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AbstractAbfsIntegrationTest(final String scheme) {
|
|
||||||
abfsScheme = scheme;
|
|
||||||
fileSystemName = ABFS_TEST_CONTAINER_PREFIX + UUID.randomUUID().toString();
|
fileSystemName = ABFS_TEST_CONTAINER_PREFIX + UUID.randomUUID().toString();
|
||||||
configuration = new Configuration();
|
configuration = new Configuration();
|
||||||
configuration.addResource(ABFS_TEST_RESOURCE_XML);
|
configuration.addResource(ABFS_TEST_RESOURCE_XML);
|
||||||
|
this.accountName = this.configuration.get(FS_AZURE_TEST_ACCOUNT_NAME);
|
||||||
|
|
||||||
|
authType = configuration.getEnum(FS_AZURE_ACCOUNT_AUTH_TYPE_PROPERTY_NAME
|
||||||
|
+ accountName, AuthType.SharedKey);
|
||||||
|
abfsScheme = authType == AuthType.SharedKey ? FileSystemUriSchemes.ABFS_SCHEME
|
||||||
|
: FileSystemUriSchemes.ABFS_SECURE_SCHEME;
|
||||||
|
|
||||||
String accountName = configuration.get(FS_AZURE_TEST_ACCOUNT_NAME, "");
|
String accountName = configuration.get(FS_AZURE_TEST_ACCOUNT_NAME, "");
|
||||||
assumeTrue("Not set: " + FS_AZURE_TEST_ACCOUNT_NAME,
|
assumeTrue("Not set: " + FS_AZURE_TEST_ACCOUNT_NAME,
|
||||||
|
@ -94,8 +93,13 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
accountName, containsString("dfs.core.windows.net"));
|
accountName, containsString("dfs.core.windows.net"));
|
||||||
String fullKey = FS_AZURE_TEST_ACCOUNT_KEY_PREFIX
|
String fullKey = FS_AZURE_TEST_ACCOUNT_KEY_PREFIX
|
||||||
+ accountName;
|
+ accountName;
|
||||||
assumeTrue("Not set: " + fullKey,
|
|
||||||
configuration.get(fullKey) != null);
|
if (authType == AuthType.SharedKey) {
|
||||||
|
assumeTrue("Not set: " + fullKey, configuration.get(fullKey) != null);
|
||||||
|
} else {
|
||||||
|
String accessTokenProviderKey = FS_AZURE_ACCOUNT_TOKEN_PROVIDER_TYPE_PROPERTY_NAME + accountName;
|
||||||
|
assumeTrue("Not set: " + accessTokenProviderKey, configuration.get(accessTokenProviderKey) != null);
|
||||||
|
}
|
||||||
|
|
||||||
final String abfsUrl = this.getFileSystemName() + "@" + this.getAccountName();
|
final String abfsUrl = this.getFileSystemName() + "@" + this.getAccountName();
|
||||||
URI defaultUri = null;
|
URI defaultUri = null;
|
||||||
|
@ -110,7 +114,6 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
configuration.set(CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY, defaultUri.toString());
|
configuration.set(CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY, defaultUri.toString());
|
||||||
configuration.setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION, true);
|
configuration.setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION, true);
|
||||||
this.isEmulator = this.configuration.getBoolean(FS_AZURE_EMULATOR_ENABLED, false);
|
this.isEmulator = this.configuration.getBoolean(FS_AZURE_EMULATOR_ENABLED, false);
|
||||||
this.accountName = this.configuration.get(FS_AZURE_TEST_ACCOUNT_NAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,7 +122,7 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
//Create filesystem first to make sure getWasbFileSystem() can return an existing filesystem.
|
//Create filesystem first to make sure getWasbFileSystem() can return an existing filesystem.
|
||||||
createFileSystem();
|
createFileSystem();
|
||||||
|
|
||||||
if (!isEmulator) {
|
if (!isEmulator && authType == AuthType.SharedKey) {
|
||||||
final URI wasbUri = new URI(abfsUrlToWasbUrl(getTestUrl()));
|
final URI wasbUri = new URI(abfsUrlToWasbUrl(getTestUrl()));
|
||||||
final AzureNativeFileSystemStore azureNativeFileSystemStore =
|
final AzureNativeFileSystemStore azureNativeFileSystemStore =
|
||||||
new AzureNativeFileSystemStore();
|
new AzureNativeFileSystemStore();
|
||||||
|
@ -234,6 +237,10 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
return isEmulator;
|
return isEmulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AuthType getAuthType() {
|
||||||
|
return this.authType;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a buffer to a file.
|
* Write a buffer to a file.
|
||||||
* @param path path
|
* @param path path
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package org.apache.hadoop.fs.azurebfs;
|
package org.apache.hadoop.fs.azurebfs;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
|
|
||||||
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.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
@ -30,6 +29,7 @@ import org.junit.Test;
|
||||||
*/
|
*/
|
||||||
public final class ITestAbfsClient extends AbstractAbfsIntegrationTest {
|
public final class ITestAbfsClient extends AbstractAbfsIntegrationTest {
|
||||||
private static final int LIST_MAX_RESULTS = 5000;
|
private static final int LIST_MAX_RESULTS = 5000;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContinuationTokenHavingEqualSign() throws Exception {
|
public void testContinuationTokenHavingEqualSign() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
final AzureBlobFileSystem fs = this.getFileSystem();
|
||||||
|
|
|
@ -22,8 +22,11 @@ import com.microsoft.azure.storage.CloudStorageAccount;
|
||||||
import com.microsoft.azure.storage.blob.CloudBlobClient;
|
import com.microsoft.azure.storage.blob.CloudBlobClient;
|
||||||
import com.microsoft.azure.storage.blob.CloudBlobContainer;
|
import com.microsoft.azure.storage.blob.CloudBlobContainer;
|
||||||
import com.microsoft.azure.storage.blob.CloudBlockBlob;
|
import com.microsoft.azure.storage.blob.CloudBlockBlob;
|
||||||
|
|
||||||
|
import org.junit.Assume;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
import org.apache.hadoop.fs.FileStatus;
|
import org.apache.hadoop.fs.FileStatus;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
|
|
||||||
|
@ -34,6 +37,7 @@ public class ITestAzureBlobFileSystemBackCompat extends
|
||||||
AbstractAbfsIntegrationTest {
|
AbstractAbfsIntegrationTest {
|
||||||
public ITestAzureBlobFileSystemBackCompat() {
|
public ITestAzureBlobFileSystemBackCompat() {
|
||||||
super();
|
super();
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.SharedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -34,9 +34,6 @@ public class ITestAzureBlobFileSystemFileStatus extends
|
||||||
AbstractAbfsIntegrationTest {
|
AbstractAbfsIntegrationTest {
|
||||||
private static final Path TEST_FILE = new Path("testFile");
|
private static final Path TEST_FILE = new Path("testFile");
|
||||||
private static final Path TEST_FOLDER = new Path("testDir");
|
private static final Path TEST_FOLDER = new Path("testDir");
|
||||||
public ITestAzureBlobFileSystemFileStatus() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEnsureStatusWorksForRoot() throws Exception {
|
public void testEnsureStatusWorksForRoot() throws Exception {
|
||||||
|
|
|
@ -25,12 +25,14 @@ import org.junit.Test;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test finalize() method when "fs.abfs.impl.disable.cache" is enabled.
|
* Test finalize() method when "fs.abfs.impl.disable.cache" is enabled.
|
||||||
*/
|
*/
|
||||||
public class ITestAzureBlobFileSystemFinalize extends AbstractAbfsScaleTest{
|
public class ITestAzureBlobFileSystemFinalize extends AbstractAbfsScaleTest{
|
||||||
static final String DISABLE_CACHE_KEY = "fs.abfs.impl.disable.cache";
|
static final String DISABLE_ABFS_CACHE_KEY = "fs.abfs.impl.disable.cache";
|
||||||
|
static final String DISABLE_ABFSSS_CACHE_KEY = "fs.abfss.impl.disable.cache";
|
||||||
|
|
||||||
public ITestAzureBlobFileSystemFinalize() throws Exception {
|
public ITestAzureBlobFileSystemFinalize() throws Exception {
|
||||||
super();
|
super();
|
||||||
|
@ -40,7 +42,9 @@ public class ITestAzureBlobFileSystemFinalize extends AbstractAbfsScaleTest{
|
||||||
public void testFinalize() throws Exception {
|
public void testFinalize() throws Exception {
|
||||||
// Disable the cache for filesystem to make sure there is no reference.
|
// Disable the cache for filesystem to make sure there is no reference.
|
||||||
Configuration configuration = this.getConfiguration();
|
Configuration configuration = this.getConfiguration();
|
||||||
configuration.setBoolean(this.DISABLE_CACHE_KEY, true);
|
configuration.setBoolean(
|
||||||
|
this.getAuthType() == AuthType.SharedKey ? DISABLE_ABFS_CACHE_KEY : DISABLE_ABFSSS_CACHE_KEY,
|
||||||
|
true);
|
||||||
|
|
||||||
AzureBlobFileSystem fs = (AzureBlobFileSystem) FileSystem.get(configuration);
|
AzureBlobFileSystem fs = (AzureBlobFileSystem) FileSystem.get(configuration);
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,9 @@ import java.io.IOException;
|
||||||
import com.microsoft.azure.storage.blob.BlockEntry;
|
import com.microsoft.azure.storage.blob.BlockEntry;
|
||||||
import com.microsoft.azure.storage.blob.BlockListingFilter;
|
import com.microsoft.azure.storage.blob.BlockListingFilter;
|
||||||
import com.microsoft.azure.storage.blob.CloudBlockBlob;
|
import com.microsoft.azure.storage.blob.CloudBlockBlob;
|
||||||
import org.apache.hadoop.fs.azure.AzureBlobStorageTestAccount;
|
|
||||||
import org.hamcrest.core.IsEqual;
|
import org.hamcrest.core.IsEqual;
|
||||||
import org.hamcrest.core.IsNot;
|
import org.hamcrest.core.IsNot;
|
||||||
|
import org.junit.Assume;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.FSDataInputStream;
|
import org.apache.hadoop.fs.FSDataInputStream;
|
||||||
|
@ -42,6 +42,9 @@ 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.apache.hadoop.fs.azure.AzureBlobStorageTestAccount;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test flush operation.
|
* Test flush operation.
|
||||||
*/
|
*/
|
||||||
|
@ -209,6 +212,8 @@ public class ITestAzureBlobFileSystemFlush extends AbstractAbfsScaleTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFlushWithFlushEnabled() throws Exception {
|
public void testFlushWithFlushEnabled() throws Exception {
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.SharedKey);
|
||||||
|
|
||||||
AzureBlobStorageTestAccount testAccount = createWasbTestAccount();
|
AzureBlobStorageTestAccount testAccount = createWasbTestAccount();
|
||||||
String wasbUrl = testAccount.getFileSystem().getName();
|
String wasbUrl = testAccount.getFileSystem().getName();
|
||||||
String abfsUrl = wasbUrlToAbfsUrl(wasbUrl);
|
String abfsUrl = wasbUrlToAbfsUrl(wasbUrl);
|
||||||
|
@ -228,6 +233,7 @@ public class ITestAzureBlobFileSystemFlush extends AbstractAbfsScaleTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFlushWithFlushDisabled() throws Exception {
|
public void testFlushWithFlushDisabled() throws Exception {
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.SharedKey);
|
||||||
AzureBlobStorageTestAccount testAccount = createWasbTestAccount();
|
AzureBlobStorageTestAccount testAccount = createWasbTestAccount();
|
||||||
String wasbUrl = testAccount.getFileSystem().getName();
|
String wasbUrl = testAccount.getFileSystem().getName();
|
||||||
String abfsUrl = wasbUrlToAbfsUrl(wasbUrl);
|
String abfsUrl = wasbUrlToAbfsUrl(wasbUrl);
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys;
|
||||||
|
import org.junit.Assume;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.fs.FSDataInputStream;
|
||||||
|
import org.apache.hadoop.fs.FSDataOutputStream;
|
||||||
|
import org.apache.hadoop.fs.FileStatus;
|
||||||
|
import org.apache.hadoop.fs.Path;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ACCOUNT_OAUTH_CLIENT_ID;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_ACCOUNT_OAUTH_CLIENT_SECRET;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_ID;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_SECRET;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.FS_AZURE_BLOB_DATA_READER_CLIENT_ID;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.FS_AZURE_BLOB_DATA_READER_CLIENT_SECRET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Azure Oauth with Blob Data contributor role and Blob Data Reader role.
|
||||||
|
* The Test AAD client need to be configured manually through Azure Portal, then save their properties in
|
||||||
|
* configuration files.
|
||||||
|
*/
|
||||||
|
public class ITestAzureBlobFileSystemOauth extends AbstractAbfsIntegrationTest{
|
||||||
|
|
||||||
|
private static final Path FILE_PATH = new Path("/testFile");
|
||||||
|
private static final Path EXISTED_FILE_PATH = new Path("/existedFile");
|
||||||
|
private static final Path EXISTED_FOLDER_PATH = new Path("/existedFolder");
|
||||||
|
|
||||||
|
public ITestAzureBlobFileSystemOauth() {
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.OAuth);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* BLOB DATA CONTRIBUTOR should have full access to the container and blobs in the container.
|
||||||
|
* */
|
||||||
|
@Test
|
||||||
|
public void testBlobDataContributor() throws Exception {
|
||||||
|
String clientId = this.getConfiguration().get(TestConfigurationKeys.FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_ID);
|
||||||
|
Assume.assumeTrue("Contributor client id not provided", clientId != null);
|
||||||
|
String secret = this.getConfiguration().get(TestConfigurationKeys.FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_SECRET);
|
||||||
|
Assume.assumeTrue("Contributor client secret not provided", secret != null);
|
||||||
|
|
||||||
|
prepareFiles();
|
||||||
|
|
||||||
|
final AzureBlobFileSystem fs = getBlobConributor();
|
||||||
|
|
||||||
|
// create and write into file in current container/fs
|
||||||
|
try(FSDataOutputStream stream = fs.create(FILE_PATH)) {
|
||||||
|
stream.write(0);
|
||||||
|
}
|
||||||
|
assertTrue(fs.exists(FILE_PATH));
|
||||||
|
FileStatus fileStatus = fs.getFileStatus(FILE_PATH);
|
||||||
|
assertEquals(1, fileStatus.getLen());
|
||||||
|
// delete file
|
||||||
|
assertTrue(fs.delete(FILE_PATH, true));
|
||||||
|
assertFalse(fs.exists(FILE_PATH));
|
||||||
|
|
||||||
|
// Verify Blob Data Contributor has full access to existed folder, file
|
||||||
|
|
||||||
|
// READ FOLDER
|
||||||
|
assertTrue(fs.exists(EXISTED_FOLDER_PATH));
|
||||||
|
|
||||||
|
//DELETE FOLDER
|
||||||
|
fs.delete(EXISTED_FOLDER_PATH, true);
|
||||||
|
assertFalse(fs.exists(EXISTED_FOLDER_PATH));
|
||||||
|
|
||||||
|
// READ FILE
|
||||||
|
try (FSDataInputStream stream = fs.open(EXISTED_FILE_PATH)) {
|
||||||
|
assertTrue(stream.read() != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, fs.getFileStatus(EXISTED_FILE_PATH).getLen());
|
||||||
|
|
||||||
|
// WRITE FILE
|
||||||
|
try (FSDataOutputStream stream = fs.append(EXISTED_FILE_PATH)) {
|
||||||
|
stream.write(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, fs.getFileStatus(EXISTED_FILE_PATH).getLen());
|
||||||
|
|
||||||
|
// REMOVE FILE
|
||||||
|
fs.delete(EXISTED_FILE_PATH, true);
|
||||||
|
assertFalse(fs.exists(EXISTED_FILE_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BLOB DATA READER should have only READ access to the container and blobs in the container.
|
||||||
|
* */
|
||||||
|
@Test
|
||||||
|
public void testBlobDataReader() throws Exception {
|
||||||
|
String clientId = this.getConfiguration().get(TestConfigurationKeys.FS_AZURE_BLOB_DATA_READER_CLIENT_ID);
|
||||||
|
Assume.assumeTrue("Reader client id not provided", clientId != null);
|
||||||
|
String secret = this.getConfiguration().get(TestConfigurationKeys.FS_AZURE_BLOB_DATA_READER_CLIENT_SECRET);
|
||||||
|
Assume.assumeTrue("Reader client secret not provided", secret != null);
|
||||||
|
|
||||||
|
prepareFiles();
|
||||||
|
final AzureBlobFileSystem fs = getBlobReader();
|
||||||
|
|
||||||
|
// Use abfsStore in this test to verify the ERROR code in AbfsRestOperationException
|
||||||
|
AzureBlobFileSystemStore abfsStore = fs.getAbfsStore();
|
||||||
|
// TEST READ FS
|
||||||
|
Map<String, String> properties = abfsStore.getFilesystemProperties();
|
||||||
|
// TEST READ FOLDER
|
||||||
|
assertTrue(fs.exists(EXISTED_FOLDER_PATH));
|
||||||
|
|
||||||
|
// TEST DELETE FOLDER
|
||||||
|
try {
|
||||||
|
abfsStore.delete(EXISTED_FOLDER_PATH, true);
|
||||||
|
} catch (AbfsRestOperationException e) {
|
||||||
|
assertEquals(AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH, e.getErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST READ FILE
|
||||||
|
try (InputStream inputStream = abfsStore.openFileForRead(EXISTED_FILE_PATH, null)) {
|
||||||
|
assertTrue(inputStream.read() != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST WRITE FILE
|
||||||
|
try {
|
||||||
|
abfsStore.openFileForWrite(EXISTED_FILE_PATH, true);
|
||||||
|
} catch (AbfsRestOperationException e) {
|
||||||
|
assertEquals(AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH, e.getErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareFiles() throws IOException {
|
||||||
|
// create test files/folders to verify access control diff between
|
||||||
|
// Blob data contributor and Blob data reader
|
||||||
|
final AzureBlobFileSystem fs = this.getFileSystem();
|
||||||
|
fs.create(EXISTED_FILE_PATH);
|
||||||
|
assertTrue(fs.exists(EXISTED_FILE_PATH));
|
||||||
|
fs.mkdirs(EXISTED_FOLDER_PATH);
|
||||||
|
assertTrue(fs.exists(EXISTED_FOLDER_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AzureBlobFileSystem getBlobConributor() throws Exception {
|
||||||
|
Configuration configuration = this.getConfiguration();
|
||||||
|
configuration.set(FS_AZURE_ACCOUNT_OAUTH_CLIENT_ID + this.getAccountName(), configuration.get(FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_ID));
|
||||||
|
configuration.set(FS_AZURE_ACCOUNT_OAUTH_CLIENT_SECRET + this.getAccountName(), configuration.get(FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_SECRET));
|
||||||
|
return getFileSystem(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AzureBlobFileSystem getBlobReader() throws Exception {
|
||||||
|
Configuration configuration = this.getConfiguration();
|
||||||
|
configuration.set(FS_AZURE_ACCOUNT_OAUTH_CLIENT_ID + this.getAccountName(), configuration.get(FS_AZURE_BLOB_DATA_READER_CLIENT_ID));
|
||||||
|
configuration.set(FS_AZURE_ACCOUNT_OAUTH_CLIENT_SECRET + this.getAccountName(), configuration.get(FS_AZURE_BLOB_DATA_READER_CLIENT_SECRET));
|
||||||
|
return getFileSystem(configuration);
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import java.io.IOException;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.junit.Assume;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -34,6 +35,7 @@ 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.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
import org.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
import org.apache.hadoop.fs.contract.ContractTestUtils;
|
import org.apache.hadoop.fs.contract.ContractTestUtils;
|
||||||
|
|
||||||
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
|
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
|
||||||
|
@ -66,6 +68,7 @@ public class ITestAzureBlobFileSystemRandomRead extends
|
||||||
|
|
||||||
public ITestAzureBlobFileSystemRandomRead() throws Exception {
|
public ITestAzureBlobFileSystemRandomRead() throws Exception {
|
||||||
super();
|
super();
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.SharedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test AzureBlobFileSystem initialization.
|
* Test AzureBlobFileSystem initialization.
|
||||||
|
@ -41,8 +42,10 @@ public class ITestFileSystemInitialization extends AbstractAbfsIntegrationTest {
|
||||||
final String accountName = getAccountName();
|
final String accountName = getAccountName();
|
||||||
final String filesystem = getFileSystemName();
|
final String filesystem = getFileSystemName();
|
||||||
|
|
||||||
|
String scheme = this.getAuthType() == AuthType.SharedKey ? FileSystemUriSchemes.ABFS_SCHEME
|
||||||
|
: FileSystemUriSchemes.ABFS_SECURE_SCHEME;
|
||||||
assertEquals(fs.getUri(),
|
assertEquals(fs.getUri(),
|
||||||
new URI(FileSystemUriSchemes.ABFS_SCHEME,
|
new URI(scheme,
|
||||||
filesystem + "@" + accountName,
|
filesystem + "@" + accountName,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
||||||
import org.apache.hadoop.fs.FileContext;
|
import org.apache.hadoop.fs.FileContext;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test AzureBlobFileSystem registration.
|
* Test AzureBlobFileSystem registration.
|
||||||
|
@ -79,8 +80,14 @@ public class ITestFileSystemRegistration extends AbstractAbfsIntegrationTest {
|
||||||
AzureBlobFileSystem fs = (AzureBlobFileSystem) FileSystem.get(getConfiguration());
|
AzureBlobFileSystem fs = (AzureBlobFileSystem) FileSystem.get(getConfiguration());
|
||||||
assertNotNull("filesystem", fs);
|
assertNotNull("filesystem", fs);
|
||||||
|
|
||||||
Abfs afs = (Abfs) FileContext.getFileContext(getConfiguration()).getDefaultFileSystem();
|
if (this.getAuthType() == AuthType.OAuth) {
|
||||||
assertNotNull("filecontext", afs);
|
Abfss afs = (Abfss) FileContext.getFileContext(getConfiguration()).getDefaultFileSystem();
|
||||||
|
assertNotNull("filecontext", afs);
|
||||||
|
} else {
|
||||||
|
Abfs afs = (Abfs) FileContext.getFileContext(getConfiguration()).getDefaultFileSystem();
|
||||||
|
assertNotNull("filecontext", afs);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
import org.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
||||||
import org.apache.hadoop.fs.contract.ContractTestUtils;
|
import org.apache.hadoop.fs.contract.ContractTestUtils;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertDeleted;
|
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertDeleted;
|
||||||
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertIsDirectory;
|
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertIsDirectory;
|
||||||
|
@ -50,6 +51,7 @@ public class ITestWasbAbfsCompatibility extends AbstractAbfsIntegrationTest {
|
||||||
|
|
||||||
public ITestWasbAbfsCompatibility() throws Exception {
|
public ITestWasbAbfsCompatibility() throws Exception {
|
||||||
Assume.assumeFalse("Emulator is not supported", isEmulator());
|
Assume.assumeFalse("Emulator is not supported", isEmulator());
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.SharedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -28,6 +28,12 @@ public final class TestConfigurationKeys {
|
||||||
public static final String FS_AZURE_TEST_HOST_PORT = "fs.azure.test.host.port";
|
public static final String FS_AZURE_TEST_HOST_PORT = "fs.azure.test.host.port";
|
||||||
public static final String FS_AZURE_CONTRACT_TEST_URI = "fs.contract.test.fs.abfs";
|
public static final String FS_AZURE_CONTRACT_TEST_URI = "fs.contract.test.fs.abfs";
|
||||||
|
|
||||||
|
public static final String FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_ID = "fs.azure.account.oauth2.contributor.client.id";
|
||||||
|
public static final String FS_AZURE_BLOB_DATA_CONTRIBUTOR_CLIENT_SECRET = "fs.azure.account.oauth2.contributor.client.secret";
|
||||||
|
|
||||||
|
public static final String FS_AZURE_BLOB_DATA_READER_CLIENT_ID = "fs.azure.account.oauth2.reader.client.id";
|
||||||
|
public static final String FS_AZURE_BLOB_DATA_READER_CLIENT_SECRET = "fs.azure.account.oauth2.reader.client.secret";
|
||||||
|
|
||||||
public static final String ABFS_TEST_RESOURCE_XML = "azure-bfs-test.xml";
|
public static final String ABFS_TEST_RESOURCE_XML = "azure-bfs-test.xml";
|
||||||
|
|
||||||
public static final String ABFS_TEST_CONTAINER_PREFIX = "abfs-testcontainer-";
|
public static final String ABFS_TEST_CONTAINER_PREFIX = "abfs-testcontainer-";
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
||||||
import org.apache.hadoop.fs.azurebfs.AbstractAbfsIntegrationTest;
|
import org.apache.hadoop.fs.azurebfs.AbstractAbfsIntegrationTest;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys;
|
import org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind ABFS contract tests to the Azure test setup/teardown.
|
* Bind ABFS contract tests to the Azure test setup/teardown.
|
||||||
|
@ -32,18 +33,17 @@ import org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys;
|
||||||
public class ABFSContractTestBinding extends AbstractAbfsIntegrationTest {
|
public class ABFSContractTestBinding extends AbstractAbfsIntegrationTest {
|
||||||
private final URI testUri;
|
private final URI testUri;
|
||||||
|
|
||||||
public ABFSContractTestBinding(final boolean secure) throws Exception {
|
public ABFSContractTestBinding() throws Exception {
|
||||||
this(secure, true);
|
this(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ABFSContractTestBinding(final boolean secure,
|
public ABFSContractTestBinding(
|
||||||
final boolean useExistingFileSystem) throws Exception{
|
final boolean useExistingFileSystem) throws Exception{
|
||||||
super(secure);
|
|
||||||
if (useExistingFileSystem) {
|
if (useExistingFileSystem) {
|
||||||
Configuration configuration = getConfiguration();
|
Configuration configuration = getConfiguration();
|
||||||
String testUrl = configuration.get(TestConfigurationKeys.FS_AZURE_CONTRACT_TEST_URI);
|
String testUrl = configuration.get(TestConfigurationKeys.FS_AZURE_CONTRACT_TEST_URI);
|
||||||
|
|
||||||
if (secure) {
|
if (getAuthType() != AuthType.SharedKey) {
|
||||||
testUrl = testUrl.replaceFirst(FileSystemUriSchemes.ABFS_SCHEME, FileSystemUriSchemes.ABFS_SECURE_SCHEME);
|
testUrl = testUrl.replaceFirst(FileSystemUriSchemes.ABFS_SCHEME, FileSystemUriSchemes.ABFS_SECURE_SCHEME);
|
||||||
}
|
}
|
||||||
setTestUrl(testUrl);
|
setTestUrl(testUrl);
|
||||||
|
@ -61,4 +61,8 @@ public class ABFSContractTestBinding extends AbstractAbfsIntegrationTest {
|
||||||
public Configuration getConfiguration() {
|
public Configuration getConfiguration() {
|
||||||
return super.getConfiguration();
|
return super.getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSecureMode() {
|
||||||
|
return this.getAuthType() == AuthType.SharedKey ? false : true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,34 +18,23 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractAppendTest;
|
import org.apache.hadoop.fs.contract.AbstractContractAppendTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.apache.hadoop.fs.contract.ContractTestUtils.skip;
|
import static org.apache.hadoop.fs.contract.ContractTestUtils.skip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract test for open operation.
|
* Contract test for open operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractAppend extends AbstractContractAppendTest {
|
public class ITestAbfsFileSystemContractAppend extends AbstractContractAppendTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractAppend(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractAppend() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(this.isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,11 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractConcatTest;
|
import org.apache.hadoop.fs.contract.AbstractContractConcatTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -29,19 +24,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for concat operation.
|
* Contract test for concat operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractConcat extends AbstractContractConcatTest{
|
public class ITestAbfsFileSystemContractConcat extends AbstractContractConcatTest{
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractConcat(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractConcat() throws Exception {
|
||||||
isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractCreateTest;
|
import org.apache.hadoop.fs.contract.AbstractContractCreateTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -30,19 +25,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for create operation.
|
* Contract test for create operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractCreate extends AbstractContractCreateTest{
|
public class ITestAbfsFileSystemContractCreate extends AbstractContractCreateTest{
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractCreate(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractCreate() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(this.isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractDeleteTest;
|
import org.apache.hadoop.fs.contract.AbstractContractDeleteTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -30,19 +25,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for delete operation.
|
* Contract test for delete operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractDelete extends AbstractContractDeleteTest {
|
public class ITestAbfsFileSystemContractDelete extends AbstractContractDeleteTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractDelete(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractDelete() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class ITestAbfsFileSystemContractDistCp extends AbstractContractDistCpTes
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractDistCp() throws Exception {
|
public ITestAbfsFileSystemContractDistCp() throws Exception {
|
||||||
binding = new ABFSContractTestBinding(false);
|
binding = new ABFSContractTestBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,11 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractGetFileStatusTest;
|
import org.apache.hadoop.fs.contract.AbstractContractGetFileStatusTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -29,19 +24,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for getFileStatus operation.
|
* Contract test for getFileStatus operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractGetFileStatus extends AbstractContractGetFileStatusTest {
|
public class ITestAbfsFileSystemContractGetFileStatus extends AbstractContractGetFileStatusTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractGetFileStatus(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractGetFileStatus() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractMkdirTest;
|
import org.apache.hadoop.fs.contract.AbstractContractMkdirTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -30,19 +25,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for mkdir operation.
|
* Contract test for mkdir operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractMkdir extends AbstractContractMkdirTest {
|
public class ITestAbfsFileSystemContractMkdir extends AbstractContractMkdirTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractMkdir(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractMkdir() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(secure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractOpenTest;
|
import org.apache.hadoop.fs.contract.AbstractContractOpenTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -30,19 +25,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for open operation.
|
* Contract test for open operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractOpen extends AbstractContractOpenTest {
|
public class ITestAbfsFileSystemContractOpen extends AbstractContractOpenTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractOpen(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractOpen() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(this.isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractRenameTest;
|
import org.apache.hadoop.fs.contract.AbstractContractRenameTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -30,19 +25,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for rename operation.
|
* Contract test for rename operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractRename extends AbstractContractRenameTest {
|
public class ITestAbfsFileSystemContractRename extends AbstractContractRenameTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractRename(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractRename() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(this.isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,31 +17,21 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractRootDirectoryTest;
|
import org.apache.hadoop.fs.contract.AbstractContractRootDirectoryTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract test for root directory operation.
|
* Contract test for root directory operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractRootDirectory extends AbstractContractRootDirectoryTest {
|
public class ITestAbfsFileSystemContractRootDirectory extends AbstractContractRootDirectoryTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractRootDirectory(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractRootDirectory() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(secure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class ITestAbfsFileSystemContractSecureDistCp extends AbstractContractDis
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractSecureDistCp() throws Exception {
|
public ITestAbfsFileSystemContractSecureDistCp() throws Exception {
|
||||||
binding = new ABFSContractTestBinding(true);
|
binding = new ABFSContractTestBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractSeekTest;
|
import org.apache.hadoop.fs.contract.AbstractContractSeekTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -30,19 +25,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for seek operation.
|
* Contract test for seek operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractSeek extends AbstractContractSeekTest{
|
public class ITestAbfsFileSystemContractSeek extends AbstractContractSeekTest{
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractSeek(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractSeek() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(this.isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,11 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.fs.azurebfs.contract;
|
package org.apache.hadoop.fs.azurebfs.contract;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.contract.AbstractContractSetTimesTest;
|
import org.apache.hadoop.fs.contract.AbstractContractSetTimesTest;
|
||||||
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
|
@ -29,19 +24,13 @@ import org.apache.hadoop.fs.contract.AbstractFSContract;
|
||||||
/**
|
/**
|
||||||
* Contract test for setTimes operation.
|
* Contract test for setTimes operation.
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class ITestAbfsFileSystemContractSetTimes extends AbstractContractSetTimesTest {
|
public class ITestAbfsFileSystemContractSetTimes extends AbstractContractSetTimesTest {
|
||||||
@Parameterized.Parameters(name = "SecureMode={0}")
|
|
||||||
public static Iterable<Object[]> secure() {
|
|
||||||
return Arrays.asList(new Object[][] { {true}, {false} });
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean isSecure;
|
private final boolean isSecure;
|
||||||
private final ABFSContractTestBinding binding;
|
private final ABFSContractTestBinding binding;
|
||||||
|
|
||||||
public ITestAbfsFileSystemContractSetTimes(final boolean secure) throws Exception {
|
public ITestAbfsFileSystemContractSetTimes() throws Exception {
|
||||||
this.isSecure = secure;
|
binding = new ABFSContractTestBinding();
|
||||||
binding = new ABFSContractTestBinding(this.isSecure);
|
this.isSecure = binding.isSecureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class ITestAzureBlobFileSystemBasics extends FileSystemContractBaseTest {
|
||||||
public ITestAzureBlobFileSystemBasics() throws Exception {
|
public ITestAzureBlobFileSystemBasics() throws Exception {
|
||||||
// If all contract tests are running in parallel, some root level tests in FileSystemContractBaseTest will fail
|
// If all contract tests are running in parallel, some root level tests in FileSystemContractBaseTest will fail
|
||||||
// due to the race condition. Hence for this contract test it should be tested in different container
|
// due to the race condition. Hence for this contract test it should be tested in different container
|
||||||
binding = new ABFSContractTestBinding(false, false);
|
binding = new ABFSContractTestBinding(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,11 @@ package org.apache.hadoop.fs.azurebfs.services;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
|
||||||
import org.apache.hadoop.fs.azurebfs.utils.SSLSocketFactoryEx;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.utils.SSLSocketFactoryEx;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public final class TestAbfsClient {
|
||||||
AbfsConfiguration config,
|
AbfsConfiguration config,
|
||||||
boolean includeSSLProvider) {
|
boolean includeSSLProvider) {
|
||||||
AbfsClient client = new AbfsClient(baseUrl, null,
|
AbfsClient client = new AbfsClient(baseUrl, null,
|
||||||
config, null);
|
config, null, null);
|
||||||
String sslProviderName = null;
|
String sslProviderName = null;
|
||||||
if (includeSSLProvider) {
|
if (includeSSLProvider) {
|
||||||
sslProviderName = SSLSocketFactoryEx.getDefaultFactory().getProviderName();
|
sslProviderName = SSLSocketFactoryEx.getDefaultFactory().getProviderName();
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* 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.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.QueryParams;
|
||||||
|
/**
|
||||||
|
* Test query params serialization.
|
||||||
|
*/
|
||||||
|
public class TestQueryParams {
|
||||||
|
private static final String SEPARATOR = "&";
|
||||||
|
private static final String[][] PARAM_ARRAY = {{"K0", "V0"}, {"K1", "V1"}, {"K2", "V2"}};
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneParam() {
|
||||||
|
String key = PARAM_ARRAY[0][0];
|
||||||
|
String value = PARAM_ARRAY[0][1];
|
||||||
|
|
||||||
|
Map<String, String> paramMap = new HashMap<>();
|
||||||
|
paramMap.put(key, value);
|
||||||
|
|
||||||
|
QueryParams qp = new QueryParams();
|
||||||
|
qp.add(key, value);
|
||||||
|
Assert.assertEquals(key + "=" + value, qp.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleParams() {
|
||||||
|
QueryParams qp = new QueryParams();
|
||||||
|
for (String[] entry : PARAM_ARRAY) {
|
||||||
|
qp.add(entry[0], entry[1]);
|
||||||
|
}
|
||||||
|
Map<String, String> paramMap = constructMap(qp.serialize());
|
||||||
|
Assert.assertEquals(PARAM_ARRAY.length, paramMap.size());
|
||||||
|
|
||||||
|
for (String[] entry : PARAM_ARRAY) {
|
||||||
|
Assert.assertTrue(paramMap.containsKey(entry[0]));
|
||||||
|
Assert.assertEquals(entry[1], paramMap.get(entry[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> constructMap(String input) {
|
||||||
|
String[] entries = input.split(SEPARATOR);
|
||||||
|
Map<String, String> paramMap = new HashMap<>();
|
||||||
|
for (String entry : entries) {
|
||||||
|
String[] keyValue = entry.split("=");
|
||||||
|
paramMap.put(keyValue[0], keyValue[1]);
|
||||||
|
}
|
||||||
|
return paramMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,9 +21,13 @@ import com.microsoft.azure.storage.CloudStorageAccount;
|
||||||
import com.microsoft.azure.storage.blob.CloudBlobClient;
|
import com.microsoft.azure.storage.blob.CloudBlobClient;
|
||||||
import com.microsoft.azure.storage.blob.CloudBlobContainer;
|
import com.microsoft.azure.storage.blob.CloudBlobContainer;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.azure.AzureBlobStorageTestAccount;
|
import org.junit.Assume;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azure.AzureBlobStorageTestAccount;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.AbstractAbfsIntegrationTest;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
|
|
||||||
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.ABFS_TEST_CONTAINER_PREFIX;
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.ABFS_TEST_CONTAINER_PREFIX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +35,12 @@ import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.ABFS
|
||||||
* In that case, dev can use this tool to list and delete all test containers.
|
* In that case, dev can use this tool to list and delete all test containers.
|
||||||
* By default, all test container used in E2E tests sharing same prefix: "abfs-testcontainer-"
|
* By default, all test container used in E2E tests sharing same prefix: "abfs-testcontainer-"
|
||||||
*/
|
*/
|
||||||
public class CleanUpAbfsTestContainer {
|
public class CleanUpAbfsTestContainer extends AbstractAbfsIntegrationTest{
|
||||||
|
|
||||||
|
public CleanUpAbfsTestContainer() {
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.SharedKey);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEnumContainers() throws Throwable {
|
public void testEnumContainers() throws Throwable {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
|
@ -22,6 +22,27 @@
|
||||||
<value>{YOURACCOUNT}</value>
|
<value>{YOURACCOUNT}</value>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.auth.type.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{AUTH TYPE}</value>
|
||||||
|
<description>The auth type can be : SharedKey, OAuth, Custom. By default "SharedKey" is used.</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.contract.test.fs.abfs</name>
|
||||||
|
<value>abfs://{CONTAINERNAME}@{ACCOUNTNAME}.dfs.core.windows.net/value>
|
||||||
|
<description>The name of the azure file system for testing.</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.contract.test.fs.abfss</name>
|
||||||
|
<value>abfss://{CONTAINERNAME}@{ACCOUNTNAME}.dfs.core.windows.net/value>
|
||||||
|
<description>The name of the azure file system for testing.</description>
|
||||||
|
</property>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--If auth type is "SharedKey", provide SharedKey credentials below -->
|
||||||
|
<!--
|
||||||
<property>
|
<property>
|
||||||
<name>fs.azure.account.key.{YOURACCOUNT}.dfs.core.windows.net</name>
|
<name>fs.azure.account.key.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
<value>{ACCOUNTKEY}</value>
|
<value>{ACCOUNTKEY}</value>
|
||||||
|
@ -31,14 +52,115 @@
|
||||||
<name>fs.azure.test.account.key.{YOURACCOUNT}.dfs.core.windows.net</name>
|
<name>fs.azure.test.account.key.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
<value>{ACCOUNTKEY}</value>
|
<value>{ACCOUNTKEY}</value>
|
||||||
</property>
|
</property>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--If auth type is "OAuth", set below properties, AAD client and tenant related properties can be obtained through Azure Portal-->
|
||||||
|
<!--
|
||||||
<property>
|
<property>
|
||||||
<name>fs.contract.test.fs.abfs</name>
|
<name>fs.azure.account.oauth.provider.type.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
<value>abfs://{CONTAINERNAME}@{ACCOUNTNAME}.dfs.core.windows.net/value>
|
<value>org.apache.hadoop.fs.azurebfs.oauth2.{Token Provider Class name}</value>
|
||||||
<description>The name of the azure file system for testing.</description>
|
<description>The full name of token provider class name.</description>
|
||||||
|
</property>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--If "ClientCredsTokenProvider" is set as key provider, set auth endpoint, client id and secret below-->
|
||||||
|
<!--
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.client.endpoint.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>https://login.microsoftonline.com/{TENANTID}/oauth2/token</value>
|
||||||
|
<description>Token end point, this can be found through Azure portal</description>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.client.id.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{client id}</value>
|
||||||
|
<description>AAD client id.</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.client.secret.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{client secret}</value>
|
||||||
|
</property>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--If "UserPasswordTokenProvider" is set as key provider, set auth endpoint, use name and password below-->
|
||||||
|
<!--
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.client.endpoint.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>https://login.microsoftonline.com/{TENANTID}/oauth2/token</value>
|
||||||
|
<description>Token end point, this can be found through Azure portal</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.user.name.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{user name}</value>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.user.password.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{user password}</value>
|
||||||
|
</property>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--If "MsiTokenProvider" is set as key provider, set tenantGuid and client id below-->
|
||||||
|
<!--
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.msi.tenant.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{tenantGuid}</value>
|
||||||
|
<description>msi tenantGuid.</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.client.id.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{client id}</value>
|
||||||
|
<description>AAD client id.</description>
|
||||||
|
</property>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!--If "RefreshTokenBasedTokenProvider" is set as key provider, set refresh token and client id below-->
|
||||||
|
<!--
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.refresh.token.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{refresh token}</value>
|
||||||
|
<description>refresh token.</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.client.id.{YOURACCOUNT}.dfs.core.windows.net</name>
|
||||||
|
<value>{client id}</value>
|
||||||
|
<description>AAD client id.</description>
|
||||||
|
</property>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--Below four configure properties are provided for Blob Data Contributor a
|
||||||
|
nd Blob Data Reader OAuth access test "ITestAzureBlobFileSystemOauth",
|
||||||
|
using"ClientCredsTokenProvider" as key
|
||||||
|
provider. These are for test only.-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.contributor.client.id</name>
|
||||||
|
<value>{ID of client which has Data Contributor role}</value>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.contributor.client.secret</name>
|
||||||
|
<value>{secret of client which has Data Contributor role}</value>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.reader.client.id</name>
|
||||||
|
<value>{ID of client which has Data Reader role}</value>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>fs.azure.account.oauth2.reader.client.secret</name>
|
||||||
|
<value>{Secret of client which has Data Reader role}</value>
|
||||||
|
</property>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Save above configuration properties in a separate file named -->
|
<!-- Save above configuration properties in a separate file named -->
|
||||||
<!-- azure-bfs-auth-keys.xml in the same directory as this file. -->
|
<!-- azure-bfs-auth-keys.xml in the same directory as this file. -->
|
||||||
<!-- DO NOT ADD azure-bfs-auth-keys.xml TO REVISION CONTROL. The keys to your -->
|
<!-- DO NOT ADD azure-bfs-auth-keys.xml TO REVISION CONTROL. The keys to your -->
|
||||||
|
|
Loading…
Reference in New Issue