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  }