istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/kclient/index.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package kclient
    16  
    17  import (
    18  	"sync"
    19  
    20  	"k8s.io/apimachinery/pkg/types"
    21  	"k8s.io/client-go/tools/cache"
    22  
    23  	"istio.io/istio/pkg/config"
    24  	"istio.io/istio/pkg/kube/controllers"
    25  	"istio.io/istio/pkg/util/sets"
    26  )
    27  
    28  // Index maintains a simple index over an informer
    29  type Index[K comparable, O controllers.ComparableObject] struct {
    30  	mu      sync.RWMutex
    31  	objects map[K]sets.Set[types.NamespacedName]
    32  	client  Informer[O]
    33  }
    34  
    35  // Lookup finds all objects matching a given key
    36  func (i *Index[K, O]) Lookup(k K) []O {
    37  	i.mu.RLock()
    38  	defer i.mu.RUnlock()
    39  	res := make([]O, 0)
    40  	for obj := range i.objects[k] {
    41  		item := i.client.Get(obj.Name, obj.Namespace)
    42  		if controllers.IsNil(item) {
    43  			// This should be extremely rare, maybe impossible due to the mutex.
    44  			continue
    45  		}
    46  		res = append(res, item)
    47  	}
    48  	return res
    49  }
    50  
    51  // CreateIndexWithDelegate creates a simple index, keyed by key K, over an informer for O. This is similar to
    52  // Informer.AddIndex, but is easier to use and can be added after an informer has already started.
    53  // An additional ResourceEventHandler can be passed in that is guaranteed to happen *after* the index is updated.
    54  // This allows the delegate to depend on the contents of the index.
    55  // TODO(https://github.com/kubernetes/kubernetes/pull/117046) remove this.
    56  func CreateIndexWithDelegate[K comparable, O controllers.ComparableObject](
    57  	client Informer[O],
    58  	extract func(o O) []K,
    59  	delegate cache.ResourceEventHandler,
    60  ) *Index[K, O] {
    61  	idx := Index[K, O]{
    62  		objects: make(map[K]sets.Set[types.NamespacedName]),
    63  		client:  client,
    64  		mu:      sync.RWMutex{},
    65  	}
    66  	addObj := func(obj any) {
    67  		ro := controllers.ExtractObject(obj)
    68  		o := ro.(O)
    69  		objectKey := config.NamespacedName(o)
    70  		for _, indexKey := range extract(o) {
    71  			sets.InsertOrNew(idx.objects, indexKey, objectKey)
    72  		}
    73  	}
    74  	deleteObj := func(obj any) {
    75  		ro := controllers.ExtractObject(obj)
    76  		o := ro.(O)
    77  		objectKey := config.NamespacedName(o)
    78  		for _, indexKey := range extract(o) {
    79  			sets.DeleteCleanupLast(idx.objects, indexKey, objectKey)
    80  		}
    81  	}
    82  	handler := cache.ResourceEventHandlerDetailedFuncs{
    83  		AddFunc: func(obj any, initialList bool) {
    84  			idx.mu.Lock()
    85  			addObj(obj)
    86  			idx.mu.Unlock()
    87  			if delegate != nil {
    88  				delegate.OnAdd(obj, initialList)
    89  			}
    90  		},
    91  		UpdateFunc: func(oldObj, newObj any) {
    92  			idx.mu.Lock()
    93  			deleteObj(oldObj)
    94  			addObj(newObj)
    95  			idx.mu.Unlock()
    96  			if delegate != nil {
    97  				delegate.OnUpdate(oldObj, newObj)
    98  			}
    99  		},
   100  		DeleteFunc: func(obj any) {
   101  			idx.mu.Lock()
   102  			deleteObj(obj)
   103  			idx.mu.Unlock()
   104  			if delegate != nil {
   105  				delegate.OnDelete(obj)
   106  			}
   107  		},
   108  	}
   109  	client.AddEventHandler(handler)
   110  	return &idx
   111  }
   112  
   113  // CreateIndex creates a simple index, keyed by key K, over an informer for O. This is similar to
   114  // Informer.AddIndex, but is easier to use and can be added after an informer has already started.
   115  func CreateIndex[K comparable, O controllers.ComparableObject](
   116  	client Informer[O],
   117  	extract func(o O) []K,
   118  ) *Index[K, O] {
   119  	return CreateIndexWithDelegate(client, extract, nil)
   120  }