2424package org .jenkinsci .plugins .docker .workflow ;
2525
2626import com .google .common .base .Optional ;
27- import org .jenkinsci .plugins .docker .workflow .client .DockerClient ;
2827import com .google .inject .Inject ;
2928import hudson .AbortException ;
3029import hudson .EnvVars ;
3938import hudson .model .Run ;
4039import hudson .model .TaskListener ;
4140import hudson .slaves .WorkspaceList ;
42- import java .io .ByteArrayOutputStream ;
43- import java .io .IOException ;
44- import java .io .Serializable ;
45- import java .nio .charset .Charset ;
41+ import hudson .util .VersionNumber ;
4642import java .util .ArrayList ;
4743import java .util .Arrays ;
4844import java .util .Collection ;
4945import java .util .Iterator ;
50- import java .util .List ;
51- import java .util .Map ;
5246import java .util .LinkedHashMap ;
5347import java .util .LinkedHashSet ;
48+ import java .util .List ;
49+ import java .util .Map ;
5450import java .util .Set ;
5551import java .util .TreeSet ;
56- import java .util .logging .Level ;
57- import java .util .logging .Logger ;
58- import javax .annotation .Nonnull ;
59-
60- import hudson .util .VersionNumber ;
61- import java .util .concurrent .TimeUnit ;
62- import java .util .stream .Collectors ;
63- import javax .annotation .CheckForNull ;
6452import org .jenkinsci .plugins .docker .commons .fingerprint .DockerFingerprints ;
6553import org .jenkinsci .plugins .docker .commons .tools .DockerTool ;
54+ import org .jenkinsci .plugins .docker .workflow .client .DockerClient ;
55+ import org .jenkinsci .plugins .docker .workflow .client .WindowsDockerClient ;
6656import org .jenkinsci .plugins .workflow .steps .AbstractStepDescriptorImpl ;
6757import org .jenkinsci .plugins .workflow .steps .AbstractStepExecutionImpl ;
6858import org .jenkinsci .plugins .workflow .steps .AbstractStepImpl ;
7363import org .kohsuke .stapler .DataBoundConstructor ;
7464import org .kohsuke .stapler .DataBoundSetter ;
7565
66+ import javax .annotation .CheckForNull ;
67+ import javax .annotation .Nonnull ;
68+ import java .io .ByteArrayOutputStream ;
69+ import java .io .IOException ;
70+ import java .io .Serializable ;
71+ import java .nio .charset .Charset ;
72+ import java .util .concurrent .TimeUnit ;
73+ import java .util .logging .Level ;
74+ import java .util .logging .Logger ;
75+ import java .util .stream .Collectors ;
76+
7677public class WithContainerStep extends AbstractStepImpl {
7778
7879 private static final Logger LOGGER = Logger .getLogger (WithContainerStep .class .getName ());
@@ -111,7 +112,6 @@ private static void destroy(String container, Launcher launcher, Node node, EnvV
111112
112113 // TODO switch to GeneralNonBlockingStepExecution
113114 public static class Execution extends AbstractStepExecutionImpl {
114-
115115 private static final long serialVersionUID = 1 ;
116116 @ Inject (optional =true ) private transient WithContainerStep step ;
117117 @ StepContextParameter private transient Launcher launcher ;
@@ -125,6 +125,9 @@ public static class Execution extends AbstractStepExecutionImpl {
125125 private String container ;
126126 private String toolName ;
127127
128+ public Execution () {
129+ }
130+
128131 @ Override public boolean start () throws Exception {
129132 EnvVars envReduced = new EnvVars (env );
130133 EnvVars envHost = computer .getEnvironment ();
@@ -136,24 +139,29 @@ public static class Execution extends AbstractStepExecutionImpl {
136139
137140 LOGGER .log (Level .FINE , "reduced environment: {0}" , envReduced );
138141 workspace .mkdirs (); // otherwise it may be owned by root when created for -v
139- String ws = workspace . getRemote ( );
142+ String ws = getPath ( workspace );
140143 toolName = step .toolName ;
141- DockerClient dockerClient = new DockerClient (launcher , node , toolName );
144+ DockerClient dockerClient = launcher .isUnix ()
145+ ? new DockerClient (launcher , node , toolName )
146+ : new WindowsDockerClient (launcher , node , toolName );
142147
143148 VersionNumber dockerVersion = dockerClient .version ();
144149 if (dockerVersion != null ) {
145150 if (dockerVersion .isOlderThan (new VersionNumber ("1.7" ))) {
146151 throw new AbortException ("The docker version is less than v1.7. Pipeline functions requiring 'docker exec' (e.g. 'docker.inside') or SELinux labeling will not work." );
147152 } else if (dockerVersion .isOlderThan (new VersionNumber ("1.8" ))) {
148153 listener .error ("The docker version is less than v1.8. Running a 'docker.inside' from inside a container will not work." );
154+ } else if (dockerVersion .isOlderThan (new VersionNumber ("1.13" ))) {
155+ if (!launcher .isUnix ())
156+ throw new AbortException ("The docker version is less than v1.13. Running a 'docker.inside' from inside a Windows container will not work." );
149157 }
150158 } else {
151159 listener .error ("Failed to parse docker version. Please note there is a minimum docker version requirement of v1.7." );
152160 }
153161
154162 FilePath tempDir = tempDir (workspace );
155163 tempDir .mkdirs ();
156- String tmp = tempDir . getRemote ( );
164+ String tmp = getPath ( tempDir );
157165
158166 Map <String , String > volumes = new LinkedHashMap <String , String >();
159167 Collection <String > volumesFromContainers = new LinkedHashSet <String >();
@@ -166,7 +174,11 @@ public static class Execution extends AbstractStepExecutionImpl {
166174 // check if there is any volume which contains the directory
167175 boolean found = false ;
168176 for (String vol : mountedVolumes ) {
169- if (dir .startsWith (vol )) {
177+ boolean dirStartsWithVol = launcher .isUnix ()
178+ ? dir .startsWith (vol ) // Linux
179+ : dir .toLowerCase ().startsWith (vol .toLowerCase ()); // Windows
180+
181+ if (dirStartsWithVol ) {
170182 volumesFromContainers .add (containerId .get ());
171183 found = true ;
172184 break ;
@@ -183,9 +195,10 @@ public static class Execution extends AbstractStepExecutionImpl {
183195 volumes .put (tmp , tmp );
184196 }
185197
186- container = dockerClient .run (env , step .image , step .args , ws , volumes , volumesFromContainers , envReduced , dockerClient .whoAmI (), /* expected to hang until killed */ "cat" );
198+ String command = launcher .isUnix () ? "cat" : "cmd.exe" ;
199+ container = dockerClient .run (env , step .image , step .args , ws , volumes , volumesFromContainers , envReduced , dockerClient .whoAmI (), /* expected to hang until killed */ command );
187200 final List <String > ps = dockerClient .listProcess (env , container );
188- if (!ps .contains ("cat" )) {
201+ if (!ps .contains (command )) {
189202 listener .error (
190203 "The container started but didn't run the expected command. " +
191204 "Please double check your ENTRYPOINT does execute the command passed as docker run argument, " +
@@ -202,6 +215,15 @@ public static class Execution extends AbstractStepExecutionImpl {
202215 return false ;
203216 }
204217
218+ private String getPath (FilePath filePath )
219+ throws IOException , InterruptedException {
220+ if (launcher .isUnix ()) {
221+ return filePath .getRemote ();
222+ } else {
223+ return filePath .toURI ().getPath ().substring (1 ).replace ("\\ " , "/" );
224+ }
225+ }
226+
205227 // TODO use 1.652 use WorkspaceList.tempDir
206228 private static FilePath tempDir (FilePath ws ) {
207229 return ws .sibling (ws .getName () + System .getProperty (WorkspaceList .class .getName (), "@" ) + "tmp" );
0 commit comments