public static DockerContainer fromContainerInfo(ContainerInfo container) {
   Map<String, String> labels = container.config().labels();
   return new DockerContainer(
       container.name().substring(1),
       container.created(),
       GSON.fromJson(labels.get(Constants.CONFIGURATION_LABEL_KEY), HashMap.class),
       labels.get(Constants.ENVIRONMENT_LABEL_KEY));
 }
  public static DockerContainer create(
      CreateAgentRequest request, PluginSettings settings, DockerClient docker)
      throws InterruptedException, DockerException, IOException {
    String containerName = UUID.randomUUID().toString();

    HashMap<String, String> labels = labelsFrom(request);
    String imageName = image(request.properties());
    List<String> env = environmentFrom(request, settings, containerName);

    try {
      docker.inspectImage(imageName);
    } catch (ImageNotFoundException ex) {
      LOG.info("Image " + imageName + " not found, attempting to download.");
      docker.pull(imageName);
    }

    ContainerConfig.Builder containerConfigBuilder = ContainerConfig.builder();
    if (request.properties().containsKey("Command")) {
      containerConfigBuilder.cmd(
          splitIntoLinesAndTrimSpaces(request.properties().get("Command"))
              .toArray(new String[] {}));
    }

    ContainerConfig containerConfig =
        containerConfigBuilder.image(imageName).labels(labels).env(env).build();
    ContainerCreation container = docker.createContainer(containerConfig, containerName);
    String id = container.id();

    ContainerInfo containerInfo = docker.inspectContainer(id);

    LOG.debug("Created container " + containerName);
    docker.startContainer(containerName);

    return new DockerContainer(
        containerName, containerInfo.created(), request.properties(), request.environment());
  }
  @Override
  public int execute(
      final List<String> commandLine,
      final File executionDirectory,
      final Map<String, String> environmentVariables,
      final File temporaryDirectory,
      final File stdoutFile,
      final File stderrFile,
      final boolean redirectErrorStream)
      throws EoulsanException {

    checkNotNull(commandLine, "commandLine argument cannot be null");
    checkNotNull(executionDirectory, "executionDirectory argument cannot be null");
    checkNotNull(stdoutFile, "stdoutFile argument cannot be null");
    checkNotNull(stderrFile, "stderrFile argument cannot be null");

    checkArgument(
        executionDirectory.isDirectory(),
        "execution directory does not exists or is not a directory: "
            + executionDirectory.getAbsolutePath());

    try {

      final List<String> env = new ArrayList<>();

      if (environmentVariables != null) {
        for (Map.Entry<String, String> e : environmentVariables.entrySet()) {
          env.add(e.getKey() + '=' + e.getValue());
        }
      }

      // Pull image if needed
      pullImageIfNotExists(this.dockerClient, this.dockerImage);

      // Create container configuration
      getLogger().fine("Configure container, command to execute: " + commandLine);

      final ContainerConfig.Builder builder =
          ContainerConfig.builder().image(dockerImage).cmd(commandLine);

      // Set the working directory
      builder.workingDir(executionDirectory.getAbsolutePath());

      // Set the UID and GID of the docker process
      if (this.userUid >= 0 && this.userGid >= 0) {
        builder.user(this.userUid + ":" + this.userGid);
      }

      // Define temporary directory
      final List<File> toBind;
      if (temporaryDirectory.isDirectory()) {
        toBind = singletonList(temporaryDirectory);
        env.add(TMP_DIR_ENV_VARIABLE + "=" + temporaryDirectory.getAbsolutePath());
      } else {
        toBind = Collections.emptyList();
      }

      builder.hostConfig(createBinds(executionDirectory, toBind));

      // Set environment variables
      builder.env(env);

      // Create container
      final ContainerCreation creation = this.dockerClient.createContainer(builder.build());

      // Get container id
      final String containerId = creation.id();

      // Start container
      getLogger().fine("Start of the Docker container: " + containerId);
      this.dockerClient.startContainer(containerId);

      // Redirect stdout and stderr
      final LogStream logStream =
          this.dockerClient.logs(
              containerId, LogsParameter.FOLLOW, LogsParameter.STDERR, LogsParameter.STDOUT);
      redirect(logStream, stdoutFile, stderrFile, redirectErrorStream);

      // Wait the end of the container
      getLogger().fine("Wait the end of the Docker container: " + containerId);
      this.dockerClient.waitContainer(containerId);

      // Get process exit code
      final ContainerInfo info = this.dockerClient.inspectContainer(containerId);
      final int exitValue = info.state().exitCode();
      getLogger().fine("Exit value: " + exitValue);

      // Stop container before removing it
      this.dockerClient.stopContainer(containerId, SECOND_TO_WAIT_BEFORE_KILLING_CONTAINER);

      // Remove container
      getLogger().fine("Remove Docker container: " + containerId);
      try {
        this.dockerClient.removeContainer(containerId);
      } catch (DockerException | InterruptedException e) {
        EoulsanLogger.getLogger().severe("Unable to remove Docker container: " + containerId);
      }

      return exitValue;
    } catch (DockerException | InterruptedException e) {
      throw new EoulsanException(e);
    }
  }
  private MineCloudDaemon(Properties properties) {
    redis = MineCloud.instance().redis();
    mongo = MineCloud.instance().mongo();
    dockerClient = new DefaultDockerClient("unix:///var/run/docker.sock");

    node = (String) properties.get("node-name");
    instance = this;

    redis.addChannel(
        SimpleRedisChannel.create("server-create", redis)
            .addCallback(
                (message) -> {
                  if (message.type() != MessageType.BINARY) {
                    return;
                  }

                  MessageInputStream stream = message.contents();

                  if (!stream.readString().equalsIgnoreCase(node)) {
                    return;
                  }

                  Network network =
                      mongo.repositoryBy(Network.class).findFirst(stream.readString());
                  ServerType type =
                      mongo.repositoryBy(ServerType.class).findFirst(stream.readString());
                  List<ServerMetadata> metadata = new ArrayList<>();
                  int size = stream.readVarInt32();

                  for (int i = 0; i < size; i++) {
                    metadata.add(new ServerMetadata(stream.readString(), stream.readString()));
                  }

                  Deployer.deployServer(network, type, metadata);
                }));

    redis.addChannel(
        SimpleRedisChannel.create("server-kill", redis)
            .addCallback(
                (message) -> {
                  if (message.type() != MessageType.BINARY) {
                    return;
                  }

                  MessageInputStream stream = message.contents();

                  if (!stream.readString().equalsIgnoreCase(node)) return;

                  Server server = mongo.repositoryBy(Server.class).findFirst(stream.readString());

                  if (!server.node().name().equals(node)) {
                    MineCloud.logger()
                        .log(
                            Level.SEVERE,
                            "Invalid request was sent to kill a server "
                                + "not on the current node");
                    return;
                  }

                  try {
                    dockerClient.killContainer(server.containerId());
                    MineCloud.logger()
                        .info(
                            "Killed server "
                                + server.name()
                                + " with container id "
                                + server.containerId());

                    mongo.repositoryBy(Server.class).delete(server);
                  } catch (DockerException | InterruptedException e) {
                    MineCloud.logger().log(Level.SEVERE, "Was unable to kill a server", e);
                  }
                }));

    redis.addChannel(
        SimpleRedisChannel.create("bungee-create", redis)
            .addCallback(
                (message) -> {
                  if (message.type() != MessageType.BINARY) {
                    return;
                  }

                  MessageInputStream stream = message.contents();

                  if (!stream.readString().equalsIgnoreCase(node)) return;

                  Network network =
                      mongo.repositoryBy(Network.class).findFirst(stream.readString());
                  BungeeType type =
                      mongo.repositoryBy(BungeeType.class).findFirst(stream.readString());

                  Deployer.deployBungee(network, type);
                }));

    redis.addChannel(
        SimpleRedisChannel.create("bungee-kill", redis)
            .addCallback(
                (message) -> {
                  if (message.type() != MessageType.BINARY) {
                    return;
                  }

                  MessageInputStream stream = message.contents();

                  if (!stream.readString().equalsIgnoreCase(node)) return;

                  Bungee bungee = mongo.repositoryBy(Bungee.class).findFirst(stream.readString());

                  if (!bungee.node().name().equals(node)) {
                    MineCloud.logger()
                        .log(
                            Level.SEVERE,
                            "Invalid request was sent to kill a bungee "
                                + "not on the current node");
                    return;
                  }

                  try {
                    dockerClient.killContainer(bungee.containerId());
                    MineCloud.logger()
                        .info(
                            "Killed bungee "
                                + bungee.name()
                                + " with container id "
                                + bungee.containerId());

                    mongo.repositoryBy(Bungee.class).delete(bungee);
                  } catch (DockerException | InterruptedException e) {
                    MineCloud.logger().log(Level.SEVERE, "Was unable to kill a server", e);
                  }
                }));

    redis.addChannel(
        SimpleRedisChannel.create("server-start-notif", redis)
            .addCallback(
                (message) -> {
                  if (message.type() != MessageType.BINARY) return;

                  MessageInputStream stream = message.contents();

                  Server server = mongo.repositoryBy(Server.class).findFirst(stream.readString());

                  if (!server.node().name().equals(node)) return;

                  if (server.port() != -1) return; // don't even know how this would happen

                  try {
                    ContainerInfo info = dockerClient.inspectContainer(server.containerId());

                    info.networkSettings()
                        .ports()
                        .forEach(
                            (s, l) -> {
                              if (!s.contains("25565")) return;

                              server.setPort(Integer.parseInt(l.get(0).hostPort()));
                              MineCloud.logger()
                                  .log(
                                      Level.INFO,
                                      "Set " + server.name() + "'s port to " + server.port());
                            });

                    mongo.repositoryBy(Server.class).save(server);
                  } catch (Exception e) {
                    MineCloud.logger()
                        .log(Level.SEVERE, "Was unable to set the port of a started server", e);
                  }
                }));

    redis.addChannel(SimpleRedisChannel.create("server-shutdown-notif", redis));

    new StatisticsWatcher().start();

    while (!Thread.currentThread().isInterrupted()) {
      try {
        dockerClient
            .listContainers(DockerClient.ListContainersParam.allContainers())
            .stream()
            .filter(
                (container) ->
                    !container.status().toLowerCase().contains("up")
                        && ((System.currentTimeMillis() / 1000L) - container.created()) > 5L)
            .forEach(
                (container) -> {
                  try {
                    String name =
                        container.names() == null || container.names().isEmpty()
                            ? "null"
                            : container.names().get(0);
                    dockerClient.removeContainer(container.id());

                    if (container.image().contains("minecloud")) {
                      String type = container.image().substring(9);

                      switch (type.toLowerCase()) {
                        case "bungee":
                          AbstractMongoRepository<Bungee> repository =
                              mongo.repositoryBy(Bungee.class);
                          Query<Bungee> query =
                              repository.createQuery().field("_id").equal(node().publicIp());
                          repository.deleteByQuery(query);
                          break;

                        case "server":
                          Server server =
                              mongo
                                  .repositoryBy(Server.class)
                                  .findOne("containerId", container.names().get(0));

                          if (server != null) {
                            mongo.repositoryBy(Server.class).delete(server);
                          }
                          break;
                      }
                    }

                    MineCloud.logger()
                        .info("Killed dead container " + container.id() + " (" + name + ")");
                  } catch (DockerException | InterruptedException e) {
                    MineCloud.logger()
                        .log(
                            Level.SEVERE,
                            "Was unable to kill exited container " + container.id(),
                            e);
                  }
                });
        ServerRepository repository = mongo.repositoryBy(Server.class);
        Query<Server> query =
            repository
                .createQuery()
                .field("node")
                .equal(node())
                .field("port")
                .notEqual(-1)
                .field("tps")
                .notEqual(-1);

        repository
            .find(query)
            .asList()
            .forEach(
                (server) -> {
                  boolean exists;

                  try {
                    dockerClient.inspectContainer(server.containerId());
                    exists = true;
                  } catch (DockerException | InterruptedException ex) {
                    exists = false;
                  }

                  if (exists) {
                    return;
                  }

                  repository.delete(server);
                  MineCloud.logger()
                      .info(
                          "Removed "
                              + server.containerId()
                              + " from DB due to not existing as a container");
                });
      } catch (DockerException | InterruptedException e) {
        MineCloud.logger().log(Level.SEVERE, "Was unable to list containers for update", e);
      }

      try {
        Thread.sleep(2000L);
      } catch (InterruptedException ignored) {
        // I don't care
      }
    }
  }