diff options
-rw-r--r-- | cloudinit/signal_handler.py | 28 | ||||
-rw-r--r-- | tests/unittests/test_signal.py | 46 |
2 files changed, 68 insertions, 6 deletions
diff --git a/cloudinit/signal_handler.py b/cloudinit/signal_handler.py index 96b618f3..b6ea592b 100644 --- a/cloudinit/signal_handler.py +++ b/cloudinit/signal_handler.py @@ -24,10 +24,14 @@ import sys from StringIO import StringIO +from cloudinit import log as logging from cloudinit import util from cloudinit import version as vr +LOG = logging.getLogger(__name__) + +BACK_FRAME_TRACE_DEPTH = 3 EXIT_FOR = { signal.SIGINT: ('Cloud-init %(version)s interrupted, exiting...', 1), signal.SIGTERM: ('Cloud-init %(version)s terminated, exiting...', 1), @@ -37,24 +41,36 @@ EXIT_FOR = { } -def _pprint_frame(frame, depth=1, max_depth=3): +def _pprint_frame(frame, depth, max_depth, contents): if depth > max_depth or not frame: return frame_info = inspect.getframeinfo(frame) prefix = " " * (depth * 2) - contents = StringIO() contents.write("%sFilename: %s\n" % (prefix, frame_info.filename)) contents.write("%sFunction: %s\n" % (prefix, frame_info.function)) contents.write("%sLine number: %s\n" % (prefix, frame_info.lineno)) - util.multi_log(contents.getvalue()) - _pprint_frame(frame.f_back, depth + 1, max_depth) + _pprint_frame(frame.f_back, depth + 1, max_depth, contents) def _handle_exit(signum, frame): (msg, rc) = EXIT_FOR[signum] + # Reset logging so that only the basic logging + # is active since the state of syslog or other + # logging processes is unknown if we are being + # signaled by a reboot process which is external and + # killing other processes while this process is being + # finished off... + try: + logging.resetLogging() + logging.setupBasicLogging() + except: + pass msg = msg % ({'version': vr.version()}) - util.multi_log("%s\n" % (msg)) - _pprint_frame(frame) + contents = StringIO() + contents.write("%s\n" % (msg)) + _pprint_frame(frame, 1, BACK_FRAME_TRACE_DEPTH, contents) + util.multi_log(contents.getvalue(), + console=True, stderr=False, log=LOG) sys.exit(rc) diff --git a/tests/unittests/test_signal.py b/tests/unittests/test_signal.py new file mode 100644 index 00000000..02fd1ef1 --- /dev/null +++ b/tests/unittests/test_signal.py @@ -0,0 +1,46 @@ +"""Tests for handling of signals within cloud init.""" + +import os +import subprocess +import sys +import time + +from StringIO import StringIO + +from mocker import MockerTestCase + + +class TestSignal(MockerTestCase): + + def test_signal_output(self): + + # This is done since nose/unittest is actually setting up + # output capturing, signal handling itself, and its easier + # to just call out to cloudinit with a loop and see what the result is + run_what = [sys.executable, + '-c', ('import time; from cloudinit import signal_handler;' + 'signal_handler.attach_handlers(); time.sleep(120)')] + + pc_info = subprocess.Popen(run_what, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + + # Let it start up + time.sleep(0.5) + dead = None + while dead is None: + pc_info.terminate() + # Ok not dead yet. try again + time.sleep(0.5) + dead = pc_info.poll() + + outputs = StringIO() + if pc_info.stdout: + outputs.write(pc_info.stdout.read()) + if pc_info.stderr: + outputs.write(pc_info.stderr.read()) + val = outputs.getvalue() + print val + + # Check some of the outputs that should of happened + self.assertEquals(1, pc_info.wait()) + self.assertTrue(len(val) != 0) + self.assertTrue(val.find("terminated") != -1) |