def test_killWorker_spawns_another(self): service = WorkersService(reactor, worker_count=1, worker_cmd="cat") dv = DeferredValue() original_unregisterWorker = service.unregisterWorker def mock_unregisterWorker(*args, **kwargs): original_unregisterWorker(*args, **kwargs) dv.set(None) self.patch( service, "unregisterWorker" ).side_effect = mock_unregisterWorker try: service.startService() self.assertEquals(1, len(service.workers)) pid = list(service.workers.keys())[0] service.killWorker(pid) yield dv.get(timeout=2) self.assertNotIn(pid, service.workers) self.assertEquals(1, len(service.workers)) finally: service.stopService()
def registerEventType(self, event_type): """Ensure that `event_type` is known to the region. This populates the cache used by `ensureEventTypeRegistered` but does not consult it; it always attempts to contact the region. :return: :class:`Deferred` """ details = EVENT_DETAILS[event_type] def register(client): return client(RegisterEventType, name=event_type, level=details.level, description=details.description) d = maybeDeferred(getRegionClient).addCallback(register) # Whatever happens, we are now done registering. d.addBoth(callOut, self._types_registering.pop, event_type) # On success, record that the event type has been registered. On # failure, ensure that the set of registered event types does NOT # contain the event type. d.addCallbacks(callback=callOut, callbackArgs=(self._types_registered.add, event_type), errback=callOut, errbackArgs=(self._types_registered.discard, event_type)) # Capture the result into a DeferredValue. result = DeferredValue() result.capture(d) # Keep track of it so that concurrent requests don't duplicate work. self._types_registering[event_type] = result return result.get()
def test__calls_handler_on_notification(self): listener = PostgresListenerService() dv = DeferredValue() listener.register("machine", lambda *args: dv.set(args)) yield listener.startService() try: yield deferToDatabase(self.send_notification, "machine_create", 1) yield dv.get(timeout=2) self.assertEqual(('create', '1'), dv.value) finally: yield listener.stopService()
def test_calls_handler_on_notification(self): listener = PostgresListenerService() listener.HANDLE_NOTIFY_DELAY = listener.CHANNEL_REGISTRAR_DELAY = 0 dv = DeferredValue() listener.register("machine", lambda *args: dv.set(args)) yield listener.startService() yield listener.channelRegistrarDone try: yield deferToDatabase(self.send_notification, "machine_create", 1) yield dv.get(timeout=2) self.assertEqual(("create", "1"), dv.value) finally: yield listener.stopService()
def test__calls_handler_on_notification_with_delayed_registration(self): listener = PostgresListenerService() dv = DeferredValue() yield listener.startService() try: # Register after the service has been started. The handler should # still be called. listener.register("machine", lambda *args: dv.set(args)) yield deferToDatabase(self.send_notification, "machine_create", 1) yield dv.get(timeout=2) self.assertEqual(('create', '1'), dv.value) finally: yield listener.stopService()
def test__calls_system_handler_on_notification(self): listener = PostgresListenerService() # Change notifications to a frozenset. This makes sure that # the system message does not go into the queue. Instead if should # call the handler directly in `doRead`. listener.notifications = frozenset() dv = DeferredValue() listener.register("sys_test", lambda *args: dv.set(args)) yield listener.startService() try: yield deferToDatabase(self.send_notification, "sys_test", 1) yield dv.get(timeout=2) self.assertEqual(("sys_test", "1"), dv.value) finally: yield listener.stopService()
def test_stopService_doesnt(self): service = WorkersService(reactor, worker_count=1, worker_cmd="cat") dv = DeferredValue() original_unregisterWorker = service.unregisterWorker def mock_unregisterWorker(*args, **kwargs): original_unregisterWorker(*args, **kwargs) dv.set(None) self.patch(service, "unregisterWorker").side_effect = mock_unregisterWorker try: service.startService() self.assertEqual(1, len(service.workers)) finally: service.stopService() yield dv.get(timeout=2) self.assertEqual({}, service.workers)
def test_stopService_doesnt(self): service = HTTPImageService(reactor, 1, worker_cmd='cat') dv = DeferredValue() original_unregisterWorker = service.unregisterWorker def mock_unregisterWorker(*args, **kwargs): original_unregisterWorker(*args, **kwargs) dv.set(None) self.patch(service, 'unregisterWorker').side_effect = (mock_unregisterWorker) try: service.startService() self.assertEquals(1, len(service.workers)) finally: service.stopService() yield dv.get(timeout=2) self.assertEqual({}, service.workers)
def test_stopping_cancels_start(self): listener = PostgresListenerService() # Start then stop immediately, without waiting for start to complete. starting = listener.startService() starting_spy = DeferredValue() starting_spy.observe(starting) stopping = listener.stopService() # Both `starting` and `stopping` have callbacks yet to fire. self.assertThat(starting.callbacks, Not(Equals([]))) self.assertThat(stopping.callbacks, Not(Equals([]))) # Wait for the listener to stop. yield stopping # Neither `starting` nor `stopping` have callbacks. This is because # `stopping` chained itself onto the end of `starting`. self.assertThat(starting.callbacks, Equals([])) self.assertThat(stopping.callbacks, Equals([])) # Confirmation that `starting` was cancelled. with ExpectedException(CancelledError): yield starting_spy.get()
def test_processNotification_gets_called_with_notification(self): socket_path = self.patch_socket_path() service = LeaseSocketService(sentinel.service, reactor) dv = DeferredValue() # Mock processNotifcation to catch the call. def mock_processNotification(*args, **kwargs): dv.set(args) self.patch(service, "processNotification", mock_processNotification) # Start the service and stop it at the end of the test. service.startService() self.addCleanup(service.stopService) # Create test payload to send. packet = {"test": factory.make_name("test")} # Send notification to the socket and wait for notification. yield deferToThread(self.send_notification, socket_path, packet) yield dv.get(timeout=10) # Packet should be the argument passed to processNotifcation self.assertEquals((packet, ), dv.value)
class IPCWorkerService(service.Service, object): """ IPC worker service. Provides the worker side of the IPC communication to the master. """ def __init__(self, reactor, socket_path=None): super(IPCWorkerService, self).__init__() self.reactor = reactor self.socket_path = socket_path if self.socket_path is None: self.socket_path = get_ipc_socket_path() self.endpoint = UNIXClientEndpoint(reactor, self.socket_path) self._protocol = None self.protocol = DeferredValue() self.processId = DeferredValue() @asynchronous def startService(self): """Connect to UNIX socket.""" super(IPCWorkerService, self).startService() protocol = IPCWorker() protocol.service = self self.starting = connectProtocol(self.endpoint, protocol) def save_protocol(protocol): self._protocol = protocol def log_failure(failure): if failure.check(CancelledError): log.msg("IPCWorkerService start-up has been cancelled.") else: log.err(failure, "IPCWorkerService start-up failed.") self.starting.addCallback(save_protocol) self.starting.addErrback(log_failure) # Twisted's service framework does not track start-up progress, i.e. # it does not check for Deferreds returned by startService(). Here we # return a Deferred anyway so that direct callers (esp. those from # tests) can easily wait for start-up. return self.starting @asynchronous def stopService(self): """Disconnect from UNIX socket.""" self.starting.cancel() if self._protocol: self.protocol = DeferredValue() self.processId = DeferredValue() self._protocol, protocol = None, self._protocol if protocol.transport: protocol.transport.loseConnection() return super(IPCWorkerService, self).stopService() @asynchronous def rpcPublish(self, port): """Publish the RPC port to the region.""" d = self.protocol.get() d.addCallback( lambda protocol: protocol.callRemote( RPCEndpointPublish, pid=os.getpid(), port=port)) return d @asynchronous def rpcRegisterConnection(self, connid, ident, host, port): """Register RPC connection on master.""" d = self.protocol.get() d.addCallback( lambda protocol: protocol.callRemote( RPCRegisterConnection, pid=os.getpid(), connid=connid, ident=ident, host=host, port=port)) return d @asynchronous def rpcUnregisterConnection(self, connid): """Unregister RPC connection on master.""" d = self.protocol.get() d.addCallback( lambda protocol: protocol.callRemote( RPCUnregisterConnection, pid=os.getpid(), connid=connid)) return d