Add property to specify Docker Compose flags

See gh-42571
This commit is contained in:
Dmytro Nosan
2024-10-05 13:24:27 +03:00
committed by Moritz Halbritter
parent 31fada6d3e
commit 2bee29c2fd
12 changed files with 227 additions and 28 deletions

View File

@@ -97,7 +97,8 @@ class DefaultDockerCompose implements DockerCompose {
if (runningPsResponses.isEmpty()) {
return Collections.emptyList();
}
DockerComposeFile dockerComposeFile = this.cli.getDockerComposeFile();
DockerCompose.Options options = this.cli.getDockerComposeOptions();
DockerComposeFile dockerComposeFile = options.getComposeFile();
List<RunningService> result = new ArrayList<>();
Map<String, DockerCliInspectResponse> inspected = inspect(runningPsResponses);
for (DockerCliComposePsResponse psResponse : runningPsResponses) {

View File

@@ -18,7 +18,6 @@ package org.springframework.boot.docker.compose.core;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -31,6 +30,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.boot.docker.compose.core.DockerCliCommand.Type;
import org.springframework.boot.logging.LogLevel;
import org.springframework.core.log.LogMessage;
import org.springframework.util.CollectionUtils;
/**
* Wrapper around {@code docker} and {@code docker-compose} command line tools.
@@ -49,22 +49,18 @@ class DockerCli {
private final DockerCommands dockerCommands;
private final DockerComposeFile composeFile;
private final Set<String> activeProfiles;
private final DockerCompose.Options dockerComposeOptions;
/**
* Create a new {@link DockerCli} instance.
* @param workingDirectory the working directory or {@code null}
* @param composeFile the Docker Compose file to use
* @param activeProfiles the Docker Compose profiles to activate
* @param dockerComposeOptions the Docker Compose options to use or {@code null}.
*/
DockerCli(File workingDirectory, DockerComposeFile composeFile, Set<String> activeProfiles) {
DockerCli(File workingDirectory, DockerCompose.Options dockerComposeOptions) {
this.processRunner = new ProcessRunner(workingDirectory);
this.dockerCommands = dockerCommandsCache.computeIfAbsent(workingDirectory,
(key) -> new DockerCommands(this.processRunner));
this.composeFile = composeFile;
this.activeProfiles = (activeProfiles != null) ? activeProfiles : Collections.emptySet();
this.dockerComposeOptions = (dockerComposeOptions != null) ? dockerComposeOptions : DockerCompose.Options.NONE;
}
/**
@@ -93,17 +89,26 @@ class DockerCli {
case DOCKER -> new ArrayList<>(this.dockerCommands.get(type));
case DOCKER_COMPOSE -> {
List<String> result = new ArrayList<>(this.dockerCommands.get(type));
if (this.composeFile != null) {
for (File file : this.composeFile.getFiles()) {
DockerCompose.Options options = this.dockerComposeOptions;
DockerComposeFile composeFile = options.getComposeFile();
if (composeFile != null) {
for (File file : composeFile.getFiles()) {
result.add("--file");
result.add(file.getPath());
}
}
result.add("--ansi");
result.add("never");
for (String profile : this.activeProfiles) {
result.add("--profile");
result.add(profile);
Set<String> activeProfiles = options.getActiveProfiles();
if (!CollectionUtils.isEmpty(activeProfiles)) {
for (String profile : activeProfiles) {
result.add("--profile");
result.add(profile);
}
}
List<String> arguments = options.getArguments();
if (!CollectionUtils.isEmpty(arguments)) {
result.addAll(arguments);
}
yield result;
}
@@ -111,11 +116,11 @@ class DockerCli {
}
/**
* Return the {@link DockerComposeFile} being used by this CLI instance.
* @return the Docker Compose file
* Return the {@link DockerCompose.Options} being used by this CLI instance.
* @return the Docker Compose options
*/
DockerComposeFile getDockerComposeFile() {
return this.composeFile;
DockerCompose.Options getDockerComposeOptions() {
return this.dockerComposeOptions;
}
/**

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.docker.compose.core;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -125,8 +126,61 @@ public interface DockerCompose {
* @return a {@link DockerCompose} instance
*/
static DockerCompose get(DockerComposeFile file, String hostname, Set<String> activeProfiles) {
DockerCli cli = new DockerCli(null, file, activeProfiles);
DockerCli cli = new DockerCli(null, Options.get(file, activeProfiles, Collections.emptyList()));
return new DefaultDockerCompose(cli, hostname);
}
/**
* Factory method used to create a {@link DockerCompose} instance.
* @param hostname the hostname used for services or {@code null} if the hostname
* @param options the Docker Compose options or {@code null}
* @return a {@link DockerCompose} instance
* @since 3.4.0
*/
static DockerCompose get(String hostname, Options options) {
DockerCli cli = new DockerCli(null, options);
return new DefaultDockerCompose(cli, hostname);
}
/**
* Docker Compose options that should be applied before any subcommand.
*/
interface Options {
/**
* No options.
*/
Options NONE = get(null, Collections.emptySet(), Collections.emptyList());
/**
* Factory method used to create a {@link DockerCompose.Options} instance.
* @param file the Docker Compose file to use
* @param activeProfiles the Docker Compose profiles to activate
* @param arguments the additional Docker Compose arguments
* @return the Docker Compose options
*/
static Options get(DockerComposeFile file, Set<String> activeProfiles, List<String> arguments) {
return new DockerComposeOptions(file, activeProfiles, arguments);
}
/**
* the Docker Compose a file to use.
* @return compose a file to use
*/
DockerComposeFile getComposeFile();
/**
* the Docker Compose profiles to activate.
* @return profiles to activate
*/
Set<String> getActiveProfiles();
/**
* the additional Docker Compose arguments.
* @return additional arguments
*/
List<String> getArguments();
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.docker.compose.core;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.springframework.core.style.ToStringCreator;
/**
* Default {@link DockerCompose.Options} implementation.
*
* @author Dmytro Nosan
*/
final class DockerComposeOptions implements DockerCompose.Options {
private final DockerComposeFile composeFile;
private final Set<String> activeProfiles;
private final List<String> arguments;
/**
* Create a new {@link DockerComposeOptions} instance.
* @param composeFile the Docker Compose file to use
* @param activeProfiles the Docker Compose profiles to activate
* @param arguments the additional Docker Compose arguments (e.g. --project-name=...)
*/
DockerComposeOptions(DockerComposeFile composeFile, Set<String> activeProfiles, List<String> arguments) {
this.composeFile = composeFile;
this.activeProfiles = (activeProfiles != null) ? activeProfiles : Collections.emptySet();
this.arguments = (arguments != null) ? arguments : Collections.emptyList();
}
@Override
public DockerComposeFile getComposeFile() {
return this.composeFile;
}
@Override
public Set<String> getActiveProfiles() {
return this.activeProfiles;
}
@Override
public List<String> getArguments() {
return this.arguments;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
DockerComposeOptions that = (DockerComposeOptions) obj;
return Objects.equals(this.composeFile, that.composeFile)
&& Objects.equals(this.activeProfiles, that.activeProfiles)
&& Objects.equals(this.arguments, that.arguments);
}
@Override
public int hashCode() {
return Objects.hash(this.composeFile, this.activeProfiles, this.arguments);
}
@Override
public String toString() {
ToStringCreator creator = new ToStringCreator(this);
creator.append("composeFile", this.composeFile);
creator.append("activeProfiles", this.activeProfiles);
creator.append("arguments", this.arguments);
return creator.toString();
}
}

View File

@@ -109,7 +109,8 @@ class DockerComposeLifecycleManager {
}
DockerComposeFile composeFile = getComposeFile();
Set<String> activeProfiles = this.properties.getProfiles().getActive();
DockerCompose dockerCompose = getDockerCompose(composeFile, activeProfiles);
List<String> arguments = this.properties.getArguments();
DockerCompose dockerCompose = getDockerCompose(composeFile, activeProfiles, arguments);
if (!dockerCompose.hasDefinedServices()) {
logger.warn(LogMessage.format("No services defined in Docker Compose file %s with active profiles %s",
composeFile, activeProfiles));
@@ -159,8 +160,10 @@ class DockerComposeLifecycleManager {
return composeFile;
}
protected DockerCompose getDockerCompose(DockerComposeFile composeFile, Set<String> activeProfiles) {
return DockerCompose.get(composeFile, this.properties.getHost(), activeProfiles);
protected DockerCompose getDockerCompose(DockerComposeFile composeFile, Set<String> activeProfiles,
List<String> arguments) {
DockerCompose.Options options = DockerCompose.Options.get(composeFile, activeProfiles, arguments);
return DockerCompose.get(this.properties.getHost(), options);
}
private boolean isIgnored(RunningService service) {

View File

@@ -46,6 +46,11 @@ public class DockerComposeProperties {
*/
private boolean enabled = true;
/**
* Arguments to pass to the docker compose command.
*/
private final List<String> arguments = new ArrayList<>();
/**
* Paths to the Docker Compose configuration files.
*/
@@ -88,6 +93,10 @@ public class DockerComposeProperties {
this.enabled = enabled;
}
public List<String> getArguments() {
return this.arguments;
}
public List<File> getFile() {
return this.file;
}