示例#1
0
def test_storing_to_status_storage_populates_keys(cls):
    storage = cls(field='status.my-operator')
    patch = Patch()
    body = Body({})
    storage.store(body=body,
                  patch=patch,
                  key=HandlerId('id1'),
                  record=CONTENT_DATA_1)

    assert patch
    assert patch['status']['my-operator']['id1'] == CONTENT_DATA_1
示例#2
0
def test_purge_progress_when_already_empty_in_body_but_not_in__patch(
        storage, handler):
    body = {}
    patch = Patch(
        {'status': {
            'kopf': {
                'progress': {
                    'some-id': {
                        'retries': 5
                    }
                }
            }
        }})
    origbody = copy.deepcopy(body)
    state = State.from_storage(body=Body(body),
                               handlers=[handler],
                               storage=storage)
    state.purge(patch=patch, body=Body(body), storage=storage)
    assert not patch
    assert body == origbody  # not modified
示例#3
0
def test_for_delete(
        kwargs, event, finalizers, deletion_ts, requires_finalizer):
    event = {'type': event, 'object': {'metadata': {}}}
    event['object']['metadata'].update(finalizers)
    event['object']['metadata'].update(deletion_ts)
    cause = detect_resource_changing_cause(
        raw_event=event,
        body=Body(event['object']),
        **kwargs)
    assert cause.reason == Reason.DELETE
    check_kwargs(cause, kwargs)
示例#4
0
def test_store_failure(storage, handler, expected_retries, expected_stopped,
                       body):
    error = Exception('some-error')
    origbody = copy.deepcopy(body)
    patch = Patch()
    state = State.from_storage(body=Body(body),
                               handlers=[handler],
                               storage=storage)
    state = state.with_outcomes(
        outcomes={handler.id: HandlerOutcome(final=True, exception=error)})
    state.store(patch=patch, body=Body(body), storage=storage)
    assert patch['status']['kopf']['progress']['some-id']['success'] is False
    assert patch['status']['kopf']['progress']['some-id']['failure'] is True
    assert patch['status']['kopf']['progress']['some-id'][
        'retries'] == expected_retries
    assert patch['status']['kopf']['progress']['some-id'][
        'stopped'] == expected_stopped
    assert patch['status']['kopf']['progress']['some-id'][
        'message'] == 'some-error'
    assert body == origbody  # not modified
示例#5
0
def test_repurposed_not_affecting_the_existing_handlers_from_storage(
        storage, handler, reason):
    body = {'status': {'kopf': {'progress': {'some-id': {'purpose': None}}}}}
    state = State.from_storage(body=Body(body),
                               handlers=[handler],
                               storage=storage)
    state = state.with_handlers([handler]).with_purpose(reason).with_handlers(
        [handler])
    assert len(state) == 1
    assert state.purpose is reason
    assert state['some-id'].purpose is None
示例#6
0
def test_fetching_from_annotations_storage(cls):
    storage = cls(name='my-operator.example.com/diff-base')
    body = Body({
        'metadata': {
            'annotations': {
                'my-operator.example.com/diff-base': ESSENCE_JSON_1,
            }
        }
    })
    content = storage.fetch(body=body)

    assert content == ESSENCE_DATA_1
示例#7
0
def test_created_empty_from_empty_storage_with_handlers(
        storage, handler, body):
    state = State.from_storage(body=Body(body),
                               handlers=[handler],
                               storage=storage)
    assert len(state) == 0
    assert state.purpose is None
    assert state.done == True
    assert state.delay is None
    assert state.delays == []
    assert state.counts == StateCounters(success=0, failure=0, running=0)
    assert state.extras == {}
示例#8
0
def test_issue_601_deletion_supersedes_other_processing(storage, reason):

    body = {
        'status': {
            'kopf': {
                'progress': {
                    'fn1': {
                        'purpose': reason.value,
                        'failure': True
                    },
                    'fn2': {
                        'purpose': reason.value,
                        'success': True
                    },
                    'fn3': {
                        'purpose': reason.value,
                        'delayed': TS1_ISO
                    },
                }
            }
        }
    }
    create_handler1 = Mock(id='fn1', spec_set=['id'])
    create_handler2 = Mock(id='fn2', spec_set=['id'])
    create_handler3 = Mock(id='fn3', spec_set=['id'])
    delete_handler9 = Mock(id='delete_fn', spec_set=['id'])
    owned_handlers = [
        create_handler1, create_handler2, create_handler3, delete_handler9
    ]
    cause_handlers = [delete_handler9]

    state = State.from_storage(body=Body(body),
                               handlers=owned_handlers,
                               storage=storage)
    state = state.with_purpose(Reason.DELETE)
    state = state.with_handlers(cause_handlers)

    assert len(state) == 4
    assert state.extras == {
        reason: StateCounters(success=1, failure=1, running=1)
    }
    assert state.counts == StateCounters(success=0, failure=0, running=1)
    assert state.done == False
    assert state.delays == [0.0]

    state = state.with_outcomes({'delete_fn': HandlerOutcome(final=True)})

    assert state.extras == {
        reason: StateCounters(success=1, failure=1, running=1)
    }
    assert state.counts == StateCounters(success=1, failure=0, running=0)
    assert state.done == True
    assert state.delays == []
def test_storing_to_status_storage_overwrites_old_content(
        cls: Type[DiffBaseStorage]):
    storage = cls(field='status.my-operator.diff-base')
    patch = Patch()
    body = Body({})
    storage.store(body=body, patch=patch, essence=ESSENCE_DATA_1)
    storage.store(body=body, patch=patch, essence=ESSENCE_DATA_2)

    assert patch
    assert patch.status['my-operator']['diff-base'][0] != '\n'
    assert patch.status['my-operator']['diff-base'][-1] != '\n'
    assert patch.status['my-operator']['diff-base'] == ESSENCE_JSON_2
def test_storing_to_annotations_storage_populates_keys(cls):
    storage = cls(prefix='my-operator.example.com', verbose=True)
    patch = Patch()
    body = Body({})
    storage.store(body=body,
                  patch=patch,
                  key=HandlerId('id1'),
                  record=CONTENT_DATA_1)

    assert patch
    assert patch['metadata']['annotations'][
        'my-operator.example.com/id1'] == CONTENT_JSON_1
示例#11
0
def test_field_longer_than_diff_for_right_field(cause_with_diff, registry, resource, old, new, reason, decorator):

    @decorator(*resource, field='level1.level2.level3')
    def some_fn(**_): ...

    cause = cause_with_diff
    cause.reason = reason
    cause.old = {'level1': {'level2': old}} if old is not None else {'level1': {'level2': {}}}
    cause.new = {'level1': {'level2': new}} if new is not None else {'level1': {'level2': {}}}
    cause.body = Body(cause.new)
    handlers = registry._resource_changing.get_handlers(cause)
    assert handlers
示例#12
0
def test_daemon_async_stopper(resource, indices):
    cause = DaemonCause(
        logger=logging.getLogger('kopf.test.fake.logger'),
        indices=indices,
        resource=resource,
        patch=Patch(),
        memo=Memo(),
        body=Body({}),
        stopper=DaemonStopper(),
    )
    kwargs = build_kwargs(cause=cause, _sync=False)
    assert kwargs['stopped'] is cause.stopper.async_checker
def test_fetching_from_annotations_storage(cls):
    storage = cls(prefix='my-operator.example.com', verbose=True)
    body = Body({
        'metadata': {
            'annotations': {
                'my-operator.example.com/id1': CONTENT_JSON_1,
            }
        }
    })
    content = storage.fetch(body=body, key=HandlerId('id1'))

    assert content == CONTENT_DATA_1
示例#14
0
def test_fetching_from_status_storage(cls, prefix, suffix):
    storage = cls(field='status.my-operator.diff-base')
    body = Body({
        'status': {
            'my-operator': {
                'diff-base': prefix + ESSENCE_JSON_1 + suffix
            }
        }
    })
    content = storage.fetch(body=body)

    assert content == ESSENCE_DATA_1
示例#15
0
def test_appending_to_kubernetes_model(kubernetes_model):
    kubernetes_model.metadata = None
    kopf.append_owner_reference(kubernetes_model, owner=Body(OWNER))
    assert kubernetes_model.metadata is not None
    assert kubernetes_model.metadata.owner_references is not None
    assert isinstance(kubernetes_model.metadata.owner_references, list)
    assert len(kubernetes_model.metadata.owner_references) == 1
    assert kubernetes_model.metadata.owner_references[
        0].api_version == OWNER_API_VERSION
    assert kubernetes_model.metadata.owner_references[0].kind == OWNER_KIND
    assert kubernetes_model.metadata.owner_references[0].name == OWNER_NAME
    assert kubernetes_model.metadata.owner_references[0].uid == OWNER_UID
示例#16
0
def test_field_longer_than_diff_for_wrong_field(cause_with_diff, registry, resource, reason, decorator):

    @decorator(*resource, field='level1.level2.level3')
    def some_fn(**_): ...

    cause = cause_with_diff
    cause.reason = reason
    cause.old = {'level1': {'level2': 'old'}}
    cause.new = {'level1': {'level2': 'new'}}
    cause.body = Body({'level1': {'level2': 'new'}})
    handlers = registry._resource_changing.get_handlers(cause)
    assert not handlers
示例#17
0
def test_daemon_kwargs(resource, indices):
    body = {
        'metadata': {
            'uid': 'uid1',
            'name': 'name1',
            'namespace': 'ns1',
            'labels': {
                'l1': 'v1'
            },
            'annotations': {
                'a1': 'v1'
            }
        },
        'spec': {
            'field': 'value'
        },
        'status': {
            'info': 'payload'
        }
    }
    cause = DaemonCause(
        logger=logging.getLogger('kopf.test.fake.logger'),
        indices=indices,
        resource=resource,
        patch=Patch(),
        memo=Memo(),
        body=Body(body),
        stopper=DaemonStopper(),
    )
    kwargs = build_kwargs(cause=cause, extrakwarg=123)
    assert set(kwargs) == {
        'extrakwarg', 'logger', 'index1', 'index2', 'resource', 'patch',
        'memo', 'body', 'spec', 'status', 'meta', 'uid', 'name', 'namespace',
        'labels', 'annotations'
    }
    assert kwargs['extrakwarg'] == 123
    assert kwargs['resource'] is cause.resource
    assert kwargs['index1'] is indices['index1']
    assert kwargs['index2'] is indices['index2']
    assert kwargs['logger'] is cause.logger
    assert kwargs['patch'] is cause.patch
    assert kwargs['memo'] is cause.memo
    assert kwargs['body'] is cause.body
    assert kwargs['spec'] is cause.body.spec
    assert kwargs['meta'] is cause.body.metadata
    assert kwargs['status'] is cause.body.status
    assert kwargs['labels'] is cause.body.metadata.labels
    assert kwargs['annotations'] is cause.body.metadata.annotations
    assert kwargs['uid'] == cause.body.metadata.uid
    assert kwargs['name'] == cause.body.metadata.name
    assert kwargs['namespace'] == cause.body.metadata.namespace
    assert 'stopped' not in kwargs
def test_fetching_from_status_storage(cls):
    storage = cls(field='status.my-operator')
    body = Body({
        'status': {
            'my-operator': {
                'id1': CONTENT_DATA_1,
                'id2': CONTENT_DATA_2
            }
        }
    })
    content = storage.fetch(body=body, key=HandlerId('id1'))

    assert content == CONTENT_DATA_1
示例#19
0
def test_removal_distinguishes_by_uid():
    owner1 = copy.deepcopy(OWNER)
    owner2 = copy.deepcopy(OWNER)
    owner3 = copy.deepcopy(OWNER)
    owner1['metadata']['uid'] = 'uid-a'
    owner2['metadata']['uid'] = 'uid-b'
    owner3['metadata']['uid'] = 'uid-c'
    obj = {}

    # Three very similar owners added, different only by uid.
    # One is removed, others must stay (even if kinds/names are the same).
    kopf.append_owner_reference(
        obj, owner=Body(owner1))  # assumed to work, tested above
    kopf.append_owner_reference(
        obj, owner=Body(owner2))  # assumed to work, tested above
    kopf.append_owner_reference(
        obj, owner=Body(owner3))  # assumed to work, tested above
    kopf.remove_owner_reference(
        obj, owner=Body(owner1))  # this one is being tested here

    uids = {ref['uid'] for ref in obj['metadata']['ownerReferences']}
    assert uids == {'uid-b', 'uid-c'}
示例#20
0
def test_with_handlers_relevant_to_the_purpose(storage, handler, body,
                                               expected_counts, expected_done,
                                               expected_delays, reason):
    body['status']['kopf']['progress']['some-id']['purpose'] = reason.value
    state = State.from_storage(body=Body(body),
                               handlers=[handler],
                               storage=storage)
    state = state.with_purpose(reason)
    assert len(state) == 1
    assert state.extras == {}
    assert state.counts == expected_counts
    assert state.done == expected_done
    assert state.delays == expected_delays
示例#21
0
def test_for_no_op(kwargs, event, finalizers, deletion_ts, old, annotations,
                   content, requires_finalizer):
    event = {'type': event, 'object': {'metadata': {}}}
    event['object'].update(content)
    event['object']['metadata'].update(finalizers)
    event['object']['metadata'].update(deletion_ts)
    event['object']['metadata'].update(annotations)
    cause = detect_resource_changing_cause(raw_event=event,
                                           body=Body(event['object']),
                                           old=old,
                                           **kwargs)
    assert cause.reason == Reason.NOOP
    check_kwargs(cause, kwargs)
示例#22
0
def test_get_essence_removes_status_and_cleans_parents(
    cls: Type[DiffBaseStorage], ):
    body = Body(
        {'status': {
            'kopf': {
                'progress': 'x',
                'anything': 'y'
            },
            'other': 'z'
        }})
    storage = cls()
    essence = storage.build(body=body)
    assert essence == {}
示例#23
0
def test_get_essence_removes_status_but_keeps_extra_fields(
    cls: Type[DiffBaseStorage], ):
    body = Body(
        {'status': {
            'kopf': {
                'progress': 'x',
                'anything': 'y'
            },
            'other': 'z'
        }})
    storage = cls()
    essence = storage.build(body=body, extra_fields=['status.other'])
    assert essence == {'status': {'other': 'z'}}
示例#24
0
def test_storing_to_annotations_storage_populates_keys(cls):
    storage = cls(prefix='my-operator.example.com', key='diff-base')
    patch = Patch()
    body = Body({})
    storage.store(body=body, patch=patch, essence=ESSENCE_DATA_1)

    assert patch
    assert patch.meta.annotations['my-operator.example.com/diff-base'][
        0] != '\n'
    assert patch.meta.annotations['my-operator.example.com/diff-base'][
        -1] == '\n'
    assert patch.meta.annotations['my-operator.example.com/diff-base'].strip(
    ) == ESSENCE_JSON_1
示例#25
0
def test_fetching_from_annotations_storage(cls, prefix, suffix):
    storage = cls(prefix='my-operator.example.com', key='diff-base')
    body = Body({
        'metadata': {
            'annotations': {
                'my-operator.example.com/diff-base':
                prefix + ESSENCE_JSON_1 + suffix,
            }
        }
    })
    content = storage.fetch(body=body)

    assert content == ESSENCE_DATA_1
示例#26
0
async def test_special_kwargs_added(fn, resource):
    body = {
        'metadata': {
            'uid': 'uid',
            'name': 'name',
            'namespace': 'ns'
        },
        'spec': {
            'field': 'value'
        },
        'status': {
            'info': 'payload'
        }
    }

    # Values can be any.
    cause = ResourceChangingCause(
        logger=logging.getLogger('kopf.test.fake.logger'),
        resource=resource,
        patch=Patch(),
        initial=False,
        reason=Reason.NOOP,
        memo=object(),
        body=Body(body),
        diff=object(),
        old=object(),
        new=object(),
    )

    fn = MagicMock(fn)
    await invoke(fn, cause=cause)

    assert fn.called
    assert fn.call_count == 1

    assert len(fn.call_args[1]) >= 2
    assert fn.call_args[1]['cause'] is cause
    assert fn.call_args[1]['event'] is cause.reason  # deprecated
    assert fn.call_args[1]['reason'] is cause.reason
    assert fn.call_args[1]['body'] is cause.body
    assert fn.call_args[1]['spec'] == cause.body['spec']
    assert fn.call_args[1]['meta'] == cause.body['metadata']
    assert fn.call_args[1]['status'] == cause.body['status']
    assert fn.call_args[1]['diff'] is cause.diff
    assert fn.call_args[1]['old'] is cause.old
    assert fn.call_args[1]['new'] is cause.new
    assert fn.call_args[1]['patch'] is cause.patch
    assert fn.call_args[1]['logger'] is cause.logger
    assert fn.call_args[1]['uid'] == cause.body['metadata']['uid']
    assert fn.call_args[1]['name'] == cause.body['metadata']['name']
    assert fn.call_args[1]['namespace'] == cause.body['metadata']['namespace']
示例#27
0
def test_storing_to_annotations_storage_overwrites_old_content(cls):
    storage = cls(name='my-operator.example.com/diff-base')
    patch = Patch()
    body = Body({})
    storage.store(body=body, patch=patch, essence=ESSENCE_DATA_1)
    storage.store(body=body, patch=patch, essence=ESSENCE_DATA_2)

    assert patch
    assert patch.meta.annotations['my-operator.example.com/diff-base'][
        0] != '\n'
    assert patch.meta.annotations['my-operator.example.com/diff-base'][
        -1] == '\n'
    assert patch.meta.annotations['my-operator.example.com/diff-base'].strip(
    ) == ESSENCE_JSON_2
示例#28
0
def test_removal_identifies_by_uid():
    owner1 = copy.deepcopy(OWNER)
    owner2 = copy.deepcopy(OWNER)
    owner3 = copy.deepcopy(OWNER)
    owner1['kind'] = 'KindA'
    owner2['kind'] = 'KindA'
    owner3['kind'] = 'KindB'
    owner1['metadata']['name'] = 'name-a'
    owner2['metadata']['name'] = 'name-b'
    owner3['metadata']['name'] = 'name-b'
    owner1['metadata']['uid'] = 'uid-0'
    owner2['metadata']['uid'] = 'uid-0'
    owner3['metadata']['uid'] = 'uid-0'
    obj = {}

    # Three different owners added, but all have the same uid.
    # One is removed and only once, all must be gone (due to same uids).
    kopf.append_owner_reference(obj, owner=Body(owner1))  # assumed to work, tested above
    kopf.append_owner_reference(obj, owner=Body(owner2))  # assumed to work, tested above
    kopf.append_owner_reference(obj, owner=Body(owner3))  # assumed to work, tested above
    kopf.remove_owner_reference(obj, owner=Body(owner1))  # this one is being tested here

    assert len(obj['metadata']['ownerReferences']) == 0
示例#29
0
def test_appending_to_dict():
    obj = {}

    kopf.append_owner_reference(obj, owner=Body(OWNER))

    assert 'metadata' in obj
    assert 'ownerReferences' in obj['metadata']
    assert isinstance(obj['metadata']['ownerReferences'], list)
    assert len(obj['metadata']['ownerReferences']) == 1
    assert isinstance(obj['metadata']['ownerReferences'][0], dict)
    assert obj['metadata']['ownerReferences'][0]['apiVersion'] == OWNER_API_VERSION
    assert obj['metadata']['ownerReferences'][0]['kind'] == OWNER_KIND
    assert obj['metadata']['ownerReferences'][0]['name'] == OWNER_NAME
    assert obj['metadata']['ownerReferences'][0]['uid'] == OWNER_UID
def test_touching_via_annotations_storage_with_none_when_present(cls):
    storage = cls(prefix='my-operator.example.com', touch_key='my-dummy')
    patch = Patch()
    body = Body({
        'metadata': {
            'annotations': {
                'my-operator.example.com/my-dummy': 'something'
            }
        }
    })
    storage.touch(body=body, patch=patch, value=None)

    assert patch
    assert patch['metadata']['annotations'][
        'my-operator.example.com/my-dummy'] is None