def test_worker_disappears_in_cleanup(self): d = MyWorkerDaemon({'workers': 2}) strategy = daemon.DaemonStrategy(d, d.logger) strategy.register_worker_start('mock-pid-1', {'mock_options': True}) strategy.register_worker_start('mock-pid-2', {'mock_options': True}) self.assertEqual(strategy.unspawned_worker_options, []) self.assertEqual( strategy.options_by_pid, { 'mock-pid-1': { 'mock_options': True }, 'mock-pid-2': { 'mock_options': True }, }) with mock.patch('swift.common.daemon.os.kill') as mock_kill: mock_kill.side_effect = [ None, OSError(errno.ECHILD, os.strerror(errno.ECHILD)) ] strategy.cleanup() self.assertEqual(strategy.unspawned_worker_options, [{ 'mock_options': True }] * 2) self.assertEqual(strategy.options_by_pid, {}) self.assertEqual( set([ ('mock-pid-1', signal.SIGTERM), ('mock-pid-2', signal.SIGTERM), ]), set(c[0] for c in mock_kill.call_args_list)) self.assertEqual( set(d.logger.get_lines_for_level('debug')[-2:]), set([ 'Cleaned up worker mock-pid-1', 'Cleaned up worker mock-pid-2' ]))
def test_restart_workers(self): d = MyWorkerDaemon({'workers': 3}) strategy = daemon.DaemonStrategy(d, d.logger) d.health_side_effects = [True, False] with self.mock_os(): self.mock_kill.side_effect = lambda *args, **kwargs: setattr( strategy, 'running', False) strategy.run() # six workers forked in total self.assertEqual([mock.call()] * 6, self.mock_fork.call_args_list) # since the daemon starts healthy, first pass checks children once self.assertEqual(self.waitpid_calls, { 'mock-pid-0': 1, 'mock-pid-1': 1, 'mock-pid-2': 1, }) # second pass is not healthy, original pid's killed self.assertEqual( set([ ('mock-pid-0', signal.SIGTERM), ('mock-pid-1', signal.SIGTERM), ('mock-pid-2', signal.SIGTERM), ]), set(c[0] for c in self.mock_kill.call_args_list[:3])) # our mock_kill side effect breaks out of running, and cleanup kills # remaining pids self.assertEqual( set([ ('mock-pid-3', signal.SIGTERM), ('mock-pid-4', signal.SIGTERM), ('mock-pid-5', signal.SIGTERM), ]), set(c[0] for c in self.mock_kill.call_args_list[3:]))
def test_worker_disappears(self): d = MyWorkerDaemon({'workers': 3}) strategy = daemon.DaemonStrategy(d, d.logger) strategy.register_worker_start('mock-pid', {'mock_options': True}) self.assertEqual(strategy.unspawned_worker_options, []) self.assertEqual(strategy.options_by_pid, {'mock-pid': { 'mock_options': True }}) # still running with mock.patch('swift.common.daemon.os.waitpid') as mock_waitpid: mock_waitpid.return_value = (0, 0) strategy.check_on_all_running_workers() self.assertEqual(strategy.unspawned_worker_options, []) self.assertEqual(strategy.options_by_pid, {'mock-pid': { 'mock_options': True }}) # finished strategy = daemon.DaemonStrategy(d, d.logger) strategy.register_worker_start('mock-pid', {'mock_options': True}) with mock.patch('swift.common.daemon.os.waitpid') as mock_waitpid: mock_waitpid.return_value = ('mock-pid', 0) strategy.check_on_all_running_workers() self.assertEqual(strategy.unspawned_worker_options, [{ 'mock_options': True }]) self.assertEqual(strategy.options_by_pid, {}) self.assertEqual( d.logger.get_lines_for_level('debug')[-1], 'Worker mock-pid exited') # disappeared strategy = daemon.DaemonStrategy(d, d.logger) strategy.register_worker_start('mock-pid', {'mock_options': True}) with mock.patch('swift.common.daemon.os.waitpid') as mock_waitpid: mock_waitpid.side_effect = OSError(errno.ECHILD, os.strerror(errno.ECHILD)) mock_waitpid.return_value = ('mock-pid', 0) strategy.check_on_all_running_workers() self.assertEqual(strategy.unspawned_worker_options, [{ 'mock_options': True }]) self.assertEqual(strategy.options_by_pid, {}) self.assertEqual( d.logger.get_lines_for_level('notice')[-1], 'Worker mock-pid died')
def test_forked_worker(self): d = MyWorkerDaemon({'workers': 3}) strategy = daemon.DaemonStrategy(d, d.logger) with mock.patch('swift.common.daemon.os.fork') as mock_fork, \ mock.patch('swift.common.daemon.os._exit') as mock_exit: mock_fork.return_value = 0 mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, strategy.run, once=True) self.assertTrue(d.once_called)
def test_fork_workers(self): d = MyWorkerDaemon({'workers': 3}) strategy = daemon.DaemonStrategy(d, d.logger) with self.mock_os(): strategy.run(once=True) self.assertEqual([mock.call()] * 3, self.mock_fork.call_args_list) self.assertEqual(self.waitpid_calls, { 'mock-pid-0': 3, 'mock-pid-1': 3, 'mock-pid-2': 3, }) self.assertEqual([], self.mock_kill.call_args_list) self.assertIn('Finished', d.logger.get_lines_for_level('notice')[-1])
def test_signal(self): d = MyDaemon({}) with mock.patch('swift.common.daemon.signal') as mock_signal: mock_signal.SIGTERM = signal.SIGTERM daemon.DaemonStrategy(d, d.logger).run() signal_args, kwargs = mock_signal.signal.call_args sig, func = signal_args self.assertEqual(sig, signal.SIGTERM) with mock.patch('swift.common.daemon.os') as mock_os: func() self.assertEqual(mock_os.method_calls, [ mock.call.killpg(0, signal.SIGTERM), # hard exit because bare except handlers can trap SystemExit mock.call._exit(0) ])