-
Notifications
You must be signed in to change notification settings - Fork 4
added informer manager #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
c1af4a3
added informer manager
cirisked c6321f6
gcilint fixes
cirisked 80f536a
better manager API
cirisked 7487e3c
cleanup
cirisked 84537ed
review fixes
cirisked 0d251b3
review fixes
cirisked 18dcaa8
ci fixes
cirisked f26c754
ci fixes
cirisked File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| // Package informer provides a shared informer manager for Kubernetes resources. | ||
| package informer | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "sync" | ||
| "time" | ||
|
|
||
| "github.com/sirupsen/logrus" | ||
| "k8s.io/client-go/informers" | ||
| "k8s.io/client-go/kubernetes" | ||
| listerv1 "k8s.io/client-go/listers/core/v1" | ||
| "k8s.io/client-go/tools/cache" | ||
|
|
||
| "github.com/castai/cluster-controller/internal/metrics" | ||
| ) | ||
|
|
||
| const ( | ||
| defaultCacheSyncTimeout = 30 * time.Second | ||
| ) | ||
|
|
||
| // Manager manages the global SharedInformerFactory and provides | ||
| // access to specific informers and listers. | ||
| type Manager struct { | ||
| log logrus.FieldLogger | ||
| factory informers.SharedInformerFactory | ||
| cacheSyncTimeout time.Duration | ||
|
|
||
| nodes *nodeInformer | ||
| pods *podInformer | ||
|
|
||
| started bool | ||
| cancelFunc context.CancelFunc | ||
| mu sync.RWMutex | ||
| } | ||
|
|
||
| // Option is a functional option for configuring the Manager. | ||
| type Option func(*Manager) | ||
|
|
||
| // WithCacheSyncTimeout sets the timeout for waiting for informer caches to sync. | ||
| func WithCacheSyncTimeout(timeout time.Duration) Option { | ||
| return func(m *Manager) { | ||
| m.cacheSyncTimeout = timeout | ||
| } | ||
| } | ||
|
|
||
| // WithNodeIndexers sets custom indexers for the node informer. | ||
| func WithNodeIndexers(indexers cache.Indexers) Option { | ||
| return func(m *Manager) { | ||
| m.nodes.indexers = indexers | ||
| } | ||
| } | ||
|
|
||
| // WithPodIndexers sets custom indexers for the pod informer. | ||
| func WithPodIndexers(indexers cache.Indexers) Option { | ||
| return func(m *Manager) { | ||
| m.pods.indexers = indexers | ||
| } | ||
| } | ||
|
|
||
| // NewManager creates a new Manager with the given clientset and resync period. | ||
| func NewManager( | ||
| log logrus.FieldLogger, | ||
| clientset kubernetes.Interface, | ||
| resyncPeriod time.Duration, | ||
| opts ...Option, | ||
| ) *Manager { | ||
| factory := informers.NewSharedInformerFactory(clientset, resyncPeriod) | ||
|
|
||
| nodes := &nodeInformer{ | ||
| informer: factory.Core().V1().Nodes().Informer(), | ||
| lister: factory.Core().V1().Nodes().Lister(), | ||
| } | ||
|
|
||
| pods := &podInformer{ | ||
| informer: factory.Core().V1().Pods().Informer(), | ||
| lister: factory.Core().V1().Pods().Lister(), | ||
| } | ||
|
|
||
| m := &Manager{ | ||
| log: log, | ||
| factory: factory, | ||
| cacheSyncTimeout: defaultCacheSyncTimeout, | ||
| nodes: nodes, | ||
| pods: pods, | ||
| } | ||
|
|
||
| for _, opt := range opts { | ||
| opt(m) | ||
| } | ||
|
|
||
| return m | ||
| } | ||
|
|
||
| // Start starts the informer factory and waits for all caches to sync. | ||
| // This method blocks until caches are synchronized or the context is canceled. | ||
| func (m *Manager) Start(ctx context.Context) error { | ||
| if m.started { | ||
| m.log.Warn("informer manager already started") | ||
| return nil | ||
| } | ||
|
|
||
| ctx, cancel := context.WithCancel(ctx) | ||
| m.cancelFunc = cancel | ||
|
|
||
| if err := m.addIndexers(); err != nil { | ||
| cancel() | ||
| return fmt.Errorf("adding indexers: %w", err) | ||
| } | ||
|
|
||
| m.log.Info("starting shared informer factory...") | ||
| m.factory.Start(ctx.Done()) | ||
|
|
||
| syncCtx, syncCancel := context.WithTimeout(ctx, m.cacheSyncTimeout) | ||
| defer syncCancel() | ||
|
|
||
| m.log.Info("waiting for informer caches to sync...") | ||
| if !cache.WaitForCacheSync(syncCtx.Done(), m.nodes.HasSynced, m.pods.HasSynced) { | ||
| cancel() | ||
| return fmt.Errorf("failed to sync informer caches within %v", m.cacheSyncTimeout) | ||
| } | ||
|
|
||
| metrics.IncrementInformerCacheSyncs("node", "success") | ||
| metrics.IncrementInformerCacheSyncs("pod", "success") | ||
|
|
||
| m.started = true | ||
|
|
||
| m.log.Info("informer caches synced successfully") | ||
|
|
||
| go m.reportCacheSize(ctx) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // Stop gracefully stops the informer factory. | ||
| func (m *Manager) Stop() { | ||
| if !m.started { | ||
| return | ||
| } | ||
|
|
||
| m.log.Info("stopping informer manager...") | ||
| if m.cancelFunc != nil { | ||
| m.cancelFunc() | ||
| m.cancelFunc = nil | ||
| } | ||
| m.started = false | ||
| m.log.Info("informer manager stopped") | ||
| } | ||
|
|
||
| // GetNodeLister returns the node lister for querying the node cache. | ||
| func (m *Manager) GetNodeLister() listerv1.NodeLister { | ||
| return m.nodes.Lister() | ||
| } | ||
|
|
||
| // GetNodeInformer returns the node informer for watching node events. | ||
| func (m *Manager) GetNodeInformer() cache.SharedIndexInformer { | ||
| return m.nodes.Informer() | ||
| } | ||
|
|
||
| // GetPodLister returns the pod lister for querying the pod cache. | ||
| func (m *Manager) GetPodLister() listerv1.PodLister { | ||
| return m.pods.Lister() | ||
| } | ||
|
|
||
| // GetPodInformer returns the pod informer for watching pod events. | ||
| func (m *Manager) GetPodInformer() cache.SharedIndexInformer { | ||
Trojan295 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return m.pods.Informer() | ||
| } | ||
|
|
||
| // GetFactory returns the underlying SharedInformerFactory for advanced use cases. | ||
| func (m *Manager) GetFactory() informers.SharedInformerFactory { | ||
| return m.factory | ||
| } | ||
|
|
||
| func (m *Manager) addIndexers() error { | ||
| if m.nodes.indexers != nil { | ||
| if err := m.nodes.informer.AddIndexers(m.nodes.indexers); err != nil { | ||
| return fmt.Errorf("adding node indexers: %w", err) | ||
| } | ||
| } | ||
| if m.pods.indexers != nil { | ||
| if err := m.pods.informer.AddIndexers(m.pods.indexers); err != nil { | ||
| return fmt.Errorf("adding pod indexers: %w", err) | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func (m *Manager) reportCacheSize(ctx context.Context) { | ||
| ticker := time.NewTicker(30 * time.Second) | ||
| defer ticker.Stop() | ||
|
|
||
| for { | ||
| select { | ||
| case <-ctx.Done(): | ||
| return | ||
| case <-ticker.C: | ||
| nodes := m.nodes.Informer().GetStore().ListKeys() | ||
| size := len(nodes) | ||
| m.log.WithField("cache_size", size).Debug("node informer cache size") | ||
| metrics.SetInformerCacheSize("node", size) | ||
|
|
||
| pods := m.pods.Informer().GetStore().ListKeys() | ||
| size = len(pods) | ||
| m.log.WithField("cache_size", size).Debug("pod informer cache size") | ||
| metrics.SetInformerCacheSize("pod", size) | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the informers have been already started,
m.cancelFuncwill not be nil, so it's enough just to check for that. Also based on anticipated usage, it seems also excessiveThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would like to disagree - i prefer verbosity over the context