def test_perms_block_panic_stop(self): ''' Test that non-root can't run the important functions. ''' oneoff = PuppetctlExecution(self.test_statefile) with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=False): with self.assertRaises(SystemExit) as fail_break, \ mock.patch('sys.stdout', new=StringIO()): oneoff.panic_stop(True) self.assertEqual(fail_break.exception.code, 2)
class TestExecutionPanicStop(unittest.TestCase): ''' Class of tests about executing puppetctl panic_stop commands. ''' def setUp(self): ''' Preparing test rig ''' self.test_statefile = '/tmp/exec-panic_stop-statefile-mods.test.txt' self.library = PuppetctlExecution(self.test_statefile) self.library.logging_tag = 'testingpuppetctl[{}]'.format(self.library.invoking_user) def tearDown(self): ''' Cleanup test rig ''' try: os.remove(self.test_statefile) except OSError: # pragma: no cover # we likely never created the file. pass def test_perms_block_panic_stop(self): ''' Test that non-root can't run the important functions. ''' oneoff = PuppetctlExecution(self.test_statefile) with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=False): with self.assertRaises(SystemExit) as fail_break, \ mock.patch('sys.stdout', new=StringIO()): oneoff.panic_stop(True) self.assertEqual(fail_break.exception.code, 2) def test_panic_stop_nothing_to_do(self): ''' Test that 'panic_stop' handles no-processes-to-kill nicely. ''' with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=True), \ mock.patch.object(PuppetctlExecution, '_puppet_processes_running', return_value={}), \ self.assertRaises(SystemExit) as exit_panicstop, \ mock.patch('sys.stdout', new=StringIO()) as fake_out: self.library.panic_stop(False) self.assertIn("No running 'puppet agent' found", fake_out.getvalue()) self.assertEqual(exit_panicstop.exception.code, 0) def test_panic_stop_well_behaved(self): ''' Test that 'panic_stop' leaves quietly if a process exits cleanly. ''' with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=True), \ mock.patch.object(PuppetctlExecution, '_puppet_processes_running', side_effect=[{'123': 'test-puppet agent'}, {}]), \ self.assertRaises(SystemExit) as exit_panicstop, \ mock.patch('os.kill') as mock_kill, \ mock.patch('time.sleep') as mock_sleep, \ mock.patch('sys.stdout', new=StringIO()) as fake_out: self.library.panic_stop(False) mock_kill.assert_called_once_with(123, signal.SIGTERM) mock_sleep.assert_called_once_with(2) self.assertIn("Sending SIGTERM to pid 123 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("No running 'puppet agent' found", fake_out.getvalue()) self.assertEqual(exit_panicstop.exception.code, 0) with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=True), \ mock.patch.object(PuppetctlExecution, '_puppet_processes_running', side_effect=[{'234': 'test-puppet agent'}, {}]), \ self.assertRaises(SystemExit) as exit_panicstop, \ mock.patch('os.kill') as mock_kill, \ mock.patch('time.sleep') as mock_sleep, \ mock.patch('sys.stdout', new=StringIO()) as fake_out: self.library.panic_stop(True) mock_kill.assert_called_once_with(234, signal.SIGTERM) mock_sleep.assert_not_called() self.assertIn("Sending SIGTERM to pid 234 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("No running 'puppet agent' found", fake_out.getvalue()) self.assertEqual(exit_panicstop.exception.code, 0) def test_panic_stop_stubborn(self): ''' Test that 'panic_stop' fires a kill if term wasn't enough. ''' with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=True), \ mock.patch.object(PuppetctlExecution, '_puppet_processes_running', side_effect=[{'123': 'test-puppet agent'}, {'123': 'test-puppet agent'}, {}]), \ self.assertRaises(SystemExit) as exit_panicstop, \ mock.patch('os.kill') as mock_kill, \ mock.patch('time.sleep') as mock_sleep, \ mock.patch('sys.stdout', new=StringIO()) as fake_out: self.library.panic_stop(False) mock_kill.assert_any_call(123, signal.SIGTERM) mock_sleep.assert_any_call(2) # the non-force pause mock_kill.assert_called_with(123, signal.SIGKILL) mock_sleep.assert_any_call(1) # the mandatory pause after the kill signal self.assertIn("Sending SIGTERM to pid 123 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("Sending SIGKILL to pid 123 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("No running 'puppet agent' found", fake_out.getvalue()) self.assertEqual(exit_panicstop.exception.code, 0) with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=True), \ mock.patch.object(PuppetctlExecution, '_puppet_processes_running', side_effect=[{'234': 'test-puppet agent'}, {'234': 'test-puppet agent'}, {}]), \ self.assertRaises(SystemExit) as exit_panicstop, \ mock.patch('os.kill') as mock_kill, \ mock.patch('time.sleep') as mock_sleep, \ mock.patch('sys.stdout', new=StringIO()) as fake_out: self.library.panic_stop(True) mock_kill.assert_any_call(234, signal.SIGTERM) mock_kill.assert_called_with(234, signal.SIGKILL) mock_sleep.assert_any_call(1) # the mandatory pause after the kill signal self.assertIn("Sending SIGTERM to pid 234 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("Sending SIGKILL to pid 234 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("No running 'puppet agent' found", fake_out.getvalue()) self.assertEqual(exit_panicstop.exception.code, 0) def test_panic_stop_zombies(self): ''' Test that 'panic_stop' complains if a process outlives a sigkill. ''' with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=True), \ mock.patch.object(PuppetctlExecution, '_puppet_processes_running', side_effect=[{'123': 'test-puppet agent'}, {'123': 'test-puppet agent'}, {'123': 'test-puppet agent'}]), \ self.assertRaises(SystemExit) as exit_panicstop, \ mock.patch('os.kill') as mock_kill, \ mock.patch('time.sleep') as mock_sleep, \ mock.patch('sys.stdout', new=StringIO()) as fake_out: self.library.panic_stop(False) mock_kill.assert_any_call(123, signal.SIGTERM) mock_sleep.assert_any_call(2) # the non-force pause mock_kill.assert_called_with(123, signal.SIGKILL) mock_sleep.assert_any_call(1) # the mandatory pause after the kill signal self.assertIn("Sending SIGTERM to pid 123 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("Sending SIGKILL to pid 123 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("pid 123 / 'test-puppet agent' did NOT die", fake_out.getvalue()) self.assertEqual(exit_panicstop.exception.code, 1) with mock.patch.object(PuppetctlExecution, '_allowed_to_run_command', return_value=True), \ mock.patch.object(PuppetctlExecution, '_puppet_processes_running', side_effect=[{'234': 'test-puppet agent'}, {'234': 'test-puppet agent'}, {'234': 'test-puppet agent'}]), \ self.assertRaises(SystemExit) as exit_panicstop, \ mock.patch('os.kill') as mock_kill, \ mock.patch('time.sleep') as mock_sleep, \ mock.patch('sys.stdout', new=StringIO()) as fake_out: self.library.panic_stop(True) mock_kill.assert_any_call(234, signal.SIGTERM) mock_kill.assert_called_with(234, signal.SIGKILL) mock_sleep.assert_any_call(1) # the mandatory pause after the kill signal self.assertIn("Sending SIGTERM to pid 234 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("Sending SIGKILL to pid 234 / 'test-puppet agent'", fake_out.getvalue()) self.assertIn("pid 234 / 'test-puppet agent' did NOT die", fake_out.getvalue()) self.assertEqual(exit_panicstop.exception.code, 1)