summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/signal_handler.py28
-rw-r--r--tests/unittests/test_signal.py46
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)