From 8a91d91d1b805b771209e8c47abb6f226f4a8b0c Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Wed, 28 Feb 2024 00:49:40 +0100 Subject: [PATCH] Feat: Add configuration allowing PortMappingPropertyWriteHelper to create parent directories Signed-off-by: Guillaume Villena --- .../asciidoc/inc/_global-configuration.adoc | 4 ++ .../inc/external/_property_configuration.adoc | 3 ++ .../asciidoc/inc/start/_configuration.adoc | 3 ++ .../asciidoc/inc/start/_port-mapping.adoc | 2 + .../io/fabric8/maven/docker/StartMojo.java | 6 ++- .../maven/docker/access/PortMapping.java | 12 +++++- .../docker/config/RunImageConfiguration.java | 12 ++++++ .../config/handler/property/ConfigKey.java | 1 + .../property/PropertyConfigHandler.java | 1 + .../PortMappingPropertyWriteHelperTest.java | 37 ++++++++++++++++++- 10 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/main/asciidoc/inc/_global-configuration.adoc b/src/main/asciidoc/inc/_global-configuration.adoc index 7c74e609d..64208d081 100644 --- a/src/main/asciidoc/inc/_global-configuration.adoc +++ b/src/main/asciidoc/inc/_global-configuration.adoc @@ -121,6 +121,10 @@ By default a progress meter is printed out on the console, which is omitted when | Global property file into which the mapped properties should be written to. The format of this file and its purpose are also described in <>. | +| *portPropertyFileCreatePaths* +| Enable parent folder creation for paths specified in `portPropertyFile`. If set to false (default) the `portPropertyFile` parent folders must exist prior the file creation. See <>. +| `docker.portPropertyFileCreatePaths` + | *registry* | Specify globally a registry to use for pulling and pushing images. See <> for details. | `docker.registry` diff --git a/src/main/asciidoc/inc/external/_property_configuration.adoc b/src/main/asciidoc/inc/external/_property_configuration.adoc index 511af0da9..fcd9173d9 100644 --- a/src/main/asciidoc/inc/external/_property_configuration.adoc +++ b/src/main/asciidoc/inc/external/_property_configuration.adoc @@ -280,6 +280,9 @@ See <>. | *docker.portPropertyFile* | specifies a path to a port mapping used when starting a container. +| *docker.portPropertyFileCreatePaths* +| Specify if parent folder of `docker.portPropertyFile` should be automatically created (default: false). + | *docker.ports.idx* | Sets a port mapping. For example `jolokia.ports:8080` maps the container port 8080 dynamically to a host port and assigns this host port to the Maven property `${jolokia.port}`. See <<_port-mapping,Port mapping>> for possible mapping options. When creating images images only the right most port is used for exposing the port. For providing multiple port mappings, the index should be count up. See <> for more information about list properties. diff --git a/src/main/asciidoc/inc/start/_configuration.adoc b/src/main/asciidoc/inc/start/_configuration.adoc index 6966d7673..2cbc21a31 100644 --- a/src/main/asciidoc/inc/start/_configuration.adoc +++ b/src/main/asciidoc/inc/start/_configuration.adoc @@ -107,6 +107,9 @@ a| *This option is deprecated, please use a `containerNamePattern` instead* Nami | <> | File path into which the mapped port properties are written. The format of this file and its purpose are also described in <> +| <> +| Enable parent folder creation for paths specified in `portPropertyFile`. If set to false (default) the `portPropertyFile` parent folders must exist prior the file creation. See <>. + | <> | <> for exposing container ports to host ports. diff --git a/src/main/asciidoc/inc/start/_port-mapping.adoc b/src/main/asciidoc/inc/start/_port-mapping.adoc index c70d3dda6..546d97438 100644 --- a/src/main/asciidoc/inc/start/_port-mapping.adoc +++ b/src/main/asciidoc/inc/start/_port-mapping.adoc @@ -51,6 +51,8 @@ Another useful configuration option is `portPropertyFile` which can be used to w This property file might be useful with tests or with other maven plugins that will be unable to use the resolved properties because they can only be updated after the container has started and plugins resolve their properties in an earlier lifecycle phase. +The `portPropertyFileCreatePaths` option can be used to allow parent folder creation for paths specified in `portPropertyFile`. If set to false (default) the `portPropertyFile` parent folders must exist prior the file creation. + If you don't need to write out such a property file and thus don't need to preserve the property names, you can use normal maven properties as well. E.g. `${host.var}:${port.var}:8080` instead of `+host.var:port.var:8080`. diff --git a/src/main/java/io/fabric8/maven/docker/StartMojo.java b/src/main/java/io/fabric8/maven/docker/StartMojo.java index e19249e18..ec30f056c 100644 --- a/src/main/java/io/fabric8/maven/docker/StartMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StartMojo.java @@ -102,6 +102,10 @@ public class StartMojo extends AbstractDockerMojo { @Parameter protected String portPropertyFile; + // allow creation of all subdirectories if absent + @Parameter(property = "docker.portPropertyFileCreatePaths", defaultValue = "false") + protected boolean portPropertyFileCreatePaths; + private static final class StartedContainer { public final ImageConfiguration imageConfig; public final String containerId; @@ -129,7 +133,7 @@ public synchronized void executeInternal(final ServiceHub hub) throws DockerAcce QueryService queryService = hub.getQueryService(); final RunService runService = hub.getRunService(); - PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper = new PortMapping.PropertyWriteHelper(portPropertyFile); + PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper = new PortMapping.PropertyWriteHelper(portPropertyFile, portPropertyFileCreatePaths); boolean success = false; diff --git a/src/main/java/io/fabric8/maven/docker/access/PortMapping.java b/src/main/java/io/fabric8/maven/docker/access/PortMapping.java index 3355dfcb3..b325e2c4b 100644 --- a/src/main/java/io/fabric8/maven/docker/access/PortMapping.java +++ b/src/main/java/io/fabric8/maven/docker/access/PortMapping.java @@ -371,14 +371,16 @@ public static class PropertyWriteHelper { private final Properties globalExport; + private final boolean createPaths; private final String globalFile; private final Map toExport; - public PropertyWriteHelper(String globalFile) { + public PropertyWriteHelper(String globalFile, boolean createPaths) { this.globalFile = globalFile; this.toExport = new HashMap<>(); this.globalExport = new Properties(); + this.createPaths = createPaths; } public void add(PortMapping portMapping, String portPropertyFile) { @@ -403,6 +405,14 @@ public void write() throws IOException { private void writeProperties(Properties props, String file) throws IOException { File propFile = new File(file); + + if (createPaths) { + File parent = propFile.getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } + } + try (OutputStream os = new FileOutputStream(propFile)) { props.store(os, "Docker ports"); } catch (IOException e) { 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..1f0aa667c 100644 --- a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java @@ -85,6 +85,9 @@ public class RunImageConfiguration implements Serializable { @Parameter private String portPropertyFile; + @Parameter + private Boolean portPropertyFileCreatePaths; + // For simple network setups. For complex stuff use "network" @Parameter private String net; @@ -309,6 +312,10 @@ public String getPortPropertyFile() { return portPropertyFile; } + public Boolean getPortPropertyFileCreatePaths() { + return portPropertyFileCreatePaths; + } + public String getWorkingDir() { return workingDir; } @@ -522,6 +529,11 @@ public Builder portPropertyFile(String portPropertyFile) { return this; } + public Builder portPropertyFileCreatePaths(Boolean createPath){ + config.portPropertyFileCreatePaths = createPath; + return this; + } + public Builder workingDir(String workingDir) { config.workingDir = workingDir; return this; diff --git a/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java b/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java index 552ab22ea..8bf6653d0 100644 --- a/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java +++ b/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java @@ -113,6 +113,7 @@ public enum ConfigKey { OPTIMISE, PLATFORM, PORT_PROPERTY_FILE, + PORT_PROPERTY_FILE_CREATE_PATHS, PORTS(ValueCombinePolicy.Merge), PRIVILEGED, READ_ONLY, 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..c2827de64 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 @@ -222,6 +222,7 @@ private RunImageConfiguration extractRunConfiguration(ImageConfiguration fromCon .namingStrategy(valueProvider.getString(NAMING_STRATEGY, config.getNamingStrategy() == null ? null : config.getNamingStrategy().name())) .exposedPropertyKey(valueProvider.getString(EXPOSED_PROPERTY_KEY, config.getExposedPropertyKey())) .portPropertyFile(valueProvider.getString(PORT_PROPERTY_FILE, config.getPortPropertyFile())) + .portPropertyFileCreatePaths(valueProvider.getBoolean(PORT_PROPERTY_FILE_CREATE_PATHS, config.getPortPropertyFileCreatePaths())) .ports(valueProvider.getList(PORTS, config.getPorts())) .shmSize(valueProvider.getLong(SHMSIZE, config.getShmSize())) .privileged(valueProvider.getBoolean(PRIVILEGED, config.getPrivileged())) diff --git a/src/test/java/io/fabric8/maven/docker/access/PortMappingPropertyWriteHelperTest.java b/src/test/java/io/fabric8/maven/docker/access/PortMappingPropertyWriteHelperTest.java index 661c6c2af..43232f2de 100644 --- a/src/test/java/io/fabric8/maven/docker/access/PortMappingPropertyWriteHelperTest.java +++ b/src/test/java/io/fabric8/maven/docker/access/PortMappingPropertyWriteHelperTest.java @@ -8,6 +8,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -84,6 +85,37 @@ void testWriteImageAndGlobal() throws IOException { thenPropsContains("other.ip1", "1.2.3.4"); } + @Test + void testWriteCreatePaths() throws IOException { + File dir = Files.createTempDirectory("dmp-").toFile(); + Assertions.assertTrue(dir.delete()); + String globalFile = Paths.get(dir.getAbsolutePath(), "dmp-tmp.properties").toFile().getAbsolutePath(); + PortMapping mapping = createPortMapping("jolokia.port:8080", "18181:8181", "127.0.0.1:9090:9090", "127.0.0.1:other.port:5678"); + + // check that we can write on non-existant path with create set to "true" + givenAPortMappingWriterThatCreatesPaths(globalFile); + whenUpdateDynamicMapping(mapping, "0.0.0.0", 8080, 49900); + whenUpdateDynamicMapping(mapping, "127.0.0.1", 5678, 49901); + whenWritePortMappings(null, mapping); + thenPropsFileExists(globalFile); + thenPropsSizeIs(2); + thenPropsContains("jolokia.port", 49900); + thenPropsContains("other.port", 49901); + + // Check that we can still write in an existing path + String globalFile2 = Paths.get(dir.getAbsolutePath(), "dmp-tmp2.properties").toFile().getAbsolutePath(); + givenAPortMappingWriterThatCreatesPaths(globalFile2); + whenUpdateDynamicMapping(mapping, "0.0.0.0", 8080, 49900); + whenUpdateDynamicMapping(mapping, "127.0.0.1", 5678, 49901); + whenWritePortMappings(null, mapping); + thenPropsFileExists(globalFile2); + thenPropsSizeIs(2); + thenPropsContains("jolokia.port", 49900); + thenPropsContains("other.port", 49901); + + + } + private void givenADockerHostAddress(String host) { projProperties.setProperty("docker.host.address", host); } @@ -124,7 +156,10 @@ private void givenAHostIpProperty(String property, String hostIp) { } private void givenAPortMappingWriter(String globalFile) { - propertyWriteHelper = new PortMapping.PropertyWriteHelper(globalFile); + propertyWriteHelper = new PortMapping.PropertyWriteHelper(globalFile, false); + } + private void givenAPortMappingWriterThatCreatesPaths(String globalFile) { + propertyWriteHelper = new PortMapping.PropertyWriteHelper(globalFile, true); } private void thenPropsContains(String variable, Object port) {