sigs.k8s.io/cluster-api@v1.6.3/internal/controllers/topology/cluster/scope/upgradetracker.go (about)

     1  /*
     2  Copyright 2021 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 scope
    18  
    19  import "k8s.io/apimachinery/pkg/util/sets"
    20  
    21  // UpgradeTracker is a helper to capture the upgrade status and make upgrade decisions.
    22  type UpgradeTracker struct {
    23  	ControlPlane       ControlPlaneUpgradeTracker
    24  	MachineDeployments WorkerUpgradeTracker
    25  	MachinePools       WorkerUpgradeTracker
    26  }
    27  
    28  // ControlPlaneUpgradeTracker holds the current upgrade status of the Control Plane.
    29  type ControlPlaneUpgradeTracker struct {
    30  	// IsPendingUpgrade is true if the Control Plane version needs to be updated. False otherwise.
    31  	// If IsPendingUpgrade is true it also means the Control Plane is not going to pick up the new version
    32  	// in the current reconcile loop.
    33  	// Example cases when IsPendingUpgrade is set to true:
    34  	// - Upgrade is blocked by BeforeClusterUpgrade hook
    35  	// - Upgrade is blocked because the current ControlPlane is not stable (provisioning OR scaling OR upgrading)
    36  	// - Upgrade is blocked because any of the current MachineDeployments or MachinePools are upgrading.
    37  	IsPendingUpgrade bool
    38  
    39  	// IsProvisioning is true if the current Control Plane is being provisioned for the first time. False otherwise.
    40  	IsProvisioning bool
    41  
    42  	// IsUpgrading is true if the Control Plane is in the middle of an upgrade.
    43  	// Note: Refer to Control Plane contract for definition of upgrading.
    44  	// IsUpgrading is set to true if the current ControlPlane (ControlPlane at the beginning of the reconcile)
    45  	// is upgrading.
    46  	// Note: IsUpgrading only represents the current ControlPlane state. If the Control Plane is about to pick up the
    47  	// version in the reconcile loop IsUpgrading will not be true, because the current ControlPlane is not upgrading,
    48  	// the desired ControlPlane is.
    49  	// Also look at: IsStartingUpgrade.
    50  	IsUpgrading bool
    51  
    52  	// IsStartingUpgrade is true if the Control Plane is picking up the new version in the current reconcile loop.
    53  	// If IsStartingUpgrade is true it implies that the desired Control Plane version and the current Control Plane
    54  	// versions are different.
    55  	IsStartingUpgrade bool
    56  
    57  	// IsScaling is true if the current Control Plane is scaling. False otherwise.
    58  	// IsScaling only represents the state of the current Control Plane. IsScaling does not represent the state
    59  	// of the desired Control Plane.
    60  	// Example:
    61  	// - IsScaling will be true if the current ControlPlane is scaling.
    62  	// - IsScaling will not be true if the current Control Plane is stable and the reconcile loop is going to scale the Control Plane.
    63  	// Note: Refer to control plane contract for definition of scaling.
    64  	// Note: IsScaling will be false if the Control Plane does not support replicas.
    65  	IsScaling bool
    66  }
    67  
    68  // WorkerUpgradeTracker holds the current upgrade status of MachineDeployments or MachinePools.
    69  type WorkerUpgradeTracker struct {
    70  	// pendingCreateTopologyNames is the set of MachineDeployment/MachinePool topology names that are newly added to the
    71  	// Cluster Topology but will not be created in the current reconcile loop.
    72  	// By marking a MachineDeployment/MachinePool topology as pendingCreate we skip creating the MachineDeployment/MachinePool.
    73  	// Nb. We use MachineDeployment/MachinePool topology names instead of MachineDeployment/MachinePool names because the new
    74  	// MachineDeployment/MachinePool names can keep changing for each reconcile loop leading to continuous updates to the
    75  	// TopologyReconciled condition.
    76  	pendingCreateTopologyNames sets.Set[string]
    77  
    78  	// pendingUpgradeNames is the set of MachineDeployment/MachinePool names that are not going to pick up the new version
    79  	// in the current reconcile loop.
    80  	// By marking a MachineDeployment/MachinePool as pendingUpgrade we skip reconciling the MachineDeployment/MachinePool.
    81  	pendingUpgradeNames sets.Set[string]
    82  
    83  	// deferredNames is the set of MachineDeployment/MachinePool names that are not going to pick up the new version
    84  	// in the current reconcile loop because they are deferred by the user.
    85  	// Note: If a MachineDeployment/MachinePool is marked as deferred it should also be marked as pendingUpgrade.
    86  	deferredNames sets.Set[string]
    87  
    88  	// upgradingNames is the set of MachineDeployment/MachinePool names that are upgrading. This set contains the names of
    89  	// MachineDeployments/MachinePools that are currently upgrading and the names of MachineDeployments/MachinePools that
    90  	// will pick up the upgrade in the current reconcile loop.
    91  	// Note: This information is used to:
    92  	// - decide if ControlPlane can be upgraded.
    93  	// - calculate MachineDeployment/MachinePool upgrade concurrency.
    94  	// - update TopologyReconciled Condition.
    95  	// - decide if the AfterClusterUpgrade hook can be called.
    96  	upgradingNames sets.Set[string]
    97  
    98  	// maxUpgradeConcurrency defines the maximum number of MachineDeployments/MachinePools that should be in an
    99  	// upgrading state. This includes the MachineDeployments/MachinePools that are currently upgrading and the
   100  	// MachineDeployments/MachinePools that will start the upgrade after the current reconcile loop.
   101  	maxUpgradeConcurrency int
   102  }
   103  
   104  // UpgradeTrackerOptions contains the options for NewUpgradeTracker.
   105  type UpgradeTrackerOptions struct {
   106  	maxMDUpgradeConcurrency int
   107  	maxMPUpgradeConcurrency int
   108  }
   109  
   110  // UpgradeTrackerOption returns an option for the NewUpgradeTracker function.
   111  type UpgradeTrackerOption interface {
   112  	ApplyToUpgradeTracker(options *UpgradeTrackerOptions)
   113  }
   114  
   115  // MaxMDUpgradeConcurrency sets the upper limit for the number of Machine Deployments that can upgrade
   116  // concurrently.
   117  type MaxMDUpgradeConcurrency int
   118  
   119  // ApplyToUpgradeTracker applies the given UpgradeTrackerOptions.
   120  func (m MaxMDUpgradeConcurrency) ApplyToUpgradeTracker(options *UpgradeTrackerOptions) {
   121  	options.maxMDUpgradeConcurrency = int(m)
   122  }
   123  
   124  // MaxMPUpgradeConcurrency sets the upper limit for the number of Machine Pools that can upgrade
   125  // concurrently.
   126  type MaxMPUpgradeConcurrency int
   127  
   128  // ApplyToUpgradeTracker applies the given UpgradeTrackerOptions.
   129  func (m MaxMPUpgradeConcurrency) ApplyToUpgradeTracker(options *UpgradeTrackerOptions) {
   130  	options.maxMPUpgradeConcurrency = int(m)
   131  }
   132  
   133  // NewUpgradeTracker returns an upgrade tracker with empty tracking information.
   134  func NewUpgradeTracker(opts ...UpgradeTrackerOption) *UpgradeTracker {
   135  	options := &UpgradeTrackerOptions{}
   136  	for _, o := range opts {
   137  		o.ApplyToUpgradeTracker(options)
   138  	}
   139  	if options.maxMDUpgradeConcurrency < 1 {
   140  		// The concurrency should be at least 1.
   141  		options.maxMDUpgradeConcurrency = 1
   142  	}
   143  	if options.maxMPUpgradeConcurrency < 1 {
   144  		// The concurrency should be at least 1.
   145  		options.maxMPUpgradeConcurrency = 1
   146  	}
   147  	return &UpgradeTracker{
   148  		MachineDeployments: WorkerUpgradeTracker{
   149  			pendingCreateTopologyNames: sets.Set[string]{},
   150  			pendingUpgradeNames:        sets.Set[string]{},
   151  			deferredNames:              sets.Set[string]{},
   152  			upgradingNames:             sets.Set[string]{},
   153  			maxUpgradeConcurrency:      options.maxMDUpgradeConcurrency,
   154  		},
   155  		MachinePools: WorkerUpgradeTracker{
   156  			pendingCreateTopologyNames: sets.Set[string]{},
   157  			pendingUpgradeNames:        sets.Set[string]{},
   158  			deferredNames:              sets.Set[string]{},
   159  			upgradingNames:             sets.Set[string]{},
   160  			maxUpgradeConcurrency:      options.maxMPUpgradeConcurrency,
   161  		},
   162  	}
   163  }
   164  
   165  // MarkUpgrading marks a MachineDeployment/MachinePool as currently upgrading or about to upgrade.
   166  func (m *WorkerUpgradeTracker) MarkUpgrading(names ...string) {
   167  	for _, name := range names {
   168  		m.upgradingNames.Insert(name)
   169  	}
   170  }
   171  
   172  // UpgradingNames returns the list of machine deployments that are upgrading or
   173  // are about to upgrade.
   174  func (m *WorkerUpgradeTracker) UpgradingNames() []string {
   175  	return sets.List(m.upgradingNames)
   176  }
   177  
   178  // UpgradeConcurrencyReached returns true if the number of MachineDeployments/MachinePools upgrading is at the concurrency limit.
   179  func (m *WorkerUpgradeTracker) UpgradeConcurrencyReached() bool {
   180  	return m.upgradingNames.Len() >= m.maxUpgradeConcurrency
   181  }
   182  
   183  // MarkPendingCreate marks a machine deployment topology that is pending to be created.
   184  // This is generally used to capture machine deployments that are yet to be created
   185  // because the control plane is not yet stable.
   186  func (m *WorkerUpgradeTracker) MarkPendingCreate(mdTopologyName string) {
   187  	m.pendingCreateTopologyNames.Insert(mdTopologyName)
   188  }
   189  
   190  // IsPendingCreate returns true is the MachineDeployment/MachinePool topology is marked as pending create.
   191  func (m *WorkerUpgradeTracker) IsPendingCreate(mdTopologyName string) bool {
   192  	return m.pendingCreateTopologyNames.Has(mdTopologyName)
   193  }
   194  
   195  // IsAnyPendingCreate returns true if any of the machine deployments are pending
   196  // to be created. Returns false, otherwise.
   197  func (m *WorkerUpgradeTracker) IsAnyPendingCreate() bool {
   198  	return len(m.pendingCreateTopologyNames) != 0
   199  }
   200  
   201  // PendingCreateTopologyNames returns the list of machine deployment topology names that
   202  // are pending create.
   203  func (m *WorkerUpgradeTracker) PendingCreateTopologyNames() []string {
   204  	return sets.List(m.pendingCreateTopologyNames)
   205  }
   206  
   207  // MarkPendingUpgrade marks a machine deployment as in need of an upgrade.
   208  // This is generally used to capture machine deployments that have not yet
   209  // picked up the topology version.
   210  func (m *WorkerUpgradeTracker) MarkPendingUpgrade(name string) {
   211  	m.pendingUpgradeNames.Insert(name)
   212  }
   213  
   214  // IsPendingUpgrade returns true is the MachineDeployment/MachinePool marked as pending upgrade.
   215  func (m *WorkerUpgradeTracker) IsPendingUpgrade(name string) bool {
   216  	return m.pendingUpgradeNames.Has(name)
   217  }
   218  
   219  // IsAnyPendingUpgrade returns true if any of the machine deployments are pending
   220  // an upgrade. Returns false, otherwise.
   221  func (m *WorkerUpgradeTracker) IsAnyPendingUpgrade() bool {
   222  	return len(m.pendingUpgradeNames) != 0
   223  }
   224  
   225  // PendingUpgradeNames returns the list of machine deployment names that
   226  // are pending an upgrade.
   227  func (m *WorkerUpgradeTracker) PendingUpgradeNames() []string {
   228  	return sets.List(m.pendingUpgradeNames)
   229  }
   230  
   231  // MarkDeferredUpgrade marks that the upgrade for a MachineDeployment/MachinePool
   232  // has been deferred.
   233  func (m *WorkerUpgradeTracker) MarkDeferredUpgrade(name string) {
   234  	m.deferredNames.Insert(name)
   235  }
   236  
   237  // DeferredUpgradeNames returns the list of MachineDeployment/MachinePool names for
   238  // which the upgrade has been deferred.
   239  func (m *WorkerUpgradeTracker) DeferredUpgradeNames() []string {
   240  	return sets.List(m.deferredNames)
   241  }
   242  
   243  // DeferredUpgrade returns true if the upgrade has been deferred for any of the
   244  // MachineDeployments/MachinePools. Returns false, otherwise.
   245  func (m *WorkerUpgradeTracker) DeferredUpgrade() bool {
   246  	return len(m.deferredNames) != 0
   247  }