configrd/configrd-service

View on GitHub
src/main/java/io/configrd/service/ConfigrdServer.java

Summary

Maintainability
C
7 hrs
Test Coverage
package io.configrd.service;

import java.io.FileNotFoundException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Throwables;
import io.configrd.core.ConfigSourceResolver;
import io.configrd.core.git.GitRepoDef;
import io.configrd.core.git.GitStreamSource;
import io.configrd.core.source.RepoDef;
import io.configrd.core.source.SecuredRepo;
import io.configrd.core.util.StringUtils;
import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.server.handlers.PathHandler;
import io.undertow.server.handlers.resource.ClassPathResourceManager;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;

public class ConfigrdServer {

  private static final Logger logger = LoggerFactory.getLogger(ConfigrdServer.class);
  private Undertow undertow;
  private static ConfigrdServer server;
  private DeploymentManager deploymentManager;

  public static final String DEFAULT_PORT = "9191";
  public static final String DEFAULT_LOCAL_CLONE = "/srv/configrd";

  public static void main(String[] args) throws Throwable {

    System.setProperty("org.jboss.logging.provider", "slf4j");

    Options options = new Options();

    Option help = new Option("help", "print this message");
    options.addOption(help);

    Option uri = Option.builder("u").optionalArg(true).argName("uri").hasArg().type(URI.class).desc(
        "Absolute path of configrd config uri. Default: " + ConfigSourceResolver.DEFAULT_CONFIG_URI)
        .longOpt("uri").build();
    options.addOption(uri);

    Option file = Option.builder("f").optionalArg(true).argName("file").hasArg().type(String.class)
        .desc("Name of the configrd config file. Default: "
            + ConfigSourceResolver.DEFAULT_CONFIG_FILE)
        .longOpt("uri").build();
    options.addOption(file);

    Option port = Option.builder("p").optionalArg(true).argName("port").longOpt("port").hasArg()
        .type(String.class).desc("Port number. Default: " + DEFAULT_PORT).build();
    options.addOption(port);

    Option stream = Option.builder("s").optionalArg(true).argName("name").longOpt("source").hasArg()
        .type(String.class).desc("Name of source [file, http, s3, git]. Default: "
            + ConfigSourceResolver.DEFAULT_SOURCENAME)
        .build();
    options.addOption(stream);

    Option trustCert = new Option("trustCert",
        "Trust all http certs. Default: " + ConfigSourceResolver.DEFAULT_TRUST_CERTS);
    options.addOption(trustCert);

    Option git_user = Option.builder("gitu").optionalArg(true)
        .desc("Git user name (CodeCommit, GitHub)").type(String.class).argName("git user").build();
    options.addOption(git_user);

    Option git_secret = Option.builder("gits").optionalArg(true).type(String.class)
        .desc("Git secret (CodeCommit, GitHub)").argName("git secret").build();
    options.addOption(git_secret);

    Option git_token = Option.builder("gitt").optionalArg(true).type(String.class)
        .desc("Git token (GitHub)").argName("git token").build();
    options.addOption(git_token);

    Option ssh_priv_key = Option.builder("pk").optionalArg(true).type(String.class)
        .desc("Ssh private key (CodeCommit, GitHub)").argName("ssh private key").build();
    options.addOption(ssh_priv_key);

    Option auth_type = Option.builder("auth").optionalArg(true).type(String.class).desc(
        "Git authentication method [CodeCommitGitCreds, CodeCommitIAMUser, GitHub, GitHubToken, SshPubKey]")
        .argName("auth method").build();
    options.addOption(auth_type);

    final CommandLineParser parser = new DefaultParser();
    final HelpFormatter formatter = new HelpFormatter();
    final Map<String, Object> init = new HashMap<>();

    try {
      // parse the command line arguments
      CommandLine line = parser.parse(options, args);

      if (line.hasOption("help") || line.getArgList().isEmpty()) {

        formatter.printHelp("java -jar configrd-service-2.0.0.jar ConfigrdServer [OPTIONS]",
            options);
        return;

      } else {

        init.put(RepoDef.URI_FIELD, line.getOptionValue("u"));

        init.put(SystemProperties.CONFIGRD_SERVER_PORT, line.getOptionValue("p", DEFAULT_PORT));
        init.put(RepoDef.SOURCE_NAME_FIELD, line.getOptionValue("s"));
        init.put(RepoDef.TRUST_CERTS_FIELD, line.getOptionValue("trustCert"));
        init.put(GitRepoDef.CONFIGRD_CONFIG_FILENAME_FIELD, line.getOptionValue("f"));
        init.put(GitRepoDef.USERNAME_FIELD, line.getOptionValue("gitu"));
        init.put(GitRepoDef.USERNAME_FIELD, line.getOptionValue("gitt"));
        init.put(GitRepoDef.USERNAME_FIELD, line.getOptionValue("pk"));
        init.put(GitRepoDef.PASSWORD_FIELD, line.getOptionValue("gits"));
        init.put(SecuredRepo.AUTH_METHOD_FIELD, line.getOptionValue("auth"));

      }

    } catch (ParseException exp) {
      logger.error("Parsing failed.  Reason: " + exp.getMessage());

      formatter.printHelp("java -jar configrd-service-2.0.0.jar ConfigrdServer [OPTIONS]", options);
      return;
    }

    if (server != null) {

      logger.warn("Calling start on a running server. Please stop first.");
      return;

    } else {

      server = new ConfigrdServer();
      server.start(init);

    }
  }

  protected void start(Map<String, Object> initParama) throws Throwable {

    if (GitStreamSource.GIT.equalsIgnoreCase((String) initParama.get(RepoDef.SOURCE_NAME_FIELD))
        && !initParama.containsKey(GitRepoDef.LOCAL_CLONE_FIELD)) {
      initParama.put(GitRepoDef.LOCAL_CLONE_FIELD, DEFAULT_LOCAL_CLONE);
    }

    logger.debug("Passed params:" + initParama);

    init_repos(initParama);

    initParama.entrySet().stream().forEach(e -> {

      if (e.getValue() != null)
        InitializationContext.get().params().put(e.getKey(), e.getValue());

    });

    long start = System.currentTimeMillis();

    PathHandler path = Handlers.path();

    String port =
        (String) initParama.getOrDefault(SystemProperties.CONFIGRD_SERVER_PORT, DEFAULT_PORT);

    if (undertow == null) {
      undertow = Undertow.builder().addHttpListener(Integer.valueOf(port), "0.0.0.0")
          .setHandler(path).build();

      try {
        undertow.start();
      } catch (Exception e) {
        Throwable ex = Throwables.getRootCause(e);
        logger.error(ex.getMessage());
        throw ex;
      }

      logger.info("Configrd starting on port " + port);

      DeploymentInfo servletBuilder = Servlets.deployment()
          .setClassLoader(ConfigrdServer.class.getClassLoader()).setContextPath("/")
          .setResourceManager(new ClassPathResourceManager(ConfigrdServer.class.getClassLoader()))
          .addServlets(Servlets.servlet("jerseyServlet", ServletContainer.class).setLoadOnStartup(1)
              .addInitParam("javax.ws.rs.Application", JerseyConfig.class.getName())
              .addMapping("/configrd/*"))
          .setDeploymentName("Application.war");

      logger.info("Starting configrd...");

      deploymentManager = Servlets.defaultContainer().addDeployment(servletBuilder);
      deploymentManager.deploy();

      try {
        
        path.addPrefixPath("/", deploymentManager.start());
        logger.info("Application deployed");
        
      } catch (Exception e) {
        Throwable ex = Throwables.getRootCause(e);
        logger.error(ex.getMessage());
        throw ex;
      }     
    }
    logger.info("Configrd started in " + (System.currentTimeMillis() - start) / 1000 + "s");

  }

  public void stop() {

    if (undertow != null) {

      logger.info("Stopping configrd...");

      if (deploymentManager != null) {
        deploymentManager.undeploy();
      }

      InitializationContext.get().clear();
      undertow.stop();
      undertow = null;

      logger.info("Configrd stopped");
    } else {
      logger.info("Configrd already stopped");
    }

  }

  protected void init_repos(Map<String, Object> initParama) throws Exception {

    String path = (String) initParama.get(RepoDef.URI_FIELD);

    URI uri = URI.create(ConfigSourceResolver.DEFAULT_CONFIG_URI);

    if (Files.notExists(Paths.get(uri), new LinkOption[] {}) && (!StringUtils.hasText(path)
        || path.toLowerCase().equals(ConfigSourceResolver.DEFAULT_CONFIG_URI.toLowerCase()))) {

      try (java.io.InputStream s = getClass().getClassLoader()
          .getResourceAsStream(ConfigSourceResolver.DEFAULT_CONFIG_FILE)) {

        if (s != null) {

          logger.warn("No alternative configrd config file specified. Creating default file "
              + ConfigSourceResolver.DEFAULT_CONFIG_URI
              + ". If you are running from within a docker container please ensure the path /srv/configrd is mapped to a volume.");

          Files.createDirectories(Paths.get("/srv/configrd"));
          FileUtils.writeStringToFile(Paths.get(uri).toFile(), IOUtils.toString(s, "UTF-8"),
              "UTF-8");

        } else {
          throw new FileNotFoundException("Unable to copy default file. File not found.");
        }

      } catch (Exception e) {
        logger.error("Unable to create default configrd config file at "
            + ConfigSourceResolver.DEFAULT_CONFIG_URI, e);
        throw e;
      }
    }
  }

}