3

Suppose I have bellow code snippet which setups a reconciler that watches external resource "External":

// SetupWithManager sets up the controller with the Manager.
func (r *SomethingReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&api.Something{}).
        WithOptions(controller.Options{
            MaxConcurrentReconciles: stdruntime.NumCPU(),
            RecoverPanic:            true,
        }).
        Watches(
            &source.Kind{Type: &somev1.External{}},
            handler.EnqueueRequestsFromMapFunc(r.findInternalObjectsForExternal),
            builder.WithPredicates(predicate.Funcs{
                UpdateFunc: func(ue event.UpdateEvent) bool { return true },
                DeleteFunc: func(de event.DeleteEvent) bool { return true },
            }),
        ).
        Complete(r)
}

My problem is that I can not import somev1.External type into my project because importing the go module containing this type would break my current project's dependencies.
Is there a way in kubebuilder to watch for external resources without having to explicitly importing their types? like GVK or something?

mammad
  • 175
  • 8

1 Answers1

2

Yes it's possible.

You can create a REST client for your resource in main.go as below:

gvkExternal := schema.GroupVersionKind{
    Group:   "some.group.io",
    Version: "v1",
    Kind:    "External",
}

restClient, err := apiutil.RESTClientForGVK(gvkExternal, false, mgr.GetConfig(), serializer.NewCodecFactory(mgr.GetScheme()))
if err != nil {
    setupLog.Error(err, "unable to create REST client")
}

Then add a field for this REST client (rest.Interface) to your reconciler (yournativeresource_controller.go) struct such as:

type YourNativeResourceReconciler struct {
    client.Client
    Scheme        *runtime.Scheme
    // add this
    RESTClient    rest.Interface
}

Last, initialize your reconciler with this REST client (main.go):

if err = (&controllers.YourNativeResourceReconciler{
    Client:        mgr.GetClient(),
    Scheme:        mgr.GetScheme(),
    RESTClient:    restClient,
}).SetupWithManager(mgr); err != nil {
    setupLog.Error(err, "unable to create controller", "controller", "YourNativeResource")
    os.Exit(1)
}

Do not forget to add RBAC marker to your project (reconciler preferably) that will generate RBAC rules allowing you to manipulate External resource:

//+kubebuilder:rbac:groups=some.group.io,resources=externals,verbs=get;list;watch;create;update;patch;delete

After these steps, you can use REST client for manipulating External resource over YourNativeResource reconciler using r.RESTClient.

EDIT:

If you want to watch resources, dynamic clients may help. Create a dynamic client in main.go:

dynamicClient, err := dynamic.NewForConfig(mgr.GetConfig())
if err != nil {
    setupLog.Error(err, "unable to create dynamic client")
}

Apply above steps, add it to your reconciler etc. Then you will be able to watch External resource as below:

resourceInterface := r.DynamicClient.Resource(schema.GroupVersionResource{
    Group:    "some.group.io",
    Version:  "",
    Resource: "externals",
})
externalWatcher, err := resourceInterface.Watch(ctx, metav1.ListOptions{})
if err != nil {
    return err
}

defer externalWatcher.Stop()

select {
case event := <-externalWatcher.ResultChan():
    if event.Type == watch.Deleted {
        logger.Info("FINALIZER: An external resource is deleted.")
    }
}
tuna
  • 136
  • 7