istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/krt/informer.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 krt
    16  
    17  import (
    18  	"fmt"
    19  
    20  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    21  	klabels "k8s.io/apimachinery/pkg/labels"
    22  	"k8s.io/client-go/tools/cache"
    23  
    24  	"istio.io/istio/pkg/kube"
    25  	"istio.io/istio/pkg/kube/controllers"
    26  	"istio.io/istio/pkg/kube/kclient"
    27  	"istio.io/istio/pkg/kube/kubetypes"
    28  	istiolog "istio.io/istio/pkg/log"
    29  	"istio.io/istio/pkg/ptr"
    30  )
    31  
    32  type informer[I controllers.ComparableObject] struct {
    33  	inf            kclient.Informer[I]
    34  	log            *istiolog.Scope
    35  	collectionName string
    36  	id             collectionUID
    37  
    38  	eventHandlers *handlers[I]
    39  	augmentation  func(a any) any
    40  	synced        chan struct{}
    41  }
    42  
    43  // nolint: unused // (not true, its to implement an interface)
    44  func (i *informer[I]) augment(a any) any {
    45  	if i.augmentation != nil {
    46  		return i.augmentation(a)
    47  	}
    48  	return a
    49  }
    50  
    51  var _ internalCollection[controllers.Object] = &informer[controllers.Object]{}
    52  
    53  func (i *informer[I]) _internalHandler() {}
    54  
    55  func (i *informer[I]) Synced() Syncer {
    56  	return channelSyncer{
    57  		name:   i.collectionName,
    58  		synced: i.synced,
    59  	}
    60  }
    61  
    62  // nolint: unused // (not true, its to implement an interface)
    63  func (i *informer[I]) dump() {
    64  	// TODO: implement some useful dump here
    65  }
    66  
    67  func (i *informer[I]) name() string {
    68  	return i.collectionName
    69  }
    70  
    71  // nolint: unused // (not true, its to implement an interface)
    72  func (i *informer[I]) uid() collectionUID {
    73  	return i.id
    74  }
    75  
    76  func (i *informer[I]) List() []I {
    77  	res := i.inf.List(metav1.NamespaceAll, klabels.Everything())
    78  	return res
    79  }
    80  
    81  func (i *informer[I]) GetKey(k Key[I]) *I {
    82  	// ns, n := splitKeyFunc(string(k))
    83  	// Internal optimization: we know kclient will eventually lookup "ns/name"
    84  	// We also have a key in this format.
    85  	// Rather than split and rejoin it later, just pass it as the name
    86  	// This is depending on "unstable" implementation details, but we own both libraries and tests would catch any issues.
    87  	if got := i.inf.Get(string(k), ""); !controllers.IsNil(got) {
    88  		return &got
    89  	}
    90  	return nil
    91  }
    92  
    93  func (i *informer[I]) Register(f func(o Event[I])) Syncer {
    94  	return registerHandlerAsBatched[I](i, f)
    95  }
    96  
    97  func (i *informer[I]) RegisterBatch(f func(o []Event[I], initialSync bool), runExistingState bool) Syncer {
    98  	// Note: runExistingState is NOT respected here.
    99  	// Informer doesn't expose a way to do that. However, due to the runtime model of informers, this isn't a dealbreaker;
   100  	// the handlers are all called async, so we don't end up with the same deadlocks we would have in the other collection types.
   101  	// While this is quite kludgy, this is an internal interface so its not too bad.
   102  	if !i.eventHandlers.Insert(f) {
   103  		i.inf.AddEventHandler(informerEventHandler[I](func(o Event[I], initialSync bool) {
   104  			f([]Event[I]{o}, initialSync)
   105  		}))
   106  	}
   107  	return pollSyncer{
   108  		name: fmt.Sprintf("%v handler", i.name()),
   109  		f:    i.inf.HasSynced,
   110  	}
   111  }
   112  
   113  func informerEventHandler[I controllers.ComparableObject](handler func(o Event[I], initialSync bool)) cache.ResourceEventHandler {
   114  	return controllers.EventHandler[I]{
   115  		AddExtendedFunc: func(obj I, initialSync bool) {
   116  			handler(Event[I]{
   117  				New:   &obj,
   118  				Event: controllers.EventAdd,
   119  			}, initialSync)
   120  		},
   121  		UpdateFunc: func(oldObj, newObj I) {
   122  			handler(Event[I]{
   123  				Old:   &oldObj,
   124  				New:   &newObj,
   125  				Event: controllers.EventUpdate,
   126  			}, false)
   127  		},
   128  		DeleteFunc: func(obj I) {
   129  			handler(Event[I]{
   130  				Old:   &obj,
   131  				Event: controllers.EventDelete,
   132  			}, false)
   133  		},
   134  	}
   135  }
   136  
   137  // WrapClient is the base entrypoint that enables the creation
   138  // of a collection from an API Server client.
   139  //
   140  // Generic types can use kclient.NewDynamic to create an
   141  // informer for a Collection of type controllers.Object
   142  func WrapClient[I controllers.ComparableObject](c kclient.Informer[I], opts ...CollectionOption) Collection[I] {
   143  	o := buildCollectionOptions(opts...)
   144  	if o.name == "" {
   145  		o.name = fmt.Sprintf("NewInformer[%v]", ptr.TypeName[I]())
   146  	}
   147  	h := &informer[I]{
   148  		inf:            c,
   149  		log:            log.WithLabels("owner", o.name),
   150  		collectionName: o.name,
   151  		id:             nextUID(),
   152  		eventHandlers:  &handlers[I]{},
   153  		augmentation:   o.augmentation,
   154  		synced:         make(chan struct{}),
   155  	}
   156  
   157  	go func() {
   158  		// First, wait for the informer to populate
   159  		if !kube.WaitForCacheSync(o.name, o.stop, c.HasSynced) {
   160  			return
   161  		}
   162  		// Now, take all our handlers we have built up and register them...
   163  		handlers := h.eventHandlers.MarkInitialized()
   164  		for _, h := range handlers {
   165  			c.AddEventHandler(informerEventHandler[I](func(o Event[I], initialSync bool) {
   166  				h([]Event[I]{o}, initialSync)
   167  			}))
   168  		}
   169  		// Now wait for handlers to sync
   170  		if !kube.WaitForCacheSync(o.name+" handlers", o.stop, c.HasSynced) {
   171  			c.ShutdownHandlers()
   172  			return
   173  		}
   174  		close(h.synced)
   175  		h.log.Infof("%v synced", h.name())
   176  	}()
   177  	return h
   178  }
   179  
   180  // NewInformer creates a Collection[I] sourced from
   181  // the results of kube.Client querying resources of type I
   182  // from the API Server.
   183  //
   184  // Resources must have their GVR and GVK registered in the
   185  // kube.Client before this method is called, otherwise
   186  // NewInformer will panic.
   187  func NewInformer[I controllers.ComparableObject](c kube.Client, opts ...CollectionOption) Collection[I] {
   188  	return NewInformerFiltered[I](c, kubetypes.Filter{}, opts...)
   189  }
   190  
   191  // NewInformerFiltered takes an argument that filters the
   192  // results from the kube.Client. Otherwise, behaves
   193  // the same as NewInformer
   194  func NewInformerFiltered[I controllers.ComparableObject](c kube.Client, filter kubetypes.Filter, opts ...CollectionOption) Collection[I] {
   195  	return WrapClient[I](kclient.NewFiltered[I](c, filter), opts...)
   196  }