Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Operator-level memos, optionally passed into embedded operators #667

Merged
merged 5 commits into from
Feb 5, 2021

Conversation

nolar
Copy link
Owner

@nolar nolar commented Feb 5, 2021

Extend the existing memo kwarg to operators' handlers and embedded operators' options, and connect global memos to per-resource memos.

Now, an external memo can be passed into an embedded operator via memo= option, and all the keys will be seen both in operator-level and resource-level handlers. An object of any arbitrary class can be passed as the operator's memo, as long as all handlers understand it and use it accordingly, and it is capable of shallow-copying.

import asyncio
import dataclasses
import kopf

@dataclasses.dataclass()
class CustomContext:
    create_tpl: str
    delete_tpl: str

@kopf.on.startup()
def startup(memo: CustomContext, **_):
    memo.some_field = 0

@kopf.on.create('kopfexamples')
def create_fn(memo: CustomContext, **kwargs):
    print(memo.create_tpl.format(**kwargs))

@kopf.on.delete('kopfexamples')
def delete_fn(memo: CustomContext, **kwargs):
    print(memo.delete_tpl.format(**kwargs))

if __name__ == '__main__':
    kopf.configure(verbose=True)
    asyncio.run(kopf.operator(
        memo=CustomContext(
            create_tpl="Hello, {name}!",
            delete_tpl="Good bye, {name}!",
        ),
    ))

Alternatively or in addition to that, a default memo dictionary (kopf.Memo) can be populated in the operator-level startup handlers, and all the keys will also be seen in all the resource-level handlers.

With Python's mutable structures as values (lists, dicts, or new memos), all handlers can exchange information across all resources of all resource kinds if needed — i.e. globally (an equivalent of global variables).

import kopf
import queue
import threading

@kopf.on.startup()
def start_background_worker(memo: kopf.Memo, **_):
    memo.my_queue = queue.Queue()
    memo.my_thread = threading.Thread(target=background, args=(memo.my_queue,))
    memo.my_thread.start()

@kopf.on.cleanup()
def stop_background_worker(memo: kopf.Memo, **_):
    memo['my_queue'].put(None)
    memo['my_thread'].join()

def background(queue: queue.Queue):
    while True:
        item = queue.get()
        if item is None:
            break
        else:
            print(item)

@kopf.on.event('KopfExample')
def pinged(memo: kopf.Memo, namespace: str, name: str, **_):
    if not memo.get('is_seen'):
        memo.my_queue.put(f"{namespace}/{name}")
        memo.is_seen = True

closes #347, closes #367

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[question] How to provide custom configuration for an operator ? Pass a context to every kopf call/handler
1 participant