2424package org .jenkinsci .plugins .docker .workflow ;
2525
2626import hudson .EnvVars ;
27- import org .jenkinsci .plugins .docker .workflow .client .DockerClient ;
2827import hudson .Launcher ;
2928import hudson .util .StreamTaskListener ;
3029import hudson .util .VersionNumber ;
30+ import org .hamcrest .Matchers ;
31+ import org .jenkinsci .plugins .docker .commons .tools .DockerTool ;
32+ import org .jenkinsci .plugins .docker .workflow .client .DockerClient ;
3133import org .junit .Assume ;
3234
35+ import java .io .ByteArrayOutputStream ;
3336import java .io .IOException ;
37+ import java .util .Arrays ;
38+ import java .util .List ;
39+ import java .util .Optional ;
3440import java .util .concurrent .TimeUnit ;
35-
36- import org . jenkinsci . plugins . docker . commons . tools . DockerTool ;
41+ import java . util . regex . Matcher ;
42+ import java . util . regex . Pattern ;
3743
3844/**
3945 * @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
4046 */
4147public class DockerTestUtil {
4248 public static String DEFAULT_MINIMUM_VERSION = "1.3" ;
4349
50+ // Major Windows kernel versions. See https://hub.docker.com/r/microsoft/windows-nanoserver
51+ private static List <String > MAJOR_WINDOWS_KERNEL_VERSIONS = Arrays .asList (
52+ "10.0.17763.6659" , // 1809
53+ "10.0.18363.1556" , // 1909
54+ "10.0.19041.1415" , // 2004
55+ "10.0.19042.1889" , // 20H2
56+ "10.0.20348.2966" , // 2022
57+ "10.0.26100.2605" // 2025
58+ );
59+
60+
4461 public static void assumeDocker () throws Exception {
4562 assumeDocker (new VersionNumber (DEFAULT_MINIMUM_VERSION ));
4663 }
@@ -61,10 +78,78 @@ public static void assumeDocker(VersionNumber minimumVersion) throws Exception {
6178 Assume .assumeFalse ("Docker version not < " + minimumVersion .toString (), dockerClient .version ().isOlderThan (minimumVersion ));
6279 }
6380
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 {
87+ Launcher .LocalLauncher localLauncher = new Launcher .LocalLauncher (StreamTaskListener .NULL );
88+ try {
89+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
90+ int status = localLauncher
91+ .launch ()
92+ .cmds (DockerTool .getExecutable (null , null , null , null ), "version" , "-f" , "{{.Server.Os}}" )
93+ .stdout (out )
94+ .start ()
95+ .joinWithTimeout (DockerClient .CLIENT_TIMEOUT , TimeUnit .SECONDS , localLauncher .getListener ());
96+ Assume .assumeTrue ("Docker working" , status == 0 );
97+ Assume .assumeThat ("Docker running in " + os + " mode" , out .toString ().trim (), Matchers .equalToIgnoringCase (os ));
98+ } catch (IOException x ) {
99+ Assume .assumeNoException ("Docker retrieve OS" , x );
100+ }
101+ }
102+
103+ public static void assumeWindows () throws Exception {
104+ Assume .assumeTrue (System .getProperty ("os.name" ).toLowerCase ().contains ("windows" ));
105+ }
106+
64107 public static void assumeNotWindows () throws Exception {
65108 Assume .assumeFalse (System .getProperty ("os.name" ).toLowerCase ().contains ("windows" ));
66109 }
67110
111+ public static String getWindowsKernelVersion () throws Exception {
112+ Launcher .LocalLauncher localLauncher = new Launcher .LocalLauncher (StreamTaskListener .NULL );
113+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
114+ ByteArrayOutputStream err = new ByteArrayOutputStream ();
115+
116+ int status = localLauncher
117+ .launch ()
118+ .cmds ("cmd" , "/c" , "ver" )
119+ .stdout (out )
120+ .stderr (err )
121+ .start ()
122+ .joinWithTimeout (DockerClient .CLIENT_TIMEOUT , TimeUnit .SECONDS , localLauncher .getListener ());
123+
124+ if (status != 0 ) {
125+ throw new RuntimeException (String .format ("Failed to obtain Windows kernel version with exit code: %d stdout: %s stderr: %s" , status , out , err ));
126+ }
127+
128+ Matcher matcher = Pattern .compile ("Microsoft Windows \\ [Version ([^\\ ]]+)\\ ]" ).matcher (out .toString ().trim ());
129+
130+ if (matcher .matches ()) {
131+ return matcher .group (1 );
132+ } else {
133+ throw new RuntimeException ("Unable to obtain Windows kernel version from output: " + out );
134+ }
135+ }
136+
137+ /**
138+ * @return The image tag of an image with a kernel version corresponding to the closest compatible Windows release
139+ * @throws Exception
140+ */
141+ public static String getWindowsImageTag () throws Exception {
142+ // Kernel must match when running Windows containers on docker on Windows if < Windows 11 with Server 2022
143+ String kernelVersion = DockerTestUtil .getWindowsKernelVersion ();
144+
145+ // Select the highest well known kernel version <= ours since sometimes an image may not exist for our version
146+ Optional <String > wellKnownKernelVersion = MAJOR_WINDOWS_KERNEL_VERSIONS .stream ()
147+ .filter (k -> k .compareTo (kernelVersion ) <= 0 ).max (java .util .Comparator .naturalOrder ());
148+
149+ // Fall back to trying our kernel version
150+ return wellKnownKernelVersion .orElse (kernelVersion );
151+ }
152+
68153 public static EnvVars newDockerLaunchEnv () {
69154 // Create the KeyMaterial for connecting to the docker host/server.
70155 // E.g. currently need to add something like the following to your env
0 commit comments