We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
_fixPermissions isn't being used in dockerCall 
From the code:
```
# We call this explicitly later on in this function, but we defer it as well to handle unexpected job failure.
    job.defer(_fixPermissions, tool, workDir)
```
Then it is never explicitly called. Things are mostly working OK right now since most of our Dockerized tools have redundant chown functionality builtin. 
_fixPermissions isn't being used in dockerCall 
From the code:
```
# We call this explicitly later on in this function, but we defer it as well to handle unexpected job failure.
    job.defer(_fixPermissions, tool, workDir)
```
Then it is never explicitly called. Things are mostly working OK right now since most of our Dockerized tools have redundant chown functionality builtin. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/toil/lib/docker.py`
Content:
```
1 """
2     Module for calling Docker. Assumes `docker` is on the PATH.
3 
4     Contains two user-facing functions: dockerCall and dockerCheckOutput
5 
6     Uses Toil's defer functionality to ensure containers are shutdown even in case of job or pipeline failure
7 
8     Example of using dockerCall in a Toil pipeline to index a FASTA file with SAMtools:
9         def toil_job(job):
10             work_dir = job.fileStore.getLocalTempDir()
11             path = job.fileStore.readGlobalFile(ref_id, os.path.join(work_dir, 'ref.fasta')
12             parameters = ['faidx', path]
13             dockerCall(job, tool='quay.io/ucgc_cgl/samtools:latest', work_dir=work_dir, parameters=parameters)
14 """
15 import base64
16 import logging
17 import subprocess
18 import pipes
19 import os
20 from bd2k.util.exceptions import require
21 
22 _logger = logging.getLogger(__name__)
23 
24 
25 def dockerCall(job,
26                tool,
27                parameters=None,
28                workDir=None,
29                dockerParameters=None,
30                outfile=None,
31                defer=None):
32     """
33     Throws CalledProcessorError if the Docker invocation returns a non-zero exit code
34     This function blocks until the subprocess call to Docker returns
35 
36     :param toil.Job.job job: The Job instance for the calling function.
37     :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools:latest).
38     :param list[str] parameters: Command line arguments to be passed to the tool.
39            If list of lists: list[list[str]], then treat as successive commands chained with pipe.
40     :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data
41     :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,
42             `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.
43              These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.
44     :param file outfile: Pipe output of Docker call to file handle
45     :param int defer: What action should be taken on the container upon job completion?
46            FORGO (0) will leave the container untouched.
47            STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).
48            RM (2) will stop the container and then forcefully remove it from the system
49            using `docker rm -f`. This is the default behavior if defer is set to None.
50     """
51     _docker(job, tool=tool, parameters=parameters, workDir=workDir, dockerParameters=dockerParameters,
52             outfile=outfile, checkOutput=False, defer=defer)
53 
54 
55 def dockerCheckOutput(job,
56                       tool,
57                       parameters=None,
58                       workDir=None,
59                       dockerParameters=None,
60                       defer=None):
61     """
62     Returns the stdout from the Docker invocation (via subprocess.check_output)
63     Throws CalledProcessorError if the Docker invocation returns a non-zero exit code
64     This function blocks until the subprocess call to Docker returns
65 
66     :param toil.Job.job job: The Job instance for the calling function.
67     :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools:latest).
68     :param list[str] parameters: Command line arguments to be passed to the tool.
69            If list of lists: list[list[str]], then treat as successive commands chained with pipe.
70     :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data
71     :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,
72             `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.
73              These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.
74     :param int defer: What action should be taken on the container upon job completion?
75            FORGO (0) will leave the container untouched.
76            STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).
77            RM (2) will stop the container and then forcefully remove it from the system
78            using `docker rm -f`. This is the default behavior if defer is set to None.
79     :returns: Stdout from the docker call
80     :rtype: str
81     """
82     return _docker(job, tool=tool, parameters=parameters, workDir=workDir,
83                    dockerParameters=dockerParameters, checkOutput=True, defer=defer)
84 
85 
86 def _docker(job,
87             tool,
88             parameters=None,
89             workDir=None,
90             dockerParameters=None,
91             outfile=None,
92             checkOutput=False,
93             defer=None):
94     """
95     :param toil.Job.job job: The Job instance for the calling function.
96     :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools).
97     :param list[str] parameters: Command line arguments to be passed to the tool.
98            If list of lists: list[list[str]], then treat as successive commands chained with pipe.
99     :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data
100     :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,
101             `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.
102              These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.
103     :param file outfile: Pipe output of Docker call to file handle
104     :param bool checkOutput: When True, this function returns docker's output.
105     :param int defer: What action should be taken on the container upon job completion?
106            FORGO (0) will leave the container untouched.
107            STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).
108            RM (2) will stop the container and then forcefully remove it from the system
109            using `docker rm -f`. This is the default behavior if defer is set to None.
110     """
111     if parameters is None:
112         parameters = []
113     if workDir is None:
114         workDir = os.getcwd()
115 
116     # Setup the outgoing subprocess call for docker
117     baseDockerCall = ['docker', 'run']
118     if dockerParameters:
119         baseDockerCall += dockerParameters
120     else:
121         baseDockerCall += ['--rm', '--log-driver', 'none', '-v',
122                            os.path.abspath(workDir) + ':/data']
123 
124     # Ensure the user has passed a valid value for defer
125     require(defer in (None, FORGO, STOP, RM),
126             'Please provide a valid value for defer.')
127 
128     # Get container name which is needed for _dockerKill
129     try:
130         if any('--name' in x for x in baseDockerCall):
131             if any('--name=' in x for x in baseDockerCall):
132                 containerName = [x.split('=')[1] for x in baseDockerCall if '--name' in x][0]
133             else:
134                 containerName = baseDockerCall[baseDockerCall.index('--name') + 1]
135         else:
136             containerName = _getContainerName(job)
137     except ValueError:
138         containerName = _getContainerName(job)
139         baseDockerCall.extend(['--name', containerName])
140     except IndexError:
141         raise RuntimeError("Couldn't parse Docker's `--name=` option, check parameters: " + str(dockerParameters))
142 
143     # Defer the container on-exit action
144     if '--rm' in baseDockerCall and defer is None:
145         defer = RM
146     if '--rm' in baseDockerCall and defer is not RM:
147         _logger.warn('--rm being passed to docker call but defer not set to dockerCall.RM, defer set to: ' + str(defer))
148     job.defer(_dockerKill, containerName, action=defer)
149     # Defer the permission fixing function which will run after this job concludes.
150     # We call this explicitly later on in this function, but we defer it as well to handle unexpected job failure.
151     job.defer(_fixPermissions, tool, workDir)
152 
153     # Make subprocess call
154 
155     # If parameters is list of lists, treat each list as separate command and chain with pipes
156     if len(parameters) > 0 and type(parameters[0]) is list:
157         # When piping, all arguments now get merged into a single string to bash -c.
158         # We try to support spaces in paths by wrapping them all in quotes first.
159         chain_params = [' '.join(p) for p in [map(pipes.quote, q) for q in parameters]]
160         call = baseDockerCall + ['--entrypoint', '/bin/bash',  tool, '-c', ' | '.join(chain_params)]
161     else:
162         call = baseDockerCall + [tool] + parameters
163     _logger.info("Calling docker with " + repr(call))
164 
165     if outfile:
166         subprocess.check_call(call, stdout=outfile)
167     else:
168         if checkOutput:
169             return subprocess.check_output(call)
170         else:
171             subprocess.check_call(call)
172 
173 
174 FORGO = 0
175 STOP = 1
176 RM = 2
177 
178 
179 def _dockerKill(containerName, action):
180     """
181     Kills the specified container.
182     :param str containerName: The name of the container created by docker_call
183     :param int action: What action should be taken on the container?  See `defer=` in
184            :func:`docker_call`
185     """
186     running = _containerIsRunning(containerName)
187     if running is None:
188         # This means that the container doesn't exist.  We will see this if the container was run
189         # with --rm and has already exited before this call.
190         _logger.info('The container with name "%s" appears to have already been removed.  Nothing to '
191                   'do.', containerName)
192     else:
193         if action in (None, FORGO):
194             _logger.info('The container with name %s continues to exist as we were asked to forgo a '
195                       'post-job action on it.', containerName)
196         else:
197             _logger.info('The container with name %s exists. Running user-specified defer functions.',
198                          containerName)
199             if running and action >= STOP:
200                 _logger.info('Stopping container "%s".', containerName)
201                 subprocess.check_call(['docker', 'stop', containerName])
202             else:
203                 _logger.info('The container "%s" was not found to be running.', containerName)
204             if action >= RM:
205                 # If the container was run with --rm, then stop will most likely remove the
206                 # container.  We first check if it is running then remove it.
207                 running = _containerIsRunning(containerName)
208                 if running is not None:
209                     _logger.info('Removing container "%s".', containerName)
210                     subprocess.check_call(['docker', 'rm', '-f', containerName])
211                 else:
212                     _logger.info('The container "%s" was not found on the system.  Nothing to remove.',
213                                  containerName)
214 
215 
216 def _fixPermissions(tool, workDir):
217     """
218     Fix permission of a mounted Docker directory by reusing the tool to change ownership.
219     Docker natively runs as a root inside the container, and files written to the
220     mounted directory are implicitly owned by root.
221 
222     :param list baseDockerCall: Docker run parameters
223     :param str tool: Name of tool
224     :param str workDir: Path of work directory to recursively chown
225     """
226     baseDockerCall = ['docker', 'run', '--log-driver=none',
227                       '-v', os.path.abspath(workDir) + ':/data', '--rm', '--entrypoint=chown']
228     stat = os.stat(workDir)
229     command = baseDockerCall + [tool] + ['-R', '{}:{}'.format(stat.st_uid, stat.st_gid), '/data']
230     subprocess.check_call(command)
231 
232 
233 def _getContainerName(job):
234     return '--'.join([str(job),
235                       base64.b64encode(os.urandom(9), '-_')]).replace("'", '').replace('_', '')
236 
237 
238 def _containerIsRunning(container_name):
239     """
240     Checks whether the container is running or not.
241     :param container_name: Name of the container being checked.
242     :returns: True if running, False if not running, None if the container doesn't exist.
243     :rtype: bool
244     """
245     try:
246         output = subprocess.check_output(['docker', 'inspect', '--format', '{{.State.Running}}',
247                                           container_name]).strip()
248     except subprocess.CalledProcessError:
249         # This will be raised if the container didn't exist.
250         _logger.debug("'docker inspect' failed. Assuming container %s doesn't exist.", container_name,
251                    exc_info=True)
252         return None
253     if output == 'true':
254         return True
255     elif output == 'false':
256         return False
257     else:
258         raise RuntimeError("Got unexpected value for State.Running (%s)" % output)
259
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/toil/lib/docker.py b/src/toil/lib/docker.py
--- a/src/toil/lib/docker.py
+++ b/src/toil/lib/docker.py
@@ -164,11 +164,16 @@
 
     if outfile:
         subprocess.check_call(call, stdout=outfile)
+        _fixPermissions(tool=tool, workDir=workDir)
     else:
         if checkOutput:
-            return subprocess.check_output(call)
+            output = subprocess.check_output(call)
+            _fixPermissions(tool=tool, workDir=workDir)
+            return output
+
         else:
             subprocess.check_call(call)
+            _fixPermissions(tool=tool, workDir=workDir)
 
 
 FORGO = 0

{"golden_diff": "diff --git a/src/toil/lib/docker.py b/src/toil/lib/docker.py\n--- a/src/toil/lib/docker.py\n+++ b/src/toil/lib/docker.py\n@@ -164,11 +164,16 @@\n \n     if outfile:\n         subprocess.check_call(call, stdout=outfile)\n+        _fixPermissions(tool=tool, workDir=workDir)\n     else:\n         if checkOutput:\n-            return subprocess.check_output(call)\n+            output = subprocess.check_output(call)\n+            _fixPermissions(tool=tool, workDir=workDir)\n+            return output\n+\n         else:\n             subprocess.check_call(call)\n+            _fixPermissions(tool=tool, workDir=workDir)\n \n \n FORGO = 0\n", "issue": "_fixPermissions isn't being used in dockerCall \nFrom the code:\r\n```\r\n# We call this explicitly later on in this function, but we defer it as well to handle unexpected job failure.\r\n    job.defer(_fixPermissions, tool, workDir)\r\n```\r\nThen it is never explicitly called. Things are mostly working OK right now since most of our Dockerized tools have redundant chown functionality builtin. \n_fixPermissions isn't being used in dockerCall \nFrom the code:\r\n```\r\n# We call this explicitly later on in this function, but we defer it as well to handle unexpected job failure.\r\n    job.defer(_fixPermissions, tool, workDir)\r\n```\r\nThen it is never explicitly called. Things are mostly working OK right now since most of our Dockerized tools have redundant chown functionality builtin. \n", "before_files": [{"content": "\"\"\"\n    Module for calling Docker. Assumes `docker` is on the PATH.\n\n    Contains two user-facing functions: dockerCall and dockerCheckOutput\n\n    Uses Toil's defer functionality to ensure containers are shutdown even in case of job or pipeline failure\n\n    Example of using dockerCall in a Toil pipeline to index a FASTA file with SAMtools:\n        def toil_job(job):\n            work_dir = job.fileStore.getLocalTempDir()\n            path = job.fileStore.readGlobalFile(ref_id, os.path.join(work_dir, 'ref.fasta')\n            parameters = ['faidx', path]\n            dockerCall(job, tool='quay.io/ucgc_cgl/samtools:latest', work_dir=work_dir, parameters=parameters)\n\"\"\"\nimport base64\nimport logging\nimport subprocess\nimport pipes\nimport os\nfrom bd2k.util.exceptions import require\n\n_logger = logging.getLogger(__name__)\n\n\ndef dockerCall(job,\n               tool,\n               parameters=None,\n               workDir=None,\n               dockerParameters=None,\n               outfile=None,\n               defer=None):\n    \"\"\"\n    Throws CalledProcessorError if the Docker invocation returns a non-zero exit code\n    This function blocks until the subprocess call to Docker returns\n\n    :param toil.Job.job job: The Job instance for the calling function.\n    :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools:latest).\n    :param list[str] parameters: Command line arguments to be passed to the tool.\n           If list of lists: list[list[str]], then treat as successive commands chained with pipe.\n    :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data\n    :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,\n            `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.\n             These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.\n    :param file outfile: Pipe output of Docker call to file handle\n    :param int defer: What action should be taken on the container upon job completion?\n           FORGO (0) will leave the container untouched.\n           STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).\n           RM (2) will stop the container and then forcefully remove it from the system\n           using `docker rm -f`. This is the default behavior if defer is set to None.\n    \"\"\"\n    _docker(job, tool=tool, parameters=parameters, workDir=workDir, dockerParameters=dockerParameters,\n            outfile=outfile, checkOutput=False, defer=defer)\n\n\ndef dockerCheckOutput(job,\n                      tool,\n                      parameters=None,\n                      workDir=None,\n                      dockerParameters=None,\n                      defer=None):\n    \"\"\"\n    Returns the stdout from the Docker invocation (via subprocess.check_output)\n    Throws CalledProcessorError if the Docker invocation returns a non-zero exit code\n    This function blocks until the subprocess call to Docker returns\n\n    :param toil.Job.job job: The Job instance for the calling function.\n    :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools:latest).\n    :param list[str] parameters: Command line arguments to be passed to the tool.\n           If list of lists: list[list[str]], then treat as successive commands chained with pipe.\n    :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data\n    :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,\n            `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.\n             These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.\n    :param int defer: What action should be taken on the container upon job completion?\n           FORGO (0) will leave the container untouched.\n           STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).\n           RM (2) will stop the container and then forcefully remove it from the system\n           using `docker rm -f`. This is the default behavior if defer is set to None.\n    :returns: Stdout from the docker call\n    :rtype: str\n    \"\"\"\n    return _docker(job, tool=tool, parameters=parameters, workDir=workDir,\n                   dockerParameters=dockerParameters, checkOutput=True, defer=defer)\n\n\ndef _docker(job,\n            tool,\n            parameters=None,\n            workDir=None,\n            dockerParameters=None,\n            outfile=None,\n            checkOutput=False,\n            defer=None):\n    \"\"\"\n    :param toil.Job.job job: The Job instance for the calling function.\n    :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools).\n    :param list[str] parameters: Command line arguments to be passed to the tool.\n           If list of lists: list[list[str]], then treat as successive commands chained with pipe.\n    :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data\n    :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,\n            `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.\n             These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.\n    :param file outfile: Pipe output of Docker call to file handle\n    :param bool checkOutput: When True, this function returns docker's output.\n    :param int defer: What action should be taken on the container upon job completion?\n           FORGO (0) will leave the container untouched.\n           STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).\n           RM (2) will stop the container and then forcefully remove it from the system\n           using `docker rm -f`. This is the default behavior if defer is set to None.\n    \"\"\"\n    if parameters is None:\n        parameters = []\n    if workDir is None:\n        workDir = os.getcwd()\n\n    # Setup the outgoing subprocess call for docker\n    baseDockerCall = ['docker', 'run']\n    if dockerParameters:\n        baseDockerCall += dockerParameters\n    else:\n        baseDockerCall += ['--rm', '--log-driver', 'none', '-v',\n                           os.path.abspath(workDir) + ':/data']\n\n    # Ensure the user has passed a valid value for defer\n    require(defer in (None, FORGO, STOP, RM),\n            'Please provide a valid value for defer.')\n\n    # Get container name which is needed for _dockerKill\n    try:\n        if any('--name' in x for x in baseDockerCall):\n            if any('--name=' in x for x in baseDockerCall):\n                containerName = [x.split('=')[1] for x in baseDockerCall if '--name' in x][0]\n            else:\n                containerName = baseDockerCall[baseDockerCall.index('--name') + 1]\n        else:\n            containerName = _getContainerName(job)\n    except ValueError:\n        containerName = _getContainerName(job)\n        baseDockerCall.extend(['--name', containerName])\n    except IndexError:\n        raise RuntimeError(\"Couldn't parse Docker's `--name=` option, check parameters: \" + str(dockerParameters))\n\n    # Defer the container on-exit action\n    if '--rm' in baseDockerCall and defer is None:\n        defer = RM\n    if '--rm' in baseDockerCall and defer is not RM:\n        _logger.warn('--rm being passed to docker call but defer not set to dockerCall.RM, defer set to: ' + str(defer))\n    job.defer(_dockerKill, containerName, action=defer)\n    # Defer the permission fixing function which will run after this job concludes.\n    # We call this explicitly later on in this function, but we defer it as well to handle unexpected job failure.\n    job.defer(_fixPermissions, tool, workDir)\n\n    # Make subprocess call\n\n    # If parameters is list of lists, treat each list as separate command and chain with pipes\n    if len(parameters) > 0 and type(parameters[0]) is list:\n        # When piping, all arguments now get merged into a single string to bash -c.\n        # We try to support spaces in paths by wrapping them all in quotes first.\n        chain_params = [' '.join(p) for p in [map(pipes.quote, q) for q in parameters]]\n        call = baseDockerCall + ['--entrypoint', '/bin/bash',  tool, '-c', ' | '.join(chain_params)]\n    else:\n        call = baseDockerCall + [tool] + parameters\n    _logger.info(\"Calling docker with \" + repr(call))\n\n    if outfile:\n        subprocess.check_call(call, stdout=outfile)\n    else:\n        if checkOutput:\n            return subprocess.check_output(call)\n        else:\n            subprocess.check_call(call)\n\n\nFORGO = 0\nSTOP = 1\nRM = 2\n\n\ndef _dockerKill(containerName, action):\n    \"\"\"\n    Kills the specified container.\n    :param str containerName: The name of the container created by docker_call\n    :param int action: What action should be taken on the container?  See `defer=` in\n           :func:`docker_call`\n    \"\"\"\n    running = _containerIsRunning(containerName)\n    if running is None:\n        # This means that the container doesn't exist.  We will see this if the container was run\n        # with --rm and has already exited before this call.\n        _logger.info('The container with name \"%s\" appears to have already been removed.  Nothing to '\n                  'do.', containerName)\n    else:\n        if action in (None, FORGO):\n            _logger.info('The container with name %s continues to exist as we were asked to forgo a '\n                      'post-job action on it.', containerName)\n        else:\n            _logger.info('The container with name %s exists. Running user-specified defer functions.',\n                         containerName)\n            if running and action >= STOP:\n                _logger.info('Stopping container \"%s\".', containerName)\n                subprocess.check_call(['docker', 'stop', containerName])\n            else:\n                _logger.info('The container \"%s\" was not found to be running.', containerName)\n            if action >= RM:\n                # If the container was run with --rm, then stop will most likely remove the\n                # container.  We first check if it is running then remove it.\n                running = _containerIsRunning(containerName)\n                if running is not None:\n                    _logger.info('Removing container \"%s\".', containerName)\n                    subprocess.check_call(['docker', 'rm', '-f', containerName])\n                else:\n                    _logger.info('The container \"%s\" was not found on the system.  Nothing to remove.',\n                                 containerName)\n\n\ndef _fixPermissions(tool, workDir):\n    \"\"\"\n    Fix permission of a mounted Docker directory by reusing the tool to change ownership.\n    Docker natively runs as a root inside the container, and files written to the\n    mounted directory are implicitly owned by root.\n\n    :param list baseDockerCall: Docker run parameters\n    :param str tool: Name of tool\n    :param str workDir: Path of work directory to recursively chown\n    \"\"\"\n    baseDockerCall = ['docker', 'run', '--log-driver=none',\n                      '-v', os.path.abspath(workDir) + ':/data', '--rm', '--entrypoint=chown']\n    stat = os.stat(workDir)\n    command = baseDockerCall + [tool] + ['-R', '{}:{}'.format(stat.st_uid, stat.st_gid), '/data']\n    subprocess.check_call(command)\n\n\ndef _getContainerName(job):\n    return '--'.join([str(job),\n                      base64.b64encode(os.urandom(9), '-_')]).replace(\"'\", '').replace('_', '')\n\n\ndef _containerIsRunning(container_name):\n    \"\"\"\n    Checks whether the container is running or not.\n    :param container_name: Name of the container being checked.\n    :returns: True if running, False if not running, None if the container doesn't exist.\n    :rtype: bool\n    \"\"\"\n    try:\n        output = subprocess.check_output(['docker', 'inspect', '--format', '{{.State.Running}}',\n                                          container_name]).strip()\n    except subprocess.CalledProcessError:\n        # This will be raised if the container didn't exist.\n        _logger.debug(\"'docker inspect' failed. Assuming container %s doesn't exist.\", container_name,\n                   exc_info=True)\n        return None\n    if output == 'true':\n        return True\n    elif output == 'false':\n        return False\n    else:\n        raise RuntimeError(\"Got unexpected value for State.Running (%s)\" % output)\n", "path": "src/toil/lib/docker.py"}], "after_files": [{"content": "\"\"\"\n    Module for calling Docker. Assumes `docker` is on the PATH.\n\n    Contains two user-facing functions: dockerCall and dockerCheckOutput\n\n    Uses Toil's defer functionality to ensure containers are shutdown even in case of job or pipeline failure\n\n    Example of using dockerCall in a Toil pipeline to index a FASTA file with SAMtools:\n        def toil_job(job):\n            work_dir = job.fileStore.getLocalTempDir()\n            path = job.fileStore.readGlobalFile(ref_id, os.path.join(work_dir, 'ref.fasta')\n            parameters = ['faidx', path]\n            dockerCall(job, tool='quay.io/ucgc_cgl/samtools:latest', work_dir=work_dir, parameters=parameters)\n\"\"\"\nimport base64\nimport logging\nimport subprocess\nimport pipes\nimport os\nfrom bd2k.util.exceptions import require\n\n_logger = logging.getLogger(__name__)\n\n\ndef dockerCall(job,\n               tool,\n               parameters=None,\n               workDir=None,\n               dockerParameters=None,\n               outfile=None,\n               defer=None):\n    \"\"\"\n    Throws CalledProcessorError if the Docker invocation returns a non-zero exit code\n    This function blocks until the subprocess call to Docker returns\n\n    :param toil.Job.job job: The Job instance for the calling function.\n    :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools:latest).\n    :param list[str] parameters: Command line arguments to be passed to the tool.\n           If list of lists: list[list[str]], then treat as successive commands chained with pipe.\n    :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data\n    :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,\n            `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.\n             These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.\n    :param file outfile: Pipe output of Docker call to file handle\n    :param int defer: What action should be taken on the container upon job completion?\n           FORGO (0) will leave the container untouched.\n           STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).\n           RM (2) will stop the container and then forcefully remove it from the system\n           using `docker rm -f`. This is the default behavior if defer is set to None.\n    \"\"\"\n    _docker(job, tool=tool, parameters=parameters, workDir=workDir, dockerParameters=dockerParameters,\n            outfile=outfile, checkOutput=False, defer=defer)\n\n\ndef dockerCheckOutput(job,\n                      tool,\n                      parameters=None,\n                      workDir=None,\n                      dockerParameters=None,\n                      defer=None):\n    \"\"\"\n    Returns the stdout from the Docker invocation (via subprocess.check_output)\n    Throws CalledProcessorError if the Docker invocation returns a non-zero exit code\n    This function blocks until the subprocess call to Docker returns\n\n    :param toil.Job.job job: The Job instance for the calling function.\n    :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools:latest).\n    :param list[str] parameters: Command line arguments to be passed to the tool.\n           If list of lists: list[list[str]], then treat as successive commands chained with pipe.\n    :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data\n    :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,\n            `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.\n             These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.\n    :param int defer: What action should be taken on the container upon job completion?\n           FORGO (0) will leave the container untouched.\n           STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).\n           RM (2) will stop the container and then forcefully remove it from the system\n           using `docker rm -f`. This is the default behavior if defer is set to None.\n    :returns: Stdout from the docker call\n    :rtype: str\n    \"\"\"\n    return _docker(job, tool=tool, parameters=parameters, workDir=workDir,\n                   dockerParameters=dockerParameters, checkOutput=True, defer=defer)\n\n\ndef _docker(job,\n            tool,\n            parameters=None,\n            workDir=None,\n            dockerParameters=None,\n            outfile=None,\n            checkOutput=False,\n            defer=None):\n    \"\"\"\n    :param toil.Job.job job: The Job instance for the calling function.\n    :param str tool: Name of the Docker image to be used (e.g. quay.io/ucsc_cgl/samtools).\n    :param list[str] parameters: Command line arguments to be passed to the tool.\n           If list of lists: list[list[str]], then treat as successive commands chained with pipe.\n    :param str workDir: Directory to mount into the container via `-v`. Destination convention is /data\n    :param list[str] dockerParameters: Parameters to pass to Docker. Default parameters are `--rm`,\n            `--log-driver none`, and the mountpoint `-v work_dir:/data` where /data is the destination convention.\n             These defaults are removed if docker_parmaters is passed, so be sure to pass them if they are desired.\n    :param file outfile: Pipe output of Docker call to file handle\n    :param bool checkOutput: When True, this function returns docker's output.\n    :param int defer: What action should be taken on the container upon job completion?\n           FORGO (0) will leave the container untouched.\n           STOP (1) will attempt to stop the container with `docker stop` (useful for debugging).\n           RM (2) will stop the container and then forcefully remove it from the system\n           using `docker rm -f`. This is the default behavior if defer is set to None.\n    \"\"\"\n    if parameters is None:\n        parameters = []\n    if workDir is None:\n        workDir = os.getcwd()\n\n    # Setup the outgoing subprocess call for docker\n    baseDockerCall = ['docker', 'run']\n    if dockerParameters:\n        baseDockerCall += dockerParameters\n    else:\n        baseDockerCall += ['--rm', '--log-driver', 'none', '-v',\n                           os.path.abspath(workDir) + ':/data']\n\n    # Ensure the user has passed a valid value for defer\n    require(defer in (None, FORGO, STOP, RM),\n            'Please provide a valid value for defer.')\n\n    # Get container name which is needed for _dockerKill\n    try:\n        if any('--name' in x for x in baseDockerCall):\n            if any('--name=' in x for x in baseDockerCall):\n                containerName = [x.split('=')[1] for x in baseDockerCall if '--name' in x][0]\n            else:\n                containerName = baseDockerCall[baseDockerCall.index('--name') + 1]\n        else:\n            containerName = _getContainerName(job)\n    except ValueError:\n        containerName = _getContainerName(job)\n        baseDockerCall.extend(['--name', containerName])\n    except IndexError:\n        raise RuntimeError(\"Couldn't parse Docker's `--name=` option, check parameters: \" + str(dockerParameters))\n\n    # Defer the container on-exit action\n    if '--rm' in baseDockerCall and defer is None:\n        defer = RM\n    if '--rm' in baseDockerCall and defer is not RM:\n        _logger.warn('--rm being passed to docker call but defer not set to dockerCall.RM, defer set to: ' + str(defer))\n    job.defer(_dockerKill, containerName, action=defer)\n    # Defer the permission fixing function which will run after this job concludes.\n    # We call this explicitly later on in this function, but we defer it as well to handle unexpected job failure.\n    job.defer(_fixPermissions, tool, workDir)\n\n    # Make subprocess call\n\n    # If parameters is list of lists, treat each list as separate command and chain with pipes\n    if len(parameters) > 0 and type(parameters[0]) is list:\n        # When piping, all arguments now get merged into a single string to bash -c.\n        # We try to support spaces in paths by wrapping them all in quotes first.\n        chain_params = [' '.join(p) for p in [map(pipes.quote, q) for q in parameters]]\n        call = baseDockerCall + ['--entrypoint', '/bin/bash',  tool, '-c', ' | '.join(chain_params)]\n    else:\n        call = baseDockerCall + [tool] + parameters\n    _logger.info(\"Calling docker with \" + repr(call))\n\n    if outfile:\n        subprocess.check_call(call, stdout=outfile)\n        _fixPermissions(tool=tool, workDir=workDir)\n    else:\n        if checkOutput:\n            output = subprocess.check_output(call)\n            _fixPermissions(tool=tool, workDir=workDir)\n            return output\n\n        else:\n            subprocess.check_call(call)\n            _fixPermissions(tool=tool, workDir=workDir)\n\n\nFORGO = 0\nSTOP = 1\nRM = 2\n\n\ndef _dockerKill(containerName, action):\n    \"\"\"\n    Kills the specified container.\n    :param str containerName: The name of the container created by docker_call\n    :param int action: What action should be taken on the container?  See `defer=` in\n           :func:`docker_call`\n    \"\"\"\n    running = _containerIsRunning(containerName)\n    if running is None:\n        # This means that the container doesn't exist.  We will see this if the container was run\n        # with --rm and has already exited before this call.\n        _logger.info('The container with name \"%s\" appears to have already been removed.  Nothing to '\n                  'do.', containerName)\n    else:\n        if action in (None, FORGO):\n            _logger.info('The container with name %s continues to exist as we were asked to forgo a '\n                      'post-job action on it.', containerName)\n        else:\n            _logger.info('The container with name %s exists. Running user-specified defer functions.',\n                         containerName)\n            if running and action >= STOP:\n                _logger.info('Stopping container \"%s\".', containerName)\n                subprocess.check_call(['docker', 'stop', containerName])\n            else:\n                _logger.info('The container \"%s\" was not found to be running.', containerName)\n            if action >= RM:\n                # If the container was run with --rm, then stop will most likely remove the\n                # container.  We first check if it is running then remove it.\n                running = _containerIsRunning(containerName)\n                if running is not None:\n                    _logger.info('Removing container \"%s\".', containerName)\n                    subprocess.check_call(['docker', 'rm', '-f', containerName])\n                else:\n                    _logger.info('The container \"%s\" was not found on the system.  Nothing to remove.',\n                                 containerName)\n\n\ndef _fixPermissions(tool, workDir):\n    \"\"\"\n    Fix permission of a mounted Docker directory by reusing the tool to change ownership.\n    Docker natively runs as a root inside the container, and files written to the\n    mounted directory are implicitly owned by root.\n\n    :param list baseDockerCall: Docker run parameters\n    :param str tool: Name of tool\n    :param str workDir: Path of work directory to recursively chown\n    \"\"\"\n    baseDockerCall = ['docker', 'run', '--log-driver=none',\n                      '-v', os.path.abspath(workDir) + ':/data', '--rm', '--entrypoint=chown']\n    stat = os.stat(workDir)\n    command = baseDockerCall + [tool] + ['-R', '{}:{}'.format(stat.st_uid, stat.st_gid), '/data']\n    subprocess.check_call(command)\n\n\ndef _getContainerName(job):\n    return '--'.join([str(job),\n                      base64.b64encode(os.urandom(9), '-_')]).replace(\"'\", '').replace('_', '')\n\n\ndef _containerIsRunning(container_name):\n    \"\"\"\n    Checks whether the container is running or not.\n    :param container_name: Name of the container being checked.\n    :returns: True if running, False if not running, None if the container doesn't exist.\n    :rtype: bool\n    \"\"\"\n    try:\n        output = subprocess.check_output(['docker', 'inspect', '--format', '{{.State.Running}}',\n                                          container_name]).strip()\n    except subprocess.CalledProcessError:\n        # This will be raised if the container didn't exist.\n        _logger.debug(\"'docker inspect' failed. Assuming container %s doesn't exist.\", container_name,\n                   exc_info=True)\n        return None\n    if output == 'true':\n        return True\n    elif output == 'false':\n        return False\n    else:\n        raise RuntimeError(\"Got unexpected value for State.Running (%s)\" % output)\n", "path": "src/toil/lib/docker.py"}]}