sigs.k8s.io/cluster-api@v1.6.3/controlplane/kubeadm/internal/controllers/upgrade.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/blang/semver/v4"
    23  	"github.com/pkg/errors"
    24  	ctrl "sigs.k8s.io/controller-runtime"
    25  
    26  	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
    27  	"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal"
    28  	"sigs.k8s.io/cluster-api/util"
    29  	"sigs.k8s.io/cluster-api/util/collections"
    30  	"sigs.k8s.io/cluster-api/util/version"
    31  )
    32  
    33  func (r *KubeadmControlPlaneReconciler) upgradeControlPlane(
    34  	ctx context.Context,
    35  	controlPlane *internal.ControlPlane,
    36  	machinesRequireUpgrade collections.Machines,
    37  ) (ctrl.Result, error) {
    38  	logger := ctrl.LoggerFrom(ctx)
    39  
    40  	if controlPlane.KCP.Spec.RolloutStrategy == nil || controlPlane.KCP.Spec.RolloutStrategy.RollingUpdate == nil {
    41  		return ctrl.Result{}, errors.New("rolloutStrategy is not set")
    42  	}
    43  
    44  	// TODO: handle reconciliation of etcd members and kubeadm config in case they get out of sync with cluster
    45  
    46  	workloadCluster, err := controlPlane.GetWorkloadCluster(ctx)
    47  	if err != nil {
    48  		logger.Error(err, "failed to get remote client for workload cluster", "cluster key", util.ObjectKey(controlPlane.Cluster))
    49  		return ctrl.Result{}, err
    50  	}
    51  
    52  	parsedVersion, err := semver.ParseTolerant(controlPlane.KCP.Spec.Version)
    53  	if err != nil {
    54  		return ctrl.Result{}, errors.Wrapf(err, "failed to parse kubernetes version %q", controlPlane.KCP.Spec.Version)
    55  	}
    56  
    57  	if err := workloadCluster.ReconcileKubeletRBACRole(ctx, parsedVersion); err != nil {
    58  		return ctrl.Result{}, errors.Wrap(err, "failed to reconcile the remote kubelet RBAC role")
    59  	}
    60  
    61  	if err := workloadCluster.ReconcileKubeletRBACBinding(ctx, parsedVersion); err != nil {
    62  		return ctrl.Result{}, errors.Wrap(err, "failed to reconcile the remote kubelet RBAC binding")
    63  	}
    64  
    65  	// Ensure kubeadm cluster role  & bindings for v1.18+
    66  	// as per https://github.com/kubernetes/kubernetes/commit/b117a928a6c3f650931bdac02a41fca6680548c4
    67  	if err := workloadCluster.AllowBootstrapTokensToGetNodes(ctx); err != nil {
    68  		return ctrl.Result{}, errors.Wrap(err, "failed to set role and role binding for kubeadm")
    69  	}
    70  
    71  	// Ensure kubeadm clusterRoleBinding for v1.29+ as per https://github.com/kubernetes/kubernetes/pull/121305
    72  	if err := workloadCluster.AllowClusterAdminPermissions(ctx, parsedVersion); err != nil {
    73  		return ctrl.Result{}, errors.Wrap(err, "failed to set cluster-admin ClusterRoleBinding for kubeadm")
    74  	}
    75  
    76  	if err := workloadCluster.UpdateKubernetesVersionInKubeadmConfigMap(ctx, parsedVersion); err != nil {
    77  		return ctrl.Result{}, errors.Wrap(err, "failed to update the kubernetes version in the kubeadm config map")
    78  	}
    79  
    80  	if controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration != nil {
    81  		// We intentionally only parse major/minor/patch so that the subsequent code
    82  		// also already applies to beta versions of new releases.
    83  		parsedVersionTolerant, err := version.ParseMajorMinorPatchTolerant(controlPlane.KCP.Spec.Version)
    84  		if err != nil {
    85  			return ctrl.Result{}, errors.Wrapf(err, "failed to parse kubernetes version %q", controlPlane.KCP.Spec.Version)
    86  		}
    87  		// Get the imageRepository or the correct value if nothing is set and a migration is necessary.
    88  		imageRepository := internal.ImageRepositoryFromClusterConfig(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration, parsedVersionTolerant)
    89  
    90  		if err := workloadCluster.UpdateImageRepositoryInKubeadmConfigMap(ctx, imageRepository, parsedVersion); err != nil {
    91  			return ctrl.Result{}, errors.Wrap(err, "failed to update the image repository in the kubeadm config map")
    92  		}
    93  	}
    94  
    95  	if controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration != nil && controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil {
    96  		meta := controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ImageMeta
    97  		if err := workloadCluster.UpdateEtcdVersionInKubeadmConfigMap(ctx, meta.ImageRepository, meta.ImageTag, parsedVersion); err != nil {
    98  			return ctrl.Result{}, errors.Wrap(err, "failed to update the etcd version in the kubeadm config map")
    99  		}
   100  
   101  		extraArgs := controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ExtraArgs
   102  		if err := workloadCluster.UpdateEtcdExtraArgsInKubeadmConfigMap(ctx, extraArgs, parsedVersion); err != nil {
   103  			return ctrl.Result{}, errors.Wrap(err, "failed to update the etcd extra args in the kubeadm config map")
   104  		}
   105  	}
   106  
   107  	if controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration != nil {
   108  		if err := workloadCluster.UpdateAPIServerInKubeadmConfigMap(ctx, controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer, parsedVersion); err != nil {
   109  			return ctrl.Result{}, errors.Wrap(err, "failed to update api server in the kubeadm config map")
   110  		}
   111  
   112  		if err := workloadCluster.UpdateControllerManagerInKubeadmConfigMap(ctx, controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.ControllerManager, parsedVersion); err != nil {
   113  			return ctrl.Result{}, errors.Wrap(err, "failed to update controller manager in the kubeadm config map")
   114  		}
   115  
   116  		if err := workloadCluster.UpdateSchedulerInKubeadmConfigMap(ctx, controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Scheduler, parsedVersion); err != nil {
   117  			return ctrl.Result{}, errors.Wrap(err, "failed to update scheduler in the kubeadm config map")
   118  		}
   119  	}
   120  
   121  	if err := workloadCluster.UpdateKubeletConfigMap(ctx, parsedVersion); err != nil {
   122  		return ctrl.Result{}, errors.Wrap(err, "failed to upgrade kubelet config map")
   123  	}
   124  
   125  	switch controlPlane.KCP.Spec.RolloutStrategy.Type {
   126  	case controlplanev1.RollingUpdateStrategyType:
   127  		// RolloutStrategy is currently defaulted and validated to be RollingUpdate
   128  		// We can ignore MaxUnavailable because we are enforcing health checks before we get here.
   129  		maxNodes := *controlPlane.KCP.Spec.Replicas + int32(controlPlane.KCP.Spec.RolloutStrategy.RollingUpdate.MaxSurge.IntValue())
   130  		if int32(controlPlane.Machines.Len()) < maxNodes {
   131  			// scaleUp ensures that we don't continue scaling up while waiting for Machines to have NodeRefs
   132  			return r.scaleUpControlPlane(ctx, controlPlane)
   133  		}
   134  		return r.scaleDownControlPlane(ctx, controlPlane, machinesRequireUpgrade)
   135  	default:
   136  		logger.Info("RolloutStrategy type is not set to RollingUpdateStrategyType, unable to determine the strategy for rolling out machines")
   137  		return ctrl.Result{}, nil
   138  	}
   139  }