diff --git a/src/main/java/io/fabric8/maven/docker/access/ContainerCreateConfig.java b/src/main/java/io/fabric8/maven/docker/access/ContainerCreateConfig.java
index 1b8545b8f..e22aadc26 100644
--- a/src/main/java/io/fabric8/maven/docker/access/ContainerCreateConfig.java
+++ b/src/main/java/io/fabric8/maven/docker/access/ContainerCreateConfig.java
@@ -138,6 +138,10 @@ public ContainerCreateConfig hostConfig(ContainerHostConfig startConfig) {
public ContainerCreateConfig networkingConfig(ContainerNetworkingConfig networkingConfig) {
return add("NetworkingConfig", networkingConfig.toJsonObject());
}
+
+ public ContainerCreateConfig healthCheck(ContainerHealthCheckConfig healthCheckConfig) {
+ return add("Healthcheck", healthCheckConfig.toJsonObject());
+ }
/**
* Get JSON which is used for creating a container
diff --git a/src/main/java/io/fabric8/maven/docker/access/ContainerHealthCheckConfig.java b/src/main/java/io/fabric8/maven/docker/access/ContainerHealthCheckConfig.java
new file mode 100644
index 000000000..933a69793
--- /dev/null
+++ b/src/main/java/io/fabric8/maven/docker/access/ContainerHealthCheckConfig.java
@@ -0,0 +1,54 @@
+package io.fabric8.maven.docker.access;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import io.fabric8.maven.docker.config.HealthCheckConfiguration;
+import io.fabric8.maven.docker.config.HealthCheckConfiguration.DurationParser;
+
+public class ContainerHealthCheckConfig {
+
+ private final JsonObject healthcheck = new JsonObject();
+
+ public ContainerHealthCheckConfig(HealthCheckConfiguration configuration) {
+ JsonArray test = new JsonArray();
+ switch (configuration.getMode()) {
+ case none:
+ test.add("NONE");
+ break;
+ case cmd:
+ test.add("CMD");
+ for (String arg : configuration.getCmd().asStrings()) {
+ test.add(arg);
+ }
+ break;
+ case shell:
+ test.add("CMD-SHELL");
+ test.add(configuration.getCmd().getShell());
+ break;
+ case inherit:
+ // case inherit can be ignored here - equivalent to an empty JSON array
+ }
+ this.healthcheck.add("Test", test);
+
+ if (configuration.getInterval() != null) {
+ this.healthcheck.addProperty("Interval", DurationParser.parseDuration(configuration.getInterval()).toNanos());
+ }
+ if (configuration.getTimeout() != null) {
+ this.healthcheck.addProperty("Timeout", DurationParser.parseDuration(configuration.getTimeout()).toNanos());
+ }
+ if (configuration.getStartPeriod() != null) {
+ this.healthcheck.addProperty("StartPeriod", DurationParser.parseDuration(configuration.getStartPeriod()).toNanos());
+ }
+ if (configuration.getRetries() != null) {
+ this.healthcheck.addProperty("Retries", configuration.getRetries());
+ }
+ }
+
+ public String toJson() {
+ return healthcheck.toString();
+ }
+
+ public JsonObject toJsonObject() {
+ return healthcheck;
+ }
+}
diff --git a/src/main/java/io/fabric8/maven/docker/assembly/DockerFileBuilder.java b/src/main/java/io/fabric8/maven/docker/assembly/DockerFileBuilder.java
index 1560ccac5..4d9255199 100644
--- a/src/main/java/io/fabric8/maven/docker/assembly/DockerFileBuilder.java
+++ b/src/main/java/io/fabric8/maven/docker/assembly/DockerFileBuilder.java
@@ -8,6 +8,7 @@
import com.google.common.base.Joiner;
import io.fabric8.maven.docker.config.Arguments;
+import io.fabric8.maven.docker.config.BuildImageConfiguration;
import io.fabric8.maven.docker.config.HealthCheckConfiguration;
import org.codehaus.plexus.util.FileUtils;
@@ -134,20 +135,24 @@ private void addUser(StringBuilder b) {
private void addHealthCheck(StringBuilder b) {
if (healthCheck != null) {
StringBuilder healthString = new StringBuilder();
-
+
+ // Context is image building, thus default to Dockerfile CMD mode (unequal to runtime version!)
+ // Note: usually done via BuildImageConfiguration.initAndValidate(), but not with low-level unit tests.
+ healthCheck.setModeIfNotPresent(BuildImageConfiguration.HC_BUILDTIME_DEFAULT);
+
switch (healthCheck.getMode()) {
- case cmd:
- buildOption(healthString, DockerFileOption.HEALTHCHECK_INTERVAL, healthCheck.getInterval());
- buildOption(healthString, DockerFileOption.HEALTHCHECK_TIMEOUT, healthCheck.getTimeout());
- buildOption(healthString, DockerFileOption.HEALTHCHECK_START_PERIOD, healthCheck.getStartPeriod());
- buildOption(healthString, DockerFileOption.HEALTHCHECK_RETRIES, healthCheck.getRetries());
- buildArguments(healthString, DockerFileKeyword.CMD, false, healthCheck.getCmd());
- break;
- case none:
- DockerFileKeyword.NONE.addTo(healthString, false);
- break;
- default:
- throw new IllegalArgumentException("Unsupported health check mode: " + healthCheck.getMode());
+ case cmd:
+ buildOption(healthString, DockerFileOption.HEALTHCHECK_INTERVAL, healthCheck.getInterval());
+ buildOption(healthString, DockerFileOption.HEALTHCHECK_TIMEOUT, healthCheck.getTimeout());
+ buildOption(healthString, DockerFileOption.HEALTHCHECK_START_PERIOD, healthCheck.getStartPeriod());
+ buildOption(healthString, DockerFileOption.HEALTHCHECK_RETRIES, healthCheck.getRetries());
+ buildArguments(healthString, DockerFileKeyword.CMD, false, healthCheck.getCmd());
+ break;
+ case none:
+ DockerFileKeyword.NONE.addTo(healthString, false);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported build time health check mode: " + healthCheck.getMode() + " - use 'cmd' or 'none'");
}
DockerFileKeyword.HEALTHCHECK.addTo(b, healthString.toString());
diff --git a/src/main/java/io/fabric8/maven/docker/config/BuildImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/BuildImageConfiguration.java
index 7c378851a..598c3a86f 100644
--- a/src/main/java/io/fabric8/maven/docker/config/BuildImageConfiguration.java
+++ b/src/main/java/io/fabric8/maven/docker/config/BuildImageConfiguration.java
@@ -753,6 +753,8 @@ public BuildImageConfiguration build() {
return config;
}
}
+
+ public static final HealthCheckMode HC_BUILDTIME_DEFAULT = HealthCheckMode.cmd;
public String initAndValidate(Logger log) throws IllegalArgumentException {
if (entryPoint != null) {
@@ -762,6 +764,8 @@ public String initAndValidate(Logger log) throws IllegalArgumentException {
cmd.validate();
}
if (healthCheck != null) {
+ // Context is image building, thus default to Dockerfile CMD mode (unequal to runtime version!)
+ healthCheck.setModeIfNotPresent(HC_BUILDTIME_DEFAULT);
healthCheck.validate();
}
diff --git a/src/main/java/io/fabric8/maven/docker/config/ConfigHelper.java b/src/main/java/io/fabric8/maven/docker/config/ConfigHelper.java
index df30b9c87..ed67c2630 100644
--- a/src/main/java/io/fabric8/maven/docker/config/ConfigHelper.java
+++ b/src/main/java/io/fabric8/maven/docker/config/ConfigHelper.java
@@ -117,12 +117,20 @@ public static String getExternalConfigActivationProperty(MavenProject project) {
* @param nameFormatter formatter for image names
* @param log a logger for printing out diagnostic messages
* @return the minimal API Docker API required to be used for the given configuration.
+ * @throws IllegalArgumentException When an image validation fails, the thrown exception will contain
+ * the image's name as its message and wrap the underlying exception as cause.
*/
public static String initAndValidate(List images, String apiVersion, NameFormatter nameFormatter,
Logger log) {
// Init and validate configs. After this step, getResolvedImages() contains the valid configuration.
for (ImageConfiguration imageConfiguration : images) {
- apiVersion = EnvUtil.extractLargerVersion(apiVersion, imageConfiguration.initAndValidate(nameFormatter, log));
+ try {
+ apiVersion = EnvUtil.extractLargerVersion(apiVersion, imageConfiguration.initAndValidate(nameFormatter, log));
+ } catch (IllegalArgumentException e) {
+ // Wrap the underlying validation and add the image's name/alias.
+ // Maven will properly unpack the wrapped exception.
+ throw new IllegalArgumentException(imageConfiguration.getName(), e);
+ }
}
return apiVersion;
}
diff --git a/src/main/java/io/fabric8/maven/docker/config/HealthCheckConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/HealthCheckConfiguration.java
index 7cd8154d1..358e6b8bf 100644
--- a/src/main/java/io/fabric8/maven/docker/config/HealthCheckConfiguration.java
+++ b/src/main/java/io/fabric8/maven/docker/config/HealthCheckConfiguration.java
@@ -1,13 +1,19 @@
package io.fabric8.maven.docker.config;
+import org.apache.commons.lang3.StringUtils;
+
import java.io.Serializable;
+import java.time.Duration;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Build configuration for health checks.
*/
public class HealthCheckConfiguration implements Serializable {
- private HealthCheckMode mode = HealthCheckMode.cmd;
+ // Default values are applied differently in build or runtime context, no default here
+ private HealthCheckMode mode;
private String interval;
@@ -19,6 +25,7 @@ public class HealthCheckConfiguration implements Serializable {
private Arguments cmd;
+ // This constructor must remain "public" as this class is deserialized from XML config
public HealthCheckConfiguration() {}
public String getInterval() {
@@ -48,7 +55,19 @@ public Arguments getCmd() {
public HealthCheckMode getMode() {
return mode;
}
-
+
+ /**
+ * Use this method to apply a default mode depending on context (build or runtime)
+ * @param mode The default mode to set
+ * @return The configuration, making the call chainable
+ */
+ public HealthCheckConfiguration setModeIfNotPresent(HealthCheckMode mode) {
+ if (this.mode == null) {
+ this.mode = mode;
+ }
+ return this;
+ }
+
public Integer getRetries() {
return retries;
}
@@ -59,23 +78,59 @@ public void validate() throws IllegalArgumentException {
}
switch(mode) {
- case none:
- if (interval != null || timeout != null || startPeriod != null || retries != null || cmd != null) {
- throw new IllegalArgumentException("HealthCheck: no parameters are allowed when the health check mode is set to 'none'");
- }
- break;
- case cmd:
- if (cmd == null) {
- throw new IllegalArgumentException("HealthCheck: the parameter 'cmd' is mandatory when the health check mode is set to 'cmd' (default)");
- }
+ case none:
+ if (interval != null || timeout != null || startPeriod != null || retries != null || cmd != null) {
+ throw new IllegalArgumentException("HealthCheck: no parameters are allowed when the health check mode is set to 'none'");
+ }
+ break;
+ case cmd:
+ case shell:
+ if (cmd == null) {
+ throw new IllegalArgumentException("HealthCheck: parameter 'cmd' is mandatory for mode set to 'cmd' (default for builds) or 'shell'");
+ }
+ // cmd.getExec() == null can be ignored here - we will simply parse the string into arguments
+ if (mode == HealthCheckMode.shell && cmd.getShell() == null) {
+ throw new IllegalArgumentException("HealthCheck: parameter 'cmd' for mode 'shell' must be given as one string, not arguments");
+ }
+ // Now fallthrough to mode inherit (which has needs the same validations for options, but not the test)
+ case inherit:
+ if (retries != null && retries < 0) {
+ throw new IllegalArgumentException("HealthCheck: the parameter 'retries' may not be negative");
+ }
+ if (interval != null && ! DurationParser.matchesDuration(interval)) {
+ throw new IllegalArgumentException("HealthCheck: illegal duration specified for interval");
+ }
+ if (timeout != null && ! DurationParser.matchesDuration(timeout)) {
+ throw new IllegalArgumentException("HealthCheck: illegal duration specified for timeout");
+ }
+ if (startPeriod != null && ! DurationParser.matchesDuration(startPeriod)) {
+ throw new IllegalArgumentException("HealthCheck: illegal duration specified for start period");
+ }
+ // Must limit check to inherit *again* because shell and cmd fall through to this case!
+ if (mode == HealthCheckMode.inherit && cmd != null) {
+ throw new IllegalArgumentException("HealthCheck: parameter 'cmd' not allowed for mode set to 'inherit'");
+ }
+ break;
}
}
-
+
+ @Override
+ public String toString() {
+ return "HealthCheckConfiguration{" +
+ "mode=" + mode +
+ ", interval='" + interval + '\'' +
+ ", timeout='" + timeout + '\'' +
+ ", startPeriod='" + startPeriod + '\'' +
+ ", retries=" + retries +
+ ", cmd=" + cmd +
+ '}';
+ }
+
// ===========================================
public static class Builder {
- private HealthCheckConfiguration config = new HealthCheckConfiguration();
+ private final HealthCheckConfiguration config;
public Builder() {
this.config = new HealthCheckConfiguration();
@@ -109,7 +164,7 @@ public Builder retries(Integer retries) {
}
public Builder mode(String mode) {
- return this.mode(mode != null ? HealthCheckMode.valueOf(mode) : (HealthCheckMode) null);
+ return this.mode(mode != null ? HealthCheckMode.valueOf(mode) : null);
}
public Builder mode(HealthCheckMode mode) {
@@ -121,4 +176,77 @@ public HealthCheckConfiguration build() {
return config;
}
}
+
+ public static final class DurationParser {
+
+ // No instances allowed
+ private DurationParser() {}
+
+ /**
+ * This complex regex allows duration in the special Docker format,
+ * which is not ISO-8601 compatible, and thus not parseable directly.
+ * (For example, it does not allow using days or even longer periods)
+ * @implSpec See Docker Compose durations for supported duration formats.
+ * Dockerfile HEALTHCHECK has only very limited specification about allowed duration formatting.
+ * @implNote Note that the Docker API requires nanosecond precision (int64/long).
+ * A conversion is easily done using {@link Duration#toNanos()}.
+ * Examples of allowed values: 23h17m1s, 10ms, 1s, 0h10ms, 1h2m1.3432s
+ */
+ @SuppressWarnings("java:S5843")
+ private static final String DURATION_REGEX = "^((?0\\d|1\\d|2[0-3]|\\d)h)?((?[0-5]?\\d)m)?(((?[0-5]?\\d)s)?((?\\d{1,3})ms)?((?\\d{1,3})us)?|(?[0-5]?\\d)\\.(?\\d{1,9})s)$";
+ private static final Matcher durationMatcher = Pattern.compile(DURATION_REGEX).matcher("");
+
+ public static boolean matchesDuration(String durationString) {
+ if (durationString == null || durationString.isEmpty()) {
+ return false;
+ }
+ return durationMatcher.reset(durationString).matches() || durationString.equals("0");
+ }
+
+ public static Duration parseDuration(String durationString) {
+ if (durationString == null || durationString.isEmpty()) {
+ return null;
+ }
+
+ if (durationString.equals("0")) {
+ return Duration.ZERO;
+ }
+
+ if (durationMatcher.reset(durationString).matches()) {
+ Duration duration = Duration.ZERO;
+ // Add hours
+ if (durationMatcher.group("hours") != null) {
+ duration = duration.plusHours(Long.parseLong(durationMatcher.group("hours")));
+ }
+ // Add minutes
+ if (durationMatcher.group("mins") != null) {
+ duration = duration.plusMinutes(Long.parseLong(durationMatcher.group("mins")));
+ }
+ // When seconds are given as an (optional) fraction
+ if (durationMatcher.group("fsecs") != null) {
+ duration = duration.plusSeconds(Long.parseLong(durationMatcher.group("fsecs")));
+
+ String fraction = durationMatcher.group("fraction");
+ // Append enough zeros to make it nanosecond precision, then tune the duration
+ fraction += StringUtils.repeat("0", 9 - fraction.length());
+ duration = duration.plusNanos(Long.parseLong(fraction));
+ } else {
+ // Add seconds
+ if (durationMatcher.group("secs") != null) {
+ duration = duration.plusSeconds(Long.parseLong(durationMatcher.group("secs")));
+ }
+ // Add milliseconds
+ if (durationMatcher.group("msecs") != null) {
+ duration = duration.plusMillis(Long.parseLong(durationMatcher.group("msecs")));
+ }
+ // Add microseconds (make them fake nanoseconds first, as Duration does not support adding micros)
+ if (durationMatcher.group("usecs") != null) {
+ duration = duration.plusNanos(Long.parseLong(durationMatcher.group("usecs") + "000"));
+ }
+ }
+ return duration;
+ }
+ return null;
+ }
+ }
}
diff --git a/src/main/java/io/fabric8/maven/docker/config/HealthCheckMode.java b/src/main/java/io/fabric8/maven/docker/config/HealthCheckMode.java
index cce8a4b17..c7278caa5 100644
--- a/src/main/java/io/fabric8/maven/docker/config/HealthCheckMode.java
+++ b/src/main/java/io/fabric8/maven/docker/config/HealthCheckMode.java
@@ -1,16 +1,30 @@
package io.fabric8.maven.docker.config;
+@SuppressWarnings("java:S115")
public enum HealthCheckMode {
/**
* Mainly used to disable any health check provided by the base image.
+ * This mode is supported at build and run time.
*/
none,
/**
- * A command based health check.
+ * A command-based health check.
+ * This mode is supported at build and run time.
*/
- cmd;
+ cmd,
+
+ /**
+ * A shell-wrapped command-based health check.
+ * This mode is supported at runtime only.
+ */
+ shell,
+
+ /**
+ * Runtime-only mode, used to change options, but not the test itself
+ */
+ inherit
}
diff --git a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java
index 78cbb68c3..6630549a3 100644
--- a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java
+++ b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java
@@ -197,7 +197,7 @@ public String toString() {
return String.format("ImageConfiguration {name='%s', alias='%s'}", name, alias);
}
- public String initAndValidate(ConfigHelper.NameFormatter nameFormatter, Logger log) {
+ public String initAndValidate(ConfigHelper.NameFormatter nameFormatter, Logger log) throws IllegalArgumentException {
name = nameFormatter.format(name);
String minimalApiVersion = null;
if (build != null) {
diff --git a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java
index cfed39a6c..3293b4b20 100644
--- a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java
+++ b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java
@@ -208,6 +208,12 @@ public class RunImageConfiguration implements Serializable {
// How to stop a container
@Parameter
private StopMode stopMode;
+
+ /**
+ * Add new or override build time provided healthcheck
+ */
+ @Parameter
+ private HealthCheckConfiguration healthCheck;
public RunImageConfiguration() {
}
@@ -222,14 +228,27 @@ public String initAndValidate() {
if (cmd != null) {
cmd.validate();
}
+ if (healthCheck != null) {
+ // Context is running an image, thus default to inheriting a check defined at build time or parent image(s)
+ // (Which still allows to change any option while keeping the test itself!)
+ healthCheck.setModeIfNotPresent(HealthCheckMode.inherit);
+ healthCheck.validate();
+ }
+
+ String minimalApiVersion = null;
// Custom networks are available since API 1.21 (Docker 1.9)
NetworkConfig config = getNetworkingConfig();
if (config != null && config.isCustomNetwork()) {
- return "1.21";
+ minimalApiVersion = "1.21";
+ }
+
+ // Runtime provided healthchecks are available since API 1.24 (Docker 1.12)
+ if (healthCheck != null) {
+ minimalApiVersion = "1.24";
}
- return null;
+ return minimalApiVersion;
}
public Map getEnv() {
@@ -447,6 +466,10 @@ public StopMode getStopMode() {
}
return stopMode;
}
+
+ public HealthCheckConfiguration getHealthCheck() {
+ return this.healthCheck;
+ }
/**
* @deprecated use {@link #getContainerNamePattern} instead
@@ -725,6 +748,11 @@ public Builder autoRemove(Boolean autoRemove) {
config.autoRemove = autoRemove;
return this;
}
+
+ public Builder healthCheck(HealthCheckConfiguration configuration) {
+ config.healthCheck = configuration;
+ return this;
+ }
public RunImageConfiguration build() {
return config;
diff --git a/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java b/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java
index 366e8f5e6..3a1d02d40 100644
--- a/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java
+++ b/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java
@@ -314,7 +314,8 @@ private HealthCheckConfiguration extractHealthCheck(HealthCheckConfiguration con
return config;
}
if (config == null) {
- config = new HealthCheckConfiguration();
+ // Create empty config to allow null safe fallback value requests during extraction
+ config = new HealthCheckConfiguration.Builder().build();
}
return new HealthCheckConfiguration.Builder()
diff --git a/src/main/java/io/fabric8/maven/docker/service/RunService.java b/src/main/java/io/fabric8/maven/docker/service/RunService.java
index cc6cd3d84..2d4a7e305 100644
--- a/src/main/java/io/fabric8/maven/docker/service/RunService.java
+++ b/src/main/java/io/fabric8/maven/docker/service/RunService.java
@@ -36,6 +36,7 @@
import java.util.concurrent.ExecutionException;
import io.fabric8.maven.docker.access.ContainerCreateConfig;
+import io.fabric8.maven.docker.access.ContainerHealthCheckConfig;
import io.fabric8.maven.docker.access.ContainerHostConfig;
import io.fabric8.maven.docker.access.ContainerNetworkingConfig;
import io.fabric8.maven.docker.access.DockerAccess;
@@ -44,6 +45,7 @@
import io.fabric8.maven.docker.access.NetworkCreateConfig;
import io.fabric8.maven.docker.access.PortMapping;
import io.fabric8.maven.docker.config.Arguments;
+import io.fabric8.maven.docker.config.HealthCheckConfiguration;
import io.fabric8.maven.docker.config.ImageConfiguration;
import io.fabric8.maven.docker.config.NetworkConfig;
import io.fabric8.maven.docker.config.RestartPolicy;
@@ -390,6 +392,11 @@ ContainerCreateConfig createContainerConfig(String imageName, RunImageConfigurat
.aliases(networkConfig);
config.networkingConfig(networkingConfig);
}
+
+ HealthCheckConfiguration healthCheckConfiguration = runConfig.getHealthCheck();
+ if (healthCheckConfiguration != null) {
+ config.healthCheck(new ContainerHealthCheckConfig(healthCheckConfiguration));
+ }
return config;
} catch (IllegalArgumentException e) {
diff --git a/src/test/java/io/fabric8/maven/docker/access/ContainerHealthCheckConfigTest.java b/src/test/java/io/fabric8/maven/docker/access/ContainerHealthCheckConfigTest.java
new file mode 100644
index 000000000..baa9056b9
--- /dev/null
+++ b/src/test/java/io/fabric8/maven/docker/access/ContainerHealthCheckConfigTest.java
@@ -0,0 +1,72 @@
+package io.fabric8.maven.docker.access;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import io.fabric8.maven.docker.config.Arguments;
+import io.fabric8.maven.docker.config.HealthCheckConfiguration;
+import io.fabric8.maven.docker.config.HealthCheckMode;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ContainerHealthCheckConfigTest {
+
+ @Test
+ void testNanoSecondTransition() {
+ HealthCheckConfiguration hcc = new HealthCheckConfiguration.Builder()
+ .mode(HealthCheckMode.cmd)
+ .cmd(Arguments.Builder.get().withShell("test").build())
+ .timeout("3s")
+ .interval("2s")
+ .startPeriod("1s")
+ .build();
+
+ ContainerHealthCheckConfig chcc = new ContainerHealthCheckConfig(hcc);
+ JsonObject sut = chcc.toJsonObject();
+
+ Assertions.assertEquals(3000000000L, sut.get("Timeout").getAsLong());
+ Assertions.assertEquals(2000000000L, sut.get("Interval").getAsLong());
+ Assertions.assertEquals(1000000000L, sut.get("StartPeriod").getAsLong());
+ }
+
+
+ // Zero as duration option means "inherit" the options from the image / base image options
+ // See also https://docs.docker.com/engine/api/latest/#tag/Container/operation/ContainerCreate
+ @Test
+ void testZeroAsDuration() {
+ HealthCheckConfiguration hcc = new HealthCheckConfiguration.Builder()
+ .mode(HealthCheckMode.cmd)
+ .cmd(Arguments.Builder.get().withShell("test").build())
+ .timeout("0")
+ .interval("0")
+ .startPeriod("0")
+ .build();
+
+ ContainerHealthCheckConfig chcc = new ContainerHealthCheckConfig(hcc);
+ JsonObject sut = chcc.toJsonObject();
+
+ Assertions.assertEquals(0, sut.get("Timeout").getAsLong());
+ Assertions.assertEquals(0, sut.get("Interval").getAsLong());
+ Assertions.assertEquals(0, sut.get("StartPeriod").getAsLong());
+ }
+
+ @Test
+ void testCmdSplitting() {
+ HealthCheckConfiguration hcc = new HealthCheckConfiguration.Builder()
+ .mode(HealthCheckMode.cmd)
+ .cmd(Arguments.Builder.get().withShell("test -f /bin/bash").build())
+ .build();
+
+ ContainerHealthCheckConfig chcc = new ContainerHealthCheckConfig(hcc);
+ JsonObject sut = chcc.toJsonObject();
+
+ JsonArray expected = new JsonArray();
+ expected.add("CMD");
+ expected.add("test");
+ expected.add("-f");
+ expected.add("/bin/bash");
+
+ Assertions.assertEquals(expected, sut.get("Test").getAsJsonArray());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/io/fabric8/maven/docker/assembly/DockerFileBuilderTest.java b/src/test/java/io/fabric8/maven/docker/assembly/DockerFileBuilderTest.java
index 615a4d5a1..f067338c6 100644
--- a/src/test/java/io/fabric8/maven/docker/assembly/DockerFileBuilderTest.java
+++ b/src/test/java/io/fabric8/maven/docker/assembly/DockerFileBuilderTest.java
@@ -195,6 +195,13 @@ void testHealthCheckNone() {
String dockerfileContent = new DockerFileBuilder().healthCheck(hc).content();
Assertions.assertEquals("NONE", dockerfileToMap(dockerfileContent).get("HEALTHCHECK"));
}
+
+ @Test
+ void testHealthCheckShell() {
+ HealthCheckConfiguration hc = new HealthCheckConfiguration.Builder().mode(HealthCheckMode.shell).build();
+ DockerFileBuilder dfb = new DockerFileBuilder().healthCheck(hc);
+ Assertions.assertThrows(IllegalArgumentException.class, dfb::content);
+ }
@Test
void testNoRootExport() {
diff --git a/src/test/java/io/fabric8/maven/docker/config/HealthCheckConfigTest.java b/src/test/java/io/fabric8/maven/docker/config/HealthCheckConfigTest.java
index 2e562f489..c748c3d0b 100644
--- a/src/test/java/io/fabric8/maven/docker/config/HealthCheckConfigTest.java
+++ b/src/test/java/io/fabric8/maven/docker/config/HealthCheckConfigTest.java
@@ -1,145 +1,130 @@
package io.fabric8.maven.docker.config;
+import io.fabric8.maven.docker.config.HealthCheckConfiguration.DurationParser;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.time.Duration;
+import java.util.stream.Stream;
/**
* Tests the health check configuration
*/
class HealthCheckConfigTest {
-
- @Test
- void testGoodHealthCheck1() {
- new HealthCheckConfiguration.Builder()
- .cmd(new Arguments("exit 0"))
- .build()
- .validate();
+
+ static Stream