|
@@ -35,11 +35,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
|
import com.amazonaws.AmazonClientException;
|
|
import com.amazonaws.AmazonClientException;
|
|
import com.amazonaws.AmazonServiceException;
|
|
import com.amazonaws.AmazonServiceException;
|
|
-import com.amazonaws.ClientConfiguration;
|
|
|
|
-import com.amazonaws.Protocol;
|
|
|
|
-import com.amazonaws.auth.AWSCredentialsProvider;
|
|
|
|
-import com.amazonaws.services.s3.AmazonS3Client;
|
|
|
|
-import com.amazonaws.services.s3.S3ClientOptions;
|
|
|
|
|
|
+import com.amazonaws.services.s3.AmazonS3;
|
|
import com.amazonaws.services.s3.model.AmazonS3Exception;
|
|
import com.amazonaws.services.s3.model.AmazonS3Exception;
|
|
import com.amazonaws.services.s3.model.CannedAccessControlList;
|
|
import com.amazonaws.services.s3.model.CannedAccessControlList;
|
|
import com.amazonaws.services.s3.model.DeleteObjectsRequest;
|
|
import com.amazonaws.services.s3.model.DeleteObjectsRequest;
|
|
@@ -58,7 +54,6 @@ import com.amazonaws.services.s3.transfer.Upload;
|
|
import com.amazonaws.event.ProgressListener;
|
|
import com.amazonaws.event.ProgressListener;
|
|
import com.amazonaws.event.ProgressEvent;
|
|
import com.amazonaws.event.ProgressEvent;
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
-import com.google.common.base.Preconditions;
|
|
|
|
|
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
import org.apache.commons.lang.StringUtils;
|
|
import org.apache.hadoop.classification.InterfaceAudience;
|
|
import org.apache.hadoop.classification.InterfaceAudience;
|
|
@@ -79,7 +74,7 @@ import org.apache.hadoop.fs.StorageStatistics;
|
|
import org.apache.hadoop.fs.permission.FsPermission;
|
|
import org.apache.hadoop.fs.permission.FsPermission;
|
|
import org.apache.hadoop.fs.s3native.S3xLoginHelper;
|
|
import org.apache.hadoop.fs.s3native.S3xLoginHelper;
|
|
import org.apache.hadoop.util.Progressable;
|
|
import org.apache.hadoop.util.Progressable;
|
|
-import org.apache.hadoop.util.VersionInfo;
|
|
|
|
|
|
+import org.apache.hadoop.util.ReflectionUtils;
|
|
|
|
|
|
import static org.apache.hadoop.fs.s3a.Constants.*;
|
|
import static org.apache.hadoop.fs.s3a.Constants.*;
|
|
import static org.apache.hadoop.fs.s3a.Listing.ACCEPT_ALL;
|
|
import static org.apache.hadoop.fs.s3a.Listing.ACCEPT_ALL;
|
|
@@ -111,7 +106,7 @@ public class S3AFileSystem extends FileSystem {
|
|
public static final int DEFAULT_BLOCKSIZE = 32 * 1024 * 1024;
|
|
public static final int DEFAULT_BLOCKSIZE = 32 * 1024 * 1024;
|
|
private URI uri;
|
|
private URI uri;
|
|
private Path workingDir;
|
|
private Path workingDir;
|
|
- private AmazonS3Client s3;
|
|
|
|
|
|
+ private AmazonS3 s3;
|
|
private String bucket;
|
|
private String bucket;
|
|
private int maxKeys;
|
|
private int maxKeys;
|
|
private Listing listing;
|
|
private Listing listing;
|
|
@@ -150,37 +145,11 @@ public class S3AFileSystem extends FileSystem {
|
|
|
|
|
|
bucket = name.getHost();
|
|
bucket = name.getHost();
|
|
|
|
|
|
- AWSCredentialsProvider credentials =
|
|
|
|
- createAWSCredentialProviderSet(name, conf, uri);
|
|
|
|
-
|
|
|
|
- ClientConfiguration awsConf = new ClientConfiguration();
|
|
|
|
- awsConf.setMaxConnections(intOption(conf, MAXIMUM_CONNECTIONS,
|
|
|
|
- DEFAULT_MAXIMUM_CONNECTIONS, 1));
|
|
|
|
- boolean secureConnections = conf.getBoolean(SECURE_CONNECTIONS,
|
|
|
|
- DEFAULT_SECURE_CONNECTIONS);
|
|
|
|
- awsConf.setProtocol(secureConnections ? Protocol.HTTPS : Protocol.HTTP);
|
|
|
|
- awsConf.setMaxErrorRetry(intOption(conf, MAX_ERROR_RETRIES,
|
|
|
|
- DEFAULT_MAX_ERROR_RETRIES, 0));
|
|
|
|
- awsConf.setConnectionTimeout(intOption(conf, ESTABLISH_TIMEOUT,
|
|
|
|
- DEFAULT_ESTABLISH_TIMEOUT, 0));
|
|
|
|
- awsConf.setSocketTimeout(intOption(conf, SOCKET_TIMEOUT,
|
|
|
|
- DEFAULT_SOCKET_TIMEOUT, 0));
|
|
|
|
- int sockSendBuffer = intOption(conf, SOCKET_SEND_BUFFER,
|
|
|
|
- DEFAULT_SOCKET_SEND_BUFFER, 2048);
|
|
|
|
- int sockRecvBuffer = intOption(conf, SOCKET_RECV_BUFFER,
|
|
|
|
- DEFAULT_SOCKET_RECV_BUFFER, 2048);
|
|
|
|
- awsConf.setSocketBufferSizeHints(sockSendBuffer, sockRecvBuffer);
|
|
|
|
- String signerOverride = conf.getTrimmed(SIGNING_ALGORITHM, "");
|
|
|
|
- if (!signerOverride.isEmpty()) {
|
|
|
|
- LOG.debug("Signer override = {}", signerOverride);
|
|
|
|
- awsConf.setSignerOverride(signerOverride);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- initProxySupport(conf, awsConf, secureConnections);
|
|
|
|
-
|
|
|
|
- initUserAgent(conf, awsConf);
|
|
|
|
-
|
|
|
|
- initAmazonS3Client(conf, credentials, awsConf);
|
|
|
|
|
|
+ Class<? extends S3ClientFactory> s3ClientFactoryClass = conf.getClass(
|
|
|
|
+ S3_CLIENT_FACTORY_IMPL, DEFAULT_S3_CLIENT_FACTORY_IMPL,
|
|
|
|
+ S3ClientFactory.class);
|
|
|
|
+ s3 = ReflectionUtils.newInstance(s3ClientFactoryClass, conf)
|
|
|
|
+ .createS3Client(name, uri);
|
|
|
|
|
|
maxKeys = intOption(conf, MAX_PAGING_KEYS, DEFAULT_MAX_PAGING_KEYS, 1);
|
|
maxKeys = intOption(conf, MAX_PAGING_KEYS, DEFAULT_MAX_PAGING_KEYS, 1);
|
|
listing = new Listing(this);
|
|
listing = new Listing(this);
|
|
@@ -276,50 +245,6 @@ public class S3AFileSystem extends FileSystem {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- void initProxySupport(Configuration conf, ClientConfiguration awsConf,
|
|
|
|
- boolean secureConnections) throws IllegalArgumentException {
|
|
|
|
- String proxyHost = conf.getTrimmed(PROXY_HOST, "");
|
|
|
|
- int proxyPort = conf.getInt(PROXY_PORT, -1);
|
|
|
|
- if (!proxyHost.isEmpty()) {
|
|
|
|
- awsConf.setProxyHost(proxyHost);
|
|
|
|
- if (proxyPort >= 0) {
|
|
|
|
- awsConf.setProxyPort(proxyPort);
|
|
|
|
- } else {
|
|
|
|
- if (secureConnections) {
|
|
|
|
- LOG.warn("Proxy host set without port. Using HTTPS default 443");
|
|
|
|
- awsConf.setProxyPort(443);
|
|
|
|
- } else {
|
|
|
|
- LOG.warn("Proxy host set without port. Using HTTP default 80");
|
|
|
|
- awsConf.setProxyPort(80);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- String proxyUsername = conf.getTrimmed(PROXY_USERNAME);
|
|
|
|
- String proxyPassword = conf.getTrimmed(PROXY_PASSWORD);
|
|
|
|
- if ((proxyUsername == null) != (proxyPassword == null)) {
|
|
|
|
- String msg = "Proxy error: " + PROXY_USERNAME + " or " +
|
|
|
|
- PROXY_PASSWORD + " set without the other.";
|
|
|
|
- LOG.error(msg);
|
|
|
|
- throw new IllegalArgumentException(msg);
|
|
|
|
- }
|
|
|
|
- awsConf.setProxyUsername(proxyUsername);
|
|
|
|
- awsConf.setProxyPassword(proxyPassword);
|
|
|
|
- awsConf.setProxyDomain(conf.getTrimmed(PROXY_DOMAIN));
|
|
|
|
- awsConf.setProxyWorkstation(conf.getTrimmed(PROXY_WORKSTATION));
|
|
|
|
- if (LOG.isDebugEnabled()) {
|
|
|
|
- LOG.debug("Using proxy server {}:{} as user {} with password {} on " +
|
|
|
|
- "domain {} as workstation {}", awsConf.getProxyHost(),
|
|
|
|
- awsConf.getProxyPort(),
|
|
|
|
- String.valueOf(awsConf.getProxyUsername()),
|
|
|
|
- awsConf.getProxyPassword(), awsConf.getProxyDomain(),
|
|
|
|
- awsConf.getProxyWorkstation());
|
|
|
|
- }
|
|
|
|
- } else if (proxyPort >= 0) {
|
|
|
|
- String msg = "Proxy error: " + PROXY_PORT + " set without " + PROXY_HOST;
|
|
|
|
- LOG.error(msg);
|
|
|
|
- throw new IllegalArgumentException(msg);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Get S3A Instrumentation. For test purposes.
|
|
* Get S3A Instrumentation. For test purposes.
|
|
* @return this instance's instrumentation.
|
|
* @return this instance's instrumentation.
|
|
@@ -328,53 +253,9 @@ public class S3AFileSystem extends FileSystem {
|
|
return instrumentation;
|
|
return instrumentation;
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Initializes the User-Agent header to send in HTTP requests to the S3
|
|
|
|
- * back-end. We always include the Hadoop version number. The user also may
|
|
|
|
- * set an optional custom prefix to put in front of the Hadoop version number.
|
|
|
|
- * The AWS SDK interally appends its own information, which seems to include
|
|
|
|
- * the AWS SDK version, OS and JVM version.
|
|
|
|
- *
|
|
|
|
- * @param conf Hadoop configuration
|
|
|
|
- * @param awsConf AWS SDK configuration
|
|
|
|
- */
|
|
|
|
- private void initUserAgent(Configuration conf, ClientConfiguration awsConf) {
|
|
|
|
- String userAgent = "Hadoop " + VersionInfo.getVersion();
|
|
|
|
- String userAgentPrefix = conf.getTrimmed(USER_AGENT_PREFIX, "");
|
|
|
|
- if (!userAgentPrefix.isEmpty()) {
|
|
|
|
- userAgent = userAgentPrefix + ", " + userAgent;
|
|
|
|
- }
|
|
|
|
- LOG.debug("Using User-Agent: {}", userAgent);
|
|
|
|
- awsConf.setUserAgent(userAgent);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void initAmazonS3Client(Configuration conf,
|
|
|
|
- AWSCredentialsProvider credentials, ClientConfiguration awsConf)
|
|
|
|
- throws IllegalArgumentException {
|
|
|
|
- s3 = new AmazonS3Client(credentials, awsConf);
|
|
|
|
- String endPoint = conf.getTrimmed(ENDPOINT, "");
|
|
|
|
- if (!endPoint.isEmpty()) {
|
|
|
|
- try {
|
|
|
|
- s3.setEndpoint(endPoint);
|
|
|
|
- } catch (IllegalArgumentException e) {
|
|
|
|
- String msg = "Incorrect endpoint: " + e.getMessage();
|
|
|
|
- LOG.error(msg);
|
|
|
|
- throw new IllegalArgumentException(msg, e);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- enablePathStyleAccessIfRequired(conf);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void enablePathStyleAccessIfRequired(Configuration conf) {
|
|
|
|
- final boolean pathStyleAccess = conf.getBoolean(PATH_STYLE_ACCESS, false);
|
|
|
|
- if (pathStyleAccess) {
|
|
|
|
- LOG.debug("Enabling path style access!");
|
|
|
|
- s3.setS3ClientOptions(new S3ClientOptions().withPathStyleAccess(true));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
private void initTransferManager() {
|
|
private void initTransferManager() {
|
|
- TransferManagerConfiguration transferConfiguration = new TransferManagerConfiguration();
|
|
|
|
|
|
+ TransferManagerConfiguration transferConfiguration =
|
|
|
|
+ new TransferManagerConfiguration();
|
|
transferConfiguration.setMinimumUploadPartSize(partSize);
|
|
transferConfiguration.setMinimumUploadPartSize(partSize);
|
|
transferConfiguration.setMultipartUploadThreshold(multiPartThreshold);
|
|
transferConfiguration.setMultipartUploadThreshold(multiPartThreshold);
|
|
transferConfiguration.setMultipartCopyPartSize(partSize);
|
|
transferConfiguration.setMultipartCopyPartSize(partSize);
|
|
@@ -445,7 +326,7 @@ public class S3AFileSystem extends FileSystem {
|
|
* @return AmazonS3Client
|
|
* @return AmazonS3Client
|
|
*/
|
|
*/
|
|
@VisibleForTesting
|
|
@VisibleForTesting
|
|
- AmazonS3Client getAmazonS3Client() {
|
|
|
|
|
|
+ AmazonS3 getAmazonS3Client() {
|
|
return s3;
|
|
return s3;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -469,10 +350,6 @@ public class S3AFileSystem extends FileSystem {
|
|
this.inputPolicy = inputPolicy;
|
|
this.inputPolicy = inputPolicy;
|
|
}
|
|
}
|
|
|
|
|
|
- public S3AFileSystem() {
|
|
|
|
- super();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Turns a path (relative or otherwise) into an S3 key.
|
|
* Turns a path (relative or otherwise) into an S3 key.
|
|
*
|
|
*
|
|
@@ -791,8 +668,10 @@ public class S3AFileSystem extends FileSystem {
|
|
|
|
|
|
while (true) {
|
|
while (true) {
|
|
for (S3ObjectSummary summary : objects.getObjectSummaries()) {
|
|
for (S3ObjectSummary summary : objects.getObjectSummaries()) {
|
|
- keysToDelete.add(new DeleteObjectsRequest.KeyVersion(summary.getKey()));
|
|
|
|
- String newDstKey = dstKey + summary.getKey().substring(srcKey.length());
|
|
|
|
|
|
+ keysToDelete.add(
|
|
|
|
+ new DeleteObjectsRequest.KeyVersion(summary.getKey()));
|
|
|
|
+ String newDstKey =
|
|
|
|
+ dstKey + summary.getKey().substring(srcKey.length());
|
|
copyFile(summary.getKey(), newDstKey, summary.getSize());
|
|
copyFile(summary.getKey(), newDstKey, summary.getSize());
|
|
|
|
|
|
if (keysToDelete.size() == MAX_ENTRIES_TO_DELETE) {
|
|
if (keysToDelete.size() == MAX_ENTRIES_TO_DELETE) {
|
|
@@ -1397,7 +1276,8 @@ public class S3AFileSystem extends FileSystem {
|
|
LOG.debug("Found file (with /): fake directory");
|
|
LOG.debug("Found file (with /): fake directory");
|
|
return new S3AFileStatus(true, true, path);
|
|
return new S3AFileStatus(true, true, path);
|
|
} else {
|
|
} else {
|
|
- LOG.warn("Found file (with /): real file? should not happen: {}", key);
|
|
|
|
|
|
+ LOG.warn("Found file (with /): real file? should not happen: {}",
|
|
|
|
+ key);
|
|
|
|
|
|
return new S3AFileStatus(meta.getContentLength(),
|
|
return new S3AFileStatus(meta.getContentLength(),
|
|
dateToLong(meta.getLastModified()),
|
|
dateToLong(meta.getLastModified()),
|
|
@@ -1994,42 +1874,4 @@ public class S3AFileSystem extends FileSystem {
|
|
getFileBlockLocations(status, 0, status.getLen())
|
|
getFileBlockLocations(status, 0, status.getLen())
|
|
: null);
|
|
: null);
|
|
}
|
|
}
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Get a integer option >= the minimum allowed value.
|
|
|
|
- * @param conf configuration
|
|
|
|
- * @param key key to look up
|
|
|
|
- * @param defVal default value
|
|
|
|
- * @param min minimum value
|
|
|
|
- * @return the value
|
|
|
|
- * @throws IllegalArgumentException if the value is below the minimum
|
|
|
|
- */
|
|
|
|
- static int intOption(Configuration conf, String key, int defVal, int min) {
|
|
|
|
- int v = conf.getInt(key, defVal);
|
|
|
|
- Preconditions.checkArgument(v >= min,
|
|
|
|
- String.format("Value of %s: %d is below the minimum value %d",
|
|
|
|
- key, v, min));
|
|
|
|
- return v;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Get a long option >= the minimum allowed value.
|
|
|
|
- * @param conf configuration
|
|
|
|
- * @param key key to look up
|
|
|
|
- * @param defVal default value
|
|
|
|
- * @param min minimum value
|
|
|
|
- * @return the value
|
|
|
|
- * @throws IllegalArgumentException if the value is below the minimum
|
|
|
|
- */
|
|
|
|
- static long longOption(Configuration conf,
|
|
|
|
- String key,
|
|
|
|
- long defVal,
|
|
|
|
- long min) {
|
|
|
|
- long v = conf.getLong(key, defVal);
|
|
|
|
- Preconditions.checkArgument(v >= min,
|
|
|
|
- String.format("Value of %s: %d is below the minimum value %d",
|
|
|
|
- key, v, min));
|
|
|
|
- return v;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
}
|
|
}
|