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 }