Skip to content

Commit 446fbce

Browse files
authored
Merge branch 'master' into windowsdoc
2 parents ed47232 + b4aa8d5 commit 446fbce

File tree

149 files changed

+7307
-1094
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+7307
-1094
lines changed

appveyor.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ test_script:
2626

2727
- "%CMD_IN_ENV% python setup.py test"
2828

29+
branches:
30+
only:
31+
- master

cwltool/builder.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818
from .stdfsaccess import StdFsAccess
1919
from .utils import aslist, get_feature, docker_windows_path_adjust, onWindows
2020

21-
# if six.PY3:
22-
# AvroSchemaFromJSONData = avro.schema.SchemaFromJSONData
23-
# else:
2421
AvroSchemaFromJSONData = avro.schema.make_avsc_object
2522

2623
CONTENT_LIMIT = 64 * 1024

cwltool/docker_uid.py renamed to cwltool/docker_id.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22
from __future__ import absolute_import
33

44
import subprocess
5-
from typing import List, Text
5+
from typing import List, Text, Tuple
66

77

8-
def docker_vm_uid(): # type: () -> int
8+
def docker_vm_id(): # type: () -> Tuple[int, int]
99
"""
10-
Returns the UID of the default docker user inside the VM
10+
Returns the User ID and Group ID of the default docker user inside the VM
1111
1212
When a host is using boot2docker or docker-machine to run docker with
1313
boot2docker.iso (As on Mac OS X), the UID that mounts the shared filesystem
1414
inside the VirtualBox VM is likely different than the user's UID on the host.
15-
:return: The numeric UID (as a string) of the docker account inside
15+
:return: A tuple containing numeric User ID and Group ID of the docker account inside
1616
the boot2docker VM
1717
"""
1818
if boot2docker_running():
19-
return boot2docker_uid()
19+
return boot2docker_id()
2020
elif docker_machine_running():
21-
return docker_machine_uid()
21+
return docker_machine_id()
2222
else:
23-
return None
23+
return (None, None)
2424

2525

2626
def check_output_and_strip(cmd): # type: (List[Text]) -> Text
@@ -95,23 +95,26 @@ def cmd_output_to_int(cmd): # type: (List[Text]) -> int
9595
return None
9696

9797

98-
def boot2docker_uid(): # type: () -> int
98+
def boot2docker_id(): # type: () -> Tuple[int, int]
9999
"""
100-
Gets the UID of the docker user inside a running boot2docker vm
101-
:return: the UID, or None if error (e.g. boot2docker not present or stopped)
100+
Gets the UID and GID of the docker user inside a running boot2docker vm
101+
:return: Tuple (UID, GID), or (None, None) if error (e.g. boot2docker not present or stopped)
102102
"""
103-
return cmd_output_to_int(['boot2docker', 'ssh', 'id', '-u'])
104-
103+
uid = cmd_output_to_int(['boot2docker', 'ssh', 'id', '-u'])
104+
gid = cmd_output_to_int(['boot2docker', 'ssh', 'id', '-g'])
105+
return (uid, gid)
105106

106-
def docker_machine_uid(): # type: () -> int
107+
def docker_machine_id(): # type: () -> Tuple[int, int]
107108
"""
108109
Asks docker-machine for active machine and gets the UID of the docker user
109110
inside the vm
110-
:return: the UID, or None if error (e.g. docker-machine not present or stopped)
111+
:return: tuple (UID, GID), or (None, None) if error (e.g. docker-machine not present or stopped)
111112
"""
112113
machine_name = docker_machine_name()
113-
return cmd_output_to_int(['docker-machine', 'ssh', machine_name, "id -u"])
114+
uid = cmd_output_to_int(['docker-machine', 'ssh', machine_name, "id -u"])
115+
gid = cmd_output_to_int(['docker-machine', 'ssh', machine_name, "id -g"])
116+
return (uid, gid)
114117

115118

116119
if __name__ == '__main__':
117-
print(docker_vm_uid())
120+
print(docker_vm_id())

cwltool/draft2tool.py

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ def revmap_file(builder, outdir, f):
109109
if f["location"].startswith("file://"):
110110
path = convert_pathsep_to_unix(uri_file_path(f["location"]))
111111
revmap_f = builder.pathmapper.reversemap(path)
112-
if revmap_f:
113-
f["location"] = revmap_f[1]
112+
if revmap_f and not builder.pathmapper.mapper(revmap_f[0]).type.startswith("Writable"):
113+
f["basename"] = os.path.basename(path)
114+
f["location"] = revmap_f[0]
114115
elif path == builder.outdir:
115116
f["location"] = outdir
116117
elif path.startswith(builder.outdir):
@@ -181,13 +182,14 @@ def __init__(self, toolpath_object, **kwargs):
181182
def makeJobRunner(self, use_container=True): # type: (Optional[bool]) -> JobBase
182183
dockerReq, _ = self.get_requirement("DockerRequirement")
183184
if not dockerReq and use_container:
184-
default_container = self.find_default_container(self)
185-
if default_container:
186-
self.requirements.insert(0, {
187-
"class": "DockerRequirement",
188-
"dockerPull": default_container
189-
})
190-
dockerReq = self.requirements[0]
185+
if self.find_default_container:
186+
default_container = self.find_default_container(self)
187+
if default_container:
188+
self.requirements.insert(0, {
189+
"class": "DockerRequirement",
190+
"dockerPull": default_container
191+
})
192+
dockerReq = self.requirements[0]
191193

192194
if dockerReq and use_container:
193195
return DockerCommandLineJob()
@@ -203,6 +205,17 @@ def makePathMapper(self, reffiles, stagedir, **kwargs):
203205
# type: (List[Any], Text, **Any) -> PathMapper
204206
return PathMapper(reffiles, kwargs["basedir"], stagedir)
205207

208+
def updatePathmap(self, outdir, pathmap, fn):
209+
# type: (Text, PathMapper, Dict) -> None
210+
if "location" in fn:
211+
pathmap.update(fn["location"], pathmap.mapper(fn["location"]).resolved,
212+
os.path.join(outdir, fn["basename"]),
213+
("Writable" if fn.get("writable") else "") + fn["class"], False)
214+
for sf in fn.get("secondaryFiles", []):
215+
self.updatePathmap(outdir, pathmap, sf)
216+
for ls in fn.get("listing", []):
217+
self.updatePathmap(os.path.join(outdir, fn["basename"]), pathmap, ls)
218+
206219
def job(self,
207220
job_order, # type: Dict[Text, Text]
208221
output_callbacks, # type: Callable[[Any, Any], Any]
@@ -228,9 +241,9 @@ def job(self,
228241

229242
cmdline = flatten(list(map(cachebuilder.generate_arg, cachebuilder.bindings)))
230243
(docker_req, docker_is_req) = self.get_requirement("DockerRequirement")
231-
if docker_req and kwargs.get("use_container") is not False:
244+
if docker_req and kwargs.get("use_container"):
232245
dockerimg = docker_req.get("dockerImageId") or docker_req.get("dockerPull")
233-
elif kwargs.get("default_container", None) is not None and kwargs.get("use_container") is not False:
246+
elif kwargs.get("default_container", None) is not None and kwargs.get("use_container"):
234247
dockerimg = kwargs.get("default_container")
235248

236249
if dockerimg:
@@ -268,7 +281,7 @@ def job(self,
268281
jobcachepending = jobcache + ".pending"
269282

270283
if os.path.isdir(jobcache) and not os.path.isfile(jobcachepending):
271-
if docker_req and kwargs.get("use_container") is not False:
284+
if docker_req and kwargs.get("use_container"):
272285
cachebuilder.outdir = kwargs.get("docker_outdir") or "/var/spool/cwl"
273286
else:
274287
cachebuilder.outdir = jobcache
@@ -327,46 +340,10 @@ def rm_pending_output_callback(output_callbacks, jobcachepending,
327340
builder.pathmapper = self.makePathMapper(reffiles, builder.stagedir, **make_path_mapper_kwargs)
328341
builder.requirements = j.requirements
329342

330-
if _logger.isEnabledFor(logging.DEBUG):
331-
_logger.debug(u"[job %s] path mappings is %s", j.name,
332-
json.dumps({p: builder.pathmapper.mapper(p) for p in builder.pathmapper.files()}, indent=4))
333-
334343
_check_adjust = partial(check_adjust, builder)
335344

336345
visit_class([builder.files, builder.bindings], ("File", "Directory"), _check_adjust)
337346

338-
if self.tool.get("stdin"):
339-
with SourceLine(self.tool, "stdin", validate.ValidationException):
340-
j.stdin = builder.do_eval(self.tool["stdin"])
341-
reffiles.append({"class": "File", "path": j.stdin})
342-
343-
if self.tool.get("stderr"):
344-
with SourceLine(self.tool, "stderr", validate.ValidationException):
345-
j.stderr = builder.do_eval(self.tool["stderr"])
346-
if os.path.isabs(j.stderr) or ".." in j.stderr:
347-
raise validate.ValidationException("stderr must be a relative path, got '%s'" % j.stderr)
348-
349-
if self.tool.get("stdout"):
350-
with SourceLine(self.tool, "stdout", validate.ValidationException):
351-
j.stdout = builder.do_eval(self.tool["stdout"])
352-
if os.path.isabs(j.stdout) or ".." in j.stdout or not j.stdout:
353-
raise validate.ValidationException("stdout must be a relative path, got '%s'" % j.stdout)
354-
355-
if _logger.isEnabledFor(logging.DEBUG):
356-
_logger.debug(u"[job %s] command line bindings is %s", j.name, json.dumps(builder.bindings, indent=4))
357-
358-
dockerReq = self.get_requirement("DockerRequirement")[0]
359-
if dockerReq and kwargs.get("use_container"):
360-
out_prefix = kwargs.get("tmp_outdir_prefix")
361-
j.outdir = kwargs.get("outdir") or tempfile.mkdtemp(prefix=out_prefix)
362-
tmpdir_prefix = kwargs.get('tmpdir_prefix')
363-
j.tmpdir = kwargs.get("tmpdir") or tempfile.mkdtemp(prefix=tmpdir_prefix)
364-
j.stagedir = tempfile.mkdtemp(prefix=tmpdir_prefix)
365-
else:
366-
j.outdir = builder.outdir
367-
j.tmpdir = builder.tmpdir
368-
j.stagedir = builder.stagedir
369-
370347
initialWorkdir = self.get_requirement("InitialWorkDirRequirement")[0]
371348
j.generatefiles = {"class": "Directory", "listing": [], "basename": ""}
372349
if initialWorkdir:
@@ -402,6 +379,45 @@ def rm_pending_output_callback(output_callbacks, jobcachepending,
402379
t["entry"]["writable"] = t.get("writable")
403380
ls[i] = t["entry"]
404381
j.generatefiles[u"listing"] = ls
382+
for l in ls:
383+
self.updatePathmap(builder.outdir, builder.pathmapper, l)
384+
visit_class([builder.files, builder.bindings], ("File", "Directory"), _check_adjust)
385+
386+
if _logger.isEnabledFor(logging.DEBUG):
387+
_logger.debug(u"[job %s] path mappings is %s", j.name,
388+
json.dumps({p: builder.pathmapper.mapper(p) for p in builder.pathmapper.files()}, indent=4))
389+
390+
if self.tool.get("stdin"):
391+
with SourceLine(self.tool, "stdin", validate.ValidationException):
392+
j.stdin = builder.do_eval(self.tool["stdin"])
393+
reffiles.append({"class": "File", "path": j.stdin})
394+
395+
if self.tool.get("stderr"):
396+
with SourceLine(self.tool, "stderr", validate.ValidationException):
397+
j.stderr = builder.do_eval(self.tool["stderr"])
398+
if os.path.isabs(j.stderr) or ".." in j.stderr:
399+
raise validate.ValidationException("stderr must be a relative path, got '%s'" % j.stderr)
400+
401+
if self.tool.get("stdout"):
402+
with SourceLine(self.tool, "stdout", validate.ValidationException):
403+
j.stdout = builder.do_eval(self.tool["stdout"])
404+
if os.path.isabs(j.stdout) or ".." in j.stdout or not j.stdout:
405+
raise validate.ValidationException("stdout must be a relative path, got '%s'" % j.stdout)
406+
407+
if _logger.isEnabledFor(logging.DEBUG):
408+
_logger.debug(u"[job %s] command line bindings is %s", j.name, json.dumps(builder.bindings, indent=4))
409+
410+
dockerReq = self.get_requirement("DockerRequirement")[0]
411+
if dockerReq and kwargs.get("use_container"):
412+
out_prefix = kwargs.get("tmp_outdir_prefix")
413+
j.outdir = kwargs.get("outdir") or tempfile.mkdtemp(prefix=out_prefix)
414+
tmpdir_prefix = kwargs.get('tmpdir_prefix')
415+
j.tmpdir = kwargs.get("tmpdir") or tempfile.mkdtemp(prefix=tmpdir_prefix)
416+
j.stagedir = tempfile.mkdtemp(prefix=tmpdir_prefix)
417+
else:
418+
j.outdir = builder.outdir
419+
j.tmpdir = builder.tmpdir
420+
j.stagedir = builder.stagedir
405421

406422
inplaceUpdateReq = self.get_requirement("http://commonwl.org/cwltool#InplaceUpdateRequirement")[0]
407423

cwltool/job.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from .utils import copytree_with_merge, docker_windows_path_adjust, onWindows
2020
from . import docker
2121
from .builder import Builder
22-
from .docker_uid import docker_vm_uid
22+
from .docker_id import docker_vm_id
2323
from .errors import WorkflowException
2424
from .pathmapper import PathMapper
2525
from .process import (UnsupportedRequirement, empty_subtree, get_feature,
@@ -89,6 +89,7 @@ def deref_links(outputs): # type: (Any) -> None
8989
if outputs.get("class") == "File":
9090
st = os.lstat(outputs["path"])
9191
if stat.S_ISLNK(st.st_mode):
92+
outputs["basename"] = os.path.basename(outputs["path"])
9293
outputs["path"] = os.readlink(outputs["path"])
9394
else:
9495
for v in outputs.values():
@@ -147,7 +148,7 @@ def _setup(self): # type: () -> None
147148

148149
for knownfile in self.pathmapper.files():
149150
p = self.pathmapper.mapper(knownfile)
150-
if p.type == "File" and not os.path.isfile(p[0]):
151+
if p.type == "File" and not os.path.isfile(p[0]) and p.staged:
151152
raise WorkflowException(
152153
u"Input file %s (at %s) not found or is not a regular "
153154
"file." % (knownfile, self.pathmapper.mapper(knownfile)[0]))
@@ -347,14 +348,14 @@ def run(self, pull_image=True, rm_container=True,
347348
env = None # type: MutableMapping[Text, Text]
348349
try:
349350
env = cast(MutableMapping[Text, Text], os.environ)
350-
if docker_req and kwargs.get("use_container") is not False:
351+
if docker_req and kwargs.get("use_container"):
351352
img_id = docker.get_from_requirements(docker_req, True, pull_image)
352353
if img_id is None:
353-
find_default_container = self.builder.find_default_container
354-
default_container = find_default_container and find_default_container()
355-
if default_container:
356-
img_id = default_container
357-
env = cast(MutableMapping[Text, Text], os.environ)
354+
if self.builder.find_default_container:
355+
default_container = self.builder.find_default_container()
356+
if default_container:
357+
img_id = default_container
358+
env = cast(MutableMapping[Text, Text], os.environ)
358359

359360
if docker_req and img_id is None and kwargs.get("use_container"):
360361
raise Exception("Docker image not available")
@@ -390,13 +391,12 @@ def run(self, pull_image=True, rm_container=True,
390391
if self.stdout:
391392
runtime.append("--log-driver=none")
392393

393-
if onWindows(): # windows os dont have getuid or geteuid functions
394-
euid = docker_vm_uid()
395-
else:
396-
euid = docker_vm_uid() or os.geteuid()
394+
euid, egid = docker_vm_id()
395+
if not onWindows(): # MS Windows does not have getuid() or geteuid() functions
396+
euid, egid = euid or os.geteuid(), egid or os.getgid()
397397

398-
if kwargs.get("no_match_user", None) is False and euid is not None:
399-
runtime.append(u"--user=%s" % (euid))
398+
if kwargs.get("no_match_user", None) is False and (euid, egid) != (None, None):
399+
runtime.append(u"--user=%d:%d" % (euid, egid))
400400

401401
if rm_container:
402402
runtime.append(u"--rm")

cwltool/load_tool.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from . import process, update
2323
from .errors import WorkflowException
2424
from .process import Process, shortname
25+
from .update import ALLUPDATES
2526

2627
_logger = logging.getLogger("cwltool")
2728

@@ -161,12 +162,17 @@ def validate_document(document_loader, # type: Loader
161162
if "cwlVersion" in workflowobj:
162163
if not isinstance(workflowobj["cwlVersion"], (str, Text)):
163164
raise Exception("'cwlVersion' must be a string, got %s" % type(workflowobj["cwlVersion"]))
165+
if workflowobj["cwlVersion"] not in list(ALLUPDATES):
166+
# print out all the Supported Versions of cwlVersion
167+
versions = list(ALLUPDATES) # ALLUPDATES is a dict
168+
versions.sort()
169+
raise ValidationException("'cwlVersion' not valid. Supported CWL versions are: \n{}".format("\n".join(versions)))
164170
workflowobj["cwlVersion"] = re.sub(
165171
r"^(?:cwl:|https://w3id.org/cwl/cwl#)", "",
166172
workflowobj["cwlVersion"])
167173
else:
168-
_logger.warning("No cwlVersion found, treating this file as draft-2.")
169-
workflowobj["cwlVersion"] = "draft-2"
174+
raise ValidationException("No cwlVersion found."
175+
"Use the following syntax in your CWL workflow to declare version: cwlVersion: <version>")
170176

171177
if workflowobj["cwlVersion"] == "draft-2":
172178
workflowobj = cast(CommentedMap, cmap(update._draft2toDraft3dev1(

cwltool/main.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,19 @@ def pathToLoc(p):
591591
p["location"] = p["path"]
592592
del p["path"]
593593

594+
def addSizes(p):
595+
if 'location' in p:
596+
try:
597+
p["size"] = os.stat(p["location"][7:]).st_size # strip off file://
598+
except OSError:
599+
pass
600+
elif 'contents' in p:
601+
p["size"] = len(p['contents'])
602+
else:
603+
return # best effort
604+
594605
visit_class(job_order_object, ("File", "Directory"), pathToLoc)
606+
visit_class(job_order_object, ("File"), addSizes)
595607
adjustDirObjs(job_order_object, trim_listing)
596608
normalizeFilesDirs(job_order_object)
597609

@@ -699,7 +711,8 @@ def main(argsl=None, # type: List[str]
699711

700712
# If On windows platform, A default Docker Container is Used if not explicitely provided by user
701713
if onWindows() and not args.default_container:
702-
args.default_container = "ubuntu"
714+
# This docker image is a minimal alpine image with bash installed(size 6 mb). source: https://github.com/frol/docker-alpine-bash
715+
args.default_container = "frolvlad/alpine-bash"
703716

704717
# If caller provided custom arguments, it may be not every expected
705718
# option is set, so fill in no-op defaults to avoid crashing when

cwltool/pathmapper.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def visit_class(rec, cls, op): # type: (Any, Iterable, Union[Callable[..., Any]
3737
"""Apply a function to with "class" in cls."""
3838

3939
if isinstance(rec, dict):
40-
if rec.get("class") in cls:
40+
if "class" in rec and rec.get("class") in cls:
4141
op(rec)
4242
for d in rec:
4343
visit_class(rec[d], cls, op)
@@ -249,3 +249,6 @@ def reversemap(self, target): # type: (Text) -> Tuple[Text, Text]
249249
if v[1] == target:
250250
return (k, v[0])
251251
return None
252+
253+
def update(self, key, resolved, target, type, stage): # type: (Text, Text, Text, Text, bool) -> None
254+
self._pathmap[key] = MapperEnt(resolved, target, type, stage)

0 commit comments

Comments
 (0)