diff --git a/docker/client.py b/docker/client.py index 00ba0de964..33c947203e 100644 --- a/docker/client.py +++ b/docker/client.py @@ -505,7 +505,7 @@ def events(self, since=None, until=None, filters=None, decode=None): decode=decode) def execute(self, container, cmd, detach=False, stdout=True, stderr=True, - stream=False, tty=False): + stream=False, tty=False, exec_id=False): if utils.compare_version('1.15', self._version) < 0: raise errors.InvalidVersion('Exec is not supported in API < 1.15') if isinstance(container, dict): @@ -527,25 +527,34 @@ def execute(self, container, cmd, detach=False, stdout=True, stderr=True, # create the command url = self._url('/containers/{0}/exec'.format(container)) - res = self._post_json(url, data=data) - self._raise_for_status(res) + cmd_res = self._post_json(url, data=data) + self._raise_for_status(cmd_res) # start the command - cmd_id = res.json().get('Id') + cmd_id = cmd_res.json().get('Id') res = self._post_json(self._url('/exec/{0}/start'.format(cmd_id)), data=data, stream=stream) self._raise_for_status(res) if stream: - return self._multiplexed_response_stream_helper(res) + out = self._multiplexed_response_stream_helper(res) elif six.PY3: - return bytes().join( + out = bytes().join( [x for x in self._multiplexed_buffer_helper(res)] ) else: - return str().join( + out = str().join( [x for x in self._multiplexed_buffer_helper(res)] ) + if exec_id: + return out, cmd_res.json().get('Id') + else: + return out + + def execute_inspect(self, exec_id): + res = self._get(self._url('/exec/{0}/json'.format(exec_id))) + return self._result(res, True) + def export(self, container): if isinstance(container, dict): container = container.get('Id') diff --git a/docs/api.md b/docs/api.md index 5afe5332ff..410e21b3be 100644 --- a/docs/api.md +++ b/docs/api.md @@ -258,7 +258,7 @@ function return a blocking generator you can iterate over to retrieve events as ```python c.execute(container, cmd, detach=False, stdout=True, stderr=True, - stream=False, tty=False) + stream=False, tty=False, exec_id=False) ``` Execute a command in a running container. @@ -279,6 +279,23 @@ running `inspect_container`), unique id or container name. * stream (bool): indicates whether to return a generator which will yield the streaming response in chunks. +* exec_id (bool) whether to return the execution id or not as a second value + +## execute_inspect + +```python +>>> _, exec_id = c.execute(container, cmd, exec_id=True) +>>> info = c.execute_inspect(exec_id) +``` + +Retrieve information about a previous execute call + +**Params**: + +* exec_id (str): an execution unique id + +**Returns** (urllib3.response.HTTPResponse object): The response from the docker daemon + ## export Export the contents of a filesystem as a tar archive to STDOUT diff --git a/tests/integration_test.py b/tests/integration_test.py index eb7a0eb6c6..7040c50ee2 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -1082,6 +1082,21 @@ def runTest(self): self.assertEqual(res, expected) +@unittest.skipIf(not EXEC_DRIVER_IS_NATIVE, 'Exec driver not native') +class TestExecuteCommandInspect(BaseTestCase): + def runTest(self): + container = self.client.create_container('busybox', 'cat', + detach=True, stdin_open=True) + id = container['Id'] + self.client.start(id) + self.tmp_containers.append(id) + + _, exec_id = self.client.execute(id, ['sh', '-c', 'exit 5'], + exec_id=True) + res = self.client.execute_inspect(exec_id) + self.assertEqual(res['ExitCode'], 5) + + class TestRunContainerStreaming(BaseTestCase): def runTest(self): container = self.client.create_container('busybox', '/bin/sh',