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'}
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_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_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 _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_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*'], replenished=asyncio.Event()) 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(): 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*'], replenished=asyncio.Event()) 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.namespaces
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
from unittest.mock import Mock, call import kopf from kopf.structs.bodies import Body, RawBody, RawMeta 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, ), ) 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
import asyncio import aiohttp.web import async_timeout import pytest import kopf from kopf.reactor.observation import process_discovered_resource_event from kopf.structs.bodies import RawBody, RawEvent from kopf.structs.references import NAMESPACES, Insights, Resource # 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',
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
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