mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
rebase: update sigs.k8s.io/controller-runtime to current version
There is no release for sigs.k8s.io/controller-runtime that supports Kubernetes v1.27. The main branch has all the required modifications, so we can use that for the time being. Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
2551a0b05f
commit
b1a4590967
488
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
488
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
@ -19,10 +19,11 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
@ -37,7 +38,10 @@ 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
|
||||
)
|
||||
|
||||
// Cache knows how to load Kubernetes objects, fetch informers to request
|
||||
// to receive events for Kubernetes objects (at a low-level),
|
||||
@ -98,310 +102,152 @@ type Informer interface {
|
||||
HasSynced() bool
|
||||
}
|
||||
|
||||
// ObjectSelector is an alias name of internal.Selector.
|
||||
type ObjectSelector internal.Selector
|
||||
|
||||
// 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.
|
||||
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
|
||||
// Namespaces restricts the cache's ListWatch to the desired namespaces
|
||||
// Default watches all namespaces
|
||||
Namespace string
|
||||
Namespaces []string
|
||||
|
||||
// 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
|
||||
// DefaultLabelSelector will be used as a label selectors for all object types
|
||||
// unless they have a more specific selector set in ByObject.
|
||||
DefaultLabelSelector labels.Selector
|
||||
|
||||
// DefaultSelector will be used as selectors for all object types
|
||||
// that do not have a selector in SelectorsByObject defined.
|
||||
DefaultSelector ObjectSelector
|
||||
// DefaultFieldSelector will be used as a field selectors for all object types
|
||||
// unless they have a more specific selector set in ByObject.
|
||||
DefaultFieldSelector fields.Selector
|
||||
|
||||
// UnsafeDisableDeepCopyByObject indicates not to deep copy objects during get or
|
||||
// list objects per GVK at the specified object.
|
||||
// DefaultTransform will be used as transform for all object types
|
||||
// unless they have a more specific transform set in ByObject.
|
||||
DefaultTransform toolscache.TransformFunc
|
||||
|
||||
// ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
|
||||
ByObject map[client.Object]ByObject
|
||||
|
||||
// UnsafeDisableDeepCopy indicates not to deep copy objects during get or
|
||||
// list objects for EVERY 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
|
||||
//
|
||||
// This is a global setting for all objects, and can be overridden by the ByObject setting.
|
||||
UnsafeDisableDeepCopy *bool
|
||||
}
|
||||
|
||||
// TransformByObject is a map from GVKs to transformer functions which
|
||||
// ByObject offers more fine-grained control over the cache's ListWatch by object.
|
||||
type ByObject struct {
|
||||
// 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 map from objects 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
|
||||
// and for updated objects.
|
||||
Transform toolscache.TransformFunc
|
||||
|
||||
// DefaultTransform is the transform used for all GVKs which do
|
||||
// not have an explicit transform func set in TransformByObject
|
||||
DefaultTransform 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.
|
||||
UnsafeDisableDeepCopy *bool
|
||||
}
|
||||
|
||||
var defaultResyncTime = 10 * time.Hour
|
||||
// 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) {
|
||||
if len(opts.Namespaces) == 0 {
|
||||
opts.Namespaces = []string{metav1.NamespaceAll}
|
||||
}
|
||||
if len(opts.Namespaces) > 1 {
|
||||
return newMultiNamespaceCache(config, opts)
|
||||
}
|
||||
|
||||
opts, err := defaultOpts(config, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selectorsByGVK, err := convertToByGVK(opts.SelectorsByObject, opts.DefaultSelector, opts.Scheme)
|
||||
|
||||
byGVK, err := convertToInformerOptsByGVK(opts.ByObject, 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)
|
||||
// Set the default selector and transform.
|
||||
byGVK[schema.GroupVersionKind{}] = internal.InformersOptsByGVK{
|
||||
Selector: internal.Selector{
|
||||
Label: opts.DefaultLabelSelector,
|
||||
Field: opts.DefaultFieldSelector,
|
||||
},
|
||||
Transform: opts.DefaultTransform,
|
||||
UnsafeDisableDeepCopy: opts.UnsafeDisableDeepCopy,
|
||||
}
|
||||
|
||||
im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace, internalSelectorsByGVK, disableDeepCopyByGVK, transformByObj)
|
||||
return &informerCache{InformersMap: im}, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options, err = defaultOpts(config, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
combined, err := options.inheritFrom(inherited)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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)
|
||||
}
|
||||
return &informerCache{
|
||||
scheme: opts.Scheme,
|
||||
Informers: internal.NewInformers(config, &internal.InformersOpts{
|
||||
HTTPClient: opts.HTTPClient,
|
||||
Scheme: opts.Scheme,
|
||||
Mapper: opts.Mapper,
|
||||
ResyncPeriod: *opts.SyncPeriod,
|
||||
Namespace: opts.Namespaces[0],
|
||||
ByGVK: byGVK,
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
||||
logger := log.WithName("setup")
|
||||
|
||||
// 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 {
|
||||
logger.Error(err, "Failed to get HTTP client")
|
||||
return opts, 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,108 +256,38 @@ 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.NewDiscoveryRESTMapper(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")
|
||||
logger.Error(err, "Failed to get API Group-Resources")
|
||||
return opts, fmt.Errorf("could not create RESTMapper from config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func convertToInformerOptsByGVK(in map[client.Object]ByObject, scheme *runtime.Scheme) (map[schema.GroupVersionKind]internal.InformersOptsByGVK, error) {
|
||||
out := map[schema.GroupVersionKind]internal.InformersOptsByGVK{}
|
||||
for object, byObject := range in {
|
||||
gvk, err := apiutil.GVKForObject(object, scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byGVK[gvk] = value
|
||||
}
|
||||
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
|
||||
if _, ok := out[gvk]; ok {
|
||||
return nil, fmt.Errorf("duplicate cache options for GVK %v, cache.Options.ByObject has multiple types with the same GroupVersionKind", gvk)
|
||||
}
|
||||
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)
|
||||
}
|
||||
cObj.GetObjectKind().SetGroupVersionKind(gvk)
|
||||
if byObject == nil {
|
||||
byObject = map[client.Object]T{}
|
||||
}
|
||||
byObject[cObj] = value
|
||||
}
|
||||
return byObject, def, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
out[gvk] = internal.InformersOptsByGVK{
|
||||
Selector: internal.Selector{
|
||||
Field: byObject.Field,
|
||||
Label: byObject.Label,
|
||||
},
|
||||
Transform: byObject.Transform,
|
||||
UnsafeDisableDeepCopy: byObject.UnsafeDisableDeepCopy,
|
||||
}
|
||||
}
|
||||
return disableDeepCopyByGVK, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return byObject, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
|
84
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
generated
vendored
84
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
generated
vendored
@ -19,10 +19,10 @@ 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"
|
||||
@ -45,19 +45,21 @@ 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.
|
||||
// informerCache is a Kubernetes Object cache populated from internal.Informers.
|
||||
// informerCache wraps internal.Informers.
|
||||
type informerCache struct {
|
||||
*internal.InformersMap
|
||||
scheme *runtime.Scheme
|
||||
*internal.Informers
|
||||
}
|
||||
|
||||
// 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.Informers.Get(ctx, gvk, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -69,13 +71,13 @@ func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out clie
|
||||
}
|
||||
|
||||
// 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.Informers.Get(ctx, *gvk, cacheTypeObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -90,54 +92,46 @@ 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) {
|
||||
func (ic *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
|
||||
// Map the gvk to an object
|
||||
obj, err := ip.Scheme.New(gvk)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -145,13 +139,13 @@ func (ip *informerCache) GetInformerForKind(ctx context.Context, gvk schema.Grou
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (ic *informerCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, ic.scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, i, err := ip.InformersMap.Get(ctx, gvk, obj)
|
||||
_, i, err := ic.Informers.Get(ctx, gvk, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -160,7 +154,7 @@ func (ip *informerCache) GetInformer(ctx context.Context, obj client.Object) (In
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -169,8 +163,8 @@ func (ip *informerCache) NeedLeaderElection() bool {
|
||||
// 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)
|
||||
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
|
||||
}
|
||||
|
4
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
4
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
@ -27,9 +27,9 @@ import (
|
||||
"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.
|
||||
@ -147,7 +147,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 {
|
||||
|
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
|
||||
}
|
560
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
generated
vendored
Normal file
560
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go
generated
vendored
Normal file
@ -0,0 +1,560 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// InformersOpts configures an InformerMap.
|
||||
type InformersOpts struct {
|
||||
HTTPClient *http.Client
|
||||
Scheme *runtime.Scheme
|
||||
Mapper meta.RESTMapper
|
||||
ResyncPeriod time.Duration
|
||||
Namespace string
|
||||
ByGVK map[schema.GroupVersionKind]InformersOptsByGVK
|
||||
}
|
||||
|
||||
// InformersOptsByGVK configured additional by group version kind (or object)
|
||||
// in an InformerMap.
|
||||
type InformersOptsByGVK struct {
|
||||
Selector Selector
|
||||
Transform cache.TransformFunc
|
||||
UnsafeDisableDeepCopy *bool
|
||||
}
|
||||
|
||||
// NewInformers creates a new InformersMap that can create informers under the hood.
|
||||
func NewInformers(config *rest.Config, options *InformersOpts) *Informers {
|
||||
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,
|
||||
byGVK: options.ByGVK,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
type tracker struct {
|
||||
Structured map[schema.GroupVersionKind]*Cache
|
||||
Unstructured map[schema.GroupVersionKind]*Cache
|
||||
Metadata map[schema.GroupVersionKind]*Cache
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
byGVK map[schema.GroupVersionKind]InformersOptsByGVK
|
||||
}
|
||||
|
||||
func (ip *Informers) getSelector(gvk schema.GroupVersionKind) Selector {
|
||||
if ip.byGVK == nil {
|
||||
return Selector{}
|
||||
}
|
||||
if res, ok := ip.byGVK[gvk]; ok {
|
||||
return res.Selector
|
||||
}
|
||||
if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok {
|
||||
return res.Selector
|
||||
}
|
||||
return Selector{}
|
||||
}
|
||||
|
||||
func (ip *Informers) getTransform(gvk schema.GroupVersionKind) cache.TransformFunc {
|
||||
if ip.byGVK == nil {
|
||||
return nil
|
||||
}
|
||||
if res, ok := ip.byGVK[gvk]; ok {
|
||||
return res.Transform
|
||||
}
|
||||
if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok {
|
||||
return res.Transform
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ip *Informers) getDisableDeepCopy(gvk schema.GroupVersionKind) bool {
|
||||
if ip.byGVK == nil {
|
||||
return false
|
||||
}
|
||||
if res, ok := ip.byGVK[gvk]; ok && res.UnsafeDisableDeepCopy != nil {
|
||||
return *res.UnsafeDisableDeepCopy
|
||||
}
|
||||
if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok && res.UnsafeDisableDeepCopy != nil {
|
||||
return *res.UnsafeDisableDeepCopy
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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.Informer)
|
||||
}
|
||||
for _, i := range ip.tracker.Unstructured {
|
||||
ip.startInformerLocked(i.Informer)
|
||||
}
|
||||
for _, i := range ip.tracker.Metadata {
|
||||
ip.startInformerLocked(i.Informer)
|
||||
}
|
||||
|
||||
// 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(informer cache.SharedIndexInformer) {
|
||||
// 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()
|
||||
informer.Run(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()...)
|
||||
}
|
||||
|
||||
func (ip *Informers) get(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) (bool, *Cache, error) {
|
||||
// Return the informer if it is found
|
||||
i, started, ok := ip.get(gvk, obj)
|
||||
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 *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
|
||||
}
|
||||
}
|
||||
|
||||
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 := cache.NewSharedIndexInformer(&cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
ip.getSelector(gvk).ApplyToList(&opts)
|
||||
return listWatcher.ListFunc(opts)
|
||||
},
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
ip.getSelector(gvk).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,
|
||||
})
|
||||
|
||||
// Check to see if there is a transformer for this gvk
|
||||
if err := sharedIndexInformer.SetTransform(ip.getTransform(gvk)); 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.getDisableDeepCopy(gvk),
|
||||
},
|
||||
}
|
||||
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.Informer)
|
||||
}
|
||||
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.getSelector(gvk))
|
||||
}
|
||||
|
||||
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
|
||||
|
8
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go
generated
vendored
8
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/transformers.go
generated
vendored
@ -8,9 +8,9 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// TransformFuncByObject provides access to the correct transform function for
|
||||
// TransformFuncByGVK provides access to the correct transform function for
|
||||
// any given GVK.
|
||||
type TransformFuncByObject interface {
|
||||
type TransformFuncByGVK interface {
|
||||
Set(runtime.Object, *runtime.Scheme, cache.TransformFunc) error
|
||||
Get(schema.GroupVersionKind) cache.TransformFunc
|
||||
SetDefault(transformer cache.TransformFunc)
|
||||
@ -21,9 +21,9 @@ type transformFuncByGVK struct {
|
||||
transformers map[schema.GroupVersionKind]cache.TransformFunc
|
||||
}
|
||||
|
||||
// TransformFuncByObjectFromMap creates a TransformFuncByObject from a map that
|
||||
// TransformFuncByGVKFromMap creates a TransformFuncByGVK from a map that
|
||||
// maps GVKs to TransformFuncs.
|
||||
func TransformFuncByObjectFromMap(in map[schema.GroupVersionKind]cache.TransformFunc) TransformFuncByObject {
|
||||
func TransformFuncByGVKFromMap(in map[schema.GroupVersionKind]cache.TransformFunc) TransformFuncByGVK {
|
||||
byGVK := &transformFuncByGVK{}
|
||||
if defaultFunc, hasDefault := in[schema.GroupVersionKind{}]; hasDefault {
|
||||
byGVK.defaultTransform = defaultFunc
|
||||
|
100
vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
generated
vendored
100
vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go
generated
vendored
@ -28,12 +28,9 @@ import (
|
||||
"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"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
|
||||
type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
|
||||
|
||||
// a new global namespaced cache to handle cluster scoped resources.
|
||||
const globalCache = "_cluster-scope"
|
||||
|
||||
@ -43,31 +40,43 @@ const globalCache = "_cluster-scope"
|
||||
// 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.
|
||||
//
|
||||
// Deprecated: Use cache.Options.Namespaces instead.
|
||||
func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
|
||||
return func(config *rest.Config, opts Options) (Cache, error) {
|
||||
opts, err := defaultOpts(config, opts)
|
||||
opts.Namespaces = namespaces
|
||||
return newMultiNamespaceCache(config, opts)
|
||||
}
|
||||
}
|
||||
|
||||
func newMultiNamespaceCache(config *rest.Config, opts Options) (Cache, error) {
|
||||
if len(opts.Namespaces) < 2 {
|
||||
return nil, fmt.Errorf("must specify more than one namespace to use multi-namespace cache")
|
||||
}
|
||||
opts, err := defaultOpts(config, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create every namespace cache.
|
||||
caches := map[string]Cache{}
|
||||
for _, ns := range opts.Namespaces {
|
||||
opts.Namespaces = []string{ns}
|
||||
c, err := New(config, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caches := map[string]Cache{}
|
||||
|
||||
// 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
|
||||
caches[ns] = c
|
||||
}
|
||||
|
||||
// Create a cache for cluster scoped resources.
|
||||
opts.Namespaces = []string{}
|
||||
gCache, err := New(config, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating global cache: %w", err)
|
||||
}
|
||||
|
||||
return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper, clusterCache: gCache}, nil
|
||||
}
|
||||
|
||||
// multiNamespaceCache knows how to handle multiple namespaced caches
|
||||
@ -89,7 +98,7 @@ func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object
|
||||
|
||||
// If the object is clusterscoped, 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
|
||||
}
|
||||
@ -119,7 +128,7 @@ func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema
|
||||
|
||||
// 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.IsGVKNamespaced(gvk, c.RESTMapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -183,9 +192,9 @@ func (c *multiNamespaceCache) WaitForCacheSync(ctx context.Context) bool {
|
||||
}
|
||||
|
||||
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 +210,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
|
||||
}
|
||||
@ -223,7 +232,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
|
||||
}
|
||||
@ -293,42 +302,63 @@ 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.
|
||||
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.
|
||||
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")
|
||||
}
|
||||
for ns, informer := range i.namespaceToInformer {
|
||||
registration, ok := handles[ns]
|
||||
registration, ok := handles.handles[ns]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
Reference in New Issue
Block a user