From 0ad288efad32df17a8993f2d0bcb237cd3b128b5 Mon Sep 17 00:00:00 2001 From: Vivek Koppuru Date: Tue, 16 Jan 2024 23:09:14 -0800 Subject: [PATCH] Add in-place upgrade controllers for kcp and md --- controllers/factory.go | 56 ++++- ...mcontrolplane_inplaceupgrade_controller.go | 219 ++++++++++++++++++ ...inedeployment_inplaceupgrade_controller.go | 185 +++++++++++++++ manager/main.go | 14 ++ pkg/constants/constants.go | 2 + 5 files changed, 466 insertions(+), 10 deletions(-) create mode 100644 controllers/kubeadmcontrolplane_inplaceupgrade_controller.go create mode 100644 controllers/machinedeployment_inplaceupgrade_controller.go diff --git a/controllers/factory.go b/controllers/factory.go index 9905f2281521d..5eda2fd7a0007 100644 --- a/controllers/factory.go +++ b/controllers/factory.go @@ -63,16 +63,18 @@ type Factory struct { } type Reconcilers struct { - ClusterReconciler *ClusterReconciler - DockerDatacenterReconciler *DockerDatacenterReconciler - VSphereDatacenterReconciler *VSphereDatacenterReconciler - SnowMachineConfigReconciler *SnowMachineConfigReconciler - TinkerbellDatacenterReconciler *TinkerbellDatacenterReconciler - CloudStackDatacenterReconciler *CloudStackDatacenterReconciler - NutanixDatacenterReconciler *NutanixDatacenterReconciler - ControlPlaneUpgradeReconciler *ControlPlaneUpgradeReconciler - MachineDeploymentUpgradeReconciler *MachineDeploymentUpgradeReconciler - NodeUpgradeReconciler *NodeUpgradeReconciler + ClusterReconciler *ClusterReconciler + DockerDatacenterReconciler *DockerDatacenterReconciler + VSphereDatacenterReconciler *VSphereDatacenterReconciler + SnowMachineConfigReconciler *SnowMachineConfigReconciler + TinkerbellDatacenterReconciler *TinkerbellDatacenterReconciler + CloudStackDatacenterReconciler *CloudStackDatacenterReconciler + NutanixDatacenterReconciler *NutanixDatacenterReconciler + KubeadmControlPlaneInPlaceUpgradeReconciler *KubeadmControlPlaneInPlaceUpgradeReconciler + MachineDeploymentInPlaceUpgradeReconciler *MachineDeploymentInPlaceUpgradeReconciler + ControlPlaneUpgradeReconciler *ControlPlaneUpgradeReconciler + MachineDeploymentUpgradeReconciler *MachineDeploymentUpgradeReconciler + NodeUpgradeReconciler *NodeUpgradeReconciler } type buildStep func(ctx context.Context) error @@ -590,6 +592,40 @@ func (f *Factory) withMachineHealthCheckReconciler() *Factory { return f } +// WithControlPlaneUpgradeReconciler builds the KubeadmControlPlaneInPlaceUpgrade reconciler. +func (f *Factory) WithKubeadmControlPlaneInPlaceUpgradeReconciler() *Factory { + f.buildSteps = append(f.buildSteps, func(ctx context.Context) error { + if f.reconcilers.KubeadmControlPlaneInPlaceUpgradeReconciler != nil { + return nil + } + + f.reconcilers.KubeadmControlPlaneInPlaceUpgradeReconciler = NewKubeadmControlPlaneInPlaceUpgradeReconciler( + f.manager.GetClient(), + ) + + return nil + }) + + return f +} + +// WithMachineDeploymentUpgradeReconciler builds the MachineDeploymentInPlace reconciler. +func (f *Factory) WithMachineDeploymentInPlaceUpgradeReconciler() *Factory { + f.buildSteps = append(f.buildSteps, func(ctx context.Context) error { + if f.reconcilers.MachineDeploymentInPlaceUpgradeReconciler != nil { + return nil + } + + f.reconcilers.MachineDeploymentInPlaceUpgradeReconciler = NewMachineDeploymentInPlaceUpgradeReconciler( + f.manager.GetClient(), + ) + + return nil + }) + + return f +} + // WithControlPlaneUpgradeReconciler builds the ControlPlaneUpgrade reconciler. func (f *Factory) WithControlPlaneUpgradeReconciler() *Factory { f.buildSteps = append(f.buildSteps, func(ctx context.Context) error { diff --git a/controllers/kubeadmcontrolplane_inplaceupgrade_controller.go b/controllers/kubeadmcontrolplane_inplaceupgrade_controller.go new file mode 100644 index 0000000000000..752ec5617654b --- /dev/null +++ b/controllers/kubeadmcontrolplane_inplaceupgrade_controller.go @@ -0,0 +1,219 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kerrors "k8s.io/apimachinery/pkg/util/errors" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" + "sigs.k8s.io/cluster-api/util/collections" + "sigs.k8s.io/cluster-api/util/patch" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + anywherev1 "github.com/aws/eks-anywhere/pkg/api/v1alpha1" + "github.com/aws/eks-anywhere/pkg/constants" +) + +// KubeadmControlPlaneInPlaceUpgradeReconciler reconciles a KubeadmControlPlaneInPlaceUpgradeReconciler object. +type KubeadmControlPlaneInPlaceUpgradeReconciler struct { + client client.Client + log logr.Logger +} + +// NewKubeadmControlPlaneReconciler returns a new instance of KubeadmControlPlaneReconciler. +func NewKubeadmControlPlaneInPlaceUpgradeReconciler(client client.Client) *KubeadmControlPlaneInPlaceUpgradeReconciler { + return &KubeadmControlPlaneInPlaceUpgradeReconciler{ + client: client, + log: ctrl.Log.WithName("KubeadmControlPlaneInPlaceUpgradeController"), + } +} + +//+kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=kubeadmcontrolplane,verbs=get;list;watch;create;update;patch +//+kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=kubeadmcontrolplane/status,verbs=get + +// Reconcile reconciles a KubeadmControlPlane object for in place upgrades. +func (r *KubeadmControlPlaneInPlaceUpgradeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, reterr error) { + log := r.log.WithValues("KubeadmControlPlane", req.NamespacedName) + + log.Info("Reconciling KubeadmControlPlane object") + kcp := &controlplanev1.KubeadmControlPlane{} + if err := r.client.Get(ctx, req.NamespacedName, kcp); err != nil { + if apierrors.IsNotFound(err) { + return reconcile.Result{}, err + } + return ctrl.Result{}, err + } + + if !r.inPlaceUpgradeNeeded(kcp) { + log.Info("In place upgraded needed annotation not detected, nothing to do") + return ctrl.Result{}, nil + } + + patchHelper, err := patch.NewHelper(kcp, r.client) + if err != nil { + return ctrl.Result{}, err + } + + defer func() { + // Always attempt to patch after each reconciliation in case annotation is removed. + if err := patchHelper.Patch(ctx, kcp); err != nil { + reterr = kerrors.NewAggregate([]error{reterr, err}) + } + + // Only requeue if we are not already re-queueing and the "in-place-upgrade-needed" annotation is not set. + // We do this to be able to update the status continuously until it becomes ready, + // since there might be changes in state of the world that don't trigger reconciliation requests + if reterr == nil && !result.Requeue && result.RequeueAfter <= 0 && r.inPlaceUpgradeNeeded(kcp) { + result = ctrl.Result{RequeueAfter: 10 * time.Second} + } + }() + + // Reconcile the KubeadmControlPlane deletion if the DeletionTimestamp is set. + if !kcp.DeletionTimestamp.IsZero() { + return r.reconcileDelete(ctx, log, kcp) + } + + // AddFinalizer is idempotent + controllerutil.AddFinalizer(kcp, controlPlaneUpgradeFinalizerName) + + return r.reconcile(ctx, log, kcp) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *KubeadmControlPlaneInPlaceUpgradeReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&controlplanev1.KubeadmControlPlane{}). + Complete(r) +} + +func (r *KubeadmControlPlaneInPlaceUpgradeReconciler) reconcile(ctx context.Context, log logr.Logger, kcp *controlplanev1.KubeadmControlPlane) (ctrl.Result, error) { + log.Info("Reconciling in place upgrade for control plane") + if kcp.Spec.Replicas != nil && (*kcp.Spec.Replicas == kcp.Status.UpdatedReplicas) { + log.Info("KubeadmControlPlane is ready, nothing else to reconcile for in place upgrade") + // Remove in-place-upgrade-needed annotation + delete(kcp.Annotations, "controlplane.clusters.x-k8s.io/in-place-upgrade-needed") + return ctrl.Result{}, nil + } + cpUpgrade := &anywherev1.ControlPlaneUpgrade{} + if err := r.client.Get(ctx, GetNamespacedNameType(cpUpgradeName(kcp.Name), constants.EksaSystemNamespace), cpUpgrade); err != nil { + if apierrors.IsNotFound(err) { + log.Info("Creating control plane upgrade object") + machines, err := r.machinesToUpgrade(ctx, kcp) + if err != nil { + return ctrl.Result{}, fmt.Errorf("retrieving list of control plane machines: %v", err) + } + if err := r.client.Create(ctx, controlPlaneUpgrade(kcp, machines)); client.IgnoreAlreadyExists(err) != nil { + return ctrl.Result{}, fmt.Errorf("failed to create control plane upgrade for KubeadmControlPlane %s: %v", kcp.Name, err) + } + return ctrl.Result{}, nil + } + return ctrl.Result{}, fmt.Errorf("getting control plane upgrade for KubeadmControlPlane %s: %v", kcp.Name, err) + } + if !cpUpgrade.Status.Ready { + return ctrl.Result{}, nil + } + // TODO: update status for templates and other resources + log.Info("Control plane upgrade complete, deleting object") + if err := r.client.Delete(ctx, cpUpgrade); err != nil { + return ctrl.Result{}, fmt.Errorf("deleting control plane upgrade object: %v", err) + } + + return ctrl.Result{}, nil +} + +func (r *KubeadmControlPlaneInPlaceUpgradeReconciler) reconcileDelete(ctx context.Context, log logr.Logger, kcp *controlplanev1.KubeadmControlPlane) (ctrl.Result, error) { + log.Info("Reconciling KubeadmControlPlane deletion of in place upgrade resources") + cpUpgrade := &anywherev1.ControlPlaneUpgrade{} + if err := r.client.Get(ctx, GetNamespacedNameType(kcp.Name, constants.EksaSystemNamespace), cpUpgrade); err != nil { + if apierrors.IsNotFound(err) { + log.Info("Control plane in place upgrade object not found, skipping ControlPlaneUpgrade deletion") + } else { + return ctrl.Result{}, fmt.Errorf("getting control plane upgrade for cluster %s: %v", kcp.Name, err) + } + } else { + log.Info("Deleting control plane upgrade", "ControlPlaneUpgrade", cpUpgradeName(kcp.Name)) + if err := r.client.Delete(ctx, cpUpgrade); err != nil { + return ctrl.Result{}, fmt.Errorf("deleting control plane upgrade object: %v", err) + } + } + + // Remove the finalizer on KubeadmControlPlane object + controllerutil.RemoveFinalizer(kcp, controlPlaneUpgradeFinalizerName) + return ctrl.Result{}, nil +} + +func (r *KubeadmControlPlaneInPlaceUpgradeReconciler) inPlaceUpgradeNeeded(kcp *controlplanev1.KubeadmControlPlane) bool { + _, ok := kcp.Annotations["controlplane.clusters.x-k8s.io/in-place-upgrade-needed"] + return ok +} + +func (r *KubeadmControlPlaneInPlaceUpgradeReconciler) machinesToUpgrade(ctx context.Context, kcp *controlplanev1.KubeadmControlPlane) ([]corev1.ObjectReference, error) { + selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"cluster.x-k8s.io/control-plane-name": kcp.Name}}) + if err != nil { + return nil, err + } + machineList := &clusterv1.MachineList{} + if err := r.client.List(ctx, machineList, &client.ListOptions{LabelSelector: selector}); err != nil { + return nil, err + } + machines := collections.FromMachineList(machineList).SortedByCreationTimestamp() + machineObjects := make([]corev1.ObjectReference, 0, len(machines)) + for _, machine := range machines { + machineObjects = append(machineObjects, + corev1.ObjectReference{ + Kind: machine.Kind, + Namespace: machine.Namespace, + Name: machine.Name, + }, + ) + } + return machineObjects, nil +} + +func controlPlaneUpgrade(kcp *controlplanev1.KubeadmControlPlane, machines []corev1.ObjectReference) *anywherev1.ControlPlaneUpgrade { + return &anywherev1.ControlPlaneUpgrade{ + ObjectMeta: metav1.ObjectMeta{ + Name: cpUpgradeName(kcp.Name), + Namespace: constants.EksaSystemNamespace, + }, + Spec: anywherev1.ControlPlaneUpgradeSpec{ + ControlPlane: corev1.ObjectReference{ + Kind: kcp.Kind, + Namespace: kcp.Namespace, + Name: kcp.Name, + }, + KubernetesVersion: "v1.28.3-eks-1-28-9", + EtcdVersion: "v3.5.9-eks-1-28-9", + MachinesRequireUpgrade: machines, + }, + } +} + +func cpUpgradeName(kcpName string) string { + return kcpName + "-cp-upgrade" +} diff --git a/controllers/machinedeployment_inplaceupgrade_controller.go b/controllers/machinedeployment_inplaceupgrade_controller.go new file mode 100644 index 0000000000000..e7dc6cd504e70 --- /dev/null +++ b/controllers/machinedeployment_inplaceupgrade_controller.go @@ -0,0 +1,185 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kerrors "k8s.io/apimachinery/pkg/util/errors" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/util/patch" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + anywherev1 "github.com/aws/eks-anywhere/pkg/api/v1alpha1" + "github.com/aws/eks-anywhere/pkg/constants" +) + +// MachineDeploymentInPlaceUpgradeReconciler reconciles a MachineDeploymentInPlaceUpgradeReconciler object. +type MachineDeploymentInPlaceUpgradeReconciler struct { + client client.Client + log logr.Logger +} + +// NewMachineDeploymentReconciler returns a new instance of MachineDeploymentReconciler. +func NewMachineDeploymentInPlaceUpgradeReconciler(client client.Client) *MachineDeploymentInPlaceUpgradeReconciler { + return &MachineDeploymentInPlaceUpgradeReconciler{ + client: client, + log: ctrl.Log.WithName("MachineDeploymentInPlaceUpgradeController"), + } +} + +//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinedeployment,verbs=get;list;watch;create;update;patch +//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinedeployment/status,verbs=get + +// Reconcile reconciles a MachineDeployment object for in place upgrades. +func (r *MachineDeploymentInPlaceUpgradeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, reterr error) { + log := r.log.WithValues("MachineDeployment", req.NamespacedName) + + log.Info("Reconciling MachineDeployment object") + md := &clusterv1.MachineDeployment{} + if err := r.client.Get(ctx, req.NamespacedName, md); err != nil { + if apierrors.IsNotFound(err) { + return reconcile.Result{}, err + } + return ctrl.Result{}, err + } + + if !r.inPlaceUpgradeNeeded(md) { + log.Info("In place upgraded needed annotation not detected, nothing to do") + return ctrl.Result{}, nil + } + + patchHelper, err := patch.NewHelper(md, r.client) + if err != nil { + return ctrl.Result{}, err + } + + defer func() { + // Always attempt to patch after each reconciliation in case annotation is removed. + if err := patchHelper.Patch(ctx, md); err != nil { + reterr = kerrors.NewAggregate([]error{reterr, err}) + } + + // Only requeue if we are not already re-queueing and the "in-place-upgrade-needed" annotation is not set. + // We do this to be able to update the status continuously until it becomes ready, + // since there might be changes in state of the world that don't trigger reconciliation requests + if reterr == nil && !result.Requeue && result.RequeueAfter <= 0 && r.inPlaceUpgradeNeeded(md) { + result = ctrl.Result{RequeueAfter: 10 * time.Second} + } + }() + + // Reconcile the MachineDeployment deletion if the DeletionTimestamp is set. + if !md.DeletionTimestamp.IsZero() { + return r.reconcileDelete(ctx, log, md) + } + + // AddFinalizer is idempotent + controllerutil.AddFinalizer(md, mdUpgradeFinalizerName) + + return r.reconcile(ctx, log, md) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *MachineDeploymentInPlaceUpgradeReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&clusterv1.MachineDeployment{}). + Complete(r) +} + +func (r *MachineDeploymentInPlaceUpgradeReconciler) reconcile(ctx context.Context, log logr.Logger, md *clusterv1.MachineDeployment) (ctrl.Result, error) { + log.Info("Reconciling in place upgrade for workers") + /*if md.Status.UpdatedReplicas == md.Spec.Replicas { + log.Info("MachineDeployment is ready, nothing else to reconcile for in place upgrade") + // Remove annotation + }*/ + mdUpgrade := mdUpgrade(md) + if err := r.client.Get(ctx, GetNamespacedNameType(md.Name, constants.EksaSystemNamespace), mdUpgrade); err != nil { + if apierrors.IsNotFound(err) { + log.Info("Creating machine deployment upgrade object") + if err := r.client.Create(ctx, mdUpgrade); client.IgnoreAlreadyExists(err) != nil { + return ctrl.Result{}, fmt.Errorf("failed to create machine deployment upgrade for MachineDeployment %s: %v", md.Name, err) + } + return ctrl.Result{}, nil + } + return ctrl.Result{}, fmt.Errorf("getting machine deployment upgrade for MachineDeployment %s: %v", md.Name, err) + } + if !mdUpgrade.Status.Ready { + return ctrl.Result{}, nil + } + // TODO: update status for templates and other resources + log.Info("Machine deployment upgrade complete, deleting object") + if err := r.client.Delete(ctx, mdUpgrade); err != nil { + return ctrl.Result{}, fmt.Errorf("deleting machine deployment upgrade object: %v", err) + } + + return ctrl.Result{}, nil +} + +func (r *MachineDeploymentInPlaceUpgradeReconciler) reconcileDelete(ctx context.Context, log logr.Logger, md *clusterv1.MachineDeployment) (ctrl.Result, error) { + log.Info("Reconciling MachineDeployment deletion of in place upgrade resources") + mdUpgrade := mdUpgrade(md) + if err := r.client.Get(ctx, GetNamespacedNameType(md.Name, constants.EksaSystemNamespace), mdUpgrade); err != nil { + if apierrors.IsNotFound(err) { + log.Info("Machine deployment in place upgrade object not found, skipping MachineDeployment deletion") + } else { + return ctrl.Result{}, fmt.Errorf("getting machine deployment upgrade for cluster %s: %v", md.Name, err) + } + } else { + log.Info("Deleting machine deployment upgrade", "MachineDeploymentUpgrade", mdUpgradeName(md.Name)) + if err := r.client.Delete(ctx, mdUpgrade); err != nil { + return ctrl.Result{}, fmt.Errorf("deleting machine deployment upgrade object: %v", err) + } + } + + // Remove the finalizer on MachineDeployment object + controllerutil.RemoveFinalizer(md, mdUpgradeFinalizerName) + return ctrl.Result{}, nil +} + +func (r *MachineDeploymentInPlaceUpgradeReconciler) inPlaceUpgradeNeeded(md *clusterv1.MachineDeployment) bool { + return strings.ToLower(md.Annotations[constants.InPlaceUpgradeNeededAnnotation]) == "true" +} + +func mdUpgrade(md *clusterv1.MachineDeployment) *anywherev1.MachineDeploymentUpgrade { + return &anywherev1.MachineDeploymentUpgrade{ + ObjectMeta: metav1.ObjectMeta{ + Name: mdUpgradeName(md.Name), + Namespace: constants.EksaSystemNamespace, + }, + Spec: anywherev1.MachineDeploymentUpgradeSpec{ + MachineDeployment: corev1.ObjectReference{ + Kind: md.Kind, + Namespace: constants.EksaSystemNamespace, + Name: md.Name, + }, + }, + } +} + +func mdUpgradeName(mdName string) string { + return mdName + "-md-upgrade" +} diff --git a/manager/main.go b/manager/main.go index 8cf7ab52849a6..545cf566e8e94 100644 --- a/manager/main.go +++ b/manager/main.go @@ -176,6 +176,8 @@ func setupReconcilers(ctx context.Context, setupLog logr.Logger, mgr ctrl.Manage WithSnowMachineConfigReconciler(). WithNutanixDatacenterReconciler(). WithCloudStackDatacenterReconciler(). + WithKubeadmControlPlaneInPlaceUpgradeReconciler(). + WithMachineDeploymentInPlaceUpgradeReconciler(). WithControlPlaneUpgradeReconciler(). WithMachineDeploymentUpgradeReconciler(). WithNodeUpgradeReconciler() @@ -217,6 +219,18 @@ func setupReconcilers(ctx context.Context, setupLog logr.Logger, mgr ctrl.Manage failed = true } + setupLog.Info("Setting up kubeadmcontrolplaneinplaceupgrade controller") + if err := (reconcilers.KubeadmControlPlaneInPlaceUpgradeReconciler).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "KubeadmControlPlaneInPlaceUpgrade") + failed = true + } + + setupLog.Info("Setting up machinedeploymentinplaceupgrade controller") + if err := (reconcilers.MachineDeploymentInPlaceUpgradeReconciler).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "MachineDeploymentInPlaceUpgrade") + failed = true + } + setupLog.Info("Setting up controlplaneupgrade controller") if err := (reconcilers.ControlPlaneUpgradeReconciler).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", anywherev1.ControlPlaneUpgradeKind) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 605aeff0cfcfc..c5b85e467e004 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -51,6 +51,8 @@ const ( FailureDomainLabelName = "cluster.x-k8s.io/failure-domain" + InPlaceUpgradeNeededAnnotation = "cluster.x-k8s.io/in-place-upgrade-needed" + // CloudstackFailureDomainPlaceholder Provider specific keywork placeholder. CloudstackFailureDomainPlaceholder = "ds.meta_data.failuredomain"