Example #1
0
def test_for_free(kwargs, event, finalizers, deletion_ts, requires_finalizer):
    event = {'type': event, 'object': {'metadata': {}}}
    event['object']['metadata'].update(finalizers)
    event['object']['metadata'].update(deletion_ts)
    cause = detect_state_changing_cause(event=event,
                                        requires_finalizer=requires_finalizer,
                                        **kwargs)
    assert cause.reason == Reason.FREE
    check_kwargs(cause, kwargs)
Example #2
0
def test_for_update(kwargs, event, finalizers, deletion_ts, annotations,
                    content, requires_finalizer):
    event = {'type': event, 'object': {'metadata': {}}}
    event['object'].update(content)
    event['object']['metadata'].update(finalizers)
    event['object']['metadata'].update(deletion_ts)
    event['object']['metadata'].update(annotations)
    cause = detect_state_changing_cause(event=event,
                                        requires_finalizer=requires_finalizer,
                                        diff=True,
                                        **kwargs)
    assert cause.reason == Reason.UPDATE
    check_kwargs(cause, kwargs)
Example #3
0
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)