Skip to content

Commit 1fda618

Browse files
committed
[JENKINS-74912] Support non-C Windows container bind mounts
1 parent 1001d94 commit 1fda618

File tree

5 files changed

+72
-35
lines changed

5 files changed

+72
-35
lines changed

src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,18 @@ public String run(@NonNull EnvVars launchEnv, @NonNull String image, @CheckForNu
3939
if (workdir != null) {
4040
argb.add("-w", workdir);
4141
}
42+
Set<String> seenNonCDrives = new HashSet();
4243
for (Map.Entry<String, String> volume : volumes.entrySet()) {
43-
argb.add("-v", volume.getKey() + ":" + volume.getValue());
44+
String sourceVolume = volume.getKey();
45+
String driveLetter = sourceVolume.contains(":")? sourceVolume.substring(0, sourceVolume.indexOf(":")) : "C";
46+
47+
if (driveLetter.equalsIgnoreCase("C")) {
48+
argb.add("-v", volume.getKey() + ":" + volume.getValue());
49+
} else if (seenNonCDrives.add(driveLetter)) {
50+
// JENKINS-74912 Docker for windows does not support mounting non-root non-C volumes
51+
LOGGER.log(Level.FINE, "Detected non-C drive \"{0}\". Mounting entire drive.", driveLetter);
52+
argb.add("-v", driveLetter + ":/:" + driveLetter + ":");
53+
}
4454
}
4555
for (String containerId : volumesFromContainers) {
4656
argb.add("--volumes-from", containerId);

src/test/java/org/jenkinsci/plugins/docker/workflow/DockerDSLTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ private static void grep(File dir, String text, String prefix, Set<String> match
270270
@Test public void buildWithMultiStage() {
271271
story.addStep(new Statement() {
272272
@Override public void evaluate() throws Throwable {
273-
assumeDocker(new VersionNumber("17.05"));
273+
assumeDocker(DockerTestUtil.DOCKER_OS_MODE.LINUX, new VersionNumber("17.05"));
274274
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj");
275275
p.setDefinition(new CpsFlowDefinition(
276276
"node {\n" +

src/test/java/org/jenkinsci/plugins/docker/workflow/DockerTestUtil.java

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.junit.Assume;
3232

3333
import java.io.ByteArrayOutputStream;
34+
import java.io.File;
3435
import java.io.IOException;
3536
import java.util.Arrays;
3637
import java.util.List;
@@ -39,7 +40,6 @@
3940
import java.util.regex.Matcher;
4041
import java.util.regex.Pattern;
4142

42-
import org.hamcrest.Matchers;
4343
import org.jenkinsci.plugins.docker.commons.tools.DockerTool;
4444

4545
/**
@@ -58,33 +58,20 @@ public class DockerTestUtil {
5858
"10.0.26100.2605" // 2025
5959
);
6060

61+
public enum DOCKER_OS_MODE {
62+
LINUX,
63+
WINDOWS
64+
}
6165

6266
public static void assumeDocker() throws Exception {
63-
assumeDocker(new VersionNumber(DEFAULT_MINIMUM_VERSION));
67+
assumeDocker(DOCKER_OS_MODE.LINUX, new VersionNumber(DEFAULT_MINIMUM_VERSION));
6468
}
65-
66-
public static void assumeDocker(VersionNumber minimumVersion) throws Exception {
67-
Launcher.LocalLauncher localLauncher = new Launcher.LocalLauncher(StreamTaskListener.NULL);
68-
try {
69-
int status = localLauncher
70-
.launch()
71-
.cmds(DockerTool.getExecutable(null, null, null, null), "ps")
72-
.start()
73-
.joinWithTimeout(DockerClient.CLIENT_TIMEOUT, TimeUnit.SECONDS, localLauncher.getListener());
74-
Assume.assumeTrue("Docker working", status == 0);
75-
} catch (IOException x) {
76-
Assume.assumeNoException("have Docker installed", x);
77-
}
78-
DockerClient dockerClient = new DockerClient(localLauncher, null, null);
79-
Assume.assumeFalse("Docker version not < " + minimumVersion.toString(), dockerClient.version().isOlderThan(minimumVersion));
69+
70+
public static void assumeDocker(DOCKER_OS_MODE osMode) throws Exception {
71+
assumeDocker(osMode, new VersionNumber(DEFAULT_MINIMUM_VERSION));
8072
}
8173

82-
/**
83-
* Used to assume docker Windows is running in a particular os mode
84-
* @param os The os [windows, linux]
85-
* @throws Exception
86-
*/
87-
public static void assumeDockerServerOSMode(String os) throws Exception {
74+
public static void assumeDocker(DOCKER_OS_MODE osMode, VersionNumber minimumVersion) throws Exception {
8875
Launcher.LocalLauncher localLauncher = new Launcher.LocalLauncher(StreamTaskListener.NULL);
8976
try {
9077
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -94,11 +81,14 @@ public static void assumeDockerServerOSMode(String os) throws Exception {
9481
.stdout(out)
9582
.start()
9683
.joinWithTimeout(DockerClient.CLIENT_TIMEOUT, TimeUnit.SECONDS, localLauncher.getListener());
84+
DOCKER_OS_MODE cmdOsMode = DOCKER_OS_MODE.valueOf(out.toString().trim().toUpperCase());
9785
Assume.assumeTrue("Docker working", status == 0);
98-
Assume.assumeThat("Docker running in " + os + " mode", out.toString().trim(), Matchers.equalToIgnoringCase(os));
86+
Assume.assumeTrue("Docker os mode " + osMode, osMode == cmdOsMode);
9987
} catch (IOException x) {
100-
Assume.assumeNoException("Docker retrieve OS", x);
88+
Assume.assumeNoException("have Docker installed", x);
10189
}
90+
DockerClient dockerClient = new DockerClient(localLauncher, null, null);
91+
Assume.assumeFalse("Docker version not < " + minimumVersion.toString(), dockerClient.version().isOlderThan(minimumVersion));
10292
}
10393

10494
public static void assumeWindows() throws Exception {
@@ -109,6 +99,10 @@ public static void assumeNotWindows() throws Exception {
10999
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("windows"));
110100
}
111101

102+
public static void assumeDrive(char drive) throws Exception {
103+
Assume.assumeTrue(new File(drive + ":/").exists());
104+
}
105+
112106
public static String getWindowsKernelVersion() throws Exception {
113107
Launcher.LocalLauncher localLauncher = new Launcher.LocalLauncher(StreamTaskListener.NULL);
114108
ByteArrayOutputStream out = new ByteArrayOutputStream();

src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import hudson.util.VersionNumber;
4646
import java.io.IOException;
4747
import java.lang.reflect.Field;
48+
import java.nio.file.Files;
49+
import java.nio.file.Path;
4850
import java.util.Set;
4951
import java.util.concurrent.TimeUnit;
5052
import org.apache.commons.fileupload.FileItem;
@@ -69,6 +71,7 @@
6971
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution;
7072
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
7173
import org.junit.Assume;
74+
import static org.junit.Assert.assertTrue;
7275
import static org.junit.Assume.assumeTrue;
7376
import org.junit.ClassRule;
7477
import org.junit.Ignore;
@@ -299,7 +302,7 @@ public class WithContainerStepTest {
299302
@Test public void cd() throws Exception {
300303
story.addStep(new Statement() {
301304
@Override public void evaluate() throws Throwable {
302-
DockerTestUtil.assumeDocker(new VersionNumber("17.12"));
305+
DockerTestUtil.assumeDocker(DockerTestUtil.DOCKER_OS_MODE.LINUX, new VersionNumber("17.12"));
303306
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj");
304307
p.setDefinition(new CpsFlowDefinition(
305308
"node {\n" +
@@ -498,8 +501,7 @@ private static final class Execution extends SynchronousNonBlockingStepExecution
498501
story.addStep(new Statement() {
499502
@Override public void evaluate() throws Throwable {
500503
DockerTestUtil.assumeWindows();
501-
DockerTestUtil.assumeDocker();
502-
DockerTestUtil.assumeDockerServerOSMode("windows");
504+
DockerTestUtil.assumeDocker(DockerTestUtil.DOCKER_OS_MODE.WINDOWS);
503505

504506
// Kernel must match when running Windows containers on docker on Windows
505507
String releaseTag = DockerTestUtil.getWindowsImageTag();
@@ -516,4 +518,37 @@ private static final class Execution extends SynchronousNonBlockingStepExecution
516518
}
517519
});
518520
}
521+
522+
@Issue("JENKINS-74912")
523+
@Test public void windowsRunningWindowsContainerAlternateDriveWorkspace() {
524+
// Run with another drive ("D") if it is mounted
525+
story.addStep(new Statement() {
526+
@Override public void evaluate() throws Throwable {
527+
DockerTestUtil.assumeWindows();
528+
DockerTestUtil.assumeDocker(DockerTestUtil.DOCKER_OS_MODE.WINDOWS);
529+
DockerTestUtil.assumeDrive('D');
530+
531+
// Manually create instead of using a Rule since not all executions will have the D drive mounted
532+
Path tempDir = Files.createTempDirectory(Path.of("D:/"), "j ws");
533+
tempDir.toFile().deleteOnExit();
534+
535+
// Kernel must match when running Windows containers on docker on Windows
536+
String releaseTag = DockerTestUtil.getWindowsImageTag();
537+
538+
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj");
539+
p.setDefinition(new CpsFlowDefinition(
540+
"node {\n" +
541+
" ws('" + tempDir.toString().replace("\\", "\\\\") + "') {\n" +
542+
" withDockerContainer('mcr.microsoft.com/windows/nanoserver:" + releaseTag + "') { \n" +
543+
" bat 'echo bar > foo.txt' \n" +
544+
" bat 'echo ran OK' \n" +
545+
" }\n" +
546+
" }\n" +
547+
"}", true));
548+
WorkflowRun b = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
549+
story.j.assertLogContains("ran OK", b);
550+
assertTrue("Mounted workspace contains foo.txt", tempDir.resolve("foo.txt").toFile().exists());
551+
}
552+
});
553+
}
519554
}

src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,23 @@
1010
import org.junit.Before;
1111
import org.junit.Test;
1212

13-
import java.io.IOException;
1413
import java.util.Collections;
1514

1615
public class WindowsDockerClientTest {
1716

1817
private DockerClient dockerClient;
1918

2019
@Before
21-
public void setup() throws Exception {
22-
DockerTestUtil.assumeDocker();
23-
20+
public void setup() {
2421
TaskListener taskListener = StreamTaskListener.fromStderr();
2522
Launcher.LocalLauncher launcher = new Launcher.LocalLauncher(taskListener);
2623

2724
dockerClient = new WindowsDockerClient(launcher, null, null);
2825
}
2926

3027
@Test
31-
public void test_run() throws IOException, InterruptedException {
28+
public void test_run() throws Exception {
29+
DockerTestUtil.assumeDocker();
3230
EnvVars launchEnv = DockerTestUtil.newDockerLaunchEnv();
3331
String containerId = dockerClient.run(
3432
launchEnv,

0 commit comments

Comments
 (0)