예제 #1
0
async def test_liveness_with_reporting(liveness_url, liveness_registry):
    def fn1(**kwargs):
        return {'x': 100}

    def fn2(**kwargs):
        return {'y': '200'}

    liveness_registry.activity_handlers.append(
        ActivityHandler(
            fn=fn1,
            id='id1',
            activity=Activity.PROBE,
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
            cooldown=None,
        ))
    liveness_registry.activity_handlers.append(
        ActivityHandler(
            fn=fn2,
            id='id2',
            activity=Activity.PROBE,
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
            cooldown=None,
        ))

    async with aiohttp.ClientSession() as session:
        async with session.get(liveness_url) as response:
            data = await response.json()
            assert isinstance(data, dict)
            assert data == {'id1': {'x': 100}, 'id2': {'y': '200'}}
예제 #2
0
async def test_results_are_returned_on_success(settings, activity):

    def sample_fn1(**_):
        return 123

    def sample_fn2(**_):
        return 456

    registry = OperatorRegistry()
    registry._activities.append(ActivityHandler(
        fn=sample_fn1, id='id1', activity=activity,
        errors=None, timeout=None, retries=None, backoff=None,
    ))
    registry._activities.append(ActivityHandler(
        fn=sample_fn2, id='id2', activity=activity,
        errors=None, timeout=None, retries=None, backoff=None,
    ))

    results = await run_activity(
        registry=registry,
        settings=settings,
        activity=activity,
        lifecycle=all_at_once,
    )

    assert set(results.keys()) == {'id1', 'id2'}
    assert results['id1'] == 123
    assert results['id2'] == 456
예제 #3
0
async def test_errors_are_raised_aggregated(settings, activity):
    def sample_fn1(**_):
        raise PermanentError("boo!123")

    def sample_fn2(**_):
        raise PermanentError("boo!456")

    registry = OperatorRegistry()
    registry._activities.append(
        ActivityHandler(
            fn=sample_fn1,
            id='id1',
            activity=activity,
            param=None,
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
        ))
    registry._activities.append(
        ActivityHandler(
            fn=sample_fn2,
            id='id2',
            activity=activity,
            param=None,
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
        ))

    with pytest.raises(ActivityError) as e:
        await run_activity(
            registry=registry,
            settings=settings,
            activity=activity,
            lifecycle=all_at_once,
            indices=OperatorIndexers().indices,
            memo=Memo(),
        )

    assert set(e.value.outcomes.keys()) == {'id1', 'id2'}
    assert e.value.outcomes['id1'].final
    assert e.value.outcomes['id1'].delay is None
    assert e.value.outcomes['id1'].result is None
    assert e.value.outcomes['id1'].exception is not None
    assert e.value.outcomes['id2'].final
    assert e.value.outcomes['id2'].delay is None
    assert e.value.outcomes['id2'].result is None
    assert e.value.outcomes['id2'].exception is not None
    assert str(e.value.outcomes['id1'].exception) == "boo!123"
    assert str(e.value.outcomes['id2'].exception) == "boo!456"
예제 #4
0
async def test_noreturn_handler_produces_no_credentials(settings):
    vault = Vault()
    registry = OperatorRegistry()

    def login_fn(**_):
        pass

    # NB: id auto-detection does not work, as it is local to the test function.
    registry._activities.append(
        ActivityHandler(
            fn=login_fn,
            id='login_fn',
            activity=Activity.AUTHENTICATION,
            param=None,
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
        ))

    await authenticate(
        registry=registry,
        settings=settings,
        vault=vault,
    )

    assert not vault
    with pytest.raises(LoginError):
        async for _, _ in vault:
            pass
예제 #5
0
async def test_delays_are_simulated(settings, activity, mocker):

    def sample_fn(**_):
        raise TemporaryError('to be retried', delay=123)

    registry = OperatorRegistry()
    registry._activities.append(ActivityHandler(
        fn=sample_fn, id='id', activity=activity,
        errors=None, timeout=None, retries=3, backoff=None,
    ))

    with freezegun.freeze_time() as frozen:

        async def sleep_or_wait_substitute(*_, **__):
            frozen.tick(123)

        sleep_or_wait = mocker.patch('kopf.reactor.effects.sleep_or_wait',
                                     wraps=sleep_or_wait_substitute)

        with pytest.raises(ActivityError) as e:
            await run_activity(
                registry=registry,
                settings=settings,
                activity=activity,
                lifecycle=all_at_once,
            )

    assert sleep_or_wait.call_count >= 3  # 3 retries, 1 sleep each
    assert sleep_or_wait.call_count <= 4  # 3 retries, 1 final success (delay=None), not more
    if sleep_or_wait.call_count > 3:
        sleep_or_wait.call_args_list[-1][0][0] is None
예제 #6
0
def test_activity_handler_with_all_args(mocker):
    fn = mocker.Mock()
    id = mocker.Mock()
    errors = mocker.Mock()
    timeout = mocker.Mock()
    retries = mocker.Mock()
    backoff = mocker.Mock()
    activity = mocker.Mock()
    handler = ActivityHandler(
        fn=fn,
        id=id,
        errors=errors,
        timeout=timeout,
        retries=retries,
        backoff=backoff,
        cooldown=None,  # deprecated, but still required
        activity=activity,
    )
    assert handler.fn is fn
    assert handler.id is id
    assert handler.errors is errors
    assert handler.timeout is timeout
    assert handler.retries is retries
    assert handler.backoff is backoff
    assert handler.activity is activity
예제 #7
0
async def test_retries_are_simulated(settings, activity, mocker):
    mock = mocker.MagicMock()

    def sample_fn(**_):
        mock()
        raise TemporaryError('to be retried', delay=0)

    registry = OperatorRegistry()
    registry._activities.append(
        ActivityHandler(
            fn=sample_fn,
            id='id',
            activity=activity,
            param=None,
            errors=None,
            timeout=None,
            retries=3,
            backoff=None,
        ))

    with pytest.raises(ActivityError) as e:
        await run_activity(
            registry=registry,
            settings=settings,
            activity=activity,
            lifecycle=all_at_once,
            indices=OperatorIndexers().indices,
            memo=Memo(),
        )

    assert isinstance(e.value.outcomes['id'].exception, PermanentError)
    assert mock.call_count == 3
예제 #8
0
async def test_errors_are_cascaded_from_one_of_the_originals(
        settings, activity):
    def sample_fn(**_):
        raise PermanentError("boo!")

    registry = OperatorRegistry()
    registry._activities.append(
        ActivityHandler(
            fn=sample_fn,
            id='id',
            activity=activity,
            param=None,
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
        ))

    with pytest.raises(ActivityError) as e:
        await run_activity(
            registry=registry,
            settings=settings,
            activity=activity,
            lifecycle=all_at_once,
            indices=OperatorIndexers().indices,
            memo=Memo(),
        )

    assert e.value.__cause__
    assert type(e.value.__cause__) is PermanentError
    assert str(e.value.__cause__) == "boo!"
예제 #9
0
async def test_single_credentials_provided_to_vault(settings):
    info = ConnectionInfo(server='https://expected/')
    vault = Vault()
    registry = OperatorRegistry()

    def login_fn(**_):
        return info

    # NB: id auto-detection does not work, as it is local to the test function.
    registry._activities.append(ActivityHandler(
        fn=login_fn, id='login_fn', activity=Activity.AUTHENTICATION,
        errors=None, timeout=None, retries=None, backoff=None,
    ))

    await authenticate(
        registry=registry,
        settings=settings,
        vault=vault,
    )

    assert vault

    items = []
    async for key, info in vault:
        items.append((key, info))

    assert len(items) == 1
    assert items[0][0] == 'login_fn'
    assert items[0][1] is info
예제 #10
0
async def test_liveness_data_is_cached(liveness_url, liveness_registry):
    counter = 0

    def fn1(**kwargs):
        nonlocal counter
        counter += 1
        return {'counter': counter}

    liveness_registry.activity_handlers.append(
        ActivityHandler(
            fn=fn1,
            id='id1',
            activity=Activity.PROBE,
            errors=None,
            timeout=None,
            retries=None,
            backoff=None,
            cooldown=None,
        ))

    async with aiohttp.ClientSession() as session:
        async with session.get(liveness_url) as response:
            data = await response.json()
            assert isinstance(data, dict)
            assert data == {'id1': {'counter': 1}}
        async with session.get(liveness_url) as response:
            data = await response.json()
            assert isinstance(data, dict)
            assert data == {'id1': {'counter': 1}}  # not 2!
예제 #11
0
def test_activity_handler_with_all_args(mocker):
    fn = mocker.Mock()
    id = mocker.Mock()
    param = mocker.Mock()
    errors = mocker.Mock()
    timeout = mocker.Mock()
    retries = mocker.Mock()
    backoff = mocker.Mock()
    activity = mocker.Mock()
    handler = ActivityHandler(
        fn=fn,
        id=id,
        param=param,
        errors=errors,
        timeout=timeout,
        retries=retries,
        backoff=backoff,
        activity=activity,
    )
    assert handler.fn is fn
    assert handler.id is id
    assert handler.param is param
    assert handler.errors is errors
    assert handler.timeout is timeout
    assert handler.retries is retries
    assert handler.backoff is backoff
    assert handler.activity is activity
예제 #12
0
def test_activity_handler_with_deprecated_cooldown_instead_of_backoff(mocker):
    fn = mocker.Mock()
    id = mocker.Mock()
    errors = mocker.Mock()
    timeout = mocker.Mock()
    retries = mocker.Mock()
    backoff = mocker.Mock()
    activity = mocker.Mock()

    with pytest.deprecated_call(match=r"use backoff="):
        handler = ActivityHandler(
            fn=fn,
            id=id,
            errors=errors,
            timeout=timeout,
            retries=retries,
            backoff=None,
            cooldown=backoff,  # deprecated, but still required
            activity=activity,
        )

    assert handler.fn is fn
    assert handler.id is id
    assert handler.errors is errors
    assert handler.timeout is timeout
    assert handler.retries is retries
    assert handler.backoff is backoff
    assert handler.activity is activity

    with pytest.deprecated_call(match=r"use handler.backoff"):
        assert handler.cooldown is backoff