Skip to content

Commit

Permalink
Merge branch 'fabric8io:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Er-Sadiq authored Oct 12, 2024
2 parents a33de4f + 1d9cf08 commit 7d225cf
Show file tree
Hide file tree
Showing 32 changed files with 617 additions and 136 deletions.
6 changes: 6 additions & 0 deletions doc/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# ChangeLog
* **0.46-SNAPSHOT**:
- Docker-compose healthcheck configuration support ([1825](https://github.com/fabric8io/docker-maven-plugin/pull/1825))
- Docker container wait timeout default value made configurable using startContainerWaitTimeout configuration option ([1825](https://github.com/fabric8io/docker-maven-plugin/pull/1825))

* **0.45.1 (2024-09-29)**:
- Make copy docker-buildx binary to temporary config directory work on windows too ([1819](https://github.com/fabric8io/docker-maven-plugin/pull/1819))
- Pull FROM images in relative path Dockerfiles ([1823](https://github.com/fabric8io/docker-maven-plugin/issues/1823))

* **0.45.0 (2024-07-27)**:
- Automatically create parent directories of portPropertyFile path ([1761](https://github.com/fabric8io/docker-maven-plugin/pull/1761))
Expand Down
6 changes: 6 additions & 0 deletions it/docker-compose-dependon/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ services:
build:
context: .
dockerfile: Postgres.Dockerfile
healthcheck:
test: pg_isready -U postgres
interval: 2s
timeout: 2s
retries: 10
start_period: 1s
environment:
POSTGRES_PASSWORD: supersecret
tmpfs:
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
<maven.version>3.8.1</maven.version>
<minimum.maven.version>3.3.9</minimum.maven.version>
<mockito.version>4.5.1</mockito.version>
<project.build.outputTimestamp>2024-07-27T16:05:55Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2024-09-29T06:28:50Z</project.build.outputTimestamp>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<surefire.version>3.0.0-M2</surefire.version>
<system-stubs.version>2.0.1</system-stubs.version>
Expand Down
40 changes: 26 additions & 14 deletions samples/build-cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,36 @@ When building the project multiple times, note that subsequent builds log "Using
```
$ mvn clean package
...
[INFO] DOCKER> Step 1 : FROM gcr.io/distroless/java-debian10
[INFO] DOCKER> ---> f6a5dc137f9b
[INFO] DOCKER> Step 2 : USER nonroot
[INFO] DOCKER> Step 1/10 : FROM gcr.io/distroless/java-debian10
[INFO] DOCKER>
[INFO] DOCKER> ---> 444adf12984c
[INFO] DOCKER> Step 2/10 : USER nonroot
[INFO] DOCKER>
[INFO] DOCKER> ---> Using cache
[INFO] DOCKER> ---> 26d64b9907e3
[INFO] DOCKER> Step 3 : ENTRYPOINT
[INFO] DOCKER> ---> 043b487914d1
[INFO] DOCKER> Step 3/10 : WORKDIR "/app"
[INFO] DOCKER>
[INFO] DOCKER> ---> Using cache
[INFO] DOCKER> ---> 6eb12980cd38
[INFO] DOCKER> Step 4 : WORKDIR "/app"
[INFO] DOCKER> ---> 08ef41fef30f
[INFO] DOCKER> Step 4/10 : CMD ["tini", "-e", "130", "-e", "143", "--", "java", "org.springframework.boot.loader.JarLauncher"]
[INFO] DOCKER>
[INFO] DOCKER> ---> Using cache
[INFO] DOCKER> ---> d6a408147d7c
[INFO] DOCKER> Step 5 : CMD tini -e 130 -e 143 -- java org.springframework.boot.loader.JarLauncher
[INFO] DOCKER> ---> 2ac94352abe0
[INFO] DOCKER> Step 5/10 : ADD ["dependencies.tar", "/"]
[INFO] DOCKER>
[INFO] DOCKER> ---> Using cache
[INFO] DOCKER> ---> 461505b7ade9
[INFO] DOCKER> Step 6 : ADD dependencies.tar /
[INFO] DOCKER> ---> aafdf67c0508
[INFO] DOCKER> Step 6/10 : ADD ["spring-boot-loader.tar", "/"]
[INFO] DOCKER>
[INFO] DOCKER> ---> Using cache
[INFO] DOCKER> ---> df87a8d003f3
[INFO] DOCKER> Step 7 : ADD spring-boot-loader.tar /
[INFO] DOCKER> ---> f593e7c3c58d
[INFO] DOCKER> Step 7/10 : ADD ["snapshot-dependencies.tar", "/"]
[INFO] DOCKER>
[INFO] DOCKER> ---> Using cache
[INFO] DOCKER> ---> 2de1bf3ac9ad
[INFO] DOCKER> ---> ef1f89b783b5
[INFO] DOCKER> Step 8/10 : ADD ["application.tar", "/"]
[INFO] DOCKER>
[INFO] DOCKER> ---> Using cache
[INFO] DOCKER> ---> 055ba36bb45f
...
```
2 changes: 1 addition & 1 deletion samples/build-cache/app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<properties>
<!-- Avoid docker build cache invalidation due to new commits -->
<project.build.outputTimestamp>1970-01-01T00:00:00Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2000-01-01T00:00:00Z</project.build.outputTimestamp>
</properties>

<dependencies>
Expand Down
2 changes: 1 addition & 1 deletion samples/build-cache/image/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

<image>fabric8/dmp-sample-build-cache-app:${project.version}</image>
<!-- Avoid docker build cache invalidation due to new commits -->
<project.build.outputTimestamp>1970-01-01T00:00:00Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2000-01-01T00:00:00Z</project.build.outputTimestamp>
<tini.sha512>8053cc21a3a9bdd6042a495349d1856ae8d3b3e7664c9654198de0087af031f5d41139ec85a2f5d7d2febd22ec3f280767ff23b9d5f63d490584e2b7ad3c218c</tini.sha512>

<tini.version>0.19.0</tini.version>
Expand Down
10 changes: 5 additions & 5 deletions samples/build-cache/image/src/main/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ FROM gcr.io/distroless/java-debian10
# Just for security
USER nonroot

# Entry point of the base image executes java which we don't need because we use tini in the image command.
# tini (https://github.com/krallin/tini) is used to enforce zero exit code when Java application is stopped
# using SIGINT or SIGTERM.
ENTRYPOINT []

# /app directory contains all items of the wanted Java class path.
WORKDIR "/app"

Expand All @@ -32,6 +27,11 @@ ADD ["snapshot-dependencies.tar", "/"]
# ... application code
ADD ["application.tar", "/"]

# Entry point of the base image executes java which we don't need because we use tini in the image command.
# tini (https://github.com/krallin/tini) is used to enforce zero exit code when Java application is stopped
# using SIGINT or SIGTERM.
ENTRYPOINT []

# Put version into the last layer because LABEL directive produces more lightweight layer than ADD.
LABEL name="@{image}" \
version="@{project.version}"
2 changes: 1 addition & 1 deletion samples/build-cache/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<git-commit-id-plugin.version>4.0.3</git-commit-id-plugin.version>

<git.commit.dateFormat>yyyy-MM-dd'T'HH:mm:ssXXX</git.commit.dateFormat>
<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
<maven-assembly-plugin.version>3.5.0</maven-assembly-plugin.version>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
Expand Down
2 changes: 1 addition & 1 deletion samples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
</modules>

<properties>
<latest-released-version>0.45.0</latest-released-version>
<latest-released-version>0.45.1</latest-released-version>
</properties>

<build>
Expand Down
2 changes: 1 addition & 1 deletion src/main/asciidoc/inc/external/_docker_compose.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ In addition to the `docker-compose.yml` you can add all known options for <<buil

The following Docker Compose file keywords are not yet supported:

* `cgroup_parent`, `devices`, `env_file`, `expose`, `pid`, `security_opt`, `stop_signal`, `cpu_quota`, `ipc`, `mac_address`, `read_only`, `healthcheck` are not yet supported (but might be in a future version).
* `cgroup_parent`, `devices`, `env_file`, `expose`, `pid`, `security_opt`, `stop_signal`, `cpu_quota`, `ipc`, `mac_address`, `read_only` are not yet supported (but might be in a future version).
* `extend` for including other Docker Compose files is not yet implemented.
* Only **services** are currently evaluated, there is no supported yet for **volumes** and **networks**.
* When using https://docs.docker.com/compose/compose-file/compose-file-v2/#depends_on[`depends_on` with long syntax] in a Docker Compose file, be advised the plugin cannot apply all usage constellations expressible in it. The root cause is this plugin uses the concept of pausing execution based on <<start-wait,wait conditions>> attached to dependent containers, while Docker Compose applies checks when starting the depending container. Keep in mind that execution of a container is continued as soon as any wait condition is fulfilled.
6 changes: 6 additions & 0 deletions src/main/asciidoc/inc/start/_configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ In addition to the <<global-configuration>>, this goal supports the following gl
| Starts docker images in parallel while dependencies expressed as <<start-links,Link>> or <<start-depends-on,dependsOn>> are respected. This option can significantly reduce the startup time because independent containers do not need to wait for each other.
| `docker.startParallel`

| *startContainerWaitTimeout*
| Overrides the default across all the containers wait timeout (<wait><time>) is milliseconds.
Overriding that property might become particularly useful when docker-compose config defines the healthchecks,
but the default wait timeout of 10000ms is too short for some containers to become healthy.
| `docker.startContainerWaitTimeout`

|===

The `<run>` configuration element knows the following sub elements:
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/io/fabric8/maven/docker/StartMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ public class StartMojo extends AbstractDockerMojo {
@Parameter(property = "docker.autoCreateCustomNetworks", defaultValue = "false")
protected boolean autoCreateCustomNetworks;

public static final String DOCKER_START_CONTAINER_WAIT_TIMEOUT = "docker.startContainerWaitTimeout";

/**
* Overrides the default across all the containers wait time is milliseconds.
* Overriding that property might become particularly useful when docker-compose config defines
* the healthchecks, but the default wait timeout {@link io.fabric8.maven.docker.wait.WaitUtil#DEFAULT_MAX_WAIT}
* is too short for some containers to become healthy.
*/
@Parameter(property = DOCKER_START_CONTAINER_WAIT_TIMEOUT, defaultValue = "10000")
protected int startContainerWaitTimeout = 10000;

// property file to write out with port mappings
@Parameter
protected String portPropertyFile;
Expand Down Expand Up @@ -274,6 +285,7 @@ private void startImage(final ImageConfiguration imageConfig,
final Properties projProperties = project.getProperties();
final RunImageConfiguration runConfig = imageConfig.getRunConfiguration();
final PortMapping portMapping = runService.createPortMapping(runConfig, projProperties);
projProperties.computeIfAbsent(DOCKER_START_CONTAINER_WAIT_TIMEOUT, key -> String.valueOf(startContainerWaitTimeout));
final LogDispatcher dispatcher = getLogDispatcher(hub);

StartContainerExecutor startExecutor = new StartContainerExecutor.Builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import com.google.gson.JsonPrimitive;
import io.fabric8.maven.docker.config.HealthCheckConfiguration;
import io.fabric8.maven.docker.config.HealthCheckMode;
import org.apache.commons.text.StrSubstitutor;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
Expand All @@ -17,6 +21,8 @@
import io.fabric8.maven.docker.config.Arguments;
import io.fabric8.maven.docker.util.JsonFactory;

import static io.fabric8.maven.docker.access.util.ComposeDurationUtil.goDurationToNanoseconds;

public class ContainerCreateConfig {

private final JsonObject createConfig = new JsonObject();
Expand Down Expand Up @@ -72,7 +78,7 @@ public ContainerCreateConfig environment(String envPropsFile, Map<String, String
String value = entry.getValue();
if (value == null) {
value = "";
} else if(value.matches("^\\+\\$\\{.*}$")) {
} else if (value.matches("^\\+\\$\\{.*}$")) {
/*
* This case is to handle the Maven interpolation issue which used
* to occur when using ${..} only without any suffix.
Expand All @@ -93,7 +99,7 @@ public ContainerCreateConfig environment(String envPropsFile, Map<String, String
return this;
}

public ContainerCreateConfig labels(Map<String,String> labels) {
public ContainerCreateConfig labels(Map<String, String> labels) {
if (labels != null && labels.size() > 0) {
createConfig.add("Labels", JsonFactory.newJsonObject(labels));
}
Expand All @@ -111,6 +117,42 @@ public ContainerCreateConfig exposedPorts(Set<String> portSpecs) {
return this;
}

public ContainerCreateConfig healthcheck(HealthCheckConfiguration healthCheckConfiguration) {
if (healthCheckConfiguration == null) {
return this;
}
JsonObject healthcheck = new JsonObject();
if (healthCheckConfiguration.getMode() == HealthCheckMode.none) {
healthcheck.add("Test", JsonFactory.newJsonArray(Collections.singletonList("NONE")));
createConfig.add("Healthcheck", healthcheck);
return this;
}

healthcheck.add("Test", JsonFactory.newJsonArray(healthCheckConfiguration.getCmd().asStrings()));

if (healthCheckConfiguration.getRetries() != null) {
healthcheck.add("Retries", new JsonPrimitive(healthCheckConfiguration.getRetries()));
}
if (healthCheckConfiguration.getInterval() != null) {
String intervalValue = healthCheckConfiguration.getInterval();
String field = "Interval";
healthcheck.add(field, new JsonPrimitive(goDurationToNanoseconds(intervalValue, field)));
}
if (healthCheckConfiguration.getStartPeriod() != null) {
String field = "StartPeriod";
String intervalValue = healthCheckConfiguration.getStartPeriod();
healthcheck.add(field, new JsonPrimitive(goDurationToNanoseconds(intervalValue, field)));
}
if (healthCheckConfiguration.getTimeout() != null) {
String field = "Timeout";
String intervalValue = healthCheckConfiguration.getTimeout();
healthcheck.add(field, new JsonPrimitive(goDurationToNanoseconds(intervalValue, field)));
}

createConfig.add("Healthcheck", healthcheck);
return this;
}

public String getImageName() {
return imageName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.fabric8.maven.docker.access.util;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Objects.requireNonNull;

/**
* Partial implementation of the patterns from https://pkg.go.dev/maze.io/x/duration
* This implementation doesn't support combinations of timeunits.
*/
public class ComposeDurationUtil {

private ComposeDurationUtil() {
}

private static final Pattern SIMPLE_GO_DURATION_FORMAT = Pattern.compile("^([\\d]+)(ns|us|ms|s|m|h|d|w|y)?$");
private static final Map<String, TimeUnit> GO_TYPES_TO_JAVA = new HashMap<>();

static {
GO_TYPES_TO_JAVA.put("ns", TimeUnit.NANOSECONDS);
GO_TYPES_TO_JAVA.put("us", TimeUnit.MICROSECONDS);
GO_TYPES_TO_JAVA.put("ms", TimeUnit.MILLISECONDS);
GO_TYPES_TO_JAVA.put("s", TimeUnit.SECONDS);
GO_TYPES_TO_JAVA.put("m", TimeUnit.MINUTES);
GO_TYPES_TO_JAVA.put("h", TimeUnit.HOURS);
GO_TYPES_TO_JAVA.put("d", TimeUnit.DAYS);
}


public static long goDurationToNanoseconds(String goDuration, String field) {
requireNonNull(goDuration);

Matcher matcher = SIMPLE_GO_DURATION_FORMAT.matcher(goDuration);
if (!matcher.matches()) {
String message = String.format("Unsupported duration value \"%s\" for the field \"%s\"", goDuration, field);
throw new IllegalArgumentException(message);
}
long duration = Long.parseLong(matcher.group(1));
if (matcher.groupCount() == 2 && matcher.group(2) != null) {
String type = matcher.group(2);

if (GO_TYPES_TO_JAVA.containsKey(type)) {
duration = TimeUnit.NANOSECONDS.convert(duration, GO_TYPES_TO_JAVA.get(type));
} else if ("w".equals(type)) {
duration = 7 * TimeUnit.NANOSECONDS.convert(duration, TimeUnit.DAYS);
} else if ("y".equals(type)) {
duration = 365 * TimeUnit.NANOSECONDS.convert(duration, TimeUnit.DAYS);
} else {
throw new IllegalArgumentException("Unsupported time unit: " + type);
}
}

return duration;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public class RunImageConfiguration implements Serializable {
@Parameter
private List<String> dependsOn;

// healthcheck
@Parameter
private HealthCheckConfiguration healthCheckConfiguration;

/**
* container entry point
*
Expand Down Expand Up @@ -264,6 +268,10 @@ public List<String> getDependsOn() {
return EnvUtil.splitAtCommasAndTrim(dependsOn);
}

public HealthCheckConfiguration getHealthCheckConfiguration() {
return healthCheckConfiguration;
}

public String getUser() {
return user;
}
Expand Down Expand Up @@ -582,6 +590,11 @@ public Builder dependsOn(List<String> dependsOn) {
return this;
}

public Builder healthcheck(HealthCheckConfiguration healthCheckConfiguration) {
config.healthCheckConfiguration = healthCheckConfiguration;
return this;
}

public Builder dns(List<String> dns) {
config.dns = dns;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ private RunImageConfiguration createRunConfiguration(DockerComposeServiceWrapper
// container_name is taken as an alias and ignored here for run config
// devices not supported
.dependsOn(wrapper.getDependsOn()) // depends_on relies that no container_name is set
.healthcheck(wrapper.getHealthCheckConfiguration())
.wait(wrapper.getWaitConfiguration())
.dns(wrapper.getDns())
.dnsSearch(wrapper.getDnsSearch())
Expand Down
Loading

0 comments on commit 7d225cf

Please sign in to comment.