sigs.k8s.io/cluster-api@v1.7.1/util/failuredomains/failure_domains.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 failuredomains implements FailureDomain utility functions.
    18  package failuredomains
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"sort"
    24  
    25  	"k8s.io/utils/ptr"
    26  	ctrl "sigs.k8s.io/controller-runtime"
    27  
    28  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    29  	"sigs.k8s.io/cluster-api/util/collections"
    30  )
    31  
    32  type failureDomainAggregation struct {
    33  	id    string
    34  	count int
    35  }
    36  type failureDomainAggregations []failureDomainAggregation
    37  
    38  // Len is the number of elements in the collection.
    39  func (f failureDomainAggregations) Len() int {
    40  	return len(f)
    41  }
    42  
    43  // Less reports whether the element with
    44  // index i should sort before the element with index j.
    45  func (f failureDomainAggregations) Less(i, j int) bool {
    46  	return f[i].count < f[j].count
    47  }
    48  
    49  // Swap swaps the elements with indexes i and j.
    50  func (f failureDomainAggregations) Swap(i, j int) {
    51  	f[i], f[j] = f[j], f[i]
    52  }
    53  
    54  // PickMost returns a failure domain that is in machines and has most of the group of machines on.
    55  func PickMost(ctx context.Context, failureDomains clusterv1.FailureDomains, groupMachines, machines collections.Machines) *string {
    56  	// orderDescending sorts failure domains according to all machines belonging to the group.
    57  	fds := orderDescending(ctx, failureDomains, groupMachines)
    58  	for _, fd := range fds {
    59  		for _, m := range machines {
    60  			if m.Spec.FailureDomain == nil {
    61  				continue
    62  			}
    63  			if *m.Spec.FailureDomain == fd.id {
    64  				return &fd.id
    65  			}
    66  		}
    67  	}
    68  	return nil
    69  }
    70  
    71  // orderDescending returns the sorted failure domains in decreasing order.
    72  func orderDescending(ctx context.Context, failureDomains clusterv1.FailureDomains, machines collections.Machines) failureDomainAggregations {
    73  	aggregations := pick(ctx, failureDomains, machines)
    74  	if len(aggregations) == 0 {
    75  		return nil
    76  	}
    77  	sort.Sort(sort.Reverse(aggregations))
    78  	return aggregations
    79  }
    80  
    81  // PickFewest returns the failure domain with the fewest number of machines.
    82  func PickFewest(ctx context.Context, failureDomains clusterv1.FailureDomains, machines collections.Machines) *string {
    83  	aggregations := pick(ctx, failureDomains, machines)
    84  	if len(aggregations) == 0 {
    85  		return nil
    86  	}
    87  	sort.Sort(aggregations)
    88  	return ptr.To(aggregations[0].id)
    89  }
    90  
    91  func pick(ctx context.Context, failureDomains clusterv1.FailureDomains, machines collections.Machines) failureDomainAggregations {
    92  	log := ctrl.LoggerFrom(ctx)
    93  
    94  	if len(failureDomains) == 0 {
    95  		return failureDomainAggregations{}
    96  	}
    97  
    98  	counters := map[string]int{}
    99  
   100  	// Initialize the known failure domain keys to find out if an existing machine is in an unsupported failure domain.
   101  	for fd := range failureDomains {
   102  		counters[fd] = 0
   103  	}
   104  
   105  	// Count how many machines are in each failure domain.
   106  	for _, m := range machines {
   107  		if m.Spec.FailureDomain == nil {
   108  			continue
   109  		}
   110  		id := *m.Spec.FailureDomain
   111  		if _, ok := failureDomains[id]; !ok {
   112  			var knownFailureDomains []string
   113  			for failureDomainID := range failureDomains {
   114  				knownFailureDomains = append(knownFailureDomains, failureDomainID)
   115  			}
   116  			log.Info(fmt.Sprintf("Unknown failure domain %q for Machine %s (known failure domains: %v)", id, m.GetName(), knownFailureDomains))
   117  			continue
   118  		}
   119  		counters[id]++
   120  	}
   121  
   122  	aggregations := make(failureDomainAggregations, 0)
   123  
   124  	// Gather up tuples of failure domains ids and counts
   125  	for fd, count := range counters {
   126  		aggregations = append(aggregations, failureDomainAggregation{id: fd, count: count})
   127  	}
   128  
   129  	return aggregations
   130  }