@@ -68,8 +68,9 @@ type scope struct {
6868// K3kControlPlaneReconciler reconciles a K3kControlPlane object
6969type K3kControlPlaneReconciler struct {
7070 client.Client
71+ Host client.Client
7172 Helm helm.Client
72- K3KVersion string
73+ K3kVersion string
7374}
7475
7576// SetupWithManager sets up the controller with the Manager.
@@ -98,21 +99,58 @@ func (r *K3kControlPlaneReconciler) SetupWithManager(_ context.Context, mgr ctrl
9899// Reconcile creates a K3k Upstream cluster based on the provided spec of the K3kControlPlane.
99100func (r * K3kControlPlaneReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
100101 log := ctrl .LoggerFrom (ctx )
101- if err := r .ensureUpstreamChart (ctx ); err != nil {
102- return ctrl.Result {}, fmt .Errorf ("failed to ensure upstream K3K release is deployed: %w" , err )
103- } else {
104- log .Info ("The upstream K3K controller Helm release is deployed without errors." )
105- }
106- k3kControlPlane := & controlplanev1.K3kControlPlane {}
107- err := r .Get (ctx , req .NamespacedName , k3kControlPlane )
108- if err != nil {
102+
103+ var k3kControlPlane controlplanev1.K3kControlPlane
104+ if err := r .Get (ctx , req .NamespacedName , & k3kControlPlane ); err != nil {
109105 if apiError .IsNotFound (err ) {
110- log .Error (err , "Couldn 't find controlplane" )
111- return ctrl.Result {}, client . IgnoreNotFound ( err )
106+ log .Error (err , "couldn 't find controlplane" )
107+ return ctrl.Result {}, nil
112108 }
113109 return ctrl.Result {}, fmt .Errorf ("unable to get controlplane for request %w" , err )
114110 }
115111
112+ r .Host = r .Client
113+
114+ if k3kControlPlane .Spec .HostKubeconfig != nil {
115+ log .Info ("targeting external host cluster" )
116+
117+ hostKubeconfigSecret := & v1.Secret {
118+ ObjectMeta : metav1.ObjectMeta {
119+ Name : k3kControlPlane .Spec .HostKubeconfig .SecretName ,
120+ Namespace : k3kControlPlane .Spec .HostKubeconfig .SecretNamespace ,
121+ },
122+ }
123+
124+ if err := r .Get (ctx , client .ObjectKeyFromObject (hostKubeconfigSecret ), hostKubeconfigSecret ); err != nil {
125+ return ctrl.Result {}, fmt .Errorf ("failed to get host kubeconfig secret: %w" , err )
126+ }
127+
128+ config , err := clientcmd .RESTConfigFromKubeConfig (hostKubeconfigSecret .Data ["value" ])
129+ if err != nil {
130+ return ctrl.Result {}, fmt .Errorf ("failed to create RESTConfig from host kubeconfig secret: %w" , err )
131+ }
132+
133+ hostClient , err := client .New (config , client.Options {Scheme : r .Scheme ()})
134+ if err != nil {
135+ return ctrl.Result {}, fmt .Errorf ("failed to create host client: %w" , err )
136+ }
137+
138+ r .Host = hostClient
139+
140+ hostRESTClientGetter , err := helm .NewRESTClientGetter (config , hostClient .RESTMapper ())
141+ if err != nil {
142+ return ctrl.Result {}, fmt .Errorf ("failed to create host helm RESTClientGetter: %w" , err )
143+ }
144+
145+ r .Helm = helm .New (hostRESTClientGetter , "charts/k3k" , "k3k" , "k3k-system" )
146+ }
147+
148+ if err := r .ensureUpstreamChart (ctx ); err != nil {
149+ return ctrl.Result {}, fmt .Errorf ("failed to ensure upstream K3k release: %w" , err )
150+ }
151+
152+ log .Info ("upstream K3k controller Helm release deployed" )
153+
116154 cluster , err := capiutil .GetOwnerCluster (ctx , r , k3kControlPlane .ObjectMeta )
117155 if err != nil {
118156 return ctrl.Result {}, fmt .Errorf ("unable to get capi cluster owner: %w" , err )
@@ -121,12 +159,12 @@ func (r *K3kControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.Requ
121159 if cluster == nil {
122160 // capi cluster owner may not be set immediately, but we don't want to process the cluster until it is
123161 log .Info ("K3kControlPlane did not have a capi cluster owner" , "controlPlane" , k3kControlPlane .Name )
124- return ctrl.Result {}, nil
162+ return ctrl.Result {}, fmt . Errorf ( "CAPI cluster owner not yet set for control plane %q" , k3kControlPlane . Name )
125163 }
126164
127165 scope := & scope {
128166 Logger : log ,
129- k3kControlPlane : k3kControlPlane ,
167+ k3kControlPlane : & k3kControlPlane ,
130168 cluster : cluster ,
131169 }
132170
@@ -138,15 +176,6 @@ func (r *K3kControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.Requ
138176}
139177
140178func (r * K3kControlPlaneReconciler ) reconcileNormal (ctx context.Context , scope * scope ) (ctrl.Result , error ) {
141- if ! controllerutil .ContainsFinalizer (scope .k3kControlPlane , finalizer ) {
142- controllerutil .AddFinalizer (scope .k3kControlPlane , finalizer )
143- err := r .Update (ctx , scope .k3kControlPlane )
144- if err != nil {
145- scope .Error (err , "Unable to add finalizer to k3k controlplane" , "name" , scope .k3kControlPlane .Name , "namespace" , scope .k3kControlPlane .Namespace )
146- return ctrl.Result {}, fmt .Errorf ("unable to add finalizer" )
147- }
148- }
149-
150179 upstreamCluster , err := r .reconcileUpstreamCluster (ctx , scope .k3kControlPlane )
151180 if err != nil {
152181 return ctrl.Result {}, fmt .Errorf ("unable to reconcile k3k cluster: %w" , err )
@@ -189,17 +218,25 @@ func (r *K3kControlPlaneReconciler) reconcileNormal(ctx context.Context, scope *
189218 scope .Error (err , "Unable to update capiCluster" )
190219 return ctrl.Result {}, fmt .Errorf ("unable to update capi cluster" )
191220 }
192- if ! scope .k3kControlPlane .Status .Ready || scope .k3kControlPlane .Status .Initialized {
221+
222+ // Update status if not ready
223+ statusUpdated := ! scope .k3kControlPlane .Status .Ready || ! scope .k3kControlPlane .Status .Initialized
224+ if statusUpdated {
193225 scope .k3kControlPlane .Status .Ready = true
194226 scope .k3kControlPlane .Status .Initialized = true
195227 scope .k3kControlPlane .Status .ExternalManagedControlPlane = true
196228 scope .k3kControlPlane .Status .ClusterStatus = * upstreamCluster .Status .DeepCopy ()
197- err = r . Status (). Update ( ctx , scope . k3kControlPlane )
198- if err != nil {
229+
230+ if err := r . Status (). Update ( ctx , scope . k3kControlPlane ); err != nil {
199231 scope .Error (err , "unable to update status on controlPlane" )
200232 return ctrl.Result {}, fmt .Errorf ("unable to update status" )
201233 }
202234 }
235+
236+ if controllerutil .AddFinalizer (scope .k3kControlPlane , finalizer ) {
237+ return ctrl.Result {Requeue : true }, r .Update (ctx , scope .k3kControlPlane )
238+ }
239+
203240 return ctrl.Result {}, nil
204241}
205242
@@ -215,24 +252,28 @@ func (r *K3kControlPlaneReconciler) reconcileDelete(ctx context.Context, scope *
215252// ensureUpstreamChart tries to install a release of the upstream K3K chart or upgrade it if there is a new version.
216253func (r * K3kControlPlaneReconciler ) ensureUpstreamChart (ctx context.Context ) error {
217254 log := ctrl .LoggerFrom (ctx )
218- if err := r .Helm .ReleasePresent (ctx , r .K3KVersion ); err != nil {
219- values := map [string ]any {}
255+
256+ release , err := r .Helm .GetRelease (ctx )
257+ if err != nil {
220258 if errors .Is (err , driver .ErrReleaseNotFound ) {
221- if err := r .Helm .DeployLocalChart (ctx , values ); err != nil {
259+ if err := r .Helm .DeployLocalChart (ctx , nil ); err != nil {
222260 return fmt .Errorf ("failed to deploy a local K3K release: %w" , err )
223261 }
224- log .Info ("Successfully deployed the upstream K3K release." )
225- return nil
226- }
227- if errors .Is (err , helm .ErrReleaseOutdated ) {
228- if err := r .Helm .UpgradeLocalChart (ctx , values ); err != nil {
229- return fmt .Errorf ("failed to upgrade a local K3K release: %w" , err )
230- }
231- log .Info ("Successfully upgraded the upstream K3K release." )
262+
263+ log .Info ("successfully deployed the upstream K3K release." )
264+
232265 return nil
233266 }
234- return fmt .Errorf ("couldn't check for the presence of a K3K release: %w" , err )
267+
268+ return fmt .Errorf ("couldn't check for the presence of a K3k release: %w" , err )
235269 }
270+
271+ log .Info ("found K3k release version " + release .Chart .Metadata .Version )
272+
273+ if release .Chart .Metadata .Version != r .K3kVersion {
274+ log .Error (helm .ErrReleaseMismatch , "K3k release version is not matching the current build. Something could break." )
275+ }
276+
236277 return nil
237278}
238279
@@ -245,6 +286,7 @@ func (r *K3kControlPlaneReconciler) reconcileUpstreamCluster(ctx context.Context
245286 if err != nil {
246287 return nil , fmt .Errorf ("failed to get current clusters for controlPlane %s/%s: %w" , controlPlane .Namespace , controlPlane .Name , err )
247288 }
289+
248290 spec := upstream.ClusterSpec {
249291 Version : controlPlane .Spec .Version ,
250292 Servers : controlPlane .Spec .Servers ,
@@ -259,6 +301,7 @@ func (r *K3kControlPlaneReconciler) reconcileUpstreamCluster(ctx context.Context
259301 Persistence : controlPlane .Spec .Persistence ,
260302 Expose : controlPlane .Spec .Expose ,
261303 }
304+
262305 if len (clusters ) > 1 {
263306 var names []string
264307 for i := range clusters {
@@ -271,12 +314,17 @@ func (r *K3kControlPlaneReconciler) reconcileUpstreamCluster(ctx context.Context
271314 }
272315 return nil , fmt .Errorf ("reprovisioning needed" )
273316 }
317+
274318 if len (clusters ) == 0 {
275- // since the upstream cluster type isn't namespaced but we are, we can't use owner refs to control deletion of the cluster
319+ namespace := "k3k-" + controlPlane .Name
320+ if controlPlane .Spec .HostTargetNamespace != "" {
321+ namespace = controlPlane .Spec .HostTargetNamespace
322+ }
323+
276324 upstreamCluster := upstream.Cluster {
277325 ObjectMeta : metav1.ObjectMeta {
278326 GenerateName : controlPlane .Name + "-" ,
279- Namespace : controlPlane . Namespace ,
327+ Namespace : namespace ,
280328 Labels : map [string ]string {
281329 ownerNameLabel : controlPlane .Name ,
282330 ownerNamespaceLabel : controlPlane .Namespace ,
@@ -285,14 +333,20 @@ func (r *K3kControlPlaneReconciler) reconcileUpstreamCluster(ctx context.Context
285333 Spec : spec ,
286334 }
287335
288- if err := ctrl .SetControllerReference (controlPlane , & upstreamCluster , r .Scheme ()); err != nil {
289- return nil , fmt .Errorf ("unable to create cluster for controlPlane %s: %w" , controlPlane .Name , err )
336+ virtualClusterNamespace := & v1.Namespace {
337+ ObjectMeta : metav1.ObjectMeta {
338+ Name : namespace ,
339+ },
290340 }
291341
292- err = r .Create (ctx , & upstreamCluster )
293- if err != nil {
342+ if err := r .Host .Create (ctx , virtualClusterNamespace ); err != nil && ! apiError .IsAlreadyExists (err ) {
343+ return nil , fmt .Errorf ("unable to create namespace for controlPlane %s: %w" , controlPlane .Name , err )
344+ }
345+
346+ if err = r .Host .Create (ctx , & upstreamCluster ); err != nil {
294347 return nil , fmt .Errorf ("unable to create cluster for controlPlane %s: %w" , controlPlane .Name , err )
295348 }
349+
296350 return & upstreamCluster , nil
297351 }
298352 // at this point we have exactly one cluster
@@ -301,7 +355,7 @@ func (r *K3kControlPlaneReconciler) reconcileUpstreamCluster(ctx context.Context
301355 return currentCluster , nil
302356 }
303357 currentCluster .Spec = spec
304- err = r .Update (ctx , currentCluster )
358+ err = r .Host . Update (ctx , currentCluster )
305359 if err != nil {
306360 return nil , fmt .Errorf ("unable to update cluster for controlPlane %s: %w" , controlPlane .Name , err )
307361 }
@@ -318,7 +372,7 @@ func (r *K3kControlPlaneReconciler) deleteUpstreamClusters(ctx context.Context,
318372 }
319373 var deleteErrs []error
320374 for i := range clusters {
321- if err := r .Delete (ctx , & clusters [i ]); err != nil && ! apiError .IsNotFound (err ) {
375+ if err := r .Host . Delete (ctx , & clusters [i ]); err != nil && ! apiError .IsNotFound (err ) {
322376 log .Error (err , "failed to delete upstream cluster " + clusters [i ].Name )
323377 deleteErrs = append (deleteErrs , err )
324378 }
@@ -345,7 +399,7 @@ func (r *K3kControlPlaneReconciler) getUpstreamClusters(ctx context.Context, con
345399 return nil , fmt .Errorf ("unable to form label for controlPlane %s: %w" , controlPlane .Name , err )
346400 }
347401 selector := labels .NewSelector ().Add (* ownerNameRequirement ).Add (* ownerNamespaceRequirement )
348- err = r .List (ctx , & currentClusters , & client.ListOptions {LabelSelector : selector })
402+ err = r .Host . List (ctx , & currentClusters , & client.ListOptions {LabelSelector : selector })
349403 if err != nil {
350404 return nil , fmt .Errorf ("unable to list current clusters %w" , err )
351405 }
@@ -367,7 +421,7 @@ func (r *K3kControlPlaneReconciler) getKubeconfig(ctx context.Context, upstreamC
367421 return nil , fmt .Errorf ("unable to extract URL from specificed hostname: %s, %w" , restConfig .Host , err )
368422 }
369423
370- return cfg .Generate (ctx , r .Client , upstreamCluster , u .Hostname ())
424+ return cfg .Generate (ctx , r .Host , upstreamCluster , u .Hostname ())
371425}
372426
373427// createKubeconfigSecret stores the kubeconfig into a secret which can be retrieved by CAPI later on
0 commit comments