Skip to content

Commit d5d05b7

Browse files
committed
JENKINS-74912 support non-C Windows container bind mounts
1 parent fadf42e commit d5d05b7

File tree

6 files changed

+78
-36
lines changed

6 files changed

+78
-36
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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import static org.hamcrest.core.IsNot.not;
5656
import static org.hamcrest.core.StringContains.containsString;
5757
import static org.hamcrest.core.StringRegularExpression.matchesRegex;
58+
import static org.jenkinsci.plugins.docker.workflow.DockerTestUtil.DOCKER_OS_MODE;
5859
import static org.jenkinsci.plugins.docker.workflow.DockerTestUtil.assumeDocker;
5960
import static org.jenkinsci.plugins.docker.workflow.DockerTestUtil.assumeNotWindows;
6061
import static org.junit.Assert.assertEquals;
@@ -270,7 +271,7 @@ private static void grep(File dir, String text, String prefix, Set<String> match
270271
@Test public void buildWithMultiStage() {
271272
story.addStep(new Statement() {
272273
@Override public void evaluate() throws Throwable {
273-
assumeDocker(new VersionNumber("17.05"));
274+
assumeDocker(DockerTestUtil.DOCKER_OS_MODE.LINUX, new VersionNumber("17.05"));
274275
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj");
275276
p.setDefinition(new CpsFlowDefinition(
276277
"node {\n" +

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

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727
import hudson.Launcher;
2828
import hudson.util.StreamTaskListener;
2929
import hudson.util.VersionNumber;
30-
import org.hamcrest.Matchers;
3130
import org.jenkinsci.plugins.docker.commons.tools.DockerTool;
3231
import org.jenkinsci.plugins.docker.workflow.client.DockerClient;
3332
import org.junit.Assume;
3433

3534
import java.io.ByteArrayOutputStream;
35+
import java.io.File;
3636
import java.io.IOException;
3737
import java.util.Arrays;
3838
import java.util.List;
@@ -57,33 +57,19 @@ public class DockerTestUtil {
5757
"10.0.26100.2605" // 2025
5858
);
5959

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

81-
/**
82-
* Used to assume docker Windows is running in a particular os mode
83-
* @param os The os [windows, linux]
84-
* @throws Exception
85-
*/
86-
public static void assumeDockerServerOSMode(String os) throws Exception {
68+
public static void assumeDocker(DOCKER_OS_MODE osMode) throws Exception {
69+
assumeDocker(osMode, new VersionNumber(DEFAULT_MINIMUM_VERSION));
70+
}
71+
72+
public static void assumeDocker(DOCKER_OS_MODE osMode, VersionNumber minimumVersion) throws Exception {
8773
Launcher.LocalLauncher localLauncher = new Launcher.LocalLauncher(StreamTaskListener.NULL);
8874
try {
8975
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -93,11 +79,14 @@ public static void assumeDockerServerOSMode(String os) throws Exception {
9379
.stdout(out)
9480
.start()
9581
.joinWithTimeout(DockerClient.CLIENT_TIMEOUT, TimeUnit.SECONDS, localLauncher.getListener());
82+
DOCKER_OS_MODE cmdOsMode = DOCKER_OS_MODE.valueOf(out.toString().trim().toUpperCase());
9683
Assume.assumeTrue("Docker working", status == 0);
97-
Assume.assumeThat("Docker running in " + os + " mode", out.toString().trim(), Matchers.equalToIgnoringCase(os));
84+
Assume.assumeTrue("Docker os mode " + osMode, osMode == cmdOsMode);
9885
} catch (IOException x) {
99-
Assume.assumeNoException("Docker retrieve OS", x);
86+
Assume.assumeNoException("have Docker installed", x);
10087
}
88+
DockerClient dockerClient = new DockerClient(localLauncher, null, null);
89+
Assume.assumeFalse("Docker version not < " + minimumVersion.toString(), dockerClient.version().isOlderThan(minimumVersion));
10190
}
10291

10392
public static void assumeWindows() throws Exception {
@@ -108,6 +97,10 @@ public static void assumeNotWindows() throws Exception {
10897
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("windows"));
10998
}
11099

100+
public static void assumeDrive(char drive) throws Exception {
101+
Assume.assumeTrue(new File(drive + ":/").exists());
102+
}
103+
111104
public static String getWindowsKernelVersion() throws Exception {
112105
Launcher.LocalLauncher localLauncher = new Launcher.LocalLauncher(StreamTaskListener.NULL);
113106
ByteArrayOutputStream out = new ByteArrayOutputStream();

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@
3232
import hudson.model.FileParameterValue;
3333
import hudson.model.Result;
3434
import hudson.model.TaskListener;
35+
import hudson.os.WindowsUtil;
3536
import hudson.slaves.DumbSlave;
3637
import hudson.tools.ToolProperty;
3738
import hudson.util.ArgumentListBuilder;
3839
import hudson.util.Secret;
3940
import hudson.util.StreamTaskListener;
4041
import hudson.util.VersionNumber;
42+
import jenkins.util.SystemProperties;
4143
import org.apache.commons.fileupload.FileItem;
4244
import org.apache.commons.io.FileUtils;
4345
import org.hamcrest.Matchers;
@@ -76,13 +78,16 @@
7678
import java.io.File;
7779
import java.io.IOException;
7880
import java.lang.reflect.Field;
81+
import java.nio.file.Files;
82+
import java.nio.file.Path;
7983
import java.util.Collection;
8084
import java.util.Collections;
8185
import java.util.Set;
8286
import java.util.concurrent.TimeUnit;
8387
import java.util.logging.Level;
8488

8589
import static org.hamcrest.Matchers.is;
90+
import static org.junit.Assert.assertTrue;
8691
import static org.junit.Assume.assumeTrue;
8792

8893
public class WithContainerStepTest {
@@ -300,7 +305,7 @@ public class WithContainerStepTest {
300305
@Test public void cd() throws Exception {
301306
story.addStep(new Statement() {
302307
@Override public void evaluate() throws Throwable {
303-
DockerTestUtil.assumeDocker(new VersionNumber("17.12"));
308+
DockerTestUtil.assumeDocker(DockerTestUtil.DOCKER_OS_MODE.LINUX, new VersionNumber("17.12"));
304309
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj");
305310
p.setDefinition(new CpsFlowDefinition(
306311
"node {\n" +
@@ -499,8 +504,7 @@ private static final class Execution extends SynchronousNonBlockingStepExecution
499504
story.addStep(new Statement() {
500505
@Override public void evaluate() throws Throwable {
501506
DockerTestUtil.assumeWindows();
502-
DockerTestUtil.assumeDocker();
503-
DockerTestUtil.assumeDockerServerOSMode("windows");
507+
DockerTestUtil.assumeDocker(DockerTestUtil.DOCKER_OS_MODE.WINDOWS);
504508

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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import hudson.util.StreamTaskListener;
3131
import hudson.util.VersionNumber;
3232
import org.jenkinsci.plugins.docker.commons.fingerprint.ContainerRecord;
33+
import org.jenkinsci.plugins.docker.workflow.DockerTestUtil;
3334
import org.junit.Assert;
3435
import org.junit.Before;
3536
import org.junit.Test;

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.junit.Assert;
1010
import org.junit.Before;
1111
import org.junit.Test;
12+
import org.jvnet.hudson.test.Issue;
1213

1314
import java.io.IOException;
1415
import java.util.Collections;
@@ -18,17 +19,16 @@ public class WindowsDockerClientTest {
1819
private DockerClient dockerClient;
1920

2021
@Before
21-
public void setup() throws Exception {
22-
DockerTestUtil.assumeDocker();
23-
22+
public void setup() {
2423
TaskListener taskListener = StreamTaskListener.fromStderr();
2524
Launcher.LocalLauncher launcher = new Launcher.LocalLauncher(taskListener);
2625

2726
dockerClient = new WindowsDockerClient(launcher, null, null);
2827
}
2928

3029
@Test
31-
public void test_run() throws IOException, InterruptedException {
30+
public void test_run() throws Exception {
31+
DockerTestUtil.assumeDocker();
3232
EnvVars launchEnv = DockerTestUtil.newDockerLaunchEnv();
3333
String containerId = dockerClient.run(
3434
launchEnv,

0 commit comments

Comments
 (0)