Package io.fathom.auto.openstack.horizon

Source Code of io.fathom.auto.openstack.horizon.HorizonProcess

package io.fathom.auto.openstack.horizon;

import io.fathom.auto.HostsFile;
import io.fathom.auto.TimeSpan;
import io.fathom.auto.openstack.horizon.config.LocalSettingsTemplate;
import io.fathom.auto.processes.Pid;
import io.fathom.auto.processes.ProcFs;
import io.fathom.auto.processes.ProcessExecution;
import io.fathom.auto.processes.Processes;
import io.fathom.http.HttpClient;
import io.fathom.http.HttpMethod;
import io.fathom.http.HttpRequest;
import io.fathom.http.HttpResponse;
import io.fathom.http.jre.JreHttpClient;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.io.Files;

public class HorizonProcess {
    private static final Logger log = LoggerFactory.getLogger(HorizonProcess.class);

    private final Pid pid;

    final File instanceDir;

    private final HorizonConfig config;

    final File installDir;

    public HorizonProcess(HorizonConfig config, Pid pid) {
        this.instanceDir = config.getInstanceDir();
        this.installDir = config.getInstallDir();
        this.config = config;
        this.pid = pid;
    }

    static File getPidFile(File instanceDir) {
        return new File(instanceDir, "horizon.pid");
    }

    static File getLogFile(File instanceDir) {
        return new File(instanceDir, "horizon.log");
    }

    static File getConfigFile(File installDir) {
        return new File(installDir, "openstack_dashboard/local/local_settings.py");
    }

    public static HorizonProcess start(HorizonConfig config) throws IOException {
        File instanceDir = config.getInstanceDir();

        configureInstance(config);

        ProcessBuilder pb = buildLauncherProcess(config);

        ProcessExecution execution = Processes.run(pb, TimeSpan.minutes(1));

        if (!execution.didExit()) {
            throw new IOException("Timeout while starting Process");
        } else {
            if (execution.getExitCode() == 0) {
                log.info("Process started OK");

                // TODO: Poll loop with timeout?
                TimeSpan.seconds(2).sleep();

                File pidFile = getPidFile(instanceDir);

                Pid pid = Pid.read(pidFile);
                if (pid == null) {
                    throw new IOException("Process started, but could not read pid from file");
                }

                return new HorizonProcess(config, pid);
            } else {
                throw new IOException("Error starting Process process");
            }
        }
    }

    private static void configureInstance(HorizonConfig config) throws IOException {
        File instanceDir = config.getInstanceDir();
        File installDir = config.getInstallDir();

        log.info("Installing new config file");

        LocalSettingsTemplate template = new LocalSettingsTemplate();
        File configFile = getConfigFile(installDir);
        template.write(configFile, config);

        HostsFile.setHosts(config.getHosts());
    }

    private static ProcessBuilder buildLauncherProcess(HorizonConfig config) throws IOException {
        File instanceDir = config.getInstanceDir();

        File logFile = getLogFile(instanceDir);
        File pidFile = getPidFile(instanceDir);

        File tmpDir = new File("/tmp");

        // Be sure we don't end up as the process owner...
        File trampolineScript = new File(tmpDir, "run-horizon.sh");

        {
            StringWriter stringWriter = new StringWriter();
            try (PrintWriter s = new PrintWriter(stringWriter)) {
                s.println("#!/bin/bash");
                s.println("");

                List<String> args = Lists.newArrayList();
                args.add("/opt/horizon/tools/with_venv.sh");
                args.add("/opt/horizon/manage.py");
                args.add("runserver");
                args.add("[::]:8080");
                args.add(">");
                args.add(logFile.getAbsolutePath());
                args.add("2>&1");
                args.add("&");

                s.println(Joiner.on(" ").join(args));

                s.println("pid=$!");
                s.println("echo ${pid} > " + pidFile.getAbsolutePath());
            }

            Files.write(stringWriter.toString(), trampolineScript, Charsets.UTF_8);
        }

        List<String> args = Lists.newArrayList();
        args.add("/bin/bash"); // Avoids need to chmod
        args.add(trampolineScript.getAbsolutePath());

        ProcessBuilder pb = new ProcessBuilder(args);
        return pb;
    }

    public static HorizonProcess find(HorizonConfig config) throws IOException {
        File pidFile = getPidFile(config.getInstanceDir());

        Pid pid = Pid.read(pidFile);
        if (pid == null) {
            return null;
        }

        HorizonProcess process = new HorizonProcess(config, pid);

        if (!process.isRunning()) {
            log.info("Found process in pid file, but was not our process: {}", pid);

            pidFile.delete();
            process = null;
        } else {
            log.info("Found existing process: {}", pid);
        }

        return process;
    }

    public boolean isRunning() throws IOException {
        ProcFs.Process process = ProcFs.findProcess(pid);
        if (process == null) {
            return false;
        }
        List<String> cmdline = process.getCmdline();
        if (!isOurProcess(cmdline)) {
            return false;
        }

        // Fetch the root page; this warms-up the app
        try {
            HttpClient httpClient = JreHttpClient.create();
            URI uri = URI.create("http://127.0.0.1:8080/");
            HttpRequest httpRequest = httpClient.buildRequest(HttpMethod.GET, uri);
            try (HttpResponse response = httpRequest.doRequest()) {
                if (response.getHttpResponseCode() != 200) {
                    throw new IllegalStateException("Bad response code from page: " + response.getHttpResponseCode());
                }
            }
        } catch (Exception e) {
            // TODO: Should we restart horizon if this failed?
            log.warn("Error while fetching warm-up page", e);
        }

        return true;
    }

    private boolean isOurProcess(List<String> cmdline) {
        if (cmdline == null) {
            // Process stopped
            return false;
        }

        if (cmdline.isEmpty()) {
            return false;
        }

        if (cmdline.get(2).equals("/opt/horizon/manage.py")) {
            return true;
        }
        return false;
    }
}
TOP

Related Classes of io.fathom.auto.openstack.horizon.HorizonProcess

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.