I write a Kubernetes controller with controller-runtime.
Imagine there are three CRDs: GrandPa
, Son
, and GrandSon
. Each has its own controller in a separate deployment.
I am writing the GrandPa
controller. When the user creates a GrandPa
object, my controller creates a Son
object and uses SetControllerReference(grandpa, son)
.
The Son
controller detects the new Son
object, creates a GrandSon
object, and uses SetControllerReference(son, grandson)
.
My GrandPa
controller needs its Reconcile()
function to be triggered when a GrandSon
object changes.
Afaik .Owns()
does not work for GrandPa
watching GrandSon
because the ownership chain is indirect (GrandPa -> Son -> GrandSon
).
I can only change the source code of GrandPa controller.
How to get Reconcile() of GrandPa get triggered when GrandSon changes?
Your question is pretty generic, however from what I can understand, you should use Watches()
to make GrandPa
reconcile when a GrandSon
changes. Since GrandPa
isn’t a direct owner, manually map GrandSon
to GrandPa
through Son
, like so:
err = ctrl.NewControllerManagedBy(mgr).
For(&GrandPa{}).
Owns(&Son{}).
Watches(
&source.Kind{Type: &GrandSon{}},
handler.EnqueueRequestsFromMapFunc(func(grandson client.Object) []reconcile.Request {
var sonRef *metav1.OwnerReference
for _, owner := range grandson.GetOwnerReferences() {
if owner.Kind == "Son" {
sonRef = &owner
break
}
}
if sonRef == nil {
return nil
}
son := &Son{}
if err := mgr.GetClient().Get(context.TODO(), types.NamespacedName{
Name: sonRef.Name,
Namespace: grandson.GetNamespace(),
}, son); err != nil {
return nil
}
var grandpaRef *metav1.OwnerReference
for _, owner := range son.GetOwnerReferences() {
if owner.Kind == "GrandPa" {
grandpaRef = &owner
break
}
}
if grandpaRef == nil {
return nil
}
return []reconcile.Request{{
NamespacedName: types.NamespacedName{
Name: grandpaRef.Name,
Namespace: son.GetNamespace(),
},
}}
}),
).Complete(r)