The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/pylxd/pull/291
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === This patch adds support to control the decoding of the stdout and stderr when executing commands in a container. It is possible to force a particular decode, or just get the raw buffers. The existing behaviour of trying to decode to utf-8 is retained if no decode or encoding paramters are passed to the execute method. Tests are in the integration/test_containers.py file as it's tricky to test properly in a unit test. Signed-off-by: Alex Kavanagh <[email protected]>
From 201f0e2d61fa3d77eb8485c0594542c74c31bee3 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh <[email protected]> Date: Tue, 20 Mar 2018 17:11:50 +0000 Subject: [PATCH] Enable decoding control over stdout/stderr on execute This patch adds support to control the decoding of the stdout and stderr when executing commands in a container. It is possible to force a particular decode, or just get the raw buffers. The existing behaviour of trying to decode to utf-8 is retained if no decode or encoding paramters are passed to the execute method. Tests are in the integration/test_containers.py file as it's tricky to test properly in a unit test. Signed-off-by: Alex Kavanagh <[email protected]> --- integration/test_containers.py | 24 +++++++++++++++++++++ pylxd/models/container.py | 47 +++++++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/integration/test_containers.py b/integration/test_containers.py index 75c6df8..ae68109 100644 --- a/integration/test_containers.py +++ b/integration/test_containers.py @@ -155,6 +155,30 @@ def test_execute(self): self.assertEqual('test\n', result.stdout) self.assertEqual('', result.stderr) + def test_execute_no_decode(self): + """A command is executed on the container that isn't utf-8 decodable + """ + self.container.start(wait=True) + self.addCleanup(self.container.stop, wait=True) + + result = self.container.execute(['printf', '\\xff'], decode=None) + + self.assertEqual(0, result.exit_code) + self.assertEqual(b'\xff', result.stdout) + self.assertEqual(b'', result.stderr) + + def test_execute_force_decode(self): + """A command is executed and force output to ascii""" + self.container.start(wait=True) + self.addCleanup(self.container.stop, wait=True) + + result = self.container.execute(['printf', 'qu\\xe9'], decode=True, + encoding='latin1') + + self.assertEqual(0, result.exit_code) + self.assertEqual('qué', result.stdout) + self.assertEqual('', result.stderr) + def test_publish(self): """A container is published.""" image = self.container.publish(wait=True) diff --git a/pylxd/models/container.py b/pylxd/models/container.py index ca03c2a..4034cb5 100644 --- a/pylxd/models/container.py +++ b/pylxd/models/container.py @@ -255,11 +255,26 @@ def unfreeze(self, timeout=30, force=True, wait=False): force=force, wait=wait) - def execute(self, commands, environment={}): + def execute(self, commands, environment={}, encoding=None, decode=True): """Execute a command on the container. In pylxd 2.2, this method will be renamed `execute` and the existing `execute` method removed. + + :param commands: The command and arguments as a list of strings + :type commands: [str] + :param environment: The environment variables to pass with the command + :type environment: {str: str} + :param encoding: The encoding to use for stdout/stderr if the param + decode is True. If encoding is None, then no override is + performed and whatever the existing encoding from LXD is used. + :type encoding: str + :param decode: Whether to decode the stdout/stderr or just return the + raw buffers. + :type decode: bool + :raises ValueError: if the ws4py library is not installed. + :returns: The return value, stdout and stdin + :rtype: _ContainerExecuteResult() namedtuple """ if not _ws4py_installed: raise ValueError( @@ -283,11 +298,13 @@ def execute(self, commands, environment={}): stdin.resource = '{}?secret={}'.format(parsed.path, fds['0']) stdin.connect() stdout = _CommandWebsocketClient( - manager, self.client.websocket_url) + manager, self.client.websocket_url, + encoding=encoding, decode=decode) stdout.resource = '{}?secret={}'.format(parsed.path, fds['1']) stdout.connect() stderr = _CommandWebsocketClient( - manager, self.client.websocket_url) + manager, self.client.websocket_url, + encoding=encoding, decode=decode) stderr.resource = '{}?secret={}'.format(parsed.path, fds['2']) stderr.connect() @@ -383,8 +400,15 @@ def publish(self, public=False, wait=False): class _CommandWebsocketClient(WebSocketBaseClient): # pragma: no cover + """Handle a websocket for container.execute(...) and manage decoding of the + returned values depending on 'decode' and 'encoding' parameters. + """ + def __init__(self, manager, *args, **kwargs): self.manager = manager + self.decode = kwargs.pop('decode', True) + self.encoding = kwargs.pop('encoding', None) + self.message_encoding = None super(_CommandWebsocketClient, self).__init__(*args, **kwargs) def handshake_ok(self): @@ -392,14 +416,21 @@ def handshake_ok(self): self.buffer = [] def received_message(self, message): - if message.encoding: - self.buffer.append(message.data.decode(message.encoding)) - else: - self.buffer.append(message.data.decode('utf-8')) + if message.encoding and self.message_encoding is None: + self.message_encoding = message.encoding + self.buffer.append(message.data) @property def data(self): - return ''.join(self.buffer) + buffer = b''.join(self.buffer) + if self.decode: + if self.encoding: + return buffer.decode(self.encoding) + if self.message_encoding: + return buffer.decode(self.message_encoding) + # This is the backwards compatible "always decode to utf-8" + return buffer.decode('utf-8') + return buffer class _StdinWebsocket(WebSocketBaseClient): # pragma: no cover
_______________________________________________ lxc-devel mailing list [email protected] http://lists.linuxcontainers.org/listinfo/lxc-devel
