diff --git a/cloudbaseinit/plugins/windows/userdata.py b/cloudbaseinit/plugins/windows/userdata.py index ef8f73b2..3bd60d8d 100644 --- a/cloudbaseinit/plugins/windows/userdata.py +++ b/cloudbaseinit/plugins/windows/userdata.py @@ -164,7 +164,7 @@ class UserDataPlugin(base.BasePlugin): if user_data.startswith(b'#cloud-config'): user_data_plugins = factory.load_plugins() cloud_config_plugin = user_data_plugins.get('text/cloud-config') - ret_val = cloud_config_plugin.process(user_data) + ret_val = cloud_config_plugin.process_non_multipart(user_data) else: ret_val = userdatautils.execute_user_data_script(user_data) diff --git a/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py b/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py index 8f7bb78d..85abd3c2 100644 --- a/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py +++ b/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py @@ -98,10 +98,15 @@ class CloudConfigPlugin(base.BaseUserDataPlugin): def __init__(self): super(CloudConfigPlugin, self).__init__("text/cloud-config") - def process(self, part): + def process_non_multipart(self, part): + """Process the given data, if it can be loaded through yaml.""" try: executor = CloudConfigPluginExecutor.from_yaml(part) except CloudConfigError: LOG.error("Could not process the type %r", type(part)) else: executor.execute() + + def process(self, part): + payload = part.get_payload() + self.process_non_multipart(payload) diff --git a/cloudbaseinit/tests/plugins/windows/test_userdata.py b/cloudbaseinit/tests/plugins/windows/test_userdata.py index 8f3cd489..2bcb5ae5 100644 --- a/cloudbaseinit/tests/plugins/windows/test_userdata.py +++ b/cloudbaseinit/tests/plugins/windows/test_userdata.py @@ -12,20 +12,34 @@ # License for the specific language governing permissions and limitations # under the License. +import os +import pkgutil +import tempfile import unittest try: import unittest.mock as mock except ImportError: import mock -from oslo.config import cfg from cloudbaseinit.metadata.services import base as metadata_services_base from cloudbaseinit.plugins import base from cloudbaseinit.plugins.windows import userdata from cloudbaseinit.tests.metadata import fake_json_response -CONF = cfg.CONF + +class FakeService(object): + def __init__(self, user_data): + self.user_data = user_data + + def get_user_data(self): + return self.user_data.encode() + + +def _create_tempfile(): + fd, tmp = tempfile.mkstemp() + os.close(fd) + return tmp class UserDataPluginTest(unittest.TestCase): @@ -281,5 +295,26 @@ class UserDataPluginTest(unittest.TestCase): user_data=user_data) mock_load_plugins.assert_called_once_with() - mock_cloud_config_plugin.process.assert_called_once_with(user_data) + (mock_cloud_config_plugin + .process_non_multipart + .assert_called_once_with(user_data)) self.assertEqual(mock_return_value, return_value) + + +class TestCloudConfig(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.plugin = userdata.UserDataPlugin() + cls.userdata = pkgutil.get_data('cloudbaseinit.tests.resources', + 'cloud_config_userdata').decode() + + def test_cloud_config_multipart(self): + tmp = _create_tempfile() + self.addCleanup(os.remove, tmp) + + service = FakeService(self.userdata.format(b64=tmp)) + self.plugin.execute(service, {}) + self.assertTrue(os.path.exists(tmp)) + + with open(tmp) as stream: + self.assertEqual('42', stream.read()) diff --git a/cloudbaseinit/tests/plugins/windows/userdataplugins/cloudconfigplugins/test_write_files.py b/cloudbaseinit/tests/plugins/windows/userdataplugins/cloudconfigplugins/test_write_files.py index a9da61bd..6cb3e550 100644 --- a/cloudbaseinit/tests/plugins/windows/userdataplugins/cloudconfigplugins/test_write_files.py +++ b/cloudbaseinit/tests/plugins/windows/userdataplugins/cloudconfigplugins/test_write_files.py @@ -90,7 +90,7 @@ class WriteFilesPluginTests(unittest.TestCase): """.format(tmp)) with testutils.LogSnatcher('cloudbaseinit.plugins.windows.' 'userdataplugins.cloudconfig') as snatcher: - self.plugin.process(code) + self.plugin.process_non_multipart(code) self.assertTrue(os.path.exists(tmp), "Expected path does not exist.") @@ -121,7 +121,7 @@ class WriteFilesPluginTests(unittest.TestCase): with testutils.LogSnatcher('cloudbaseinit.plugins.windows.' 'userdataplugins.cloudconfigplugins.' 'write_files') as snatcher: - self.plugin.process(code) + self.plugin.process_non_multipart(code) self.assertEqual(expected_return, snatcher.output) @@ -137,7 +137,7 @@ class WriteFilesPluginTests(unittest.TestCase): with testutils.LogSnatcher('cloudbaseinit.plugins.windows.' 'userdataplugins.cloudconfig') as snatcher: - self.plugin.process(code) + self.plugin.process_non_multipart(code) self.assertTrue(snatcher.output[0].startswith( "Processing plugin write_files failed")) diff --git a/cloudbaseinit/tests/plugins/windows/userdataplugins/test_cloudconfig.py b/cloudbaseinit/tests/plugins/windows/userdataplugins/test_cloudconfig.py index 9dfad980..f3eac368 100644 --- a/cloudbaseinit/tests/plugins/windows/userdataplugins/test_cloudconfig.py +++ b/cloudbaseinit/tests/plugins/windows/userdataplugins/test_cloudconfig.py @@ -64,7 +64,7 @@ class CloudConfigPluginTests(unittest.TestCase): def test_invalid_type(self): with testutils.LogSnatcher('cloudbaseinit.plugins.windows.' 'userdataplugins.cloudconfig') as snatcher: - self.plugin.process({'unsupported'}) + self.plugin.process_non_multipart({'unsupported'}) expected = ["Invalid yaml stream provided.", "Could not process the type %r" % set] diff --git a/cloudbaseinit/tests/resources/__init__.py b/cloudbaseinit/tests/resources/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cloudbaseinit/tests/resources/cloud_config_userdata b/cloudbaseinit/tests/resources/cloud_config_userdata new file mode 100644 index 00000000..9252bb71 --- /dev/null +++ b/cloudbaseinit/tests/resources/cloud_config_userdata @@ -0,0 +1,14 @@ +Content-Type: multipart/mixed; boundary="===============1598784645116016685==" +MIME-Version: 1.0 + +--===============1598784645116016685== +Content-Type: text/cloud-config; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; filename="cloud-config" + +write_files: +- encoding: b64 + content: NDI= + path: {b64} + permissions: '0644' \ No newline at end of file