def decorator( # lgtm[py/similar-function] fn: callbacks.DaemonFn, ) -> callbacks.DaemonFn: _warn_conflicting_values(field, value) _verify_filters(labels, annotations) real_registry = registry if registry is not None else registries.get_default_registry() 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 [])) selector = references.Selector( __group_or_groupversion_or_name, __version_or_name, __name, group=group, version=version, kind=kind, plural=plural, singular=singular, shortcut=shortcut, category=category, ) handler = handlers.DaemonHandler( fn=fn, id=real_id, param=param, errors=errors, timeout=timeout, retries=retries, backoff=backoff, selector=selector, labels=labels, annotations=annotations, when=when, field=real_field, value=value, initial_delay=initial_delay, requires_finalizer=True, cancellation_backoff=cancellation_backoff, cancellation_timeout=cancellation_timeout, cancellation_polling=cancellation_polling, ) real_registry._spawning.append(handler) return fn
def __init__( self, *, name: str = 'kopf', field: dicts.FieldSpec = 'status.{name}.progress', touch_field: dicts.FieldSpec = 'status.{name}.dummy', ) -> None: super().__init__() self._name = name real_field = field.format( name=name) if isinstance(field, str) else field self._field = dicts.parse_field(real_field) real_field = touch_field.format( name=name) if isinstance(touch_field, str) else touch_field self._touch_field = dicts.parse_field(real_field)
def __init__( self, *, name: str = 'kopf', field: dicts.FieldSpec = 'status.{name}.last-handled-configuration', ) -> None: super().__init__() self._name = name real_field = field.format( name=self._name) if isinstance(field, str) else field self._field = dicts.parse_field(real_field)
def decorator( # lgtm[py/similar-function] fn: callbacks.ChangingFn, ) -> callbacks.ChangingFn: _warn_conflicting_values(field, value) _verify_filters(labels, annotations) real_registry = registry if registry is not None else registries.get_default_registry( ) 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 [])) selector = references.Selector( __group_or_groupversion_or_name, __version_or_name, __name, group=group, version=version, kind=kind, plural=plural, singular=singular, shortcut=shortcut, category=category, ) handler = handlers.ChangingHandler( fn=fn, id=real_id, param=param, errors=errors, timeout=timeout, retries=retries, backoff=backoff, selector=selector, labels=labels, annotations=annotations, when=when, field=real_field, value=value, old=None, new=None, field_needs_change=False, initial=None, deleted=None, requires_finalizer=bool(not optional), reason=causes.Reason.DELETE, ) real_registry._changing.append(handler) return fn
def decorator( # lgtm[py/similar-function] fn: callbacks.WebhookFn, ) -> callbacks.WebhookFn: _warn_conflicting_values(field, value) _verify_filters(labels, annotations) real_registry = registry if registry is not None else registries.get_default_registry( ) 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 [])) selector = references.Selector( __group_or_groupversion_or_name, __version_or_name, __name, group=group, version=version, kind=kind, plural=plural, singular=singular, shortcut=shortcut, category=category, ) handler = handlers.WebhookHandler( fn=fn, id=real_id, param=param, errors=None, timeout=None, retries=None, backoff=None, # TODO: add some meaning later selector=selector, labels=labels, annotations=annotations, when=when, field=real_field, value=value, reason=causes.WebhookType.MUTATING, operation=operation, persistent=persistent, side_effects=side_effects, ignore_failures=ignore_failures, ) real_registry._webhooks.append(handler) return fn
def decorator( # lgtm[py/similar-function] fn: callbacks.IndexingFn, ) -> callbacks.IndexingFn: _warn_conflicting_values(field, value) _verify_filters(labels, annotations) real_registry = registry if registry is not None else registries.get_default_registry() 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) selector = references.Selector( __group_or_groupversion_or_name, __version_or_name, __name, group=group, version=version, kind=kind, plural=plural, singular=singular, shortcut=shortcut, category=category, ) handler = handlers.IndexingHandler( fn=fn, id=real_id, param=param, errors=errors, timeout=timeout, retries=retries, backoff=backoff, selector=selector, labels=labels, annotations=annotations, when=when, field=real_field, value=value, ) real_registry._indexing.append(handler) return fn
def decorator( # lgtm[py/similar-function] fn: callbacks.ChangingFn, ) -> callbacks.ChangingFn: parent_handler = execution.handler_var.get() if not isinstance(parent_handler, handlers.ChangingHandler): raise TypeError( "Sub-handlers are only supported for resource-changing handlers." ) _warn_incompatible_parent_with_oldnew(parent_handler, old, new) _warn_conflicting_values(field, value, old, new) _verify_filters(labels, annotations) real_registry = subhandling.subregistry_var.get() 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, prefix=parent_handler.id if parent_handler else None) handler = handlers.ChangingHandler( fn=fn, id=real_id, param=param, errors=errors, timeout=timeout, retries=retries, backoff=backoff, selector=None, labels=labels, annotations=annotations, when=when, field=real_field, value=value, old=old, new=new, field_needs_change=parent_handler. field_needs_change, # inherit dymaically initial=None, deleted=None, requires_finalizer=None, reason=None, ) real_registry.append(handler) return fn
def handler(request, callback, selector): handler = WatchingHandler( selector=selector, annotations={'known': 'value'}, labels={'known': 'value'}, field=parse_field('spec.field'), value='value', when=None, fn=some_fn, id='a', param=None, errors=None, timeout=None, retries=None, backoff=None, ) if request.param in ['annotations', 'labels']: handler = dataclasses.replace(handler, **{request.param: { 'known': callback }}) else: handler = dataclasses.replace(handler, **{request.param: callback}) return handler
def test_from_none(): path = parse_field(None) assert isinstance(path, tuple) assert len(path) == 0
def test_from_others_fails(val): with pytest.raises(ValueError): parse_field(val)
def test_from_tuple(): path = parse_field(('field', 'subfield')) assert isinstance(path, tuple) assert path == ('field', 'subfield')
def test_from_list(): path = parse_field(['field', 'subfield']) assert isinstance(path, tuple) assert path == ('field', 'subfield')
def test_from_string_two_levels(): path = parse_field('field.subfield') assert isinstance(path, tuple) assert path == ('field', 'subfield')
def test_from_string_one_level(): path = parse_field('field') assert isinstance(path, tuple) assert path == ('field', )
def test_catchall_handlers_with_field_found( cause_with_field, registry, handler_factory): cause = cause_with_field handler_factory(field=parse_field('known-field')) handlers = registry._spawning.get_handlers(cause) assert handlers
import datetime from typing import Collection, Optional from kopf._cogs.aiokits import aiotime from kopf._cogs.clients import patching from kopf._cogs.configs import configuration from kopf._cogs.helpers import typedefs from kopf._cogs.structs import bodies, dicts, diffs, patches, references from kopf._core.actions import loggers # How often to wake up from the long sleep, to show liveness in the logs. WAITING_KEEPALIVE_INTERVAL = 10 * 60 # K8s-managed fields that are removed completely when patched to an empty list/dict. KNOWN_INCONSISTENCIES = ( dicts.parse_field('metadata.annotations'), dicts.parse_field('metadata.finalizers'), dicts.parse_field('metadata.labels'), ) async def apply( *, settings: configuration.OperatorSettings, resource: references.Resource, body: bodies.Body, patch: patches.Patch, delays: Collection[float], logger: loggers.ObjectLogger, stream_pressure: Optional[asyncio.Event] = None, # None for tests ) -> bool:
def test_catchall_handlers_with_field_ignored( cause_no_diff, registry, handler_factory): cause = cause_no_diff handler_factory(reason=None, field=parse_field('known-field')) handlers = registry._changing.get_handlers(cause) assert not handlers
def test_catchall_handlers_with_field_ignored(cause_no_field, registry, handler_factory): cause = cause_no_field handler_factory(field=parse_field('known-field')) handlers = registry._watching.get_handlers(cause) assert not handlers
def touch_field(self, field: dicts.FieldSpec) -> None: real_field = field.format( name=self._name) if isinstance(field, str) else field self._touch_field = dicts.parse_field(real_field)