2015-03-22 05:51:11 +00:00
|
|
|
/*
|
2017-02-16 07:14:47 +00:00
|
|
|
* Copyright 2014-2017 Andrew Gaul <andrew@gaul.org>
|
2015-03-22 05:51:11 +00:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.gaul.s3proxy;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
2015-09-01 04:33:05 +00:00
|
|
|
import java.io.IOException;
|
2015-03-22 05:51:11 +00:00
|
|
|
import java.io.InputStream;
|
|
|
|
import java.net.URI;
|
2016-02-27 01:25:01 +00:00
|
|
|
import java.nio.charset.StandardCharsets;
|
2015-09-01 04:33:05 +00:00
|
|
|
import java.util.Map;
|
2015-03-22 05:51:11 +00:00
|
|
|
import java.util.Properties;
|
2015-09-01 04:33:05 +00:00
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
2015-03-22 05:51:11 +00:00
|
|
|
|
2015-12-15 00:00:00 +00:00
|
|
|
import com.google.common.base.Strings;
|
2015-03-22 05:51:11 +00:00
|
|
|
import com.google.common.collect.ImmutableList;
|
2016-02-27 01:25:01 +00:00
|
|
|
import com.google.common.io.Files;
|
2015-03-22 05:51:11 +00:00
|
|
|
import com.google.inject.Module;
|
|
|
|
|
|
|
|
import org.jclouds.Constants;
|
|
|
|
import org.jclouds.ContextBuilder;
|
2015-10-14 20:07:27 +00:00
|
|
|
import org.jclouds.blobstore.BlobStore;
|
2015-03-22 05:51:11 +00:00
|
|
|
import org.jclouds.blobstore.BlobStoreContext;
|
2015-10-14 20:07:27 +00:00
|
|
|
import org.jclouds.location.reference.LocationConstants;
|
2015-03-22 05:51:11 +00:00
|
|
|
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
2015-10-14 20:07:27 +00:00
|
|
|
import org.jclouds.openstack.swift.v1.blobstore.RegionScopedBlobStoreContext;
|
2014-08-28 19:15:07 +00:00
|
|
|
import org.kohsuke.args4j.CmdLineException;
|
|
|
|
import org.kohsuke.args4j.CmdLineParser;
|
|
|
|
import org.kohsuke.args4j.Option;
|
2015-03-22 05:51:11 +00:00
|
|
|
|
|
|
|
public final class Main {
|
|
|
|
private Main() {
|
|
|
|
throw new AssertionError("intentionally not implemented");
|
|
|
|
}
|
|
|
|
|
2014-08-28 19:15:07 +00:00
|
|
|
private static final class Options {
|
|
|
|
@Option(name = "--properties",
|
|
|
|
usage = "S3Proxy configuration (required)")
|
|
|
|
private File propertiesFile;
|
|
|
|
|
|
|
|
@Option(name = "--version", usage = "display version")
|
|
|
|
private boolean version;
|
|
|
|
}
|
|
|
|
|
2015-03-22 05:51:11 +00:00
|
|
|
public static void main(String[] args) throws Exception {
|
2014-08-28 19:15:07 +00:00
|
|
|
Options options = new Options();
|
|
|
|
CmdLineParser parser = new CmdLineParser(options);
|
|
|
|
try {
|
|
|
|
parser.parseArgument(args);
|
|
|
|
} catch (CmdLineException cle) {
|
|
|
|
usage(parser);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.version) {
|
2015-03-22 05:51:11 +00:00
|
|
|
System.err.println(
|
|
|
|
Main.class.getPackage().getImplementationVersion());
|
|
|
|
System.exit(0);
|
2014-08-28 19:15:07 +00:00
|
|
|
} else if (options.propertiesFile == null) {
|
|
|
|
usage(parser);
|
2015-03-22 05:51:11 +00:00
|
|
|
}
|
2014-08-28 19:15:07 +00:00
|
|
|
|
2015-03-22 05:51:11 +00:00
|
|
|
Properties properties = new Properties();
|
2014-08-28 19:15:07 +00:00
|
|
|
try (InputStream is = new FileInputStream(options.propertiesFile)) {
|
2015-03-22 05:51:11 +00:00
|
|
|
properties.load(is);
|
|
|
|
}
|
|
|
|
properties.putAll(System.getProperties());
|
|
|
|
|
|
|
|
String s3ProxyEndpointString = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_ENDPOINT);
|
2015-05-12 01:28:49 +00:00
|
|
|
String secureEndpoint = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_SECURE_ENDPOINT);
|
2016-11-14 05:11:33 +00:00
|
|
|
String s3ProxyAuthorizationString = properties.getProperty(
|
2015-03-22 05:51:11 +00:00
|
|
|
S3ProxyConstants.PROPERTY_AUTHORIZATION);
|
2015-09-01 04:33:05 +00:00
|
|
|
if ((s3ProxyEndpointString == null && secureEndpoint == null) ||
|
2016-11-14 05:11:33 +00:00
|
|
|
s3ProxyAuthorizationString == null) {
|
2015-03-22 05:51:11 +00:00
|
|
|
System.err.println("Properties file must contain:\n" +
|
2015-05-12 01:28:49 +00:00
|
|
|
S3ProxyConstants.PROPERTY_AUTHORIZATION + "\n" +
|
|
|
|
"and one of\n" +
|
2015-03-22 05:51:11 +00:00
|
|
|
S3ProxyConstants.PROPERTY_ENDPOINT + "\n" +
|
2015-05-12 01:28:49 +00:00
|
|
|
S3ProxyConstants.PROPERTY_SECURE_ENDPOINT);
|
2015-03-22 05:51:11 +00:00
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
|
2016-11-14 05:11:33 +00:00
|
|
|
AuthenticationType s3ProxyAuthorization =
|
|
|
|
AuthenticationType.fromString(s3ProxyAuthorizationString);
|
2015-03-22 05:51:11 +00:00
|
|
|
String localIdentity = null;
|
|
|
|
String localCredential = null;
|
2016-11-14 05:11:33 +00:00
|
|
|
switch (s3ProxyAuthorization) {
|
|
|
|
case AWS_V2:
|
|
|
|
case AWS_V4:
|
|
|
|
case AWS_V2_OR_V4:
|
2015-03-22 05:51:11 +00:00
|
|
|
localIdentity = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_IDENTITY);
|
|
|
|
localCredential = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_CREDENTIAL);
|
2015-12-14 23:48:19 +00:00
|
|
|
if (localIdentity == null || localCredential == null) {
|
|
|
|
System.err.println("Must specify both " +
|
|
|
|
S3ProxyConstants.PROPERTY_IDENTITY + " and " +
|
|
|
|
S3ProxyConstants.PROPERTY_CREDENTIAL +
|
2016-11-14 05:11:33 +00:00
|
|
|
" when using authentication");
|
2015-12-14 23:48:19 +00:00
|
|
|
System.exit(1);
|
|
|
|
}
|
2016-11-14 05:11:33 +00:00
|
|
|
break;
|
|
|
|
case NONE:
|
|
|
|
break;
|
|
|
|
default:
|
2015-03-22 05:51:11 +00:00
|
|
|
System.err.println(S3ProxyConstants.PROPERTY_AUTHORIZATION +
|
2016-11-14 05:11:33 +00:00
|
|
|
" invalid value, was: " + s3ProxyAuthorization);
|
2015-03-22 05:51:11 +00:00
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
String keyStorePath = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_KEYSTORE_PATH);
|
|
|
|
String keyStorePassword = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_KEYSTORE_PASSWORD);
|
|
|
|
String virtualHost = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_VIRTUAL_HOST);
|
2016-01-10 04:14:29 +00:00
|
|
|
String v4MaxNonChunkedRequestSize = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_V4_MAX_NON_CHUNKED_REQUEST_SIZE);
|
2016-03-13 06:15:00 +00:00
|
|
|
String ignoreUnknownHeaders = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_IGNORE_UNKNOWN_HEADERS);
|
2017-02-20 16:44:05 +00:00
|
|
|
String ignoreUnknownParameters = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_IGNORE_UNKNOWN_PARAMETERS);
|
2016-07-02 01:21:08 +00:00
|
|
|
String corsAllowAll = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_CORS_ALLOW_ALL);
|
2015-03-22 05:51:11 +00:00
|
|
|
|
2015-09-01 04:33:05 +00:00
|
|
|
BlobStore blobStore = createBlobStore(properties);
|
|
|
|
|
|
|
|
Properties altProperties = new Properties();
|
|
|
|
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
|
|
|
String key = (String) entry.getKey();
|
|
|
|
if (key.startsWith(S3ProxyConstants.PROPERTY_ALT_JCLOUDS_PREFIX)) {
|
|
|
|
key = key.substring(
|
|
|
|
S3ProxyConstants.PROPERTY_ALT_JCLOUDS_PREFIX.length());
|
|
|
|
altProperties.put(key, (String) entry.getValue());
|
|
|
|
}
|
2015-03-22 05:51:11 +00:00
|
|
|
}
|
2015-09-01 04:33:05 +00:00
|
|
|
|
|
|
|
String eventualConsistency = properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_EVENTUAL_CONSISTENCY);
|
|
|
|
if ("true".equalsIgnoreCase(eventualConsistency)) {
|
|
|
|
BlobStore altBlobStore = createBlobStore(altProperties);
|
|
|
|
int delay = Integer.parseInt(properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_EVENTUAL_CONSISTENCY_DELAY,
|
|
|
|
"5"));
|
|
|
|
double probability = Double.parseDouble(properties.getProperty(
|
|
|
|
S3ProxyConstants.PROPERTY_EVENTUAL_CONSISTENCY_PROBABILITY,
|
|
|
|
"1.0"));
|
|
|
|
System.err.println("Emulating eventual consistency with delay " +
|
|
|
|
delay + " seconds and probability " + (probability * 100) +
|
|
|
|
"%");
|
|
|
|
blobStore = EventualBlobStore.newEventualBlobStore(
|
|
|
|
blobStore, altBlobStore,
|
|
|
|
Executors.newScheduledThreadPool(1),
|
|
|
|
delay, TimeUnit.SECONDS, probability);
|
2015-10-14 20:07:27 +00:00
|
|
|
}
|
2015-03-22 05:51:11 +00:00
|
|
|
|
|
|
|
S3Proxy s3Proxy;
|
|
|
|
try {
|
|
|
|
S3Proxy.Builder s3ProxyBuilder = S3Proxy.builder()
|
2015-10-14 20:07:27 +00:00
|
|
|
.blobStore(blobStore);
|
2015-05-12 01:28:49 +00:00
|
|
|
if (s3ProxyEndpointString != null) {
|
|
|
|
s3ProxyBuilder.endpoint(new URI(s3ProxyEndpointString));
|
|
|
|
}
|
2015-05-12 01:19:42 +00:00
|
|
|
if (secureEndpoint != null) {
|
|
|
|
s3ProxyBuilder.secureEndpoint(new URI(secureEndpoint));
|
|
|
|
}
|
2015-03-22 05:51:11 +00:00
|
|
|
if (localIdentity != null || localCredential != null) {
|
2016-11-14 05:11:33 +00:00
|
|
|
s3ProxyBuilder.awsAuthentication(
|
|
|
|
s3ProxyAuthorization, localIdentity,
|
2015-03-22 05:51:11 +00:00
|
|
|
localCredential);
|
|
|
|
}
|
|
|
|
if (keyStorePath != null || keyStorePassword != null) {
|
|
|
|
s3ProxyBuilder.keyStore(keyStorePath, keyStorePassword);
|
|
|
|
}
|
|
|
|
if (virtualHost != null) {
|
|
|
|
s3ProxyBuilder.virtualHost(virtualHost);
|
|
|
|
}
|
2016-01-10 04:14:29 +00:00
|
|
|
if (v4MaxNonChunkedRequestSize != null) {
|
|
|
|
s3ProxyBuilder.v4MaxNonChunkedRequestSize(Long.parseLong(
|
2016-03-13 06:15:00 +00:00
|
|
|
v4MaxNonChunkedRequestSize));
|
|
|
|
}
|
|
|
|
if (ignoreUnknownHeaders != null) {
|
|
|
|
s3ProxyBuilder.ignoreUnknownHeaders(Boolean.parseBoolean(
|
|
|
|
ignoreUnknownHeaders));
|
2016-01-10 04:14:29 +00:00
|
|
|
}
|
2017-02-20 16:44:05 +00:00
|
|
|
if (ignoreUnknownParameters != null) {
|
|
|
|
s3ProxyBuilder.ignoreUnknownParameters(Boolean.parseBoolean(
|
|
|
|
ignoreUnknownParameters));
|
|
|
|
}
|
2016-07-02 01:21:08 +00:00
|
|
|
if (corsAllowAll != null) {
|
|
|
|
s3ProxyBuilder.corsAllowAll(Boolean.parseBoolean(
|
|
|
|
corsAllowAll));
|
|
|
|
}
|
2015-03-22 05:51:11 +00:00
|
|
|
s3Proxy = s3ProxyBuilder.build();
|
|
|
|
} catch (IllegalArgumentException | IllegalStateException e) {
|
|
|
|
System.err.println(e.getMessage());
|
|
|
|
System.exit(1);
|
|
|
|
throw e;
|
|
|
|
}
|
2015-08-22 21:25:12 +00:00
|
|
|
try {
|
|
|
|
s3Proxy.start();
|
|
|
|
} catch (Exception e) {
|
|
|
|
System.err.println(e.getMessage());
|
|
|
|
System.exit(1);
|
|
|
|
}
|
2015-03-22 05:51:11 +00:00
|
|
|
}
|
2014-08-28 19:15:07 +00:00
|
|
|
|
2015-09-01 04:33:05 +00:00
|
|
|
private static BlobStore createBlobStore(Properties properties)
|
|
|
|
throws IOException {
|
|
|
|
String provider = properties.getProperty(Constants.PROPERTY_PROVIDER);
|
|
|
|
String identity = properties.getProperty(Constants.PROPERTY_IDENTITY);
|
|
|
|
String credential = properties.getProperty(
|
|
|
|
Constants.PROPERTY_CREDENTIAL);
|
|
|
|
String endpoint = properties.getProperty(Constants.PROPERTY_ENDPOINT);
|
|
|
|
String region = properties.getProperty(
|
|
|
|
LocationConstants.PROPERTY_REGION);
|
|
|
|
|
|
|
|
if (provider.equals("filesystem") || provider.equals("transient")) {
|
|
|
|
// local blobstores do not require credentials
|
|
|
|
identity = Strings.nullToEmpty(identity);
|
|
|
|
credential = Strings.nullToEmpty(credential);
|
|
|
|
} else if (provider.equals("google-cloud-storage")) {
|
|
|
|
File credentialFile = new File(credential);
|
|
|
|
if (credentialFile.exists()) {
|
|
|
|
credential = Files.toString(credentialFile,
|
|
|
|
StandardCharsets.UTF_8);
|
|
|
|
}
|
2016-03-09 21:23:17 +00:00
|
|
|
properties.remove(Constants.PROPERTY_CREDENTIAL);
|
2015-09-01 04:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (provider == null || identity == null || credential == null) {
|
|
|
|
System.err.println("Properties file must contain:\n" +
|
|
|
|
Constants.PROPERTY_PROVIDER + "\n" +
|
|
|
|
Constants.PROPERTY_IDENTITY + "\n" +
|
|
|
|
Constants.PROPERTY_CREDENTIAL + "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
ContextBuilder builder = ContextBuilder
|
|
|
|
.newBuilder(provider)
|
|
|
|
.credentials(identity, credential)
|
|
|
|
.modules(ImmutableList.<Module>of(new SLF4JLoggingModule()))
|
|
|
|
.overrides(properties);
|
|
|
|
if (endpoint != null) {
|
|
|
|
builder = builder.endpoint(endpoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
BlobStoreContext context = builder.build(BlobStoreContext.class);
|
|
|
|
BlobStore blobStore = context.getBlobStore();
|
|
|
|
if (context instanceof RegionScopedBlobStoreContext &&
|
|
|
|
region != null) {
|
|
|
|
blobStore = ((RegionScopedBlobStoreContext) context)
|
|
|
|
.getBlobStore(region);
|
|
|
|
}
|
|
|
|
return blobStore;
|
|
|
|
}
|
|
|
|
|
2014-08-28 19:15:07 +00:00
|
|
|
private static void usage(CmdLineParser parser) {
|
|
|
|
System.err.println("Usage: s3proxy [options...]");
|
|
|
|
parser.printUsage(System.err);
|
|
|
|
System.exit(1);
|
|
|
|
}
|
2015-03-22 05:51:11 +00:00
|
|
|
}
|