From e231968272ca31a8cae5660ea89f6f26f8fdea28 Mon Sep 17 00:00:00 2001 From: Carlos Salas Date: Wed, 9 Aug 2023 17:02:31 +0200 Subject: [PATCH 1/2] feat: create client from kubeconfig if flag is set Signed-off-by: Carlos Salas --- main.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index aa6781f2..9c43dbb1 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,9 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/component-base/version" "k8s.io/klog/v2" @@ -57,6 +60,7 @@ var ( syncPeriod time.Duration healthAddr string concurrencyNumber int + rancherKubeconfig string ) func init() { @@ -97,6 +101,9 @@ func initFlags(fs *pflag.FlagSet) { fs.IntVar(&concurrencyNumber, "concurrency", 1, "Number of resources to process simultaneously") + + fs.StringVar(&rancherKubeconfig, "rancher-kubeconfig", "", + "Path to a kubeconfig file. Only required if out-of-cluster.") } func main() { @@ -154,12 +161,48 @@ func setupChecks(mgr ctrl.Manager) { } } +// setupReconcilers can either create a client for an in-cluster installation (rancher and rancher-turtles in the same cluster) +// or create a client for an out-of-cluster installation (rancher and rancher-turtles in different clusters). func setupReconcilers(ctx context.Context, mgr ctrl.Manager) { + var c client.Client + + if len(rancherKubeconfig) > 0 { + setupLog.Info("out-of-cluster installation of rancher-turtles", "using kubeconfig from path", rancherKubeconfig) + + restConfig, err := loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: rancherKubeconfig}, "") + if err != nil { + setupLog.Error(err, "unable to load kubeconfig from file") + os.Exit(1) + } + + c, err = client.New(restConfig, client.Options{Scheme: mgr.GetClient().Scheme()}) + + if err != nil { + setupLog.Error(err, "failed to create client") + os.Exit(1) + } + } else { + setupLog.Info("in-cluster installation of rancher-turtles") + c = mgr.GetClient() + } + if err := (&controllers.CAPIImportReconciler{ - Client: mgr.GetClient(), + Client: c, WatchFilterValue: watchFilterValue, }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: concurrencyNumber}); err != nil { setupLog.Error(err, "unable to create capi controller") os.Exit(1) } } + +// loadConfigWithContext loads a REST Config from a path using a logic similar to the one used in controller-runtime. +func loadConfigWithContext(apiServerURL string, loader clientcmd.ClientConfigLoader, context string) (*rest.Config, error) { + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + loader, + &clientcmd.ConfigOverrides{ + ClusterInfo: clientcmdapi.Cluster{ + Server: apiServerURL, + }, + CurrentContext: context, + }).ClientConfig() +} From db01ba9bd3c971e4f182d236d739b6ec22f5b72d Mon Sep 17 00:00:00 2001 From: Carlos Salas Date: Thu, 17 Aug 2023 16:44:43 +0200 Subject: [PATCH 2/2] feat: use two differentiated clients in CAPIImportReconciler Signed-off-by: Carlos Salas --- internal/controllers/import_controller.go | 9 ++-- .../controllers/import_controller_test.go | 1 + main.go | 48 +++++++++++-------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/internal/controllers/import_controller.go b/internal/controllers/import_controller.go index d2b308f2..bf66f4f5 100644 --- a/internal/controllers/import_controller.go +++ b/internal/controllers/import_controller.go @@ -65,7 +65,8 @@ const ( // CAPIImportReconciler represents a reconciler for importing CAPI clusters in Rancher. type CAPIImportReconciler struct { - client.Client + Client client.Client + RancherClient client.Client recorder record.EventRecorder WatchFilterValue string Scheme *runtime.Scheme @@ -178,7 +179,7 @@ func (r *CAPIImportReconciler) Reconcile(ctx context.Context, req ctrl.Request) errs = append(errs, fmt.Errorf("error reconciling cluster: %w", err)) } - if err := r.Patch(ctx, capiCluster, patchBase); err != nil { + if err := r.Client.Patch(ctx, capiCluster, patchBase); err != nil { errs = append(errs, fmt.Errorf("failed to patch cluster: %w", err)) } @@ -191,7 +192,7 @@ func (r *CAPIImportReconciler) Reconcile(ctx context.Context, req ctrl.Request) func (r *CAPIImportReconciler) reconcile(ctx context.Context, capiCluster *clusterv1.Cluster) (ctrl.Result, error) { // fetch the rancher clusters - rancherClusterHandler := rancher.NewClusterHandler(ctx, r.Client) + rancherClusterHandler := rancher.NewClusterHandler(ctx, r.RancherClient) rancherClusterName := turtelesnaming.Name(capiCluster.Name).ToRancherName() rancherCluster, err := rancherClusterHandler.Get(client.ObjectKey{Namespace: capiCluster.Namespace, Name: rancherClusterName}) @@ -346,7 +347,7 @@ func (r *CAPIImportReconciler) getClusterRegistrationManifest(ctx context.Contex log := log.FromContext(ctx) key := client.ObjectKey{Name: clusterRegistrationTokenName, Namespace: clusterName} - tokenHandler := rancher.NewClusterRegistrationTokenHandler(ctx, r.Client) + tokenHandler := rancher.NewClusterRegistrationTokenHandler(ctx, r.RancherClient) token, err := tokenHandler.Get(key) if err != nil { diff --git a/internal/controllers/import_controller_test.go b/internal/controllers/import_controller_test.go index 766e8615..a7b55864 100644 --- a/internal/controllers/import_controller_test.go +++ b/internal/controllers/import_controller_test.go @@ -59,6 +59,7 @@ var _ = Describe("reconcile CAPI Cluster", func() { BeforeEach(func() { r = &CAPIImportReconciler{ Client: cl, + RancherClient: cl, // rancher and rancher-turtles deployed in the same cluster remoteClientGetter: remote.NewClusterClient, } diff --git a/main.go b/main.go index 9c43dbb1..f5e9db71 100644 --- a/main.go +++ b/main.go @@ -103,7 +103,7 @@ func initFlags(fs *pflag.FlagSet) { "Number of resources to process simultaneously") fs.StringVar(&rancherKubeconfig, "rancher-kubeconfig", "", - "Path to a kubeconfig file. Only required if out-of-cluster.") + "Path to the Rancher kubeconfig file. Only required if running out-of-cluster.") } func main() { @@ -161,38 +161,46 @@ func setupChecks(mgr ctrl.Manager) { } } -// setupReconcilers can either create a client for an in-cluster installation (rancher and rancher-turtles in the same cluster) -// or create a client for an out-of-cluster installation (rancher and rancher-turtles in different clusters). func setupReconcilers(ctx context.Context, mgr ctrl.Manager) { - var c client.Client + rancherClient, err := setupRancherClient(mgr) + if err != nil { + setupLog.Error(err, "failed to create client") + os.Exit(1) + } + + if err := (&controllers.CAPIImportReconciler{ + Client: mgr.GetClient(), + RancherClient: rancherClient, + WatchFilterValue: watchFilterValue, + }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: concurrencyNumber}); err != nil { + setupLog.Error(err, "unable to create capi controller") + os.Exit(1) + } +} +// setupRancherClient can either create a client for an in-cluster installation (rancher and rancher-turtles in the same cluster) +// or create a client for an out-of-cluster installation (rancher and rancher-turtles in different clusters) based on the +// existence of Rancher kubeconfig file. +func setupRancherClient(mgr ctrl.Manager) (client.Client, error) { if len(rancherKubeconfig) > 0 { setupLog.Info("out-of-cluster installation of rancher-turtles", "using kubeconfig from path", rancherKubeconfig) restConfig, err := loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: rancherKubeconfig}, "") if err != nil { - setupLog.Error(err, "unable to load kubeconfig from file") - os.Exit(1) + return nil, fmt.Errorf("unable to load kubeconfig from file: %w", err) } - c, err = client.New(restConfig, client.Options{Scheme: mgr.GetClient().Scheme()}) - + rancherClient, err := client.New(restConfig, client.Options{Scheme: mgr.GetClient().Scheme()}) if err != nil { - setupLog.Error(err, "failed to create client") - os.Exit(1) + return nil, err } - } else { - setupLog.Info("in-cluster installation of rancher-turtles") - c = mgr.GetClient() - } - if err := (&controllers.CAPIImportReconciler{ - Client: c, - WatchFilterValue: watchFilterValue, - }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: concurrencyNumber}); err != nil { - setupLog.Error(err, "unable to create capi controller") - os.Exit(1) + return rancherClient, nil } + + setupLog.Info("in-cluster installation of rancher-turtles") + + return mgr.GetClient(), nil } // loadConfigWithContext loads a REST Config from a path using a logic similar to the one used in controller-runtime.