コード例 #1
0
ファイル: handling.py プロジェクト: harshanarayana/kopf
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)
コード例 #2
0
ファイル: handling.py プロジェクト: kevindwei/kopf
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)