istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/kclient/delayed.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  	"sync/atomic"
    20  
    21  	klabels "k8s.io/apimachinery/pkg/labels"
    22  	"k8s.io/apimachinery/pkg/runtime/schema"
    23  	"k8s.io/client-go/tools/cache"
    24  
    25  	"istio.io/istio/pkg/kube/controllers"
    26  	"istio.io/istio/pkg/kube/kubetypes"
    27  	"istio.io/istio/pkg/ptr"
    28  )
    29  
    30  // delayedClient is a client wrapper that initially starts with an "empty client",
    31  // but can later be swapped with a real client.
    32  // The "empty client" returns empty responses for all reads, and fails all writes.
    33  type delayedClient[T controllers.ComparableObject] struct {
    34  	inf *atomic.Pointer[Informer[T]]
    35  
    36  	delayed kubetypes.DelayedFilter
    37  
    38  	hm       sync.Mutex
    39  	handlers []cache.ResourceEventHandler
    40  	started  <-chan struct{}
    41  }
    42  
    43  func (s *delayedClient[T]) Get(name, namespace string) T {
    44  	if c := s.inf.Load(); c != nil {
    45  		return (*c).Get(name, namespace)
    46  	}
    47  	return ptr.Empty[T]()
    48  }
    49  
    50  func (s *delayedClient[T]) List(namespace string, selector klabels.Selector) []T {
    51  	if c := s.inf.Load(); c != nil {
    52  		return (*c).List(namespace, selector)
    53  	}
    54  	return nil
    55  }
    56  
    57  func (s *delayedClient[T]) ListUnfiltered(namespace string, selector klabels.Selector) []T {
    58  	if c := s.inf.Load(); c != nil {
    59  		return (*c).ListUnfiltered(namespace, selector)
    60  	}
    61  	return nil
    62  }
    63  
    64  func (s *delayedClient[T]) AddEventHandler(h cache.ResourceEventHandler) {
    65  	if c := s.inf.Load(); c != nil {
    66  		(*c).AddEventHandler(h)
    67  	} else {
    68  		s.hm.Lock()
    69  		defer s.hm.Unlock()
    70  		s.handlers = append(s.handlers, h)
    71  	}
    72  }
    73  
    74  func (s *delayedClient[T]) HasSynced() bool {
    75  	if c := s.inf.Load(); c != nil {
    76  		return (*c).HasSynced()
    77  	}
    78  	// If we haven't loaded the informer yet, we want to check if the delayed filter is synced.
    79  	// This ensures that at startup, we only return HasSynced=true if we are sure the CRD is not ready.
    80  	hs := s.delayed.HasSynced()
    81  	return hs
    82  }
    83  
    84  func (s *delayedClient[T]) ShutdownHandlers() {
    85  	if c := s.inf.Load(); c != nil {
    86  		(*c).ShutdownHandlers()
    87  	} else {
    88  		s.hm.Lock()
    89  		defer s.hm.Unlock()
    90  		s.handlers = nil
    91  	}
    92  }
    93  
    94  func (s *delayedClient[T]) Start(stop <-chan struct{}) {
    95  	if c := s.inf.Load(); c != nil {
    96  		(*c).Start(stop)
    97  	}
    98  	s.hm.Lock()
    99  	defer s.hm.Unlock()
   100  	s.started = stop
   101  }
   102  
   103  var _ Informer[controllers.Object] = &delayedClient[controllers.Object]{}
   104  
   105  func (s *delayedClient[T]) set(inf Informer[T]) {
   106  	if inf != nil {
   107  		s.inf.Swap(&inf)
   108  		s.hm.Lock()
   109  		defer s.hm.Unlock()
   110  		for _, h := range s.handlers {
   111  			inf.AddEventHandler(h)
   112  		}
   113  		s.handlers = nil
   114  		if s.started != nil {
   115  			inf.Start(s.started)
   116  		}
   117  	}
   118  }
   119  
   120  type delayedFilter struct {
   121  	Watcher  kubetypes.CrdWatcher
   122  	Resource schema.GroupVersionResource
   123  }
   124  
   125  func (d *delayedFilter) HasSynced() bool {
   126  	return d.Watcher.HasSynced()
   127  }
   128  
   129  func (d *delayedFilter) KnownOrCallback(f func(stop <-chan struct{})) bool {
   130  	return d.Watcher.KnownOrCallback(d.Resource, f)
   131  }
   132  
   133  func newDelayedFilter(resource schema.GroupVersionResource, watcher kubetypes.CrdWatcher) *delayedFilter {
   134  	return &delayedFilter{
   135  		Watcher:  watcher,
   136  		Resource: resource,
   137  	}
   138  }