summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Moser <smoser@ubuntu.com>2018-03-09 04:22:24 +0100
committerChad Smith <chad.smith@canonical.com>2018-03-09 04:22:24 +0100
commitb7a790246e52520b47a467fe89b81199b9cf03f6 (patch)
treef39c712b38233faad1896ef6de50b9f7a3a6bb6d
parent1e2e810f3f7cb6a163a0229ac37037e8c6744d72 (diff)
downloadvyos-cloud-init-b7a790246e52520b47a467fe89b81199b9cf03f6.tar.gz
vyos-cloud-init-b7a790246e52520b47a467fe89b81199b9cf03f6.zip
shellify: raise TypeError on bad input.
This makes 2 changes to shellify's behavior: a.) raise a TypeError rather than a RuntimeError. b.) raise a TypeError if input is not a list or tuple.
-rw-r--r--cloudinit/tests/test_util.py23
-rw-r--r--cloudinit/util.py14
-rw-r--r--tests/unittests/test_handler/test_handler_bootcmd.py7
3 files changed, 37 insertions, 7 deletions
diff --git a/cloudinit/tests/test_util.py b/cloudinit/tests/test_util.py
index ba6bf699..c3e2e404 100644
--- a/cloudinit/tests/test_util.py
+++ b/cloudinit/tests/test_util.py
@@ -44,3 +44,26 @@ class TestUtil(CiTestCase):
m_mount_info.return_value = ('/dev/sda1', 'btrfs', '/', 'ro,relatime')
is_rw = util.mount_is_read_write('/')
self.assertEqual(is_rw, False)
+
+
+class TestShellify(CiTestCase):
+
+ def test_input_dict_raises_type_error(self):
+ self.assertRaisesRegex(
+ TypeError, 'Input.*was.*dict.*xpected',
+ util.shellify, {'mykey': 'myval'})
+
+ def test_input_str_raises_type_error(self):
+ self.assertRaisesRegex(
+ TypeError, 'Input.*was.*str.*xpected', util.shellify, "foobar")
+
+ def test_value_with_int_raises_type_error(self):
+ self.assertRaisesRegex(
+ TypeError, 'shellify.*int', util.shellify, ["foo", 1])
+
+ def test_supports_strings_and_lists(self):
+ self.assertEqual(
+ '\n'.join(["#!/bin/sh", "echo hi mom", "'echo' 'hi dad'",
+ "'echo' 'hi' 'sis'", ""]),
+ util.shellify(["echo hi mom", ["echo", "hi dad"],
+ ('echo', 'hi', 'sis')]))
diff --git a/cloudinit/util.py b/cloudinit/util.py
index 1d6cd1c5..083a8efe 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -1922,6 +1922,11 @@ def abs_join(*paths):
# if it is an array, shell protect it (with single ticks)
# if it is a string, do nothing
def shellify(cmdlist, add_header=True):
+ if not isinstance(cmdlist, (tuple, list)):
+ raise TypeError(
+ "Input to shellify was type '%s'. Expected list or tuple." %
+ (type_utils.obj_name(cmdlist)))
+
content = ''
if add_header:
content += "#!/bin/sh\n"
@@ -1930,7 +1935,7 @@ def shellify(cmdlist, add_header=True):
for args in cmdlist:
# If the item is a list, wrap all items in single tick.
# If its not, then just write it directly.
- if isinstance(args, list):
+ if isinstance(args, (list, tuple)):
fixed = []
for f in args:
fixed.append("'%s'" % (six.text_type(f).replace("'", escaped)))
@@ -1940,9 +1945,10 @@ def shellify(cmdlist, add_header=True):
content = "%s%s\n" % (content, args)
cmds_made += 1
else:
- raise RuntimeError(("Unable to shellify type %s"
- " which is not a list or string")
- % (type_utils.obj_name(args)))
+ raise TypeError(
+ "Unable to shellify type '%s'. Expected list, string, tuple. "
+ "Got: %s" % (type_utils.obj_name(args), args))
+
LOG.debug("Shellified %s commands.", cmds_made)
return content
diff --git a/tests/unittests/test_handler/test_handler_bootcmd.py b/tests/unittests/test_handler/test_handler_bootcmd.py
index dbf43e0d..09d4c681 100644
--- a/tests/unittests/test_handler/test_handler_bootcmd.py
+++ b/tests/unittests/test_handler/test_handler_bootcmd.py
@@ -69,7 +69,7 @@ class TestBootcmd(CiTestCase):
cc_bootcmd.handle('cc_bootcmd', invalid_config, cc, LOG, [])
self.assertIn('Failed to shellify bootcmd', self.logs.getvalue())
self.assertEqual(
- "'int' object is not iterable",
+ "Input to shellify was type 'int'. Expected list or tuple.",
str(context_manager.exception))
@skipIf(_missing_jsonschema_dep, "No python-jsonschema dependency")
@@ -98,7 +98,7 @@ class TestBootcmd(CiTestCase):
invalid_config = {
'bootcmd': ['ls /', 20, ['wget', 'http://stuff/blah'], {'a': 'n'}]}
cc = self._get_cloud('ubuntu')
- with self.assertRaises(RuntimeError) as context_manager:
+ with self.assertRaises(TypeError) as context_manager:
cc_bootcmd.handle('cc_bootcmd', invalid_config, cc, LOG, [])
expected_warnings = [
'bootcmd.1: 20 is not valid under any of the given schemas',
@@ -110,7 +110,8 @@ class TestBootcmd(CiTestCase):
self.assertIn(warning, logs)
self.assertIn('Failed to shellify', logs)
self.assertEqual(
- 'Unable to shellify type int which is not a list or string',
+ ("Unable to shellify type 'int'. Expected list, string, tuple. "
+ "Got: 20"),
str(context_manager.exception))
def test_handler_creates_and_runs_bootcmd_script_with_instance_id(self):