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  }