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 }