diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index d489a2d66896..7137cef0e983 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -22,6 +22,9 @@ import ( ) const ( + // TakeOverCluster is the label used to mark the nodes that run on takeover-cluster instances. + TakeOverCluster = "cluster.x-k8s.io/takeover-cluster" + // ClusterLabelName is the label set on machines linked to a cluster and // external objects(bootstrap and infrastructure providers). ClusterLabelName = "cluster.x-k8s.io/cluster-name" diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go index b7702f80c072..7805e8515ccc 100644 --- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go @@ -279,7 +279,7 @@ func (r *KubeadmConfigReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // Note: can't use IsFalse here because we need to handle the absence of the condition as well as false. - if !conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) { + if !annotations.IsTakeOverCluster(cluster.GetObjectMeta()) && !conditions.IsTrue(cluster, clusterv1.ControlPlaneInitializedCondition) { return r.handleClusterNotInitialized(ctx, scope) } diff --git a/controlplane/kubeadm/internal/controllers/controller.go b/controlplane/kubeadm/internal/controllers/controller.go index 05d69b52b00b..b3734de5c77c 100644 --- a/controlplane/kubeadm/internal/controllers/controller.go +++ b/controlplane/kubeadm/internal/controllers/controller.go @@ -394,6 +394,11 @@ func (r *KubeadmControlPlaneReconciler) reconcile(ctx context.Context, cluster * switch { // We are creating the first replica case numMachines < desiredReplicas && numMachines == 0: + if annotations.IsTakeOverCluster(controlPlane.Cluster.GetObjectMeta()) { + // Create a new Machine w/ join + log.Info("Scaling up control plane for TakeOverCluster", "Desired", desiredReplicas, "Existing", numMachines) + return r.scaleUpControlPlane(ctx, cluster, kcp, controlPlane) + } // Create new Machine w/ init log.Info("Initializing control plane", "Desired", desiredReplicas, "Existing", numMachines) conditions.MarkFalse(controlPlane.KCP, controlplanev1.AvailableCondition, controlplanev1.WaitingForKubeadmInitReason, clusterv1.ConditionSeverityInfo, "") @@ -568,7 +573,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileEtcdMembers(ctx context.Context log := ctrl.LoggerFrom(ctx) // If etcd is not managed by KCP this is a no-op. - if !controlPlane.IsEtcdManaged() { + if annotations.IsTakeOverCluster(controlPlane.Cluster.GetObjectMeta()) || !controlPlane.IsEtcdManaged() { return ctrl.Result{}, nil } diff --git a/controlplane/kubeadm/internal/controllers/helpers.go b/controlplane/kubeadm/internal/controllers/helpers.go index 3355f942409c..40e5f8b9c4f6 100644 --- a/controlplane/kubeadm/internal/controllers/helpers.go +++ b/controlplane/kubeadm/internal/controllers/helpers.go @@ -73,9 +73,11 @@ func (r *KubeadmControlPlaneReconciler) reconcileKubeconfig(ctx context.Context, case err != nil: return ctrl.Result{}, errors.Wrap(err, "failed to retrieve kubeconfig Secret") } - - if err := r.adoptKubeconfigSecret(ctx, cluster, configSecret, kcp); err != nil { - return ctrl.Result{}, err + + if util.IsOwnedByObject(configSecret, cluster) && !util.IsControlledBy(configSecret, kcp) { + if err := r.adoptKubeconfigSecret(ctx, cluster, configSecret, kcp); err != nil { + return ctrl.Result{}, err + } } // only do rotation on owned secrets diff --git a/internal/controllers/cluster/cluster_controller_phases.go b/internal/controllers/cluster/cluster_controller_phases.go index 741a423cb244..06d130c28557 100644 --- a/internal/controllers/cluster/cluster_controller_phases.go +++ b/internal/controllers/cluster/cluster_controller_phases.go @@ -245,7 +245,7 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, cluster *cluster if err != nil { return ctrl.Result{}, err } - if initialized { + if annotations.IsTakeOverCluster(cluster.GetObjectMeta()) || initialized { conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition) } else { conditions.MarkFalse(cluster, clusterv1.ControlPlaneInitializedCondition, clusterv1.WaitingForControlPlaneProviderInitializedReason, clusterv1.ConditionSeverityInfo, "Waiting for control plane provider to indicate the control plane has been initialized") diff --git a/util/annotations/helpers.go b/util/annotations/helpers.go index 0ec9ef9388ac..da4ada4d2c21 100644 --- a/util/annotations/helpers.go +++ b/util/annotations/helpers.go @@ -25,6 +25,11 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) +// IsTakeOverCluster returns true if the object has the `managed-by` annotation. +func IsTakeOverCluster(o metav1.Object) bool { + return hasAnnotation(o, clusterv1.TakeOverCluster) +} + // IsPaused returns true if the Cluster is paused or the object has the `paused` annotation. func IsPaused(cluster *clusterv1.Cluster, o metav1.Object) bool { if cluster.Spec.Paused {