From 7cb8004479af477bf8f6335d55caf300c3ef1a96 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 25 Jul 2024 21:40:52 +0200 Subject: [PATCH 1/4] Add working_dir option. --- changelogs/fragments/943-connection.yml | 2 ++ plugins/connection/docker.py | 22 ++++++++++++++++++++++ plugins/connection/docker_api.py | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 changelogs/fragments/943-connection.yml diff --git a/changelogs/fragments/943-connection.yml b/changelogs/fragments/943-connection.yml new file mode 100644 index 000000000..7caaf6fda --- /dev/null +++ b/changelogs/fragments/943-connection.yml @@ -0,0 +1,2 @@ +minor_changes: + - "docker, docker_api connection plugins - allow to determine the working directory when executing commands with the new ``working_dir`` option (https://github.com/ansible-collections/community.docker/pull/943)." diff --git a/plugins/connection/docker.py b/plugins/connection/docker.py index 9c5e2eaf2..577037612 100644 --- a/plugins/connection/docker.py +++ b/plugins/connection/docker.py @@ -92,6 +92,19 @@ - name: ansible_docker_extra_env type: dict version_added: 3.12.0 + working_dir: + description: + - The directory inside the container to run commands in. + - Requires Docker CLI version 18.06 or later. + env: + - name: ANSIBLE_DOCKER_WORKING_DIR + ini: + - key: working_dir + section: docker_connection + vars: + - name: ansible_docker_working_dir + type: string + version_added: 3.12.0 ''' import fcntl @@ -239,6 +252,15 @@ def _build_exec_cmd(self, cmd): ) local_cmd += [b'-e', b'%s=%s' % (to_bytes(k, errors='surrogate_or_strict'), to_bytes(v, errors='surrogate_or_strict'))] + if self.get_option('working_dir') is not None: + local_cmd += [b'-w', to_bytes(self.get_option('working_dir'), errors='surrogate_or_strict')] + if self.docker_version != u'dev' and LooseVersion(self.docker_version) < LooseVersion(u'18.06'): + # https://github.com/docker/cli/pull/732, first appeared in release 18.06.0 + raise AnsibleConnectionFailure( + 'Providing the working directory requires Docker CLI version 18.06 or newer. You have Docker CLI version {0}.' + .format(self.docker_version) + ) + # -i is needed to keep stdin open which allows pipelining to work local_cmd += [b'-i', self.get_option('remote_addr')] + cmd diff --git a/plugins/connection/docker_api.py b/plugins/connection/docker_api.py index 52b8eebfc..6f40e2e78 100644 --- a/plugins/connection/docker_api.py +++ b/plugins/connection/docker_api.py @@ -86,6 +86,19 @@ - name: ansible_docker_extra_env type: dict version_added: 3.12.0 + working_dir: + description: + - The directory inside the container to run commands in. + - Requires Docker API version 1.35 or later. + env: + - name: ANSIBLE_DOCKER_WORKING_DIR + ini: + - key: working_dir + section: docker_connection + vars: + - name: ansible_docker_working_dir + type: string + version_added: 3.12.0 ''' import os @@ -116,6 +129,8 @@ from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException, NotFound +from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion + MIN_DOCKER_API = None @@ -233,6 +248,15 @@ def exec_command(self, cmd, in_data=None, sudoable=False): ) data['Env'].append(u'{0}={1}'.format(to_text(k, errors='surrogate_or_strict'), to_text(v, errors='surrogate_or_strict'))) + if self.get_option('working_dir') is not None: + data['WorkingDir'] = self.get_option('working_dir') + if self.client.docker_api_version < LooseVersion('1.35'): + raise AnsibleConnectionFailure( + 'Providing the working directory requires Docker API version 1.35 or newer.' + ' The Docker daemon the connection is using has API version {0}.' + .format(self.client.docker_api_version_str) + ) + exec_data = self._call_client(lambda: self.client.post_json_to_json('/containers/{0}/exec', self.get_option('remote_addr'), data=data)) exec_id = exec_data['Id'] From 11e219f5fa16b93f01e8be850e7cb1ddfe357774 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 25 Jul 2024 21:43:57 +0200 Subject: [PATCH 2/4] Add privileged option. --- changelogs/fragments/943-connection.yml | 1 + plugins/connection/docker.py | 17 +++++++++++++++++ plugins/connection/docker_api.py | 16 +++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/changelogs/fragments/943-connection.yml b/changelogs/fragments/943-connection.yml index 7caaf6fda..3ccadb63a 100644 --- a/changelogs/fragments/943-connection.yml +++ b/changelogs/fragments/943-connection.yml @@ -1,2 +1,3 @@ minor_changes: - "docker, docker_api connection plugins - allow to determine the working directory when executing commands with the new ``working_dir`` option (https://github.com/ansible-collections/community.docker/pull/943)." + - "docker, docker_api connection plugins - allow to execute commands with extended privileges with the new ``privileges`` option (https://github.com/ansible-collections/community.docker/pull/943)." diff --git a/plugins/connection/docker.py b/plugins/connection/docker.py index 577037612..33913a8a6 100644 --- a/plugins/connection/docker.py +++ b/plugins/connection/docker.py @@ -105,6 +105,20 @@ - name: ansible_docker_working_dir type: string version_added: 3.12.0 + privileged: + description: + - Whether commands should be run with extended privileges. + - B(Note) that this allows command to potentially break out of the container. Use with care! + env: + - name: ANSIBLE_DOCKER_PRIVILEGED + ini: + - key: privileged + section: docker_connection + vars: + - name: ansible_docker_privileged + type: boolean + default: false + version_added: 3.12.0 ''' import fcntl @@ -261,6 +275,9 @@ def _build_exec_cmd(self, cmd): .format(self.docker_version) ) + if self.get_option('privileged'): + local_cmd += [b'--privileged'] + # -i is needed to keep stdin open which allows pipelining to work local_cmd += [b'-i', self.get_option('remote_addr')] + cmd diff --git a/plugins/connection/docker_api.py b/plugins/connection/docker_api.py index 6f40e2e78..d5bd3b6ac 100644 --- a/plugins/connection/docker_api.py +++ b/plugins/connection/docker_api.py @@ -99,6 +99,20 @@ - name: ansible_docker_working_dir type: string version_added: 3.12.0 + privileged: + description: + - Whether commands should be run with extended privileges. + - B(Note) that this allows command to potentially break out of the container. Use with care! + env: + - name: ANSIBLE_DOCKER_PRIVILEGED + ini: + - key: privileged + section: docker_connection + vars: + - name: ansible_docker_privileged + type: boolean + default: false + version_added: 3.12.0 ''' import os @@ -225,7 +239,7 @@ def exec_command(self, cmd, in_data=None, sudoable=False): data = { 'Container': self.get_option('remote_addr'), 'User': self.get_option('remote_user') or '', - 'Privileged': False, + 'Privileged': self.get_option('privileged'), 'Tty': False, 'AttachStdin': need_stdin, 'AttachStdout': True, From 76046c9431df6e122f4a074a5cd7ae03f5515699 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 25 Jul 2024 21:53:23 +0200 Subject: [PATCH 3/4] Add basic tests. --- tests/integration/targets/connection_docker/runme.sh | 3 +++ tests/integration/targets/connection_docker_api/runme.sh | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/integration/targets/connection_docker/runme.sh b/tests/integration/targets/connection_docker/runme.sh index 4ebbf22e2..096903ffe 100755 --- a/tests/integration/targets/connection_docker/runme.sh +++ b/tests/integration/targets/connection_docker/runme.sh @@ -54,6 +54,9 @@ cat > test_connection.inventory << EOF [docker] docker-no-pipelining ansible_pipelining=false docker-pipelining ansible_pipelining=true +docker-working-dir ansible_docker_working_dir=/home +# This will only work on VMs: +# docker-privileged ansible_docker_privileged=true [docker:vars] ansible_host=docker-connection-test-container${CONTAINER_SUFFIX} diff --git a/tests/integration/targets/connection_docker_api/runme.sh b/tests/integration/targets/connection_docker_api/runme.sh index ea5588a6a..d4def6acc 100755 --- a/tests/integration/targets/connection_docker_api/runme.sh +++ b/tests/integration/targets/connection_docker_api/runme.sh @@ -54,6 +54,9 @@ cat > test_connection.inventory << EOF [docker_api] docker_api-no-pipelining ansible_pipelining=false docker_api-pipelining ansible_pipelining=true +docker_api-working-dir ansible_docker_working_dir=/home +# This will only work on VMs: +# docker_api-privileged ansible_docker_privileged=true [docker_api:vars] ansible_host=docker-connection-test-container${CONTAINER_SUFFIX} From c7643e16d574e9cc817a2f89f8f8a7be7701c96d Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 25 Jul 2024 22:21:11 +0200 Subject: [PATCH 4/4] Also test privileged. --- tests/integration/targets/connection_docker/aliases | 2 +- tests/integration/targets/connection_docker/runme.sh | 3 +-- tests/integration/targets/connection_docker_api/aliases | 2 +- tests/integration/targets/connection_docker_api/runme.sh | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/integration/targets/connection_docker/aliases b/tests/integration/targets/connection_docker/aliases index 40eff16f5..bc79a0153 100644 --- a/tests/integration/targets/connection_docker/aliases +++ b/tests/integration/targets/connection_docker/aliases @@ -3,5 +3,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/4 -skip/docker # coverage does not work if we're inside a docker container, since we cannot access this container's /tmp dir from the new container +skip/docker # coverage does not work if we're inside a docker container, since we cannot access this container's /tmp dir from the new container; also privileged doesn't work destructive diff --git a/tests/integration/targets/connection_docker/runme.sh b/tests/integration/targets/connection_docker/runme.sh index 096903ffe..3cd6bddbc 100755 --- a/tests/integration/targets/connection_docker/runme.sh +++ b/tests/integration/targets/connection_docker/runme.sh @@ -55,8 +55,7 @@ cat > test_connection.inventory << EOF docker-no-pipelining ansible_pipelining=false docker-pipelining ansible_pipelining=true docker-working-dir ansible_docker_working_dir=/home -# This will only work on VMs: -# docker-privileged ansible_docker_privileged=true +docker-privileged ansible_docker_privileged=true [docker:vars] ansible_host=docker-connection-test-container${CONTAINER_SUFFIX} diff --git a/tests/integration/targets/connection_docker_api/aliases b/tests/integration/targets/connection_docker_api/aliases index 40eff16f5..bc79a0153 100644 --- a/tests/integration/targets/connection_docker_api/aliases +++ b/tests/integration/targets/connection_docker_api/aliases @@ -3,5 +3,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/4 -skip/docker # coverage does not work if we're inside a docker container, since we cannot access this container's /tmp dir from the new container +skip/docker # coverage does not work if we're inside a docker container, since we cannot access this container's /tmp dir from the new container; also privileged doesn't work destructive diff --git a/tests/integration/targets/connection_docker_api/runme.sh b/tests/integration/targets/connection_docker_api/runme.sh index d4def6acc..186e1e5cb 100755 --- a/tests/integration/targets/connection_docker_api/runme.sh +++ b/tests/integration/targets/connection_docker_api/runme.sh @@ -55,8 +55,7 @@ cat > test_connection.inventory << EOF docker_api-no-pipelining ansible_pipelining=false docker_api-pipelining ansible_pipelining=true docker_api-working-dir ansible_docker_working_dir=/home -# This will only work on VMs: -# docker_api-privileged ansible_docker_privileged=true +docker_api-privileged ansible_docker_privileged=true [docker_api:vars] ansible_host=docker-connection-test-container${CONTAINER_SUFFIX}