DockerCli Class — spring-boot Architecture
Architecture documentation for the DockerCli class in DockerCli.java from the spring-boot codebase.
Entity Profile
Relationship Graph
Source Code
core/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DockerCli.java lines 45–225
class DockerCli {
private static final Map<@Nullable File, DockerCommands> dockerCommandsCache = new HashMap<>();
private static final Log logger = LogFactory.getLog(DockerCli.class);
private final ProcessRunner processRunner;
private final DockerCommands dockerCommands;
private final DockerComposeOptions dockerComposeOptions;
private final ComposeVersion composeVersion;
/**
* Create a new {@link DockerCli} instance.
* @param workingDirectory the working directory or {@code null}
* @param dockerComposeOptions the Docker Compose options to use or {@code null}.
*/
DockerCli(@Nullable File workingDirectory, @Nullable DockerComposeOptions dockerComposeOptions) {
this.processRunner = new ProcessRunner(workingDirectory);
this.dockerCommands = dockerCommandsCache.computeIfAbsent(workingDirectory,
(key) -> new DockerCommands(this.processRunner));
this.dockerComposeOptions = (dockerComposeOptions != null) ? dockerComposeOptions : DockerComposeOptions.none();
this.composeVersion = ComposeVersion.of(this.dockerCommands.get(Type.DOCKER_COMPOSE).version());
}
/**
* Run the given {@link DockerCli} command and return the response.
* @param <R> the response type
* @param dockerCommand the command to run
* @return the response
*/
<R> R run(DockerCliCommand<R> dockerCommand) {
List<String> command = createCommand(dockerCommand.getType());
command.addAll(dockerCommand.getCommand(this.composeVersion));
Consumer<String> outputConsumer = createOutputConsumer(dockerCommand.getLogLevel());
String json = this.processRunner.run(outputConsumer, command.toArray(new String[0]));
return dockerCommand.deserialize(json);
}
private @Nullable Consumer<String> createOutputConsumer(@Nullable LogLevel logLevel) {
if (logLevel == null || logLevel == LogLevel.OFF) {
return null;
}
return (line) -> logLevel.log(logger, line);
}
private List<String> createCommand(Type type) {
return switch (type) {
case DOCKER -> new ArrayList<>(this.dockerCommands.get(type).command());
case DOCKER_COMPOSE -> {
List<String> result = new ArrayList<>(this.dockerCommands.get(type).command());
DockerComposeFile composeFile = this.dockerComposeOptions.composeFile();
if (composeFile != null) {
for (File file : composeFile.getFiles()) {
result.add("--file");
result.add(file.getPath());
}
}
result.add("--ansi");
result.add("never");
Set<String> activeProfiles = this.dockerComposeOptions.activeProfiles();
if (!CollectionUtils.isEmpty(activeProfiles)) {
for (String profile : activeProfiles) {
result.add("--profile");
result.add(profile);
}
}
List<String> arguments = this.dockerComposeOptions.arguments();
if (!CollectionUtils.isEmpty(arguments)) {
result.addAll(arguments);
}
yield result;
}
};
}
/**
* Return the {@link DockerComposeFile} being used by this CLI instance.
* @return the Docker Compose file
*/
@Nullable DockerComposeFile getDockerComposeFile() {
return this.dockerComposeOptions.composeFile();
}
/**
* Holds details of the actual CLI commands to invoke.
*/
private static class DockerCommands {
private final DockerCommand dockerCommand;
private final DockerCommand dockerComposeCommand;
DockerCommands(ProcessRunner processRunner) {
this.dockerCommand = getDockerCommand(processRunner);
this.dockerComposeCommand = getDockerComposeCommand(processRunner);
}
private DockerCommand getDockerCommand(ProcessRunner processRunner) {
try {
String version = processRunner.run("docker", "version", "--format", "{{.Client.Version}}");
logger.trace(LogMessage.format("Using docker %s", version));
return new DockerCommand(version, List.of("docker"));
}
catch (ProcessStartException ex) {
throw new DockerProcessStartException("Unable to start docker process. Is docker correctly installed?",
ex);
}
catch (ProcessExitException ex) {
if (ex.getStdErr().contains("docker daemon is not running")
|| ex.getStdErr().contains("Cannot connect to the Docker daemon")) {
throw new DockerNotRunningException(ex.getStdErr(), ex);
}
throw ex;
}
}
private DockerCommand getDockerComposeCommand(ProcessRunner processRunner) {
try {
DockerCliComposeVersionResponse response = DockerJson.deserialize(
processRunner.run("docker", "compose", "version", "--format", "json"),
DockerCliComposeVersionResponse.class);
logger.trace(LogMessage.format("Using Docker Compose %s", response.version()));
return new DockerCommand(response.version(), List.of("docker", "compose"));
}
catch (ProcessExitException ex) {
// Ignore and try docker-compose
}
try {
DockerCliComposeVersionResponse response = DockerJson.deserialize(
processRunner.run("docker-compose", "version", "--format", "json"),
DockerCliComposeVersionResponse.class);
logger.trace(LogMessage.format("Using docker-compose %s", response.version()));
return new DockerCommand(response.version(), List.of("docker-compose"));
}
catch (ProcessStartException ex) {
throw new DockerProcessStartException(
"Unable to start 'docker-compose' process or use 'docker compose'. Is docker correctly installed?",
ex);
}
}
DockerCommand get(Type type) {
return switch (type) {
case DOCKER -> this.dockerCommand;
case DOCKER_COMPOSE -> this.dockerComposeCommand;
};
}
}
private record DockerCommand(String version, List<String> command) {
}
/**
* Options for Docker Compose.
*
* @param composeFile the Docker Compose file to use
* @param activeProfiles the profiles to activate
* @param arguments the arguments to pass to Docker Compose
*/
record DockerComposeOptions(@Nullable DockerComposeFile composeFile, Set<String> activeProfiles,
List<String> arguments) {
DockerComposeOptions(@Nullable DockerComposeFile composeFile, @Nullable Set<String> activeProfiles,
@Nullable List<String> arguments) {
this.composeFile = composeFile;
this.activeProfiles = (activeProfiles != null) ? activeProfiles : Collections.emptySet();
this.arguments = (arguments != null) ? arguments : Collections.emptyList();
}
static DockerComposeOptions none() {
return new DockerComposeOptions(null, null, null);
}
}
}
Domain
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free