From 9d0c88a47bf871c5f5698aa77c9433a4bf1baa31 Mon Sep 17 00:00:00 2001 From: Valerii Kovalchuk Date: Fri, 29 Apr 2016 15:52:31 +0300 Subject: [PATCH] Convert script line endings for the target OS This patch adds 'binary' parameter to ResourceManager 'string' method. It allows to open text files in 'rU' mode which recognizes all types of newlines, and binary files in 'rb' mode to prevent their corruption. Change-Id: I505070df187dfd910e68c555319e3ef92ce2c826 Closes-bug: #1496376 --- murano/engine/system/agent.py | 10 ++-- murano/engine/system/resource_manager.py | 5 +- murano/tests/unit/engine/system/test_agent.py | 51 +++++++++++++++++++ .../script-line-endings-db632db9e24237a3.yaml | 5 ++ 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/script-line-endings-db632db9e24237a3.yaml diff --git a/murano/engine/system/agent.py b/murano/engine/system/agent.py index 74a6baf7..6c1fa753 100644 --- a/murano/engine/system/agent.py +++ b/murano/engine/system/agent.py @@ -218,7 +218,7 @@ class Agent(object): for script in script_files: script_path = os.path.join(scripts_folder, script) script_path = base64.encode_as_text(script_path) + "\n" - scripts.append(resources.string(script_path)) + scripts.append(resources.string(script_path, binary=True)) template['Scripts'] = scripts return template @@ -288,11 +288,13 @@ class Agent(object): def _get_body(self, file, resources, folder): use_base64 = self._is_base64(file) - if use_base64 and file.startswith('<') and file.endswith('>'): - file = file[1: -1] - body = resources.string(os.path.join(folder, file)) if use_base64: + path = os.path.join(folder, file[1: -1]) + body = resources.string(path, binary=True) body = base64.encode_as_text(body) + "\n" + else: + path = os.path.join(folder, file) + body = resources.string(path) return body def _is_base64(self, file): diff --git a/murano/engine/system/resource_manager.py b/murano/engine/system/resource_manager.py index be5a396b..4dcd3257 100644 --- a/murano/engine/system/resource_manager.py +++ b/murano/engine/system/resource_manager.py @@ -52,9 +52,10 @@ class ResourceManager(object): @staticmethod @specs.parameter('owner', dsl.MuranoTypeParameter(nullable=True)) @specs.inject('receiver', yaqltypes.Receiver()) - def string(receiver, name, owner=None): + def string(receiver, name, owner=None, binary=False): path = ResourceManager._get_package(owner, receiver).get_resource(name) - with open(path) as file: + mode = 'rb' if binary else 'rU' + with open(path, mode) as file: return file.read() @classmethod diff --git a/murano/tests/unit/engine/system/test_agent.py b/murano/tests/unit/engine/system/test_agent.py index 0cfca6b9..2b004ad5 100644 --- a/murano/tests/unit/engine/system/test_agent.py +++ b/murano/tests/unit/engine/system/test_agent.py @@ -13,8 +13,10 @@ # under the License. import os +import tempfile import mock +from oslo_serialization import base64 import yaml as yamllib from murano.dsl import murano_object @@ -309,3 +311,52 @@ class TestExecutionPlan(base.MuranoTestCase): mock_uuid4 = mock.patch('uuid.uuid4').start() mock_uuid4.side_effect = [FakeUUID(v) for v in values] return mock_uuid4 + + @mock.patch('murano.engine.system.resource_manager.ResourceManager' + '._get_package') + def test_file_line_endings(self, _get_package): + class FakeResources(object): + """Class with only string() method from ResourceManager class""" + @staticmethod + def string(name, owner=None, binary=False): + return resource_manager.ResourceManager.string( + receiver=None, name=name, owner=owner, binary=binary) + + # make path equal to provided name inside resources.string() + package = mock.Mock() + package.get_resource.side_effect = lambda m: m + _get_package.return_value = package + + text = b"First line\nSecond line\rThird line\r\nFourth line" + modified_text = u"First line\nSecond line\nThird line\nFourth line" + encoded_text = base64.encode_as_text(text) + "\n" + resources = FakeResources() + + with tempfile.NamedTemporaryFile() as script_file: + script_file.write(text) + script_file.file.flush() + os.fsync(script_file.file.fileno()) + + # check that data has been written correctly + script_file.seek(0) + file_data = script_file.read() + self.assertEqual(text, file_data) + + # check resources.string() output + # text file + result = resources.string(script_file.name) + self.assertEqual(modified_text, result) + # binary file + result = resources.string(script_file.name, binary=True) + self.assertEqual(text, result) + + # check _get_body() output + filename = os.path.basename(script_file.name) + folder = os.path.dirname(script_file.name) + # text file + body = self.agent._get_body(filename, resources, folder) + self.assertEqual(modified_text, body) + # binary file + filename = '<{0}>'.format(filename) + body = self.agent._get_body(filename, resources, folder) + self.assertEqual(encoded_text, body) diff --git a/releasenotes/notes/script-line-endings-db632db9e24237a3.yaml b/releasenotes/notes/script-line-endings-db632db9e24237a3.yaml new file mode 100644 index 00000000..fec1ac82 --- /dev/null +++ b/releasenotes/notes/script-line-endings-db632db9e24237a3.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - Text script files are opened in 'rU' mode which recognizes all types of + newlines, and binary files are opened in 'rb' mode to prevent their + corruption.