Example #1
0
File: on.py Project: zhutony/kopf
 def decorator(
         fn: callbacks.ResourceHandlerFn) -> callbacks.ResourceHandlerFn:
     parent_handler = handling.handler_var.get()
     real_registry = registry if registry is not None else handling.subregistry_var.get(
     )
     real_id = registries.generate_id(
         fn=fn, id=id, prefix=parent_handler.id if parent_handler else None)
     handler = handlers.ResourceHandler(
         fn=fn,
         id=real_id,
         field=None,
         errors=errors,
         timeout=timeout,
         retries=retries,
         backoff=backoff,
         cooldown=cooldown,
         labels=labels,
         annotations=annotations,
         when=when,
         initial=None,
         deleted=None,
         requires_finalizer=None,
         reason=None,
     )
     real_registry.append(handler)
     return fn
Example #2
0
File: on.py Project: zhutony/kopf
 def decorator(
         fn: callbacks.ResourceHandlerFn) -> callbacks.ResourceHandlerFn:
     real_registry = registry if registry is not None else registries.get_default_registry(
     )
     real_resource = resources.Resource(group, version, plural)
     real_field = dicts.parse_field(
         field) or None  # to not store tuple() as a no-field case.
     real_id = registries.generate_id(fn=fn,
                                      id=id,
                                      suffix=".".join(real_field or []))
     handler = handlers.ResourceHandler(
         fn=fn,
         id=real_id,
         field=real_field,
         errors=errors,
         timeout=timeout,
         retries=retries,
         backoff=backoff,
         cooldown=cooldown,
         labels=labels,
         annotations=annotations,
         when=when,
         initial=None,
         deleted=None,
         requires_finalizer=None,
         reason=None,
     )
     real_registry.resource_changing_handlers[real_resource].append(handler)
     return fn
Example #3
0
File: on.py Project: zhutony/kopf
 def decorator(
         fn: callbacks.ResourceHandlerFn) -> callbacks.ResourceHandlerFn:
     real_registry = registry if registry is not None else registries.get_default_registry(
     )
     real_resource = resources.Resource(group, version, plural)
     real_id = registries.generate_id(fn=fn, id=id)
     handler = handlers.ResourceHandler(
         fn=fn,
         id=real_id,
         field=None,
         errors=None,
         timeout=None,
         retries=None,
         backoff=None,
         cooldown=None,
         labels=labels,
         annotations=annotations,
         when=when,
         initial=None,
         deleted=None,
         requires_finalizer=None,
         reason=None,
     )
     real_registry.resource_watching_handlers[real_resource].append(handler)
     return fn
Example #4
0
    def register(
            self,
            fn: callbacks.ResourceHandlerFn,
            *,
            id: Optional[str] = None,
            reason: Optional[causation.Reason] = None,
            event: Optional[str] = None,  # deprecated, use `reason`
            field: Optional[dicts.FieldSpec] = None,
            errors: Optional[errors_.ErrorsMode] = None,
            timeout: Optional[float] = None,
            retries: Optional[int] = None,
            backoff: Optional[float] = None,
            cooldown: Optional[float] = None,  # deprecated, use `backoff`
            initial: Optional[bool] = None,
            deleted: Optional[bool] = None,
            requires_finalizer: bool = False,
            labels: Optional[bodies.Labels] = None,
            annotations: Optional[bodies.Annotations] = None,
            when: Optional[callbacks.WhenHandlerFn] = None,
    ) -> callbacks.ResourceHandlerFn:
        if reason is None and event is not None:
            reason = causation.Reason(event)

        real_field = dicts.parse_field(field) or None  # to not store tuple() as a no-field case.
        real_id = generate_id(fn=fn, id=id, prefix=self.prefix, suffix=".".join(real_field or []))
        handler = handlers.ResourceHandler(
            id=real_id, fn=fn, reason=reason, field=real_field,
            errors=errors, timeout=timeout, retries=retries, backoff=backoff, cooldown=cooldown,
            initial=initial, deleted=deleted, requires_finalizer=requires_finalizer,
            labels=labels, annotations=annotations, when=when,
        )

        self.append(handler)
        return fn
Example #5
0
async def execute(
    *,
    fns: Optional[Iterable[invocation.Invokable]] = None,
    handlers: Optional[Iterable[handlers_.ResourceHandler]] = None,
    registry: Optional[registries.ResourceChangingRegistry] = None,
    lifecycle: Optional[lifecycles.LifeCycleFn] = None,
    cause: Optional[causation.BaseCause] = None,
) -> None:
    """
    Execute the handlers in an isolated lifecycle.

    This function is just a public wrapper for `execute` with multiple
    ways to specify the handlers: either as the raw functions, or as the
    pre-created handlers, or as a registry (as used in the object handling).

    If no explicit functions or handlers or registry are passed,
    the sub-handlers of the current handler are assumed, as accumulated
    in the per-handler registry with ``@kopf.on.this``.

    If the call to this method for the sub-handlers is not done explicitly
    in the handler, it is done implicitly after the handler is exited.
    One way or another, it is executed for the sub-handlers.
    """

    # Restore the current context as set in the handler execution cycle.
    lifecycle = lifecycle if lifecycle is not None else sublifecycle_var.get()
    cause = cause if cause is not None else cause_var.get()
    parent_handler: handlers_.BaseHandler = handler_var.get()
    parent_prefix = parent_handler.id if parent_handler is not None else None

    # Validate the inputs; the function signatures cannot put these kind of restrictions, so we do.
    if len([v for v in [fns, handlers, registry] if v is not None]) > 1:
        raise TypeError(
            "Only one of the fns, handlers, registry can be passed. Got more.")

    elif fns is not None and isinstance(fns, collections.abc.Mapping):
        subregistry = registries.ResourceChangingRegistry()
        for id, fn in fns.items():
            real_id = registries.generate_id(fn=fn,
                                             id=id,
                                             prefix=parent_prefix)
            handler = handlers_.ResourceHandler(
                fn=fn,
                id=real_id,
                errors=None,
                timeout=None,
                retries=None,
                backoff=None,
                cooldown=None,
                labels=None,
                annotations=None,
                when=None,
                initial=None,
                deleted=None,
                requires_finalizer=None,
                reason=None,
                field=None,
            )
            subregistry.append(handler)

    elif fns is not None and isinstance(fns, collections.abc.Iterable):
        subregistry = registries.ResourceChangingRegistry()
        for fn in fns:
            real_id = registries.generate_id(fn=fn,
                                             id=None,
                                             prefix=parent_prefix)
            handler = handlers_.ResourceHandler(
                fn=fn,
                id=real_id,
                errors=None,
                timeout=None,
                retries=None,
                backoff=None,
                cooldown=None,
                labels=None,
                annotations=None,
                when=None,
                initial=None,
                deleted=None,
                requires_finalizer=None,
                reason=None,
                field=None,
            )
            subregistry.append(handler)

    elif fns is not None:
        raise ValueError(
            f"fns must be a mapping or an iterable, got {fns.__class__}.")

    elif handlers is not None:
        subregistry = registries.ResourceChangingRegistry()
        for handler in handlers:
            subregistry.append(handler)

    # Use the registry as is; assume that the caller knows what they do.
    elif registry is not None:
        subregistry = registry

    # Prevent double implicit execution.
    elif subexecuted_var.get():
        return

    # If no explicit args were passed, implicitly use the accumulated handlers from `@kopf.on.this`.
    else:
        subexecuted_var.set(True)
        subregistry = subregistry_var.get()

    # The sub-handlers are only for upper-level causes, not for lower-level events.
    if not isinstance(cause, causation.ResourceChangingCause):
        raise RuntimeError(
            "Sub-handlers of event-handlers are not supported and have "
            "no practical use (there are no retries or state tracking).")

    # Execute the real handlers (all or few or one of them, as per the lifecycle).
    subhandlers = subregistry.get_handlers(cause=cause)
    state = states.State.from_body(body=cause.body, handlers=subhandlers)
    outcomes = await execute_handlers_once(
        lifecycle=lifecycle,
        handlers=subhandlers,
        cause=cause,
        state=state,
    )
    state = state.with_outcomes(outcomes)
    state.store(patch=cause.patch)
    states.deliver_results(outcomes=outcomes, patch=cause.patch)

    # Escalate `HandlerChildrenRetry` if the execute should be continued on the next iteration.
    if not state.done:
        raise HandlerChildrenRetry(delay=state.delay)