From 4494cf5db9d41d04a29240e7190fd039e6afcaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Maa=C3=9F?= Date: Tue, 8 Jun 2021 14:23:11 +0100 Subject: [PATCH] Fix host directory mounts on Windows On Windows, it is not possible to mount host directories to a subfolder of a non-C: drive. Due to this restriction, having a workspace on another drive will make the docker run execution fail since the plugin tries to map the workspace into the container. This fixes that by collecting all the non-C: drive volume mappings and handling those explicitly by mapping the entire drive into the container. --- .../workflow/client/WindowsDockerClient.java | 35 ++++++++++++++++++- .../client/WindowsDockerClientTest.java | 6 ++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java index 2de11c25d..a9ab11a73 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java @@ -12,6 +12,9 @@ import javax.annotation.Nonnull; import java.io.*; import java.nio.charset.Charset; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -39,8 +42,38 @@ public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNu if (workdir != null) { argb.add("-w", workdir); } + Set drives = new HashSet<>(); for (Map.Entry volume : volumes.entrySet()) { - argb.add("-v", volume.getKey() + ":" + volume.getValue()); + String driveName = null; + + try { + Path hostPath = Paths.get(volume.getKey()); + Path rootPath = hostPath.getRoot(); + // If we have a valid root we can check if we need to do our special root handling + if (rootPath != null) { + driveName = rootPath.toString(); + if (driveName.endsWith("\\")) { + driveName = driveName.substring(0, driveName.length() - 1); + } + } + } catch (InvalidPathException e) { + // We got a value that is not a valid path. Keeping driveName at null allows us to gracefully handle this + // and skip the special drive path handling for those cases + } + if (driveName == null || driveName.equals("C:")) { + // C: path can be mapped directly + argb.add("-v", volume.getKey() + ":" + volume.getValue()); + } + else + { + // Non C: drive paths in the container can not be mapped due to Windows limitations. It is only possible + // to map an entire drive so we collect the used drives and map the entire drive + drives.add(driveName); + } + } + for (String drive : drives) { + // Windows requires that the host part is a directory but the container path must be an entire drive + argb.add("-v", String.format("%s\\:%s", drive, drive)); } for (String containerId : volumesFromContainers) { argb.add("--volumes-from", containerId); diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java index c390e0735..2e0c62504 100644 --- a/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public class WindowsDockerClientTest { @@ -35,7 +37,7 @@ public void test_run() throws IOException, InterruptedException { "learn/tutorial", null, null, - Collections.emptyMap(), + Collections.singletonMap("D:\\Jenkins\\workspace", "D:\\Jenkins\\workspace"), Collections.emptyList(), new EnvVars(), dockerClient.whoAmI(), @@ -47,7 +49,7 @@ public void test_run() throws IOException, InterruptedException { Assert.assertTrue(containerRecord.getContainerName().length() > 0); Assert.assertTrue(containerRecord.getHost().length() > 0); Assert.assertTrue(containerRecord.getCreated() > 1000000000000L); - Assert.assertEquals(Collections.emptyList(), dockerClient.getVolumes(launchEnv, containerId)); + Assert.assertEquals(Collections.singletonList("d:"), dockerClient.getVolumes(launchEnv, containerId)); // Also test that the stop works and cleans up after itself Assert.assertNotNull(dockerClient.inspect(launchEnv, containerId, ".Name"));