5555type Interface interface {
5656 IndexLookup ([]cni.SecondaryNetKey , string ) * model.ResourceMetaData
5757 GetNodeByName (string ) (* model.ResourceMetaData , error )
58- InitFromConfig (string , Config , * operational.Metrics ) error
58+ InitFromConfig (string , * Config , * operational.Metrics ) error
5959}
6060
6161type Informers struct {
@@ -65,7 +65,11 @@ type Informers struct {
6565 nodes cache.SharedIndexInformer
6666 services cache.SharedIndexInformer
6767 // replicaSets caches the ReplicaSets as partially-filled *ObjectMeta pointers
68- replicaSets cache.SharedIndexInformer
68+ replicaSets cache.SharedIndexInformer
69+ // New informers for ownership tracking
70+ deployments cache.SharedIndexInformer
71+ // Config and channels
72+ config Config
6973 stopChan chan struct {}
7074 mdStopChan chan struct {}
7175 indexerHitMetric * prometheus.CounterVec
@@ -178,19 +182,120 @@ func (k *Informers) GetNodeByName(name string) (*model.ResourceMetaData, error)
178182}
179183
180184func (k * Informers ) checkParent (info * model.ResourceMetaData ) {
181- if info .OwnerKind == "ReplicaSet" {
182- item , ok , err := k .replicaSets .GetIndexer ().GetByKey (info .Namespace + "/" + info .OwnerName )
183- if err != nil {
184- log .WithError (err ).WithField ("key" , info .Namespace + "/" + info .OwnerName ).
185- Debug ("can't get ReplicaSet info from informer. Ignoring" )
186- } else if ok {
187- rsInfo := item .(* metav1.ObjectMeta )
188- if len (rsInfo .OwnerReferences ) > 0 {
189- info .OwnerKind = rsInfo .OwnerReferences [0 ].Kind
190- info .OwnerName = rsInfo .OwnerReferences [0 ].Name
185+ // Maximum 3 ownership hops: Pod → ReplicaSet → Deployment → Gateway
186+ // This allows tracking up to 3 levels beyond the initial resource
187+ const maxHops = 3
188+
189+ // If trackedKinds is empty, use legacy behavior (stop after ReplicaSet resolution)
190+ if len (k .config .trackedKinds ) == 0 {
191+ // Legacy behavior: only resolve ReplicaSet
192+ if info .OwnerKind == "ReplicaSet" {
193+ item , ok , err := k .replicaSets .GetIndexer ().GetByKey (info .Namespace + "/" + info .OwnerName )
194+ if err != nil {
195+ log .WithError (err ).WithField ("key" , info .Namespace + "/" + info .OwnerName ).
196+ Debug ("can't get ReplicaSet info from informer. Ignoring" )
197+ return
198+ }
199+ if ok {
200+ rsInfo := item .(* metav1.ObjectMeta )
201+ if len (rsInfo .OwnerReferences ) > 0 {
202+ info .OwnerKind = rsInfo .OwnerReferences [0 ].Kind
203+ info .OwnerName = rsInfo .OwnerReferences [0 ].Name
204+ }
205+ }
206+ }
207+ return
208+ }
209+
210+ // New behavior with trackedKinds: traverse ownership chain until we find a tracked kind or hit max depth
211+ for i := 0 ; i < maxHops ; i ++ {
212+ // Check if current owner is in trackedKinds
213+ if k .isTracked (info .OwnerKind ) {
214+ // This owner IS tracked. Try to get its parent to see if we can go higher.
215+ parent := k .getOwnerFromInformer (info .OwnerKind , info .Namespace , info .OwnerName )
216+ if parent == nil {
217+ // No parent exists → STOP at current tracked kind
218+ break
219+ }
220+ // Parent exists - check if parent is ALSO tracked
221+ if k .isTracked (parent .Kind ) {
222+ // Parent is also tracked → update and continue (prefer higher level)
223+ info .OwnerKind = parent .Kind
224+ info .OwnerName = parent .Name
225+ continue
191226 }
227+ // Parent exists but is NOT tracked → STOP at current tracked kind
228+ break
229+ }
230+
231+ // Current owner is NOT tracked → try to find a tracked parent
232+ parent := k .getOwnerFromInformer (info .OwnerKind , info .Namespace , info .OwnerName )
233+ if parent == nil {
234+ // No parent found → STOP at current (untracked) owner
235+ break
236+ }
237+
238+ // Update to parent and continue
239+ info .OwnerKind = parent .Kind
240+ info .OwnerName = parent .Name
241+ }
242+ }
243+
244+ // isTracked returns true if the given kind is in the trackedKinds configuration
245+ func (k * Informers ) isTracked (kind string ) bool {
246+ for _ , tracked := range k .config .trackedKinds {
247+ if tracked == kind {
248+ return true
192249 }
193250 }
251+ return false
252+ }
253+
254+ // OwnerInfo contains basic ownership information
255+ type OwnerInfo struct {
256+ Kind string
257+ Name string
258+ }
259+
260+ // getOwnerFromInformer retrieves the owner of a resource from the appropriate informer
261+ func (k * Informers ) getOwnerFromInformer (kind , namespace , name string ) * OwnerInfo {
262+ var informer cache.SharedIndexInformer
263+
264+ switch kind {
265+ case "ReplicaSet" :
266+ informer = k .replicaSets
267+ case "Deployment" :
268+ informer = k .deployments
269+ default :
270+ return nil
271+ }
272+
273+ if informer == nil {
274+ log .WithField ("kind" , kind ).Debug ("informer not initialized for this kind" )
275+ return nil
276+ }
277+
278+ item , ok , err := informer .GetIndexer ().GetByKey (namespace + "/" + name )
279+ if err != nil {
280+ log .WithError (err ).
281+ WithField ("kind" , kind ).
282+ WithField ("key" , namespace + "/" + name ).
283+ Debug ("can't get resource info from informer" )
284+ return nil
285+ }
286+ if ! ok {
287+ return nil
288+ }
289+
290+ meta := item .(* metav1.ObjectMeta )
291+ if len (meta .OwnerReferences ) == 0 {
292+ return nil
293+ }
294+
295+ return & OwnerInfo {
296+ Kind : meta .OwnerReferences [0 ].Kind ,
297+ Name : meta .OwnerReferences [0 ].Name ,
298+ }
194299}
195300
196301func (k * Informers ) getHostName (hostIP string ) string {
@@ -202,7 +307,7 @@ func (k *Informers) getHostName(hostIP string) string {
202307 return ""
203308}
204309
205- func (k * Informers ) initNodeInformer (informerFactory inf.SharedInformerFactory , cfg Config ) error {
310+ func (k * Informers ) initNodeInformer (informerFactory inf.SharedInformerFactory , cfg * Config ) error {
206311 nodes := informerFactory .Core ().V1 ().Nodes ().Informer ()
207312 // Transform any *v1.Node instance into a *Info instance to save space
208313 // in the informer's cache
@@ -255,7 +360,7 @@ func (k *Informers) initNodeInformer(informerFactory inf.SharedInformerFactory,
255360 return nil
256361}
257362
258- func (k * Informers ) initPodInformer (informerFactory inf.SharedInformerFactory , cfg Config , dynClient * dynamic.DynamicClient ) error {
363+ func (k * Informers ) initPodInformer (informerFactory inf.SharedInformerFactory , cfg * Config , dynClient * dynamic.DynamicClient ) error {
259364 pods := informerFactory .Core ().V1 ().Pods ().Informer ()
260365 // Transform any *v1.Pod instance into a *Info instance to save space
261366 // in the informer's cache
@@ -379,8 +484,32 @@ func (k *Informers) initReplicaSetInformer(informerFactory metadatainformer.Shar
379484 return nil
380485}
381486
382- func (k * Informers ) InitFromConfig (kubeconfig string , infConfig Config , opMetrics * operational.Metrics ) error {
487+ func (k * Informers ) initDeploymentInformer (informerFactory metadatainformer.SharedInformerFactory ) error {
488+ k .deployments = informerFactory .ForResource (
489+ schema.GroupVersionResource {
490+ Group : "apps" ,
491+ Version : "v1" ,
492+ Resource : "deployments" ,
493+ }).Informer ()
494+ if err := k .deployments .SetTransform (func (i interface {}) (interface {}, error ) {
495+ deploy , ok := i .(* metav1.PartialObjectMetadata )
496+ if ! ok {
497+ return nil , fmt .Errorf ("was expecting a Deployment. Got: %T" , i )
498+ }
499+ return & metav1.ObjectMeta {
500+ Name : deploy .Name ,
501+ Namespace : deploy .Namespace ,
502+ OwnerReferences : deploy .OwnerReferences ,
503+ }, nil
504+ }); err != nil {
505+ return fmt .Errorf ("can't set Deployments transform: %w" , err )
506+ }
507+ return nil
508+ }
509+
510+ func (k * Informers ) InitFromConfig (kubeconfig string , infConfig * Config , opMetrics * operational.Metrics ) error {
383511 // Initialization variables
512+ k .config = * infConfig
384513 k .stopChan = make (chan struct {})
385514 k .mdStopChan = make (chan struct {})
386515
@@ -413,7 +542,7 @@ func (k *Informers) InitFromConfig(kubeconfig string, infConfig Config, opMetric
413542 return nil
414543}
415544
416- func (k * Informers ) initInformers (client kubernetes.Interface , metaClient metadata.Interface , dynClient * dynamic.DynamicClient , cfg Config ) error {
545+ func (k * Informers ) initInformers (client kubernetes.Interface , metaClient metadata.Interface , dynClient * dynamic.DynamicClient , cfg * Config ) error {
417546 informerFactory := inf .NewSharedInformerFactory (client , syncTime )
418547 metadataInformerFactory := metadatainformer .NewSharedInformerFactory (metaClient , syncTime )
419548 err := k .initNodeInformer (informerFactory , cfg )
@@ -433,6 +562,17 @@ func (k *Informers) initInformers(client kubernetes.Interface, metaClient metada
433562 return err
434563 }
435564
565+ // Initialize additional informers based on trackedKinds configuration
566+ for _ , kind := range cfg .trackedKinds {
567+ if kind == "Deployment" {
568+ // Gateway requires Deployment informer to navigate ownership chain
569+ log .Debugf ("initializing Deployment informer (trackedKinds)" )
570+ if err := k .initDeploymentInformer (metadataInformerFactory ); err != nil {
571+ return err
572+ }
573+ }
574+ }
575+
436576 // Informers expose an indexer
437577 log .Debugf ("adding indexers" )
438578 byIP := cache.Indexers {IndexIP : ipIndexer }
0 commit comments