001package io.ebean.docker.commands;
002
003import io.ebean.docker.commands.process.ProcessHandler;
004import io.ebean.docker.commands.process.ProcessResult;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008import java.util.ArrayList;
009import java.util.Arrays;
010import java.util.List;
011
012/**
013 * Common Docker container commands.
014 */
015public class Commands {
016
017  private static final Logger log = LoggerFactory.getLogger(Commands.class);
018
019  private final String docker;
020
021  /**
022   * Create with 'docker' as the command.
023   */
024  public Commands() {
025    this("docker");
026  }
027
028  /**
029   * Construct with explicit docker command.
030   */
031  public Commands(String docker) {
032    this.docker = docker;
033  }
034
035  /**
036   * Stop the container checking to see if it is running first.
037   */
038  public void stopIfRunning(String containerName) {
039    if (isRunning(containerName)) {
040      stop(containerName);
041    }
042  }
043
044  /**
045   * Stop and remove the container.
046   */
047  public void stopRemove(String containerName) {
048    if (isRunning(containerName)) {
049      stop(containerName);
050    }
051
052    if (isRegistered(containerName)) {
053      remove(containerName);
054    }
055  }
056
057  /**
058   * Remove the container.
059   */
060  public void remove(String containerName) {
061    log.debug("remove {}", containerName);
062    ProcessHandler.command(docker, "rm", containerName);
063  }
064
065  /**
066   * Start the container.
067   */
068  public void start(String containerName) {
069    log.debug("start {}", containerName);
070    ProcessHandler.command(docker, "start", containerName);
071  }
072
073  /**
074   * Stop the container.
075   */
076  public void stop(String containerName) {
077    log.debug("stop {}", containerName);
078    ProcessHandler.command(docker, "stop", containerName);
079  }
080
081  public void removeContainers(String... containerNames) {
082    log.debug("remove {}", Arrays.toString(containerNames));
083    try {
084      dockerCmd("rm", containerNames);
085    } catch (CommandException e) {
086      log.debug("removing containers that don't exist " + e.getMessage());
087    }
088  }
089
090  public void stopContainers(String... containerNames) {
091    log.debug("stop {}", Arrays.toString(containerNames));
092    try {
093      dockerCmd("stop", containerNames);
094    } catch (CommandException e) {
095      log.debug("stopping containers that don't exist " + e.getMessage());
096    }
097  }
098
099  private void dockerCmd(String cmd, String[] containerNames) {
100    final List<String> cmds = new ArrayList<>();
101    cmds.add(docker);
102    cmds.add(cmd);
103    for (String containerName : containerNames) {
104      cmds.add(containerName);
105    }
106    ProcessHandler.command(cmds);
107  }
108
109  /**
110   * Return true if the container is running.
111   */
112  public boolean isRunning(String containerName) {
113    return running().contains(containerName);
114  }
115
116  /**
117   * Return true if the container is registered (exists and maybe running or not).
118   */
119  public boolean isRegistered(String containerName) {
120    return registered().contains(containerName);
121  }
122
123  /**
124   * Return true if the logs of the container contain the match text.
125   */
126  public boolean logsContain(String containerName, String match) {
127    return logsContain(containerName, match, null);
128  }
129
130  public boolean logsContain(String containerName, String match, String clearMatch) {
131    List<String> matchLines = logsWithMatch(containerName, match, clearMatch);
132    return !matchLines.isEmpty();
133  }
134
135  /**
136   * Return true if the logs of the container contain the match text.
137   */
138  public List<String> logsWithMatch(String containerName, String match, String clearMatch) {
139    ProcessResult result = ProcessHandler.matchCommand(match, clearMatch, docker, "logs", containerName);
140    return result.getOutLines();
141  }
142
143  /**
144   * Return true if the logs of the container contain the match text.
145   */
146  public List<String> logs(String containerName) {
147    ProcessResult result = ProcessHandler.command(docker, "logs", containerName);
148    return result.getOutLines();
149  }
150
151  /**
152   * Return the list of containers currently running.
153   */
154  private List<String> running() {
155    ProcessResult result = ProcessHandler.command(docker, "ps", "--format", "{{.Names}}");
156    return result.getOutLines();
157  }
158
159  /**
160   * Return the list of containers which maybe running or not.
161   */
162  private List<String> registered() {
163    ProcessResult result = ProcessHandler.command(docker, "ps", "-a", "--format", "{{.Names}}");
164    return result.getOutLines();
165  }
166
167  /**
168   * Check if the port matches the existing port bindings and if not return the existing port bindings.
169   */
170  public String registeredPortMatch(String containerName, int matchPort) {
171    ProcessResult result = ProcessHandler.command(docker, "container", "inspect", containerName, "--format={{.HostConfig.PortBindings}}");
172    List<String> outLines = result.getOutLines();
173    for (String outLine : outLines) {
174      if (outLine.startsWith("map")) {
175        if (outLine.contains("{ " + matchPort + "}")) {
176          // port matching all good
177          return null;
178        } else {
179          // mismatch - return all the PortBindings to include in exception message
180          return outLine;
181        }
182      }
183    }
184    // container doesn't exist
185    return null;
186  }
187
188  /**
189   * Return true if the container logs contains the logMessage.
190   * <p>
191   * Usually used to find a log entry to indicate the container is ready for use.
192   * </p>
193   *
194   * @param containerName The container logs to search
195   * @param logMessage    The logMessage we are looking for (to usually indicate the container is ready).
196   * @param tail          The number of logs to tail (such that we get recent and minimal logs)
197   * @return True if the logs searched contain the logMessage
198   */
199  public boolean logsContain(String containerName, String logMessage, int tail) {
200
201    List<String> lines = logs(containerName, tail);
202    for (String line : lines) {
203      if (line.contains(logMessage)) {
204        return true;
205      }
206    }
207    return false;
208  }
209
210  /**
211   * Return the logs for the container with a tail.
212   * <p>
213   * If tail = 0 then all logs are returned (but we should be careful using that).
214   * </p>
215   */
216  public List<String> logs(String containerName, int tail) {
217
218    ProcessResult result;
219    if (tail > 0) {
220      result = ProcessHandler.command(docker, "logs", "--tail", Integer.toString(tail), containerName);
221    } else {
222      result = ProcessHandler.command(docker, "logs", containerName);
223    }
224    return result.getOutLines();
225  }
226
227}