diff --git a/docker/api/exec_api.py b/docker/api/exec_api.py index 6c3e6383..0382a647 100644 --- a/docker/api/exec_api.py +++ b/docker/api/exec_api.py @@ -8,7 +8,8 @@ class ExecApiMixin(object): @utils.minimum_version('1.15') @utils.check_resource def exec_create(self, container, cmd, stdout=True, stderr=True, - stdin=False, tty=False, privileged=False, user=''): + stdin=False, tty=False, privileged=False, user='', + environment=None): """ Sets up an exec instance in a running container. @@ -22,6 +23,9 @@ class ExecApiMixin(object): tty (bool): Allocate a pseudo-TTY. Default: False privileged (bool): Run as privileged. user (str): User to execute command as. Default: root + environment (dict or list): A dictionary or a list of strings in + the following format ``["PASSWORD=xxx"]`` or + ``{"PASSWORD": "xxx"}``. Returns: (dict): A dictionary with an exec ``Id`` key. @@ -39,9 +43,16 @@ class ExecApiMixin(object): raise errors.InvalidVersion( 'User-specific exec is not supported in API < 1.19' ) + if environment and utils.compare_version('1.25', self._version) < 0: + raise errors.InvalidVersion( + 'Setting environment for exec is not supported in API < 1.25' + ) if isinstance(cmd, six.string_types): cmd = utils.split_command(cmd) + if isinstance(environment, dict): + environment = utils.utils.format_environment(environment) + data = { 'Container': container, 'User': user, @@ -50,7 +61,8 @@ class ExecApiMixin(object): 'AttachStdin': stdin, 'AttachStdout': stdout, 'AttachStderr': stderr, - 'Cmd': cmd + 'Cmd': cmd, + 'Env': environment, } url = self._url('/containers/{0}/exec', container) diff --git a/docker/models/containers.py b/docker/models/containers.py index 2213583a..7a1cd716 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -125,7 +125,7 @@ class Container(Model): def exec_run(self, cmd, stdout=True, stderr=True, stdin=False, tty=False, privileged=False, user='', detach=False, stream=False, - socket=False): + socket=False, environment=None): """ Run a command inside this container. Similar to ``docker exec``. @@ -141,6 +141,9 @@ class Container(Model): detach (bool): If true, detach from the exec command. Default: False stream (bool): Stream response data. Default: False + environment (dict or list): A dictionary or a list of strings in + the following format ``["PASSWORD=xxx"]`` or + ``{"PASSWORD": "xxx"}``. Returns: (generator or str): If ``stream=True``, a generator yielding @@ -152,7 +155,7 @@ class Container(Model): """ resp = self.client.api.exec_create( self.id, cmd, stdout=stdout, stderr=stderr, stdin=stdin, tty=tty, - privileged=privileged, user=user + privileged=privileged, user=user, environment=environment ) return self.client.api.exec_start( resp['Id'], detach=detach, tty=tty, stream=stream, socket=socket diff --git a/tests/integration/api_exec_test.py b/tests/integration/api_exec_test.py index 55286e37..fb3c6f93 100644 --- a/tests/integration/api_exec_test.py +++ b/tests/integration/api_exec_test.py @@ -2,6 +2,7 @@ from docker.utils.socket import next_frame_size from docker.utils.socket import read_exactly from .base import BaseAPIIntegrationTest, BUSYBOX +from ..helpers import requires_api_version class ExecTest(BaseAPIIntegrationTest): @@ -121,3 +122,17 @@ class ExecTest(BaseAPIIntegrationTest): exec_info = self.client.exec_inspect(exec_id) self.assertIn('ExitCode', exec_info) self.assertNotEqual(exec_info['ExitCode'], 0) + + @requires_api_version('1.25') + def test_exec_command_with_env(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) + + res = self.client.exec_create(id, 'env', environment=["X=Y"]) + self.assertIn('Id', res) + + exec_log = self.client.exec_start(res) + self.assertIn(b'X=Y\n', exec_log) diff --git a/tests/unit/models_containers_test.py b/tests/unit/models_containers_test.py index 4f4dc0f3..e74bb7cd 100644 --- a/tests/unit/models_containers_test.py +++ b/tests/unit/models_containers_test.py @@ -366,7 +366,7 @@ class ContainerTest(unittest.TestCase): container.exec_run("echo hello world", privileged=True, stream=True) client.api.exec_create.assert_called_with( FAKE_CONTAINER_ID, "echo hello world", stdout=True, stderr=True, - stdin=False, tty=False, privileged=True, user='' + stdin=False, tty=False, privileged=True, user='', environment=None ) client.api.exec_start.assert_called_with( FAKE_EXEC_ID, detach=False, tty=False, stream=True, socket=False