Open junglie85 opened 3 years ago
Sorry for the late response (just noticed the question). I'd suggest using labels to mark the parent-children relationships:
You can use the patch
kwarg to directly decide what to store where:
@kopf.on.create('mycr')
def crt(patch, **_):
patch.status['readyReplicas'] = 99
@kopf.on.update('mycr')
def upd(patch, status, **_):
patch.status['readyReplicas'] = status.get('readyReplicas', 0) + 1
Cross-resource updates are not that easy. Kopf does not support that yet. You have to use a client library. E.g., with pykube-ng, it would be like this (I use KopfExample-aka-kex for the parent — to quickly test it):
import kopf
import yaml
import pykube
@kopf.on.create('kex')
def create_children(name, **_):
obj = yaml.safe_load("""
apiVersion: apps/v1
kind: StatefulSet
spec:
selector:
matchLabels:
app: my # has to match .spec.template.metadata.labels
serviceName: mysvc
replicas: 3 # by default is 1
template:
metadata:
labels:
app: my # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
""")
kopf.adopt(obj) # includes namespace, name, existing labels.
kopf.label(obj, {'mycr': name}, nested=['spec.template'])
api = pykube.HTTPClient(pykube.KubeConfig.from_env())
pykube.StatefulSet(api, obj).create()
@kopf.on.create('statefulset', labels={'mycr': kopf.PRESENT})
@kopf.on.update('statefulset', labels={'mycr': kopf.PRESENT}, field='status')
@kopf.on.resume('statefulset', labels={'mycr': kopf.PRESENT})
def child_of_mycr_seen(namespace, labels, status, **_):
parent_name = labels['mycr']
replicas = status.get('readyReplicas')
api = pykube.HTTPClient(pykube.KubeConfig.from_env())
obj = Kex.objects(api, namespace=namespace).get_by_name(parent_name)
obj.patch({'status': {'readyReplicas': replicas}}) # Beware of HTTP 404! Ignore?
@kopf.on.delete('statefulset', labels={'mycr': kopf.PRESENT})
def child_of_mycr_gone(namespace, labels, name, old, new, **_):
parent_name = labels['mycr']
api = pykube.HTTPClient(pykube.KubeConfig.from_env())
obj = Kex.objects(api, namespace=namespace).get_by_name(parent_name)
obj.patch({'status': {'readyReplicas': 0, 'status': 'deleted'}}) # Beware of HTTP 404! Ignore?
class Kex(pykube.objects.NamespacedAPIObject):
version = "kopf.dev/v1"
endpoint = "kopfexamples"
kind = 'KopfExample'
Note a few things:
field="status"
is important for on-update decorators. Otherwise, status is considered non-essential and the changes there are not noticed.pykube.ObjectNotFound
exceptions.mycr
is used to establish the parent-child relationship — both for the filters on which StatefulSets to handle, and for the parent name to patch on the children changes (assuming they are in the same namespace).metadata
in the YAML template: Kopf will automatically apply namespace, name, and existing labels on adoption of the resource.nested=
is optional — it can be used to later filter and handle individual pods of that CR/StatefulSet.I've tested it locally — it works; even when the StatefulSet is rescaled.
Thanks - I've not had time to check this out yet but appreciate you taking the time to help.
Question
I would like to display the number of replicas available in the printer columns of my CRD based on the number of available replicas in a StatefulSet that my operator creates. I can't figure out how to subscribe to events and update my status when they change:
I can set the number of replicas in the results delivery:
However, this means I have to put the
readyReplicas
underupdate
in my CRD, which prevents me from setting the number ofreadyReplicas
on creation.I'd like to easily be able to get events from the StatefulSet created by my operator and whenever one of its replicas changes I want to update the status of my CRD at both creation and update. I'm not quite sure how to do that with Kopf and would appreciate some guidance.
Checklist
Keywords
status