istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/krt/internal.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  	"reflect"
    20  
    21  	"go.uber.org/atomic"
    22  	"google.golang.org/protobuf/proto"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/client-go/tools/cache"
    25  
    26  	"istio.io/api/type/v1beta1"
    27  	"istio.io/istio/pkg/config"
    28  	"istio.io/istio/pkg/kube/controllers"
    29  	"istio.io/istio/pkg/ptr"
    30  )
    31  
    32  // registerHandlerAsBatched is a helper to register the provided handler as a batched handler. This allows collections to
    33  // only implement RegisterBatch.
    34  func registerHandlerAsBatched[T any](c internalCollection[T], f func(o Event[T])) Syncer {
    35  	return c.RegisterBatch(func(events []Event[T], initialSync bool) {
    36  		for _, o := range events {
    37  			f(o)
    38  		}
    39  	}, true)
    40  }
    41  
    42  // castEvent converts an Event[I] to Event[O].
    43  // Caller is responsible for making sure these can be type converted.
    44  // Typically this is converting to or from `any`.
    45  func castEvent[I, O any](o Event[I]) Event[O] {
    46  	e := Event[O]{
    47  		Event: o.Event,
    48  	}
    49  	if o.Old != nil {
    50  		e.Old = ptr.Of(any(*o.Old).(O))
    51  	}
    52  	if o.New != nil {
    53  		e.New = ptr.Of(any(*o.New).(O))
    54  	}
    55  	return e
    56  }
    57  
    58  func buildCollectionOptions(opts ...CollectionOption) collectionOptions {
    59  	c := &collectionOptions{}
    60  	for _, o := range opts {
    61  		o(c)
    62  	}
    63  	if c.stop == nil {
    64  		c.stop = make(chan struct{})
    65  	}
    66  	return *c
    67  }
    68  
    69  // collectionOptions tracks options for a collection
    70  type collectionOptions struct {
    71  	name         string
    72  	augmentation func(o any) any
    73  	stop         <-chan struct{}
    74  }
    75  
    76  // dependency is a specific thing that can be depended on
    77  type dependency struct {
    78  	id             collectionUID
    79  	collectionName string
    80  	// Filter over the collection
    81  	filter *filter
    82  }
    83  
    84  type erasedEventHandler = func(o []Event[any], initialSync bool)
    85  
    86  // registerDependency is an internal interface for things that can register dependencies.
    87  // This is called from Fetch to Collections, generally.
    88  type registerDependency interface {
    89  	// Registers a dependency, returning true if it is finalized
    90  	registerDependency(*dependency, Syncer, func(f erasedEventHandler))
    91  	name() string
    92  }
    93  
    94  // tryGetKey returns the Key for an object. If not possible, returns false
    95  func tryGetKey[O any](a O) (Key[O], bool) {
    96  	as, ok := any(a).(string)
    97  	if ok {
    98  		return Key[O](as), true
    99  	}
   100  	ao, ok := any(a).(controllers.Object)
   101  	if ok {
   102  		k, _ := cache.MetaNamespaceKeyFunc(ao)
   103  		return Key[O](k), true
   104  	}
   105  	ac, ok := any(a).(config.Config)
   106  	if ok {
   107  		return Key[O](keyFunc(ac.Name, ac.Namespace)), true
   108  	}
   109  	arn, ok := any(a).(ResourceNamer)
   110  	if ok {
   111  		return Key[O](arn.ResourceName()), true
   112  	}
   113  	ack := GetApplyConfigKey(a)
   114  	if ack != nil {
   115  		return *ack, true
   116  	}
   117  	return "", false
   118  }
   119  
   120  // getLabels returns the labels for an object, if possible.
   121  // Warning: this will panic if the labels is not available.
   122  func getLabels(a any) map[string]string {
   123  	al, ok := a.(Labeler)
   124  	if ok {
   125  		return al.GetLabels()
   126  	}
   127  	pal, ok := any(&a).(Labeler)
   128  	if ok {
   129  		return pal.GetLabels()
   130  	}
   131  	ak, ok := a.(metav1.Object)
   132  	if ok {
   133  		return ak.GetLabels()
   134  	}
   135  	ac, ok := a.(config.Config)
   136  	if ok {
   137  		return ac.Labels
   138  	}
   139  	panic(fmt.Sprintf("No Labels, got %T", a))
   140  }
   141  
   142  // getLabelSelector returns the labels for an object, if possible.
   143  // Warning: this will panic if the labelSelectors is not available.
   144  func getLabelSelector(a any) map[string]string {
   145  	ak, ok := a.(LabelSelectorer)
   146  	if ok {
   147  		return ak.GetLabelSelector()
   148  	}
   149  	val := reflect.ValueOf(a)
   150  
   151  	if val.Kind() == reflect.Ptr {
   152  		val = val.Elem()
   153  	}
   154  
   155  	specField := val.FieldByName("Spec")
   156  	if !specField.IsValid() {
   157  		panic(fmt.Sprintf("obj %T has no Spec", a))
   158  	}
   159  
   160  	labelsField := specField.FieldByName("Selector")
   161  	if !labelsField.IsValid() {
   162  		panic(fmt.Sprintf("obj %T has no Selector", a))
   163  	}
   164  
   165  	switch s := labelsField.Interface().(type) {
   166  	case *v1beta1.WorkloadSelector:
   167  		return s.GetMatchLabels()
   168  	case map[string]string:
   169  		return s
   170  	default:
   171  		panic(fmt.Sprintf("obj %T has unknown Selector", s))
   172  	}
   173  }
   174  
   175  // equal checks if two objects are equal. This is done through a variety of different methods, depending on the input type.
   176  func equal[O any](a, b O) bool {
   177  	ak, ok := any(a).(Equaler[O])
   178  	if ok {
   179  		return ak.Equals(b)
   180  	}
   181  	pk, ok := any(&a).(Equaler[O])
   182  	if ok {
   183  		return pk.Equals(b)
   184  	}
   185  	// Future improvement: add a default Kubernetes object implementation
   186  	// ResourceVersion is tempting but probably not safe. If we are comparing objects from the API server its fine,
   187  	// but often we will be operating on types generated by the controller itself.
   188  	// We should have a way to opt-in to RV comparison, but not default to it.
   189  
   190  	ap, ok := any(a).(proto.Message)
   191  	if ok {
   192  		if reflect.TypeOf(ap.ProtoReflect().Interface()) == reflect.TypeOf(ap) {
   193  			return proto.Equal(ap, any(b).(proto.Message))
   194  		}
   195  		// If not, this is an embedded proto most likely... Sneaky.
   196  		// DeepEqual on proto is broken, so fail fast to avoid subtle errors.
   197  		panic(fmt.Sprintf("unable to compare object %T; perhaps it is embedding a protobuf? Provide an Equaler implementation", a))
   198  	}
   199  	return reflect.DeepEqual(a, b)
   200  }
   201  
   202  type collectionUID uint64
   203  
   204  var globalUIDCounter = atomic.NewUint64(1)
   205  
   206  func nextUID() collectionUID {
   207  	return collectionUID(globalUIDCounter.Inc())
   208  }