All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.minecloud.daemon.Deployer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015, Mazen Kotb 
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
package io.minecloud.daemon;

import io.minecloud.MineCloud;
import io.minecloud.MineCloudException;
import io.minecloud.db.Credentials;
import io.minecloud.models.bungee.Bungee;
import io.minecloud.models.bungee.BungeeRepository;
import io.minecloud.models.bungee.type.BungeeType;
import io.minecloud.models.network.Network;
import io.minecloud.models.nodes.Node;
import io.minecloud.models.server.Server;
import io.minecloud.models.server.ServerMetadata;
import io.minecloud.models.server.ServerRepository;
import io.minecloud.models.server.type.ServerType;
import redis.clients.jedis.Jedis;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

public final class Deployer {
    public static final AtomicInteger PORT_COUNTER = new AtomicInteger(32812);

    private Deployer() {
    }

    public static void deployServer(Network network, ServerType type, List metadata) {
        Credentials mongoCreds = MineCloud.instance().mongo().credentials();
        Credentials redisCreds = MineCloud.instance().redis().credentials();
        ServerRepository repository = MineCloud.instance().mongo().repositoryBy(Server.class);
        Server server = new Server();

        server.setType(type);
        server.setNumber(repository.nextNumberFor(type));
        server.setNetwork(network);
        server.setNode(MineCloudDaemon.instance().node());
        server.setOnlinePlayers(new ArrayList<>());
        server.setRamUsage(-1);
        server.setId(server.type().name() + server.number());
        server.setMetadata(metadata);
        server.setPort(PORT_COUNTER.incrementAndGet());
        server.setContainerId("null");
        server.setStartTime(System.currentTimeMillis());

        try {
            if (isRunning(server.name())) {
                return;
            }
        } catch (IOException | InterruptedException ignored) {
        }

        Map env = new HashMap() {{
            put("mongo_hosts", mongoCreds.formattedHosts());
            put("mongo_username", mongoCreds.username());
            put("mongo_password", new String(mongoCreds.password()));
            put("mongo_database", mongoCreds.database());

            put("redis_host", redisCreds.hosts()[0]);
            put("redis_password", new String(redisCreds.password()));
            put("SERVER_MOD", server.type().mod());
            put("DEDICATED_RAM", String.valueOf(server.type().dedicatedRam()));
            put("MAX_PLAYERS", String.valueOf(server.type().maxPlayers()));

            put("server_id", server.entityId());
            put("DEFAULT_WORLD", type.defaultWorld().name());
            put("DEFAULT_WORLD_VERSION", type.defaultWorld().version());

            put("PORT", String.valueOf(server.port()));
            put("PRIVATE_IP", server.node().privateIp());
        }};

        startApplication(processScript("/mnt/minecloud/server/bukkit/" + server.type().mod() + "/init.sh", env), server.name());
        repository.save(server);
        MineCloud.logger().info("Started server " + server.name() + " with container id " + server.containerId());
    }

    public static void deployBungee(Network network, BungeeType type) {
        BungeeRepository repository = MineCloud.instance().mongo().repositoryBy(Bungee.class);
        Node node = MineCloudDaemon.instance().node();
        Bungee bungee = new Bungee();

        if (repository.count("_id", node.publicIp()) > 0) {
            MineCloud.logger().log(Level.WARNING, "Did not create bungee on this node; public ip is already in use");
            return;
        }

        bungee.setId(node.publicIp());
        bungee.setType(type);

        Credentials mongoCreds = MineCloud.instance().mongo().credentials();
        Credentials redisCreds = MineCloud.instance().redis().credentials();
        Map env = new HashMap() {{
            put("mongo_hosts", mongoCreds.formattedHosts());
            put("mongo_username", mongoCreds.username());
            put("mongo_password", new String(mongoCreds.password()));
            put("mongo_database", mongoCreds.database());

            put("redis_host", redisCreds.hosts()[0]);
            put("redis_password", new String(redisCreds.password()));
            put("DEDICATED_RAM", String.valueOf(type.dedicatedRam()));

            put("bungee_id", node.publicIp());
        }};

        startApplication(processScript("/mnt/minecloud/scripts/bungee-init.sh", env), "bungee");

        bungee.setNetwork(network);
        bungee.setNode(node);
        bungee.setPublicIp(node.publicIp());

        repository.save(bungee);
        MineCloud.logger().info("Started bungee " + bungee.name() + " with id " + bungee.containerId());
    }

    public static int pidOf(String app) throws IOException {
        return Integer.parseInt(Files.readAllLines(Paths.get("/var/minecloud/" + app + "/app.pid")).get(0));
    }

    public static long timeStarted(String app) throws IOException {
        return Long.parseLong(Files.readAllLines(Paths.get("/var/minecloud/" + app + "/started.ts")).get(0));
    }

    public static void killServer(String name) {
        try (Jedis jedis = MineCloudDaemon.instance().redis().grabResource()) {
            jedis.hdel("server:" + name, "heartbeat");
        }

        try {
            int pid = Deployer.pidOf(name);
            new ProcessBuilder().command("/usr/bin/kill", "-9", String.valueOf(pid)).start();
            MineCloud.logger().info("Killed pid " + pid + " belonging to " + name);
            Deployer.runExit(name);
            MineCloud.logger().info("Executed exit for " + name + " successfully");
        } catch (IOException ignored) {
        }

        try {
            Runtime.getRuntime().exec(("/usr/bin/rm -rf " + new File("/var/minecloud/" + name)).split(" "));
            MineCloud.logger().info("Deleted folder of dead server " + name);
        } catch (IOException ignored) {
        }
    }

    public static void runExit(String app) throws IOException {
        File file = new File("/var/minecloud/" + app + "/exit.sh");

        if (!file.exists()) {
            return;
        }

        new ProcessBuilder()
                .directory(new File("/var/minecloud/" + app))
                .redirectErrorStream(true)
                .command("sh", "exit.sh", app)
                .start();
    }

    public static boolean isRunning(String app) throws InterruptedException, IOException {
        Process process = Runtime.getRuntime().exec("ps -p " + pidOf(app));

        process.waitFor();
        return process.exitValue() == 0;
    }

    private static List processScript(String file, Map env) {
        List script;

        try {
            script = Files.readAllLines(Paths.get(file));
        } catch (IOException ex) {
            throw new MineCloudException(ex);
        }

        script.replaceAll((s) -> {
            Container container = new Container<>(s);

            env.forEach((find, replace) -> container.set(container.get().replace("]" + find, replace)));

            return container.get();
        });

        return script;
    }

    private static void startApplication(List startScript, String name) {
        File runDir = new File("/var/minecloud/" + name);

        if (runDir.exists()) {
            runDir.delete();
        }

        runDir.mkdirs();

        try {
            Files.write(Paths.get(runDir.getAbsolutePath(), "init.sh"), startScript);
            Files.write(Paths.get(runDir.getAbsolutePath(), "started.ts"), Arrays.asList(String.valueOf(System.currentTimeMillis())));
            new File(runDir, "init.sh").setExecutable(true);

            Process process = new ProcessBuilder()
                    .directory(runDir)
                    .redirectErrorStream(true)
                    .command("/usr/bin/screen", "-dm", "-S", name, "sh", "init.sh")
                    .start();
        } catch (IOException ex) {
            throw new MineCloudException(ex);
        }
    }

    private static class Container {
        private T value;

        public Container(T value) {
            this.value = value;
        }

        public Container() {
            this.value = null;
        }

        public T get() {
            return value;
        }

        public void set(T value) {
            this.value = value;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy