Resource specification
The following notations are supported to specify the resources to be handled.
As a rule of thumb, they are designed so that the intentions of a developer
are guessed the best way possible, and similar to kubectl
semantics.
The resource name is always expected in the first place as the rightmost value. The remaining parts are considered as an API group and an API version of the resource – given as either two separate strings, or as one, but separated with a slash:
@kopf.on.event('kopf.dev', 'v1', 'kopfexamples')
@kopf.on.event('kopf.dev/v1', 'kopfexamples')
@kopf.on.event('apps', 'v1', 'deployments')
@kopf.on.event('apps/v1', 'deployments')
@kopf.on.event('', 'v1', 'pods')
def fn(**_):
pass
If only one API specification is given (except for v1
), it is treated
as an API group, and the preferred API version of that API group is used:
@kopf.on.event('kopf.dev', 'kopfexamples')
@kopf.on.event('apps', 'deployments')
def fn(**_):
pass
It is also possible to specify the resources with kubectl
’s semantics:
@kopf.on.event('kopfexamples.kopf.dev')
@kopf.on.event('deployments.apps')
def fn(**_):
pass
One exceptional case is v1
as the API specification: it corresponds
to K8s’s legacy core API (before API groups appeared), and is equivalent
to an empty API group name. The following specifications are equivalent:
@kopf.on.event('v1', 'pods')
@kopf.on.event('', 'v1', 'pods')
def fn(**_):
pass
If neither the API group nor the API version is specified, all resources with that name would match regardless of the API groups/versions. However, it is reasonable to expect only one:
@kopf.on.event('kopfexamples')
@kopf.on.event('deployments')
@kopf.on.event('pods')
def fn(**_):
pass
In all examples above, where the resource identifier is expected, it can be any name: plural, singular, kind, or a short name. As it is impossible to guess which one is which, the name is remembered as is, and is later checked for all possible names of the specific resources once those are discovered:
@kopf.on.event('kopfexamples')
@kopf.on.event('kopfexample')
@kopf.on.event('KopfExample')
@kopf.on.event('kex')
@kopf.on.event('StatefulSet')
@kopf.on.event('deployments')
@kopf.on.event('pod')
def fn(**_):
pass
The resource specification can be more specific on which name to match:
@kopf.on.event(kind='KopfExample')
@kopf.on.event(plural='kopfexamples')
@kopf.on.event(singular='kopfexample')
@kopf.on.event(shortcut='kex')
def fn(**_):
pass
The whole categories of resources can be served, but they must be explicitly specified to avoid unintended consequences:
@kopf.on.event(category='all')
def fn(**_):
pass
Note that the conventional category all
does not really mean all resources,
but only those explicitly added to this category; some built-in resources
are excluded (e.g. ingresses, secrets).
To handle all resources in an API group/version, use a special marker instead of the mandatory resource name:
@kopf.on.event('kopf.dev', 'v1', kopf.EVERYTHING)
@kopf.on.event('kopf.dev/v1', kopf.EVERYTHING)
@kopf.on.event('kopf.dev', kopf.EVERYTHING)
def fn(**_):
pass
As a consequence of the above, to handle every resource in the cluster – which might be not the best idea per se, but is technically possible – omit the API group/version, and use the marker only:
@kopf.on.event(kopf.EVERYTHING)
def fn(**_):
pass
Serving everything is better when it is used with filters:
@kopf.on.event(kopf.EVERYTHING, labels={'only-this': kopf.PRESENT})
def fn(**_):
pass
Note
Core v1 events are excluded from EVERYTHING
: they are created during
handling of other resources in the implicit Events from log messages,
so they would cause unnecessary handling cycles for every essential change.
To handle core v1 events, they must be named explicitly, e.g. like this:
@kopf.on.event('v1', 'events')
def fn(**_):
pass
The resource specifications do not support multiple values, masks or globs. To handle multiple independent resources, add multiple decorators to the same handler function – as shown above. The handlers are deduplicated by the underlying function and its handler id (which, in turn, equals to the function’s name by default unless overridden), so one function will never be triggered multiple times for the same resource if there are some accidental overlaps in the specifications.
Warning
Kopf tries to make it easy to specify resources a la kubectl
.
However, some things cannot be made that easy. If resources are specified
ambiguously, i.e. if 2+ resources of different API groups match the same
resource specification, neither of them will be served, and a warning
will be issued.
This only applies to resource specifications where it is intended to have a specific resource by its name; specifications with intentional multi-resource mode are served as usually (e.g. by categories).
However, v1
resources have priority over all other resources. This
resolves the conflict of pods.v1
vs. pods.v1beta1.metrics.k8s.io
,
so just "pods"
can be specified and the intention will be understood.
This mimics the behaviour of kubectl
, where such API priorities
are hard-coded.
While it might be convenient to write short forms of resource names, the proper way is to always add at least an API group:
import kopf
@kopf.on.event('pods') # NOT SO GOOD, ambiguous, though works
@kopf.on.event('pods.v1') # GOOD, specific
@kopf.on.event('v1', 'pods') # GOOD, specific
@kopf.on.event('pods.metrics.k8s.io') # GOOD, specific
@kopf.on.event('metrics.k8s.io', 'pods') # GOOD, specific
def fn(**_):
pass
Keep the short forms only for prototyping and experimentation mode, and for ad-hoc operators with custom resources (not reusable and running in controlled clusters where no other similar resources can be defined).
Warning
Some API groups are served by API extensions: e.g. metrics.k8s.io
.
If the extension’s deployment/service/pods are down, such a group will
not be scannable (failing with “HTTP 503 Service Unavailable”)
and will block scanning the whole cluster if resources are specified
with no group name (e.g. ('pods')
instead of ('v1', 'pods')
).
To avoid scanning the whole cluster and all (even unused) API groups, it is recommended to specify at least the group names for all resources, especially in reusable and publicly distributed operators.