async def test_diffs_logged_if_present(registry, resource, handlers,
                                       cause_type, cause_mock, caplog,
                                       assert_logs, diff):
    caplog.set_level(logging.DEBUG)

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_type
    cause_mock.diff = diff
    cause_mock.new = object()  # checked for `not None`
    cause_mock.old = object()  # checked for `not None`

    await resource_handler(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        resource=resource,
        memories=ResourceMemories(),
        event={
            'type': event_type,
            'object': cause_mock.body
        },
        freeze=asyncio.Event(),
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )
    assert_logs([" event: ", " diff: "])
Beispiel #2
0
async def test_handlers_called_always(
        registry, settings, handlers, extrahandlers, resource, cause_mock, cause_type,
        caplog, assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = cause_type

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={'type': 'ev-type', 'object': {'field': 'value'}},
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )

    assert handlers.event_mock.call_count == 1
    assert extrahandlers.event_mock.call_count == 1

    event = handlers.event_mock.call_args_list[0][1]['event']
    assert 'field' in event['object']
    assert event['object']['field'] == 'value'
    assert event['type'] == 'ev-type'

    assert_logs([
        "Handler 'event_fn' is invoked.",
        "Handler 'event_fn' succeeded.",
        "Handler 'event_fn2' is invoked.",
        "Handler 'event_fn2' succeeded.",
    ])
Beispiel #3
0
async def test_stealth_mode_with_mismatching_handlers(
        registry, settings, selector, resource, cause_mock, cause_type,
        caplog, assert_logs, k8s_mocked, annotations, labels, when, deleted, initial):
    caplog.set_level(logging.DEBUG)

    event_type = None
    event_body = {'metadata': {'finalizers': []}}
    cause_mock.reason = cause_type

    assert not registry._resource_changing.has_handlers(resource=resource)  # prerequisite
    registry._resource_changing.append(ResourceChangingHandler(
        reason=None,
        fn=lambda **_: None, id='id',
        errors=None, timeout=None, retries=None, backoff=None,
        selector=selector, annotations=annotations, labels=labels, when=when,
        field=None, value=None, old=None, new=None, field_needs_change=None,
        deleted=deleted, initial=initial, requires_finalizer=None,
    ))

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={'type': event_type, 'object': event_body},
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )

    assert not k8s_mocked.sleep_or_wait.called
    assert not k8s_mocked.patch_obj.called
    assert not caplog.messages  # total stealth mode!
Beispiel #4
0
async def test_retries_limited_handler_fails(registry, settings, handlers,
                                             extrahandlers, resource,
                                             cause_mock, cause_type, caplog,
                                             assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    name1 = f'{cause_type}_fn'

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    event_body = {
        'status': {
            'kopf': {
                'progress': {
                    'create_fn': {
                        'retries': 100
                    },
                    'update_fn': {
                        'retries': 100
                    },
                    'delete_fn': {
                        'retries': 100
                    },
                    'resume_fn': {
                        'retries': 100
                    },
                }
            }
        }
    }
    cause_mock.reason = cause_type

    await process_resource_event(
        lifecycle=kopf.lifecycles.one_by_one,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': event_type,
            'object': event_body
        },
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )

    assert not handlers.create_mock.called
    assert not handlers.update_mock.called
    assert not handlers.delete_mock.called
    assert not handlers.resume_mock.called

    # Progress is reset, as the handler is not going to retry.
    assert not k8s_mocked.sleep_or_wait.called
    assert k8s_mocked.patch_obj.called

    patch = k8s_mocked.patch_obj.call_args_list[0][1]['patch']
    assert patch['status']['kopf']['progress'] is not None
    assert patch['status']['kopf']['progress'][name1]['failure'] is True

    assert_logs([
        r"Handler .+ has exceeded \d+ retries",
    ])
Beispiel #5
0
async def test_reporting_on_resource_readiness(resource, settings, registry,
                                               indexers, caplog, event_type,
                                               handlers, timer):
    caplog.set_level(logging.DEBUG)

    operator_indexed = ToggleSet(all)
    resource_indexed = await operator_indexed.make_toggle()
    async with timer, async_timeout.timeout(0.5) as timeout:
        await process_resource_event(
            lifecycle=all_at_once,
            registry=registry,
            settings=settings,
            resource=resource,
            indexers=indexers,
            memories=ResourceMemories(),
            memobase=Memo(),
            raw_event={
                'type': event_type,
                'object': {}
            },
            event_queue=asyncio.Queue(),
            operator_indexed=operator_indexed,
            resource_indexed=resource_indexed,
        )
    assert not timeout.expired
    assert timer.seconds < 0.2  # asap, nowait
    assert operator_indexed.is_on()
    assert set(operator_indexed) == set()  # save RAM
    assert handlers.event_mock.called
Beispiel #6
0
async def test_preserved_on_logical_deletion(resource, settings, registry,
                                             indexers, index, caplog,
                                             event_type, handlers):
    caplog.set_level(logging.DEBUG)
    body = {
        'metadata': {
            'namespace': 'ns1',
            'name': 'name1',
            'deletionTimestamp': '...'
        }
    }
    handlers.index_mock.return_value = 456
    await process_resource_event(
        lifecycle=all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=indexers,
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': event_type,
            'object': body
        },
        event_queue=asyncio.Queue(),
        resource_indexed=Toggle(),  # used! only to enable indexing.
    )
    assert set(index) == {None}
    assert set(index[None]) == {456}
Beispiel #7
0
async def test_gone(registry, handlers, resource, cause_mock, event_type,
                    caplog, assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = Reason.GONE

    event_queue = asyncio.Queue()
    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        replenished=asyncio.Event(),
        event_queue=event_queue,
    )

    assert not handlers.create_mock.called
    assert not handlers.update_mock.called
    assert not handlers.delete_mock.called

    assert not k8s_mocked.asyncio_sleep.called
    assert not k8s_mocked.sleep_or_wait.called
    assert not k8s_mocked.patch_obj.called
    assert event_queue.empty()

    assert_logs([
        "Deleted, really deleted",
    ])
Beispiel #8
0
async def test_diffs_logged_if_present(registry, settings, resource, handlers,
                                       cause_type, cause_mock, caplog,
                                       assert_logs, diff):
    caplog.set_level(logging.DEBUG)

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_type
    cause_mock.diff = diff
    cause_mock.new = {
        'field': 'old'
    }  # checked for `not None`, and JSON-serialised
    cause_mock.old = {
        'field': 'new'
    }  # checked for `not None`, and JSON-serialised

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )
    assert_logs([" event: ", " diff: "])
Beispiel #9
0
async def test_supersession_is_logged(
        registry, settings, resource, handlers, cause_types, cause_mock, caplog, assert_logs):
    caplog.set_level(logging.DEBUG)

    settings.persistence.progress_storage = StatusProgressStorage()
    body = {'status': {'kopf': {'progress': {
        'create_fn': {'purpose': cause_types[0]},
        'update_fn': {'purpose': cause_types[0]},
        'resume_fn': {'purpose': cause_types[0]},
        'delete_fn': {'purpose': cause_types[0]},
    }}}}

    cause_mock.reason = cause_types[1]
    event_type = None if cause_types[1] == Reason.RESUME else 'irrelevant'

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={'type': event_type, 'object': body},
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )
    assert_logs([
        "(Creation|Updating|Resuming|Deletion) is superseded by (creation|updating|resuming|deletion): ",
        "(Creation|Updating|Resuming|Deletion) is in progress: ",
        "(Creation|Updating|Resuming|Deletion) is processed: ",
    ])
Beispiel #10
0
async def test_errors_are_ignored(registry, handlers, extrahandlers, resource,
                                  cause_mock, cause_type, caplog, assert_logs,
                                  k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = cause_type
    handlers.event_mock.side_effect = Exception("oops")

    await resource_handler(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        resource=resource,
        memories=ResourceMemories(),
        event={
            'type': 'ev-type',
            'object': cause_mock.body
        },
        freeze=asyncio.Event(),
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )

    assert handlers.event_mock.called
    assert extrahandlers.event_mock.called

    assert_logs([
        "Invoking handler 'event_fn'.",
        "Handler 'event_fn' failed with an exception. Will ignore.",
        "Invoking handler 'event_fn2'.",
        "Handler 'event_fn2' succeeded.",
    ])
Beispiel #11
0
async def test_diffs_logged_if_present(registry, settings, resource, handlers,
                                       cause_type, cause_mock, caplog,
                                       assert_logs, diff):
    caplog.set_level(logging.DEBUG)

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_type
    cause_mock.diff = diff
    cause_mock.new = {
        'field': 'old'
    }  # checked for `not None`, and JSON-serialised
    cause_mock.old = {
        'field': 'new'
    }  # checked for `not None`, and JSON-serialised

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=OperatorIndexers(),
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        event_queue=asyncio.Queue(),
    )
    assert_logs([
        "(Creation|Updating|Resuming|Deletion) is in progress: ",
        "(Creation|Updating|Resuming|Deletion) diff: "
    ])
Beispiel #12
0
async def test_handlers_called_always(registry, handlers, extrahandlers,
                                      resource, cause_mock, cause_type, caplog,
                                      assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = cause_type

    await resource_handler(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        resource=resource,
        memories=ResourceMemories(),
        event={
            'type': 'ev-type',
            'object': cause_mock.body
        },
        freeze=asyncio.Event(),
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )

    assert handlers.event_mock.call_count == 1
    assert extrahandlers.event_mock.call_count == 1

    event = handlers.event_mock.call_args_list[0][1]['event']
    assert event['type'] == 'ev-type'
    assert event['object'] is cause_mock.body
    assert event['type'] == 'ev-type'
    assert event['object'] is cause_mock.body

    assert_logs([
        "Invoking handler 'event_fn'.",
        "Handler 'event_fn' succeeded.",
        "Invoking handler 'event_fn2'.",
        "Handler 'event_fn2' succeeded.",
    ])
Beispiel #13
0
async def test_errors_are_ignored(registry, settings, handlers, extrahandlers,
                                  resource, cause_mock, cause_type, caplog,
                                  assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = cause_type
    handlers.event_mock.side_effect = Exception("oops")

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=OperatorIndexers(),
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': 'ev-type',
            'object': {}
        },
        event_queue=asyncio.Queue(),
    )

    assert handlers.event_mock.called
    assert extrahandlers.event_mock.called

    assert_logs([
        "Handler 'event_fn' is invoked.",
        "Handler 'event_fn' failed with an exception. Will ignore.",
        "Handler 'event_fn2' is invoked.",
        "Handler 'event_fn2' succeeded.",
    ])
Beispiel #14
0
async def test_timed_out_handler_fails(registry, handlers, extrahandlers,
                                       resource, cause_mock, cause_type,
                                       caplog, assert_logs, k8s_mocked, now,
                                       ts):
    caplog.set_level(logging.DEBUG)
    name1 = f'{cause_type}_fn'

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_type
    cause_mock.body.update({
        'status': {
            'kopf': {
                'progress': {
                    'create_fn': {
                        'started': ts
                    },
                    'update_fn': {
                        'started': ts
                    },
                    'delete_fn': {
                        'started': ts
                    },
                    'resume_fn': {
                        'started': ts
                    },
                }
            }
        }
    })

    with freezegun.freeze_time(now):
        await process_resource_event(
            lifecycle=kopf.lifecycles.one_by_one,
            registry=registry,
            resource=resource,
            memories=ResourceMemories(),
            event={
                'type': event_type,
                'object': cause_mock.body
            },
            replenished=asyncio.Event(),
            event_queue=asyncio.Queue(),
        )

    assert not handlers.create_mock.called
    assert not handlers.update_mock.called
    assert not handlers.delete_mock.called
    assert not handlers.resume_mock.called

    # Progress is reset, as the handler is not going to retry.
    assert not k8s_mocked.sleep_or_wait.called
    assert k8s_mocked.patch_obj.called

    patch = k8s_mocked.patch_obj.call_args_list[0][1]['patch']
    assert patch['status']['kopf']['progress'] is not None
    assert patch['status']['kopf']['progress'][name1]['failure'] is True

    assert_logs([
        "Handler .+ has timed out after",
    ])
Beispiel #15
0
async def test_diffs_not_logged_if_absent(registry, settings, resource,
                                          handlers, cause_type, cause_mock,
                                          caplog, assert_logs, diff):
    caplog.set_level(logging.DEBUG)

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_type
    cause_mock.diff = diff

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=OperatorIndexers(),
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        event_queue=asyncio.Queue(),
    )
    assert_logs([
        "(Creation|Updating|Resuming|Deletion) is in progress: ",
    ],
                prohibited=[" diff: "])
Beispiel #16
0
async def test_free(registry, handlers, resource, cause_mock, event_type,
                    caplog, assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = Reason.FREE

    event_queue = asyncio.Queue()
    await resource_handler(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        resource=resource,
        memories=ResourceMemories(),
        event={
            'type': event_type,
            'object': cause_mock.body
        },
        replenished=asyncio.Event(),
        event_queue=event_queue,
    )

    assert not handlers.create_mock.called
    assert not handlers.update_mock.called
    assert not handlers.delete_mock.called

    assert not k8s_mocked.asyncio_sleep.called
    assert not k8s_mocked.sleep_or_wait.called
    assert not k8s_mocked.patch_obj.called
    assert event_queue.empty()

    assert_logs([
        "Deletion event, but we are done with it",
    ])
Beispiel #17
0
async def test_blocking_when_operator_is_not_ready(resource, settings,
                                                   registry, indexers, caplog,
                                                   event_type, handlers,
                                                   timer):
    caplog.set_level(logging.DEBUG)

    operator_indexed = ToggleSet(all)
    resource_listed = await operator_indexed.make_toggle()
    resource_indexed = await operator_indexed.make_toggle()
    with pytest.raises(asyncio.TimeoutError):
        async with timer, async_timeout.timeout(0.2) as timeout:
            await process_resource_event(
                lifecycle=all_at_once,
                registry=registry,
                settings=settings,
                resource=resource,
                indexers=indexers,
                memories=ResourceMemories(),
                memobase=Memo(),
                raw_event={
                    'type': event_type,
                    'object': {}
                },
                event_queue=asyncio.Queue(),
                operator_indexed=operator_indexed,
                resource_indexed=resource_indexed,
            )
    assert timeout.expired
    assert 0.2 < timer.seconds < 0.4
    assert operator_indexed.is_off()
    assert set(operator_indexed) == {resource_listed}
    assert not handlers.event_mock.called
Beispiel #18
0
async def test_unblocking_once_operator_is_ready(resource, settings, registry,
                                                 indexers, caplog, event_type,
                                                 handlers, timer):
    caplog.set_level(logging.DEBUG)

    async def delayed_readiness(delay: float):
        await asyncio.sleep(delay)
        await resource_listed.turn_to(True)

    operator_indexed = ToggleSet(all)
    resource_listed = await operator_indexed.make_toggle()
    resource_indexed = await operator_indexed.make_toggle()
    async with timer, async_timeout.timeout(1.0) as timeout:
        asyncio.create_task(delayed_readiness(0.2))
        await process_resource_event(
            lifecycle=all_at_once,
            registry=registry,
            settings=settings,
            resource=resource,
            indexers=indexers,
            memories=ResourceMemories(),
            memobase=Memo(),
            raw_event={
                'type': event_type,
                'object': {}
            },
            event_queue=asyncio.Queue(),
            operator_indexed=operator_indexed,
            resource_indexed=resource_indexed,
        )
    assert not timeout.expired
    assert 0.2 < timer.seconds < 0.4
    assert operator_indexed.is_on()
    assert set(operator_indexed) == {resource_listed}
    assert handlers.event_mock.called
Beispiel #19
0
async def test_skipped_with_no_handlers(registry, settings, resource,
                                        cause_mock, cause_type, caplog,
                                        assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)

    event_type = None
    event_body = {'metadata': {'finalizers': []}}
    cause_mock.reason = cause_type

    assert not registry.resource_changing_handlers[resource]  # prerequisite
    registry.resource_changing_handlers[resource].append(
        ResourceChangingHandler(
            reason='a-non-existent-cause-type',
            fn=lambda **_: None,
            id='id',
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
            cooldown=None,
            annotations=None,
            labels=None,
            when=None,
            field=None,
            deleted=None,
            initial=None,
            requires_finalizer=None,
        ))

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': event_type,
            'object': event_body
        },
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )

    assert not k8s_mocked.sleep_or_wait.called
    assert k8s_mocked.patch_obj.called

    # The patch must contain ONLY the last-seen update, and nothing else.
    patch = k8s_mocked.patch_obj.call_args_list[0][1]['patch']
    assert set(patch.keys()) == {'metadata'}
    assert set(patch['metadata'].keys()) == {'annotations'}
    assert set(
        patch['metadata']['annotations'].keys()) == {LAST_SEEN_ANNOTATION}

    assert_logs([
        ".* event:",
        "Patching with:",
    ],
                prohibited=[
                    "event is processed:",
                ])
Beispiel #20
0
async def test_removed_on_filters_mismatch(resource, settings, registry,
                                           indexers, index, caplog, event_type,
                                           handlers, mocker):

    # Simulate the indexing handler is gone out of scope (this is only one of the ways to do it):
    mocker.patch.object(registry._resource_indexing,
                        'get_handlers',
                        return_value=[])

    caplog.set_level(logging.DEBUG)
    body = {'metadata': {'namespace': 'ns1', 'name': 'name1'}}
    handlers.index_mock.return_value = 123
    await process_resource_event(
        lifecycle=all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=indexers,
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': event_type,
            'object': body
        },
        event_queue=asyncio.Queue(),
        resource_indexed=Toggle(),  # used! only to enable indexing.
    )
    assert set(index) == set()
Beispiel #21
0
async def test_2nd_step_finishes_the_handlers(caplog, registry, handlers,
                                              extrahandlers, resource,
                                              cause_mock, cause_type,
                                              k8s_mocked):
    name1 = f'{cause_type}_fn'
    name2 = f'{cause_type}_fn2'

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    event_body = {
        'status': {
            'kopf': {
                'progress': {
                    'resume_fn': {
                        'started': '1979-01-01T00:00:00',
                        'success': True
                    },
                    'resume_fn2': {
                        'started': '1979-01-01T00:00:00',
                        'success': True
                    },
                    name1: {
                        'started': '1979-01-01T00:00:00',
                        'success': True
                    },
                    name2: {
                        'started': '1979-01-01T00:00:00'
                    },
                }
            }
        }
    }
    cause_mock.reason = cause_type

    await process_resource_event(
        lifecycle=kopf.lifecycles.one_by_one,
        registry=registry,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': event_type,
            'object': event_body
        },
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )

    assert extrahandlers.create_mock.call_count == (1 if cause_type
                                                    == Reason.CREATE else 0)
    assert extrahandlers.update_mock.call_count == (1 if cause_type
                                                    == Reason.UPDATE else 0)
    assert extrahandlers.delete_mock.call_count == (1 if cause_type
                                                    == Reason.DELETE else 0)
    assert extrahandlers.resume_mock.call_count == (1 if cause_type
                                                    == Reason.RESUME else 0)

    assert not k8s_mocked.sleep_or_wait.called
    assert k8s_mocked.patch_obj.called

    patch = k8s_mocked.patch_obj.call_args_list[0][1]['patch']
    assert patch['status']['kopf']['progress'] is None
Beispiel #22
0
async def test_parameter_is_passed_when_specified(resource, cause_mock,
                                                  registry, settings):
    mock = Mock()

    # If it works for this handler, we assume it works for all of them.
    # Otherwise, it is too difficult to trigger the actual invocation.
    @kopf.on.event(*resource, param=123)
    def fn(**kwargs):
        mock(**kwargs)

    event_queue = asyncio.Queue()
    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': None,
            'object': {}
        },
        replenished=asyncio.Event(),
        event_queue=event_queue,
    )

    assert mock.called
    assert mock.call_args_list[0][1]['param'] == 123
Beispiel #23
0
async def test_free(registry, settings, handlers, resource, cause_mock,
                    event_type, caplog, assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = Reason.FREE

    event_queue = asyncio.Queue()
    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=OperatorIndexers(),
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        event_queue=event_queue,
    )

    assert not handlers.create_mock.called
    assert not handlers.update_mock.called
    assert not handlers.delete_mock.called

    assert not k8s_mocked.sleep_or_wait.called
    assert not k8s_mocked.patch_obj.called
    assert event_queue.empty()

    assert_logs([
        "Deletion, but we are done with it",
    ])
Beispiel #24
0
async def test_noop(registry, settings, handlers, resource, cause_mock,
                    event_type, caplog, assert_logs, k8s_mocked):
    caplog.set_level(logging.DEBUG)
    cause_mock.reason = Reason.NOOP

    event_queue = asyncio.Queue()
    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        replenished=asyncio.Event(),
        event_queue=event_queue,
    )

    assert not handlers.create_mock.called
    assert not handlers.update_mock.called
    assert not handlers.delete_mock.called

    assert not k8s_mocked.sleep_or_wait.called
    assert not k8s_mocked.patch_obj.called
    assert event_queue.empty()

    assert_logs([
        "Something has changed, but we are not interested",
    ])
Beispiel #25
0
async def test_diffs_not_logged_if_absent(registry, settings, resource,
                                          handlers, cause_type, cause_mock,
                                          caplog, assert_logs, diff):
    caplog.set_level(logging.DEBUG)

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_type
    cause_mock.diff = diff

    await process_resource_event(
        lifecycle=kopf.lifecycles.all_at_once,
        registry=registry,
        settings=settings,
        resource=resource,
        memories=ResourceMemories(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        replenished=asyncio.Event(),
        event_queue=asyncio.Queue(),
    )
    assert_logs([
        " event: ",
    ], prohibited=[" diff: "])
Beispiel #26
0
async def test_1st_step_stores_progress_by_patching(registry, settings,
                                                    handlers, extrahandlers,
                                                    resource, cause_mock,
                                                    cause_type, k8s_mocked,
                                                    deletion_ts):
    name1 = f'{cause_type}_fn'
    name2 = f'{cause_type}_fn2'

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    event_body = {
        'metadata': {
            'finalizers': [settings.persistence.finalizer]
        },
    }
    event_body['metadata'].update(deletion_ts)
    cause_mock.reason = cause_type

    await process_resource_event(
        lifecycle=kopf.lifecycles.asap,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=OperatorIndexers(),
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': event_type,
            'object': event_body
        },
        event_queue=asyncio.Queue(),
    )

    assert handlers.create_mock.call_count == (1 if cause_type == Reason.CREATE
                                               else 0)
    assert handlers.update_mock.call_count == (1 if cause_type == Reason.UPDATE
                                               else 0)
    assert handlers.delete_mock.call_count == (1 if cause_type == Reason.DELETE
                                               else 0)
    assert handlers.resume_mock.call_count == (1 if cause_type == Reason.RESUME
                                               else 0)

    assert not k8s_mocked.sleep_or_wait.called
    assert k8s_mocked.patch_obj.called

    patch = k8s_mocked.patch_obj.call_args_list[0][1]['patch']
    assert patch['status']['kopf']['progress'] is not None

    assert patch['status']['kopf']['progress'][name1]['retries'] == 1
    assert patch['status']['kopf']['progress'][name1]['success'] is True

    assert patch['status']['kopf']['progress'][name2]['retries'] == 0
    assert patch['status']['kopf']['progress'][name2]['success'] is False

    assert patch['status']['kopf']['progress'][name1]['started']
    assert patch['status']['kopf']['progress'][name2]['started']

    # Premature removal of finalizers can prevent the 2nd step for deletion handlers.
    # So, the finalizers must never be removed on the 1st step.
    assert 'finalizers' not in patch['metadata']
async def test_forgetting_deletes_when_present():
    memories = ResourceMemories()
    memory1 = await memories.recall(BODY)
    await memories.forget(BODY)

    # Check by recalling -- it should be a new one.
    memory2 = await memories.recall(BODY)
    assert memory1 is not memory2
Beispiel #28
0
async def test_memo_is_shallow_copied():
    class MyMemo(Memo):
        def __copy__(self):
            mock()
            return MyMemo()

    mock = Mock()
    memo = MyMemo()
    memories = ResourceMemories()
    memory = await memories.recall(BODY, memo=memo)
    assert mock.call_count == 1
    assert memory.memo is not memo
Beispiel #29
0
async def test_retry_error_delays_handler(registry, settings, handlers,
                                          extrahandlers, resource, cause_mock,
                                          cause_type, caplog, assert_logs,
                                          k8s_mocked):
    caplog.set_level(logging.DEBUG)
    name1 = f'{cause_type}_fn'

    event_type = None if cause_type == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_type
    handlers.create_mock.side_effect = TemporaryError("oops")
    handlers.update_mock.side_effect = TemporaryError("oops")
    handlers.delete_mock.side_effect = TemporaryError("oops")
    handlers.resume_mock.side_effect = TemporaryError("oops")

    await process_resource_event(
        lifecycle=kopf.lifecycles.one_by_one,
        registry=registry,
        settings=settings,
        resource=resource,
        indexers=OperatorIndexers(),
        memories=ResourceMemories(),
        memobase=Memo(),
        raw_event={
            'type': event_type,
            'object': {}
        },
        event_queue=asyncio.Queue(),
    )

    assert handlers.create_mock.call_count == (1 if cause_type == Reason.CREATE
                                               else 0)
    assert handlers.update_mock.call_count == (1 if cause_type == Reason.UPDATE
                                               else 0)
    assert handlers.delete_mock.call_count == (1 if cause_type == Reason.DELETE
                                               else 0)
    assert handlers.resume_mock.call_count == (1 if cause_type == Reason.RESUME
                                               else 0)

    assert not k8s_mocked.sleep_or_wait.called
    assert k8s_mocked.patch_obj.called

    patch = k8s_mocked.patch_obj.call_args_list[0][1]['patch']
    assert patch['status']['kopf']['progress'] is not None
    assert patch['status']['kopf']['progress'][name1]['failure'] is False
    assert patch['status']['kopf']['progress'][name1]['success'] is False
    assert patch['status']['kopf']['progress'][name1]['delayed']

    assert_logs([
        "Handler .+ failed temporarily: oops",
    ])
Beispiel #30
0
async def test_delayed_handlers_progress(registry, settings, handlers,
                                         resource, cause_mock, cause_reason,
                                         caplog, assert_logs, k8s_mocked, now,
                                         delayed_iso, delay):
    caplog.set_level(logging.DEBUG)

    handlers.create_mock.side_effect = TemporaryError("oops", delay=delay)
    handlers.update_mock.side_effect = TemporaryError("oops", delay=delay)
    handlers.delete_mock.side_effect = TemporaryError("oops", delay=delay)
    handlers.resume_mock.side_effect = TemporaryError("oops", delay=delay)

    event_type = None if cause_reason == Reason.RESUME else 'irrelevant'
    cause_mock.reason = cause_reason

    with freezegun.freeze_time(now):
        await process_resource_event(
            lifecycle=kopf.lifecycles.all_at_once,
            registry=registry,
            settings=settings,
            resource=resource,
            indexers=OperatorIndexers(),
            memories=ResourceMemories(),
            memobase=Memo(),
            raw_event={
                'type': event_type,
                'object': {}
            },
            event_queue=asyncio.Queue(),
        )

    assert handlers.create_mock.call_count == (1 if cause_reason
                                               == Reason.CREATE else 0)
    assert handlers.update_mock.call_count == (1 if cause_reason
                                               == Reason.UPDATE else 0)
    assert handlers.delete_mock.call_count == (1 if cause_reason
                                               == Reason.DELETE else 0)
    assert handlers.resume_mock.call_count == (1 if cause_reason
                                               == Reason.RESUME else 0)

    assert not k8s_mocked.sleep_or_wait.called
    assert k8s_mocked.patch_obj.called

    fname = f'{cause_reason}_fn'
    patch = k8s_mocked.patch_obj.call_args_list[0][1]['patch']
    assert patch['status']['kopf']['progress'][fname]['delayed'] == delayed_iso

    assert_logs([
        "Handler .+ is invoked",
        "Handler .+ failed temporarily: oops",
    ])