Skip to content

Commit c1a006b

Browse files
committed
Restrict warning to userdata without MIME wrapping only while still ignoring most types
1 parent de31608 commit c1a006b

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

cloudinit/UserDataHandler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def process_includes(msg, appendmsg=None):
180180

181181
payload = part.get_payload(decode=True)
182182

183-
if ctype_orig == "text/plain":
183+
if ctype_orig in ("text/plain", "text/x-not-multipart"):
184184
ctype = type_from_startswith(payload)
185185

186186
if ctype is None:
@@ -213,7 +213,7 @@ def message_from_string(data, headers=None):
213213
else:
214214
msg[key] = val
215215
else:
216-
mtype = headers.get("Content-Type", "text/plain")
216+
mtype = headers.get("Content-Type", "text/x-not-multipart")
217217
maintype, subtype = mtype.split("/", 1)
218218
msg = MIMEBase(maintype, subtype, *headers)
219219
msg.set_payload(data)

cloudinit/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -606,12 +606,14 @@ def partwalker_callback(pdata, ctype, filename, payload):
606606
partwalker_handle_handler(pdata, ctype, filename, payload)
607607
return
608608
if ctype not in pdata['handlers']:
609-
start = payload.split("\n", 1)[0][:24] # Use first line or 24 bytes
610-
if start < payload:
611-
details = "starting '%s...'" % start.encode("string-escape")
612-
else:
613-
details = repr(payload)
614-
log.warning("Unhandled userdata part of type %s %s", ctype, details)
609+
if ctype == "text/x-not-multipart":
610+
# Extract the first line or 24 bytes for displaying in the log
611+
start = payload.split("\n", 1)[0][:24]
612+
if start < payload:
613+
details = "starting '%s...'" % start.encode("string-escape")
614+
else:
615+
details = repr(payload)
616+
log.warning("Unhandled non-multipart userdata %s", details)
615617
return
616618
handler_handle_part(pdata['handlers'][ctype], pdata['data'],
617619
ctype, filename, payload, pdata['frequency'])

tests/unittests/test_userdata.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import logging
44
import StringIO
55

6+
from email.mime.base import MIMEBase
7+
68
from mocker import MockerTestCase
79

810
import cloudinit
@@ -42,7 +44,28 @@ def capture_log(self):
4244
self._log = logging.getLogger(cloudinit.logger_name)
4345
self._log.addHandler(self._log_handler)
4446

45-
def test_script(self):
47+
def test_unhandled_type_warning(self):
48+
"""Raw text without magic is ignored but shows warning"""
49+
self.mocker.replay()
50+
ci = cloudinit.CloudInit()
51+
ci.datasource = FakeDataSource("arbitrary text\n")
52+
ci.consume_userdata()
53+
self.assertEqual(
54+
"Unhandled non-multipart userdata starting 'arbitrary text...'\n",
55+
self.log_file.getvalue())
56+
57+
def test_mime_text_plain(self):
58+
"""Mime message of type text/plain is ignored without warning"""
59+
self.mocker.replay()
60+
ci = cloudinit.CloudInit()
61+
message = MIMEBase("text", "plain")
62+
message.set_payload("Just text")
63+
ci.datasource = FakeDataSource(message.as_string())
64+
ci.consume_userdata()
65+
self.assertEqual("", self.log_file.getvalue())
66+
67+
def test_shellscript(self):
68+
"""Raw text starting #!/bin/sh is treated as script"""
4669
script = "#!/bin/sh\necho hello\n"
4770
outpath = cloudinit.get_ipath_cur("scripts") + "/part-001"
4871
self.mock_write(outpath, script, 0700)
@@ -52,9 +75,28 @@ def test_script(self):
5275
ci.consume_userdata()
5376
self.assertEqual("", self.log_file.getvalue())
5477

55-
def test_unhandled_type_warning(self):
78+
def test_mime_text_x_shellscript(self):
79+
"""Mime message of type text/x-shellscript is treated as script"""
80+
script = "#!/bin/sh\necho hello\n"
81+
outpath = cloudinit.get_ipath_cur("scripts") + "/part-001"
82+
self.mock_write(outpath, script, 0700)
5683
self.mocker.replay()
5784
ci = cloudinit.CloudInit()
58-
ci.datasource = FakeDataSource("arbitrary text\n")
85+
message = MIMEBase("text", "x-shellscript")
86+
message.set_payload(script)
87+
ci.datasource = FakeDataSource(message.as_string())
5988
ci.consume_userdata()
60-
self.assertIn("Unhandled userdata part", self.log_file.getvalue())
89+
self.assertEqual("", self.log_file.getvalue())
90+
91+
def test_mime_text_plain_shell(self):
92+
"""Mime type text/plain starting #!/bin/sh is treated as script"""
93+
script = "#!/bin/sh\necho hello\n"
94+
outpath = cloudinit.get_ipath_cur("scripts") + "/part-001"
95+
self.mock_write(outpath, script, 0700)
96+
self.mocker.replay()
97+
ci = cloudinit.CloudInit()
98+
message = MIMEBase("text", "plain")
99+
message.set_payload(script)
100+
ci.datasource = FakeDataSource(message.as_string())
101+
ci.consume_userdata()
102+
self.assertEqual("", self.log_file.getvalue())

0 commit comments

Comments
 (0)