Exemple #1
0
def owner(request, resource):
    if request.param == 'state-changing-cause':
        cause = ResourceChangingCause(
            logger=logging.getLogger('kopf.test.fake.logger'),
            resource=resource,
            patch=Patch(),
            memo=ObjectDict(),
            body=OWNER,
            initial=False,
            reason=Reason.NOOP,
        )
        with context([(cause_var, cause)]):
            yield
    elif request.param == 'event-watching-cause':
        cause = ResourceWatchingCause(
            logger=logging.getLogger('kopf.test.fake.logger'),
            resource=resource,
            patch=Patch(),
            memo=ObjectDict(),
            body=OWNER,
            type='irrelevant',
            raw=Event(type='irrelevant', object=OWNER),
        )
        with context([(cause_var, cause)]):
            yield
    else:
        raise RuntimeError(
            f"Wrong param for `owner` fixture: {request.param!r}")
def owner(request, resource):
    body = Body(copy.deepcopy(OWNER))
    if request.param == 'state-changing-cause':
        cause = ResourceChangingCause(
            logger=logging.getLogger('kopf.test.fake.logger'),
            indices=OperatorIndexers().indices,
            resource=resource,
            patch=Patch(),
            memo=Memo(),
            body=body,
            initial=False,
            reason=Reason.NOOP,
        )
        with context([(cause_var, cause)]):
            yield body
    elif request.param == 'event-watching-cause':
        cause = ResourceWatchingCause(
            logger=logging.getLogger('kopf.test.fake.logger'),
            indices=OperatorIndexers().indices,
            resource=resource,
            patch=Patch(),
            memo=Memo(),
            body=body,
            type='irrelevant',
            raw=RawEvent(type='irrelevant', object=OWNER),
        )
        with context([(cause_var, cause)]):
            yield body
    else:
        raise RuntimeError(
            f"Wrong param for `owner` fixture: {request.param!r}")
Exemple #3
0
async def invoke_handler(
    handler: handlers_.BaseHandler,
    *args: Any,
    cause: causation.BaseCause,
    settings: configuration.OperatorSettings,
    lifecycle: Optional[lifecycles.LifeCycleFn],
    subrefs: Set[handlers_.HandlerId],
    **kwargs: Any,
) -> Optional[callbacks.Result]:
    """
    Invoke one handler only, according to the calling conventions.

    Specifically, calculate the handler-specific fields (e.g. field diffs).

    Ensure the global context for this asyncio task is set to the handler and
    its cause -- for proper population of the sub-handlers via the decorators
    (see `@kopf.subhandler`).
    """

    # For the field-handlers, the old/new/diff values must match the field, not the whole object.
    if (True and  # for readable indenting
            isinstance(cause, causation.ResourceChangingCause) and
            isinstance(handler, handlers_.ResourceHandler) and handler.field
            is not None):
        old = dicts.resolve(cause.old, handler.field, None)
        new = dicts.resolve(cause.new, handler.field, None)
        diff = diffs.reduce(cause.diff, handler.field)
        cause = causation.enrich_cause(cause=cause,
                                       old=old,
                                       new=new,
                                       diff=diff)

    # Store the context of the current handler, to be used in `@kopf.subhandler`,
    # and maybe other places, and consumed in the recursive `execute()` calls for the children.
    # This replaces the multiple kwargs passing through the whole call stack (easy to forget).
    with invocation.context([
        (sublifecycle_var, lifecycle),
        (subregistry_var, registries.ResourceChangingRegistry()),
        (subsettings_var, settings),
        (subexecuted_var, False),
        (subrefs_var, list(subrefs_var.get([])) + [subrefs]),
        (handler_var, handler),
        (cause_var, cause),
    ]):
        # And call it. If the sub-handlers are not called explicitly, run them implicitly
        # as if it was done inside of the handler (i.e. under try-finally block).
        result = await invocation.invoke(
            handler.fn,
            *args,
            settings=settings,
            cause=cause,
            **kwargs,
        )

        if not subexecuted_var.get() and isinstance(
                cause, causation.ResourceChangingCause):
            await execute()

        # Since we know that we invoked the handler, we cast "any" result to a handler result.
        return callbacks.Result(result)
def test_with_parent(mocker, parent_handler, resource_registry_cls):

    registry = resource_registry_cls()

    with context([(handler_var, parent_handler)]):
        kopf.on.this(registry=registry)(child_fn)

    handlers = registry.get_handlers(mocker.MagicMock())
    assert len(handlers) == 1
    assert handlers[0].fn is child_fn
    assert handlers[0].id == 'parent_fn/child_fn'
Exemple #5
0
def test_with_parent(parent_handler, resource_registry_cls, cause_factory):

    cause = cause_factory(resource_registry_cls)
    registry = resource_registry_cls()

    with context([(handler_var, parent_handler)]):
        kopf.on.this(registry=registry)(child_fn)

    handlers = registry.get_handlers(cause)
    assert len(handlers) == 1
    assert handlers[0].fn is child_fn
    assert handlers[0].id == 'parent_fn/child_fn'
Exemple #6
0
async def _call_handler(
    handler: registries.ResourceHandler,
    *args: Any,
    cause: causation.BaseCause,
    lifecycle: lifecycles.LifeCycleFn,
    **kwargs: Any,
) -> Any:
    """
    Invoke one handler only, according to the calling conventions.

    Specifically, calculate the handler-specific fields (e.g. field diffs).

    Ensure the global context for this asyncio task is set to the handler and
    its cause -- for proper population of the sub-handlers via the decorators
    (see `@kopf.on.this`).
    """

    # For the field-handlers, the old/new/diff values must match the field, not the whole object.
    if isinstance(
            cause,
            causation.ResourceChangingCause) and handler.field is not None:
        old = dicts.resolve(cause.old, handler.field, None, assume_empty=True)
        new = dicts.resolve(cause.new, handler.field, None, assume_empty=True)
        diff = diffs.reduce(cause.diff, handler.field)
        cause = causation.enrich_cause(cause=cause,
                                       old=old,
                                       new=new,
                                       diff=diff)

    # Store the context of the current resource-object-event-handler, to be used in `@kopf.on.this`,
    # and maybe other places, and consumed in the recursive `execute()` calls for the children.
    # This replaces the multiple kwargs passing through the whole call stack (easy to forget).
    with invocation.context([
        (sublifecycle_var, lifecycle),
        (subregistry_var, registries.ResourceRegistry(prefix=handler.id)),
        (subexecuted_var, False),
        (handler_var, handler),
        (cause_var, cause),
    ]):
        # And call it. If the sub-handlers are not called explicitly, run them implicitly
        # as if it was done inside of the handler (i.e. under try-finally block).
        result = await invocation.invoke(
            handler.fn,
            *args,
            cause=cause,
            **kwargs,
        )

        if not subexecuted_var.get() and isinstance(
                cause, causation.ResourceChangingCause):
            await execute()

        return result
Exemple #7
0
def test_invalid_oldnew_for_inappropriate_subhandlers(resource, decorator, registry):

    @decorator(resource.group, resource.version, resource.plural)
    def fn(**_):
        @kopf.on.this(field='f', old='x')
        def fn2(**_):
            pass

    subregistry = ResourceChangingRegistry()
    handler = registry.resource_changing_handlers.get_all_handlers()[0]
    with context([(handler_var, handler), (subregistry_var, subregistry)]):
        with pytest.raises(TypeError, match="can only be used in update handlers"):
            handler.fn()
Exemple #8
0
def test_subhandler_declaratively(parent_handler, cause_factory):
    cause = cause_factory(reason=Reason.UPDATE)

    registry = ResourceChangingRegistry()
    subregistry_var.set(registry)

    with context([(handler_var, parent_handler)]):
        @kopf.subhandler()
        def fn(**_):
            pass

    handlers = registry.get_handlers(cause)
    assert len(handlers) == 1
    assert handlers[0].fn is fn
def test_subhandler_declaratively(mocker, parent_handler):
    cause = mocker.MagicMock(reason=Reason.UPDATE, diff=None)

    registry = ResourceChangingRegistry()
    subregistry_var.set(registry)

    with context([(handler_var, parent_handler)]):
        @kopf.on.this()
        def fn(**_):
            pass

    handlers = registry.get_handlers(cause)
    assert len(handlers) == 1
    assert handlers[0].fn is fn
Exemple #10
0
def test_subhandler_declaratively(parent_handler, cause_factory):
    cause = cause_factory(reason=Reason.UPDATE)

    registry = SimpleRegistry()
    subregistry_var.set(registry)

    with context([(handler_var, parent_handler)]):
        @kopf.on.this()
        def fn(**_):
            pass

    with pytest.deprecated_call(match=r"cease using the internal registries"):
        handlers = registry.get_cause_handlers(cause)

    assert len(handlers) == 1
    assert handlers[0].fn is fn
Exemple #11
0
def test_subhandler_imperatively(parent_handler, cause_factory):
    cause = cause_factory(reason=Reason.UPDATE)

    registry = SimpleRegistry()
    subregistry_var.set(registry)

    def fn(**_):
        pass

    with context([(handler_var, parent_handler)]):
        kopf.register(fn)

    with pytest.deprecated_call(
            match=r"use ResourceChangingRegistry.get_handlers\(\)"):
        handlers = registry.get_cause_handlers(cause)

    assert len(handlers) == 1
    assert handlers[0].fn is fn
Exemple #12
0
def test_subhandler_declaratively(mocker, parent_handler):
    cause = mocker.MagicMock(reason=Reason.UPDATE, diff=None)

    registry = SimpleRegistry()
    subregistry_var.set(registry)

    with context([(handler_var, parent_handler)]):

        @kopf.on.this()
        def fn(**_):
            pass

    with pytest.deprecated_call(
            match=r"use ResourceChangingRegistry.get_handlers\(\)"):
        handlers = registry.get_cause_handlers(cause)

    assert len(handlers) == 1
    assert handlers[0].fn is fn