diff --git a/compose/cli/main.py b/compose/cli/main.py index face38e6d..c3e30919d 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -14,6 +14,8 @@ from distutils.spawn import find_executable from inspect import getdoc from operator import attrgetter +import docker + from . import errors from . import signals from .. import __version__ @@ -402,7 +404,7 @@ class TopLevelCommand(object): """ Execute a command in a running container - Usage: exec [options] SERVICE COMMAND [ARGS...] + Usage: exec [options] [-e KEY=VAL...] SERVICE COMMAND [ARGS...] Options: -d Detached mode: Run command in the background. @@ -412,11 +414,16 @@ class TopLevelCommand(object): allocates a TTY. --index=index index of the container if there are multiple instances of a service [default: 1] + -e, --env KEY=VAL Set environment variables (can be used multiple times, + not supported in API < 1.25) """ index = int(options.get('--index')) service = self.project.get_service(options['SERVICE']) detach = options['-d'] + if options['--env'] and docker.utils.version_lt(self.project.client.api_version, '1.25'): + raise UserError("Setting environment for exec is not supported in API < 1.25'") + try: container = service.get_container(number=index) except ValueError as e: @@ -425,26 +432,7 @@ class TopLevelCommand(object): tty = not options["-T"] if IS_WINDOWS_PLATFORM and not detach: - args = ["exec"] - - if options["-d"]: - args += ["--detach"] - else: - args += ["--interactive"] - - if not options["-T"]: - args += ["--tty"] - - if options["--privileged"]: - args += ["--privileged"] - - if options["--user"]: - args += ["--user", options["--user"]] - - args += [container.id] - args += command - - sys.exit(call_docker(args)) + sys.exit(call_docker(build_exec_command(options, container.id, command))) create_exec_options = { "privileged": options["--privileged"], @@ -453,6 +441,9 @@ class TopLevelCommand(object): "stdin": tty, } + if docker.utils.version_gte(self.project.client.api_version, '1.25'): + create_exec_options["environment"] = options["--env"] + exec_id = container.create_exec(command, **create_exec_options) if detach: @@ -1295,3 +1286,29 @@ def parse_scale_args(options): ) res[service_name] = num return res + + +def build_exec_command(options, container_id, command): + args = ["exec"] + + if options["-d"]: + args += ["--detach"] + else: + args += ["--interactive"] + + if not options["-T"]: + args += ["--tty"] + + if options["--privileged"]: + args += ["--privileged"] + + if options["--user"]: + args += ["--user", options["--user"]] + + if options["--env"]: + for env_variable in options["--env"]: + args += ["--env", env_variable] + + args += [container_id] + args += command + return args diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index c9420e011..6c18a175c 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -33,6 +33,7 @@ from tests.integration.testcases import no_cluster from tests.integration.testcases import pull_busybox from tests.integration.testcases import SWARM_SKIP_RM_VOLUMES from tests.integration.testcases import v2_1_only +from tests.integration.testcases import v2_2_only from tests.integration.testcases import v2_only from tests.integration.testcases import v3_only @@ -1369,6 +1370,31 @@ class CLITestCase(DockerClientTestCase): self.assertEqual(stdout, "operator\n") self.assertEqual(stderr, "") + @v2_2_only() + def test_exec_service_with_environment_overridden(self): + name = 'service' + self.base_dir = 'tests/fixtures/environment-exec' + self.dispatch(['up', '-d']) + self.assertEqual(len(self.project.containers()), 1) + + stdout, stderr = self.dispatch([ + 'exec', + '-T', + '-e', 'foo=notbar', + '--env', 'alpha=beta', + name, + 'env', + ]) + + # env overridden + assert 'foo=notbar' in stdout + # keep environment from yaml + assert 'hello=world' in stdout + # added option from command line + assert 'alpha=beta' in stdout + + self.assertEqual(stderr, '') + def test_run_service_without_links(self): self.base_dir = 'tests/fixtures/links-composefile' self.dispatch(['run', 'console', '/bin/true']) diff --git a/tests/fixtures/environment-exec/docker-compose.yml b/tests/fixtures/environment-exec/docker-compose.yml new file mode 100644 index 000000000..813606eb8 --- /dev/null +++ b/tests/fixtures/environment-exec/docker-compose.yml @@ -0,0 +1,10 @@ +version: "2.2" + +services: + service: + image: busybox:latest + command: top + + environment: + foo: bar + hello: world