sigs.k8s.io/cluster-api@v1.6.3/internal/controllers/machineset/machineset_delete_policy.go (about) 1 /* 2 Copyright 2018 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 machineset 18 19 import ( 20 "math" 21 "sort" 22 23 "github.com/pkg/errors" 24 corev1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 27 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 28 "sigs.k8s.io/cluster-api/util/conditions" 29 ) 30 31 type ( 32 deletePriority float64 33 deletePriorityFunc func(machine *clusterv1.Machine) deletePriority 34 ) 35 36 const ( 37 mustDelete deletePriority = 100.0 38 betterDelete deletePriority = 50.0 39 couldDelete deletePriority = 20.0 40 mustNotDelete deletePriority = 0.0 41 42 secondsPerTenDays float64 = 864000 43 ) 44 45 // maps the creation timestamp onto the 0-100 priority range. 46 func oldestDeletePriority(machine *clusterv1.Machine) deletePriority { 47 if !machine.DeletionTimestamp.IsZero() { 48 return mustDelete 49 } 50 if _, ok := machine.ObjectMeta.Annotations[clusterv1.DeleteMachineAnnotation]; ok { 51 return mustDelete 52 } 53 if !isMachineHealthy(machine) { 54 return mustDelete 55 } 56 if machine.ObjectMeta.CreationTimestamp.Time.IsZero() { 57 return mustNotDelete 58 } 59 d := metav1.Now().Sub(machine.ObjectMeta.CreationTimestamp.Time) 60 if d.Seconds() < 0 { 61 return mustNotDelete 62 } 63 return deletePriority(float64(mustDelete) * (1.0 - math.Exp(-d.Seconds()/secondsPerTenDays))) 64 } 65 66 func newestDeletePriority(machine *clusterv1.Machine) deletePriority { 67 if !machine.DeletionTimestamp.IsZero() { 68 return mustDelete 69 } 70 if _, ok := machine.ObjectMeta.Annotations[clusterv1.DeleteMachineAnnotation]; ok { 71 return mustDelete 72 } 73 if !isMachineHealthy(machine) { 74 return mustDelete 75 } 76 return mustDelete - oldestDeletePriority(machine) 77 } 78 79 func randomDeletePolicy(machine *clusterv1.Machine) deletePriority { 80 if !machine.DeletionTimestamp.IsZero() { 81 return mustDelete 82 } 83 if _, ok := machine.ObjectMeta.Annotations[clusterv1.DeleteMachineAnnotation]; ok { 84 return betterDelete 85 } 86 if !isMachineHealthy(machine) { 87 return betterDelete 88 } 89 return couldDelete 90 } 91 92 type sortableMachines struct { 93 machines []*clusterv1.Machine 94 priority deletePriorityFunc 95 } 96 97 func (m sortableMachines) Len() int { return len(m.machines) } 98 func (m sortableMachines) Swap(i, j int) { m.machines[i], m.machines[j] = m.machines[j], m.machines[i] } 99 func (m sortableMachines) Less(i, j int) bool { 100 priorityI, priorityJ := m.priority(m.machines[i]), m.priority(m.machines[j]) 101 if priorityI == priorityJ { 102 // In cases where the priority is identical, it should be ensured that the same machine order is returned each time. 103 // Ordering by name is a simple way to do this. 104 return m.machines[i].Name < m.machines[j].Name 105 } 106 return priorityJ < priorityI // high to low 107 } 108 109 func getMachinesToDeletePrioritized(filteredMachines []*clusterv1.Machine, diff int, fun deletePriorityFunc) []*clusterv1.Machine { 110 if diff >= len(filteredMachines) { 111 return filteredMachines 112 } else if diff <= 0 { 113 return []*clusterv1.Machine{} 114 } 115 116 sortable := sortableMachines{ 117 machines: filteredMachines, 118 priority: fun, 119 } 120 sort.Sort(sortable) 121 122 return sortable.machines[:diff] 123 } 124 125 func getDeletePriorityFunc(ms *clusterv1.MachineSet) (deletePriorityFunc, error) { 126 // Map the Spec.DeletePolicy value to the appropriate delete priority function 127 switch msdp := clusterv1.MachineSetDeletePolicy(ms.Spec.DeletePolicy); msdp { 128 case clusterv1.RandomMachineSetDeletePolicy: 129 return randomDeletePolicy, nil 130 case clusterv1.NewestMachineSetDeletePolicy: 131 return newestDeletePriority, nil 132 case clusterv1.OldestMachineSetDeletePolicy: 133 return oldestDeletePriority, nil 134 case "": 135 return randomDeletePolicy, nil 136 default: 137 return nil, errors.Errorf("Unsupported delete policy %s. Must be one of 'Random', 'Newest', or 'Oldest'", msdp) 138 } 139 } 140 141 func isMachineHealthy(machine *clusterv1.Machine) bool { 142 if machine.Status.NodeRef == nil { 143 return false 144 } 145 if machine.Status.FailureReason != nil || machine.Status.FailureMessage != nil { 146 return false 147 } 148 nodeHealthyCondition := conditions.Get(machine, clusterv1.MachineNodeHealthyCondition) 149 if nodeHealthyCondition != nil && nodeHealthyCondition.Status != corev1.ConditionTrue { 150 return false 151 } 152 return true 153 }