sigs.k8s.io/cluster-api@v1.7.1/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  	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
    27  	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
    28  	"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal"
    29  	"sigs.k8s.io/cluster-api/util"
    30  	"sigs.k8s.io/cluster-api/util/collections"
    31  	"sigs.k8s.io/cluster-api/util/version"
    32  )
    33  
    34  func (r *KubeadmControlPlaneReconciler) upgradeControlPlane(
    35  	ctx context.Context,
    36  	controlPlane *internal.ControlPlane,
    37  	machinesRequireUpgrade collections.Machines,
    38  ) (ctrl.Result, error) {
    39  	logger := ctrl.LoggerFrom(ctx)
    40  
    41  	if controlPlane.KCP.Spec.RolloutStrategy == nil || controlPlane.KCP.Spec.RolloutStrategy.RollingUpdate == nil {
    42  		return ctrl.Result{}, errors.New("rolloutStrategy is not set")
    43  	}
    44  
    45  	// TODO: handle reconciliation of etcd members and kubeadm config in case they get out of sync with cluster
    46  
    47  	workloadCluster, err := controlPlane.GetWorkloadCluster(ctx)
    48  	if err != nil {
    49  		logger.Error(err, "failed to get remote client for workload cluster", "cluster key", util.ObjectKey(controlPlane.Cluster))
    50  		return ctrl.Result{}, err
    51  	}
    52  
    53  	parsedVersion, err := semver.ParseTolerant(controlPlane.KCP.Spec.Version)
    54  	if err != nil {
    55  		return ctrl.Result{}, errors.Wrapf(err, "failed to parse kubernetes version %q", controlPlane.KCP.Spec.Version)
    56  	}
    57  
    58  	if err := workloadCluster.ReconcileKubeletRBACRole(ctx, parsedVersion); err != nil {
    59  		return ctrl.Result{}, errors.Wrap(err, "failed to reconcile the remote kubelet RBAC role")
    60  	}
    61  
    62  	if err := workloadCluster.ReconcileKubeletRBACBinding(ctx, parsedVersion); err != nil {
    63  		return ctrl.Result{}, errors.Wrap(err, "failed to reconcile the remote kubelet RBAC binding")
    64  	}
    65  
    66  	// Ensure kubeadm cluster role  & bindings for v1.18+
    67  	// as per https://github.com/kubernetes/kubernetes/commit/b117a928a6c3f650931bdac02a41fca6680548c4
    68  	if err := workloadCluster.AllowBootstrapTokensToGetNodes(ctx); err != nil {
    69  		return ctrl.Result{}, errors.Wrap(err, "failed to set role and role binding for kubeadm")
    70  	}
    71  
    72  	// Ensure kubeadm clusterRoleBinding for v1.29+ as per https://github.com/kubernetes/kubernetes/pull/121305
    73  	if err := workloadCluster.AllowClusterAdminPermissions(ctx, parsedVersion); err != nil {
    74  		return ctrl.Result{}, errors.Wrap(err, "failed to set cluster-admin ClusterRoleBinding for kubeadm")
    75  	}
    76  
    77  	kubeadmCMMutators := make([]func(*bootstrapv1.ClusterConfiguration), 0)
    78  	kubeadmCMMutators = append(kubeadmCMMutators, workloadCluster.UpdateKubernetesVersionInKubeadmConfigMap(parsedVersion))
    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  
    88  		// Get the imageRepository or the correct value if nothing is set and a migration is necessary.
    89  		imageRepository := internal.ImageRepositoryFromClusterConfig(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration, parsedVersionTolerant)
    90  
    91  		kubeadmCMMutators = append(kubeadmCMMutators,
    92  			workloadCluster.UpdateImageRepositoryInKubeadmConfigMap(imageRepository),
    93  			workloadCluster.UpdateFeatureGatesInKubeadmConfigMap(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.FeatureGates),
    94  			workloadCluster.UpdateAPIServerInKubeadmConfigMap(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer),
    95  			workloadCluster.UpdateControllerManagerInKubeadmConfigMap(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.ControllerManager),
    96  			workloadCluster.UpdateSchedulerInKubeadmConfigMap(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Scheduler))
    97  
    98  		// Etcd local and external are mutually exclusive and they cannot be switched, once set.
    99  		if controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil {
   100  			kubeadmCMMutators = append(kubeadmCMMutators,
   101  				workloadCluster.UpdateEtcdLocalInKubeadmConfigMap(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local))
   102  		} else {
   103  			kubeadmCMMutators = append(kubeadmCMMutators,
   104  				workloadCluster.UpdateEtcdExternalInKubeadmConfigMap(controlPlane.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.External))
   105  		}
   106  	}
   107  
   108  	// collectively update Kubeadm config map
   109  	if err = workloadCluster.UpdateClusterConfiguration(ctx, parsedVersion, kubeadmCMMutators...); err != nil {
   110  		return ctrl.Result{}, err
   111  	}
   112  
   113  	if err := workloadCluster.UpdateKubeletConfigMap(ctx, parsedVersion); err != nil {
   114  		return ctrl.Result{}, errors.Wrap(err, "failed to upgrade kubelet config map")
   115  	}
   116  
   117  	switch controlPlane.KCP.Spec.RolloutStrategy.Type {
   118  	case controlplanev1.RollingUpdateStrategyType:
   119  		// RolloutStrategy is currently defaulted and validated to be RollingUpdate
   120  		// We can ignore MaxUnavailable because we are enforcing health checks before we get here.
   121  		maxNodes := *controlPlane.KCP.Spec.Replicas + int32(controlPlane.KCP.Spec.RolloutStrategy.RollingUpdate.MaxSurge.IntValue())
   122  		if int32(controlPlane.Machines.Len()) < maxNodes {
   123  			// scaleUp ensures that we don't continue scaling up while waiting for Machines to have NodeRefs
   124  			return r.scaleUpControlPlane(ctx, controlPlane)
   125  		}
   126  		return r.scaleDownControlPlane(ctx, controlPlane, machinesRequireUpgrade)
   127  	default:
   128  		logger.Info("RolloutStrategy type is not set to RollingUpdateStrategyType, unable to determine the strategy for rolling out machines")
   129  		return ctrl.Result{}, nil
   130  	}
   131  }