feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
745
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
745
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
@@ -19,10 +19,13 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -30,6 +33,7 @@ import (
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
toolscache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -37,16 +41,33 @@ import (
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
)
|
||||
|
||||
var log = logf.RuntimeLog.WithName("object-cache")
|
||||
var (
|
||||
log = logf.RuntimeLog.WithName("object-cache")
|
||||
defaultSyncPeriod = 10 * time.Hour
|
||||
)
|
||||
|
||||
// InformerGetOptions defines the behavior of how informers are retrieved.
|
||||
type InformerGetOptions internal.GetOptions
|
||||
|
||||
// InformerGetOption defines an option that alters the behavior of how informers are retrieved.
|
||||
type InformerGetOption func(*InformerGetOptions)
|
||||
|
||||
// BlockUntilSynced determines whether a get request for an informer should block
|
||||
// until the informer's cache has synced.
|
||||
func BlockUntilSynced(shouldBlock bool) InformerGetOption {
|
||||
return func(opts *InformerGetOptions) {
|
||||
opts.BlockUntilSynced = &shouldBlock
|
||||
}
|
||||
}
|
||||
|
||||
// Cache knows how to load Kubernetes objects, fetch informers to request
|
||||
// to receive events for Kubernetes objects (at a low-level),
|
||||
// and add indices to fields on the objects stored in the cache.
|
||||
type Cache interface {
|
||||
// Cache acts as a client to objects stored in the cache.
|
||||
// Reader acts as a client to objects stored in the cache.
|
||||
client.Reader
|
||||
|
||||
// Cache loads informers and adds field indices.
|
||||
// Informers loads informers and adds field indices.
|
||||
Informers
|
||||
}
|
||||
|
||||
@@ -56,352 +77,333 @@ type Cache interface {
|
||||
type Informers interface {
|
||||
// GetInformer fetches or constructs an informer for the given object that corresponds to a single
|
||||
// API kind and resource.
|
||||
GetInformer(ctx context.Context, obj client.Object) (Informer, error)
|
||||
GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error)
|
||||
|
||||
// GetInformerForKind is similar to GetInformer, except that it takes a group-version-kind, instead
|
||||
// of the underlying object.
|
||||
GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error)
|
||||
GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error)
|
||||
|
||||
// RemoveInformer removes an informer entry and stops it if it was running.
|
||||
RemoveInformer(ctx context.Context, obj client.Object) error
|
||||
|
||||
// Start runs all the informers known to this cache until the context is closed.
|
||||
// It blocks.
|
||||
Start(ctx context.Context) error
|
||||
|
||||
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
|
||||
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
|
||||
WaitForCacheSync(ctx context.Context) bool
|
||||
|
||||
// Informers knows how to add indices to the caches (informers) that it manages.
|
||||
// FieldIndexer adds indices to the managed informers.
|
||||
client.FieldIndexer
|
||||
}
|
||||
|
||||
// Informer - informer allows you interact with the underlying informer.
|
||||
// Informer allows you to interact with the underlying informer.
|
||||
type Informer interface {
|
||||
// AddEventHandler adds an event handler to the shared informer using the shared informer's resync
|
||||
// period. Events to a single handler are delivered sequentially, but there is no coordination
|
||||
// period. Events to a single handler are delivered sequentially, but there is no coordination
|
||||
// between different handlers.
|
||||
// It returns a registration handle for the handler that can be used to remove
|
||||
// the handler again.
|
||||
// the handler again and an error if the handler cannot be added.
|
||||
AddEventHandler(handler toolscache.ResourceEventHandler) (toolscache.ResourceEventHandlerRegistration, error)
|
||||
|
||||
// AddEventHandlerWithResyncPeriod adds an event handler to the shared informer using the
|
||||
// specified resync period. Events to a single handler are delivered sequentially, but there is
|
||||
// specified resync period. Events to a single handler are delivered sequentially, but there is
|
||||
// no coordination between different handlers.
|
||||
// It returns a registration handle for the handler that can be used to remove
|
||||
// the handler again and an error if the handler cannot be added.
|
||||
AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration) (toolscache.ResourceEventHandlerRegistration, error)
|
||||
// RemoveEventHandler removes a formerly added event handler given by
|
||||
|
||||
// RemoveEventHandler removes a previously added event handler given by
|
||||
// its registration handle.
|
||||
// This function is guaranteed to be idempotent, and thread-safe.
|
||||
// This function is guaranteed to be idempotent and thread-safe.
|
||||
RemoveEventHandler(handle toolscache.ResourceEventHandlerRegistration) error
|
||||
// AddIndexers adds more indexers to this store. If you call this after you already have data
|
||||
|
||||
// AddIndexers adds indexers to this store. If this is called after there is already data
|
||||
// in the store, the results are undefined.
|
||||
AddIndexers(indexers toolscache.Indexers) error
|
||||
|
||||
// HasSynced return true if the informers underlying store has synced.
|
||||
HasSynced() bool
|
||||
// IsStopped returns true if the informer has been stopped.
|
||||
IsStopped() bool
|
||||
}
|
||||
|
||||
// ObjectSelector is an alias name of internal.Selector.
|
||||
type ObjectSelector internal.Selector
|
||||
// AllNamespaces should be used as the map key to deliminate namespace settings
|
||||
// that apply to all namespaces that themselves do not have explicit settings.
|
||||
const AllNamespaces = metav1.NamespaceAll
|
||||
|
||||
// SelectorsByObject associate a client.Object's GVK to a field/label selector.
|
||||
// There is also `DefaultSelector` to set a global default (which will be overridden by
|
||||
// a more specific setting here, if any).
|
||||
type SelectorsByObject map[client.Object]ObjectSelector
|
||||
|
||||
// Options are the optional arguments for creating a new InformersMap object.
|
||||
// Options are the optional arguments for creating a new Cache object.
|
||||
type Options struct {
|
||||
// HTTPClient is the http client to use for the REST client
|
||||
HTTPClient *http.Client
|
||||
|
||||
// Scheme is the scheme to use for mapping objects to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// Mapper is the RESTMapper to use for mapping GroupVersionKinds to Resources
|
||||
Mapper meta.RESTMapper
|
||||
|
||||
// Resync is the base frequency the informers are resynced.
|
||||
// Defaults to defaultResyncTime.
|
||||
// A 10 percent jitter will be added to the Resync period between informers
|
||||
// So that all informers will not send list requests simultaneously.
|
||||
Resync *time.Duration
|
||||
// SyncPeriod determines the minimum frequency at which watched resources are
|
||||
// reconciled. A lower period will correct entropy more quickly, but reduce
|
||||
// responsiveness to change if there are many watched resources. Change this
|
||||
// value only if you know what you are doing. Defaults to 10 hours if unset.
|
||||
// there will a 10 percent jitter between the SyncPeriod of all controllers
|
||||
// so that all controllers will not send list requests simultaneously.
|
||||
//
|
||||
// This applies to all controllers.
|
||||
//
|
||||
// A period sync happens for two reasons:
|
||||
// 1. To insure against a bug in the controller that causes an object to not
|
||||
// be requeued, when it otherwise should be requeued.
|
||||
// 2. To insure against an unknown bug in controller-runtime, or its dependencies,
|
||||
// that causes an object to not be requeued, when it otherwise should be
|
||||
// requeued, or to be removed from the queue, when it otherwise should not
|
||||
// be removed.
|
||||
//
|
||||
// If you want
|
||||
// 1. to insure against missed watch events, or
|
||||
// 2. to poll services that cannot be watched,
|
||||
// then we recommend that, instead of changing the default period, the
|
||||
// controller requeue, with a constant duration `t`, whenever the controller
|
||||
// is "done" with an object, and would otherwise not requeue it, i.e., we
|
||||
// recommend the `Reconcile` function return `reconcile.Result{RequeueAfter: t}`,
|
||||
// instead of `reconcile.Result{}`.
|
||||
SyncPeriod *time.Duration
|
||||
|
||||
// Namespace restricts the cache's ListWatch to the desired namespace
|
||||
// Default watches all namespaces
|
||||
Namespace string
|
||||
// ReaderFailOnMissingInformer configures the cache to return a ErrResourceNotCached error when a user
|
||||
// requests, using Get() and List(), a resource the cache does not already have an informer for.
|
||||
//
|
||||
// This error is distinct from an errors.NotFound.
|
||||
//
|
||||
// Defaults to false, which means that the cache will start a new informer
|
||||
// for every new requested resource.
|
||||
ReaderFailOnMissingInformer bool
|
||||
|
||||
// SelectorsByObject restricts the cache's ListWatch to the desired
|
||||
// fields per GVK at the specified object, the map's value must implement
|
||||
// Selector [1] using for example a Set [2]
|
||||
// [1] https://pkg.go.dev/k8s.io/apimachinery/pkg/fields#Selector
|
||||
// [2] https://pkg.go.dev/k8s.io/apimachinery/pkg/fields#Set
|
||||
SelectorsByObject SelectorsByObject
|
||||
// DefaultNamespaces maps namespace names to cache configs. If set, only
|
||||
// the namespaces in here will be watched and it will by used to default
|
||||
// ByObject.Namespaces for all objects if that is nil.
|
||||
//
|
||||
// It is possible to have specific Config for just some namespaces
|
||||
// but cache all namespaces by using the AllNamespaces const as the map key.
|
||||
// This will then include all namespaces that do not have a more specific
|
||||
// setting.
|
||||
//
|
||||
// The options in the Config that are nil will be defaulted from
|
||||
// the respective Default* settings.
|
||||
DefaultNamespaces map[string]Config
|
||||
|
||||
// DefaultSelector will be used as selectors for all object types
|
||||
// that do not have a selector in SelectorsByObject defined.
|
||||
DefaultSelector ObjectSelector
|
||||
// DefaultLabelSelector will be used as a label selector for all objects
|
||||
// unless there is already one set in ByObject or DefaultNamespaces.
|
||||
DefaultLabelSelector labels.Selector
|
||||
|
||||
// UnsafeDisableDeepCopyByObject indicates not to deep copy objects during get or
|
||||
// DefaultFieldSelector will be used as a field selector for all object types
|
||||
// unless there is already one set in ByObject or DefaultNamespaces.
|
||||
DefaultFieldSelector fields.Selector
|
||||
|
||||
// DefaultTransform will be used as transform for all object types
|
||||
// unless there is already one set in ByObject or DefaultNamespaces.
|
||||
DefaultTransform toolscache.TransformFunc
|
||||
|
||||
// DefaultWatchErrorHandler will be used to the WatchErrorHandler which is called
|
||||
// whenever ListAndWatch drops the connection with an error.
|
||||
//
|
||||
// After calling this handler, the informer will backoff and retry.
|
||||
DefaultWatchErrorHandler toolscache.WatchErrorHandler
|
||||
|
||||
// DefaultUnsafeDisableDeepCopy is the default for UnsafeDisableDeepCopy
|
||||
// for everything that doesn't specify this.
|
||||
//
|
||||
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
|
||||
// otherwise you will mutate the object in the cache.
|
||||
//
|
||||
// This will be used for all object types, unless it is set in ByObject or
|
||||
// DefaultNamespaces.
|
||||
DefaultUnsafeDisableDeepCopy *bool
|
||||
|
||||
// ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
|
||||
// object, this will fall through to Default* settings.
|
||||
ByObject map[client.Object]ByObject
|
||||
|
||||
// newInformer allows overriding of NewSharedIndexInformer for testing.
|
||||
newInformer *func(toolscache.ListerWatcher, runtime.Object, time.Duration, toolscache.Indexers) toolscache.SharedIndexInformer
|
||||
}
|
||||
|
||||
// ByObject offers more fine-grained control over the cache's ListWatch by object.
|
||||
type ByObject struct {
|
||||
// Namespaces maps a namespace name to cache configs. If set, only the
|
||||
// namespaces in this map will be cached.
|
||||
//
|
||||
// Settings in the map value that are unset will be defaulted.
|
||||
// Use an empty value for the specific setting to prevent that.
|
||||
//
|
||||
// It is possible to have specific Config for just some namespaces
|
||||
// but cache all namespaces by using the AllNamespaces const as the map key.
|
||||
// This will then include all namespaces that do not have a more specific
|
||||
// setting.
|
||||
//
|
||||
// A nil map allows to default this to the cache's DefaultNamespaces setting.
|
||||
// An empty map prevents this and means that all namespaces will be cached.
|
||||
//
|
||||
// The defaulting follows the following precedence order:
|
||||
// 1. ByObject
|
||||
// 2. DefaultNamespaces[namespace]
|
||||
// 3. Default*
|
||||
//
|
||||
// This must be unset for cluster-scoped objects.
|
||||
Namespaces map[string]Config
|
||||
|
||||
// Label represents a label selector for the object.
|
||||
Label labels.Selector
|
||||
|
||||
// Field represents a field selector for the object.
|
||||
Field fields.Selector
|
||||
|
||||
// Transform is a transformer function for the object which gets applied
|
||||
// when objects of the transformation are about to be committed to the cache.
|
||||
//
|
||||
// This function is called both for new objects to enter the cache,
|
||||
// and for updated objects.
|
||||
Transform toolscache.TransformFunc
|
||||
|
||||
// UnsafeDisableDeepCopy indicates not to deep copy objects during get or
|
||||
// list objects per GVK at the specified object.
|
||||
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
|
||||
// otherwise you will mutate the object in the cache.
|
||||
UnsafeDisableDeepCopyByObject DisableDeepCopyByObject
|
||||
|
||||
// TransformByObject is a map from GVKs to transformer functions which
|
||||
// get applied when objects of the transformation are about to be committed
|
||||
// to cache.
|
||||
//
|
||||
// This function is called both for new objects to enter the cache,
|
||||
// and for updated objects.
|
||||
TransformByObject TransformByObject
|
||||
|
||||
// DefaultTransform is the transform used for all GVKs which do
|
||||
// not have an explicit transform func set in TransformByObject
|
||||
DefaultTransform toolscache.TransformFunc
|
||||
UnsafeDisableDeepCopy *bool
|
||||
}
|
||||
|
||||
var defaultResyncTime = 10 * time.Hour
|
||||
// Config describes all potential options for a given watch.
|
||||
type Config struct {
|
||||
// LabelSelector specifies a label selector. A nil value allows to
|
||||
// default this.
|
||||
//
|
||||
// Set to labels.Everything() if you don't want this defaulted.
|
||||
LabelSelector labels.Selector
|
||||
|
||||
// FieldSelector specifics a field selector. A nil value allows to
|
||||
// default this.
|
||||
//
|
||||
// Set to fields.Everything() if you don't want this defaulted.
|
||||
FieldSelector fields.Selector
|
||||
|
||||
// Transform specifies a transform func. A nil value allows to default
|
||||
// this.
|
||||
//
|
||||
// Set to an empty func to prevent this:
|
||||
// func(in interface{}) (interface{}, error) { return in, nil }
|
||||
Transform toolscache.TransformFunc
|
||||
|
||||
// UnsafeDisableDeepCopy specifies if List and Get requests against the
|
||||
// cache should not DeepCopy. A nil value allows to default this.
|
||||
UnsafeDisableDeepCopy *bool
|
||||
}
|
||||
|
||||
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
|
||||
type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
|
||||
|
||||
// New initializes and returns a new Cache.
|
||||
func New(config *rest.Config, opts Options) (Cache, error) {
|
||||
opts, err := defaultOpts(config, opts)
|
||||
func New(cfg *rest.Config, opts Options) (Cache, error) {
|
||||
opts, err := defaultOpts(cfg, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selectorsByGVK, err := convertToByGVK(opts.SelectorsByObject, opts.DefaultSelector, opts.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disableDeepCopyByGVK, err := convertToDisableDeepCopyByGVK(opts.UnsafeDisableDeepCopyByObject, opts.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transformByGVK, err := convertToByGVK(opts.TransformByObject, opts.DefaultTransform, opts.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transformByObj := internal.TransformFuncByObjectFromMap(transformByGVK)
|
||||
|
||||
internalSelectorsByGVK := internal.SelectorsByGVK{}
|
||||
for gvk, selector := range selectorsByGVK {
|
||||
internalSelectorsByGVK[gvk] = internal.Selector(selector)
|
||||
}
|
||||
|
||||
im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace, internalSelectorsByGVK, disableDeepCopyByGVK, transformByObj)
|
||||
return &informerCache{InformersMap: im}, nil
|
||||
}
|
||||
newCacheFunc := newCache(cfg, opts)
|
||||
|
||||
// BuilderWithOptions returns a Cache constructor that will build a cache
|
||||
// honoring the options argument, this is useful to specify options like
|
||||
// SelectorsByObject
|
||||
// WARNING: If SelectorsByObject is specified, filtered out resources are not
|
||||
// returned.
|
||||
// WARNING: If UnsafeDisableDeepCopy is enabled, you must DeepCopy any object
|
||||
// returned from cache get/list before mutating it.
|
||||
func BuilderWithOptions(options Options) NewCacheFunc {
|
||||
return func(config *rest.Config, inherited Options) (Cache, error) {
|
||||
var err error
|
||||
inherited, err = defaultOpts(config, inherited)
|
||||
var defaultCache Cache
|
||||
if len(opts.DefaultNamespaces) > 0 {
|
||||
defaultConfig := optionDefaultsToConfig(&opts)
|
||||
defaultCache = newMultiNamespaceCache(newCacheFunc, opts.Scheme, opts.Mapper, opts.DefaultNamespaces, &defaultConfig)
|
||||
} else {
|
||||
defaultCache = newCacheFunc(optionDefaultsToConfig(&opts), corev1.NamespaceAll)
|
||||
}
|
||||
|
||||
if len(opts.ByObject) == 0 {
|
||||
return defaultCache, nil
|
||||
}
|
||||
|
||||
delegating := &delegatingByGVKCache{
|
||||
scheme: opts.Scheme,
|
||||
caches: make(map[schema.GroupVersionKind]Cache, len(opts.ByObject)),
|
||||
defaultCache: defaultCache,
|
||||
}
|
||||
|
||||
for obj, config := range opts.ByObject {
|
||||
gvk, err := apiutil.GVKForObject(obj, opts.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to get GVK for type %T: %w", obj, err)
|
||||
}
|
||||
options, err = defaultOpts(config, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var cache Cache
|
||||
if len(config.Namespaces) > 0 {
|
||||
cache = newMultiNamespaceCache(newCacheFunc, opts.Scheme, opts.Mapper, config.Namespaces, nil)
|
||||
} else {
|
||||
cache = newCacheFunc(byObjectToConfig(config), corev1.NamespaceAll)
|
||||
}
|
||||
combined, err := options.inheritFrom(inherited)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
delegating.caches[gvk] = cache
|
||||
}
|
||||
|
||||
return delegating, nil
|
||||
}
|
||||
|
||||
func optionDefaultsToConfig(opts *Options) Config {
|
||||
return Config{
|
||||
LabelSelector: opts.DefaultLabelSelector,
|
||||
FieldSelector: opts.DefaultFieldSelector,
|
||||
Transform: opts.DefaultTransform,
|
||||
UnsafeDisableDeepCopy: opts.DefaultUnsafeDisableDeepCopy,
|
||||
}
|
||||
}
|
||||
|
||||
func byObjectToConfig(byObject ByObject) Config {
|
||||
return Config{
|
||||
LabelSelector: byObject.Label,
|
||||
FieldSelector: byObject.Field,
|
||||
Transform: byObject.Transform,
|
||||
UnsafeDisableDeepCopy: byObject.UnsafeDisableDeepCopy,
|
||||
}
|
||||
}
|
||||
|
||||
type newCacheFunc func(config Config, namespace string) Cache
|
||||
|
||||
func newCache(restConfig *rest.Config, opts Options) newCacheFunc {
|
||||
return func(config Config, namespace string) Cache {
|
||||
return &informerCache{
|
||||
scheme: opts.Scheme,
|
||||
Informers: internal.NewInformers(restConfig, &internal.InformersOpts{
|
||||
HTTPClient: opts.HTTPClient,
|
||||
Scheme: opts.Scheme,
|
||||
Mapper: opts.Mapper,
|
||||
ResyncPeriod: *opts.SyncPeriod,
|
||||
Namespace: namespace,
|
||||
Selector: internal.Selector{
|
||||
Label: config.LabelSelector,
|
||||
Field: config.FieldSelector,
|
||||
},
|
||||
Transform: config.Transform,
|
||||
WatchErrorHandler: opts.DefaultWatchErrorHandler,
|
||||
UnsafeDisableDeepCopy: ptr.Deref(config.UnsafeDisableDeepCopy, false),
|
||||
NewInformer: opts.newInformer,
|
||||
}),
|
||||
readerFailOnMissingInformer: opts.ReaderFailOnMissingInformer,
|
||||
}
|
||||
return New(config, *combined)
|
||||
}
|
||||
}
|
||||
|
||||
func (options Options) inheritFrom(inherited Options) (*Options, error) {
|
||||
var (
|
||||
combined Options
|
||||
err error
|
||||
)
|
||||
combined.Scheme = combineScheme(inherited.Scheme, options.Scheme)
|
||||
combined.Mapper = selectMapper(inherited.Mapper, options.Mapper)
|
||||
combined.Resync = selectResync(inherited.Resync, options.Resync)
|
||||
combined.Namespace = selectNamespace(inherited.Namespace, options.Namespace)
|
||||
combined.SelectorsByObject, combined.DefaultSelector, err = combineSelectors(inherited, options, combined.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
combined.UnsafeDisableDeepCopyByObject, err = combineUnsafeDeepCopy(inherited, options, combined.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
combined.TransformByObject, combined.DefaultTransform, err = combineTransforms(inherited, options, combined.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &combined, nil
|
||||
}
|
||||
|
||||
func combineScheme(schemes ...*runtime.Scheme) *runtime.Scheme {
|
||||
var out *runtime.Scheme
|
||||
for _, sch := range schemes {
|
||||
if sch == nil {
|
||||
continue
|
||||
}
|
||||
for gvk, t := range sch.AllKnownTypes() {
|
||||
if out == nil {
|
||||
out = runtime.NewScheme()
|
||||
}
|
||||
out.AddKnownTypeWithName(gvk, reflect.New(t).Interface().(runtime.Object))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func selectMapper(def, override meta.RESTMapper) meta.RESTMapper {
|
||||
if override != nil {
|
||||
return override
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func selectResync(def, override *time.Duration) *time.Duration {
|
||||
if override != nil {
|
||||
return override
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func selectNamespace(def, override string) string {
|
||||
if override != "" {
|
||||
return override
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func combineSelectors(inherited, options Options, scheme *runtime.Scheme) (SelectorsByObject, ObjectSelector, error) {
|
||||
// Selectors are combined via logical AND.
|
||||
// - Combined label selector is a union of the selectors requirements from both sets of options.
|
||||
// - Combined field selector uses fields.AndSelectors with the combined list of non-nil field selectors
|
||||
// defined in both sets of options.
|
||||
//
|
||||
// There is a bunch of complexity here because we need to convert to SelectorsByGVK
|
||||
// to be able to match keys between options and inherited and then convert back to SelectorsByObject
|
||||
optionsSelectorsByGVK, err := convertToByGVK(options.SelectorsByObject, options.DefaultSelector, scheme)
|
||||
if err != nil {
|
||||
return nil, ObjectSelector{}, err
|
||||
}
|
||||
inheritedSelectorsByGVK, err := convertToByGVK(inherited.SelectorsByObject, inherited.DefaultSelector, inherited.Scheme)
|
||||
if err != nil {
|
||||
return nil, ObjectSelector{}, err
|
||||
}
|
||||
|
||||
for gvk, inheritedSelector := range inheritedSelectorsByGVK {
|
||||
optionsSelectorsByGVK[gvk] = combineSelector(inheritedSelector, optionsSelectorsByGVK[gvk])
|
||||
}
|
||||
return convertToByObject(optionsSelectorsByGVK, scheme)
|
||||
}
|
||||
|
||||
func combineSelector(selectors ...ObjectSelector) ObjectSelector {
|
||||
ls := make([]labels.Selector, 0, len(selectors))
|
||||
fs := make([]fields.Selector, 0, len(selectors))
|
||||
for _, s := range selectors {
|
||||
ls = append(ls, s.Label)
|
||||
fs = append(fs, s.Field)
|
||||
}
|
||||
return ObjectSelector{
|
||||
Label: combineLabelSelectors(ls...),
|
||||
Field: combineFieldSelectors(fs...),
|
||||
}
|
||||
}
|
||||
|
||||
func combineLabelSelectors(ls ...labels.Selector) labels.Selector {
|
||||
var combined labels.Selector
|
||||
for _, l := range ls {
|
||||
if l == nil {
|
||||
continue
|
||||
}
|
||||
if combined == nil {
|
||||
combined = labels.NewSelector()
|
||||
}
|
||||
reqs, _ := l.Requirements()
|
||||
combined = combined.Add(reqs...)
|
||||
}
|
||||
return combined
|
||||
}
|
||||
|
||||
func combineFieldSelectors(fs ...fields.Selector) fields.Selector {
|
||||
nonNil := fs[:0]
|
||||
for _, f := range fs {
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
nonNil = append(nonNil, f)
|
||||
}
|
||||
if len(nonNil) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(nonNil) == 1 {
|
||||
return nonNil[0]
|
||||
}
|
||||
return fields.AndSelectors(nonNil...)
|
||||
}
|
||||
|
||||
func combineUnsafeDeepCopy(inherited, options Options, scheme *runtime.Scheme) (DisableDeepCopyByObject, error) {
|
||||
// UnsafeDisableDeepCopyByObject is combined via precedence. Only if a value for a particular GVK is unset
|
||||
// in options will a value from inherited be used.
|
||||
optionsDisableDeepCopyByGVK, err := convertToDisableDeepCopyByGVK(options.UnsafeDisableDeepCopyByObject, options.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inheritedDisableDeepCopyByGVK, err := convertToDisableDeepCopyByGVK(inherited.UnsafeDisableDeepCopyByObject, inherited.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for gvk, inheritedDeepCopy := range inheritedDisableDeepCopyByGVK {
|
||||
if _, ok := optionsDisableDeepCopyByGVK[gvk]; !ok {
|
||||
if optionsDisableDeepCopyByGVK == nil {
|
||||
optionsDisableDeepCopyByGVK = map[schema.GroupVersionKind]bool{}
|
||||
}
|
||||
optionsDisableDeepCopyByGVK[gvk] = inheritedDeepCopy
|
||||
}
|
||||
}
|
||||
return convertToDisableDeepCopyByObject(optionsDisableDeepCopyByGVK, scheme)
|
||||
}
|
||||
|
||||
func combineTransforms(inherited, options Options, scheme *runtime.Scheme) (TransformByObject, toolscache.TransformFunc, error) {
|
||||
// Transform functions are combined via chaining. If both inherited and options define a transform
|
||||
// function, the transform function from inherited will be called first, and the transform function from
|
||||
// options will be called second.
|
||||
optionsTransformByGVK, err := convertToByGVK(options.TransformByObject, options.DefaultTransform, options.Scheme)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
inheritedTransformByGVK, err := convertToByGVK(inherited.TransformByObject, inherited.DefaultTransform, inherited.Scheme)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for gvk, inheritedTransform := range inheritedTransformByGVK {
|
||||
if optionsTransformByGVK == nil {
|
||||
optionsTransformByGVK = map[schema.GroupVersionKind]toolscache.TransformFunc{}
|
||||
}
|
||||
optionsTransformByGVK[gvk] = combineTransform(inheritedTransform, optionsTransformByGVK[gvk])
|
||||
}
|
||||
return convertToByObject(optionsTransformByGVK, scheme)
|
||||
}
|
||||
|
||||
func combineTransform(inherited, current toolscache.TransformFunc) toolscache.TransformFunc {
|
||||
if inherited == nil {
|
||||
return current
|
||||
}
|
||||
if current == nil {
|
||||
return inherited
|
||||
}
|
||||
return func(in interface{}) (interface{}, error) {
|
||||
mid, err := inherited(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return current(mid)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
||||
config = rest.CopyConfig(config)
|
||||
if config.UserAgent == "" {
|
||||
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||
}
|
||||
|
||||
// Use the rest HTTP client for the provided config if unset
|
||||
if opts.HTTPClient == nil {
|
||||
var err error
|
||||
opts.HTTPClient, err = rest.HTTPClientFor(config)
|
||||
if err != nil {
|
||||
return Options{}, fmt.Errorf("could not create HTTP client from config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Use the default Kubernetes Scheme if unset
|
||||
if opts.Scheme == nil {
|
||||
opts.Scheme = scheme.Scheme
|
||||
@@ -410,107 +412,106 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
||||
// Construct a new Mapper if unset
|
||||
if opts.Mapper == nil {
|
||||
var err error
|
||||
opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config)
|
||||
opts.Mapper, err = apiutil.NewDynamicRESTMapper(config, opts.HTTPClient)
|
||||
if err != nil {
|
||||
log.WithName("setup").Error(err, "Failed to get API Group-Resources")
|
||||
return opts, fmt.Errorf("could not create RESTMapper from config")
|
||||
return Options{}, fmt.Errorf("could not create RESTMapper from config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for namespace, cfg := range opts.DefaultNamespaces {
|
||||
cfg = defaultConfig(cfg, optionDefaultsToConfig(&opts))
|
||||
if namespace == metav1.NamespaceAll {
|
||||
cfg.FieldSelector = fields.AndSelectors(appendIfNotNil(namespaceAllSelector(maps.Keys(opts.DefaultNamespaces)), cfg.FieldSelector)...)
|
||||
}
|
||||
opts.DefaultNamespaces[namespace] = cfg
|
||||
}
|
||||
|
||||
for obj, byObject := range opts.ByObject {
|
||||
isNamespaced, err := apiutil.IsObjectNamespaced(obj, opts.Scheme, opts.Mapper)
|
||||
if err != nil {
|
||||
return opts, fmt.Errorf("failed to determine if %T is namespaced: %w", obj, err)
|
||||
}
|
||||
if !isNamespaced && byObject.Namespaces != nil {
|
||||
return opts, fmt.Errorf("type %T is not namespaced, but its ByObject.Namespaces setting is not nil", obj)
|
||||
}
|
||||
|
||||
// Default the namespace-level configs first, because they need to use the undefaulted type-level config.
|
||||
for namespace, config := range byObject.Namespaces {
|
||||
// 1. Default from the undefaulted type-level config
|
||||
config = defaultConfig(config, byObjectToConfig(byObject))
|
||||
|
||||
// 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
|
||||
// might not have an entry for the current namespace.
|
||||
if defaultNamespaceSettings, hasDefaultNamespace := opts.DefaultNamespaces[namespace]; hasDefaultNamespace {
|
||||
config = defaultConfig(config, defaultNamespaceSettings)
|
||||
}
|
||||
|
||||
// 3. Default from the global defaults
|
||||
config = defaultConfig(config, optionDefaultsToConfig(&opts))
|
||||
|
||||
if namespace == metav1.NamespaceAll {
|
||||
config.FieldSelector = fields.AndSelectors(
|
||||
appendIfNotNil(
|
||||
namespaceAllSelector(maps.Keys(byObject.Namespaces)),
|
||||
config.FieldSelector,
|
||||
)...,
|
||||
)
|
||||
}
|
||||
|
||||
byObject.Namespaces[namespace] = config
|
||||
}
|
||||
|
||||
defaultedConfig := defaultConfig(byObjectToConfig(byObject), optionDefaultsToConfig(&opts))
|
||||
byObject.Label = defaultedConfig.LabelSelector
|
||||
byObject.Field = defaultedConfig.FieldSelector
|
||||
byObject.Transform = defaultedConfig.Transform
|
||||
byObject.UnsafeDisableDeepCopy = defaultedConfig.UnsafeDisableDeepCopy
|
||||
|
||||
if isNamespaced && byObject.Namespaces == nil {
|
||||
byObject.Namespaces = opts.DefaultNamespaces
|
||||
}
|
||||
|
||||
opts.ByObject[obj] = byObject
|
||||
}
|
||||
|
||||
// Default the resync period to 10 hours if unset
|
||||
if opts.Resync == nil {
|
||||
opts.Resync = &defaultResyncTime
|
||||
if opts.SyncPeriod == nil {
|
||||
opts.SyncPeriod = &defaultSyncPeriod
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func convertToByGVK[T any](byObject map[client.Object]T, def T, scheme *runtime.Scheme) (map[schema.GroupVersionKind]T, error) {
|
||||
byGVK := map[schema.GroupVersionKind]T{}
|
||||
for object, value := range byObject {
|
||||
gvk, err := apiutil.GVKForObject(object, scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byGVK[gvk] = value
|
||||
func defaultConfig(toDefault, defaultFrom Config) Config {
|
||||
if toDefault.LabelSelector == nil {
|
||||
toDefault.LabelSelector = defaultFrom.LabelSelector
|
||||
}
|
||||
byGVK[schema.GroupVersionKind{}] = def
|
||||
return byGVK, nil
|
||||
}
|
||||
|
||||
func convertToByObject[T any](byGVK map[schema.GroupVersionKind]T, scheme *runtime.Scheme) (map[client.Object]T, T, error) {
|
||||
var byObject map[client.Object]T
|
||||
def := byGVK[schema.GroupVersionKind{}]
|
||||
for gvk, value := range byGVK {
|
||||
if gvk == (schema.GroupVersionKind{}) {
|
||||
continue
|
||||
}
|
||||
obj, err := scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, def, err
|
||||
}
|
||||
cObj, ok := obj.(client.Object)
|
||||
if !ok {
|
||||
return nil, def, fmt.Errorf("object %T for GVK %q does not implement client.Object", obj, gvk)
|
||||
}
|
||||
if byObject == nil {
|
||||
byObject = map[client.Object]T{}
|
||||
}
|
||||
byObject[cObj] = value
|
||||
if toDefault.FieldSelector == nil {
|
||||
toDefault.FieldSelector = defaultFrom.FieldSelector
|
||||
}
|
||||
return byObject, def, nil
|
||||
if toDefault.Transform == nil {
|
||||
toDefault.Transform = defaultFrom.Transform
|
||||
}
|
||||
if toDefault.UnsafeDisableDeepCopy == nil {
|
||||
toDefault.UnsafeDisableDeepCopy = defaultFrom.UnsafeDisableDeepCopy
|
||||
}
|
||||
|
||||
return toDefault
|
||||
}
|
||||
|
||||
// DisableDeepCopyByObject associate a client.Object's GVK to disable DeepCopy during get or list from cache.
|
||||
type DisableDeepCopyByObject map[client.Object]bool
|
||||
|
||||
var _ client.Object = &ObjectAll{}
|
||||
|
||||
// ObjectAll is the argument to represent all objects' types.
|
||||
type ObjectAll struct {
|
||||
client.Object
|
||||
}
|
||||
|
||||
func convertToDisableDeepCopyByGVK(disableDeepCopyByObject DisableDeepCopyByObject, scheme *runtime.Scheme) (internal.DisableDeepCopyByGVK, error) {
|
||||
disableDeepCopyByGVK := internal.DisableDeepCopyByGVK{}
|
||||
for obj, disable := range disableDeepCopyByObject {
|
||||
switch obj.(type) {
|
||||
case ObjectAll, *ObjectAll:
|
||||
disableDeepCopyByGVK[internal.GroupVersionKindAll] = disable
|
||||
default:
|
||||
gvk, err := apiutil.GVKForObject(obj, scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disableDeepCopyByGVK[gvk] = disable
|
||||
func namespaceAllSelector(namespaces []string) fields.Selector {
|
||||
selectors := make([]fields.Selector, 0, len(namespaces)-1)
|
||||
for _, namespace := range namespaces {
|
||||
if namespace != metav1.NamespaceAll {
|
||||
selectors = append(selectors, fields.OneTermNotEqualSelector("metadata.namespace", namespace))
|
||||
}
|
||||
}
|
||||
return disableDeepCopyByGVK, nil
|
||||
|
||||
return fields.AndSelectors(selectors...)
|
||||
}
|
||||
|
||||
func convertToDisableDeepCopyByObject(byGVK internal.DisableDeepCopyByGVK, scheme *runtime.Scheme) (DisableDeepCopyByObject, error) {
|
||||
var byObject DisableDeepCopyByObject
|
||||
for gvk, value := range byGVK {
|
||||
if byObject == nil {
|
||||
byObject = DisableDeepCopyByObject{}
|
||||
}
|
||||
if gvk == (schema.GroupVersionKind{}) {
|
||||
byObject[ObjectAll{}] = value
|
||||
continue
|
||||
}
|
||||
obj, err := scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cObj, ok := obj.(client.Object)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("object %T for GVK %q does not implement client.Object", obj, gvk)
|
||||
}
|
||||
|
||||
byObject[cObj] = value
|
||||
func appendIfNotNil[T comparable](a, b T) []T {
|
||||
if b != *new(T) {
|
||||
return []T{a, b}
|
||||
}
|
||||
return byObject, nil
|
||||
return []T{a}
|
||||
}
|
||||
|
||||
// TransformByObject associate a client.Object's GVK to a transformer function
|
||||
// to be applied when storing the object into the cache.
|
||||
type TransformByObject map[client.Object]toolscache.TransformFunc
|
||||
|
||||
135
vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go
generated
vendored
Normal file
135
vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// delegatingByGVKCache delegates to a type-specific cache if present
|
||||
// and uses the defaultCache otherwise.
|
||||
type delegatingByGVKCache struct {
|
||||
scheme *runtime.Scheme
|
||||
caches map[schema.GroupVersionKind]Cache
|
||||
defaultCache Cache
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
|
||||
cache, err := dbt.cacheForObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cache.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
|
||||
cache, err := dbt.cacheForObject(list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cache.List(ctx, list, opts...)
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) RemoveInformer(ctx context.Context, obj client.Object) error {
|
||||
cache, err := dbt.cacheForObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cache.RemoveInformer(ctx, obj)
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error) {
|
||||
cache, err := dbt.cacheForObject(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cache.GetInformer(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error) {
|
||||
return dbt.cacheForGVK(gvk).GetInformerForKind(ctx, gvk, opts...)
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) Start(ctx context.Context) error {
|
||||
allCaches := maps.Values(dbt.caches)
|
||||
allCaches = append(allCaches, dbt.defaultCache)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
errs := make(chan error)
|
||||
for idx := range allCaches {
|
||||
cache := allCaches[idx]
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := cache.Start(ctx); err != nil {
|
||||
errs <- err
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-errs:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) WaitForCacheSync(ctx context.Context) bool {
|
||||
synced := true
|
||||
for _, cache := range append(maps.Values(dbt.caches), dbt.defaultCache) {
|
||||
if !cache.WaitForCacheSync(ctx) {
|
||||
synced = false
|
||||
}
|
||||
}
|
||||
|
||||
return synced
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
|
||||
cache, err := dbt.cacheForObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cache.IndexField(ctx, obj, field, extractValue)
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) cacheForObject(o runtime.Object) (Cache, error) {
|
||||
gvk, err := apiutil.GVKForObject(o, dbt.scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
return dbt.cacheForGVK(gvk), nil
|
||||
}
|
||||
|
||||
func (dbt *delegatingByGVKCache) cacheForGVK(gvk schema.GroupVersionKind) Cache {
|
||||
if specific, hasSpecific := dbt.caches[gvk]; hasSpecific {
|
||||
return specific
|
||||
}
|
||||
|
||||
return dbt.defaultCache
|
||||
}
|
||||
171
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
generated
vendored
171
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
generated
vendored
@@ -19,14 +19,15 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
@@ -45,19 +46,38 @@ func (*ErrCacheNotStarted) Error() string {
|
||||
return "the cache is not started, can not read objects"
|
||||
}
|
||||
|
||||
// informerCache is a Kubernetes Object cache populated from InformersMap. informerCache wraps an InformersMap.
|
||||
var _ error = (*ErrCacheNotStarted)(nil)
|
||||
|
||||
// ErrResourceNotCached indicates that the resource type
|
||||
// the client asked the cache for is not cached, i.e. the
|
||||
// corresponding informer does not exist yet.
|
||||
type ErrResourceNotCached struct {
|
||||
GVK schema.GroupVersionKind
|
||||
}
|
||||
|
||||
// Error returns the error
|
||||
func (r ErrResourceNotCached) Error() string {
|
||||
return fmt.Sprintf("%s is not cached", r.GVK.String())
|
||||
}
|
||||
|
||||
var _ error = (*ErrResourceNotCached)(nil)
|
||||
|
||||
// informerCache is a Kubernetes Object cache populated from internal.Informers.
|
||||
// informerCache wraps internal.Informers.
|
||||
type informerCache struct {
|
||||
*internal.InformersMap
|
||||
scheme *runtime.Scheme
|
||||
*internal.Informers
|
||||
readerFailOnMissingInformer bool
|
||||
}
|
||||
|
||||
// Get implements Reader.
|
||||
func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out client.Object, opts ...client.GetOption) error {
|
||||
gvk, err := apiutil.GVKForObject(out, ip.Scheme)
|
||||
func (ic *informerCache) Get(ctx context.Context, key client.ObjectKey, out client.Object, opts ...client.GetOption) error {
|
||||
gvk, err := apiutil.GVKForObject(out, ic.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
started, cache, err := ip.InformersMap.Get(ctx, gvk, out)
|
||||
started, cache, err := ic.getInformerForKind(ctx, gvk, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -65,17 +85,17 @@ func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out clie
|
||||
if !started {
|
||||
return &ErrCacheNotStarted{}
|
||||
}
|
||||
return cache.Reader.Get(ctx, key, out)
|
||||
return cache.Reader.Get(ctx, key, out, opts...)
|
||||
}
|
||||
|
||||
// List implements Reader.
|
||||
func (ip *informerCache) List(ctx context.Context, out client.ObjectList, opts ...client.ListOption) error {
|
||||
gvk, cacheTypeObj, err := ip.objectTypeForListObject(out)
|
||||
func (ic *informerCache) List(ctx context.Context, out client.ObjectList, opts ...client.ListOption) error {
|
||||
gvk, cacheTypeObj, err := ic.objectTypeForListObject(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
started, cache, err := ip.InformersMap.Get(ctx, *gvk, cacheTypeObj)
|
||||
started, cache, err := ic.getInformerForKind(ctx, *gvk, cacheTypeObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -90,94 +110,117 @@ func (ip *informerCache) List(ctx context.Context, out client.ObjectList, opts .
|
||||
// objectTypeForListObject tries to find the runtime.Object and associated GVK
|
||||
// for a single object corresponding to the passed-in list type. We need them
|
||||
// because they are used as cache map key.
|
||||
func (ip *informerCache) objectTypeForListObject(list client.ObjectList) (*schema.GroupVersionKind, runtime.Object, error) {
|
||||
gvk, err := apiutil.GVKForObject(list, ip.Scheme)
|
||||
func (ic *informerCache) objectTypeForListObject(list client.ObjectList) (*schema.GroupVersionKind, runtime.Object, error) {
|
||||
gvk, err := apiutil.GVKForObject(list, ic.scheme)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// we need the non-list GVK, so chop off the "List" from the end of the kind
|
||||
if strings.HasSuffix(gvk.Kind, "List") && apimeta.IsListType(list) {
|
||||
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
|
||||
}
|
||||
// We need the non-list GVK, so chop off the "List" from the end of the kind.
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
|
||||
_, isUnstructured := list.(*unstructured.UnstructuredList)
|
||||
var cacheTypeObj runtime.Object
|
||||
if isUnstructured {
|
||||
// Handle unstructured.UnstructuredList.
|
||||
if _, isUnstructured := list.(runtime.Unstructured); isUnstructured {
|
||||
u := &unstructured.Unstructured{}
|
||||
u.SetGroupVersionKind(gvk)
|
||||
cacheTypeObj = u
|
||||
} else {
|
||||
itemsPtr, err := apimeta.GetItemsPtr(list)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// http://knowyourmeme.com/memes/this-is-fine
|
||||
elemType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
|
||||
if elemType.Kind() != reflect.Ptr {
|
||||
elemType = reflect.PtrTo(elemType)
|
||||
}
|
||||
|
||||
cacheTypeValue := reflect.Zero(elemType)
|
||||
var ok bool
|
||||
cacheTypeObj, ok = cacheTypeValue.Interface().(runtime.Object)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", list, cacheTypeValue.Interface())
|
||||
}
|
||||
return &gvk, u, nil
|
||||
}
|
||||
// Handle metav1.PartialObjectMetadataList.
|
||||
if _, isPartialObjectMetadata := list.(*metav1.PartialObjectMetadataList); isPartialObjectMetadata {
|
||||
pom := &metav1.PartialObjectMetadata{}
|
||||
pom.SetGroupVersionKind(gvk)
|
||||
return &gvk, pom, nil
|
||||
}
|
||||
|
||||
// Any other list type should have a corresponding non-list type registered
|
||||
// in the scheme. Use that to create a new instance of the non-list type.
|
||||
cacheTypeObj, err := ic.scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &gvk, cacheTypeObj, nil
|
||||
}
|
||||
|
||||
// GetInformerForKind returns the informer for the GroupVersionKind.
|
||||
func (ip *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
|
||||
// Map the gvk to an object
|
||||
obj, err := ip.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func applyGetOptions(opts ...InformerGetOption) *internal.GetOptions {
|
||||
cfg := &InformerGetOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
|
||||
_, i, err := ip.InformersMap.Get(ctx, gvk, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.Informer, err
|
||||
return (*internal.GetOptions)(cfg)
|
||||
}
|
||||
|
||||
// GetInformer returns the informer for the obj.
|
||||
func (ip *informerCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, ip.Scheme)
|
||||
// GetInformerForKind returns the informer for the GroupVersionKind. If no informer exists, one will be started.
|
||||
func (ic *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error) {
|
||||
// Map the gvk to an object
|
||||
obj, err := ic.scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, i, err := ip.InformersMap.Get(ctx, gvk, obj)
|
||||
_, i, err := ic.Informers.Get(ctx, gvk, obj, applyGetOptions(opts...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.Informer, err
|
||||
return i.Informer, nil
|
||||
}
|
||||
|
||||
// GetInformer returns the informer for the obj. If no informer exists, one will be started.
|
||||
func (ic *informerCache) GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, ic.scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, i, err := ic.Informers.Get(ctx, gvk, obj, applyGetOptions(opts...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.Informer, nil
|
||||
}
|
||||
|
||||
func (ic *informerCache) getInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *internal.Cache, error) {
|
||||
if ic.readerFailOnMissingInformer {
|
||||
cache, started, ok := ic.Informers.Peek(gvk, obj)
|
||||
if !ok {
|
||||
return false, nil, &ErrResourceNotCached{GVK: gvk}
|
||||
}
|
||||
return started, cache, nil
|
||||
}
|
||||
|
||||
return ic.Informers.Get(ctx, gvk, obj, &internal.GetOptions{})
|
||||
}
|
||||
|
||||
// RemoveInformer deactivates and removes the informer from the cache.
|
||||
func (ic *informerCache) RemoveInformer(_ context.Context, obj client.Object) error {
|
||||
gvk, err := apiutil.GVKForObject(obj, ic.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ic.Informers.Remove(gvk, obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NeedLeaderElection implements the LeaderElectionRunnable interface
|
||||
// to indicate that this can be started without requiring the leader lock.
|
||||
func (ip *informerCache) NeedLeaderElection() bool {
|
||||
func (ic *informerCache) NeedLeaderElection() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IndexField adds an indexer to the underlying cache, using extraction function to get
|
||||
// value(s) from the given field. This index can then be used by passing a field selector
|
||||
// IndexField adds an indexer to the underlying informer, using extractValue function to get
|
||||
// value(s) from the given field. This index can then be used by passing a field selector
|
||||
// to List. For one-to-one compatibility with "normal" field selectors, only return one value.
|
||||
// The values may be anything. They will automatically be prefixed with the namespace of the
|
||||
// given object, if present. The objects passed are guaranteed to be objects of the correct type.
|
||||
func (ip *informerCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
|
||||
informer, err := ip.GetInformer(ctx, obj)
|
||||
// The values may be anything. They will automatically be prefixed with the namespace of the
|
||||
// given object, if present. The objects passed are guaranteed to be objects of the correct type.
|
||||
func (ic *informerCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
|
||||
informer, err := ic.GetInformer(ctx, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return indexByField(informer, field, extractValue)
|
||||
}
|
||||
|
||||
func indexByField(indexer Informer, field string, extractor client.IndexerFunc) error {
|
||||
func indexByField(informer Informer, field string, extractValue client.IndexerFunc) error {
|
||||
indexFunc := func(objRaw interface{}) ([]string, error) {
|
||||
// TODO(directxman12): check if this is the correct type?
|
||||
obj, isObj := objRaw.(client.Object)
|
||||
@@ -190,7 +233,7 @@ func indexByField(indexer Informer, field string, extractor client.IndexerFunc)
|
||||
}
|
||||
ns := meta.GetNamespace()
|
||||
|
||||
rawVals := extractor(obj)
|
||||
rawVals := extractValue(obj)
|
||||
var vals []string
|
||||
if ns == "" {
|
||||
// if we're not doubling the keys for the namespaced case, just create a new slice with same length
|
||||
@@ -213,5 +256,5 @@ func indexByField(indexer Informer, field string, extractor client.IndexerFunc)
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
return indexer.AddIndexers(cache.Indexers{internal.FieldIndexName(field): indexFunc})
|
||||
return informer.AddIndexers(cache.Indexers{internal.FieldIndexName(field): indexFunc})
|
||||
}
|
||||
|
||||
141
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informertest/fake_cache.go
generated
vendored
Normal file
141
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informertest/fake_cache.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package informertest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
toolscache "k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
|
||||
)
|
||||
|
||||
var _ cache.Cache = &FakeInformers{}
|
||||
|
||||
// FakeInformers is a fake implementation of Informers.
|
||||
type FakeInformers struct {
|
||||
InformersByGVK map[schema.GroupVersionKind]toolscache.SharedIndexInformer
|
||||
Scheme *runtime.Scheme
|
||||
Error error
|
||||
Synced *bool
|
||||
}
|
||||
|
||||
// GetInformerForKind implements Informers.
|
||||
func (c *FakeInformers) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...cache.InformerGetOption) (cache.Informer, error) {
|
||||
if c.Scheme == nil {
|
||||
c.Scheme = scheme.Scheme
|
||||
}
|
||||
obj, err := c.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.informerFor(gvk, obj)
|
||||
}
|
||||
|
||||
// FakeInformerForKind implements Informers.
|
||||
func (c *FakeInformers) FakeInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (*controllertest.FakeInformer, error) {
|
||||
i, err := c.GetInformerForKind(ctx, gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.(*controllertest.FakeInformer), nil
|
||||
}
|
||||
|
||||
// GetInformer implements Informers.
|
||||
func (c *FakeInformers) GetInformer(ctx context.Context, obj client.Object, opts ...cache.InformerGetOption) (cache.Informer, error) {
|
||||
if c.Scheme == nil {
|
||||
c.Scheme = scheme.Scheme
|
||||
}
|
||||
gvks, _, err := c.Scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
return c.informerFor(gvk, obj)
|
||||
}
|
||||
|
||||
// RemoveInformer implements Informers.
|
||||
func (c *FakeInformers) RemoveInformer(ctx context.Context, obj client.Object) error {
|
||||
if c.Scheme == nil {
|
||||
c.Scheme = scheme.Scheme
|
||||
}
|
||||
gvks, _, err := c.Scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
delete(c.InformersByGVK, gvk)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForCacheSync implements Informers.
|
||||
func (c *FakeInformers) WaitForCacheSync(ctx context.Context) bool {
|
||||
if c.Synced == nil {
|
||||
return true
|
||||
}
|
||||
return *c.Synced
|
||||
}
|
||||
|
||||
// FakeInformerFor implements Informers.
|
||||
func (c *FakeInformers) FakeInformerFor(ctx context.Context, obj client.Object) (*controllertest.FakeInformer, error) {
|
||||
i, err := c.GetInformer(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.(*controllertest.FakeInformer), nil
|
||||
}
|
||||
|
||||
func (c *FakeInformers) informerFor(gvk schema.GroupVersionKind, _ runtime.Object) (toolscache.SharedIndexInformer, error) {
|
||||
if c.Error != nil {
|
||||
return nil, c.Error
|
||||
}
|
||||
if c.InformersByGVK == nil {
|
||||
c.InformersByGVK = map[schema.GroupVersionKind]toolscache.SharedIndexInformer{}
|
||||
}
|
||||
informer, ok := c.InformersByGVK[gvk]
|
||||
if ok {
|
||||
return informer, nil
|
||||
}
|
||||
|
||||
c.InformersByGVK[gvk] = &controllertest.FakeInformer{}
|
||||
return c.InformersByGVK[gvk], nil
|
||||
}
|
||||
|
||||
// Start implements Informers.
|
||||
func (c *FakeInformers) Start(ctx context.Context) error {
|
||||
return c.Error
|
||||
}
|
||||
|
||||
// IndexField implements Cache.
|
||||
func (c *FakeInformers) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get implements Cache.
|
||||
func (c *FakeInformers) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List implements Cache.
|
||||
func (c *FakeInformers) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
|
||||
return nil
|
||||
}
|
||||
77
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
77
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
@@ -23,13 +23,14 @@ import (
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/field/selector"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/field/selector"
|
||||
)
|
||||
|
||||
// CacheReader is a client.Reader.
|
||||
@@ -53,7 +54,7 @@ type CacheReader struct {
|
||||
}
|
||||
|
||||
// Get checks the indexer for the object and writes a copy of it if found.
|
||||
func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object, opts ...client.GetOption) error {
|
||||
func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object, _ ...client.GetOption) error {
|
||||
if c.scopeName == apimeta.RESTScopeNameRoot {
|
||||
key.Namespace = ""
|
||||
}
|
||||
@@ -67,9 +68,9 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Ob
|
||||
|
||||
// Not found, return an error
|
||||
if !exists {
|
||||
// Resource gets transformed into Kind in the error anyway, so this is fine
|
||||
return apierrors.NewNotFound(schema.GroupResource{
|
||||
Group: c.groupVersionKind.Group,
|
||||
Group: c.groupVersionKind.Group,
|
||||
// Resource gets set as Kind in the error so this is fine
|
||||
Resource: c.groupVersionKind.Kind,
|
||||
}, key.Name)
|
||||
}
|
||||
@@ -111,18 +112,20 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
|
||||
listOpts := client.ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
|
||||
if listOpts.Continue != "" {
|
||||
return fmt.Errorf("continue list option is not supported by the cache")
|
||||
}
|
||||
|
||||
switch {
|
||||
case listOpts.FieldSelector != nil:
|
||||
// TODO(directxman12): support more complicated field selectors by
|
||||
// combining multiple indices, GetIndexers, etc
|
||||
field, val, requiresExact := selector.RequiresExactMatch(listOpts.FieldSelector)
|
||||
requiresExact := selector.RequiresExactMatch(listOpts.FieldSelector)
|
||||
if !requiresExact {
|
||||
return fmt.Errorf("non-exact field matches are not supported by the cache")
|
||||
}
|
||||
// list all objects by the field selector. If this is namespaced and we have one, ask for the
|
||||
// namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
|
||||
// list all objects by the field selector. If this is namespaced and we have one, ask for the
|
||||
// namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
|
||||
// namespace.
|
||||
objs, err = c.indexer.ByIndex(FieldIndexName(field), KeyToNamespacedKey(listOpts.Namespace, val))
|
||||
objs, err = byIndexes(c.indexer, listOpts.FieldSelector.Requirements(), listOpts.Namespace)
|
||||
case listOpts.Namespace != "":
|
||||
objs, err = c.indexer.ByIndex(cache.NamespaceIndex, listOpts.Namespace)
|
||||
default:
|
||||
@@ -147,7 +150,7 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
|
||||
}
|
||||
obj, isObj := item.(runtime.Object)
|
||||
if !isObj {
|
||||
return fmt.Errorf("cache contained %T, which is not an Object", obj)
|
||||
return fmt.Errorf("cache contained %T, which is not an Object", item)
|
||||
}
|
||||
meta, err := apimeta.Accessor(obj)
|
||||
if err != nil {
|
||||
@@ -174,8 +177,56 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
|
||||
return apimeta.SetList(out, runtimeObjs)
|
||||
}
|
||||
|
||||
func byIndexes(indexer cache.Indexer, requires fields.Requirements, namespace string) ([]interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
objs []interface{}
|
||||
vals []string
|
||||
)
|
||||
indexers := indexer.GetIndexers()
|
||||
for idx, req := range requires {
|
||||
indexName := FieldIndexName(req.Field)
|
||||
indexedValue := KeyToNamespacedKey(namespace, req.Value)
|
||||
if idx == 0 {
|
||||
// we use first require to get snapshot data
|
||||
// TODO(halfcrazy): use complicated index when client-go provides byIndexes
|
||||
// https://github.com/kubernetes/kubernetes/issues/109329
|
||||
objs, err = indexer.ByIndex(indexName, indexedValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(objs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
fn, exist := indexers[indexName]
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("index with name %s does not exist", indexName)
|
||||
}
|
||||
filteredObjects := make([]interface{}, 0, len(objs))
|
||||
for _, obj := range objs {
|
||||
vals, err = fn(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, val := range vals {
|
||||
if val == indexedValue {
|
||||
filteredObjects = append(filteredObjects, obj)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(filteredObjects) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
objs = filteredObjects
|
||||
}
|
||||
return objs, nil
|
||||
}
|
||||
|
||||
// objectKeyToStorageKey converts an object key to store key.
|
||||
// It's akin to MetaNamespaceKeyFunc. It's separate from
|
||||
// It's akin to MetaNamespaceKeyFunc. It's separate from
|
||||
// String to allow keeping the key format easily in sync with
|
||||
// MetaNamespaceKeyFunc.
|
||||
func objectKeyToStoreKey(k client.ObjectKey) string {
|
||||
@@ -191,7 +242,7 @@ func FieldIndexName(field string) string {
|
||||
return "field:" + field
|
||||
}
|
||||
|
||||
// noNamespaceNamespace is used as the "namespace" when we want to list across all namespaces.
|
||||
// allNamespacesNamespace is used as the "namespace" when we want to list across all namespaces.
|
||||
const allNamespacesNamespace = "__all_namespaces"
|
||||
|
||||
// KeyToNamespacedKey prefixes the given index key with a namespace
|
||||
|
||||
126
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
generated
vendored
126
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
generated
vendored
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
// InformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
|
||||
// It uses a standard parameter codec constructed based on the given generated Scheme.
|
||||
type InformersMap struct {
|
||||
// we abstract over the details of structured/unstructured/metadata with the specificInformerMaps
|
||||
// TODO(directxman12): genericize this over different projections now that we have 3 different maps
|
||||
|
||||
structured *specificInformersMap
|
||||
unstructured *specificInformersMap
|
||||
metadata *specificInformersMap
|
||||
|
||||
// Scheme maps runtime.Objects to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// NewInformersMap creates a new InformersMap that can create informers for
|
||||
// both structured and unstructured objects.
|
||||
func NewInformersMap(config *rest.Config,
|
||||
scheme *runtime.Scheme,
|
||||
mapper meta.RESTMapper,
|
||||
resync time.Duration,
|
||||
namespace string,
|
||||
selectors SelectorsByGVK,
|
||||
disableDeepCopy DisableDeepCopyByGVK,
|
||||
transformers TransformFuncByObject,
|
||||
) *InformersMap {
|
||||
return &InformersMap{
|
||||
structured: newStructuredInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers),
|
||||
unstructured: newUnstructuredInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers),
|
||||
metadata: newMetadataInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers),
|
||||
|
||||
Scheme: scheme,
|
||||
}
|
||||
}
|
||||
|
||||
// Start calls Run on each of the informers and sets started to true. Blocks on the context.
|
||||
func (m *InformersMap) Start(ctx context.Context) error {
|
||||
go m.structured.Start(ctx)
|
||||
go m.unstructured.Start(ctx)
|
||||
go m.metadata.Start(ctx)
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForCacheSync waits until all the caches have been started and synced.
|
||||
func (m *InformersMap) WaitForCacheSync(ctx context.Context) bool {
|
||||
syncedFuncs := append([]cache.InformerSynced(nil), m.structured.HasSyncedFuncs()...)
|
||||
syncedFuncs = append(syncedFuncs, m.unstructured.HasSyncedFuncs()...)
|
||||
syncedFuncs = append(syncedFuncs, m.metadata.HasSyncedFuncs()...)
|
||||
|
||||
if !m.structured.waitForStarted(ctx) {
|
||||
return false
|
||||
}
|
||||
if !m.unstructured.waitForStarted(ctx) {
|
||||
return false
|
||||
}
|
||||
if !m.metadata.waitForStarted(ctx) {
|
||||
return false
|
||||
}
|
||||
return cache.WaitForCacheSync(ctx.Done(), syncedFuncs...)
|
||||
}
|
||||
|
||||
// Get will create a new Informer and add it to the map of InformersMap if none exists. Returns
|
||||
// the Informer from the map.
|
||||
func (m *InformersMap) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *MapEntry, error) {
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return m.unstructured.Get(ctx, gvk, obj)
|
||||
case *unstructured.UnstructuredList:
|
||||
return m.unstructured.Get(ctx, gvk, obj)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return m.metadata.Get(ctx, gvk, obj)
|
||||
case *metav1.PartialObjectMetadataList:
|
||||
return m.metadata.Get(ctx, gvk, obj)
|
||||
default:
|
||||
return m.structured.Get(ctx, gvk, obj)
|
||||
}
|
||||
}
|
||||
|
||||
// newStructuredInformersMap creates a new InformersMap for structured objects.
|
||||
func newStructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
|
||||
namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK, transformers TransformFuncByObject) *specificInformersMap {
|
||||
return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers, createStructuredListWatch)
|
||||
}
|
||||
|
||||
// newUnstructuredInformersMap creates a new InformersMap for unstructured objects.
|
||||
func newUnstructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
|
||||
namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK, transformers TransformFuncByObject) *specificInformersMap {
|
||||
return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers, createUnstructuredListWatch)
|
||||
}
|
||||
|
||||
// newMetadataInformersMap creates a new InformersMap for metadata-only objects.
|
||||
func newMetadataInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration,
|
||||
namespace string, selectors SelectorsByGVK, disableDeepCopy DisableDeepCopyByGVK, transformers TransformFuncByObject) *specificInformersMap {
|
||||
return newSpecificInformersMap(config, scheme, mapper, resync, namespace, selectors, disableDeepCopy, transformers, createMetadataListWatch)
|
||||
}
|
||||
35
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go
generated
vendored
35
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/disabledeepcopy.go
generated
vendored
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
// GroupVersionKindAll is the argument to represent all GroupVersionKind types.
|
||||
var GroupVersionKindAll = schema.GroupVersionKind{}
|
||||
|
||||
// DisableDeepCopyByGVK associate a GroupVersionKind to disable DeepCopy during get or list from cache.
|
||||
type DisableDeepCopyByGVK map[schema.GroupVersionKind]bool
|
||||
|
||||
// IsDisabled returns whether a GroupVersionKind is disabled DeepCopy.
|
||||
func (disableByGVK DisableDeepCopyByGVK) IsDisabled(gvk schema.GroupVersionKind) bool {
|
||||
if d, ok := disableByGVK[gvk]; ok {
|
||||
return d
|
||||
} else if d, ok = disableByGVK[GroupVersionKindAll]; ok {
|
||||
return d
|
||||
}
|
||||
return false
|
||||
}
|
||||
587
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
generated
vendored
Normal file
587
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
generated
vendored
Normal file
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/metadata"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/syncs"
|
||||
)
|
||||
|
||||
// InformersOpts configures an InformerMap.
|
||||
type InformersOpts struct {
|
||||
HTTPClient *http.Client
|
||||
Scheme *runtime.Scheme
|
||||
Mapper meta.RESTMapper
|
||||
ResyncPeriod time.Duration
|
||||
Namespace string
|
||||
NewInformer *func(cache.ListerWatcher, runtime.Object, time.Duration, cache.Indexers) cache.SharedIndexInformer
|
||||
Selector Selector
|
||||
Transform cache.TransformFunc
|
||||
UnsafeDisableDeepCopy bool
|
||||
WatchErrorHandler cache.WatchErrorHandler
|
||||
}
|
||||
|
||||
// NewInformers creates a new InformersMap that can create informers under the hood.
|
||||
func NewInformers(config *rest.Config, options *InformersOpts) *Informers {
|
||||
newInformer := cache.NewSharedIndexInformer
|
||||
if options.NewInformer != nil {
|
||||
newInformer = *options.NewInformer
|
||||
}
|
||||
return &Informers{
|
||||
config: config,
|
||||
httpClient: options.HTTPClient,
|
||||
scheme: options.Scheme,
|
||||
mapper: options.Mapper,
|
||||
tracker: tracker{
|
||||
Structured: make(map[schema.GroupVersionKind]*Cache),
|
||||
Unstructured: make(map[schema.GroupVersionKind]*Cache),
|
||||
Metadata: make(map[schema.GroupVersionKind]*Cache),
|
||||
},
|
||||
codecs: serializer.NewCodecFactory(options.Scheme),
|
||||
paramCodec: runtime.NewParameterCodec(options.Scheme),
|
||||
resync: options.ResyncPeriod,
|
||||
startWait: make(chan struct{}),
|
||||
namespace: options.Namespace,
|
||||
selector: options.Selector,
|
||||
transform: options.Transform,
|
||||
unsafeDisableDeepCopy: options.UnsafeDisableDeepCopy,
|
||||
newInformer: newInformer,
|
||||
watchErrorHandler: options.WatchErrorHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// Cache contains the cached data for an Cache.
|
||||
type Cache struct {
|
||||
// Informer is the cached informer
|
||||
Informer cache.SharedIndexInformer
|
||||
|
||||
// CacheReader wraps Informer and implements the CacheReader interface for a single type
|
||||
Reader CacheReader
|
||||
|
||||
// Stop can be used to stop this individual informer.
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
// Start starts the informer managed by a MapEntry.
|
||||
// Blocks until the informer stops. The informer can be stopped
|
||||
// either individually (via the entry's stop channel) or globally
|
||||
// via the provided stop argument.
|
||||
func (c *Cache) Start(stop <-chan struct{}) {
|
||||
// Stop on either the whole map stopping or just this informer being removed.
|
||||
internalStop, cancel := syncs.MergeChans(stop, c.stop)
|
||||
defer cancel()
|
||||
c.Informer.Run(internalStop)
|
||||
}
|
||||
|
||||
type tracker struct {
|
||||
Structured map[schema.GroupVersionKind]*Cache
|
||||
Unstructured map[schema.GroupVersionKind]*Cache
|
||||
Metadata map[schema.GroupVersionKind]*Cache
|
||||
}
|
||||
|
||||
// GetOptions provides configuration to customize the behavior when
|
||||
// getting an informer.
|
||||
type GetOptions struct {
|
||||
// BlockUntilSynced controls if the informer retrieval will block until the informer is synced. Defaults to `true`.
|
||||
BlockUntilSynced *bool
|
||||
}
|
||||
|
||||
// Informers create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
|
||||
// It uses a standard parameter codec constructed based on the given generated Scheme.
|
||||
type Informers struct {
|
||||
// httpClient is used to create a new REST client
|
||||
httpClient *http.Client
|
||||
|
||||
// scheme maps runtime.Objects to GroupVersionKinds
|
||||
scheme *runtime.Scheme
|
||||
|
||||
// config is used to talk to the apiserver
|
||||
config *rest.Config
|
||||
|
||||
// mapper maps GroupVersionKinds to Resources
|
||||
mapper meta.RESTMapper
|
||||
|
||||
// tracker tracks informers keyed by their type and groupVersionKind
|
||||
tracker tracker
|
||||
|
||||
// codecs is used to create a new REST client
|
||||
codecs serializer.CodecFactory
|
||||
|
||||
// paramCodec is used by list and watch
|
||||
paramCodec runtime.ParameterCodec
|
||||
|
||||
// resync is the base frequency the informers are resynced
|
||||
// a 10 percent jitter will be added to the resync period between informers
|
||||
// so that all informers will not send list requests simultaneously.
|
||||
resync time.Duration
|
||||
|
||||
// mu guards access to the map
|
||||
mu sync.RWMutex
|
||||
|
||||
// started is true if the informers have been started
|
||||
started bool
|
||||
|
||||
// startWait is a channel that is closed after the
|
||||
// informer has been started.
|
||||
startWait chan struct{}
|
||||
|
||||
// waitGroup is the wait group that is used to wait for all informers to stop
|
||||
waitGroup sync.WaitGroup
|
||||
|
||||
// stopped is true if the informers have been stopped
|
||||
stopped bool
|
||||
|
||||
// ctx is the context to stop informers
|
||||
ctx context.Context
|
||||
|
||||
// namespace is the namespace that all ListWatches are restricted to
|
||||
// default or empty string means all namespaces
|
||||
namespace string
|
||||
|
||||
selector Selector
|
||||
transform cache.TransformFunc
|
||||
unsafeDisableDeepCopy bool
|
||||
|
||||
// NewInformer allows overriding of the shared index informer constructor for testing.
|
||||
newInformer func(cache.ListerWatcher, runtime.Object, time.Duration, cache.Indexers) cache.SharedIndexInformer
|
||||
|
||||
// WatchErrorHandler allows the shared index informer's
|
||||
// watchErrorHandler to be set by overriding the options
|
||||
// or to use the default watchErrorHandler
|
||||
watchErrorHandler cache.WatchErrorHandler
|
||||
}
|
||||
|
||||
// Start calls Run on each of the informers and sets started to true. Blocks on the context.
|
||||
// It doesn't return start because it can't return an error, and it's not a runnable directly.
|
||||
func (ip *Informers) Start(ctx context.Context) error {
|
||||
func() {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
// Set the context so it can be passed to informers that are added later
|
||||
ip.ctx = ctx
|
||||
|
||||
// Start each informer
|
||||
for _, i := range ip.tracker.Structured {
|
||||
ip.startInformerLocked(i)
|
||||
}
|
||||
for _, i := range ip.tracker.Unstructured {
|
||||
ip.startInformerLocked(i)
|
||||
}
|
||||
for _, i := range ip.tracker.Metadata {
|
||||
ip.startInformerLocked(i)
|
||||
}
|
||||
|
||||
// Set started to true so we immediately start any informers added later.
|
||||
ip.started = true
|
||||
close(ip.startWait)
|
||||
}()
|
||||
<-ctx.Done() // Block until the context is done
|
||||
ip.mu.Lock()
|
||||
ip.stopped = true // Set stopped to true so we don't start any new informers
|
||||
ip.mu.Unlock()
|
||||
ip.waitGroup.Wait() // Block until all informers have stopped
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ip *Informers) startInformerLocked(cacheEntry *Cache) {
|
||||
// Don't start the informer in case we are already waiting for the items in
|
||||
// the waitGroup to finish, since waitGroups don't support waiting and adding
|
||||
// at the same time.
|
||||
if ip.stopped {
|
||||
return
|
||||
}
|
||||
|
||||
ip.waitGroup.Add(1)
|
||||
go func() {
|
||||
defer ip.waitGroup.Done()
|
||||
cacheEntry.Start(ip.ctx.Done())
|
||||
}()
|
||||
}
|
||||
|
||||
func (ip *Informers) waitForStarted(ctx context.Context) bool {
|
||||
select {
|
||||
case <-ip.startWait:
|
||||
return true
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// getHasSyncedFuncs returns all the HasSynced functions for the informers in this map.
|
||||
func (ip *Informers) getHasSyncedFuncs() []cache.InformerSynced {
|
||||
ip.mu.RLock()
|
||||
defer ip.mu.RUnlock()
|
||||
|
||||
res := make([]cache.InformerSynced, 0,
|
||||
len(ip.tracker.Structured)+len(ip.tracker.Unstructured)+len(ip.tracker.Metadata),
|
||||
)
|
||||
for _, i := range ip.tracker.Structured {
|
||||
res = append(res, i.Informer.HasSynced)
|
||||
}
|
||||
for _, i := range ip.tracker.Unstructured {
|
||||
res = append(res, i.Informer.HasSynced)
|
||||
}
|
||||
for _, i := range ip.tracker.Metadata {
|
||||
res = append(res, i.Informer.HasSynced)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// WaitForCacheSync waits until all the caches have been started and synced.
|
||||
func (ip *Informers) WaitForCacheSync(ctx context.Context) bool {
|
||||
if !ip.waitForStarted(ctx) {
|
||||
return false
|
||||
}
|
||||
return cache.WaitForCacheSync(ctx.Done(), ip.getHasSyncedFuncs()...)
|
||||
}
|
||||
|
||||
// Peek attempts to get the informer for the GVK, but does not start one if one does not exist.
|
||||
func (ip *Informers) Peek(gvk schema.GroupVersionKind, obj runtime.Object) (res *Cache, started bool, ok bool) {
|
||||
ip.mu.RLock()
|
||||
defer ip.mu.RUnlock()
|
||||
i, ok := ip.informersByType(obj)[gvk]
|
||||
return i, ip.started, ok
|
||||
}
|
||||
|
||||
// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
|
||||
// the Informer from the map.
|
||||
func (ip *Informers) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object, opts *GetOptions) (bool, *Cache, error) {
|
||||
// Return the informer if it is found
|
||||
i, started, ok := ip.Peek(gvk, obj)
|
||||
if !ok {
|
||||
var err error
|
||||
if i, started, err = ip.addInformerToMap(gvk, obj); err != nil {
|
||||
return started, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
shouldBlock := true
|
||||
if opts.BlockUntilSynced != nil {
|
||||
shouldBlock = *opts.BlockUntilSynced
|
||||
}
|
||||
|
||||
if shouldBlock && started && !i.Informer.HasSynced() {
|
||||
// Wait for it to sync before returning the Informer so that folks don't read from a stale cache.
|
||||
if !cache.WaitForCacheSync(ctx.Done(), i.Informer.HasSynced) {
|
||||
return started, nil, apierrors.NewTimeoutError(fmt.Sprintf("failed waiting for %T Informer to sync", obj), 0)
|
||||
}
|
||||
}
|
||||
|
||||
return started, i, nil
|
||||
}
|
||||
|
||||
// Remove removes an informer entry and stops it if it was running.
|
||||
func (ip *Informers) Remove(gvk schema.GroupVersionKind, obj runtime.Object) {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
informerMap := ip.informersByType(obj)
|
||||
|
||||
entry, ok := informerMap[gvk]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
close(entry.stop)
|
||||
delete(informerMap, gvk)
|
||||
}
|
||||
|
||||
func (ip *Informers) informersByType(obj runtime.Object) map[schema.GroupVersionKind]*Cache {
|
||||
switch obj.(type) {
|
||||
case runtime.Unstructured:
|
||||
return ip.tracker.Unstructured
|
||||
case *metav1.PartialObjectMetadata, *metav1.PartialObjectMetadataList:
|
||||
return ip.tracker.Metadata
|
||||
default:
|
||||
return ip.tracker.Structured
|
||||
}
|
||||
}
|
||||
|
||||
// addInformerToMap either returns an existing informer or creates a new informer, adds it to the map and returns it.
|
||||
func (ip *Informers) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.Object) (*Cache, bool, error) {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
// Check the cache to see if we already have an Informer. If we do, return the Informer.
|
||||
// This is for the case where 2 routines tried to get the informer when it wasn't in the map
|
||||
// so neither returned early, but the first one created it.
|
||||
if i, ok := ip.informersByType(obj)[gvk]; ok {
|
||||
return i, ip.started, nil
|
||||
}
|
||||
|
||||
// Create a NewSharedIndexInformer and add it to the map.
|
||||
listWatcher, err := ip.makeListWatcher(gvk, obj)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
sharedIndexInformer := ip.newInformer(&cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
ip.selector.ApplyToList(&opts)
|
||||
return listWatcher.ListFunc(opts)
|
||||
},
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
ip.selector.ApplyToList(&opts)
|
||||
opts.Watch = true // Watch needs to be set to true separately
|
||||
return listWatcher.WatchFunc(opts)
|
||||
},
|
||||
}, obj, calculateResyncPeriod(ip.resync), cache.Indexers{
|
||||
cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
|
||||
})
|
||||
|
||||
// Set WatchErrorHandler on SharedIndexInformer if set
|
||||
if ip.watchErrorHandler != nil {
|
||||
if err := sharedIndexInformer.SetWatchErrorHandler(ip.watchErrorHandler); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if there is a transformer for this gvk
|
||||
if err := sharedIndexInformer.SetTransform(ip.transform); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Create the new entry and set it in the map.
|
||||
i := &Cache{
|
||||
Informer: sharedIndexInformer,
|
||||
Reader: CacheReader{
|
||||
indexer: sharedIndexInformer.GetIndexer(),
|
||||
groupVersionKind: gvk,
|
||||
scopeName: mapping.Scope.Name(),
|
||||
disableDeepCopy: ip.unsafeDisableDeepCopy,
|
||||
},
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
ip.informersByType(obj)[gvk] = i
|
||||
|
||||
// Start the informer in case the InformersMap has started, otherwise it will be
|
||||
// started when the InformersMap starts.
|
||||
if ip.started {
|
||||
ip.startInformerLocked(i)
|
||||
}
|
||||
return i, ip.started, nil
|
||||
}
|
||||
|
||||
func (ip *Informers) makeListWatcher(gvk schema.GroupVersionKind, obj runtime.Object) (*cache.ListWatch, error) {
|
||||
// Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
|
||||
// groupVersionKind to the Resource API we will use.
|
||||
mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Figure out if the GVK we're dealing with is global, or namespace scoped.
|
||||
var namespace string
|
||||
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
|
||||
namespace = restrictNamespaceBySelector(ip.namespace, ip.selector)
|
||||
}
|
||||
|
||||
switch obj.(type) {
|
||||
//
|
||||
// Unstructured
|
||||
//
|
||||
case runtime.Unstructured:
|
||||
// If the rest configuration has a negotiated serializer passed in,
|
||||
// we should remove it and use the one that the dynamic client sets for us.
|
||||
cfg := rest.CopyConfig(ip.config)
|
||||
cfg.NegotiatedSerializer = nil
|
||||
dynamicClient, err := dynamic.NewForConfigAndClient(cfg, ip.httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resources := dynamicClient.Resource(mapping.Resource)
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
if namespace != "" {
|
||||
return resources.Namespace(namespace).List(ip.ctx, opts)
|
||||
}
|
||||
return resources.List(ip.ctx, opts)
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
if namespace != "" {
|
||||
return resources.Namespace(namespace).Watch(ip.ctx, opts)
|
||||
}
|
||||
return resources.Watch(ip.ctx, opts)
|
||||
},
|
||||
}, nil
|
||||
//
|
||||
// Metadata
|
||||
//
|
||||
case *metav1.PartialObjectMetadata, *metav1.PartialObjectMetadataList:
|
||||
// Always clear the negotiated serializer and use the one
|
||||
// set from the metadata client.
|
||||
cfg := rest.CopyConfig(ip.config)
|
||||
cfg.NegotiatedSerializer = nil
|
||||
|
||||
// Grab the metadata metadataClient.
|
||||
metadataClient, err := metadata.NewForConfigAndClient(cfg, ip.httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resources := metadataClient.Resource(mapping.Resource)
|
||||
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
var (
|
||||
list *metav1.PartialObjectMetadataList
|
||||
err error
|
||||
)
|
||||
if namespace != "" {
|
||||
list, err = resources.Namespace(namespace).List(ip.ctx, opts)
|
||||
} else {
|
||||
list, err = resources.List(ip.ctx, opts)
|
||||
}
|
||||
if list != nil {
|
||||
for i := range list.Items {
|
||||
list.Items[i].SetGroupVersionKind(gvk)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watcher watch.Interface, err error) {
|
||||
if namespace != "" {
|
||||
watcher, err = resources.Namespace(namespace).Watch(ip.ctx, opts)
|
||||
} else {
|
||||
watcher, err = resources.Watch(ip.ctx, opts)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newGVKFixupWatcher(gvk, watcher), nil
|
||||
},
|
||||
}, nil
|
||||
//
|
||||
// Structured.
|
||||
//
|
||||
default:
|
||||
client, err := apiutil.RESTClientForGVK(gvk, false, ip.config, ip.codecs, ip.httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listGVK := gvk.GroupVersion().WithKind(gvk.Kind + "List")
|
||||
listObj, err := ip.scheme.New(listGVK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
// Build the request.
|
||||
req := client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec)
|
||||
if namespace != "" {
|
||||
req.Namespace(namespace)
|
||||
}
|
||||
|
||||
// Create the resulting object, and execute the request.
|
||||
res := listObj.DeepCopyObject()
|
||||
if err := req.Do(ip.ctx).Into(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
// Build the request.
|
||||
req := client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec)
|
||||
if namespace != "" {
|
||||
req.Namespace(namespace)
|
||||
}
|
||||
// Call the watch.
|
||||
return req.Watch(ip.ctx)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// newGVKFixupWatcher adds a wrapper that preserves the GVK information when
|
||||
// events come in.
|
||||
//
|
||||
// This works around a bug where GVK information is not passed into mapping
|
||||
// functions when using the OnlyMetadata option in the builder.
|
||||
// This issue is most likely caused by kubernetes/kubernetes#80609.
|
||||
// See kubernetes-sigs/controller-runtime#1484.
|
||||
//
|
||||
// This was originally implemented as a cache.ResourceEventHandler wrapper but
|
||||
// that contained a data race which was resolved by setting the GVK in a watch
|
||||
// wrapper, before the objects are written to the cache.
|
||||
// See kubernetes-sigs/controller-runtime#1650.
|
||||
//
|
||||
// The original watch wrapper was found to be incompatible with
|
||||
// k8s.io/client-go/tools/cache.Reflector so it has been re-implemented as a
|
||||
// watch.Filter which is compatible.
|
||||
// See kubernetes-sigs/controller-runtime#1789.
|
||||
func newGVKFixupWatcher(gvk schema.GroupVersionKind, watcher watch.Interface) watch.Interface {
|
||||
return watch.Filter(
|
||||
watcher,
|
||||
func(in watch.Event) (watch.Event, bool) {
|
||||
in.Object.GetObjectKind().SetGroupVersionKind(gvk)
|
||||
return in, true
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// calculateResyncPeriod returns a duration based on the desired input
|
||||
// this is so that multiple controllers don't get into lock-step and all
|
||||
// hammer the apiserver with list requests simultaneously.
|
||||
func calculateResyncPeriod(resync time.Duration) time.Duration {
|
||||
// the factor will fall into [0.9, 1.1)
|
||||
factor := rand.Float64()/5.0 + 0.9 //nolint:gosec
|
||||
return time.Duration(float64(resync.Nanoseconds()) * factor)
|
||||
}
|
||||
|
||||
// restrictNamespaceBySelector returns either a global restriction for all ListWatches
|
||||
// if not default/empty, or the namespace that a ListWatch for the specific resource
|
||||
// is restricted to, based on a specified field selector for metadata.namespace field.
|
||||
func restrictNamespaceBySelector(namespaceOpt string, s Selector) string {
|
||||
if namespaceOpt != "" {
|
||||
// namespace is already restricted
|
||||
return namespaceOpt
|
||||
}
|
||||
fieldSelector := s.Field
|
||||
if fieldSelector == nil || fieldSelector.Empty() {
|
||||
return ""
|
||||
}
|
||||
// check whether a selector includes the namespace field
|
||||
value, found := fieldSelector.RequiresExactMatch("metadata.namespace")
|
||||
if found {
|
||||
return value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
480
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
generated
vendored
480
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
generated
vendored
@@ -1,480 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/metadata"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// clientListWatcherFunc knows how to create a ListWatcher.
|
||||
type createListWatcherFunc func(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error)
|
||||
|
||||
// newSpecificInformersMap returns a new specificInformersMap (like
|
||||
// the generical InformersMap, except that it doesn't implement WaitForCacheSync).
|
||||
func newSpecificInformersMap(config *rest.Config,
|
||||
scheme *runtime.Scheme,
|
||||
mapper meta.RESTMapper,
|
||||
resync time.Duration,
|
||||
namespace string,
|
||||
selectors SelectorsByGVK,
|
||||
disableDeepCopy DisableDeepCopyByGVK,
|
||||
transformers TransformFuncByObject,
|
||||
createListWatcher createListWatcherFunc,
|
||||
) *specificInformersMap {
|
||||
ip := &specificInformersMap{
|
||||
config: config,
|
||||
Scheme: scheme,
|
||||
mapper: mapper,
|
||||
informersByGVK: make(map[schema.GroupVersionKind]*MapEntry),
|
||||
codecs: serializer.NewCodecFactory(scheme),
|
||||
paramCodec: runtime.NewParameterCodec(scheme),
|
||||
resync: resync,
|
||||
startWait: make(chan struct{}),
|
||||
createListWatcher: createListWatcher,
|
||||
namespace: namespace,
|
||||
selectors: selectors.forGVK,
|
||||
disableDeepCopy: disableDeepCopy,
|
||||
transformers: transformers,
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
// MapEntry contains the cached data for an Informer.
|
||||
type MapEntry struct {
|
||||
// Informer is the cached informer
|
||||
Informer cache.SharedIndexInformer
|
||||
|
||||
// CacheReader wraps Informer and implements the CacheReader interface for a single type
|
||||
Reader CacheReader
|
||||
}
|
||||
|
||||
// specificInformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
|
||||
// It uses a standard parameter codec constructed based on the given generated Scheme.
|
||||
type specificInformersMap struct {
|
||||
// Scheme maps runtime.Objects to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// config is used to talk to the apiserver
|
||||
config *rest.Config
|
||||
|
||||
// mapper maps GroupVersionKinds to Resources
|
||||
mapper meta.RESTMapper
|
||||
|
||||
// informersByGVK is the cache of informers keyed by groupVersionKind
|
||||
informersByGVK map[schema.GroupVersionKind]*MapEntry
|
||||
|
||||
// codecs is used to create a new REST client
|
||||
codecs serializer.CodecFactory
|
||||
|
||||
// paramCodec is used by list and watch
|
||||
paramCodec runtime.ParameterCodec
|
||||
|
||||
// stop is the stop channel to stop informers
|
||||
stop <-chan struct{}
|
||||
|
||||
// resync is the base frequency the informers are resynced
|
||||
// a 10 percent jitter will be added to the resync period between informers
|
||||
// so that all informers will not send list requests simultaneously.
|
||||
resync time.Duration
|
||||
|
||||
// mu guards access to the map
|
||||
mu sync.RWMutex
|
||||
|
||||
// start is true if the informers have been started
|
||||
started bool
|
||||
|
||||
// startWait is a channel that is closed after the
|
||||
// informer has been started.
|
||||
startWait chan struct{}
|
||||
|
||||
// createClient knows how to create a client and a list object,
|
||||
// and allows for abstracting over the particulars of structured vs
|
||||
// unstructured objects.
|
||||
createListWatcher createListWatcherFunc
|
||||
|
||||
// namespace is the namespace that all ListWatches are restricted to
|
||||
// default or empty string means all namespaces
|
||||
namespace string
|
||||
|
||||
// selectors are the label or field selectors that will be added to the
|
||||
// ListWatch ListOptions.
|
||||
selectors func(gvk schema.GroupVersionKind) Selector
|
||||
|
||||
// disableDeepCopy indicates not to deep copy objects during get or list objects.
|
||||
disableDeepCopy DisableDeepCopyByGVK
|
||||
|
||||
// transform funcs are applied to objects before they are committed to the cache
|
||||
transformers TransformFuncByObject
|
||||
}
|
||||
|
||||
// Start calls Run on each of the informers and sets started to true. Blocks on the context.
|
||||
// It doesn't return start because it can't return an error, and it's not a runnable directly.
|
||||
func (ip *specificInformersMap) Start(ctx context.Context) {
|
||||
func() {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
// Set the stop channel so it can be passed to informers that are added later
|
||||
ip.stop = ctx.Done()
|
||||
|
||||
// Start each informer
|
||||
for _, informer := range ip.informersByGVK {
|
||||
go informer.Informer.Run(ctx.Done())
|
||||
}
|
||||
|
||||
// Set started to true so we immediately start any informers added later.
|
||||
ip.started = true
|
||||
close(ip.startWait)
|
||||
}()
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func (ip *specificInformersMap) waitForStarted(ctx context.Context) bool {
|
||||
select {
|
||||
case <-ip.startWait:
|
||||
return true
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// HasSyncedFuncs returns all the HasSynced functions for the informers in this map.
|
||||
func (ip *specificInformersMap) HasSyncedFuncs() []cache.InformerSynced {
|
||||
ip.mu.RLock()
|
||||
defer ip.mu.RUnlock()
|
||||
syncedFuncs := make([]cache.InformerSynced, 0, len(ip.informersByGVK))
|
||||
for _, informer := range ip.informersByGVK {
|
||||
syncedFuncs = append(syncedFuncs, informer.Informer.HasSynced)
|
||||
}
|
||||
return syncedFuncs
|
||||
}
|
||||
|
||||
// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
|
||||
// the Informer from the map.
|
||||
func (ip *specificInformersMap) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *MapEntry, error) {
|
||||
// Return the informer if it is found
|
||||
i, started, ok := func() (*MapEntry, bool, bool) {
|
||||
ip.mu.RLock()
|
||||
defer ip.mu.RUnlock()
|
||||
i, ok := ip.informersByGVK[gvk]
|
||||
return i, ip.started, ok
|
||||
}()
|
||||
|
||||
if !ok {
|
||||
var err error
|
||||
if i, started, err = ip.addInformerToMap(gvk, obj); err != nil {
|
||||
return started, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if started && !i.Informer.HasSynced() {
|
||||
// Wait for it to sync before returning the Informer so that folks don't read from a stale cache.
|
||||
if !cache.WaitForCacheSync(ctx.Done(), i.Informer.HasSynced) {
|
||||
return started, nil, apierrors.NewTimeoutError(fmt.Sprintf("failed waiting for %T Informer to sync", obj), 0)
|
||||
}
|
||||
}
|
||||
|
||||
return started, i, nil
|
||||
}
|
||||
|
||||
func (ip *specificInformersMap) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.Object) (*MapEntry, bool, error) {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
// Check the cache to see if we already have an Informer. If we do, return the Informer.
|
||||
// This is for the case where 2 routines tried to get the informer when it wasn't in the map
|
||||
// so neither returned early, but the first one created it.
|
||||
if i, ok := ip.informersByGVK[gvk]; ok {
|
||||
return i, ip.started, nil
|
||||
}
|
||||
|
||||
// Create a NewSharedIndexInformer and add it to the map.
|
||||
var lw *cache.ListWatch
|
||||
lw, err := ip.createListWatcher(gvk, ip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
ni := cache.NewSharedIndexInformer(lw, obj, resyncPeriod(ip.resync)(), cache.Indexers{
|
||||
cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
|
||||
})
|
||||
|
||||
// Check to see if there is a transformer for this gvk
|
||||
if err := ni.SetTransform(ip.transformers.Get(gvk)); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
rm, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
i := &MapEntry{
|
||||
Informer: ni,
|
||||
Reader: CacheReader{
|
||||
indexer: ni.GetIndexer(),
|
||||
groupVersionKind: gvk,
|
||||
scopeName: rm.Scope.Name(),
|
||||
disableDeepCopy: ip.disableDeepCopy.IsDisabled(gvk),
|
||||
},
|
||||
}
|
||||
ip.informersByGVK[gvk] = i
|
||||
|
||||
// Start the Informer if need by
|
||||
// TODO(seans): write thorough tests and document what happens here - can you add indexers?
|
||||
// can you add eventhandlers?
|
||||
if ip.started {
|
||||
go i.Informer.Run(ip.stop)
|
||||
}
|
||||
return i, ip.started, nil
|
||||
}
|
||||
|
||||
// newListWatch returns a new ListWatch object that can be used to create a SharedIndexInformer.
|
||||
func createStructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
|
||||
// Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
|
||||
// groupVersionKind to the Resource API we will use.
|
||||
mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := apiutil.RESTClientForGVK(gvk, false, ip.config, ip.codecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listGVK := gvk.GroupVersion().WithKind(gvk.Kind + "List")
|
||||
listObj, err := ip.Scheme.New(listGVK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: the functions that make use of this ListWatch should be adapted to
|
||||
// pass in their own contexts instead of relying on this fixed one here.
|
||||
ctx := context.TODO()
|
||||
// Create a new ListWatch for the obj
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
ip.selectors(gvk).ApplyToList(&opts)
|
||||
res := listObj.DeepCopyObject()
|
||||
namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
|
||||
isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
|
||||
err := client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do(ctx).Into(res)
|
||||
return res, err
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
ip.selectors(gvk).ApplyToList(&opts)
|
||||
// Watch needs to be set to true separately
|
||||
opts.Watch = true
|
||||
namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
|
||||
isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
|
||||
return client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Watch(ctx)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createUnstructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
|
||||
// Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
|
||||
// groupVersionKind to the Resource API we will use.
|
||||
mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the rest configuration has a negotiated serializer passed in,
|
||||
// we should remove it and use the one that the dynamic client sets for us.
|
||||
cfg := rest.CopyConfig(ip.config)
|
||||
cfg.NegotiatedSerializer = nil
|
||||
dynamicClient, err := dynamic.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: the functions that make use of this ListWatch should be adapted to
|
||||
// pass in their own contexts instead of relying on this fixed one here.
|
||||
ctx := context.TODO()
|
||||
// Create a new ListWatch for the obj
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
ip.selectors(gvk).ApplyToList(&opts)
|
||||
namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
|
||||
if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
||||
return dynamicClient.Resource(mapping.Resource).Namespace(namespace).List(ctx, opts)
|
||||
}
|
||||
return dynamicClient.Resource(mapping.Resource).List(ctx, opts)
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
ip.selectors(gvk).ApplyToList(&opts)
|
||||
// Watch needs to be set to true separately
|
||||
opts.Watch = true
|
||||
namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
|
||||
if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
||||
return dynamicClient.Resource(mapping.Resource).Namespace(namespace).Watch(ctx, opts)
|
||||
}
|
||||
return dynamicClient.Resource(mapping.Resource).Watch(ctx, opts)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createMetadataListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
|
||||
// Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
|
||||
// groupVersionKind to the Resource API we will use.
|
||||
mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Always clear the negotiated serializer and use the one
|
||||
// set from the metadata client.
|
||||
cfg := rest.CopyConfig(ip.config)
|
||||
cfg.NegotiatedSerializer = nil
|
||||
|
||||
// grab the metadata client
|
||||
client, err := metadata.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: the functions that make use of this ListWatch should be adapted to
|
||||
// pass in their own contexts instead of relying on this fixed one here.
|
||||
ctx := context.TODO()
|
||||
|
||||
// create the relevant listwatch
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
ip.selectors(gvk).ApplyToList(&opts)
|
||||
|
||||
var (
|
||||
list *metav1.PartialObjectMetadataList
|
||||
err error
|
||||
)
|
||||
namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
|
||||
if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
||||
list, err = client.Resource(mapping.Resource).Namespace(namespace).List(ctx, opts)
|
||||
} else {
|
||||
list, err = client.Resource(mapping.Resource).List(ctx, opts)
|
||||
}
|
||||
if list != nil {
|
||||
for i := range list.Items {
|
||||
list.Items[i].SetGroupVersionKind(gvk)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
ip.selectors(gvk).ApplyToList(&opts)
|
||||
// Watch needs to be set to true separately
|
||||
opts.Watch = true
|
||||
|
||||
var (
|
||||
watcher watch.Interface
|
||||
err error
|
||||
)
|
||||
namespace := restrictNamespaceBySelector(ip.namespace, ip.selectors(gvk))
|
||||
if namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
||||
watcher, err = client.Resource(mapping.Resource).Namespace(namespace).Watch(ctx, opts)
|
||||
} else {
|
||||
watcher, err = client.Resource(mapping.Resource).Watch(ctx, opts)
|
||||
}
|
||||
if watcher != nil {
|
||||
watcher = newGVKFixupWatcher(gvk, watcher)
|
||||
}
|
||||
return watcher, err
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newGVKFixupWatcher adds a wrapper that preserves the GVK information when
|
||||
// events come in.
|
||||
//
|
||||
// This works around a bug where GVK information is not passed into mapping
|
||||
// functions when using the OnlyMetadata option in the builder.
|
||||
// This issue is most likely caused by kubernetes/kubernetes#80609.
|
||||
// See kubernetes-sigs/controller-runtime#1484.
|
||||
//
|
||||
// This was originally implemented as a cache.ResourceEventHandler wrapper but
|
||||
// that contained a data race which was resolved by setting the GVK in a watch
|
||||
// wrapper, before the objects are written to the cache.
|
||||
// See kubernetes-sigs/controller-runtime#1650.
|
||||
//
|
||||
// The original watch wrapper was found to be incompatible with
|
||||
// k8s.io/client-go/tools/cache.Reflector so it has been re-implemented as a
|
||||
// watch.Filter which is compatible.
|
||||
// See kubernetes-sigs/controller-runtime#1789.
|
||||
func newGVKFixupWatcher(gvk schema.GroupVersionKind, watcher watch.Interface) watch.Interface {
|
||||
return watch.Filter(
|
||||
watcher,
|
||||
func(in watch.Event) (watch.Event, bool) {
|
||||
in.Object.GetObjectKind().SetGroupVersionKind(gvk)
|
||||
return in, true
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// resyncPeriod returns a function which generates a duration each time it is
|
||||
// invoked; this is so that multiple controllers don't get into lock-step and all
|
||||
// hammer the apiserver with list requests simultaneously.
|
||||
func resyncPeriod(resync time.Duration) func() time.Duration {
|
||||
return func() time.Duration {
|
||||
// the factor will fall into [0.9, 1.1)
|
||||
factor := rand.Float64()/5.0 + 0.9 //nolint:gosec
|
||||
return time.Duration(float64(resync.Nanoseconds()) * factor)
|
||||
}
|
||||
}
|
||||
|
||||
// restrictNamespaceBySelector returns either a global restriction for all ListWatches
|
||||
// if not default/empty, or the namespace that a ListWatch for the specific resource
|
||||
// is restricted to, based on a specified field selector for metadata.namespace field.
|
||||
func restrictNamespaceBySelector(namespaceOpt string, s Selector) string {
|
||||
if namespaceOpt != "" {
|
||||
// namespace is already restricted
|
||||
return namespaceOpt
|
||||
}
|
||||
fieldSelector := s.Field
|
||||
if fieldSelector == nil || fieldSelector.Empty() {
|
||||
return ""
|
||||
}
|
||||
// check whether a selector includes the namespace field
|
||||
value, found := fieldSelector.RequiresExactMatch("metadata.namespace")
|
||||
if found {
|
||||
return value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
15
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go
generated
vendored
15
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go
generated
vendored
@@ -20,23 +20,8 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// SelectorsByGVK associate a GroupVersionKind to a field/label selector.
|
||||
type SelectorsByGVK map[schema.GroupVersionKind]Selector
|
||||
|
||||
func (s SelectorsByGVK) forGVK(gvk schema.GroupVersionKind) Selector {
|
||||
if specific, found := s[gvk]; found {
|
||||
return specific
|
||||
}
|
||||
if defaultSelector, found := s[schema.GroupVersionKind{}]; found {
|
||||
return defaultSelector
|
||||
}
|
||||
|
||||
return Selector{}
|
||||
}
|
||||
|
||||
// Selector specify the label/field selector to fill in ListOptions.
|
||||
type Selector struct {
|
||||
Label labels.Selector
|
||||
|
||||
55
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go
generated
vendored
55
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go
generated
vendored
@@ -1,55 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// TransformFuncByObject provides access to the correct transform function for
|
||||
// any given GVK.
|
||||
type TransformFuncByObject interface {
|
||||
Set(runtime.Object, *runtime.Scheme, cache.TransformFunc) error
|
||||
Get(schema.GroupVersionKind) cache.TransformFunc
|
||||
SetDefault(transformer cache.TransformFunc)
|
||||
}
|
||||
|
||||
type transformFuncByGVK struct {
|
||||
defaultTransform cache.TransformFunc
|
||||
transformers map[schema.GroupVersionKind]cache.TransformFunc
|
||||
}
|
||||
|
||||
// TransformFuncByObjectFromMap creates a TransformFuncByObject from a map that
|
||||
// maps GVKs to TransformFuncs.
|
||||
func TransformFuncByObjectFromMap(in map[schema.GroupVersionKind]cache.TransformFunc) TransformFuncByObject {
|
||||
byGVK := &transformFuncByGVK{}
|
||||
if defaultFunc, hasDefault := in[schema.GroupVersionKind{}]; hasDefault {
|
||||
byGVK.defaultTransform = defaultFunc
|
||||
}
|
||||
delete(in, schema.GroupVersionKind{})
|
||||
byGVK.transformers = in
|
||||
return byGVK
|
||||
}
|
||||
|
||||
func (t *transformFuncByGVK) SetDefault(transformer cache.TransformFunc) {
|
||||
t.defaultTransform = transformer
|
||||
}
|
||||
|
||||
func (t *transformFuncByGVK) Set(obj runtime.Object, scheme *runtime.Scheme, transformer cache.TransformFunc) error {
|
||||
gvk, err := apiutil.GVKForObject(obj, scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.transformers[gvk] = transformer
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t transformFuncByGVK) Get(gvk schema.GroupVersionKind) cache.TransformFunc {
|
||||
if val, ok := t.transformers[gvk]; ok {
|
||||
return val
|
||||
}
|
||||
return t.defaultTransform
|
||||
}
|
||||
235
vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
generated
vendored
235
vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
generated
vendored
@@ -23,50 +23,42 @@ import (
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/rest"
|
||||
toolscache "k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
|
||||
)
|
||||
|
||||
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
|
||||
type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// a new global namespaced cache to handle cluster scoped resources.
|
||||
const globalCache = "_cluster-scope"
|
||||
|
||||
// MultiNamespacedCacheBuilder - Builder function to create a new multi-namespaced cache.
|
||||
// This will scope the cache to a list of namespaces. Listing for all namespaces
|
||||
// will list for all the namespaces that this knows about. By default this will create
|
||||
// a global cache for cluster scoped resource. Note that this is not intended
|
||||
// to be used for excluding namespaces, this is better done via a Predicate. Also note that
|
||||
// you may face performance issues when using this with a high number of namespaces.
|
||||
func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
|
||||
return func(config *rest.Config, opts Options) (Cache, error) {
|
||||
opts, err := defaultOpts(config, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func newMultiNamespaceCache(
|
||||
newCache newCacheFunc,
|
||||
scheme *runtime.Scheme,
|
||||
restMapper apimeta.RESTMapper,
|
||||
namespaces map[string]Config,
|
||||
globalConfig *Config, // may be nil in which case no cache for cluster-scoped objects will be created
|
||||
) Cache {
|
||||
// Create every namespace cache.
|
||||
caches := map[string]Cache{}
|
||||
for namespace, config := range namespaces {
|
||||
caches[namespace] = newCache(config, namespace)
|
||||
}
|
||||
|
||||
caches := map[string]Cache{}
|
||||
// Create a cache for cluster scoped resources if requested
|
||||
var clusterCache Cache
|
||||
if globalConfig != nil {
|
||||
clusterCache = newCache(*globalConfig, corev1.NamespaceAll)
|
||||
}
|
||||
|
||||
// create a cache for cluster scoped resources
|
||||
gCache, err := New(config, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating global cache: %w", err)
|
||||
}
|
||||
|
||||
for _, ns := range namespaces {
|
||||
opts.Namespace = ns
|
||||
c, err := New(config, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
caches[ns] = c
|
||||
}
|
||||
return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper, clusterCache: gCache}, nil
|
||||
return &multiNamespaceCache{
|
||||
namespaceToCache: caches,
|
||||
Scheme: scheme,
|
||||
RESTMapper: restMapper,
|
||||
clusterCache: clusterCache,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,90 +67,117 @@ func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
|
||||
// operator to a list of namespaces instead of watching every namespace
|
||||
// in the cluster.
|
||||
type multiNamespaceCache struct {
|
||||
namespaceToCache map[string]Cache
|
||||
Scheme *runtime.Scheme
|
||||
RESTMapper apimeta.RESTMapper
|
||||
namespaceToCache map[string]Cache
|
||||
clusterCache Cache
|
||||
}
|
||||
|
||||
var _ Cache = &multiNamespaceCache{}
|
||||
|
||||
// Methods for multiNamespaceCache to conform to the Informers interface.
|
||||
func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
|
||||
informers := map[string]Informer{}
|
||||
|
||||
// If the object is clusterscoped, get the informer from clusterCache,
|
||||
func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error) {
|
||||
// If the object is cluster scoped, get the informer from clusterCache,
|
||||
// if not use the namespaced caches.
|
||||
isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
|
||||
isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isNamespaced {
|
||||
clusterCacheInf, err := c.clusterCache.GetInformer(ctx, obj)
|
||||
clusterCacheInformer, err := c.clusterCache.GetInformer(ctx, obj, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
informers[globalCache] = clusterCacheInf
|
||||
|
||||
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
|
||||
return &multiNamespaceInformer{
|
||||
namespaceToInformer: map[string]Informer{
|
||||
globalCache: clusterCacheInformer,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
namespaceToInformer := map[string]Informer{}
|
||||
for ns, cache := range c.namespaceToCache {
|
||||
informer, err := cache.GetInformer(ctx, obj)
|
||||
informer, err := cache.GetInformer(ctx, obj, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
informers[ns] = informer
|
||||
namespaceToInformer[ns] = informer
|
||||
}
|
||||
|
||||
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
|
||||
return &multiNamespaceInformer{namespaceToInformer: namespaceToInformer}, nil
|
||||
}
|
||||
|
||||
func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
|
||||
informers := map[string]Informer{}
|
||||
|
||||
func (c *multiNamespaceCache) RemoveInformer(ctx context.Context, obj client.Object) error {
|
||||
// If the object is clusterscoped, get the informer from clusterCache,
|
||||
// if not use the namespaced caches.
|
||||
isNamespaced, err := objectutil.IsAPINamespacedWithGVK(gvk, c.Scheme, c.RESTMapper)
|
||||
isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isNamespaced {
|
||||
return c.clusterCache.RemoveInformer(ctx, obj)
|
||||
}
|
||||
|
||||
for _, cache := range c.namespaceToCache {
|
||||
err := cache.RemoveInformer(ctx, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error) {
|
||||
// If the object is cluster scoped, get the informer from clusterCache,
|
||||
// if not use the namespaced caches.
|
||||
isNamespaced, err := apiutil.IsGVKNamespaced(gvk, c.RESTMapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isNamespaced {
|
||||
clusterCacheInf, err := c.clusterCache.GetInformerForKind(ctx, gvk)
|
||||
clusterCacheInformer, err := c.clusterCache.GetInformerForKind(ctx, gvk, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
informers[globalCache] = clusterCacheInf
|
||||
|
||||
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
|
||||
return &multiNamespaceInformer{
|
||||
namespaceToInformer: map[string]Informer{
|
||||
globalCache: clusterCacheInformer,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
namespaceToInformer := map[string]Informer{}
|
||||
for ns, cache := range c.namespaceToCache {
|
||||
informer, err := cache.GetInformerForKind(ctx, gvk)
|
||||
informer, err := cache.GetInformerForKind(ctx, gvk, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
informers[ns] = informer
|
||||
namespaceToInformer[ns] = informer
|
||||
}
|
||||
|
||||
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
|
||||
return &multiNamespaceInformer{namespaceToInformer: namespaceToInformer}, nil
|
||||
}
|
||||
|
||||
func (c *multiNamespaceCache) Start(ctx context.Context) error {
|
||||
// start global cache
|
||||
go func() {
|
||||
err := c.clusterCache.Start(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "cluster scoped cache failed to start")
|
||||
}
|
||||
}()
|
||||
if c.clusterCache != nil {
|
||||
go func() {
|
||||
err := c.clusterCache.Start(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "cluster scoped cache failed to start")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// start namespaced caches
|
||||
for ns, cache := range c.namespaceToCache {
|
||||
go func(ns string, cache Cache) {
|
||||
err := cache.Start(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "multinamespace cache failed to start namespaced informer", "namespace", ns)
|
||||
if err := cache.Start(ctx); err != nil {
|
||||
log.Error(err, "multi-namespace cache failed to start namespaced informer", "namespace", ns)
|
||||
}
|
||||
}(ns, cache)
|
||||
}
|
||||
@@ -170,22 +189,22 @@ func (c *multiNamespaceCache) Start(ctx context.Context) error {
|
||||
func (c *multiNamespaceCache) WaitForCacheSync(ctx context.Context) bool {
|
||||
synced := true
|
||||
for _, cache := range c.namespaceToCache {
|
||||
if s := cache.WaitForCacheSync(ctx); !s {
|
||||
synced = s
|
||||
if !cache.WaitForCacheSync(ctx) {
|
||||
synced = false
|
||||
}
|
||||
}
|
||||
|
||||
// check if cluster scoped cache has synced
|
||||
if !c.clusterCache.WaitForCacheSync(ctx) {
|
||||
if c.clusterCache != nil && !c.clusterCache.WaitForCacheSync(ctx) {
|
||||
synced = false
|
||||
}
|
||||
return synced
|
||||
}
|
||||
|
||||
func (c *multiNamespaceCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
|
||||
isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
|
||||
isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
|
||||
if err != nil {
|
||||
return nil //nolint:nilerr
|
||||
return err
|
||||
}
|
||||
|
||||
if !isNamespaced {
|
||||
@@ -201,7 +220,7 @@ func (c *multiNamespaceCache) IndexField(ctx context.Context, obj client.Object,
|
||||
}
|
||||
|
||||
func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
|
||||
isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
|
||||
isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -213,9 +232,12 @@ func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj
|
||||
|
||||
cache, ok := c.namespaceToCache[key.Namespace]
|
||||
if !ok {
|
||||
if global, hasGlobal := c.namespaceToCache[metav1.NamespaceAll]; hasGlobal {
|
||||
return global.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
return fmt.Errorf("unable to get: %v because of unknown namespace for the cache", key)
|
||||
}
|
||||
return cache.Get(ctx, key, obj)
|
||||
return cache.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
|
||||
// List multi namespace cache will get all the objects in the namespaces that the cache is watching if asked for all namespaces.
|
||||
@@ -223,7 +245,7 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
|
||||
listOpts := client.ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
|
||||
isNamespaced, err := objectutil.IsAPINamespaced(list, c.Scheme, c.RESTMapper)
|
||||
isNamespaced, err := apiutil.IsObjectNamespaced(list, c.Scheme, c.RESTMapper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -236,7 +258,7 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
|
||||
if listOpts.Namespace != corev1.NamespaceAll {
|
||||
cache, ok := c.namespaceToCache[listOpts.Namespace]
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to get: %v because of unknown namespace for the cache", listOpts.Namespace)
|
||||
return fmt.Errorf("unable to list: %v because of unknown namespace for the cache", listOpts.Namespace)
|
||||
}
|
||||
return cache.List(ctx, list, opts...)
|
||||
}
|
||||
@@ -269,12 +291,14 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
|
||||
return fmt.Errorf("object: %T must be a list type", list)
|
||||
}
|
||||
allItems = append(allItems, items...)
|
||||
|
||||
// The last list call should have the most correct resource version.
|
||||
resourceVersion = accessor.GetResourceVersion()
|
||||
if limitSet {
|
||||
// decrement Limit by the number of items
|
||||
// fetched from the current namespace.
|
||||
listOpts.Limit -= int64(len(items))
|
||||
|
||||
// if a Limit was set and the number of
|
||||
// items read has reached this set limit,
|
||||
// then stop reading.
|
||||
@@ -293,42 +317,71 @@ type multiNamespaceInformer struct {
|
||||
namespaceToInformer map[string]Informer
|
||||
}
|
||||
|
||||
type handlerRegistration struct {
|
||||
handles map[string]toolscache.ResourceEventHandlerRegistration
|
||||
}
|
||||
|
||||
type syncer interface {
|
||||
HasSynced() bool
|
||||
}
|
||||
|
||||
// HasSynced asserts that the handler has been called for the full initial state of the informer.
|
||||
// This uses syncer to be compatible between client-go 1.27+ and older versions when the interface changed.
|
||||
func (h handlerRegistration) HasSynced() bool {
|
||||
for _, reg := range h.handles {
|
||||
if s, ok := reg.(syncer); ok {
|
||||
if !s.HasSynced() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var _ Informer = &multiNamespaceInformer{}
|
||||
|
||||
// AddEventHandler adds the handler to each namespaced informer.
|
||||
// AddEventHandler adds the handler to each informer.
|
||||
func (i *multiNamespaceInformer) AddEventHandler(handler toolscache.ResourceEventHandler) (toolscache.ResourceEventHandlerRegistration, error) {
|
||||
handles := make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))
|
||||
handles := handlerRegistration{
|
||||
handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer)),
|
||||
}
|
||||
|
||||
for ns, informer := range i.namespaceToInformer {
|
||||
registration, err := informer.AddEventHandler(handler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handles[ns] = registration
|
||||
handles.handles[ns] = registration
|
||||
}
|
||||
|
||||
return handles, nil
|
||||
}
|
||||
|
||||
// AddEventHandlerWithResyncPeriod adds the handler with a resync period to each namespaced informer.
|
||||
func (i *multiNamespaceInformer) AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration) (toolscache.ResourceEventHandlerRegistration, error) {
|
||||
handles := make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))
|
||||
handles := handlerRegistration{
|
||||
handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer)),
|
||||
}
|
||||
|
||||
for ns, informer := range i.namespaceToInformer {
|
||||
registration, err := informer.AddEventHandlerWithResyncPeriod(handler, resyncPeriod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handles[ns] = registration
|
||||
handles.handles[ns] = registration
|
||||
}
|
||||
|
||||
return handles, nil
|
||||
}
|
||||
|
||||
// RemoveEventHandler removes a formerly added event handler given by its registration handle.
|
||||
// RemoveEventHandler removes a previously added event handler given by its registration handle.
|
||||
func (i *multiNamespaceInformer) RemoveEventHandler(h toolscache.ResourceEventHandlerRegistration) error {
|
||||
handles, ok := h.(map[string]toolscache.ResourceEventHandlerRegistration)
|
||||
handles, ok := h.(handlerRegistration)
|
||||
if !ok {
|
||||
return fmt.Errorf("it is not the registration returned by multiNamespaceInformer")
|
||||
return fmt.Errorf("registration is not a registration returned by multiNamespaceInformer")
|
||||
}
|
||||
for ns, informer := range i.namespaceToInformer {
|
||||
registration, ok := handles[ns]
|
||||
registration, ok := handles.handles[ns]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -339,7 +392,7 @@ func (i *multiNamespaceInformer) RemoveEventHandler(h toolscache.ResourceEventHa
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddIndexers adds the indexer for each namespaced informer.
|
||||
// AddIndexers adds the indexers to each informer.
|
||||
func (i *multiNamespaceInformer) AddIndexers(indexers toolscache.Indexers) error {
|
||||
for _, informer := range i.namespaceToInformer {
|
||||
err := informer.AddIndexers(indexers)
|
||||
@@ -350,11 +403,21 @@ func (i *multiNamespaceInformer) AddIndexers(indexers toolscache.Indexers) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasSynced checks if each namespaced informer has synced.
|
||||
// HasSynced checks if each informer has synced.
|
||||
func (i *multiNamespaceInformer) HasSynced() bool {
|
||||
for _, informer := range i.namespaceToInformer {
|
||||
if ok := informer.HasSynced(); !ok {
|
||||
return ok
|
||||
if !informer.HasSynced() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsStopped checks if each namespaced informer has stopped, returns false if any are still running.
|
||||
func (i *multiNamespaceInformer) IsStopped() bool {
|
||||
for _, informer := range i.namespaceToInformer {
|
||||
if stopped := informer.IsStopped(); !stopped {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user