gokuberneteskubernetes-operator

Invoke kubernetes operator reconcile loop on external resources changes


I'm working on developing a k8s custom resource that as part of the business logic needs to reconcile its state when an external Job in the cluster have changed its own state.

Those Jobs aren't created by the custom resource itself but are externally created for a third party service, however I need to reconcile the state of the CRO for example when any of those external jobs have finished.

After reading bunch of documentation, I came up with setting a watcher for the controller, to watch Jobs like the following example

func (r *DatasetReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&datasetv1beta1.Dataset{}).
        Watches(&source.Kind{Type: &batchv1.Job{}}, &handler.EnqueueRequestForObject{} /* filter by predicates, see https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.6/pkg/controller#Controller */).
        Complete(r)
}

No I'm having my reconcile loop triggered for Jobs and my CRs with the corresponding name and namespace but I don't know anything about the object kind.

func (r *DatasetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    l := log.FromContext(ctx)
    l.Info("Enter Reconcile loop")
    l.Info("Request", "Req", req)

    //if this is triggered by my CR
    dataset := &datasetv1beta1.Dataset{}
    r.Get(ctx, types.NamespacedName{Name: req.Name, Namespace: req.Namespace}, dataset)
    //whereas when triggered by a Job
    job := &batchv1.Job{}
    r.Get(ctx, types.NamespacedName{Name: req.Name, Namespace: req.Namespace}, job)


    return ctrl.Result{}, nil
}

How can I check within Reconcile the object kind? so I can retrieve the full object data calling r.Get


Solution

  • By design, the event that triggered reconciliation is not passed to the reconciler so that you are forced to define and act on a state instead. This approach is referred to as level-based, as opposed to edge-based.

    In your example you have two resources you are trying to keep track of. I would suggest either:

    1. Using ownerReferences or labels if these resources are related. That way you can get all related Datasets for a given Job (or vice versa) and reconcile things that way.
    2. If the two resources are not related, create a separate controller for each resource.

    If you want to prevent reconciliation on certain events you can make use of predicates. From the event in the predicate function you can get the object type by e.Object.(*core.Pod) for example.