def test_events_for_deletion_via_event_type(registry): e1 = RawEvent(type=None, object=RawBody(metadata={'name': 'ns1'})) e2 = RawEvent(type='DELETED', object=RawBody(metadata={'name': 'ns1'})) insights = Insights() revise_namespaces(insights=insights, namespaces=['ns*'], raw_events=[e1]) revise_namespaces(insights=insights, namespaces=['ns*'], raw_events=[e2]) assert not insights.namespaces
def test_bodies_for_additional_population(registry): b1 = RawBody(metadata={'name': 'ns1'}) b2 = RawBody(metadata={'name': 'ns2'}) insights = Insights() revise_namespaces(insights=insights, namespaces=['ns*'], raw_bodies=[b1]) revise_namespaces(insights=insights, namespaces=['ns*'], raw_bodies=[b2]) assert insights.namespaces == {'ns1', 'ns2'}
def test_bodies_for_deletion_via_timestamp(registry): b1 = RawBody(metadata={'name': 'ns1'}) b2 = RawBody(metadata={'name': 'ns1', 'deletionTimestamp': '...'}) insights = Insights() revise_namespaces(insights=insights, namespaces=['ns*'], raw_bodies=[b1]) revise_namespaces(insights=insights, namespaces=['ns*'], raw_bodies=[b2]) assert not insights.namespaces
def test_events_for_additional_population(registry): e1 = RawEvent(type=None, object=RawBody(metadata={'name': 'ns1'})) e2 = RawEvent(type=None, object=RawBody(metadata={'name': 'ns2'})) insights = Insights() revise_namespaces(insights=insights, namespaces=['ns*'], raw_events=[e1]) revise_namespaces(insights=insights, namespaces=['ns*'], raw_events=[e2]) assert insights.namespaces == {'ns1', 'ns2'}
async def test_nonwatchable_resources_are_ignored(settings, registry, apis_mock, group1_mock, timer, etype, decorator, insights): @decorator('group1', 'version1', 'plural1') def fn(**_): ... e1 = RawEvent(type=etype, object=RawBody(spec={'group': 'group1'})) async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_resource_event(insights=insights, raw_event=e1, registry=registry, settings=settings) task = asyncio.create_task(delayed_injection(0.1)) with timer: async with insights.revised: await insights.revised.wait() await task assert 0.1 < timer.seconds < 1.0 assert not insights.watched_resources assert apis_mock.called assert group1_mock.called
async def test_followups_for_deletion_of_resource(settings, registry, apis_mock, group1_empty_mock, timer, etype, insights, insights_resources): e1 = RawEvent(type=etype, object=RawBody(spec={'group': 'group1'})) r1 = Resource(group='group1', version='version1', plural='plural1') insights_resources.add(r1) async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_resource_event(insights=insights, raw_event=e1, registry=registry, settings=settings) task = asyncio.create_task(delayed_injection(0.1)) with timer: async with insights.revised: await insights.revised.wait() await task assert 0.1 < timer.seconds < 1.0 assert not insights_resources assert apis_mock.called assert group1_empty_mock.called
def _build_key( self, raw_body: bodies.RawBody, ) -> str: """ Construct an immutable persistent key of a resource. Generally, a uid is sufficient, as it is unique within the cluster. But it can be e.g. plural/namespace/name triplet, or anything else, even of different types (as long as it satisfies the type checkers). But it must be consistent within a single process lifetime. """ return raw_body.get('metadata', {}).get('uid') or ''
async def test_initial_listing_is_ignored(): insights = Insights() e1 = RawEvent(type=None, object=RawBody(metadata={'name': 'ns1'})) async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_namespace_event(insights=insights, raw_event=e1, namespaces=['ns*']) task = asyncio.create_task(delayed_injection(0)) with pytest.raises(asyncio.TimeoutError): async with insights.revised: await asyncio.wait_for(insights.revised.wait(), timeout=0.1) await task assert not insights.namespaces
async def test_followups_for_addition(timer, etype): insights = Insights() e1 = RawEvent(type=etype, object=RawBody(metadata={'name': 'ns1'})) async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_namespace_event(insights=insights, raw_event=e1, namespaces=['ns*']) task = asyncio.create_task(delayed_injection(0.1)) async with timer, async_timeout.timeout(1): async with insights.revised: await insights.revised.wait() await task assert 0.1 < timer.seconds < 0.11 assert insights.namespaces == {'ns1'}
async def test_backbone_is_filled(registry, core_mock, corev1_mock, timer, etype): e1 = RawEvent(type=etype, object=RawBody(spec={'group': ''})) insights = Insights() async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_resource_event(insights=insights, raw_event=e1, registry=registry) task = asyncio.create_task(delayed_injection(0.1)) async with timer, async_timeout.timeout(1.0): await insights.backbone.wait_for(NAMESPACES) await task assert 0.1 < timer.seconds < 1.0 assert NAMESPACES in insights.backbone assert core_mock.called assert corev1_mock.called
async def test_initial_listing_is_ignored(registry, apis_mock, group1_mock): insights = Insights() e1 = RawEvent(type=None, object=RawBody(spec={'group': 'group1'})) async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_resource_event(insights=insights, raw_event=e1, registry=registry) task = asyncio.create_task(delayed_injection(0)) with pytest.raises(asyncio.TimeoutError): async with async_timeout.timeout(0.1) as timeout: async with insights.revised: await insights.revised.wait() await task assert timeout.expired assert not insights.resources assert not apis_mock.called assert not group1_mock.called
async def test_followups_for_addition(registry, apis_mock, group1_mock, timer, etype): e1 = RawEvent(type=etype, object=RawBody(spec={'group': 'group1'})) r1 = Resource(group='group1', version='version1', plural='plural1') insights = Insights() async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_resource_event(insights=insights, raw_event=e1, registry=registry) task = asyncio.create_task(delayed_injection(0.1)) async with timer, async_timeout.timeout(1.0): async with insights.revised: await insights.revised.wait() await task assert 0.1 < timer.seconds < 1.0 assert insights.resources == {r1} assert apis_mock.called assert group1_mock.called
async def test_initial_listing_is_ignored(settings, registry, apis_mock, group1_mock, insights): e1 = RawEvent(type=None, object=RawBody(spec={'group': 'group1'})) async def delayed_injection(delay: float): await asyncio.sleep(delay) await process_discovered_resource_event(insights=insights, raw_event=e1, registry=registry, settings=settings) task = asyncio.create_task(delayed_injection(0)) with pytest.raises(asyncio.TimeoutError): async with insights.revised: await asyncio.wait_for(insights.revised.wait(), timeout=0.1) await task assert not insights.indexed_resources assert not insights.watched_resources assert not insights.webhook_resources assert not apis_mock.called assert not group1_mock.called
from kopf._core.actions.execution import cause_var from kopf._core.actions.invocation import context from kopf._core.engines.indexing import OperatorIndexers from kopf._core.intents.causes import ChangingCause, Reason, WatchingCause OWNER_API_VERSION = 'owner-api-version' OWNER_NAMESPACE = 'owner-namespace' OWNER_KIND = 'OwnerKind' OWNER_NAME = 'owner-name' OWNER_UID = 'owner-uid' OWNER_LABELS = {'label-1': 'value-1', 'label-2': 'value-2'} OWNER = RawBody( apiVersion=OWNER_API_VERSION, kind=OWNER_KIND, metadata=RawMeta( namespace=OWNER_NAMESPACE, name=OWNER_NAME, uid=OWNER_UID, labels=OWNER_LABELS, ), ) @pytest.fixture(params=['state-changing-cause', 'event-watching-cause']) def owner(request, resource): body = Body(copy.deepcopy(OWNER)) if request.param == 'state-changing-cause': cause = ChangingCause( logger=logging.getLogger('kopf.test.fake.logger'), indices=OperatorIndexers().indices, resource=resource, patch=Patch(),
def test_bodies_ignored_for_mismatching(registry): b1 = RawBody(metadata={'name': 'def1'}) insights = Insights() revise_namespaces(insights=insights, namespaces=['ns*'], raw_bodies=[b1]) assert not insights.namespaces
import asyncio import aiohttp.web import pytest import kopf from kopf._cogs.structs.bodies import RawBody, RawEvent from kopf._cogs.structs.references import NAMESPACES, Insights, Resource from kopf._core.reactor.observation import process_discovered_resource_event # Implementation awareness: the events only trigger the re-scan, so the fields can be reduced # to only the group name which is being rescanned. Other fields are ignored in the events. # The actual data is taken from the API. It is tested elsewhere, so we rely on its correctness here. GROUP1_EVENT = RawBody(spec={'group': 'group1'}) @pytest.fixture() def core_mock(resp_mocker, aresponses, hostname): mock = resp_mocker( return_value=aiohttp.web.json_response({'versions': ['v1']})) aresponses.add(hostname, '/api', 'get', mock) return mock @pytest.fixture() def apis_mock(resp_mocker, aresponses, hostname): mock = resp_mocker(return_value=aiohttp.web.json_response({ 'groups': [ { 'name': 'group1', 'preferredVersion': {
def test_events_ignored_for_mismatching(registry): e1 = RawEvent(type=None, object=RawBody(metadata={'name': 'def1'})) insights = Insights() revise_namespaces(insights=insights, namespaces=['ns*'], raw_events=[e1]) assert not insights.namespaces