async def run_activity( *, lifecycle: lifecycles.LifeCycleFn, registry: registries.OperatorRegistry, settings: configuration.OperatorSettings, activity: handlers_.Activity, indices: ephemera.Indices, memo: ephemera.AnyMemo, ) -> Mapping[ids.HandlerId, callbacks.Result]: logger = logging.getLogger(f'kopf.activities.{activity.value}') # For the activity handlers, we have neither bodies, nor patches, just the state. cause = causation.ActivityCause( logger=logger, activity=activity, settings=settings, indices=indices, memo=memo, ) handlers = registry._activities.get_handlers(activity=activity) state = states.State.from_scratch().with_handlers(handlers) outcomes: MutableMapping[ids.HandlerId, states.HandlerOutcome] = {} while not state.done: current_outcomes = await handling.execute_handlers_once( lifecycle=lifecycle, settings=settings, handlers=handlers, cause=cause, state=state, ) outcomes.update(current_outcomes) state = state.with_outcomes(current_outcomes) await primitives.sleep_or_wait(state.delay) # Activities assume that all handlers must eventually succeed. # We raise from the 1st exception only: just to have something real in the tracebacks. # For multiple handlers' errors, the logs should be investigated instead. exceptions = [ outcome.exception for outcome in outcomes.values() if outcome.exception is not None ] if exceptions: raise ActivityError("One or more handlers failed.", outcomes=outcomes) from exceptions[0] # If nothing has failed, we return identifiable results. The outcomes/states are internal. # The order of results is not guaranteed (the handlers can succeed on one of the retries). results = { handler_id: outcome.result for handler_id, outcome in outcomes.items() if outcome.result is not None } return results
async def activity_trigger( *, lifecycle: lifecycles.LifeCycleFn, registry: registries.OperatorRegistry, activity: causation.Activity, ) -> Mapping[registries.HandlerId, registries.HandlerResult]: """ Execute a handling cycle until succeeded or permanently failed. This mimics the behaviour of patching-watching in Kubernetes, but in-memory. """ logger = logging.getLogger(f'kopf.activities.{activity.value}') # For the activity handlers, we have neither bodies, nor patches, just the state. cause = causation.ActivityCause(logger=logger, activity=activity) handlers = registry.get_activity_handlers(activity=activity) state = states.State.from_scratch(handlers=handlers) latest_outcomes: MutableMapping[registries.HandlerId, states.HandlerOutcome] = {} while not state.done: outcomes = await _execute_handlers( lifecycle=lifecycle, handlers=handlers, cause=cause, state=state, ) latest_outcomes.update(outcomes) state = state.with_outcomes(outcomes) delay = state.delay if delay: await sleeping.sleep_or_wait( min(delay, WAITING_KEEPALIVE_INTERVAL), asyncio.Event()) # Activities assume that all handlers must eventually succeed. # We raise from the 1st exception only: just to have something real in the tracebacks. # For multiple handlers' errors, the logs should be investigated instead. exceptions = [ outcome.exception for outcome in latest_outcomes.values() if outcome.exception is not None ] if exceptions: raise ActivityError("One or more handlers failed.", outcomes=latest_outcomes) \ from exceptions[0] # If nothing has failed, we return identifiable results. The outcomes/states are internal. # The order of results is not guaranteed (the handlers can succeed on one of the retries). results = { handler_id: outcome.result for handler_id, outcome in latest_outcomes.items() if outcome.result is not None } return results
async def run_activity( *, lifecycle: lifecycles.LifeCycleFn, registry: registries.OperatorRegistry, activity: causation.Activity, ) -> Mapping[handlers.HandlerId, callbacks.HandlerResult]: logger = logging.getLogger(f'kopf.activities.{activity.value}') # For the activity handlers, we have neither bodies, nor patches, just the state. cause = causation.ActivityCause(logger=logger, activity=activity) handlers = registry.get_activity_handlers(activity=activity) outcomes = await handling.run_handlers_until_done( cause=cause, handlers=handlers, lifecycle=lifecycle, ) # Activities assume that all handlers must eventually succeed. # We raise from the 1st exception only: just to have something real in the tracebacks. # For multiple handlers' errors, the logs should be investigated instead. exceptions = [ outcome.exception for outcome in outcomes.values() if outcome.exception is not None ] if exceptions: raise ActivityError("One or more handlers failed.", outcomes=outcomes) from exceptions[0] # If nothing has failed, we return identifiable results. The outcomes/states are internal. # The order of results is not guaranteed (the handlers can succeed on one of the retries). results = { handler_id: outcome.result for handler_id, outcome in outcomes.items() if outcome.result is not None } return results