From 4877a389190f6598e13a0704e77d1c1586d25fb4 Mon Sep 17 00:00:00 2001 From: Martin Packman Date: Thu, 8 Mar 2012 17:56:10 +0000 Subject: Add tests for writing of userdata parts to the filesystem --- tests/unittests/test_userdata.py | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/unittests/test_userdata.py (limited to 'tests') diff --git a/tests/unittests/test_userdata.py b/tests/unittests/test_userdata.py new file mode 100644 index 00000000..949f36dc --- /dev/null +++ b/tests/unittests/test_userdata.py @@ -0,0 +1,60 @@ +"""Tests for handling of userdata within cloud init""" + +import logging +import StringIO + +from mocker import MockerTestCase + +import cloudinit +from cloudinit.DataSource import DataSource + + +instance_id = "i-testing" + + +class FakeDataSource(DataSource): + + def __init__(self, userdata): + self.metadata = {'instance-id': instance_id} + self.userdata_raw = userdata + + +class TestConsumeUserData(MockerTestCase): + + def setUp(self): + self.mock_write = self.mocker.replace("cloudinit.util.write_file", + passthrough=False) + self.mock_write(self.get_ipath("cloud_config"), "", 0600) + self.capture_log() + + def tearDown(self): + self._log.removeHandler(self._log_handler) + + @staticmethod + def get_ipath(name): + return "%s/instances/%s%s" % (cloudinit.varlibdir, instance_id, + cloudinit.pathmap[name]) + + def capture_log(self): + self.log_file = StringIO.StringIO() + self._log_handler = logging.StreamHandler(self.log_file) + self._log_handler.setLevel(logging.DEBUG) + self._log = logging.getLogger(cloudinit.logger_name) + self._log.addHandler(self._log_handler) + + def test_script(self): + script = "#!/bin/sh\necho hello\n" + outpath = cloudinit.get_ipath_cur("scripts") + "/part-001" + self.mock_write(outpath, script, 0700) + self.mocker.replay() + ci = cloudinit.CloudInit() + ci.datasource = FakeDataSource(script) + ci.consume_userdata() + self.assertEqual("", self.log_file.getvalue()) + + def test_unhandled_type_warning(self): + self.mocker.replay() + ci = cloudinit.CloudInit() + ci.datasource = FakeDataSource("arbitrary text\n") + ci.consume_userdata() + self.assertIn("Unhandled userdata part", self.log_file.getvalue()) -- cgit v1.2.3 From c1a006bb9ee915c08075e9adeab245f02452ee43 Mon Sep 17 00:00:00 2001 From: Martin Packman Date: Mon, 12 Mar 2012 13:56:40 +0000 Subject: Restrict warning to userdata without MIME wrapping only while still ignoring most types --- cloudinit/UserDataHandler.py | 4 ++-- cloudinit/__init__.py | 14 ++++++----- tests/unittests/test_userdata.py | 50 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 56 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/cloudinit/UserDataHandler.py b/cloudinit/UserDataHandler.py index 98729056..ec914480 100644 --- a/cloudinit/UserDataHandler.py +++ b/cloudinit/UserDataHandler.py @@ -180,7 +180,7 @@ def process_includes(msg, appendmsg=None): payload = part.get_payload(decode=True) - if ctype_orig == "text/plain": + if ctype_orig in ("text/plain", "text/x-not-multipart"): ctype = type_from_startswith(payload) if ctype is None: @@ -213,7 +213,7 @@ def message_from_string(data, headers=None): else: msg[key] = val else: - mtype = headers.get("Content-Type", "text/plain") + mtype = headers.get("Content-Type", "text/x-not-multipart") maintype, subtype = mtype.split("/", 1) msg = MIMEBase(maintype, subtype, *headers) msg.set_payload(data) diff --git a/cloudinit/__init__.py b/cloudinit/__init__.py index 98a48fec..1c3f861c 100644 --- a/cloudinit/__init__.py +++ b/cloudinit/__init__.py @@ -606,12 +606,14 @@ def partwalker_callback(pdata, ctype, filename, payload): partwalker_handle_handler(pdata, ctype, filename, payload) return if ctype not in pdata['handlers']: - start = payload.split("\n", 1)[0][:24] # Use first line or 24 bytes - if start < payload: - details = "starting '%s...'" % start.encode("string-escape") - else: - details = repr(payload) - log.warning("Unhandled userdata part of type %s %s", ctype, details) + if ctype == "text/x-not-multipart": + # Extract the first line or 24 bytes for displaying in the log + start = payload.split("\n", 1)[0][:24] + if start < payload: + details = "starting '%s...'" % start.encode("string-escape") + else: + details = repr(payload) + log.warning("Unhandled non-multipart userdata %s", details) return handler_handle_part(pdata['handlers'][ctype], pdata['data'], ctype, filename, payload, pdata['frequency']) diff --git a/tests/unittests/test_userdata.py b/tests/unittests/test_userdata.py index 949f36dc..37ad9b13 100644 --- a/tests/unittests/test_userdata.py +++ b/tests/unittests/test_userdata.py @@ -3,6 +3,8 @@ import logging import StringIO +from email.mime.base import MIMEBase + from mocker import MockerTestCase import cloudinit @@ -42,7 +44,28 @@ class TestConsumeUserData(MockerTestCase): self._log = logging.getLogger(cloudinit.logger_name) self._log.addHandler(self._log_handler) - def test_script(self): + def test_unhandled_type_warning(self): + """Raw text without magic is ignored but shows warning""" + self.mocker.replay() + ci = cloudinit.CloudInit() + ci.datasource = FakeDataSource("arbitrary text\n") + ci.consume_userdata() + self.assertEqual( + "Unhandled non-multipart userdata starting 'arbitrary text...'\n", + self.log_file.getvalue()) + + def test_mime_text_plain(self): + """Mime message of type text/plain is ignored without warning""" + self.mocker.replay() + ci = cloudinit.CloudInit() + message = MIMEBase("text", "plain") + message.set_payload("Just text") + ci.datasource = FakeDataSource(message.as_string()) + ci.consume_userdata() + self.assertEqual("", self.log_file.getvalue()) + + def test_shellscript(self): + """Raw text starting #!/bin/sh is treated as script""" script = "#!/bin/sh\necho hello\n" outpath = cloudinit.get_ipath_cur("scripts") + "/part-001" self.mock_write(outpath, script, 0700) @@ -52,9 +75,28 @@ class TestConsumeUserData(MockerTestCase): ci.consume_userdata() self.assertEqual("", self.log_file.getvalue()) - def test_unhandled_type_warning(self): + def test_mime_text_x_shellscript(self): + """Mime message of type text/x-shellscript is treated as script""" + script = "#!/bin/sh\necho hello\n" + outpath = cloudinit.get_ipath_cur("scripts") + "/part-001" + self.mock_write(outpath, script, 0700) self.mocker.replay() ci = cloudinit.CloudInit() - ci.datasource = FakeDataSource("arbitrary text\n") + message = MIMEBase("text", "x-shellscript") + message.set_payload(script) + ci.datasource = FakeDataSource(message.as_string()) ci.consume_userdata() - self.assertIn("Unhandled userdata part", self.log_file.getvalue()) + self.assertEqual("", self.log_file.getvalue()) + + def test_mime_text_plain_shell(self): + """Mime type text/plain starting #!/bin/sh is treated as script""" + script = "#!/bin/sh\necho hello\n" + outpath = cloudinit.get_ipath_cur("scripts") + "/part-001" + self.mock_write(outpath, script, 0700) + self.mocker.replay() + ci = cloudinit.CloudInit() + message = MIMEBase("text", "plain") + message.set_payload(script) + ci.datasource = FakeDataSource(message.as_string()) + ci.consume_userdata() + self.assertEqual("", self.log_file.getvalue()) -- cgit v1.2.3