async def custom_object_handler( lifecycle: Callable, registry: registries.GlobalRegistry, resource: registries.Resource, event: dict, freeze: asyncio.Event, event_queue: asyncio.Queue, ) -> None: """ Handle a single custom object low-level watch-event. Convert the low-level events, as provided by the watching/queueing tasks, to the high-level causes, and then call the cause-handling logic. All the internally provoked changes are intercepted, do not create causes, and therefore do not call the handling logic. """ body = event['object'] delay = None patch = {} # Each object has its own prefixed logger, to distinguish parallel handling. logger = ObjectLogger(logging.getLogger(__name__), extra=dict( namespace=body.get('metadata', {}).get('namespace', 'default'), name=body.get('metadata', {}).get('name', body.get('metadata', {}).get('uid', None)), )) posting.event_queue_var.set(event_queue) # till the end of this object's task. # If the global freeze is set for the processing (i.e. other operator overrides), do nothing. if freeze.is_set(): logger.debug("Ignoring the events due to freeze.") return # Invoke all silent spies. No causation, no progress storage is performed. if registry.has_event_handlers(resource=resource): await handle_event(registry=registry, resource=resource, event=event, logger=logger, patch=patch) # Object patch accumulator. Populated by the methods. Applied in the end of the handler. # Detect the cause and handle it (or at least log this happened). if registry.has_cause_handlers(resource=resource): extra_fields = registry.get_extra_fields(resource=resource) old, new, diff = lastseen.get_state_diffs(body=body, extra_fields=extra_fields) cause = causation.detect_cause( event=event, resource=resource, logger=logger, patch=patch, old=old, new=new, diff=diff, requires_finalizer=registry.requires_finalizer(resource=resource), ) delay = await handle_cause(lifecycle=lifecycle, registry=registry, cause=cause) # Provoke a dummy change to trigger the reactor after sleep. # TODO: reimplement via the handler delayed statuses properly. if delay and not patch: patch.setdefault('status', {}).setdefault('kopf', {})['dummy'] = datetime.datetime.utcnow().isoformat() # Whatever was done, apply the accumulated changes to the object. # But only once, to reduce the number of API calls and the generated irrelevant events. if patch: logger.debug("Patching with: %r", patch) await patching.patch_obj(resource=resource, patch=patch, body=body) # Sleep strictly after patching, never before -- to keep the status proper. if delay: logger.info(f"Sleeping for {delay} seconds for the delayed handlers.") await asyncio.sleep(delay)
async def custom_object_handler( lifecycle: lifecycles.LifeCycleFn, registry: registries.GlobalRegistry, resource: resources.Resource, event: bodies.Event, freeze: asyncio.Event, replenished: asyncio.Event, event_queue: posting.K8sEventQueue, ) -> None: """ Handle a single custom object low-level watch-event. Convert the low-level events, as provided by the watching/queueing tasks, to the high-level causes, and then call the cause-handling logic. All the internally provoked changes are intercepted, do not create causes, and therefore do not call the handling logic. """ body: bodies.Body = event['object'] patch: patches.Patch = patches.Patch() delay: Optional[float] = None # Each object has its own prefixed logger, to distinguish parallel handling. logger = logging_engine.ObjectLogger(body=body) posting.event_queue_loop_var.set(asyncio.get_running_loop()) posting.event_queue_var.set( event_queue) # till the end of this object's task. # If the global freeze is set for the processing (i.e. other operator overrides), do nothing. if freeze.is_set(): logger.debug("Ignoring the events due to freeze.") return # Invoke all silent spies. No causation, no progress storage is performed. if registry.has_event_watching_handlers(resource=resource): event_watching_cause = causation.detect_event_watching_cause( event=event, resource=resource, logger=logger, patch=patch, ) await handle_event_watching_cause( lifecycle=lifecycles.all_at_once, registry=registry, cause=event_watching_cause, ) # Object patch accumulator. Populated by the methods. Applied in the end of the handler. # Detect the cause and handle it (or at least log this happened). if registry.has_state_changing_handlers(resource=resource): extra_fields = registry.get_extra_fields(resource=resource) old, new, diff = lastseen.get_essential_diffs( body=body, extra_fields=extra_fields) state_changing_cause = causation.detect_state_changing_cause( event=event, resource=resource, logger=logger, patch=patch, old=old, new=new, diff=diff, requires_finalizer=registry.requires_finalizer(resource=resource, body=body), ) delay = await handle_state_changing_cause( lifecycle=lifecycle, registry=registry, cause=state_changing_cause, ) # Whatever was done, apply the accumulated changes to the object. # But only once, to reduce the number of API calls and the generated irrelevant events. if patch: logger.debug("Patching with: %r", patch) await patching.patch_obj(resource=resource, patch=patch, body=body) # Sleep strictly after patching, never before -- to keep the status proper. # The patching above, if done, interrupts the sleep instantly, so we skip it at all. if delay and not patch: logger.debug(f"Sleeping for {delay} seconds for the delayed handlers.") unslept = await sleeping.sleep_or_wait(delay, replenished) if unslept is not None: logger.debug( f"Sleeping was interrupted by new changes, {unslept} seconds left." ) else: now = datetime.datetime.utcnow() dummy = patches.Patch( {'status': { 'kopf': { 'dummy': now.isoformat() } }}) logger.debug("Provoking reaction with: %r", dummy) await patching.patch_obj(resource=resource, patch=dummy, body=body)