package io.fathom.cloud;
import io.fathom.cloud.cluster.ServiceRegistration;
import io.fathom.cloud.commands.EmbeddedSshd;
import io.fathom.cloud.identity.secrets.Secrets;
import io.fathom.cloud.keyczar.KeyczarFactory;
import io.fathom.cloud.lifecycle.Lifecycle;
import io.fathom.cloud.log.LogbackHook;
import io.fathom.cloud.persist.ZookeeperPersistModule;
import io.fathom.cloud.server.FathomCloudGuiceModule;
import io.fathom.cloud.server.OpenstackServerServletModule;
import io.fathom.cloud.server.auth.SharedSecretTokenService;
import io.fathom.cloud.zookeeper.ZookeeperClient;
import java.io.IOException;
import java.net.InetAddress;
import java.util.EnumSet;
import java.util.List;
import java.util.TimeZone;
import javax.inject.Inject;
import org.apache.wink.guice.server.internal.lifecycle.WinkGuiceModule;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.Code;
import org.apache.zookeeper.data.Stat;
import org.eclipse.jetty.server.Server;
import org.keyczar.Crypter;
import org.keyczar.DefaultKeyType;
import org.keyczar.GenericKeyczar;
import org.keyczar.KeyMetadata;
import org.keyczar.enums.KeyPurpose;
import org.keyczar.exceptions.KeyczarException;
import org.platformlayer.metrics.MetricReporter;
import org.platformlayer.metrics.NullMetricsModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fathomdb.Configuration;
import com.fathomdb.TimeSpan;
import com.fathomdb.config.ConfigurationImpl;
import com.fathomdb.crypto.EncryptionStore;
import com.fathomdb.discovery.ClasspathDiscovery;
import com.fathomdb.discovery.Discovery;
import com.fathomdb.extensions.Extensions;
import com.fathomdb.server.http.GuiceServletConfig;
import com.fathomdb.server.http.SslOption;
import com.fathomdb.server.http.WebServerBuilder;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.net.InetAddresses;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.persist.PersistService;
public class CloudServer {
private static final Logger log = LoggerFactory.getLogger(CloudServer.class);
static final int DEFAULT_HTTP_PORT = 8080;
static final int DEFAULT_HTTPS_PORT = 8443;
// static final int S3_PORT = 8082;
@Inject
WebServerBuilder serverBuilder;
@Inject
Lifecycle lifecycle;
@Inject
EncryptionStore encryptionStore;
@Inject
Configuration config;
@Inject
Configuration configuration;
@Inject
MetricReporter metricReporter;
@Inject
KeyczarFactory keyczarFactory;
@Inject
ServiceRegistration serviceRegistration;
@Inject
ZookeeperClient zk;
private Server jettyServer;
@Inject
PersistService persistService;
@Inject
EmbeddedSshd sshd;
@Inject
GuiceServletConfig guiceServletConfig;
// @Inject
// RestEasyExternalInjector resteasy;
public static void main(String[] args) throws Exception {
try {
Configuration configuration = ConfigurationImpl.load();
CloudServer server = build(configuration);
// server.startZk();
server.start();
} catch (Exception e) {
log.error("Error during startup", e);
System.exit(1);
}
}
public static CloudServer build(Configuration configuration) throws Exception {
// Everything works better when we're in a sensible timezone
// (in particular the default XML serialization)
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.setProperty("user.timezone", "UTC");
/*
* Removed until Zookeeper 3.5...
*
* if (args.length != 0) { String command = args[0];
*
* if (command.equals("join")) { try { JoinZookeeper join = new
* JoinZookeeper(); join.join(args[1], args[2], args[3]); } catch
* (Exception e) { e.printStackTrace(); System.exit(1); }
* System.exit(0); } }
*/
LogbackHook.attachToRootLogger();
// TODO: Switch to ServiceLoadDiscovery
Discovery discovery = new ClasspathDiscovery("com.fathomdb.", "io.fathom.");
Extensions extensions = new Extensions(configuration, discovery);
List<Module> modules = Lists.newArrayList();
modules.add(new NullMetricsModule());
modules.add(new DiscoveryModule(discovery));
// modules.add(new ConfigurationModule());
// modules.add(new CacheModule());
// modules.add(new JdbcGuiceModule());
modules.add(new FathomCloudGuiceModule(configuration));
modules.add(new OpenstackServerServletModule(configuration, extensions));
modules.add(new ZookeeperPersistModule());
modules.add(new WinkGuiceModule());
// modules.add(new AnnotationsModule());
Injector injector = extensions.createInjector(configuration, modules);
CloudServer server = injector.getInstance(CloudServer.class);
// server.startZk();
return server;
}
// @Inject
// CreatePasswordRecoveryKey cprk;
void start() throws Exception {
waitForZk();
persistService.start();
createKeys();
// {
// cprk.path = IoUtils.resolve("~/passwordrecovery");
// cprk.run();
// }
lifecycle.start();
register();
sshd.start();
startHttp();
}
private void waitForZk() throws KeeperException, IOException {
while (true) {
try {
Stat stat = zk.exists("/", false);
return;
} catch (KeeperException e) {
if (e.code() == Code.CONNECTIONLOSS) {
log.warn("Cannot connect to zookeeper (no quorum?)");
TimeSpan.FIVE_SECONDS.doSafeSleep();
} else {
throw new IOException("Error reading for zookeeper", e);
}
}
}
}
private void register() throws CloudException {
serviceRegistration.register();
}
private void createKeys() throws KeyczarException {
{
Crypter crypter = null;
GenericKeyczar store = keyczarFactory.find(Secrets.KEY_TOKEN_ENCRYPT, crypter);
if (store == null) {
String nameFlag = "Authentication Token Encryption";
KeyMetadata kmd = new KeyMetadata(nameFlag, KeyPurpose.DECRYPT_AND_ENCRYPT, DefaultKeyType.RSA_PRIV);
store = keyczarFactory.create(Secrets.KEY_TOKEN_ENCRYPT, kmd, crypter);
}
keyczarFactory.ensureKeyCreated(store);
}
{
Crypter crypter = null;
GenericKeyczar store = keyczarFactory.find(SharedSecretTokenService.KEYSTORE_ID, crypter);
if (store == null) {
String nameFlag = "Authentication Token Signing";
KeyMetadata kmd = new KeyMetadata(nameFlag, KeyPurpose.SIGN_AND_VERIFY, DefaultKeyType.HMAC_SHA1);
store = keyczarFactory.create(SharedSecretTokenService.KEYSTORE_ID, kmd, crypter);
}
keyczarFactory.ensureKeyCreated(store);
}
}
public void startHttp() throws Exception {
InetAddress address = configuration.lookup("listen.address", (InetAddress) null);
{
int port = configuration.lookup("listen.http.port", DEFAULT_HTTP_PORT);
serverBuilder.addHttpConnector(address, port, true);
}
if (encryptionStore.findCertificateAndKey("https") != null) {
int port = configuration.lookup("listen.https.port", DEFAULT_HTTPS_PORT);
EnumSet<SslOption> options = EnumSet.noneOf(SslOption.class);
serverBuilder.addHttpsConnector(address, port, options);
}
// {
// int metadataPort = configuration.lookup("metadata.port", 8775);
// InetAddress metadataHost = configuration.lookup("metadata.host",
// InetAddresses.forString("100.64.0.1"));
// serverBuilder.addHttpConnector(metadataHost, metadataPort, true);
// }
{
int metadataPort = configuration.lookup("metadata.port", 8775);
InetAddress metadataHost = null;
if (!Objects.equal("*", configuration.find("metadata.host"))) {
metadataHost = configuration.lookup("metadata.host", InetAddresses.forString("fd00::feed"));
}
serverBuilder.addHttpConnector(metadataHost, metadataPort, true);
}
// {
// int cloudPort = configuration.lookup("cloud.port", 8080);
// InetAddress cloudHost = configuration.lookup("cloud.host",
// InetAddresses.forString("fd00::c10d"));
// serverBuilder.addHttpConnector(cloudHost, cloudPort, true);
// }
// guiceServletConfig.addServletContextListener(resteasy);
serverBuilder.addGuiceContext("/", guiceServletConfig);
// serverBuilder.enableRequestLogging();
this.jettyServer = serverBuilder.start();
// this.jettyServer.setSendServerVersion(false);
metricReporter.start();
}
public void stop() throws Exception {
if (jettyServer != null) {
jettyServer.stop();
}
}
}