GithubHelp home page GithubHelp logo

Comments (2)

nolar avatar nolar commented on June 18, 2024

I am not sure that I got the idea right. So the answer may be wrong or irrelevant. But this is how I would approach it:

Since there are pods involved, there is a need for pod-handler. Since not all pods should be involved, we have to filter them. Since the criteria of filtering are quite sophisticated, I would use callbacks:

import kopf

def does_it_match(**_) -> bool:
    return True

@kopf.on.event('', 'v1', 'pods', when=does_it_match)
def pod_event(**_):
    pass

So, at this moment, all pods in the cluster/namespace will be intercepted. Now, we need to narrow the criteria. Since there is a selector in a CR, I would keep that global state of all selectors in memory, mapping to the original CRs they came from:

from typing import MutableMapping, Mapping

import kopf

SelectorKey = Tuple[str, str]  # (namespace, name)
SelectorLabels = Mapping[str, str]

SELECTORS: MutableMapping[SelectorKey, SelectorLabels] = {}


@kopf.on.create('zalando.org', 'v1', 'kopfexamples')
@kopf.on.resume('zalando.org', 'v1', 'kopfexamples')
@kopf.on.update('zalando.org', 'v1', 'kopfexamples')  # optionally
def cr_appears(namespace, name, spec, **_):
    key = (namespace, name)
    SELECTORS[key] = spec.get('selector', {})
  

@kopf.on.delete('zalando.org', 'v1', 'kopfexamples')
def cr_disappears(spec, **_):
    key = (namespace, name)
    try:
        del SELECTORS[key]
    except KeyError:
        pass

So, at this point, we would have data for filtering the pods. Now, I would actually filter in that function above:

def does_it_match(labels: Mapping[str, str], **_) -> bool:
    for (namespace, name), selector_labels in SELECTORS.items():
        if all(labels.get(key) == val for key, val in selector_labels.items()):
            return True
    return False

Now, the pods that do not match any known selector, will be silently ignored. Notice: they will get into the sight of the operator itself — in one and only one watch-stream — but will be filtered out in the earliest stages, with no logs produced (silently).


This is a difference here from your suggested approach: instead of having N watch-stream with labels in the URL (where N is the number of CRs with selectors), there will be one and only one watch-stream (and therefore TCP/HTTP/API connection), seeing all the pods, and just picking those of our interest, and ignoring others.

This will easy the API side, but will put some CPU load to the operator. The RAM footprint will be minimal, though not zero: every pod will spawn its own worker task (asyncio.Task), where the pod events will be routed to, and almost instantly ignored; but the tasks are objects too — on a cluster with thousands of pods this can be noticed.


As a continuation, using the same for + if, I would be able to detect to which CRs each individual pod corresponds (one or or even a few of them) — in the handler itself. And do something with that pod as the contextual object (in kwargs) and the detected CRs. Perhaps, the CRs' spec should be also preserved somewhere in the global state, so that we would know what to do specifically — after the matching CRs are identified by their selectors.


The downside here is that you have to keep some state in memory — for all the CRs, or all the pods, or all of something, depending on which of them you expect to be the least memory consuming.

I am not yet sure if it is possible to solve the cross-resource communication in any other way: when an event happens on a pod, no events happen on the CRs, so we have nothing to "join". You either scan your own in-memory state, or K8s's in-memory state via the K8s API on every pod event (costly!). But the up-to-date state must be somewhere there.


PS: The typing thing is fully optional, and is ignored at runtime. I just got a habit of using it for clarity.

from kopf.

nemethf avatar nemethf commented on June 18, 2024

I still think the dynamic handler registration is a bit more convenient, but you are right that it is less scalable than the approach you outlined.

However, with your approach, it might happen that a pod is created (and generates no events afterwards) before the corresponding CR appears in the system or vice versa, so I think the operator should store all the relevant info for both CRs and pods. This is doable but gets complicated when the CRD contains a namespace field in addition to the selector field.

At any rate, I'm going to ignore the namespace field in my operator and go with your idea. Thank you for enlightening me.

from kopf.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.