sigs.k8s.io/cluster-api@v1.7.1/exp/topology/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 // IsControlPlaneStable returns true is the ControlPlane is stable. 166 func (t *ControlPlaneUpgradeTracker) IsControlPlaneStable() bool { 167 // If the current control plane is upgrading it is not considered stable. 168 if t.IsUpgrading { 169 return false 170 } 171 172 // If control plane supports replicas, check if the control plane is in the middle of a scale operation. 173 // If the current control plane is scaling then it is not considered stable. 174 if t.IsScaling { 175 return false 176 } 177 178 // Check if we are about to upgrade the control plane. Since the control plane is about to start its upgrade process 179 // it cannot be considered stable. 180 if t.IsStartingUpgrade { 181 return false 182 } 183 184 // If the ControlPlane is pending picking up an upgrade then it is not yet at the desired state and 185 // cannot be considered stable. 186 if t.IsPendingUpgrade { 187 return false 188 } 189 190 return true 191 } 192 193 // MarkUpgrading marks a MachineDeployment/MachinePool as currently upgrading or about to upgrade. 194 func (m *WorkerUpgradeTracker) MarkUpgrading(names ...string) { 195 for _, name := range names { 196 m.upgradingNames.Insert(name) 197 } 198 } 199 200 // UpgradingNames returns the list of machine deployments that are upgrading or 201 // are about to upgrade. 202 func (m *WorkerUpgradeTracker) UpgradingNames() []string { 203 return sets.List(m.upgradingNames) 204 } 205 206 // UpgradeConcurrencyReached returns true if the number of MachineDeployments/MachinePools upgrading is at the concurrency limit. 207 func (m *WorkerUpgradeTracker) UpgradeConcurrencyReached() bool { 208 return m.upgradingNames.Len() >= m.maxUpgradeConcurrency 209 } 210 211 // MarkPendingCreate marks a machine deployment topology that is pending to be created. 212 // This is generally used to capture machine deployments that are yet to be created 213 // because the control plane is not yet stable. 214 func (m *WorkerUpgradeTracker) MarkPendingCreate(mdTopologyName string) { 215 m.pendingCreateTopologyNames.Insert(mdTopologyName) 216 } 217 218 // IsPendingCreate returns true is the MachineDeployment/MachinePool topology is marked as pending create. 219 func (m *WorkerUpgradeTracker) IsPendingCreate(mdTopologyName string) bool { 220 return m.pendingCreateTopologyNames.Has(mdTopologyName) 221 } 222 223 // IsAnyPendingCreate returns true if any of the machine deployments are pending 224 // to be created. Returns false, otherwise. 225 func (m *WorkerUpgradeTracker) IsAnyPendingCreate() bool { 226 return len(m.pendingCreateTopologyNames) != 0 227 } 228 229 // PendingCreateTopologyNames returns the list of machine deployment topology names that 230 // are pending create. 231 func (m *WorkerUpgradeTracker) PendingCreateTopologyNames() []string { 232 return sets.List(m.pendingCreateTopologyNames) 233 } 234 235 // MarkPendingUpgrade marks a machine deployment as in need of an upgrade. 236 // This is generally used to capture machine deployments that have not yet 237 // picked up the topology version. 238 func (m *WorkerUpgradeTracker) MarkPendingUpgrade(name string) { 239 m.pendingUpgradeNames.Insert(name) 240 } 241 242 // IsPendingUpgrade returns true is the MachineDeployment/MachinePool marked as pending upgrade. 243 func (m *WorkerUpgradeTracker) IsPendingUpgrade(name string) bool { 244 return m.pendingUpgradeNames.Has(name) 245 } 246 247 // IsAnyPendingUpgrade returns true if any of the machine deployments are pending 248 // an upgrade. Returns false, otherwise. 249 func (m *WorkerUpgradeTracker) IsAnyPendingUpgrade() bool { 250 return len(m.pendingUpgradeNames) != 0 251 } 252 253 // PendingUpgradeNames returns the list of machine deployment names that 254 // are pending an upgrade. 255 func (m *WorkerUpgradeTracker) PendingUpgradeNames() []string { 256 return sets.List(m.pendingUpgradeNames) 257 } 258 259 // MarkDeferredUpgrade marks that the upgrade for a MachineDeployment/MachinePool 260 // has been deferred. 261 func (m *WorkerUpgradeTracker) MarkDeferredUpgrade(name string) { 262 m.deferredNames.Insert(name) 263 } 264 265 // DeferredUpgradeNames returns the list of MachineDeployment/MachinePool names for 266 // which the upgrade has been deferred. 267 func (m *WorkerUpgradeTracker) DeferredUpgradeNames() []string { 268 return sets.List(m.deferredNames) 269 } 270 271 // DeferredUpgrade returns true if the upgrade has been deferred for any of the 272 // MachineDeployments/MachinePools. Returns false, otherwise. 273 func (m *WorkerUpgradeTracker) DeferredUpgrade() bool { 274 return len(m.deferredNames) != 0 275 }