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.