Пример #1
0
def remove_owner_reference(
    objs: K8sObjects,
    owner: Optional[bodies.Body] = None,
) -> None:
    """
    Remove an owner reference to the resource(s), if it is there.

    Note: the owned objects are usually not the one being processed,
    so the whole body can be modified, no patches are needed.
    """
    real_owner = _guess_owner(owner)
    owner_ref = bodies.build_owner_reference(real_owner)
    for obj in cast(Iterator[K8sObject], dicts.walk(objs)):
        # Pykube is yielded as a usual dict, no need to specially treat it.
        if isinstance(obj, collections.abc.MutableMapping):
            refs = obj.setdefault('metadata',
                                  {}).setdefault('ownerReferences', [])
            if any(ref.get('uid') == owner_ref['uid'] for ref in refs):
                refs[:] = [
                    ref for ref in refs if ref.get('uid') != owner_ref['uid']
                ]
        elif isinstance(obj, thirdparty.KubernetesModel):
            if obj.metadata is None:
                obj.metadata = thirdparty.V1ObjectMeta()
            if obj.metadata.owner_references is None:
                obj.metadata.owner_references = []
            refs = obj.metadata.owner_references
            if any(ref.uid == owner_ref['uid'] for ref in refs):
                refs[:] = [ref for ref in refs if ref.uid != owner_ref['uid']]
        else:
            raise TypeError(f"K8s object class is not supported: {type(obj)}")
Пример #2
0
def test_none_is_ignored():
    obj1 = {}
    obj2 = {}
    result = list(walk([obj1, None, obj2]))
    assert len(result) == 2
    assert result[0] is obj1
    assert result[1] is obj2
Пример #3
0
def harmonize_naming(
    objs: K8sObjects,
    name: Optional[str] = None,
    strict: bool = False,
) -> None:
    """
    Adjust the names or prefixes of the objects.

    In strict mode, the provided name is used as is. It can be helpful
    if the object is referred by that name in other objects.

    In non-strict mode (the default), the object uses the provided name
    as a prefix, while the suffix is added by Kubernetes remotely.
    The actual name should be taken from Kubernetes response
    (this is the recommended scenario).

    If the objects already have their own names, auto-naming is not applied,
    and the existing names are used as is.
    """

    # Try to use the current object being handled if possible.
    if name is None:
        real_owner = _guess_owner(None)
        name = real_owner.get('metadata', {}).get('name', None)

    # Set name/prefix based on the explicitly specified or guessed name.
    for obj in cast(Iterator[K8sObject], dicts.walk(objs)):
        if obj.get('metadata', {}).get('name', None) is None:
            if strict:
                obj.setdefault('metadata', {}).setdefault('name', name)
            else:
                obj.setdefault('metadata',
                               {}).setdefault('generateName', f'{name}-')
Пример #4
0
def harmonize_naming(
    objs: K8sObjects,
    name: Optional[str] = None,
    *,
    forced: bool = False,
    strict: bool = False,
) -> None:
    """
    Adjust the names or prefixes of the objects.

    In strict mode, the provided name is used as is. It can be helpful
    if the object is referred by that name in other objects.

    In non-strict mode (the default), the object uses the provided name
    as a prefix, while the suffix is added by Kubernetes remotely.
    The actual name should be taken from Kubernetes response
    (this is the recommended scenario).

    If the objects already have their own names, auto-naming is not applied,
    and the existing names are used as is.
    """

    # Try to use the current object being handled if possible.
    if name is None:
        real_owner = _guess_owner(None)
        name = real_owner.get('metadata', {}).get('name', None)
    if name is None:
        raise LookupError(
            "Name must be set explicitly: couldn't find it automatically.")

    # Set name/prefix based on the explicitly specified or guessed name.
    for obj in cast(Iterator[K8sObject], dicts.walk(objs)):
        # Pykube is yielded as a usual dict, no need to specially treat it.
        if isinstance(obj, collections.abc.MutableMapping):
            noname = 'metadata' not in obj or not set(
                obj['metadata']) & {'name', 'generateName'}
            if forced or noname:
                if strict:
                    obj.setdefault('metadata', {})['name'] = name
                    if 'generateName' in obj['metadata']:
                        del obj['metadata']['generateName']
                else:
                    obj.setdefault('metadata', {})['generateName'] = f'{name}-'
                    if 'name' in obj['metadata']:
                        del obj['metadata']['name']
        elif isinstance(obj, thirdparty.KubernetesModel):
            if obj.metadata is None:
                obj.metadata = thirdparty.V1ObjectMeta()
            noname = obj.metadata.name is None and obj.metadata.generate_name is None
            if forced or noname:
                if strict:
                    obj.metadata.name = name
                    if obj.metadata.generate_name is not None:
                        obj.metadata.generate_name = None
                else:
                    obj.metadata.generate_name = f'{name}-'
                    if obj.metadata.name is not None:
                        obj.metadata.name = None
        else:
            raise TypeError(f"K8s object class is not supported: {type(obj)}")
Пример #5
0
def test_over_a_tuple_of_dicts():
    obj1 = {}
    obj2 = {}
    result = list(walk((obj1, obj2)))
    assert len(result) == 2
    assert result[0] is obj1
    assert result[1] is obj2
Пример #6
0
def test_simple_nested():
    obj1 = {'field': {'subfield': 'val'}}
    obj2 = {'field': {}}
    result = list(walk([obj1, obj2], nested=['field.subfield']))
    assert len(result) == 3
    assert result[0] is obj1
    assert result[1] == 'val'
    assert result[2] is obj2
Пример #7
0
def event(
    objs: Union[bodies.Body, Iterable[bodies.Body]],
    *,
    type: str,
    reason: str,
    message: str = '',
) -> None:
    for obj in cast(Iterator[bodies.Body], dicts.walk(objs)):
        ref = bodies.build_object_reference(obj)
        enqueue(ref=ref, type=type, reason=reason, message=message)
Пример #8
0
def test_double_nested():
    obj1 = {'field': {'subfield': 'val'}}
    obj2 = {'field': {}}
    result = list(walk([obj1, obj2], nested=['field.subfield', 'field']))
    assert len(result) == 5
    assert result[0] is obj1
    assert result[1] == 'val'
    assert result[2] == {'subfield': 'val'}
    assert result[3] is obj2
    assert result[4] == {}
Пример #9
0
def info(
        objs: Union[bodies.Body, Iterable[bodies.Body]],
        *,
        reason: str,
        message: str = '',
) -> None:
    settings: configuration.OperatorSettings = settings_var.get()
    if settings.posting.enabled and settings.posting.level <= logging.INFO:
        for obj in cast(Iterator[bodies.Body], dicts.walk(objs)):
            ref = bodies.build_object_reference(obj)
            enqueue(ref=ref, type='Normal', reason=reason, message=message)
Пример #10
0
def label(objs, labels, *, force=False, nested=None):
    """
    Apply the labels to the object(s).
    """
    for obj in dicts.walk(objs, nested=nested):
        obj_labels = obj.setdefault('metadata', {}).setdefault('labels', {})
        for key, val in labels.items():
            if force:
                obj_labels[key] = val
            else:
                obj_labels.setdefault(key, val)
Пример #11
0
def adjust_namespace(objs, namespace=None):
    """
    Adjust the namespace of the objects.

    If the objects already have the namespace set, it will be preserved.

    It is a common practice to keep the children objects in the same
    namespace as their owner, unless explicitly overridden at time of creation.
    """
    for obj in dicts.walk(objs):
        obj.setdefault('metadata', {}).setdefault('namespace', namespace)
Пример #12
0
def event(
        objs: Union[bodies.Body, Iterable[bodies.Body]],
        *,
        type: str,
        reason: str,
        message: str = '',
) -> None:
    settings: configuration.OperatorSettings = settings_var.get()
    if settings.posting.enabled:
        for obj in cast(Iterator[bodies.Body], dicts.walk(objs)):
            ref = bodies.build_object_reference(obj)
            enqueue(ref=ref, type=type, reason=reason, message=message)
Пример #13
0
def append_owner_reference(objs, owner):
    """
    Append an owner reference to the resource(s), if it is not yet there.

    Note: the owned objects are usually not the one being processed,
    so the whole body can be modified, no patches are needed.
    """
    owner = build_owner_reference(owner)
    for obj in dicts.walk(objs):
        refs = obj.setdefault('metadata', {}).setdefault('ownerReferences', [])
        matching = [ref for ref in refs if ref['uid'] == owner['uid']]
        if not matching:
            refs.append(owner)
Пример #14
0
def remove_owner_reference(objs, owner):
    """
    Remove an owner reference to the resource(s), if it is there.

    Note: the owned objects are usually not the one being processed,
    so the whole body can be modified, no patches are needed.
    """
    owner = build_owner_reference(owner)
    for obj in dicts.walk(objs):
        refs = obj.setdefault('metadata', {}).setdefault('ownerReferences', [])
        matching = [ref for ref in refs if ref.get('uid') == owner.get('uid')]
        for ref in matching:
            refs.remove(ref)
Пример #15
0
def exception(
        objs: Union[bodies.Body, Iterable[bodies.Body]],
        *,
        reason: str = '',
        message: str = '',
        exc: Optional[BaseException] = None,
) -> None:
    if exc is None:
        _, exc, _ = sys.exc_info()
    reason = reason if reason else type(exc).__name__
    message = f'{message} {exc}' if message and exc else f'{exc}' if exc else f'{message}'
    settings: configuration.OperatorSettings = settings_var.get()
    if settings.posting.enabled and settings.posting.level <= logging.ERROR:
        for obj in cast(Iterator[bodies.Body], dicts.walk(objs)):
            ref = bodies.build_object_reference(obj)
            enqueue(ref=ref, type='Error', reason=reason, message=message)
Пример #16
0
def remove_owner_reference(
    objs: K8sObjects,
    owner: Optional[bodies.Body] = None,
) -> None:
    """
    Remove an owner reference to the resource(s), if it is there.

    Note: the owned objects are usually not the one being processed,
    so the whole body can be modified, no patches are needed.
    """
    real_owner = _guess_owner(owner)
    owner_ref = bodies.build_owner_reference(real_owner)
    for obj in cast(Iterator[K8sObject], dicts.walk(objs)):
        refs = obj.setdefault('metadata', {}).setdefault('ownerReferences', [])
        matching = [ref for ref in refs if ref.get('uid') == owner_ref['uid']]
        for ref in matching:
            refs.remove(ref)
Пример #17
0
def label(
    objs: K8sObjects,
    labels: Mapping[str, Union[None, str]],
    *,
    force: bool = False,
    nested: Optional[Iterable[dicts.FieldSpec]] = None,
) -> None:
    """
    Apply the labels to the object(s).
    """
    for obj in cast(Iterator[K8sObject], dicts.walk(objs, nested=nested)):
        obj_labels = obj.setdefault('metadata', {}).setdefault('labels', {})
        for key, val in labels.items():
            if force:
                obj_labels[key] = val
            else:
                obj_labels.setdefault(key, val)
Пример #18
0
def label(
        objs: K8sObjects,
        labels: Union[Mapping[str, Union[None, str]], _UNSET] = _UNSET.token,
        *,
        forced: bool = False,
        nested: Optional[Union[str, Iterable[dicts.FieldSpec]]] = None,
        force: Optional[bool] = None,  # deprecated
) -> None:
    """
    Apply the labels to the object(s).
    """
    nested = [nested] if isinstance(nested, str) else nested
    if force is not None:
        warnings.warn("force= is deprecated in kopf.label(); use forced=...",
                      DeprecationWarning)
        forced = force

    # Try to use the current object being handled if possible.
    if isinstance(labels, _UNSET):
        real_owner = _guess_owner(None)
        labels = real_owner.get('metadata', {}).get('labels', {})
    if isinstance(labels, _UNSET):
        raise RuntimeError(
            "Impossible error: labels are not resolved.")  # for type-checking

    # Set labels based on the explicitly specified or guessed ones.
    for obj in cast(Iterator[K8sObject], dicts.walk(objs, nested=nested)):
        # Pykube is yielded as a usual dict, no need to specially treat it.
        if isinstance(obj, collections.abc.MutableMapping):
            obj_labels = obj.setdefault('metadata',
                                        {}).setdefault('labels', {})
        elif isinstance(obj, thirdparty.KubernetesModel):
            if obj.metadata is None:
                obj.metadata = thirdparty.V1ObjectMeta()
            if obj.metadata.labels is None:
                obj.metadata.labels = {}
            obj_labels = obj.metadata.labels
        else:
            raise TypeError(f"K8s object class is not supported: {type(obj)}")

        for key, val in labels.items():
            if forced:
                obj_labels[key] = val
            else:
                obj_labels.setdefault(key, val)
Пример #19
0
def harmonize_naming(objs, name=None, strict=False):
    """
    Adjust the names or prefixes of the objects.

    In strict mode, the provided name is used as is. It can be helpful
    if the object is referred by that name in other objects.

    In non-strict mode (the default), the object uses the provided name
    as a prefix, while the suffix is added by Kubernetes remotely.
    The actual name should be taken from Kubernetes response
    (this is the recommended scenario).

    If the objects already have their own names, auto-naming is not applied,
    and the existing names are used as is.
    """
    for obj in dicts.walk(objs):
        if obj.get('metadata', {}).get('name', None) is None:
            if strict:
                obj.setdefault('metadata', {}).setdefault('name', name)
            else:
                obj.setdefault('metadata', {}).setdefault('generateName', f'{name}-')
Пример #20
0
def adjust_namespace(
    objs: K8sObjects,
    namespace: Optional[str] = None,
) -> None:
    """
    Adjust the namespace of the objects.

    If the objects already have the namespace set, it will be preserved.

    It is a common practice to keep the children objects in the same
    namespace as their owner, unless explicitly overridden at time of creation.
    """

    # Try to use the current object being handled if possible.
    if namespace is None:
        real_owner = _guess_owner(None)
        namespace = real_owner.get('metadata', {}).get('namespace', None)

    # Set namespace based on the explicitly specified or guessed namespace.
    for obj in cast(Iterator[K8sObject], dicts.walk(objs)):
        obj.setdefault('metadata', {}).setdefault('namespace', namespace)
Пример #21
0
def append_owner_reference(
    objs: K8sObjects,
    owner: Optional[bodies.Body] = None,
) -> None:
    """
    Append an owner reference to the resource(s), if it is not yet there.

    Note: the owned objects are usually not the one being processed,
    so the whole body can be modified, no patches are needed.
    """
    real_owner = _guess_owner(owner)
    owner_ref = bodies.build_owner_reference(real_owner)
    for obj in cast(Iterator[K8sObject], dicts.walk(objs)):
        # Pykube is yielded as a usual dict, no need to specially treat it.
        if isinstance(obj, collections.abc.MutableMapping):
            refs = obj.setdefault('metadata',
                                  {}).setdefault('ownerReferences', [])
            if not any(ref.get('uid') == owner_ref['uid'] for ref in refs):
                refs.append(owner_ref)
        elif isinstance(obj, thirdparty.KubernetesModel):
            if obj.metadata is None:
                obj.metadata = thirdparty.V1ObjectMeta()
            if obj.metadata.owner_references is None:
                obj.metadata.owner_references = []
            refs = obj.metadata.owner_references
            if not any(ref.uid == owner_ref['uid'] for ref in refs):
                refs.append(
                    thirdparty.V1OwnerReference(
                        api_version=owner_ref['apiVersion'],
                        kind=owner_ref['kind'],
                        name=owner_ref['name'],
                        uid=owner_ref['uid'],
                        controller=owner_ref['controller'],
                        block_owner_deletion=owner_ref['blockOwnerDeletion'],
                    ))
        else:
            raise TypeError(f"K8s object class is not supported: {type(obj)}")
Пример #22
0
def adjust_namespace(
    objs: K8sObjects,
    namespace: Optional[str] = None,
    *,
    forced: bool = False,
) -> None:
    """
    Adjust the namespace of the objects.

    If the objects already have the namespace set, it will be preserved.

    It is a common practice to keep the children objects in the same
    namespace as their owner, unless explicitly overridden at time of creation.
    """

    # Try to use the current object being handled if possible.
    if namespace is None:
        real_owner = _guess_owner(None)
        namespace = real_owner.get('metadata', {}).get('namespace', None)
    if namespace is None:
        raise LookupError(
            "Namespace must be set explicitly: couldn't find it automatically."
        )

    # Set namespace based on the explicitly specified or guessed namespace.
    for obj in cast(Iterator[K8sObject], dicts.walk(objs)):
        # Pykube is yielded as a usual dict, no need to specially treat it.
        if isinstance(obj, collections.abc.MutableMapping):
            if forced or obj.get('metadata', {}).get('namespace') is None:
                obj.setdefault('metadata', {})['namespace'] = namespace
        elif isinstance(obj, thirdparty.KubernetesModel):
            if obj.metadata is None:
                obj.metadata = thirdparty.V1ObjectMeta()
            if forced or obj.metadata.namespace is None:
                obj.metadata.namespace = namespace
        else:
            raise TypeError(f"K8s object class is not supported: {type(obj)}")
Пример #23
0
def test_over_a_none():
    result = list(walk(None))
    assert len(result) == 0
Пример #24
0
def event(objs, *, type, reason, message=''):
    queue = event_queue_var.get()
    for obj in dicts.walk(objs):
        ref = hierarchies.build_object_reference(obj)
        event = K8sEvent(ref=ref, type=type, reason=reason, message=message)
        queue.put_nowait(event)
Пример #25
0
def test_over_a_dict():
    obj = {}
    result = list(walk(obj))
    assert len(result) == 1
    assert result[0] is obj
Пример #26
0
def event(objs, *, type, reason, message=''):
    for obj in dicts.walk(objs):
        ref = hierarchies.build_object_reference(obj)
        enqueue(ref=ref, type=type, reason=reason, message=message)