2020-10-21 05:49:41 +00:00
|
|
|
/*
|
|
|
|
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 cache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2023-06-01 17:01:19 +00:00
|
|
|
"net/http"
|
2020-10-21 05:49:41 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
2023-06-01 17:01:19 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2023-02-01 17:06:36 +00:00
|
|
|
"k8s.io/apimachinery/pkg/fields"
|
|
|
|
"k8s.io/apimachinery/pkg/labels"
|
2020-10-21 05:49:41 +00:00
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
|
|
"k8s.io/client-go/rest"
|
|
|
|
toolscache "k8s.io/client-go/tools/cache"
|
2023-02-01 17:06:36 +00:00
|
|
|
|
2020-10-21 05:49:41 +00:00
|
|
|
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
|
|
|
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
|
|
|
)
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
var (
|
|
|
|
log = logf.RuntimeLog.WithName("object-cache")
|
|
|
|
defaultSyncPeriod = 10 * time.Hour
|
|
|
|
)
|
2020-10-21 05:49:41 +00:00
|
|
|
|
|
|
|
// Cache knows how to load Kubernetes objects, fetch informers to request
|
|
|
|
// to receive events for Kubernetes objects (at a low-level),
|
|
|
|
// and add indices to fields on the objects stored in the cache.
|
|
|
|
type Cache interface {
|
|
|
|
// Cache acts as a client to objects stored in the cache.
|
|
|
|
client.Reader
|
|
|
|
|
|
|
|
// Cache loads informers and adds field indices.
|
|
|
|
Informers
|
|
|
|
}
|
|
|
|
|
|
|
|
// Informers knows how to create or fetch informers for different
|
|
|
|
// group-version-kinds, and add indices to those informers. It's safe to call
|
|
|
|
// GetInformer from multiple threads.
|
|
|
|
type Informers interface {
|
|
|
|
// GetInformer fetches or constructs an informer for the given object that corresponds to a single
|
|
|
|
// API kind and resource.
|
2021-06-25 05:02:01 +00:00
|
|
|
GetInformer(ctx context.Context, obj client.Object) (Informer, error)
|
2020-10-21 05:49:41 +00:00
|
|
|
|
|
|
|
// GetInformerForKind is similar to GetInformer, except that it takes a group-version-kind, instead
|
|
|
|
// of the underlying object.
|
|
|
|
GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error)
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// Start runs all the informers known to this cache until the context is closed.
|
2020-10-21 05:49:41 +00:00
|
|
|
// It blocks.
|
2021-06-25 05:02:01 +00:00
|
|
|
Start(ctx context.Context) error
|
2020-10-21 05:49:41 +00:00
|
|
|
|
|
|
|
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
|
2021-06-25 05:02:01 +00:00
|
|
|
WaitForCacheSync(ctx context.Context) bool
|
2020-10-21 05:49:41 +00:00
|
|
|
|
|
|
|
// Informers knows how to add indices to the caches (informers) that it manages.
|
|
|
|
client.FieldIndexer
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// Informer - informer allows you interact with the underlying informer.
|
2020-10-21 05:49:41 +00:00
|
|
|
type Informer interface {
|
|
|
|
// AddEventHandler adds an event handler to the shared informer using the shared informer's resync
|
|
|
|
// period. Events to a single handler are delivered sequentially, but there is no coordination
|
|
|
|
// between different handlers.
|
2023-02-01 17:06:36 +00:00
|
|
|
// It returns a registration handle for the handler that can be used to remove
|
|
|
|
// the handler again.
|
|
|
|
AddEventHandler(handler toolscache.ResourceEventHandler) (toolscache.ResourceEventHandlerRegistration, error)
|
2020-10-21 05:49:41 +00:00
|
|
|
// AddEventHandlerWithResyncPeriod adds an event handler to the shared informer using the
|
|
|
|
// specified resync period. Events to a single handler are delivered sequentially, but there is
|
|
|
|
// no coordination between different handlers.
|
2023-02-01 17:06:36 +00:00
|
|
|
// It returns a registration handle for the handler that can be used to remove
|
|
|
|
// the handler again and an error if the handler cannot be added.
|
|
|
|
AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration) (toolscache.ResourceEventHandlerRegistration, error)
|
|
|
|
// RemoveEventHandler removes a formerly added event handler given by
|
|
|
|
// its registration handle.
|
|
|
|
// This function is guaranteed to be idempotent, and thread-safe.
|
|
|
|
RemoveEventHandler(handle toolscache.ResourceEventHandlerRegistration) error
|
2020-10-21 05:49:41 +00:00
|
|
|
// AddIndexers adds more indexers to this store. If you call this after you already have data
|
|
|
|
// in the store, the results are undefined.
|
|
|
|
AddIndexers(indexers toolscache.Indexers) error
|
2021-06-25 05:02:01 +00:00
|
|
|
// HasSynced return true if the informers underlying store has synced.
|
2020-10-21 05:49:41 +00:00
|
|
|
HasSynced() bool
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// Options are the optional arguments for creating a new InformersMap object.
|
2020-10-21 05:49:41 +00:00
|
|
|
type Options struct {
|
2023-06-01 17:01:19 +00:00
|
|
|
// HTTPClient is the http client to use for the REST client
|
|
|
|
HTTPClient *http.Client
|
|
|
|
|
2020-10-21 05:49:41 +00:00
|
|
|
// 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
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// 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
|
|
|
|
|
|
|
|
// Namespaces restricts the cache's ListWatch to the desired namespaces
|
2020-10-21 05:49:41 +00:00
|
|
|
// Default watches all namespaces
|
2023-06-01 17:01:19 +00:00
|
|
|
Namespaces []string
|
2021-06-25 05:02:01 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// 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
|
2021-09-02 12:01:06 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// 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
|
2021-12-08 13:50:47 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// 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.
|
2021-09-02 12:01:06 +00:00
|
|
|
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
|
|
|
|
// otherwise you will mutate the object in the cache.
|
2023-06-01 17:01:19 +00:00
|
|
|
//
|
|
|
|
// This is a global setting for all objects, and can be overridden by the ByObject setting.
|
|
|
|
UnsafeDisableDeepCopy *bool
|
|
|
|
}
|
2023-02-01 17:06:36 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// 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
|
2023-02-01 17:06:36 +00:00
|
|
|
// 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,
|
2023-06-01 17:01:19 +00:00
|
|
|
// and for updated objects.
|
|
|
|
Transform toolscache.TransformFunc
|
2023-02-01 17:06:36 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// 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
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
|
|
|
|
type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
|
2020-10-21 05:49:41 +00:00
|
|
|
|
|
|
|
// New initializes and returns a new Cache.
|
|
|
|
func New(config *rest.Config, opts Options) (Cache, error) {
|
2023-06-01 17:01:19 +00:00
|
|
|
if len(opts.Namespaces) == 0 {
|
|
|
|
opts.Namespaces = []string{metav1.NamespaceAll}
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
2023-06-01 17:01:19 +00:00
|
|
|
if len(opts.Namespaces) > 1 {
|
|
|
|
return newMultiNamespaceCache(config, opts)
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
opts, err := defaultOpts(config, opts)
|
2023-02-01 17:06:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
byGVK, err := convertToInformerOptsByGVK(opts.ByObject, opts.Scheme)
|
2023-02-01 17:06:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-06-01 17:01:19 +00:00
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
2023-08-14 20:06:34 +00:00
|
|
|
config = rest.CopyConfig(config)
|
|
|
|
if config.UserAgent == "" {
|
|
|
|
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
|
|
|
}
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
logger := log.WithName("setup")
|
2023-02-01 17:06:36 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// Use the rest HTTP client for the provided config if unset
|
|
|
|
if opts.HTTPClient == nil {
|
|
|
|
var err error
|
|
|
|
opts.HTTPClient, err = rest.HTTPClientFor(config)
|
2023-02-01 17:06:36 +00:00
|
|
|
if err != nil {
|
2023-06-01 17:01:19 +00:00
|
|
|
logger.Error(err, "Failed to get HTTP client")
|
|
|
|
return opts, fmt.Errorf("could not create HTTP client from config: %w", err)
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
2021-06-25 05:02:01 +00:00
|
|
|
}
|
|
|
|
|
2020-10-21 05:49:41 +00:00
|
|
|
// Use the default Kubernetes Scheme if unset
|
|
|
|
if opts.Scheme == nil {
|
|
|
|
opts.Scheme = scheme.Scheme
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct a new Mapper if unset
|
|
|
|
if opts.Mapper == nil {
|
|
|
|
var err error
|
2023-06-01 17:01:19 +00:00
|
|
|
opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config, opts.HTTPClient)
|
2020-10-21 05:49:41 +00:00
|
|
|
if err != nil {
|
2023-06-01 17:01:19 +00:00
|
|
|
logger.Error(err, "Failed to get API Group-Resources")
|
|
|
|
return opts, fmt.Errorf("could not create RESTMapper from config: %w", err)
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default the resync period to 10 hours if unset
|
2023-06-01 17:01:19 +00:00
|
|
|
if opts.SyncPeriod == nil {
|
|
|
|
opts.SyncPeriod = &defaultSyncPeriod
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
|
|
|
return opts, nil
|
|
|
|
}
|
2021-06-25 05:02:01 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
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 {
|
2021-06-25 05:02:01 +00:00
|
|
|
gvk, err := apiutil.GVKForObject(object, scheme)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-06-01 17:01:19 +00:00
|
|
|
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)
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
2023-06-01 17:01:19 +00:00
|
|
|
out[gvk] = internal.InformersOptsByGVK{
|
|
|
|
Selector: internal.Selector{
|
|
|
|
Field: byObject.Field,
|
|
|
|
Label: byObject.Label,
|
|
|
|
},
|
|
|
|
Transform: byObject.Transform,
|
|
|
|
UnsafeDisableDeepCopy: byObject.UnsafeDisableDeepCopy,
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-01 17:01:19 +00:00
|
|
|
return out, nil
|
2021-06-25 05:02:01 +00:00
|
|
|
}
|