sigs.k8s.io/cluster-api@v1.7.1/util/collections/machine_filters.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 collections 18 19 import ( 20 "time" 21 22 "github.com/blang/semver/v4" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/labels" 25 "k8s.io/apimachinery/pkg/selection" 26 "sigs.k8s.io/controller-runtime/pkg/client" 27 28 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 29 controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" 30 "sigs.k8s.io/cluster-api/util" 31 "sigs.k8s.io/cluster-api/util/conditions" 32 ) 33 34 // Func is the functon definition for a filter. 35 type Func func(machine *clusterv1.Machine) bool 36 37 // And returns a filter that returns true if all of the given filters returns true. 38 func And(filters ...Func) Func { 39 return func(machine *clusterv1.Machine) bool { 40 for _, f := range filters { 41 if !f(machine) { 42 return false 43 } 44 } 45 return true 46 } 47 } 48 49 // Or returns a filter that returns true if any of the given filters returns true. 50 func Or(filters ...Func) Func { 51 return func(machine *clusterv1.Machine) bool { 52 for _, f := range filters { 53 if f(machine) { 54 return true 55 } 56 } 57 return false 58 } 59 } 60 61 // Not returns a filter that returns the opposite of the given filter. 62 func Not(mf Func) Func { 63 return func(machine *clusterv1.Machine) bool { 64 return !mf(machine) 65 } 66 } 67 68 // HasControllerRef is a filter that returns true if the machine has a controller ref. 69 func HasControllerRef(machine *clusterv1.Machine) bool { 70 if machine == nil { 71 return false 72 } 73 return metav1.GetControllerOf(machine) != nil 74 } 75 76 // InFailureDomains returns a filter to find all machines 77 // in any of the given failure domains. 78 func InFailureDomains(failureDomains ...*string) Func { 79 return func(machine *clusterv1.Machine) bool { 80 if machine == nil { 81 return false 82 } 83 for i := range failureDomains { 84 fd := failureDomains[i] 85 if fd == nil { 86 if fd == machine.Spec.FailureDomain { 87 return true 88 } 89 continue 90 } 91 if machine.Spec.FailureDomain == nil { 92 continue 93 } 94 if *fd == *machine.Spec.FailureDomain { 95 return true 96 } 97 } 98 return false 99 } 100 } 101 102 // OwnedMachines returns a filter to find all machines owned by specified owner. 103 // Usage: GetFilteredMachinesForCluster(ctx, client, cluster, OwnedMachines(controlPlane)). 104 func OwnedMachines(owner client.Object) func(machine *clusterv1.Machine) bool { 105 return func(machine *clusterv1.Machine) bool { 106 if machine == nil { 107 return false 108 } 109 return util.IsOwnedByObject(machine, owner) 110 } 111 } 112 113 // ControlPlaneMachines returns a filter to find all control plane machines for a cluster, regardless of ownership. 114 // Usage: GetFilteredMachinesForCluster(ctx, client, cluster, ControlPlaneMachines(cluster.Name)). 115 func ControlPlaneMachines(clusterName string) func(machine *clusterv1.Machine) bool { 116 selector := ControlPlaneSelectorForCluster(clusterName) 117 return func(machine *clusterv1.Machine) bool { 118 if machine == nil { 119 return false 120 } 121 return selector.Matches(labels.Set(machine.Labels)) 122 } 123 } 124 125 // AdoptableControlPlaneMachines returns a filter to find all un-controlled control plane machines. 126 // Usage: GetFilteredMachinesForCluster(ctx, client, cluster, AdoptableControlPlaneMachines(cluster.Name, controlPlane)). 127 func AdoptableControlPlaneMachines(clusterName string) func(machine *clusterv1.Machine) bool { 128 return And( 129 ControlPlaneMachines(clusterName), 130 Not(HasControllerRef), 131 ) 132 } 133 134 // ActiveMachines returns a filter to find all active machines. 135 // Usage: GetFilteredMachinesForCluster(ctx, client, cluster, ActiveMachines). 136 func ActiveMachines(machine *clusterv1.Machine) bool { 137 if machine == nil { 138 return false 139 } 140 return machine.DeletionTimestamp.IsZero() 141 } 142 143 // HasDeletionTimestamp returns a filter to find all machines that have a deletion timestamp. 144 func HasDeletionTimestamp(machine *clusterv1.Machine) bool { 145 if machine == nil { 146 return false 147 } 148 return !machine.DeletionTimestamp.IsZero() 149 } 150 151 // HasUnhealthyCondition returns a filter to find all machines that have a MachineHealthCheckSucceeded condition set to False, 152 // indicating a problem was detected on the machine, and the MachineOwnerRemediated condition set, indicating that KCP is 153 // responsible of performing remediation as owner of the machine. 154 func HasUnhealthyCondition(machine *clusterv1.Machine) bool { 155 if machine == nil { 156 return false 157 } 158 return conditions.IsFalse(machine, clusterv1.MachineHealthCheckSucceededCondition) && conditions.IsFalse(machine, clusterv1.MachineOwnerRemediatedCondition) 159 } 160 161 // HasUnhealthyControlPlaneComponents returns a filter to find all unhealthy control plane machines that 162 // have any of the following control plane component conditions set to False: 163 // APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy & EtcdMemberHealthy (if using managed etcd). 164 // It is different from the HasUnhealthyCondition func which checks MachineHealthCheck conditions. 165 func HasUnhealthyControlPlaneComponents(isEtcdManaged bool) Func { 166 controlPlaneMachineHealthConditions := []clusterv1.ConditionType{ 167 controlplanev1.MachineAPIServerPodHealthyCondition, 168 controlplanev1.MachineControllerManagerPodHealthyCondition, 169 controlplanev1.MachineSchedulerPodHealthyCondition, 170 } 171 if isEtcdManaged { 172 controlPlaneMachineHealthConditions = append(controlPlaneMachineHealthConditions, 173 controlplanev1.MachineEtcdPodHealthyCondition, 174 controlplanev1.MachineEtcdMemberHealthyCondition, 175 ) 176 } 177 return func(machine *clusterv1.Machine) bool { 178 if machine == nil { 179 return false 180 } 181 182 // The machine without a node could be in failure status due to the kubelet config error, or still provisioning components (including etcd). 183 // So do not treat it as unhealthy. 184 185 for _, condition := range controlPlaneMachineHealthConditions { 186 // Do not return true when the condition is not set or is set to Unknown because 187 // it means a transient state and can not be considered as unhealthy. 188 // preflightCheckCondition() can cover these two cases and skip the scaling up/down. 189 if conditions.IsFalse(machine, condition) { 190 return true 191 } 192 } 193 return false 194 } 195 } 196 197 // IsReady returns a filter to find all machines with the ReadyCondition equals to True. 198 func IsReady() Func { 199 return func(machine *clusterv1.Machine) bool { 200 if machine == nil { 201 return false 202 } 203 return conditions.IsTrue(machine, clusterv1.ReadyCondition) 204 } 205 } 206 207 // ShouldRolloutAfter returns a filter to find all machines where 208 // CreationTimestamp < rolloutAfter < reconciliationTIme. 209 func ShouldRolloutAfter(reconciliationTime, rolloutAfter *metav1.Time) Func { 210 return func(machine *clusterv1.Machine) bool { 211 if machine == nil { 212 return false 213 } 214 if reconciliationTime == nil || rolloutAfter == nil { 215 return false 216 } 217 return machine.CreationTimestamp.Before(rolloutAfter) && rolloutAfter.Before(reconciliationTime) 218 } 219 } 220 221 // ShouldRolloutBefore returns a filter to find all machine whose 222 // certificates will expire within the specified days. 223 func ShouldRolloutBefore(reconciliationTime *metav1.Time, rolloutBefore *controlplanev1.RolloutBefore) Func { 224 return func(machine *clusterv1.Machine) bool { 225 if rolloutBefore == nil || rolloutBefore.CertificatesExpiryDays == nil { 226 return false 227 } 228 if machine == nil || machine.Status.CertificatesExpiryDate == nil { 229 return false 230 } 231 certsExpiryTime := machine.Status.CertificatesExpiryDate.Time 232 return reconciliationTime.Add(time.Duration(*rolloutBefore.CertificatesExpiryDays) * 24 * time.Hour).After(certsExpiryTime) 233 } 234 } 235 236 // HasAnnotationKey returns a filter to find all machines that have the 237 // specified Annotation key present. 238 func HasAnnotationKey(key string) Func { 239 return func(machine *clusterv1.Machine) bool { 240 if machine == nil || machine.Annotations == nil { 241 return false 242 } 243 if _, ok := machine.Annotations[key]; ok { 244 return true 245 } 246 return false 247 } 248 } 249 250 // ControlPlaneSelectorForCluster returns the label selector necessary to get control plane machines for a given cluster. 251 func ControlPlaneSelectorForCluster(clusterName string) labels.Selector { 252 must := func(r *labels.Requirement, err error) labels.Requirement { 253 if err != nil { 254 panic(err) 255 } 256 return *r 257 } 258 return labels.NewSelector().Add( 259 must(labels.NewRequirement(clusterv1.ClusterNameLabel, selection.Equals, []string{clusterName})), 260 must(labels.NewRequirement(clusterv1.MachineControlPlaneLabel, selection.Exists, []string{})), 261 ) 262 } 263 264 // MatchesKubernetesVersion returns a filter to find all machines that match a given Kubernetes version. 265 func MatchesKubernetesVersion(kubernetesVersion string) Func { 266 return func(machine *clusterv1.Machine) bool { 267 if machine == nil { 268 return false 269 } 270 if machine.Spec.Version == nil { 271 return false 272 } 273 return *machine.Spec.Version == kubernetesVersion 274 } 275 } 276 277 // WithVersion returns a filter to find machine that have a non empty and valid version. 278 func WithVersion() Func { 279 return func(machine *clusterv1.Machine) bool { 280 if machine == nil { 281 return false 282 } 283 if machine.Spec.Version == nil { 284 return false 285 } 286 if _, err := semver.ParseTolerant(*machine.Spec.Version); err != nil { 287 return false 288 } 289 return true 290 } 291 } 292 293 // HealthyAPIServer returns a filter to find all machines that have a MachineAPIServerPodHealthyCondition 294 // set to true. 295 func HealthyAPIServer() Func { 296 return func(machine *clusterv1.Machine) bool { 297 if machine == nil { 298 return false 299 } 300 return conditions.IsTrue(machine, controlplanev1.MachineAPIServerPodHealthyCondition) 301 } 302 } 303 304 // HasNode returns a filter to find all machines that have a corresponding Kubernetes node. 305 func HasNode() Func { 306 return func(machine *clusterv1.Machine) bool { 307 if machine == nil { 308 return false 309 } 310 return machine.Status.NodeRef != nil 311 } 312 }