summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Moser <smoser@ubuntu.com>2012-02-28 15:51:37 -0500
committerScott Moser <smoser@ubuntu.com>2012-02-28 15:51:37 -0500
commitb3522f1044b6023fbcaf8ddf7aaff7f0166ed4b3 (patch)
tree4b896528586c79019ab0d7a52a2641dddae0a581
parent3b3386dd794c9063db99fc0c9422119e8536b18d (diff)
parentf54e9394e9d9dd0496d8084f9ca9ff5abee5f41c (diff)
downloadvyos-cloud-init-b3522f1044b6023fbcaf8ddf7aaff7f0166ed4b3.tar.gz
vyos-cloud-init-b3522f1044b6023fbcaf8ddf7aaff7f0166ed4b3.zip
Catch exceptions from part-handlers and log the error before continuing.
This branch also adds tests for part-handler registration and part-handler handling.
-rw-r--r--cloudinit/__init__.py17
-rw-r--r--tests/unittests/test__init__.py195
-rw-r--r--tests/unittests/test_handler/test_handler_ca_certs.py (renamed from tests/unittests/test_handler_ca_certs.py)0
3 files changed, 204 insertions, 8 deletions
diff --git a/cloudinit/__init__.py b/cloudinit/__init__.py
index 0fc61795..ccaa28c8 100644
--- a/cloudinit/__init__.py
+++ b/cloudinit/__init__.py
@@ -60,7 +60,6 @@ import cPickle
import sys
import os.path
import errno
-import pwd
import subprocess
import yaml
import logging
@@ -572,10 +571,14 @@ def handler_handle_part(mod, data, ctype, filename, payload, frequency):
if not (modfreq == per_always or
(frequency == per_instance and modfreq == per_instance)):
return
- if mod.handler_version == 1:
- mod.handle_part(data, ctype, filename, payload)
- else:
- mod.handle_part(data, ctype, filename, payload, frequency)
+ try:
+ if mod.handler_version == 1:
+ mod.handle_part(data, ctype, filename, payload)
+ else:
+ mod.handle_part(data, ctype, filename, payload, frequency)
+ except:
+ util.logexc(log)
+ traceback.print_exc(file=sys.stderr)
def partwalker_handle_handler(pdata, _ctype, _filename, payload):
@@ -586,15 +589,13 @@ def partwalker_handle_handler(pdata, _ctype, _filename, payload):
modfname = modname + ".py"
util.write_file("%s/%s" % (pdata['handlerdir'], modfname), payload, 0600)
- pdata['handlercount'] = curcount + 1
-
try:
mod = __import__(modname)
handler_register(mod, pdata['handlers'], pdata['data'], frequency)
+ pdata['handlercount'] = curcount + 1
except:
util.logexc(log)
traceback.print_exc(file=sys.stderr)
- return
def partwalker_callback(pdata, ctype, filename, payload):
diff --git a/tests/unittests/test__init__.py b/tests/unittests/test__init__.py
new file mode 100644
index 00000000..e157fa77
--- /dev/null
+++ b/tests/unittests/test__init__.py
@@ -0,0 +1,195 @@
+from mocker import MockerTestCase, ANY, ARGS, KWARGS
+import os
+
+from cloudinit import (partwalker_handle_handler, handler_handle_part,
+ handler_register)
+from cloudinit.util import write_file, logexc
+
+
+class TestPartwalkerHandleHandler(MockerTestCase):
+ def setUp(self):
+ self.data = {
+ "handlercount": 0,
+ "frequency": "?",
+ "handlerdir": "?",
+ "handlers": [],
+ "data": None}
+
+ self.expected_module_name = "part-handler-%03d" % (
+ self.data["handlercount"],)
+ expected_file_name = "%s.py" % self.expected_module_name
+ expected_file_fullname = os.path.join(self.data["handlerdir"],
+ expected_file_name)
+ self.module_fake = "fake module handle"
+ self.ctype = None
+ self.filename = None
+ self.payload = "dummy payload"
+
+ # Mock the write_file function
+ write_file_mock = self.mocker.replace(write_file, passthrough=False)
+ write_file_mock(expected_file_fullname, self.payload, 0600)
+
+ def test_no_errors(self):
+ """Payload gets written to file and added to C{pdata}."""
+ # Mock the __import__ builtin
+ import_mock = self.mocker.replace("__builtin__.__import__")
+ import_mock(self.expected_module_name)
+ self.mocker.result(self.module_fake)
+ # Mock the handle_register function
+ handle_reg_mock = self.mocker.replace(handler_register,
+ passthrough=False)
+ handle_reg_mock(self.module_fake, self.data["handlers"],
+ self.data["data"], self.data["frequency"])
+ # Activate mocks
+ self.mocker.replay()
+
+ partwalker_handle_handler(self.data, self.ctype, self.filename,
+ self.payload)
+
+ self.assertEqual(1, self.data["handlercount"])
+
+ def test_import_error(self):
+ """Module import errors are logged. No handler added to C{pdata}"""
+ # Mock the __import__ builtin
+ import_mock = self.mocker.replace("__builtin__.__import__")
+ import_mock(self.expected_module_name)
+ self.mocker.throw(ImportError())
+ # Mock log function
+ logexc_mock = self.mocker.replace(logexc, passthrough=False)
+ logexc_mock(ANY)
+ # Mock the print_exc function
+ print_exc_mock = self.mocker.replace("traceback.print_exc",
+ passthrough=False)
+ print_exc_mock(ARGS, KWARGS)
+ # Activate mocks
+ self.mocker.replay()
+
+ partwalker_handle_handler(self.data, self.ctype, self.filename,
+ self.payload)
+
+ self.assertEqual(0, self.data["handlercount"])
+
+ def test_attribute_error(self):
+ """Attribute errors are logged. No handler added to C{pdata}"""
+ # Mock the __import__ builtin
+ import_mock = self.mocker.replace("__builtin__.__import__")
+ import_mock(self.expected_module_name)
+ self.mocker.result(self.module_fake)
+ # Mock the handle_register function
+ handle_reg_mock = self.mocker.replace(handler_register,
+ passthrough=False)
+ handle_reg_mock(self.module_fake, self.data["handlers"],
+ self.data["data"], self.data["frequency"])
+ self.mocker.throw(AttributeError())
+ # Mock log function
+ logexc_mock = self.mocker.replace(logexc, passthrough=False)
+ logexc_mock(ANY)
+ # Mock the print_exc function
+ print_exc_mock = self.mocker.replace("traceback.print_exc",
+ passthrough=False)
+ print_exc_mock(ARGS, KWARGS)
+ # Activate mocks
+ self.mocker.replay()
+
+ partwalker_handle_handler(self.data, self.ctype, self.filename,
+ self.payload)
+
+ self.assertEqual(0, self.data["handlercount"])
+
+
+class TestHandlerHandlePart(MockerTestCase):
+ def setUp(self):
+ self.data = "fake data"
+ self.ctype = "fake ctype"
+ self.filename = "fake filename"
+ self.payload = "fake payload"
+ self.frequency = "once-per-instance"
+
+ def test_normal_version_1(self):
+ """
+ C{handle_part} is called without C{frequency} for
+ C{handler_version} == 1.
+ """
+ # Build a mock part-handler module
+ mod_mock = self.mocker.mock()
+ getattr(mod_mock, "frequency")
+ self.mocker.result("once-per-instance")
+ getattr(mod_mock, "handler_version")
+ self.mocker.result(1)
+ mod_mock.handle_part(self.data, self.ctype, self.filename,
+ self.payload)
+ self.mocker.replay()
+
+ handler_handle_part(mod_mock, self.data, self.ctype, self.filename,
+ self.payload, self.frequency)
+
+ def test_normal_version_2(self):
+ """
+ C{handle_part} is called with C{frequency} for
+ C{handler_version} == 2.
+ """
+ # Build a mock part-handler module
+ mod_mock = self.mocker.mock()
+ getattr(mod_mock, "frequency")
+ self.mocker.result("once-per-instance")
+ getattr(mod_mock, "handler_version")
+ self.mocker.result(2)
+ mod_mock.handle_part(self.data, self.ctype, self.filename,
+ self.payload, self.frequency)
+ self.mocker.replay()
+
+ handler_handle_part(mod_mock, self.data, self.ctype, self.filename,
+ self.payload, self.frequency)
+
+ def test_modfreq_per_always(self):
+ """
+ C{handle_part} is called regardless of frequency if nofreq is always.
+ """
+ self.frequency = "once"
+ # Build a mock part-handler module
+ mod_mock = self.mocker.mock()
+ getattr(mod_mock, "frequency")
+ self.mocker.result("always")
+ getattr(mod_mock, "handler_version")
+ self.mocker.result(1)
+ mod_mock.handle_part(self.data, self.ctype, self.filename,
+ self.payload)
+ self.mocker.replay()
+
+ handler_handle_part(mod_mock, self.data, self.ctype, self.filename,
+ self.payload, self.frequency)
+
+ def test_no_handle_when_modfreq_once(self):
+ """C{handle_part} is not called if frequency is once"""
+ self.frequency = "once"
+ # Build a mock part-handler module
+ mod_mock = self.mocker.mock()
+ getattr(mod_mock, "frequency")
+ self.mocker.result("once-per-instance")
+ self.mocker.replay()
+
+ handler_handle_part(mod_mock, self.data, self.ctype, self.filename,
+ self.payload, self.frequency)
+
+ def test_exception_is_caught(self):
+ """Exceptions within C{handle_part} are caught and logged."""
+ # Build a mock part-handler module
+ mod_mock = self.mocker.mock()
+ getattr(mod_mock, "frequency")
+ self.mocker.result("once-per-instance")
+ getattr(mod_mock, "handler_version")
+ self.mocker.result(1)
+ mod_mock.handle_part(self.data, self.ctype, self.filename,
+ self.payload)
+ self.mocker.throw(Exception())
+ # Mock log function
+ logexc_mock = self.mocker.replace(logexc, passthrough=False)
+ logexc_mock(ANY)
+ # Mock the print_exc function
+ print_exc_mock = self.mocker.replace("traceback.print_exc",
+ passthrough=False)
+ print_exc_mock(ARGS, KWARGS)
+ self.mocker.replay()
+
+ handler_handle_part(mod_mock, self.data, self.ctype, self.filename,
+ self.payload, self.frequency)
diff --git a/tests/unittests/test_handler_ca_certs.py b/tests/unittests/test_handler/test_handler_ca_certs.py
index d6513b5b..d6513b5b 100644
--- a/tests/unittests/test_handler_ca_certs.py
+++ b/tests/unittests/test_handler/test_handler_ca_certs.py