k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/util/controlled_pods_indexer.go (about) 1 /* 2 Copyright 2022 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 util 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 24 appsv1 "k8s.io/api/apps/v1" 25 corev1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/api/meta" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/types" 29 appsinformers "k8s.io/client-go/informers/apps/v1" 30 coreinformers "k8s.io/client-go/informers/core/v1" 31 "k8s.io/client-go/tools/cache" 32 "k8s.io/klog/v2" 33 ) 34 35 const ( 36 controllerUIDIndex = "controllerUID" 37 ) 38 39 // ControlledPodsIndexer is able to efficiently find pods with ownerReference pointing a given controller object. 40 // For Deployments, it performs indirect lookup with ReplicaSets in the middle. 41 type ControlledPodsIndexer struct { 42 podsIndexer cache.Indexer 43 podsSynced cache.InformerSynced 44 rsSynced cache.InformerSynced 45 46 rsIndexer cache.Indexer 47 48 // lock is a lock for accessing rsPendingDeletion. 49 lock sync.Mutex 50 // rsPendingDeletion are replicasets that have been deleted, but there are still pods referencing them, 51 // so we have to postpone deletion from `rsIndexer`. They should be deleted as soon as the last pod 52 // referencing it is deleted. 53 rsPendingDeletion map[types.UID]bool 54 } 55 56 // ReplicaSetState stores information relevant to a specific ReplicaSet object, 57 // i.e. how many pods it owns exist, whether the RS object itself exists 58 // and its latest known owner's UID. 59 type ReplicaSetState struct { 60 NumPods int 61 Exists bool 62 } 63 64 // UIDSet is a collection of ReplicaSet objects UIDs. 65 type UIDSet map[types.UID]bool 66 67 func deletionHandlingUIDKeyFunc(obj interface{}) (string, error) { 68 if d, ok := obj.(cache.DeletedFinalStateUnknown); ok { 69 return d.Key, nil 70 } 71 return string(getObjUID(obj)), nil 72 } 73 74 // NewControlledPodsIndexer creates a new ControlledPodsIndexer instance. 75 func NewControlledPodsIndexer(podsInformer coreinformers.PodInformer, rsInformer appsinformers.ReplicaSetInformer) (*ControlledPodsIndexer, error) { 76 if err := podsInformer.Informer().AddIndexers(cache.Indexers{controllerUIDIndex: controllerUIDIndexFunc}); err != nil { 77 return nil, fmt.Errorf("failed to register indexer: %w", err) 78 } 79 80 // We need a separate storage from rsInformer as we postpone deletion until all pods are removed. 81 rsIndexer := cache.NewIndexer(deletionHandlingUIDKeyFunc, cache.Indexers{controllerUIDIndex: controllerUIDIndexFunc}) 82 83 cpi := &ControlledPodsIndexer{ 84 podsIndexer: podsInformer.Informer().GetIndexer(), 85 podsSynced: podsInformer.Informer().HasSynced, 86 rsIndexer: rsIndexer, 87 rsSynced: rsInformer.Informer().HasSynced, 88 rsPendingDeletion: make(map[types.UID]bool), 89 } 90 91 podsInformer.Informer().AddEventHandler( 92 cache.ResourceEventHandlerFuncs{ 93 UpdateFunc: func(oldObj, newObj interface{}) { 94 oldOwnerUID, _ := getControllerInfo(oldObj) 95 newOwnerUID, _ := getControllerInfo(newObj) 96 if oldOwnerUID == newOwnerUID { 97 return 98 } 99 100 cpi.lock.Lock() 101 defer cpi.lock.Unlock() 102 if err := cpi.clearRSDataIfPossibleLocked(oldOwnerUID); err != nil { 103 klog.Errorf("error while deleting %v: %v", oldOwnerUID, err) 104 } 105 }, 106 DeleteFunc: func(obj interface{}) { 107 ownerUID, _ := getControllerInfo(obj) 108 109 cpi.lock.Lock() 110 defer cpi.lock.Unlock() 111 if err := cpi.clearRSDataIfPossibleLocked(ownerUID); err != nil { 112 klog.Errorf("error while deleting %v: %v", ownerUID, err) 113 } 114 }, 115 }, 116 ) 117 rsInformer.Informer().AddEventHandler( 118 cache.ResourceEventHandlerFuncs{ 119 AddFunc: func(obj interface{}) { 120 if err := rsIndexer.Add(obj); err != nil { 121 klog.Errorf("error while adding %v: %v", obj, err) 122 } 123 }, 124 UpdateFunc: func(_, newObj interface{}) { 125 if err := rsIndexer.Update(newObj); err != nil { 126 klog.Errorf("error while updating %v: %v", newObj, err) 127 } 128 }, 129 DeleteFunc: func(obj interface{}) { 130 rsUID := getObjUID(obj) 131 cpi.lock.Lock() 132 defer cpi.lock.Unlock() 133 cpi.rsPendingDeletion[rsUID] = true 134 if err := cpi.clearRSDataIfPossibleLocked(rsUID); err != nil { 135 klog.Errorf("error while deleting %v: %v", rsUID, err) 136 } 137 }, 138 }, 139 ) 140 141 return cpi, nil 142 } 143 144 func getControllerInfo(obj interface{}) (types.UID, string) { 145 metaAccessor, err := meta.Accessor(obj) 146 if err != nil { 147 return "", "" 148 } 149 controller := metav1.GetControllerOf(metaAccessor) 150 if controller == nil { 151 return "", "" 152 } 153 return controller.UID, controller.Kind 154 } 155 156 func getObjUID(obj interface{}) types.UID { 157 metaAccessor, err := meta.Accessor(obj) 158 if err != nil { 159 return "" 160 } 161 return metaAccessor.GetUID() 162 } 163 164 func (p *ControlledPodsIndexer) clearRSDataIfPossibleLocked(rsUID types.UID) error { 165 if !p.rsPendingDeletion[rsUID] { 166 return nil 167 } 168 pods, err := p.appendPodsControlledBy(nil, rsUID) 169 if err != nil { 170 return fmt.Errorf("failed to list pods for %q: %w", rsUID, err) 171 } 172 173 if len(pods) != 0 { 174 return nil 175 } 176 delete(p.rsPendingDeletion, rsUID) 177 obj, exists, err := p.rsIndexer.GetByKey(string(rsUID)) 178 if err != nil { 179 return err 180 } 181 if !exists { 182 return nil 183 } 184 185 return p.rsIndexer.Delete(obj) 186 } 187 188 func controllerUIDIndexFunc(obj interface{}) ([]string, error) { 189 meta, err := meta.Accessor(obj) 190 if err != nil { 191 return nil, fmt.Errorf("object has no meta: %v", err) 192 } 193 controllerRef := metav1.GetControllerOf(meta) 194 if controllerRef == nil { 195 return []string{}, nil 196 } 197 return []string{string(controllerRef.UID)}, nil 198 } 199 200 // WaitForCacheSync waits for all required informers to be initialized. 201 func (p *ControlledPodsIndexer) WaitForCacheSync(ctx context.Context) bool { 202 return cache.WaitForNamedCacheSync("PodsIndexer", ctx.Done(), p.podsSynced, p.rsSynced) 203 } 204 205 // PodsControlledBy returns pods controlled by a given controller object. 206 func (p *ControlledPodsIndexer) PodsControlledBy(obj interface{}) ([]*corev1.Pod, error) { 207 metaAccessor, err := meta.Accessor(obj) 208 if err != nil { 209 return nil, fmt.Errorf("object has no meta: %w", err) 210 } 211 typeAccessor, err := meta.TypeAccessor(obj) 212 if err != nil { 213 return nil, fmt.Errorf("object has unknown type: %w", err) 214 } 215 216 var podOwners []types.UID 217 switch typeAccessor.GetKind() { 218 case "Deployment": 219 replicaSets, err := p.rsIndexer.ByIndex(controllerUIDIndex, string(metaAccessor.GetUID())) 220 if err != nil { 221 return nil, fmt.Errorf("failed to get replicasets controlled by %v: %w", metaAccessor.GetUID(), err) 222 } 223 for _, replicaSet := range replicaSets { 224 replicaSet, ok := replicaSet.(*appsv1.ReplicaSet) 225 if !ok { 226 return nil, fmt.Errorf("expected *appsv1.ReplicaSet; got: %T", replicaSet) 227 } 228 podOwners = append(podOwners, replicaSet.GetUID()) 229 } 230 default: 231 podOwners = append(podOwners, metaAccessor.GetUID()) 232 } 233 234 var res []*corev1.Pod 235 for _, podOwner := range podOwners { 236 res, err = p.appendPodsControlledBy(res, podOwner) 237 if err != nil { 238 return nil, fmt.Errorf("failed to get pods controlled by %v: %w", podOwner, err) 239 } 240 } 241 242 return res, nil 243 } 244 245 func (p *ControlledPodsIndexer) appendPodsControlledBy(res []*corev1.Pod, uid types.UID) ([]*corev1.Pod, error) { 246 objs, err := p.podsIndexer.ByIndex(controllerUIDIndex, string(uid)) 247 if err != nil { 248 return nil, fmt.Errorf("method ByIndex failed: %w", err) 249 } 250 251 for _, obj := range objs { 252 pod, ok := obj.(*corev1.Pod) 253 if !ok { 254 return nil, fmt.Errorf("expected *corev1.Pod; got: %T", obj) 255 } 256 res = append(res, pod) 257 } 258 return res, nil 259 }