Define byte strings versus text strings in docker connection

Fixes python3 string handling in the docker connection plugin

Fixes #24776
pull/24379/head
Toshio Kuratomi 7 years ago
parent 5953a428a8
commit 3277e6328b

@ -51,7 +51,7 @@ from distutils.version import LooseVersion
import ansible.constants as C import ansible.constants as C
from ansible.errors import AnsibleError, AnsibleFileNotFound from ansible.errors import AnsibleError, AnsibleFileNotFound
from ansible.module_utils.six.moves import shlex_quote from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils._text import to_bytes, to_native from ansible.module_utils._text import to_bytes, to_native, to_text
from ansible.plugins.connection import ConnectionBase, BUFSIZE from ansible.plugins.connection import ConnectionBase, BUFSIZE
@ -89,7 +89,7 @@ class Connection(ConnectionBase):
raise AnsibleError("docker command not found in PATH") raise AnsibleError("docker command not found in PATH")
docker_version = self._get_docker_version() docker_version = self._get_docker_version()
if LooseVersion(docker_version) < LooseVersion('1.3'): if LooseVersion(docker_version) < LooseVersion(u'1.3'):
raise AnsibleError('docker connection type requires docker 1.3 or higher') raise AnsibleError('docker connection type requires docker 1.3 or higher')
# The remote user we will request from docker (if supported) # The remote user we will request from docker (if supported)
@ -98,7 +98,7 @@ class Connection(ConnectionBase):
self.actual_user = None self.actual_user = None
if self._play_context.remote_user is not None: if self._play_context.remote_user is not None:
if LooseVersion(docker_version) >= LooseVersion('1.7'): if LooseVersion(docker_version) >= LooseVersion(u'1.7'):
# Support for specifying the exec user was added in docker 1.7 # Support for specifying the exec user was added in docker 1.7
self.remote_user = self._play_context.remote_user self.remote_user = self._play_context.remote_user
self.actual_user = self.remote_user self.actual_user = self.remote_user
@ -106,8 +106,8 @@ class Connection(ConnectionBase):
self.actual_user = self._get_docker_remote_user() self.actual_user = self._get_docker_remote_user()
if self.actual_user != self._play_context.remote_user: if self.actual_user != self._play_context.remote_user:
display.warning('docker {0} does not support remote_user, using container default: {1}' display.warning(u'docker {0} does not support remote_user, using container default: {1}'
.format(docker_version, self.actual_user or '?')) .format(docker_version, self.actual_user or u'?'))
elif self._display.verbosity > 2: elif self._display.verbosity > 2:
# Since we're not setting the actual_user, look it up so we have it for logging later # Since we're not setting the actual_user, look it up so we have it for logging later
# Only do this if display verbosity is high enough that we'll need the value # Only do this if display verbosity is high enough that we'll need the value
@ -116,7 +116,7 @@ class Connection(ConnectionBase):
@staticmethod @staticmethod
def _sanitize_version(version): def _sanitize_version(version):
return re.sub('[^0-9a-zA-Z\.]', '', version) return re.sub(u'[^0-9a-zA-Z\.]', u'', version)
def _old_docker_version(self): def _old_docker_version(self):
cmd_args = [] cmd_args = []
@ -148,15 +148,15 @@ class Connection(ConnectionBase):
cmd, cmd_output, err, returncode = self._old_docker_version() cmd, cmd_output, err, returncode = self._old_docker_version()
if returncode == 0: if returncode == 0:
for line in cmd_output.split('\n'): for line in to_text(cmd_output, errors='surrogate_or_strict').split(u'\n'):
if line.startswith('Server version:'): # old docker versions if line.startswith(u'Server version:'): # old docker versions
return self._sanitize_version(line.split()[2]) return self._sanitize_version(line.split()[2])
cmd, cmd_output, err, returncode = self._new_docker_version() cmd, cmd_output, err, returncode = self._new_docker_version()
if returncode: if returncode:
raise AnsibleError('Docker version check (%s) failed: %s' % (cmd, err)) raise AnsibleError('Docker version check (%s) failed: %s' % (to_native(cmd), to_native(err)))
return self._sanitize_version(cmd_output) return self._sanitize_version(to_text(cmd_output, errors='surrogate_or_strict'))
def _get_docker_remote_user(self): def _get_docker_remote_user(self):
""" Get the default user configured in the docker container """ """ Get the default user configured in the docker container """
@ -164,13 +164,14 @@ class Connection(ConnectionBase):
stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate() out, err = p.communicate()
out = to_text(out, errors='surrogate_or_strict')
if p.returncode != 0: if p.returncode != 0:
display.warning('unable to retrieve default user from docker container: %s' % out + err) display.warning(u'unable to retrieve default user from docker container: %s %s' % (out, to_text(err)))
return None return None
# The default exec user is root, unless it was changed in the Dockerfile with USER # The default exec user is root, unless it was changed in the Dockerfile with USER
return out.strip() or 'root' return out.strip() or u'root'
def _build_exec_cmd(self, cmd): def _build_exec_cmd(self, cmd):
""" Build the local docker exec command to run cmd on remote_host """ Build the local docker exec command to run cmd on remote_host
@ -184,13 +185,13 @@ class Connection(ConnectionBase):
if self._play_context.docker_extra_args: if self._play_context.docker_extra_args:
local_cmd += self._play_context.docker_extra_args.split(' ') local_cmd += self._play_context.docker_extra_args.split(' ')
local_cmd += ['exec'] local_cmd += [b'exec']
if self.remote_user is not None: if self.remote_user is not None:
local_cmd += ['-u', self.remote_user] local_cmd += [b'-u', self.remote_user]
# -i is needed to keep stdin open which allows pipelining to work # -i is needed to keep stdin open which allows pipelining to work
local_cmd += ['-i', self._play_context.remote_addr] + cmd local_cmd += [b'-i', self._play_context.remote_addr] + cmd
return local_cmd return local_cmd
@ -199,7 +200,7 @@ class Connection(ConnectionBase):
super(Connection, self)._connect() super(Connection, self)._connect()
if not self._connected: if not self._connected:
display.vvv(u"ESTABLISH DOCKER CONNECTION FOR USER: {0}".format( display.vvv(u"ESTABLISH DOCKER CONNECTION FOR USER: {0}".format(
self.actual_user or '?'), host=self._play_context.remote_addr self.actual_user or u'?'), host=self._play_context.remote_addr
) )
self._connected = True self._connected = True
@ -239,7 +240,7 @@ class Connection(ConnectionBase):
out_path = self._prefix_login_path(out_path) out_path = self._prefix_login_path(out_path)
if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')): if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')):
raise AnsibleFileNotFound( raise AnsibleFileNotFound(
"file or module does not exist: %s" % in_path) "file or module does not exist: %s" % to_native(in_path))
out_path = shlex_quote(out_path) out_path = shlex_quote(out_path)
# Older docker doesn't have native support for copying files into # Older docker doesn't have native support for copying files into
@ -257,7 +258,8 @@ class Connection(ConnectionBase):
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
if p.returncode != 0: if p.returncode != 0:
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr)) raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" %
(to_native(in_path), to_native(out_path), to_native(stdout), to_native(stderr)))
def fetch_file(self, in_path, out_path): def fetch_file(self, in_path, out_path):
""" Fetch a file from container to local. """ """ Fetch a file from container to local. """

Loading…
Cancel
Save