From 7910361b522b87fb48ffe15b6d7018d1518cd883 Mon Sep 17 00:00:00 2001 From: Sergey Date: Sat, 15 Jun 2019 14:35:55 +0300 Subject: [PATCH] Fix failed mounts in podman connector and handle errors (#57741) Like it's described in issue #57740 ansible podman fails to run because of failed mount of rootless container. --- ...41-podman-connection-plugin-fix-mount.yaml | 2 + lib/ansible/plugins/connection/podman.py | 53 +++++++++++++------ 2 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 changelogs/fragments/57741-podman-connection-plugin-fix-mount.yaml diff --git a/changelogs/fragments/57741-podman-connection-plugin-fix-mount.yaml b/changelogs/fragments/57741-podman-connection-plugin-fix-mount.yaml new file mode 100644 index 00000000000..76f7cb68c0a --- /dev/null +++ b/changelogs/fragments/57741-podman-connection-plugin-fix-mount.yaml @@ -0,0 +1,2 @@ +bugfixes: + - ansible-podman connection plugin - Fix case when mount of podman container fails and files can't be copied to/from container. Also add error handling in case of failed podman commands. (https://github.com/ansible/ansible/issues/57740) diff --git a/lib/ansible/plugins/connection/podman.py b/lib/ansible/plugins/connection/podman.py index 26e53c22783..690243bf08e 100644 --- a/lib/ansible/plugins/connection/podman.py +++ b/lib/ansible/plugins/connection/podman.py @@ -14,6 +14,7 @@ import shlex import shutil import subprocess +from ansible.errors import AnsibleError from ansible.module_utils._text import to_bytes, to_native from ansible.plugins.connection import ConnectionBase, ensure_connect from ansible.utils.display import Display @@ -68,7 +69,7 @@ class Connection(ConnectionBase): self._mount_point = None self.user = self._play_context.remote_user - def _podman(self, cmd, cmd_args=None, in_data=None): + def _podman(self, cmd, cmd_args=None, in_data=None, use_container_id=True): """ run podman executable @@ -77,7 +78,9 @@ class Connection(ConnectionBase): :param in_data: data passed to podman's stdin :return: return code, stdout, stderr """ - local_cmd = ['podman', cmd, self._container_id] + local_cmd = ['podman', cmd] + if use_container_id: + local_cmd.append(self._container_id) if cmd_args: local_cmd += cmd_args local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd] @@ -87,6 +90,9 @@ class Connection(ConnectionBase): stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate(input=in_data) + display.vvvvv("STDOUT %s" % stdout) + display.vvvvv("STDERR %s" % stderr) + display.vvvvv("RC CODE %s" % p.returncode) stdout = to_bytes(stdout, errors='surrogate_or_strict') stderr = to_bytes(stderr, errors='surrogate_or_strict') return p.returncode, stdout, stderr @@ -98,8 +104,11 @@ class Connection(ConnectionBase): """ super(Connection, self)._connect() rc, self._mount_point, stderr = self._podman("mount") - self._mount_point = self._mount_point.strip() - display.vvvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)) + if rc != 0: + display.v("Failed to mount container %s: %s" % (self._container_id, stderr.strip())) + else: + self._mount_point = self._mount_point.strip() + display.vvvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)) self._connected = True @ensure_connect @@ -121,23 +130,35 @@ class Connection(ConnectionBase): """ Place a local file located in 'in_path' inside container at 'out_path' """ super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._container_id) - - real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict') - shutil.copyfile( - to_bytes(in_path, errors='surrogate_or_strict'), - to_bytes(real_out_path, errors='surrogate_or_strict') - ) + if not self._mount_point: + rc, stdout, stderr = self._podman( + "cp", [in_path, self._container_id + ":" + out_path], use_container_id=False) + if rc != 0: + raise AnsibleError("Failed to copy file from %s to %s in container %s\n%s" % ( + in_path, out_path, self._container_id, stderr)) + else: + real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict') + shutil.copyfile( + to_bytes(in_path, errors='surrogate_or_strict'), + to_bytes(real_out_path, errors='surrogate_or_strict') + ) def fetch_file(self, in_path, out_path): """ obtain file specified via 'in_path' from the container and place it at 'out_path' """ super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._container_id) - - real_in_path = self._mount_point + to_bytes(in_path, errors='surrogate_or_strict') - shutil.copyfile( - to_bytes(real_in_path, errors='surrogate_or_strict'), - to_bytes(out_path, errors='surrogate_or_strict') - ) + if not self._mount_point: + rc, stdout, stderr = self._podman( + "cp", [self._container_id + ":" + in_path, out_path], use_container_id=False) + if rc != 0: + raise AnsibleError("Failed to fetch file from %s to %s from container %s\n%s" % ( + in_path, out_path, self._container_id, stderr)) + else: + real_in_path = self._mount_point + to_bytes(in_path, errors='surrogate_or_strict') + shutil.copyfile( + to_bytes(real_in_path, errors='surrogate_or_strict'), + to_bytes(out_path, errors='surrogate_or_strict') + ) def close(self): """ unmount container's filesystem """