
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 com.spotify.docker.client.ContainerNotFoundException;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.DockerException;
import com.spotify.docker.client.messages.*;
import io.minecloud.MineCloud;
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.World;
import io.minecloud.models.server.type.ServerType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
public final class Deployer {
private static final AtomicInteger FAILED_STARTS = new AtomicInteger(0);
private Deployer() {}
public static void deployServer(Network network, ServerType type, List metadata) {
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.setPort(-1);
server.setContainerId(server.type().name() + server.number());
server.setId(server.containerId());
server.setMetadata(metadata);
repository.save(server);
new Thread(() -> {
boolean deployed = true;
for (int i = 0; i < 3 && !deployServer(server); i++) {
deployed = i != 2;
}
if (!deployed) {
failedStart(network);
repository.deleteById(server.entityId());
}
}, server.name() + " creator").start();
}
public static boolean deployServer(Server server) {
Credentials mongoCreds = MineCloud.instance().mongo().credentials();
Credentials redisCreds = MineCloud.instance().redis().credentials();
String name = server.type().name() + server.number();
World defaultWorld = server.type().defaultWorld();
ContainerConfig config = ContainerConfig.builder()
.hostname(name)
.image("minecloud/server")
.openStdin(true)
.env(new EnvironmentBuilder()
.append("mongo_hosts", mongoCreds.formattedHosts())
.append("mongo_username", mongoCreds.username())
.append("mongo_password", new String(mongoCreds.password()))
.append("mongo_database", mongoCreds.database())
.append("redis_host", redisCreds.hosts()[0])
.append("redis_password", new String(redisCreds.password()))
.append("SERVER_MOD", server.type().mod())
.append("DEDICATED_RAM", String.valueOf(server.type().dedicatedRam()))
.append("MAX_PLAYERS", String.valueOf(server.type().maxPlayers()))
.append("server_id", server.entityId())
.append("DEFAULT_WORLD", defaultWorld.name())
.append("DEFAULT_WORLD_VERSION", defaultWorld.version())
.build())
.build();
ContainerCreation creation;
try {
DockerClient client = MineCloudDaemon.instance().dockerClient();
try {
ContainerInfo info = client.inspectContainer(name);
if (info.state().running()) {
client.killContainer(name);
}
client.removeContainer(info.id());
} catch (ContainerNotFoundException ignored) {}
creation = client.createContainer(config, name);
client.startContainer(creation.id(), HostConfig.builder()
.binds("/mnt/minecloud:/mnt/minecloud")
.publishAllPorts(true)
.build());
} catch (InterruptedException | DockerException ex) {
MineCloud.logger().log(Level.SEVERE, "Was unable to create server with type " + server.type().name(),
ex);
return false;
}
MineCloud.logger().info("Started server " + server.name()
+ " with container id " + server.containerId());
return true;
}
public static void deployBungee(Network network, BungeeType type) {
Bungee bungee;
for (int i = 0; i < 3 && ((bungee = deployBungeeCord(network, type)) == null || bungee.network() == null); i++) {
}
}
public static Bungee deployBungeeCord(Network network, BungeeType type) {
DockerClient client = MineCloudDaemon.instance().dockerClient();
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 null;
}
bungee.setId(node.publicIp());
Credentials mongoCreds = MineCloud.instance().mongo().credentials();
Credentials redisCreds = MineCloud.instance().redis().credentials();
ContainerConfig config = ContainerConfig.builder()
.image("minecloud/bungee")
.hostname("bungee" + bungee.publicIp())
.exposedPorts("25565")
.openStdin(true)
.env(new EnvironmentBuilder()
.append("mongo_hosts", mongoCreds.formattedHosts())
.append("mongo_username", mongoCreds.username())
.append("mongo_password", new String(mongoCreds.password()))
.append("mongo_database", mongoCreds.database())
.append("redis_host", redisCreds.hosts()[0])
.append("redis_password", new String(redisCreds.password()))
.append("DEDICATED_RAM", String.valueOf(type.dedicatedRam()))
.append("bungee_id", node.publicIp())
.build())
.build();
HostConfig hostConfig = HostConfig.builder()
.binds("/mnt/minecloud:/mnt/minecloud")
.portBindings(new HashMap>() {{
put("25565", Arrays.asList(PortBinding.of(node.publicIp(), 25565))); // I'm sorry
}})
.publishAllPorts(true)
.build();
try {
ContainerInfo info = client.inspectContainer("bungee");
if (info.state().running()) {
client.killContainer("bungee");
}
client.removeContainer(info.id());
} catch (DockerException | InterruptedException ignored) {}
ContainerCreation creation;
try {
creation = client.createContainer(config, type.name());
client.startContainer(creation.id(), hostConfig);
} catch (InterruptedException | DockerException ex) {
MineCloud.logger().log(Level.SEVERE, "Was unable to create bungee with type " + type.name(),
ex);
return bungee;
}
bungee.setNetwork(network);
bungee.setNode(node);
bungee.setPublicIp(node.publicIp());
bungee.setType(type);
repository.save(bungee);
MineCloud.logger().info("Started bungee " + bungee.name() + " with container id " + bungee.containerId());
return bungee;
}
private static void failedStart(Network network) {
if (FAILED_STARTS.incrementAndGet() < 3) {
MineCloud.logger().warning("Failed to start a container on " + network.name() + ", adding to failed starts...");
return;
}
List nodes = network.nodes();
nodes.removeIf((node) -> node.name().equalsIgnoreCase(System.getenv("node-name")));
network.setNodes(nodes);
MineCloud.instance().mongo().repositoryBy(Network.class).save(network);
MineCloud.logger().log(Level.SEVERE, "Failed to create containers 3 times! Removing node from network...");
}
private static class EnvironmentBuilder {
private List environmentVars = new ArrayList<>();
private EnvironmentBuilder() {
}
public EnvironmentBuilder append(String key, String value) {
environmentVars.add(key + "=" + value);
return this;
}
public String[] build() {
return environmentVars.stream().toArray(String[]::new);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy