mirror of https://github.com/docker/docker-py.git
				
				
				
			
						commit
						5eeb61a1e5
					
				|  | @ -17,6 +17,7 @@ import os | |||
| import re | ||||
| import shlex | ||||
| import struct | ||||
| import warnings | ||||
| from datetime import datetime | ||||
| 
 | ||||
| import requests | ||||
|  | @ -515,6 +516,17 @@ class Client(requests.Session): | |||
|     @check_resource | ||||
|     def execute(self, container, cmd, detach=False, stdout=True, stderr=True, | ||||
|                 stream=False, tty=False): | ||||
|         warnings.warn( | ||||
|             'Client.execute is being deprecated. Please use exec_create & ' | ||||
|             'exec_start instead', DeprecationWarning | ||||
|         ) | ||||
|         create_res = self.exec_create( | ||||
|             container, cmd, detach, stdout, stderr, tty | ||||
|         ) | ||||
| 
 | ||||
|         return self.exec_start(create_res, detach, tty, stream) | ||||
| 
 | ||||
|     def exec_create(self, container, cmd, stdout=True, stderr=True, tty=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): | ||||
|  | @ -530,18 +542,47 @@ class Client(requests.Session): | |||
|             'AttachStdin': False, | ||||
|             'AttachStdout': stdout, | ||||
|             'AttachStderr': stderr, | ||||
|             'Detach': detach, | ||||
|             'Cmd': cmd | ||||
|         } | ||||
| 
 | ||||
|         # create the command | ||||
|         url = self._url('/containers/{0}/exec'.format(container)) | ||||
|         res = self._post_json(url, data=data) | ||||
|         self._raise_for_status(res) | ||||
|         return self._result(res, True) | ||||
| 
 | ||||
|         # start the command | ||||
|         cmd_id = res.json().get('Id') | ||||
|         res = self._post_json(self._url('/exec/{0}/start'.format(cmd_id)), | ||||
|     def exec_inspect(self, exec_id): | ||||
|         if utils.compare_version('1.15', self._version) < 0: | ||||
|             raise errors.InvalidVersion('Exec is not supported in API < 1.15') | ||||
|         if isinstance(exec_id, dict): | ||||
|             exec_id = exec_id.get('Id') | ||||
|         res = self._get(self._url("/exec/{0}/json".format(exec_id))) | ||||
|         return self._result(res, True) | ||||
| 
 | ||||
|     def exec_resize(self, exec_id, height=None, width=None): | ||||
|         if utils.compare_version('1.15', self._version) < 0: | ||||
|             raise errors.InvalidVersion('Exec is not supported in API < 1.15') | ||||
|         if isinstance(exec_id, dict): | ||||
|             exec_id = exec_id.get('Id') | ||||
|         data = { | ||||
|             'h': height, | ||||
|             'w': width | ||||
|         } | ||||
|         res = self._post_json( | ||||
|             self._url('/exec/{0}/resize'.format(exec_id)), data | ||||
|         ) | ||||
|         res.raise_for_status() | ||||
| 
 | ||||
|     def exec_start(self, exec_id, detach=False, tty=False, stream=False): | ||||
|         if utils.compare_version('1.15', self._version) < 0: | ||||
|             raise errors.InvalidVersion('Exec is not supported in API < 1.15') | ||||
|         if isinstance(exec_id, dict): | ||||
|             exec_id = exec_id.get('Id') | ||||
| 
 | ||||
|         data = { | ||||
|             'Tty': tty, | ||||
|             'Detach': detach | ||||
|         } | ||||
| 
 | ||||
|         res = self._post_json(self._url('/exec/{0}/start'.format(exec_id)), | ||||
|                               data=data, stream=stream) | ||||
|         self._raise_for_status(res) | ||||
|         if stream: | ||||
|  |  | |||
							
								
								
									
										56
									
								
								docs/api.md
								
								
								
								
							
							
						
						
									
										56
									
								
								docs/api.md
								
								
								
								
							|  | @ -256,28 +256,58 @@ function return a blocking generator you can iterate over to retrieve events as | |||
| 
 | ||||
| ## execute | ||||
| 
 | ||||
| ```python | ||||
| c.execute(container, cmd, detach=False, stdout=True, stderr=True, | ||||
|        stream=False, tty=False) | ||||
| ``` | ||||
| This command is deprecated for docker-py >= 1.2.0 ; use `exec_create` and | ||||
| `exec_start` instead. | ||||
| 
 | ||||
| Execute a command in a running container. | ||||
| ## exec_create | ||||
| 
 | ||||
| Sets up an exec instance in a running container. | ||||
| 
 | ||||
| **Params**: | ||||
| 
 | ||||
| * container (str): can be a container dictionary (result of | ||||
| running `inspect_container`), unique id or container name. | ||||
| * container (str): Target container where exec instance will be created | ||||
| * cmd (str or list): Command to be executed | ||||
| * stdout (bool): Attach to stdout of the exec command if true. Default: True | ||||
| * stderr (bool): Attach to stderr of the exec command if true. Default: True | ||||
| * tty (bool): Allocate a pseudo-TTY. Default: False | ||||
| 
 | ||||
| **Returns** (dict): A dictionary with an exec 'Id' key. | ||||
| 
 | ||||
| 
 | ||||
| * cmd (str or list): representing the command and its arguments. | ||||
| ## exec_inspect | ||||
| 
 | ||||
| * detach (bool): flag to `True` will run the process in the background. | ||||
| Return low-level information about an exec command. | ||||
| 
 | ||||
| * stdout (bool): indicates which output streams to read from. | ||||
| * stderr (bool): indicates which output streams to read from. | ||||
| **Params**: | ||||
| 
 | ||||
| * stream (bool): indicates whether to return a generator which will yield | ||||
|   the streaming response in chunks. | ||||
| * exec_id (str): ID of the exec instance | ||||
| 
 | ||||
| **Returns** (dict): Dictionary of values returned by the endpoint. | ||||
| 
 | ||||
| 
 | ||||
| ## exec_resize | ||||
| 
 | ||||
| Resize the tty session used by the specified exec command. | ||||
| 
 | ||||
| **Params**: | ||||
| 
 | ||||
| * exec_id (str): ID of the exec instance | ||||
| * height (int): Height of tty session | ||||
| * width (int): Width of tty session | ||||
| 
 | ||||
| ## exec_start | ||||
| 
 | ||||
| Start a previously set up exec instance. | ||||
| 
 | ||||
| **Params**: | ||||
| 
 | ||||
| * exec_id (str): ID of the exec instance | ||||
| * detach (bool): If true, detach from the exec command. Default: False | ||||
| * tty (bool): Allocate a pseudo-TTY. Default: False | ||||
| * stream (bool): Stream response data | ||||
| 
 | ||||
| **Returns** (generator or str): If `stream=True`, a generator yielding response | ||||
| chunks. A string containing response data otherwise. | ||||
| 
 | ||||
| ## export | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ CURRENT_VERSION = 'v1.18' | |||
| 
 | ||||
| FAKE_CONTAINER_ID = '3cc2351ab11b' | ||||
| FAKE_IMAGE_ID = 'e9aa60c60128' | ||||
| FAKE_EXEC_ID = 'd5d177f121dc' | ||||
| FAKE_IMAGE_NAME = 'test_image' | ||||
| FAKE_TARBALL_PATH = '/path/to/tarball' | ||||
| FAKE_REPO_NAME = 'repo' | ||||
|  | @ -247,13 +248,13 @@ def get_fake_export(): | |||
|     return status_code, response | ||||
| 
 | ||||
| 
 | ||||
| def post_fake_execute(): | ||||
| def post_fake_exec_create(): | ||||
|     status_code = 200 | ||||
|     response = {'Id': FAKE_CONTAINER_ID} | ||||
|     response = {'Id': FAKE_EXEC_ID} | ||||
|     return status_code, response | ||||
| 
 | ||||
| 
 | ||||
| def post_fake_execute_start(): | ||||
| def post_fake_exec_start(): | ||||
|     status_code = 200 | ||||
|     response = (b'\x01\x00\x00\x00\x00\x00\x00\x11bin\nboot\ndev\netc\n' | ||||
|                 b'\x01\x00\x00\x00\x00\x00\x00\x12lib\nmnt\nproc\nroot\n' | ||||
|  | @ -261,6 +262,30 @@ def post_fake_execute_start(): | |||
|     return status_code, response | ||||
| 
 | ||||
| 
 | ||||
| def post_fake_exec_resize(): | ||||
|     status_code = 201 | ||||
|     return status_code, '' | ||||
| 
 | ||||
| 
 | ||||
| def get_fake_exec_inspect(): | ||||
|     return 200, { | ||||
|         'OpenStderr': True, | ||||
|         'OpenStdout': True, | ||||
|         'Container': get_fake_inspect_container()[1], | ||||
|         'Running': False, | ||||
|         'ProcessConfig': { | ||||
|             'arguments': ['hello world'], | ||||
|             'tty': False, | ||||
|             'entrypoint': 'echo', | ||||
|             'privileged': False, | ||||
|             'user': '' | ||||
|         }, | ||||
|         'ExitCode': 0, | ||||
|         'ID': FAKE_EXEC_ID, | ||||
|         'OpenStdin': False | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| def post_fake_stop_container(): | ||||
|     status_code = 200 | ||||
|     response = {'Id': FAKE_CONTAINER_ID} | ||||
|  | @ -393,9 +418,14 @@ fake_responses = { | |||
|     '{1}/{0}/containers/3cc2351ab11b/export'.format(CURRENT_VERSION, prefix): | ||||
|     get_fake_export, | ||||
|     '{1}/{0}/containers/3cc2351ab11b/exec'.format(CURRENT_VERSION, prefix): | ||||
|     post_fake_execute, | ||||
|     '{1}/{0}/exec/3cc2351ab11b/start'.format(CURRENT_VERSION, prefix): | ||||
|     post_fake_execute_start, | ||||
|     post_fake_exec_create, | ||||
|     '{1}/{0}/exec/d5d177f121dc/start'.format(CURRENT_VERSION, prefix): | ||||
|     post_fake_exec_start, | ||||
|     '{1}/{0}/exec/d5d177f121dc/json'.format(CURRENT_VERSION, prefix): | ||||
|     get_fake_exec_inspect, | ||||
|     '{1}/{0}/exec/d5d177f121dc/resize'.format(CURRENT_VERSION, prefix): | ||||
|     post_fake_exec_resize, | ||||
| 
 | ||||
|     '{1}/{0}/containers/3cc2351ab11b/stats'.format(CURRENT_VERSION, prefix): | ||||
|     get_fake_stats, | ||||
|     '{1}/{0}/containers/3cc2351ab11b/stop'.format(CURRENT_VERSION, prefix): | ||||
|  |  | |||
|  | @ -1071,9 +1071,12 @@ class TestExecuteCommand(BaseTestCase): | |||
|         self.client.start(id) | ||||
|         self.tmp_containers.append(id) | ||||
| 
 | ||||
|         res = self.client.execute(id, ['echo', 'hello']) | ||||
|         res = self.client.exec_create(id, ['echo', 'hello']) | ||||
|         self.assertIn('Id', res) | ||||
| 
 | ||||
|         exec_log = self.client.exec_start(res) | ||||
|         expected = b'hello\n' if six.PY3 else 'hello\n' | ||||
|         self.assertEqual(res, expected) | ||||
|         self.assertEqual(exec_log, expected) | ||||
| 
 | ||||
| 
 | ||||
| @unittest.skipIf(not EXEC_DRIVER_IS_NATIVE, 'Exec driver not native') | ||||
|  | @ -1085,9 +1088,12 @@ class TestExecuteCommandString(BaseTestCase): | |||
|         self.client.start(id) | ||||
|         self.tmp_containers.append(id) | ||||
| 
 | ||||
|         res = self.client.execute(id, 'echo hello world', stdout=True) | ||||
|         res = self.client.exec_create(id, 'echo hello world') | ||||
|         self.assertIn('Id', res) | ||||
| 
 | ||||
|         exec_log = self.client.exec_start(res) | ||||
|         expected = b'hello world\n' if six.PY3 else 'hello world\n' | ||||
|         self.assertEqual(res, expected) | ||||
|         self.assertEqual(exec_log, expected) | ||||
| 
 | ||||
| 
 | ||||
| @unittest.skipIf(not EXEC_DRIVER_IS_NATIVE, 'Exec driver not native') | ||||
|  | @ -1099,14 +1105,33 @@ class TestExecuteCommandStreaming(BaseTestCase): | |||
|         self.client.start(id) | ||||
|         self.tmp_containers.append(id) | ||||
| 
 | ||||
|         chunks = self.client.execute(id, ['echo', 'hello\nworld'], stream=True) | ||||
|         exec_id = self.client.exec_create(id, ['echo', 'hello\nworld']) | ||||
|         self.assertIn('Id', exec_id) | ||||
| 
 | ||||
|         res = b'' if six.PY3 else '' | ||||
|         for chunk in chunks: | ||||
|         for chunk in self.client.exec_start(exec_id, stream=True): | ||||
|             res += chunk | ||||
|         expected = b'hello\nworld\n' if six.PY3 else 'hello\nworld\n' | ||||
|         self.assertEqual(res, expected) | ||||
| 
 | ||||
| 
 | ||||
| @unittest.skipIf(not EXEC_DRIVER_IS_NATIVE, 'Exec driver not native') | ||||
| class TestExecInspect(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.exec_create(id, ['mkdir', '/does/not/exist']) | ||||
|         self.assertIn('Id', exec_id) | ||||
|         self.client.exec_start(exec_id) | ||||
|         exec_info = self.client.exec_inspect(exec_id) | ||||
|         self.assertIn('ExitCode', exec_info) | ||||
|         self.assertNotEqual(exec_info['ExitCode'], 0) | ||||
| 
 | ||||
| 
 | ||||
| class TestRunContainerStreaming(BaseTestCase): | ||||
|     def runTest(self): | ||||
|         container = self.client.create_container('busybox', '/bin/sh', | ||||
|  |  | |||
|  | @ -1583,31 +1583,95 @@ class DockerClientTest(Cleanup, base.BaseTestCase): | |||
|             timeout=(docker.client.DEFAULT_TIMEOUT_SECONDS + timeout) | ||||
|         ) | ||||
| 
 | ||||
|     def test_execute_command(self): | ||||
|     def test_exec_create(self): | ||||
|         try: | ||||
|             self.client.execute(fake_api.FAKE_CONTAINER_ID, ['ls', '-1']) | ||||
|             self.client.exec_create(fake_api.FAKE_CONTAINER_ID, ['ls', '-1']) | ||||
|         except Exception as e: | ||||
|             self.fail('Command should not raise exception: {0}'.format(e)) | ||||
| 
 | ||||
|         args = fake_request.call_args | ||||
|         self.assertEqual(args[0][0], | ||||
|                          url_prefix + 'exec/3cc2351ab11b/start') | ||||
|         self.assertEqual( | ||||
|             args[0][0], url_prefix + 'containers/{0}/exec'.format( | ||||
|                 fake_api.FAKE_CONTAINER_ID | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual(json.loads(args[1]['data']), | ||||
|                          json.loads('''{ | ||||
|                             "Tty": false, | ||||
|                             "AttachStderr": true, | ||||
|                             "Container": "3cc2351ab11b", | ||||
|                             "Cmd": ["ls", "-1"], | ||||
|                             "AttachStdin": false, | ||||
|                             "User": "", | ||||
|                             "Detach": false, | ||||
|                             "Privileged": false, | ||||
|                             "AttachStdout": true}''')) | ||||
|         self.assertEqual( | ||||
|             json.loads(args[1]['data']), { | ||||
|                 'Tty': False, | ||||
|                 'AttachStdout': True, | ||||
|                 'Container': fake_api.FAKE_CONTAINER_ID, | ||||
|                 'Cmd': ['ls', '-1'], | ||||
|                 'Privileged': False, | ||||
|                 'AttachStdin': False, | ||||
|                 'AttachStderr': True, | ||||
|                 'User': '' | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual(args[1]['headers'], | ||||
|                          {'Content-Type': 'application/json'}) | ||||
| 
 | ||||
|     def test_exec_start(self): | ||||
|         try: | ||||
|             self.client.exec_start(fake_api.FAKE_EXEC_ID) | ||||
|         except Exception as e: | ||||
|             self.fail('Command should not raise exception: {0}'.format(e)) | ||||
| 
 | ||||
|         args = fake_request.call_args | ||||
|         self.assertEqual( | ||||
|             args[0][0], url_prefix + 'exec/{0}/start'.format( | ||||
|                 fake_api.FAKE_EXEC_ID | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual( | ||||
|             json.loads(args[1]['data']), { | ||||
|                 'Tty': False, | ||||
|                 'Detach': False, | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual(args[1]['headers'], | ||||
|                          {'Content-Type': 'application/json'}) | ||||
| 
 | ||||
|     def test_exec_inspect(self): | ||||
|         try: | ||||
|             self.client.exec_inspect(fake_api.FAKE_EXEC_ID) | ||||
|         except Exception as e: | ||||
|             self.fail('Command should not raise exception: {0}'.format(e)) | ||||
| 
 | ||||
|         args = fake_request.call_args | ||||
|         self.assertEqual( | ||||
|             args[0][0], url_prefix + 'exec/{0}/json'.format( | ||||
|                 fake_api.FAKE_EXEC_ID | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|     def test_exec_resize(self): | ||||
|         try: | ||||
|             self.client.exec_resize(fake_api.FAKE_EXEC_ID, height=20, width=60) | ||||
|         except Exception as e: | ||||
|             self.fail('Command should not raise exception: {0}'.format(e)) | ||||
| 
 | ||||
|         args = fake_request.call_args | ||||
|         self.assertEqual( | ||||
|             args[0][0], url_prefix + 'exec/{0}/resize'.format( | ||||
|                 fake_api.FAKE_EXEC_ID | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual( | ||||
|             json.loads(args[1]['data']), { | ||||
|                 'h': 20, | ||||
|                 'w': 60, | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual( | ||||
|             args[1]['headers'], {'Content-Type': 'application/json'} | ||||
|         ) | ||||
| 
 | ||||
|     def test_pause_container(self): | ||||
|         try: | ||||
|             self.client.pause(fake_api.FAKE_CONTAINER_ID) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue