@@ -25,11 +25,13 @@ def docker_call(tool,
2525 outputs = None ,
2626 docker_parameters = None ,
2727 check_output = False ,
28+ return_stderr = False ,
2829 mock = None ):
2930 """
3031 Calls Docker, passing along parameters and tool.
3132
32- :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools)
33+ :param str OR list[str] tool: Single str or list of names of the Docker images and order to be used
34+ (e.g. quay.io/ucsc_cgl/samtools)
3335 :param list[str] parameters: Command line arguments to be passed to the tool
3436 :param str work_dir: Directory to mount into the container via `-v`. Destination convention is /data
3537 :param bool rm: Set to True to pass `--rm` flag.
@@ -41,8 +43,24 @@ def docker_call(tool,
4143 or a url. The value is only used if mock=True
4244 :param dict[str,str] docker_parameters: Parameters to pass to docker
4345 :param bool check_output: When True, this function returns docker's output
46+ :param bool return_stderr: When True, this function includes stderr in docker's output
4447 :param bool mock: Whether to run in mock mode. If this variable is unset, its value will be determined by
4548 the environment variable.
49+
50+ Piping docker command examples:
51+ Running 'pipe-in-single-container' mode for command
52+ 'head -c 1M </dev/urandom | tee >(md5sum 1>&2) | gzip | gunzip | md5sum 1>&2':
53+ command= ['head -c 1M </dev/urandom | tee >(md5sum 1>&2)', 'gzip -', 'gunzip -', 'md5sum - 1>&2']
54+ work_dir=curr_work_dir
55+ docker_tools=['ubuntu']
56+ stdout = docker_call(work_dir=docker_work_dir, parameters=command, tool=docker_tools, check_output=True)
57+
58+ Running 'pipe-of-containers' mode for command
59+ 'head -c 1M </dev/urandom | tee >(md5sum 1>&2) | gzip | gunzip | md5sum 1>&2':
60+ command= ['head -c 1M </dev/urandom | tee >(md5sum 1>&2)', 'gzip -', 'gunzip -', 'md5sum - 1>&2']
61+ work_dir=curr_work_dir
62+ docker_tools=['ubuntu', 'ubuntu', 'ubuntu', 'ubuntu']
63+ stdout = docker_call(work_dir=docker_work_dir, parameters=command, tool=docker_tools, check_output=True)
4664 """
4765 from toil_scripts .lib .urls import download_url
4866
@@ -83,21 +101,53 @@ def docker_call(tool,
83101 if env :
84102 for e , v in env .iteritems ():
85103 base_docker_call .extend (['-e' , '{}={}' .format (e , v )])
104+
86105 if docker_parameters :
87106 base_docker_call += docker_parameters
88-
89- _log .debug ("Calling docker with %s." % " " .join (base_docker_call + [tool ] + parameters ))
90-
91- docker_call = base_docker_call + [tool ] + parameters
92-
107+
108+ docker_call = []
109+
110+ # Pipe functionality
111+ # each element in the parameters list must represent a sub-pipe command
112+ shell_flag = False # Flag for running subprocess with string command or list command
113+ if isinstance (tool , list ):
114+ command_list = []
115+ if len (tool ) > 1 :
116+ # If tool is a list containing multiple docker container name strings
117+ # then format the docker call in the 'pipe-of-containers' mode
118+ docker_call .extend (base_docker_call + ['--entrypoint /bin/bash' , tool [0 ], '-c \' {}\' ' .format (parameters [0 ])])
119+ for i in xrange (1 , len (tool )):
120+ docker_call .extend (['|' ] + base_docker_call + ['-i --entrypoint /bin/bash' , tool [i ], '-c \' {}\' ' .format (parameters [i ])])
121+ docker_call = " " .join (docker_call )
122+ shell_flag = True
123+ print ("Calling docker with {}" .format (docker_call ))
124+ _log .debug ("Calling docker with %s." % docker_call )
125+
126+ elif len (tool ) == 1 :
127+ # If tool is a list containing a single docker container name string
128+ # then format the docker call in the 'pipe-in-single-container' mode
129+ docker_call .extend (base_docker_call + ['--entrypoint /bin/bash' , tool [0 ], '-c \' {}\' ' .format (" | " .join (parameters ))])
130+ docker_call = " " .join (docker_call )
131+ shell_flag = True
132+ print ("Calling docker with {}" .format (docker_call ))
133+ _log .debug ("Calling docker with %s." % docker_call )
134+
135+ else :
136+ docker_call = base_docker_call + [tool ] + parameters
137+ _log .debug ("Calling docker with %s." % " " .join (docker_call ))
138+
93139 try :
94140 if outfile :
95- subprocess .check_call (docker_call , stdout = outfile )
141+ subprocess .check_call (docker_call , stdout = outfile , shell = shell_flag )
96142 else :
97143 if check_output :
98- return subprocess .check_output (docker_call )
144+ #TODO change stderr
145+ if return_stderr :
146+ return subprocess .check_output (docker_call , shell = shell_flag , stderr = subprocess .STDOUT )
147+ else :
148+ return subprocess .check_output (docker_call , shell = shell_flag )
99149 else :
100- subprocess .check_call (docker_call )
150+ subprocess .check_call (docker_call , shell = shell_flag )
101151 # Fix root ownership of output files
102152 except :
103153 # Panic avoids hiding the exception raised in the try block
@@ -122,5 +172,19 @@ def _fix_permissions(base_docker_call, tool, work_dir):
122172 """
123173 base_docker_call .append ('--entrypoint=chown' )
124174 stat = os .stat (work_dir )
125- command = base_docker_call + [tool ] + ['-R' , '{}:{}' .format (stat .st_uid , stat .st_gid ), '/data' ]
126- subprocess .check_call (command )
175+ command = []
176+ command_list = []
177+ if isinstance (tool , list ):
178+ for i in xrange (len (tool )):
179+ command = base_docker_call + [tool [i ]] + ['-R' , '{}:{}' .format (stat .st_uid , stat .st_gid ), '/data' ]
180+ command_list .append (command )
181+ else :
182+ command = base_docker_call + [tool ] + ['-R' , '{}:{}' .format (stat .st_uid , stat .st_gid ), '/data' ]
183+ command_list .append (command )
184+
185+ for i in xrange (len (command_list )):
186+ subprocess .check_call (command_list [i ])
187+
188+
189+
190+
0 commit comments