package org.testcontainers.dockerclient;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.*;
import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Proxy;
import java.util.function.BiConsumer;
import static org.testcontainers.utility.AuditLogger.doLog;
/**
* Wrapper for {@link DockerClient} to facilitate 'audit logging' of potentially destruction actions using
* {@link org.testcontainers.utility.AuditLogger}.
*/
@Slf4j
@SuppressWarnings("unchecked")
public class AuditLoggingDockerClient implements DockerClient {
@Delegate(excludes = InterceptedMethods.class)
private final DockerClient wrappedClient;
public AuditLoggingDockerClient(DockerClient wrappedClient) {
this.wrappedClient = wrappedClient;
}
@Override
public CreateContainerCmd createContainerCmd(@NotNull String image) {
return wrappedCommand(CreateContainerCmd.class,
wrappedClient.createContainerCmd(image),
(cmd, res) -> doLog("CREATE", image, res.getId(), cmd),
(cmd, e) -> doLog("CREATE", image, null, cmd, e));
}
@Override
public StartContainerCmd startContainerCmd(@NotNull String containerId) {
return wrappedCommand(StartContainerCmd.class,
wrappedClient.startContainerCmd(containerId),
(cmd, res) -> doLog("START", null, containerId, cmd),
(cmd, e) -> doLog("START", null, containerId, cmd, e));
}
@Override
public RemoveContainerCmd removeContainerCmd(@NotNull String containerId) {
return wrappedCommand(RemoveContainerCmd.class,
wrappedClient.removeContainerCmd(containerId),
(cmd, res) -> doLog("REMOVE", null, containerId, cmd),
(cmd, e) -> doLog("REMOVE", null, containerId, cmd, e));
}
@Override
public StopContainerCmd stopContainerCmd(@NotNull String containerId) {
return wrappedCommand(StopContainerCmd.class,
wrappedClient.stopContainerCmd(containerId),
(cmd, res) -> doLog("STOP", null, containerId, cmd),
(cmd, e) -> doLog("STOP", null, containerId, cmd, e));
}
@Override
public KillContainerCmd killContainerCmd(@NotNull String containerId) {
return wrappedCommand(KillContainerCmd.class,
wrappedClient.killContainerCmd(containerId),
(cmd, res) -> doLog("KILL", null, containerId, cmd),
(cmd, e) -> doLog("KILL", null, containerId, cmd, e));
}
@Override
public CreateNetworkCmd createNetworkCmd() {
return wrappedCommand(CreateNetworkCmd.class,
wrappedClient.createNetworkCmd(),
(cmd, res) -> doLog("CREATE_NETWORK", null, null, cmd),
(cmd, e) -> doLog("CREATE_NETWORK", null, null, cmd, e));
}
@Override
public RemoveNetworkCmd removeNetworkCmd(@NotNull String networkId) {
return wrappedCommand(RemoveNetworkCmd.class,
wrappedClient.removeNetworkCmd(networkId),
(cmd, res) -> doLog("REMOVE_NETWORK", null, null, cmd),
(cmd, e) -> doLog("REMOVE_NETWORK", null, null, cmd, e));
}
private <T extends SyncDockerCmd<R>, R> T wrappedCommand(Class<T> clazz,
T cmd,
BiConsumer<T, R> successConsumer,
BiConsumer<T, Exception> failureConsumer) {
return (T) Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class<?>[]{clazz},
(proxy, method, args) -> {
if (method.getName().equals("exec")) {
try {
R r = (R) method.invoke(cmd, args);
successConsumer.accept(cmd, r);
return r;
} catch (Exception e) {
failureConsumer.accept(cmd, e);
throw e;
}
} else {
return method.invoke(cmd, args);
}
});
}
@SuppressWarnings("unused")
private interface InterceptedMethods {
CreateContainerCmd createContainerCmd(String image);
StartContainerCmd startContainerCmd(String containerId);
RemoveContainerCmd removeContainerCmd(String containerId);
StopContainerCmd stopContainerCmd(String containerId);
KillContainerCmd killContainerCmd(String containerId);
CreateNetworkCmd createNetworkCmd();
RemoveNetworkCmd removeNetworkCmd(String networkId);
}
}