Documentation
¶
Overview ¶
pkg/informer/factory.go
pkg/informer/namespace_filter.go
NamespaceFilter — pre-enqueue namespace restriction for the informer factory.
Problem: without this, namespace guards are enforced at the reconciler level. Every event from every namespace hits the queue. Workers dequeue and no-op. Under high event volume in restricted namespaces, the queue fills with items that do no real work, creating false queue pressure.
Solution: two-tier filtering.
Tier 1 — Namespace-scoped ListerWatcher. When allowedNamespaces has exactly one entry, the ListerWatcher itself is scoped to that namespace. The informer never receives events from other namespaces. Cache is clean. Zero overhead. Tier 2 — Pre-enqueue NamespaceFilter in handleEvent. For multiple allowed namespaces or any restricted namespaces, a NamespaceFilter is stored per GVK on the factory. handleEvent checks it before building the queue key. Items that fail the filter are dropped — they never enter the queue. Tier 3 — Reconciler check. Remains as a safety net for race conditions where the filter map was not yet populated. Defense in depth.
Priority: allowedNamespaces takes precedence over restrictedNamespaces when both are declared (same semantics as the existing reconciler guard).
Index ¶
- func NamespaceFilterSummary(f *NamespaceFilter) string
- type ClientProvider
- type Factory
- func (f *Factory) For(obj runtime.Object, ctx context.Context, opts Options) cache.SharedIndexInformer
- func (f *Factory) ForListerWatcher(lw cache.ListerWatcher, obj runtime.Object, ctx context.Context, opts Options) cache.SharedIndexInformer
- func (f *Factory) IsMissing(key string) bool
- func (f *Factory) IsReady() bool
- func (f *Factory) Missing() map[string]*InformerEntry
- func (f *Factory) Name() string
- func (f *Factory) RegisterNamespaceFilter(gvkStr string, filter *NamespaceFilter)
- func (f *Factory) Registered() map[string]*InformerEntry
- func (f *Factory) RemoveMissing(gvkStr string)
- func (f *Factory) SetMissingOnStartup(missing map[string]*InformerEntry)
- func (f *Factory) Shutdown(ctx context.Context)
- func (f *Factory) Start(ctx context.Context) error
- func (f *Factory) Started() bool
- func (f *Factory) Status() string
- func (f *Factory) WaitForCacheSync(ctx context.Context) bool
- type GenericClient
- type InformerEntry
- type NamespaceFilter
- type Options
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NamespaceFilterSummary ¶ added in v0.2.5
func NamespaceFilterSummary(f *NamespaceFilter) string
NamespaceFilterSummary returns a human-readable description of the filter for logging. Called once at informer registration, not on the hot path.
Types ¶
type ClientProvider ¶
type ClientProvider interface {
For(obj runtime.Object) (GenericClient, error)
}
type Factory ¶
type Factory struct {
// contains filtered or unexported fields
}
All mappings key: gvk
func SharedInformerFactory ¶
func (*Factory) For ¶
func (f *Factory) For(obj runtime.Object, ctx context.Context, opts Options) cache.SharedIndexInformer
For creates or returns a SharedIndexInformer for the given object type. Uses the client provider to build the ListerWatcher via newListWatch. Each type gets exactly one informer — subsequent calls return the cached one. The informer is not started here — Start() starts all registered informers.
func (*Factory) ForListerWatcher ¶
func (f *Factory) ForListerWatcher(lw cache.ListerWatcher, obj runtime.Object, ctx context.Context, opts Options) cache.SharedIndexInformer
ForListerWatcher creates or returns a SharedIndexInformer using an explicit ListerWatcher. Used for unstructured CRDs where the dynamic client provides the ListerWatcher directly, bypassing the typed client provider entirely. The scheme is never consulted — no conversion errors for unstructured types.
func (*Factory) Missing ¶
func (f *Factory) Missing() map[string]*InformerEntry
Missing CRDs on startup
func (*Factory) RegisterNamespaceFilter ¶ added in v0.2.5
func (f *Factory) RegisterNamespaceFilter(gvkStr string, filter *NamespaceFilter)
RegisterNamespaceFilter stores a namespace filter for a GVK on the factory. Called during CRD entry registration, before informers are started. Thread-safe — uses the factory's existing mutex.
func (*Factory) Registered ¶
func (f *Factory) Registered() map[string]*InformerEntry
Registered CRDs
func (*Factory) RemoveMissing ¶
RemoveMissing CRDs in between runs
func (*Factory) SetMissingOnStartup ¶
func (f *Factory) SetMissingOnStartup(missing map[string]*InformerEntry)
SetMissing CRDs on startup
func (*Factory) Start ¶
Start signals readiness and starts all registered informers. Must be called exactly once — enforced by ErrFactoryAlreadyStarted.
func (*Factory) Status ¶
Status returns a summary of all informers and their sync state. Used by the health server /katalog endpoint.
func (*Factory) WaitForCacheSync ¶
WaitForCacheSync blocks until all informer caches have synced or ctx is done. It only waits on informers that were successfully created and started. Informers for missing CRDs (where ListWatch failed) should never be added to the factory, so they are naturally excluded here.
type GenericClient ¶
type GenericClient interface {
List(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error)
Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
// ListInNamespace and WatchInNamespace scope the request to a single namespace.
// Used by Tier 1 (namespace-scoped ListerWatcher) when IsSingleNamespace() is true.
ListInNamespace(ctx context.Context, namespace string, opts metav1.ListOptions) (runtime.Object, error)
WatchInNamespace(ctx context.Context, namespace string, opts metav1.ListOptions) (watch.Interface, error)
}
type InformerEntry ¶
type InformerEntry struct {
Informer cache.SharedIndexInformer
Name string
Resync time.Duration
Missing bool
GVK *schema.GroupVersionKind
// Store the context and cancel function
Ctx context.Context // stored context
Cancel context.CancelFunc // stored cancel function
WasNeverStarted bool
}
InformerEntry holds an informer and its metadata — avoids storing a single shared opts on the factory which caused the name bug.
type NamespaceFilter ¶ added in v0.2.5
type NamespaceFilter struct {
// AllowedNamespaces — when non-empty, only events from these namespaces
// are enqueued. Everything else is dropped before reaching the queue.
// Takes precedence over RestrictedNamespaces when both are set.
AllowedNamespaces []string
// RestrictedNamespaces — when non-empty, events from these namespaces
// are dropped before reaching the queue. All other namespaces pass.
RestrictedNamespaces []string
}
NamespaceFilter holds the namespace restriction configuration for one GVK. Stored on the Factory keyed by GVK string. Checked in handleEvent before the item enters the queue.
func (*NamespaceFilter) Allows ¶ added in v0.2.5
func (f *NamespaceFilter) Allows(namespace string) bool
Allows returns true when the namespace passes this filter.
Logic (mirrors the existing CheckNamespace in pkg/reconciler):
- If AllowedNamespaces is declared — namespace must be in the list.
- Else if RestrictedNamespaces is declared — namespace must NOT be in the list.
- Otherwise — allow all.
func (*NamespaceFilter) IsActive ¶ added in v0.2.5
func (f *NamespaceFilter) IsActive() bool
IsActive returns true when this filter has any namespace restrictions. Filters with no restrictions are no-ops and can be skipped.
func (*NamespaceFilter) IsSingleNamespace ¶ added in v0.2.5
func (f *NamespaceFilter) IsSingleNamespace() bool
IsSingleNamespace returns true when the filter allows exactly one namespace. Used to decide whether to use a namespace-scoped ListerWatcher (Tier 1).
func (*NamespaceFilter) SingleNamespace ¶ added in v0.2.5
func (f *NamespaceFilter) SingleNamespace() string
SingleNamespace returns the single allowed namespace when IsSingleNamespace is true.
type Options ¶
type Options struct {
Name string
Resync time.Duration
Wq *queue.Workqueue
LabelSelector string
FieldSelector string
// Namespace scopes the ListerWatcher to a single namespace (Tier 1 filter).
// "" means cluster-scoped watch (all namespaces). Set by RegisterNamespaceFilter
// when IsSingleNamespace() is true.
Namespace string
}