async def handle_event( registry: registries.BaseRegistry, resource: registries.Resource, logger: logging_engine.ObjectLogger, patch: dict, event: dict, ): """ Handle a received event, log but ignore all errors. This is a lightweight version of the cause handling, but for the raw events, without any progress persistence. Multi-step calls are also not supported. If the handler fails, it fails and is never retried. Note: K8s-event posting is skipped for `kopf.on.event` handlers, as they should be silent. Still, the messages are logged normally. """ handlers = registry.get_event_handlers(resource=resource, event=event) for handler in handlers: # The exceptions are handled locally and are not re-raised, to keep the operator running. try: logger.debug(f"Invoking handler {handler.id!r}.") # TODO: also set the context-vars, despite most of the make no sense here. result = await invocation.invoke( handler.fn, event=event, patch=patch, logger=logger, ) except Exception: logger.exception( f"Handler {handler.id!r} failed with an exception. Will ignore.", local=True) else: logger.info(f"Handler {handler.id!r} succeeded.", local=True) status.store_result(patch=patch, handler=handler, result=result)
async def apply_reaction_outcomes( *, settings: configuration.OperatorSettings, resource: resources.Resource, body: bodies.Body, patch: patches.Patch, delays: Collection[float], logger: logging_engine.ObjectLogger, replenished: asyncio.Event, ) -> None: delay = min(delays) if delays else None # Delete dummies on occasion, but don't trigger special patching for them [discussable]. if patch: # TODO: LATER: and the dummies are there (without additional methods?) settings.persistence.progress_storage.touch(body=body, patch=patch, value=None) # Actually patch if it contained payload originally or after dummies removal. 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. # Note: a zero-second or negative sleep is still a sleep, it will trigger a dummy patch. if delay and patch: logger.debug( f"Sleeping was skipped because of the patch, {delay} seconds left." ) elif delay is None and not patch: logger.debug( f"Handling cycle is finished, waiting for new changes since now.") elif delay is not None: if delay > WAITING_KEEPALIVE_INTERVAL: limit = WAITING_KEEPALIVE_INTERVAL logger.debug( f"Sleeping for {delay} (capped {limit}) seconds for the delayed handlers." ) unslept_delay = await sleeping.sleep_or_wait(limit, replenished) elif delay > 0: logger.debug( f"Sleeping for {delay} seconds for the delayed handlers.") unslept_delay = await sleeping.sleep_or_wait(delay, replenished) else: unslept_delay = None # no need to sleep? means: slept in full. # Exclude cases when touching immediately after patching (including: ``delay == 0``). if patch and not delay: pass elif unslept_delay is not None: logger.debug( f"Sleeping was interrupted by new changes, {unslept_delay} seconds left." ) else: # Any unique always-changing value will work; not necessary a timestamp. value = datetime.datetime.utcnow().isoformat() touch_patch = patches.Patch() settings.persistence.progress_storage.touch(body=body, patch=touch_patch, value=value) if touch_patch: logger.debug("Provoking reaction with: %r", touch_patch) await patching.patch_obj(resource=resource, patch=touch_patch, body=body)
async def apply_reaction_outcomes( *, resource: resources.Resource, body: bodies.Body, patch: patches.Patch, delays: Collection[float], logger: logging_engine.ObjectLogger, replenished: asyncio.Event, ) -> None: delay = min(delays) if delays else None 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. # Note: a zero-second or negative sleep is still a sleep, it will trigger a dummy patch. if delay and patch: logger.debug( f"Sleeping was skipped because of the patch, {delay} seconds left." ) elif delay is None and not patch: logger.debug( f"Handling cycle is finished, waiting for new changes since now.") elif delay is not None: if delay > WAITING_KEEPALIVE_INTERVAL: limit = WAITING_KEEPALIVE_INTERVAL logger.debug( f"Sleeping for {delay} (capped {limit}) seconds for the delayed handlers." ) unslept_delay = await sleeping.sleep_or_wait(limit, replenished) elif delay > 0: logger.debug( f"Sleeping for {delay} seconds for the delayed handlers.") unslept_delay = await sleeping.sleep_or_wait(delay, replenished) else: unslept_delay = None # no need to sleep? means: slept in full. if unslept_delay is not None: logger.debug( f"Sleeping was interrupted by new changes, {unslept_delay} seconds left." ) else: # Any unique always-changing value will work; not necessary a timestamp. dummy_value = datetime.datetime.utcnow().isoformat() dummy_patch = patches.Patch( {'status': { 'kopf': { 'dummy': dummy_value } }}) logger.debug("Provoking reaction with: %r", dummy_patch) await patching.patch_obj(resource=resource, patch=dummy_patch, body=body)