istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/krt/join.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  	"istio.io/istio/pkg/ptr"
    21  	"istio.io/istio/pkg/slices"
    22  	"istio.io/istio/pkg/util/sets"
    23  )
    24  
    25  type join[T any] struct {
    26  	collectionName string
    27  	id             collectionUID
    28  	collections    []internalCollection[T]
    29  	synced         <-chan struct{}
    30  }
    31  
    32  func (j *join[T]) GetKey(k Key[T]) *T {
    33  	for _, c := range j.collections {
    34  		if r := c.GetKey(k); r != nil {
    35  			return r
    36  		}
    37  	}
    38  	return nil
    39  }
    40  
    41  func (j *join[T]) List() []T {
    42  	res := []T{}
    43  	found := sets.New[Key[T]]()
    44  	for _, c := range j.collections {
    45  		for _, i := range c.List() {
    46  			key := GetKey(i)
    47  			if !found.InsertContains(key) {
    48  				// Only keep it if it is the first time we saw it, as our merging mechanism is to keep the first one
    49  				res = append(res, i)
    50  			}
    51  		}
    52  	}
    53  	return res
    54  }
    55  
    56  func (j *join[T]) Register(f func(o Event[T])) Syncer {
    57  	return registerHandlerAsBatched[T](j, f)
    58  }
    59  
    60  func (j *join[T]) RegisterBatch(f func(o []Event[T], initialSync bool), runExistingState bool) Syncer {
    61  	sync := multiSyncer{}
    62  	for _, c := range j.collections {
    63  		sync.syncers = append(sync.syncers, c.RegisterBatch(f, runExistingState))
    64  	}
    65  	return sync
    66  }
    67  
    68  // nolint: unused // (not true, its to implement an interface)
    69  func (j *join[T]) augment(a any) any {
    70  	// not supported in this collection type
    71  	return a
    72  }
    73  
    74  // nolint: unused // (not true, its to implement an interface)
    75  func (j *join[T]) name() string { return j.collectionName }
    76  
    77  // nolint: unused // (not true, its to implement an interface)
    78  func (j *join[T]) uid() collectionUID { return j.id }
    79  
    80  // nolint: unused // (not true, its to implement an interface)
    81  func (j *join[I]) dump() {
    82  	log.Errorf("> BEGIN DUMP (join %v)", j.collectionName)
    83  	for _, c := range j.collections {
    84  		c.dump()
    85  	}
    86  	log.Errorf("< END DUMP (join %v)", j.collectionName)
    87  }
    88  
    89  func (j *join[T]) Synced() Syncer {
    90  	return channelSyncer{
    91  		name:   j.collectionName,
    92  		synced: j.synced,
    93  	}
    94  }
    95  
    96  // JoinCollection combines multiple Collection[T] into a single
    97  // Collection[T] merging equal objects into one record
    98  // in the resulting Collection
    99  func JoinCollection[T any](cs []Collection[T], opts ...CollectionOption) Collection[T] {
   100  	o := buildCollectionOptions(opts...)
   101  	if o.name == "" {
   102  		o.name = fmt.Sprintf("Join[%v]", ptr.TypeName[T]())
   103  	}
   104  	synced := make(chan struct{})
   105  	c := slices.Map(cs, func(e Collection[T]) internalCollection[T] {
   106  		return e.(internalCollection[T])
   107  	})
   108  	go func() {
   109  		for _, c := range c {
   110  			if !c.Synced().WaitUntilSynced(o.stop) {
   111  				return
   112  			}
   113  		}
   114  		close(synced)
   115  		log.Infof("%v synced", o.name)
   116  	}()
   117  	// TODO: in the future, we could have a custom merge function. For now, since we just take the first, we optimize around that case
   118  	return &join[T]{
   119  		collectionName: o.name,
   120  		id:             nextUID(),
   121  		synced:         synced,
   122  		collections:    c,
   123  	}
   124  }