Skip to content

Commit

Permalink
Add in-place upgrade controllers for kcp and md
Browse files Browse the repository at this point in the history
  • Loading branch information
vivek-koppuru committed Jan 20, 2024
1 parent 5a474bf commit 0ad288e
Show file tree
Hide file tree
Showing 5 changed files with 466 additions and 10 deletions.
56 changes: 46 additions & 10 deletions controllers/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -590,6 +592,40 @@ func (f *Factory) withMachineHealthCheckReconciler() *Factory {
return f
}

// WithControlPlaneUpgradeReconciler builds the KubeadmControlPlaneInPlaceUpgrade reconciler.

Check warning on line 595 in controllers/factory.go

View workflow job for this annotation

GitHub Actions / lint

exported: comment on exported method Factory.WithKubeadmControlPlaneInPlaceUpgradeReconciler should be of the form "WithKubeadmControlPlaneInPlaceUpgradeReconciler ..." (revive)
func (f *Factory) WithKubeadmControlPlaneInPlaceUpgradeReconciler() *Factory {
f.buildSteps = append(f.buildSteps, func(ctx context.Context) error {
if f.reconcilers.KubeadmControlPlaneInPlaceUpgradeReconciler != nil {
return nil
}

Check warning on line 600 in controllers/factory.go

View check run for this annotation

Codecov / codecov/patch

controllers/factory.go#L596-L600

Added lines #L596 - L600 were not covered by tests

f.reconcilers.KubeadmControlPlaneInPlaceUpgradeReconciler = NewKubeadmControlPlaneInPlaceUpgradeReconciler(
f.manager.GetClient(),
)

return nil

Check warning on line 606 in controllers/factory.go

View check run for this annotation

Codecov / codecov/patch

controllers/factory.go#L602-L606

Added lines #L602 - L606 were not covered by tests
})

return f

Check warning on line 609 in controllers/factory.go

View check run for this annotation

Codecov / codecov/patch

controllers/factory.go#L609

Added line #L609 was not covered by tests
}

// WithMachineDeploymentUpgradeReconciler builds the MachineDeploymentInPlace reconciler.

Check warning on line 612 in controllers/factory.go

View workflow job for this annotation

GitHub Actions / lint

exported: comment on exported method Factory.WithMachineDeploymentInPlaceUpgradeReconciler should be of the form "WithMachineDeploymentInPlaceUpgradeReconciler ..." (revive)
func (f *Factory) WithMachineDeploymentInPlaceUpgradeReconciler() *Factory {
f.buildSteps = append(f.buildSteps, func(ctx context.Context) error {
if f.reconcilers.MachineDeploymentInPlaceUpgradeReconciler != nil {
return nil
}

Check warning on line 617 in controllers/factory.go

View check run for this annotation

Codecov / codecov/patch

controllers/factory.go#L613-L617

Added lines #L613 - L617 were not covered by tests

f.reconcilers.MachineDeploymentInPlaceUpgradeReconciler = NewMachineDeploymentInPlaceUpgradeReconciler(
f.manager.GetClient(),
)

return nil

Check warning on line 623 in controllers/factory.go

View check run for this annotation

Codecov / codecov/patch

controllers/factory.go#L619-L623

Added lines #L619 - L623 were not covered by tests
})

return f

Check warning on line 626 in controllers/factory.go

View check run for this annotation

Codecov / codecov/patch

controllers/factory.go#L626

Added line #L626 was not covered by tests
}

// WithControlPlaneUpgradeReconciler builds the ControlPlaneUpgrade reconciler.
func (f *Factory) WithControlPlaneUpgradeReconciler() *Factory {
f.buildSteps = append(f.buildSteps, func(ctx context.Context) error {
Expand Down
219 changes: 219 additions & 0 deletions controllers/kubeadmcontrolplane_inplaceupgrade_controller.go
Original file line number Diff line number Diff line change
@@ -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.

Check warning on line 48 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View workflow job for this annotation

GitHub Actions / lint

exported: comment on exported function NewKubeadmControlPlaneInPlaceUpgradeReconciler should be of the form "NewKubeadmControlPlaneInPlaceUpgradeReconciler ..." (revive)
func NewKubeadmControlPlaneInPlaceUpgradeReconciler(client client.Client) *KubeadmControlPlaneInPlaceUpgradeReconciler {
return &KubeadmControlPlaneInPlaceUpgradeReconciler{
client: client,
log: ctrl.Log.WithName("KubeadmControlPlaneInPlaceUpgradeController"),
}

Check warning on line 53 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L49-L53

Added lines #L49 - L53 were not covered by tests
}

//+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) {

Check failure on line 60 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View workflow job for this annotation

GitHub Actions / lint

cyclomatic complexity 11 of func `(*KubeadmControlPlaneInPlaceUpgradeReconciler).Reconcile` is high (> 10) (gocyclo)
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

Check warning on line 69 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L60-L69

Added lines #L60 - L69 were not covered by tests
}

if !r.inPlaceUpgradeNeeded(kcp) {
log.Info("In place upgraded needed annotation not detected, nothing to do")
return ctrl.Result{}, nil
}

Check warning on line 75 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L72-L75

Added lines #L72 - L75 were not covered by tests

patchHelper, err := patch.NewHelper(kcp, r.client)
if err != nil {
return ctrl.Result{}, err
}

Check warning on line 80 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L77-L80

Added lines #L77 - L80 were not covered by tests

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})
}

Check warning on line 86 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L82-L86

Added lines #L82 - L86 were not covered by tests

// 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}
}

Check warning on line 93 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L91-L93

Added lines #L91 - L93 were not covered by tests
}()

// Reconcile the KubeadmControlPlane deletion if the DeletionTimestamp is set.
if !kcp.DeletionTimestamp.IsZero() {
return r.reconcileDelete(ctx, log, kcp)
}

Check warning on line 99 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L97-L99

Added lines #L97 - L99 were not covered by tests

// AddFinalizer is idempotent
controllerutil.AddFinalizer(kcp, controlPlaneUpgradeFinalizerName)

return r.reconcile(ctx, log, kcp)

Check warning on line 104 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L102-L104

Added lines #L102 - L104 were not covered by tests
}

// 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)

Check warning on line 111 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L108-L111

Added lines #L108 - L111 were not covered by tests
}

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

Check warning on line 133 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L114-L133

Added lines #L114 - L133 were not covered by tests
}
return ctrl.Result{}, fmt.Errorf("getting control plane upgrade for KubeadmControlPlane %s: %v", kcp.Name, err)

Check warning on line 135 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L135

Added line #L135 was not covered by tests
}
if !cpUpgrade.Status.Ready {
return ctrl.Result{}, nil
}

Check warning on line 139 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L137-L139

Added lines #L137 - L139 were not covered by tests
// 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)
}

Check warning on line 144 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L141-L144

Added lines #L141 - L144 were not covered by tests

return ctrl.Result{}, nil

Check warning on line 146 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L146

Added line #L146 was not covered by tests
}

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)
}

Check warning on line 162 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L149-L162

Added lines #L149 - L162 were not covered by tests
}

// Remove the finalizer on KubeadmControlPlane object
controllerutil.RemoveFinalizer(kcp, controlPlaneUpgradeFinalizerName)
return ctrl.Result{}, nil

Check warning on line 167 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L166-L167

Added lines #L166 - L167 were not covered by tests
}

func (r *KubeadmControlPlaneInPlaceUpgradeReconciler) inPlaceUpgradeNeeded(kcp *controlplanev1.KubeadmControlPlane) bool {
_, ok := kcp.Annotations["controlplane.clusters.x-k8s.io/in-place-upgrade-needed"]
return ok

Check warning on line 172 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L170-L172

Added lines #L170 - L172 were not covered by tests
}

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

Check warning on line 195 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L175-L195

Added lines #L175 - L195 were not covered by tests
}

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,
},
}

Check warning on line 214 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L198-L214

Added lines #L198 - L214 were not covered by tests
}

func cpUpgradeName(kcpName string) string {
return kcpName + "-cp-upgrade"

Check warning on line 218 in controllers/kubeadmcontrolplane_inplaceupgrade_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/kubeadmcontrolplane_inplaceupgrade_controller.go#L217-L218

Added lines #L217 - L218 were not covered by tests
}
Loading

0 comments on commit 0ad288e

Please sign in to comment.