@@ -72,78 +72,86 @@ def _check_docker_machine_path(path): # type: (Optional[Text]) -> None
72
72
73
73
74
74
class DockerCommandLineJob (ContainerCommandLineJob ):
75
+ """Runs a CommandLineJob in a sofware container using the Docker engine."""
75
76
76
77
@staticmethod
77
- def get_image (dockerRequirement , # type: Dict[Text, Text]
78
+ def get_image (docker_requirement , # type: Dict[Text, Text]
78
79
pull_image , # type: bool
79
80
force_pull = False , # type: bool
80
81
tmp_outdir_prefix = DEFAULT_TMP_PREFIX # type: Text
81
82
): # type: (...) -> bool
83
+ """
84
+ Retrieve the relevant Docker container image.
85
+
86
+ Returns True upon success
87
+ """
82
88
found = False
83
89
84
- if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement :
85
- dockerRequirement ["dockerImageId" ] = dockerRequirement ["dockerPull" ]
90
+ if "dockerImageId" not in docker_requirement \
91
+ and "dockerPull" in docker_requirement :
92
+ docker_requirement ["dockerImageId" ] = docker_requirement ["dockerPull" ]
86
93
87
94
with found_images_lock :
88
- if dockerRequirement ["dockerImageId" ] in found_images :
95
+ if docker_requirement ["dockerImageId" ] in found_images :
89
96
return True
90
97
91
- for ln in subprocess .check_output (
98
+ for line in subprocess .check_output (
92
99
["docker" , "images" , "--no-trunc" , "--all" ]).decode ('utf-8' ).splitlines ():
93
100
try :
94
- m = re .match (r"^([^ ]+)\s+([^ ]+)\s+([^ ]+)" , ln )
95
- sp = dockerRequirement ["dockerImageId" ].split (":" )
96
- if len (sp ) == 1 :
97
- sp .append ("latest" )
98
- elif len (sp ) == 2 :
99
- # if sp [1] doesn't match valid tag names, it is a part of repository
100
- if not re .match (r'[\w][\w.-]{0,127}' , sp [1 ]):
101
- sp [0 ] = sp [0 ] + ":" + sp [1 ]
102
- sp [1 ] = "latest"
103
- elif len (sp ) == 3 :
104
- if re .match (r'[\w][\w.-]{0,127}' , sp [2 ]):
105
- sp [0 ] = sp [0 ] + ":" + sp [1 ]
106
- sp [1 ] = sp [2 ]
107
- del sp [2 ]
101
+ match = re .match (r"^([^ ]+)\s+([^ ]+)\s+([^ ]+)" , line )
102
+ split = docker_requirement ["dockerImageId" ].split (":" )
103
+ if len (split ) == 1 :
104
+ split .append ("latest" )
105
+ elif len (split ) == 2 :
106
+ # if split [1] doesn't match valid tag names, it is a part of repository
107
+ if not re .match (r'[\w][\w.-]{0,127}' , split [1 ]):
108
+ split [0 ] = split [0 ] + ":" + split [1 ]
109
+ split [1 ] = "latest"
110
+ elif len (split ) == 3 :
111
+ if re .match (r'[\w][\w.-]{0,127}' , split [2 ]):
112
+ split [0 ] = split [0 ] + ":" + split [1 ]
113
+ split [1 ] = split [2 ]
114
+ del split [2 ]
108
115
109
116
# check for repository:tag match or image id match
110
- if (m and
111
- ((sp [0 ] == m .group (1 ) and sp [1 ] == m .group (2 )) or
112
- dockerRequirement ["dockerImageId" ] == m .group (3 ))):
117
+ if (match and
118
+ ((split [0 ] == match .group (1 ) and split [1 ] == match .group (2 )) or
119
+ docker_requirement ["dockerImageId" ] == match .group (3 ))):
113
120
found = True
114
121
break
115
122
except ValueError :
116
123
pass
117
124
118
125
if (force_pull or not found ) and pull_image :
119
126
cmd = [] # type: List[Text]
120
- if "dockerPull" in dockerRequirement :
121
- cmd = ["docker" , "pull" , str (dockerRequirement ["dockerPull" ])]
127
+ if "dockerPull" in docker_requirement :
128
+ cmd = ["docker" , "pull" , str (docker_requirement ["dockerPull" ])]
122
129
_logger .info (Text (cmd ))
123
130
subprocess .check_call (cmd , stdout = sys .stderr )
124
131
found = True
125
- elif "dockerFile" in dockerRequirement :
132
+ elif "dockerFile" in docker_requirement :
126
133
dockerfile_dir = str (tempfile .mkdtemp (prefix = tmp_outdir_prefix ))
127
- with open (os .path .join (dockerfile_dir , "Dockerfile" ), "wb" ) as df :
128
- df .write (dockerRequirement ["dockerFile" ].encode ('utf-8' ))
134
+ with open (os .path .join (
135
+ dockerfile_dir , "Dockerfile" ), "wb" ) as dfile :
136
+ dfile .write (docker_requirement ["dockerFile" ].encode ('utf-8' ))
129
137
cmd = ["docker" , "build" , "--tag=%s" %
130
- str (dockerRequirement ["dockerImageId" ]), dockerfile_dir ]
138
+ str (docker_requirement ["dockerImageId" ]), dockerfile_dir ]
131
139
_logger .info (Text (cmd ))
132
140
subprocess .check_call (cmd , stdout = sys .stderr )
133
141
found = True
134
- elif "dockerLoad" in dockerRequirement :
142
+ elif "dockerLoad" in docker_requirement :
135
143
cmd = ["docker" , "load" ]
136
144
_logger .info (Text (cmd ))
137
- if os .path .exists (dockerRequirement ["dockerLoad" ]):
138
- _logger .info (u"Loading docker image from %s" , dockerRequirement ["dockerLoad" ])
139
- with open (dockerRequirement ["dockerLoad" ], "rb" ) as f :
140
- loadproc = subprocess .Popen (cmd , stdin = f , stdout = sys .stderr )
145
+ if os .path .exists (docker_requirement ["dockerLoad" ]):
146
+ _logger .info (u"Loading docker image from %s" , docker_requirement ["dockerLoad" ])
147
+ with open (docker_requirement ["dockerLoad" ], "rb" ) as dload :
148
+ loadproc = subprocess .Popen (cmd , stdin = dload , stdout = sys .stderr )
141
149
else :
142
150
loadproc = subprocess .Popen (cmd , stdin = subprocess .PIPE ,
143
151
stdout = sys .stderr )
144
152
assert loadproc .stdin is not None
145
- _logger .info (u"Sending GET request to %s" , dockerRequirement ["dockerLoad" ])
146
- req = requests .get (dockerRequirement ["dockerLoad" ], stream = True )
153
+ _logger .info (u"Sending GET request to %s" , docker_requirement ["dockerLoad" ])
154
+ req = requests .get (docker_requirement ["dockerLoad" ], stream = True )
147
155
size = 0
148
156
for chunk in req .iter_content (1024 * 1024 ):
149
157
size += len (chunk )
@@ -152,18 +160,19 @@ def get_image(dockerRequirement, # type: Dict[Text, Text]
152
160
loadproc .stdin .close ()
153
161
rcode = loadproc .wait ()
154
162
if rcode != 0 :
155
- raise WorkflowException ("Docker load returned non-zero exit status %i" % (rcode ))
163
+ raise WorkflowException (
164
+ "Docker load returned non-zero exit status %i" % (rcode ))
156
165
found = True
157
- elif "dockerImport" in dockerRequirement :
158
- cmd = ["docker" , "import" , str (dockerRequirement ["dockerImport" ]),
159
- str (dockerRequirement ["dockerImageId" ])]
166
+ elif "dockerImport" in docker_requirement :
167
+ cmd = ["docker" , "import" , str (docker_requirement ["dockerImport" ]),
168
+ str (docker_requirement ["dockerImageId" ])]
160
169
_logger .info (Text (cmd ))
161
170
subprocess .check_call (cmd , stdout = sys .stderr )
162
171
found = True
163
172
164
173
if found :
165
174
with found_images_lock :
166
- found_images .add (dockerRequirement ["dockerImageId" ])
175
+ found_images .add (docker_requirement ["dockerImageId" ])
167
176
168
177
return found
169
178
@@ -178,10 +187,10 @@ def get_from_requirements(self,
178
187
errmsg = None
179
188
try :
180
189
subprocess .check_output (["docker" , "version" ])
181
- except subprocess .CalledProcessError as e :
182
- errmsg = "Cannot communicate with docker daemon: " + Text (e )
183
- except OSError as e :
184
- errmsg = "'docker' executable not found: " + Text (e )
190
+ except subprocess .CalledProcessError as err :
191
+ errmsg = "Cannot communicate with docker daemon: " + Text (err )
192
+ except OSError as err :
193
+ errmsg = "'docker' executable not found: " + Text (err )
185
194
186
195
if errmsg :
187
196
if req :
@@ -191,18 +200,18 @@ def get_from_requirements(self,
191
200
192
201
if self .get_image (r , pull_image , force_pull , tmp_outdir_prefix ):
193
202
return r ["dockerImageId" ]
194
- else :
195
- if req :
196
- raise WorkflowException (u"Docker image %s not found" % r ["dockerImageId" ])
203
+ if req :
204
+ raise WorkflowException (u"Docker image %s not found" % r ["dockerImageId" ])
197
205
198
206
return None
199
207
200
208
def add_volumes (self , pathmapper , runtime , secret_store = None ):
201
209
# type: (PathMapper, List[Text], SecretStore) -> None
210
+ """Append volume mappings to the runtime option list."""
202
211
203
212
host_outdir = self .outdir
204
213
container_outdir = self .builder .outdir
205
- for src , vol in pathmapper .items ():
214
+ for _ , vol in pathmapper .items ():
206
215
if not vol .staged :
207
216
continue
208
217
host_outdir_tgt = None # type: Optional[Text]
@@ -256,12 +265,12 @@ def add_volumes(self, pathmapper, runtime, secret_store=None):
256
265
else :
257
266
contents = vol .resolved
258
267
if host_outdir_tgt :
259
- with open (host_outdir_tgt , "wb" ) as f :
260
- f .write (contents .encode ("utf-8" ))
268
+ with open (host_outdir_tgt , "wb" ) as file_literal :
269
+ file_literal .write (contents .encode ("utf-8" ))
261
270
else :
262
- fd , createtmp = tempfile .mkstemp (dir = self .tmpdir )
263
- with os .fdopen (fd , "wb" ) as f :
264
- f .write (contents .encode ("utf-8" ))
271
+ tmp_fd , createtmp = tempfile .mkstemp (dir = self .tmpdir )
272
+ with os .fdopen (tmp_fd , "wb" ) as file_literal :
273
+ file_literal .write (contents .encode ("utf-8" ))
265
274
runtime .append (u"--volume=%s:%s:rw" % (
266
275
docker_windows_path_adjust (os .path .realpath (createtmp )),
267
276
vol .target ))
@@ -270,7 +279,12 @@ def create_runtime(self, env, runtimeContext):
270
279
# type: (MutableMapping[Text, Text], RuntimeContext) -> List
271
280
user_space_docker_cmd = runtimeContext .user_space_docker_cmd
272
281
if user_space_docker_cmd :
273
- runtime = [user_space_docker_cmd , u"run" ]
282
+ if 'udocker' in user_space_docker_cmd and not runtimeContext .debug :
283
+ runtime = [user_space_docker_cmd , u"--quiet" , u"run" ]
284
+ # udocker 1.1.1 will output diagnostic messages to stdout
285
+ # without this
286
+ else :
287
+ runtime = [user_space_docker_cmd , u"run" ]
274
288
else :
275
289
runtime = [u"docker" , u"run" , u"-i" ]
276
290
@@ -325,27 +339,38 @@ def create_runtime(self, env, runtimeContext):
325
339
326
340
# add parameters to docker to write a container ID file
327
341
if runtimeContext .record_container_id :
328
- if runtimeContext .cidfile_dir != "" :
329
- if not os .path .isdir (runtimeContext .cidfile_dir ):
330
- _logger .error ("--cidfile-dir %s error:\n %s" , runtimeContext .cidfile_dir ,
331
- runtimeContext .cidfile_dir + " is not a directory or "
332
- "directory doesn't exist, please check it first" )
342
+ cidfile_dir = runtimeContext .cidfile_
343
+ if cidfile_dir != "" :
344
+ if not os .path .isdir (cidfile_dir ):
345
+ _logger .error ("--cidfile-dir %s error:\n %s" , cidfile_dir ,
346
+ cidfile_dir + "%s is not a directory or "
347
+ "directory doesn't exist, please check it first" )
333
348
exit (2 )
334
- if not os .path .exists (runtimeContext . cidfile_dir ):
335
- _logger .error ("--cidfile-dir %s error:\n %s" , runtimeContext . cidfile_dir ,
349
+ if not os .path .exists (cidfile_dir ):
350
+ _logger .error ("--cidfile-dir %s error:\n %s" , cidfile_dir ,
336
351
"directory doesn't exist, please create it first" )
337
352
exit (2 )
338
353
else :
339
354
cidfile_dir = os .getcwd ()
340
355
cidfile_name = datetime .datetime .now ().strftime ("%Y%m%d%H%M%S-%f" ) + ".cid"
341
356
if runtimeContext .cidfile_prefix != "" :
342
357
cidfile_name = str (runtimeContext .cidfile_prefix + "-" + cidfile_name )
343
- cidfile_path = os .path .join (runtimeContext . cidfile_dir , cidfile_name )
358
+ cidfile_path = os .path .join (cidfile_dir , cidfile_name )
344
359
runtime .append (u"--cidfile=%s" % cidfile_path )
345
360
346
- for t , v in self .environment .items ():
347
- runtime .append (u"--env=%s=%s" % (t , v ))
348
-
349
- runtime .append ("--memory=%dm" % self .builder .resources ["ram" ])
361
+ for key , value in self .environment .items ():
362
+ runtime .append (u"--env=%s=%s" % (key , value ))
363
+
364
+ if runtimeContext .strict_memory_limit and not user_space_docker_cmd :
365
+ runtime .append ("--memory=%dm" % self .builder .resources ["ram" ])
366
+ elif not user_space_docker_cmd :
367
+ res_req = self .builder .get_requirement ("ResourceRequirement" )[0 ]
368
+ if res_req and ("ramMin" in res_req or "ramMax" is res_req ):
369
+ _logger .warning (
370
+ u"[job %s] Skipping Docker software container '--memory' limit "
371
+ "despite presence of ResourceRequirement with ramMin "
372
+ "and/or ramMax setting. Consider running with "
373
+ "--strict-memory-limit for increased portability "
374
+ "assurance." , self .name )
350
375
351
376
return runtime
0 commit comments