gokuberneteskubernetes-custom-resourcescontroller-runtime

controller runtime: GrandPa, Son, GrandSon and setting OwnerReferences


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?


Solution

  • 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)