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