From f09dc6431a624e45aa2ac4d00cfe400f49b3331c Mon Sep 17 00:00:00 2001 From: Madalin Date: Mon, 2 Dec 2019 16:16:20 +0200 Subject: [PATCH] write_files: Added option to append the file with given content If the option is set to true, the code will append the file with given content. Cloud-config example: write_files: - content: "append" path: 'C:\\path.txt' append: true Fixes: https://github.com/cloudbase/cloudbase-init/issues/25 Change-Id: Ifd3d21842042dd34f7fde38a992e7fd74c1a595d --- .../cloudconfigplugins/write_files.py | 8 ++- .../cloudconfigplugins/test_write_files.py | 49 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/cloudbaseinit/plugins/common/userdataplugins/cloudconfigplugins/write_files.py b/cloudbaseinit/plugins/common/userdataplugins/cloudconfigplugins/write_files.py index c966442c..1ea3cced 100644 --- a/cloudbaseinit/plugins/common/userdataplugins/cloudconfigplugins/write_files.py +++ b/cloudbaseinit/plugins/common/userdataplugins/cloudconfigplugins/write_files.py @@ -132,6 +132,7 @@ class WriteFilesPlugin(base.BaseCloudConfigPlugin): permissions: The octal permissions set that should be given for this file. encoding: An optional encoding specification for the file. + append: An optional flag to append the content The only required keys in this dictionary are `path` and `content`. """ @@ -146,7 +147,12 @@ class WriteFilesPlugin(base.BaseCloudConfigPlugin): content = _process_content(item['content'], item.get('encoding')) permissions = _convert_permissions(item.get('permissions')) - _write_file(path, content, permissions) + + open_mode = "wb" + if item.get('append', False): + open_mode = "ab" + + _write_file(path, content, permissions, open_mode) def process(self, data): """Process the given data received from the cloud-config userdata. diff --git a/cloudbaseinit/tests/plugins/common/userdataplugins/cloudconfigplugins/test_write_files.py b/cloudbaseinit/tests/plugins/common/userdataplugins/cloudconfigplugins/test_write_files.py index 73707b33..c769e5d4 100644 --- a/cloudbaseinit/tests/plugins/common/userdataplugins/cloudconfigplugins/test_write_files.py +++ b/cloudbaseinit/tests/plugins/common/userdataplugins/cloudconfigplugins/test_write_files.py @@ -266,3 +266,52 @@ class WriteFilesPluginTests(unittest.TestCase): expected = "Can't process the type of data %r" % type(1) self.assertEqual(expected, str(cm.exception)) + + def test_process_item_fail(self): + fake_data = {} + + with testutils.LogSnatcher('cloudbaseinit.plugins.common.' + 'userdataplugins.cloudconfigplugins.' + 'write_files') as snatcher: + write_files.WriteFilesPlugin()._process_item(fake_data) + + self.assertEqual(['Missing required keys from file information {}'], + snatcher.output) + + @mock.patch('cloudbaseinit.plugins.common.userdataplugins.' + 'cloudconfigplugins.write_files._process_content') + @mock.patch('cloudbaseinit.plugins.common.userdataplugins.' + 'cloudconfigplugins.write_files._write_file') + @mock.patch('os.path.abspath') + def _test_process_item(self, fake_data, + mock_os_path, + mock_write_file, + mock_process_content): + fake_path = mock.MagicMock() + mock_os_path.return_value = fake_path + + fake_content = mock.MagicMock() + mock_process_content.return_value = fake_content + + with testutils.LogSnatcher('cloudbaseinit.plugins.common.' + 'userdataplugins.cloudconfigplugins.' + 'write_files') as snatcher: + write_files.WriteFilesPlugin()._process_item(fake_data) + + self.assertEqual(['Fail to process permissions None, assuming 420'], + snatcher.output) + + open_mode = 'wb' + if fake_data.get('append', False) is True: + open_mode = 'ab' + + mock_write_file.assert_called_with(fake_path, fake_content, 420, + open_mode) + + def test_process_item_write(self): + self._test_process_item( + {'path': 'fake_path', 'content': 'fake_content', 'append': False}) + + def test_process_item_append(self): + self._test_process_item( + {'path': 'fake_path', 'content': 'fake_content', 'append': True})