volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/pdb/pdb.go (about) 1 /* 2 Copyright 2023 The Volcano 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 pdb 18 19 import ( 20 pdbPolicy "k8s.io/api/policy/v1" 21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 "k8s.io/apimachinery/pkg/labels" 23 "k8s.io/client-go/informers" 24 policylisters "k8s.io/client-go/listers/policy/v1" 25 "k8s.io/klog/v2" 26 27 "volcano.sh/volcano/pkg/scheduler/api" 28 "volcano.sh/volcano/pkg/scheduler/framework" 29 "volcano.sh/volcano/pkg/scheduler/plugins/util" 30 ) 31 32 // PluginName indicates name of volcano scheduler plugin 33 const PluginName = "pdb" 34 35 type pdbPlugin struct { 36 // Arguments given for pdb plugin 37 pluginArguments framework.Arguments 38 // Lister for PodDisruptionBudget 39 lister policylisters.PodDisruptionBudgetLister 40 } 41 42 // New function returns pdb plugin object 43 func New(arguments framework.Arguments) framework.Plugin { 44 return &pdbPlugin{ 45 pluginArguments: arguments, 46 lister: nil, 47 } 48 } 49 50 // Name function returns pdb plugin name 51 func (pp *pdbPlugin) Name() string { 52 return PluginName 53 } 54 55 func (pp *pdbPlugin) OnSessionOpen(ssn *framework.Session) { 56 klog.V(4).Infof("Enter pdb plugin ...") 57 defer klog.V(4).Infof("Leaving pdb plugin.") 58 59 // 0. Init the PDB lister 60 if pp.lister == nil { 61 pp.lister = getPDBLister(ssn.InformerFactory()) 62 } 63 64 // 1. define the func to filter out tasks that violate PDB constraints 65 pdbFilterFn := func(tasks []*api.TaskInfo) []*api.TaskInfo { 66 var victims []*api.TaskInfo 67 68 // (a. get all PDBs 69 pdbs, err := getPodDisruptionBudgets(pp.lister) 70 if err != nil { 71 klog.Errorf("Failed to list pdbs condition: %v", err) 72 return victims 73 } 74 75 // (b. init the pdbsAllowed array 76 pdbsAllowed := make([]int32, len(pdbs)) 77 for i, pdb := range pdbs { 78 pdbsAllowed[i] = pdb.Status.DisruptionsAllowed 79 } 80 81 // (c. range every task to check if it violates the PDB constraints. 82 // If task does not violate the PDB constraints, then add it to victims. 83 for _, task := range tasks { 84 pod := task.Pod 85 pdbForPodIsViolated := false 86 87 // A pod with no labels will not match any PDB. So, no need to check. 88 if len(pod.Labels) == 0 { 89 continue 90 } 91 92 for i, pdb := range pdbs { 93 if pdb.Namespace != pod.Namespace { 94 continue 95 } 96 selector, err := metav1.LabelSelectorAsSelector(pdb.Spec.Selector) 97 if err != nil { 98 continue 99 } 100 // A PDB with a nil or empty selector matches nothing. 101 if selector.Empty() || !selector.Matches(labels.Set(pod.Labels)) { 102 continue 103 } 104 105 // Existing in DisruptedPods means it has been processed in API server, 106 // we don't treat it as a violating case. 107 if _, exist := pdb.Status.DisruptedPods[pod.Name]; exist { 108 continue 109 } 110 // Only decrement the matched pdb when it's not in its <DisruptedPods>; 111 // otherwise we may over-decrement the budget number. 112 pdbsAllowed[i]-- 113 114 if pdbsAllowed[i] < 0 { 115 pdbForPodIsViolated = true 116 } 117 } 118 119 if !pdbForPodIsViolated { 120 victims = append(victims, task) 121 } else { 122 klog.V(4).Infof("The pod <%s> of task <%s> violates the pdb constraint, so filter it from the victim list", task.Name, task.Pod.Name) 123 } 124 } 125 return victims 126 } 127 128 // 2. wrap pdbFilterFn to meet reclaimable and preemptable interface requirements 129 wrappedPdbFilterFn := func(preemptor *api.TaskInfo, preemptees []*api.TaskInfo) ([]*api.TaskInfo, int) { 130 return pdbFilterFn(preemptees), util.Permit 131 } 132 133 // 3. register VictimTasksFns, ReclaimableFn and PreemptableFn 134 victimsFns := []api.VictimTasksFn{pdbFilterFn} 135 ssn.AddVictimTasksFns(pp.Name(), victimsFns) 136 ssn.AddReclaimableFn(pp.Name(), wrappedPdbFilterFn) 137 ssn.AddPreemptableFn(pp.Name(), wrappedPdbFilterFn) 138 } 139 140 func (pp *pdbPlugin) OnSessionClose(ssn *framework.Session) {} 141 142 // getPDBLister returns the lister of PodDisruptionBudget 143 func getPDBLister(informerFactory informers.SharedInformerFactory) policylisters.PodDisruptionBudgetLister { 144 return informerFactory.Policy().V1().PodDisruptionBudgets().Lister() 145 } 146 147 // getPodDisruptionBudgets returns all pdbs 148 func getPodDisruptionBudgets(pdbLister policylisters.PodDisruptionBudgetLister) ([]*pdbPolicy.PodDisruptionBudget, error) { 149 if pdbLister != nil { 150 return pdbLister.List(labels.Everything()) 151 } 152 return nil, nil 153 }