Search code examples
azuregokuberneteskubebuilder

Watch event for specific object


We have an k8s operator (based on kubebuilder) which works as expected, now we need support for listening to secrets on the cluster.

The following code is working however I got event for all the secrets in the cluster which is not efficient,

I WANT to get the event only for specific secret, lets say secret with specific labels/annotation, how we can do it?

func (r *InvReconciler) SetupWithManager(mgr ctrl.Manager) error {
    manager := ctrl.NewControllerManagedBy(mgr).
        For(&corev1alpha1.Inv{}, builder.WithPredicates(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}))).
        WithOptions(controller.Options{
        })

    manager = manager.Watches(&source.Kind{Type: &v1.Secret{}}, handler.EnqueueRequestsFromMapFunc(func(a client.Object) []reconcile.Request {
        return r.secretHandler.GetSecret(a.GetNamespace(), a.GetName())
    }))

    return manager.Complete(r)
}

this is the function

func (secretReq secretHandler) GetSecret(namespace string, name string) []reconcile.Request {

    fmt.Println("secret is: ", namespace, "--", name)
    return nil
}

lets say secret like the following and only for this secret (with labels foo: bar )i'll get the event when it was created or modified

apiVersion: v1
kind: Secret
metadata:
  labels:
    foo: bar
  name: mysecret
  namespace: dev
type: Opaque
data:
  USER_NAME: YWRtaW4=
  PASSWORD: dGVzdBo=

Im not talking about an if statement after I got the event as it already bring all the secrets event in the cluster.


Solution

  • This is possible, in a roundabout way. You need to configure the selector into the controller Manager that you use to set up your reconciler.

    You can use label or field selectors for this. You can either set the same selector for all types of objects using DefaultSelector, or you can use SelectorsByObject to have different selectors for different types of objects.

    import (
        corev1 "k8s.io/api/core/v1"
        "k8s.io/apimachinery/pkg/labels"
        "k8s.io/apimachinery/pkg/selection"
        "k8s.io/client-go/rest"
        ctrl "sigs.k8s.io/controller-runtime"
        "sigs.k8s.io/controller-runtime/pkg/cache"
        "sigs.k8s.io/controller-runtime/pkg/manager"
    )
    
    func startReconciler(cnf *rest.Config) error {
        mgr, err := ctrl.NewManager(cnf, manager.Options{
            NewCache: func(conf *rest.Config, opts cache.Options) (cache.Cache, error) {
                // Use this selector for everything that is not mentioned in SelectorsByObject
                opts.DefaultSelector = cache.ObjectSelector{
                    Label: labels.SelectorFromSet(labels.Set{"foo": "bar"}),
                }
                // Specific selectors per type of object
                opts.SelectorsByObject[&corev1.Secret{}] = cache.ObjectSelector{
                    Label: labels.SelectorFromSet(labels.Set{"foo": "bar"}),
                }
                return cache.New(conf, opts)
            },
        })
        if err != nil {
            return err
        }
        r := &InvReconciler{}
        if err := r.SetupWithManager(mgr); err != nil {
            return err
        }