예제 #1
0
파일: states.py 프로젝트: blaisep/kopf
def deliver_results(
    *,
    outcomes: Mapping[handlers_.HandlerId, HandlerOutcome],
    patch: patches.Patch,
) -> None:
    """
    Store the results (as returned from the handlers) to the resource.

    This is not the handlers' state persistence, but the results' persistence.

    First, the state persistence is stored under ``.status.kopf.progress``,
    and can (later) be configured to be stored in different fields for different
    operators operating the same objects: ``.status.kopf.{somename}.progress``.
    The handlers' result are stored in the top-level ``.status``.

    Second, the handler results can (also later) be delivered to other objects,
    e.g. to their owners or label-selected related objects. For this, another
    class/module will be added.

    For now, we keep state- and result persistence in one module, but separated.
    """
    for handler_id, outcome in outcomes.items():
        if outcome.exception is not None:
            pass
        elif outcome.result is None:
            pass
        elif isinstance(outcome.result, collections.abc.Mapping):
            # TODO: merge recursively (patch-merge), do not overwrite the keys if they are present.
            patch.setdefault('status',
                             {}).setdefault(handler_id,
                                            {}).update(outcome.result)
        else:
            patch.setdefault('status',
                             {})[handler_id] = copy.deepcopy(outcome.result)
예제 #2
0
파일: finalizers.py 프로젝트: yongming/kopf
def block_deletion(
    *,
    body: bodies.Body,
    patch: patches.Patch,
) -> None:
    if not is_deletion_blocked(body=body):
        finalizers = body.get('metadata', {}).get('finalizers', [])
        patch.setdefault('metadata', {}).setdefault('finalizers',
                                                    list(finalizers))
        patch['metadata']['finalizers'].append(FINALIZER)
예제 #3
0
파일: finalizers.py 프로젝트: zhutony/kopf
def append_finalizers(
    *,
    body: bodies.Body,
    patch: patches.Patch,
) -> None:
    if not has_finalizers(body=body):
        finalizers = body.get('metadata', {}).get('finalizers', [])
        patch.setdefault('metadata', {}).setdefault('finalizers',
                                                    list(finalizers))
        patch['metadata']['finalizers'].append(FINALIZER)
예제 #4
0
파일: finalizers.py 프로젝트: nnazeer/kopf
def allow_deletion(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    finalizer: str,
) -> None:
    if is_deletion_blocked(body=body, finalizer=finalizer):
        finalizers = body.get('metadata', {}).get('finalizers', [])
        patch.setdefault('metadata', {}).setdefault('finalizers',
                                                    list(finalizers))
        if finalizer in patch['metadata']['finalizers']:
            patch['metadata']['finalizers'].remove(finalizer)
예제 #5
0
파일: finalizers.py 프로젝트: yongming/kopf
def allow_deletion(
    *,
    body: bodies.Body,
    patch: patches.Patch,
) -> None:
    if is_deletion_blocked(body=body):
        finalizers = body.get('metadata', {}).get('finalizers', [])
        patch.setdefault('metadata', {}).setdefault('finalizers',
                                                    list(finalizers))
        if LEGACY_FINALIZER in patch['metadata']['finalizers']:
            patch['metadata']['finalizers'].remove(LEGACY_FINALIZER)
        if FINALIZER in patch['metadata']['finalizers']:
            patch['metadata']['finalizers'].remove(FINALIZER)
예제 #6
0
파일: finalizers.py 프로젝트: zhutony/kopf
def remove_finalizers(
    *,
    body: bodies.Body,
    patch: patches.Patch,
) -> None:
    if has_finalizers(body=body):
        finalizers = body.get('metadata', {}).get('finalizers', [])
        patch.setdefault('metadata', {}).setdefault('finalizers',
                                                    list(finalizers))
        if LEGACY_FINALIZER in patch['metadata']['finalizers']:
            patch['metadata']['finalizers'].remove(LEGACY_FINALIZER)
        if FINALIZER in patch['metadata']['finalizers']:
            patch['metadata']['finalizers'].remove(FINALIZER)
예제 #7
0
def store_result(
    *,
    patch: patches.Patch,
    handler: registries.ResourceHandler,
    result: Any = None,
) -> None:
    if result is None:
        pass
    elif isinstance(result, collections.abc.Mapping):
        # TODO: merge recursively (patch-merge), do not overwrite the keys if they are present.
        patch.setdefault('status', {}).setdefault(handler.id,
                                                  {}).update(result)
    else:
        # TODO? Fail if already present?
        patch.setdefault('status', {})[handler.id] = copy.deepcopy(result)
예제 #8
0
파일: states.py 프로젝트: corka149/kopf
 def store(self, patch: patches.Patch) -> None:
     for handler_id, handler_state in self.items():
         # Nones are not stored by Kubernetes, so we filter them out for comparison.
         if handler_state.as_dict() != handler_state._origin:
             # Note: create the 'progress' key only if there are handlers to store, not always.
             storage = patch.setdefault('status', {}).setdefault('kopf', {})
             storage.setdefault('progress', {})[handler_id] = handler_state.as_patch()
예제 #9
0
def refresh_essence(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    extra_fields: Optional[Iterable[dicts.FieldSpec]] = None,
) -> None:
    state = get_essence(body, extra_fields=extra_fields)
    patch.setdefault('metadata', {}).setdefault(
        'annotations', {})[LAST_SEEN_ANNOTATION] = json.dumps(state)
예제 #10
0
파일: lastseen.py 프로젝트: corka149/kopf
def refresh_essence(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    extra_fields: Optional[Iterable[dicts.FieldSpec]] = None,
) -> None:
    old_essence = retrieve_essence(body=body)
    new_essence = get_essence(body, extra_fields=extra_fields)
    if new_essence != old_essence:
        annotations = patch.setdefault('metadata',
                                       {}).setdefault('annotations', {})
        annotations[LAST_SEEN_ANNOTATION] = json.dumps(new_essence)
예제 #11
0
파일: states.py 프로젝트: corka149/kopf
    def purge(self, patch: patches.Patch, body: bodies.Body) -> None:
        if 'progress' in body.get('status', {}).get('kopf', {}):
            patch_storage = patch.setdefault('status', {}).setdefault('kopf', {})
            patch_storage['progress'] = None
        elif 'progress' in patch.get('status', {}).get('kopf', {}):
            del patch['status']['kopf']['progress']

        # Avoid storing the empty status dicts (but do so if they have any content).
        if 'status' in patch and 'kopf' in patch['status'] and not patch['status']['kopf']:
            del patch['status']['kopf']
        if 'status' in patch and not patch['status']:
            del patch['status']
예제 #12
0
def set_start_time(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    handler: registries.ResourceHandler,
) -> None:
    progress = patch.setdefault('status',
                                {}).setdefault('kopf',
                                               {}).setdefault('progress', {})
    progress.setdefault(handler.id, {}).update({
        'started':
        datetime.datetime.utcnow().isoformat(),
    })
예제 #13
0
def set_retry_time(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    handler: registries.ResourceHandler,
    delay: Optional[float] = None,
) -> None:
    retry = get_retry_count(body=body, handler=handler)
    progress = patch.setdefault('status',
                                {}).setdefault('kopf',
                                               {}).setdefault('progress', {})
    progress.setdefault(handler.id, {}).update({
        'retries': retry + 1,
    })
    set_awake_time(body=body, patch=patch, handler=handler, delay=delay)
예제 #14
0
def set_awake_time(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    handler: registries.ResourceHandler,
    delay: Optional[float] = None,
) -> None:
    ts_str: Optional[str]
    if delay is not None:
        ts = datetime.datetime.utcnow() + datetime.timedelta(seconds=delay)
        ts_str = ts.isoformat()
    else:
        ts_str = None
    progress = patch.setdefault('status',
                                {}).setdefault('kopf',
                                               {}).setdefault('progress', {})
    progress.setdefault(handler.id, {}).update({
        'delayed': ts_str,
    })
예제 #15
0
def store_failure(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    handler: registries.ResourceHandler,
    exc: BaseException,
) -> None:
    retry = get_retry_count(body=body, handler=handler)
    progress = patch.setdefault('status',
                                {}).setdefault('kopf',
                                               {}).setdefault('progress', {})
    progress.setdefault(handler.id, {}).update({
        'stopped':
        datetime.datetime.utcnow().isoformat(),
        'failure':
        True,
        'retries':
        retry + 1,
        'message':
        f'{exc}',
    })
예제 #16
0
def store_success(
    *,
    body: bodies.Body,
    patch: patches.Patch,
    handler: registries.ResourceHandler,
    result: Any = None,
) -> None:
    retry = get_retry_count(body=body, handler=handler)
    progress = patch.setdefault('status',
                                {}).setdefault('kopf',
                                               {}).setdefault('progress', {})
    progress.setdefault(handler.id, {}).update({
        'stopped':
        datetime.datetime.utcnow().isoformat(),
        'success':
        True,
        'retries':
        retry + 1,
        'message':
        None,
    })
    store_result(patch=patch, handler=handler, result=result)
예제 #17
0
 def purge(self, patch: patches.Patch) -> None:
     storage = patch.setdefault('status', {}).setdefault('kopf', {})
     storage['progress'] = None
예제 #18
0
def purge_progress(
    *,
    body: bodies.Body,
    patch: patches.Patch,
) -> None:
    patch.setdefault('status', {}).setdefault('kopf', {})['progress'] = None