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})