package org.arquillian.cube.docker.impl.await;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.core.async.ResultCallbackTemplate;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.arquillian.cube.docker.impl.client.config.Await;
import org.arquillian.cube.docker.impl.docker.DockerClientExecutor;
import org.arquillian.cube.spi.Cube;
public class LogScanningAwaitStrategy extends SleepingAwaitStrategyBase {
public static final String TAG = "log";
private static final Logger logger = Logger.getLogger(LogScanningAwaitStrategy.class.getName());
private static final String REGEXP_PREFIX = "regexp:";
private final LogMatcher matcher;
private int timeout = 15;
private boolean stdOut;
private boolean stdErr;
private int occurrences = 1;
private Cube<?> cube;
private DockerClientExecutor dockerClientExecutor;
public LogScanningAwaitStrategy(Cube<?> cube, DockerClientExecutor dockerClientExecutor, Await params) {
super(params.getSleepPollingTime());
this.cube = cube;
this.dockerClientExecutor = dockerClientExecutor;
this.stdOut = params.isStdOut();
this.stdErr = params.isStdErr();
this.occurrences = params.getOccurrences();
if (params.getMatch().startsWith(REGEXP_PREFIX)) {
matcher = new RegexpLogMatcher(params.getMatch().substring(REGEXP_PREFIX.length()));
} else {
matcher = new ContainsLogMatcher(params.getMatch());
}
if (params.getTimeout() != null) {
this.timeout = params.getTimeout();
}
}
public int getTimeout() {
return timeout;
}
public boolean isStdOut() {
return stdOut;
}
public boolean isStdErr() {
return stdErr;
}
public int getOccurrences() {
return occurrences;
}
@Override
public boolean await() {
final DockerClient client = dockerClientExecutor.getDockerClient();
final CountDownLatch containerUp = new CountDownLatch(1);
try (final LogContainerResultCallback callback = new LogContainerResultCallback(containerUp, this.occurrences)) {
client.logContainerCmd(cube.getId())
.withStdErr(true)
.withStdOut(true)
.withFollowStream(true)
.exec(callback);
return containerUp.await(this.timeout, TimeUnit.SECONDS);
} catch (IOException | InterruptedException e) {
logger.log(Level.SEVERE, String.format("Log Await Strategy failed with %s", e.getMessage()));
return false;
}
}
private interface LogMatcher {
boolean match(String line);
}
private static final class ContainsLogMatcher implements LogMatcher {
private String substring;
public ContainsLogMatcher(String substring) {
this.substring = substring;
}
@Override
public boolean match(String line) {
return line.contains(substring);
}
}
private static final class RegexpLogMatcher implements LogMatcher {
private Pattern regex;
public RegexpLogMatcher(String pattern) {
regex = Pattern.compile(pattern, Pattern.DOTALL);
}
@Override
public boolean match(String line) {
return regex.matcher(line).matches();
}
}
private class LogContainerResultCallback extends ResultCallbackTemplate<LogContainerResultCallback, Frame> {
private CountDownLatch containerUp;
private int occurrences;
public LogContainerResultCallback(CountDownLatch containerUp, int occurrences) {
this.containerUp = containerUp;
this.occurrences = occurrences;
}
@Override
public void onNext(Frame item) {
String line = new String(item.getPayload());
if (matcher.match(line)) {
this.occurrences--;
if (this.occurrences == 0) {
this.containerUp.countDown();
}
}
}
}
}