-
-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #125 from nolar/background-events
Post k8s-events in the background
- Loading branch information
Showing
16 changed files
with
265 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
""" | ||
All the functions to write the Kubernetes events for the Kubernetes objects. | ||
They are used internally in the handling routines to show the progress, | ||
and can be used directly from the handlers to add arbitrary custom events. | ||
The actual k8s-event posting runs in the background, | ||
and posts the k8s-events as soon as they are queued. | ||
""" | ||
import asyncio | ||
import sys | ||
from contextvars import ContextVar | ||
from typing import Mapping, Text, NamedTuple | ||
|
||
from kopf import config | ||
from kopf.clients import events | ||
from kopf.structs import dicts | ||
from kopf.structs import hierarchies | ||
|
||
event_queue_var: ContextVar[asyncio.Queue] = ContextVar('event_queue_var') | ||
|
||
|
||
class K8sEvent(NamedTuple): | ||
""" | ||
A single k8s-event to be posted, with all ref-information preserved. | ||
It can exist and be posted even after the object is garbage-collected. | ||
""" | ||
ref: Mapping | ||
type: Text | ||
reason: Text | ||
message: Text | ||
|
||
|
||
def event(objs, *, type, reason, message=''): | ||
queue = event_queue_var.get() | ||
for obj in dicts.walk(objs): | ||
ref = hierarchies.build_object_reference(obj) | ||
event = K8sEvent(ref=ref, type=type, reason=reason, message=message) | ||
queue.put_nowait(event) | ||
|
||
|
||
def info(obj, *, reason, message=''): | ||
if config.EventsConfig.events_loglevel > config.LOGLEVEL_INFO: | ||
return | ||
event(obj, type='Normal', reason=reason, message=message) | ||
|
||
|
||
def warn(obj, *, reason, message=''): | ||
if config.EventsConfig.events_loglevel > config.LOGLEVEL_WARNING: | ||
return | ||
event(obj, type='Warning', reason=reason, message=message) | ||
|
||
|
||
def exception(obj, *, reason='', message='', exc=None): | ||
if config.EventsConfig.events_loglevel > config.LOGLEVEL_ERROR: | ||
return | ||
if exc is None: | ||
_, exc, _ = sys.exc_info() | ||
reason = reason if reason else type(exc).__name__ | ||
message = f'{message} {exc}' if message and exc else f'{exc}' if exc else f'{message}' | ||
event(obj, type='Error', reason=reason, message=message) | ||
|
||
|
||
async def poster( | ||
event_queue: asyncio.Queue, | ||
): | ||
""" | ||
Post events in the background as they are queued. | ||
When the events come from the logging system, they have | ||
their reason, type, and other fields adjusted to meet Kubernetes's concepts. | ||
When the events are explicitly defined via `kopf.event` and similar calls, | ||
they have these special fields defined already. | ||
In either case, we pass the queued events directly to the K8s client | ||
(or a client wrapper/adapter), with no extra processing. | ||
This task is defined in this module only because all other tasks are here, | ||
so we keep all forever-running tasks together. | ||
""" | ||
while True: | ||
posted_event = await event_queue.get() | ||
await events.post_event( | ||
ref=posted_event.ref, | ||
type=posted_event.type, | ||
reason=posted_event.reason, | ||
message=posted_event.message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,20 @@ | ||
""" | ||
All the functions to write the Kubernetes events on the Kubernetes objects. | ||
They are used internally in the handling routine to show the progress, | ||
and can be used directly from the handlers to add arbitrary custom events. | ||
The events look like this: | ||
kubectl describe -f myres.yaml | ||
... | ||
TODO | ||
**THIS MODULE IS DEPRECATED AND WILL BE REMOVED.** | ||
""" | ||
import asyncio | ||
import sys | ||
|
||
from kopf import config | ||
from kopf.clients import events | ||
|
||
|
||
# TODO: rename it it kopf.log()? kopf.events.log()? kopf.events.warn()? | ||
async def event_async(obj, *, type, reason, message=''): | ||
""" | ||
Issue an event for the object. | ||
""" | ||
if isinstance(obj, (list, tuple)): | ||
for item in obj: | ||
await events.post_event(obj=item, type=type, reason=reason, message=message) | ||
else: | ||
await events.post_event(obj=obj, type=type, reason=reason, message=message) | ||
|
||
|
||
# Shortcuts for the only two officially documented event types as of now. | ||
# However, any arbitrary strings can be used as an event type to the base function. | ||
async def info_async(obj, *, reason, message=''): | ||
if config.EventsConfig.events_loglevel > config.LOGLEVEL_INFO: | ||
return | ||
await event_async(obj, reason=reason, message=message, type='Normal') | ||
|
||
|
||
async def warn_async(obj, *, reason, message=''): | ||
if config.EventsConfig.events_loglevel > config.LOGLEVEL_WARNING: | ||
return | ||
await event_async(obj, reason=reason, message=message, type='Warning') | ||
|
||
|
||
async def exception_async(obj, *, reason='', message='', exc=None): | ||
if config.EventsConfig.events_loglevel > config.LOGLEVEL_ERROR: | ||
return | ||
|
||
if exc is None: | ||
_, exc, _ = sys.exc_info() | ||
reason = reason if reason else type(exc).__name__ | ||
message = f'{message} {exc}' if message else f'{exc}' | ||
await event_async(obj, reason=reason, message=message, type='Error') | ||
|
||
|
||
# Next 4 funcs are just synchronous interface for async event functions. | ||
def event(obj, *, type, reason, message=''): | ||
asyncio.wait_for(event_async(obj, type=type, reason=reason, message=message), timeout=None) | ||
|
||
|
||
def info(obj, *, reason, message=''): | ||
asyncio.wait_for(info_async(obj, reason=reason, message=message), timeout=None) | ||
import warnings | ||
|
||
from kopf.engines.posting import ( | ||
event, | ||
info, | ||
warn, | ||
exception, | ||
) | ||
|
||
def warn(obj, *, reason, message=''): | ||
asyncio.wait_for(warn_async(obj, reason=reason, message=message), timeout=None) | ||
__all__ = ['event', 'info', 'warn', 'exception'] | ||
|
||
|
||
def exception(obj, *, reason='', message='', exc=None): | ||
asyncio.wait_for(exception_async(obj, reason=reason, message=message, exc=exc), timeout=None) | ||
# Triggered on explicit `import kopf.events` (not imported this way normally). | ||
warnings.warn( | ||
"`kopf.events` is deprecated; " | ||
"use `kopf` directly: e.g. `kopf.event(...)`.", | ||
DeprecationWarning, stacklevel=0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.