@@ -168,23 +168,33 @@ type appHistoryIncident struct {
168168}
169169
170170// appRelationships is the structural neighborhood of an app, derived from the
171- // topology graph: what fronts it (Services/Ingress/Routes) and what supports it
172- // (config, autoscalers, disruption budgets). Counts where names add no value.
171+ // topology graph: what fronts it (Services/Ingress/Routes) and what supports it.
173172type appRelationships struct {
174- Services []string `json:"services,omitempty"`
175- Ingresses []string `json:"ingresses,omitempty"`
176- // "Kind/name" (routes are polymorphic); Services/Ingresses carry bare names
177- // since their kind is fixed.
178- Routes []string `json:"routes,omitempty"`
179- Configs int `json:"configs,omitempty"`
180- Scalers int `json:"scalers,omitempty"`
181- Storage int `json:"storage,omitempty"`
182- PDBs int `json:"pdbs,omitempty"`
183-
184- configRefs map [string ]struct {}
185- scalerRefs map [string ]struct {}
186- storageRefs map [string ]struct {}
187- pdbRefs map [string ]struct {}
173+ Services []string `json:"services,omitempty"`
174+ Ingresses []string `json:"ingresses,omitempty"`
175+ Routes []string `json:"routes,omitempty"`
176+ Configs int `json:"configs,omitempty"`
177+ Scalers int `json:"scalers,omitempty"`
178+ Storage int `json:"storage,omitempty"`
179+ PDBs int `json:"pdbs,omitempty"`
180+ NetworkPolicies int `json:"networkPolicies,omitempty"`
181+ ServiceRefs []topology.ResourceRef `json:"serviceRefs,omitempty"`
182+ IngressRefs []topology.ResourceRef `json:"ingressRefs,omitempty"`
183+ RouteRefs []topology.ResourceRef `json:"routeRefs,omitempty"`
184+ ConfigRefs []topology.ResourceRef `json:"configRefs,omitempty"`
185+ ScalerRefs []topology.ResourceRef `json:"scalerRefs,omitempty"`
186+ StorageRefs []topology.ResourceRef `json:"storageRefs,omitempty"`
187+ PDBRefs []topology.ResourceRef `json:"pdbRefs,omitempty"`
188+ NetworkPolicyRefs []topology.ResourceRef `json:"networkPolicyRefs,omitempty"`
189+
190+ serviceRefs map [string ]topology.ResourceRef
191+ ingressRefs map [string ]topology.ResourceRef
192+ routeRefs map [string ]topology.ResourceRef
193+ configRefs map [string ]topology.ResourceRef
194+ scalerRefs map [string ]topology.ResourceRef
195+ storageRefs map [string ]topology.ResourceRef
196+ pdbRefs map [string ]topology.ResourceRef
197+ networkPolicyRefs map [string ]topology.ResourceRef
188198}
189199
190200// appEvent is a recent k8s Warning event correlated to an app's workloads/pods
@@ -688,30 +698,71 @@ func (g *appGraph) relationshipsFor(kind, ns, name string) *appRelationships {
688698 if rel == nil {
689699 return nil
690700 }
691- out := & appRelationships {Configs : len (rel .ConfigRefs ), Scalers : len (rel .Scalers ), Storage : len (rel .StorageRefs ), PDBs : len (rel .PDBs )}
692- out .configRefs = refsSet (rel .ConfigRefs )
693- out .scalerRefs = refsSet (rel .Scalers )
694- out .storageRefs = refsSet (rel .StorageRefs )
695- out .pdbRefs = refsSet (rel .PDBs )
696- for _ , s := range rel .Services {
697- out .Services = append (out .Services , s .Name )
701+ out := & appRelationships {
702+ Configs : len (rel .ConfigRefs ),
703+ Scalers : len (rel .Scalers ),
704+ Storage : len (rel .StorageRefs ),
705+ PDBs : len (rel .PDBs ),
706+ NetworkPolicies : len (rel .NetworkPolicies ),
707+ serviceRefs : refsByKey (rel .Services ),
708+ ingressRefs : refsByKey (rel .Ingresses ),
709+ routeRefs : refsByKey (appRouteRefs (rel .Routes , rel .Gateways )),
710+ configRefs : refsByKey (rel .ConfigRefs ),
711+ scalerRefs : refsByKey (rel .Scalers ),
712+ storageRefs : refsByKey (rel .StorageRefs ),
713+ pdbRefs : refsByKey (rel .PDBs ),
714+ networkPolicyRefs : refsByKey (rel .NetworkPolicies ),
715+ }
716+ g .addServiceEntrypoints (out , rel .Services )
717+ out .Services = refNames (sortedRefs (out .serviceRefs , 20 ), 20 )
718+ out .Ingresses = refNames (sortedRefs (out .ingressRefs , 20 ), 20 )
719+ out .Routes = routeRefNames (sortedRefs (out .routeRefs , 20 ), 20 )
720+ if len (out .Services ) == 0 && len (out .Ingresses ) == 0 && len (out .Routes ) == 0 &&
721+ len (out .serviceRefs ) == 0 && len (out .ingressRefs ) == 0 && len (out .routeRefs ) == 0 &&
722+ out .Configs == 0 && out .Scalers == 0 && out .Storage == 0 && out .PDBs == 0 && out .NetworkPolicies == 0 {
723+ return nil
698724 }
699- for _ , i := range rel .Ingresses {
700- out .Ingresses = append (out .Ingresses , i .Name )
725+ return out
726+ }
727+
728+ func (g * appGraph ) addServiceEntrypoints (out * appRelationships , services []topology.ResourceRef ) {
729+ if g == nil || g .topo == nil || out == nil {
730+ return
701731 }
702- for _ , r := range rel .Routes {
703- // Routes are polymorphic (HTTPRoute/GRPCRoute/TCPRoute/TLSRoute), so ship
704- // "Kind/name": the client keys its membership index on the concrete kind
705- // (matching the lane ids), which a bare name can't reconstruct.
706- out .Routes = append (out .Routes , r .Kind + "/" + r .Name )
732+ for _ , svc := range services {
733+ if ! strings .EqualFold (svc .Kind , "Service" ) {
734+ continue
735+ }
736+ rel := topology .GetRelationshipsWithIndex (svc .Kind , svc .Namespace , svc .Name , g .topo , g .provider , g .dp , g .idx )
737+ if rel == nil {
738+ continue
739+ }
740+ out .ingressRefs = mergeRefs (out .ingressRefs , refsByKey (rel .Ingresses ))
741+ out .routeRefs = mergeRefs (out .routeRefs , refsByKey (appRouteRefs (rel .Routes , rel .Gateways , rel .Services )))
707742 }
708- if len (out .Services ) == 0 && len (out .Ingresses ) == 0 && len (out .Routes ) == 0 &&
709- out .Configs == 0 && out .Scalers == 0 && out .Storage == 0 && out .PDBs == 0 {
710- return nil
743+ }
744+
745+ func appRouteRefs (routes []topology.ResourceRef , routeLikeGroups ... []topology.ResourceRef ) []topology.ResourceRef {
746+ out := append ([]topology.ResourceRef {}, routes ... )
747+ for _ , group := range routeLikeGroups {
748+ for _ , ref := range group {
749+ if isAppRouteKind (ref .Kind ) {
750+ out = append (out , ref )
751+ }
752+ }
711753 }
712754 return out
713755}
714756
757+ func isAppRouteKind (kind string ) bool {
758+ switch strings .ToLower (kind ) {
759+ case "httproute" , "grpcroute" , "tcproute" , "tlsroute" , "route" , "ingressroute" , "ingressroutetcp" , "ingressrouteudp" , "virtualservice" , "httpproxy" :
760+ return true
761+ default :
762+ return false
763+ }
764+ }
765+
715766// appWorkloadInput is the pre-grouping shape: one workload plus the signals
716767// that decide which app it belongs to (structural root + label overlay) and how
717768// it is classified.
@@ -1289,10 +1340,14 @@ func mergeRelationships(r *appRow, rel *appRelationships) {
12891340 agg .Services = append (agg .Services , rel .Services ... )
12901341 agg .Ingresses = append (agg .Ingresses , rel .Ingresses ... )
12911342 agg .Routes = append (agg .Routes , rel .Routes ... )
1292- agg .configRefs = mergeRefSets (agg .configRefs , rel .configRefs )
1293- agg .scalerRefs = mergeRefSets (agg .scalerRefs , rel .scalerRefs )
1294- agg .storageRefs = mergeRefSets (agg .storageRefs , rel .storageRefs )
1295- agg .pdbRefs = mergeRefSets (agg .pdbRefs , rel .pdbRefs )
1343+ agg .serviceRefs = mergeRefs (agg .serviceRefs , rel .serviceRefs )
1344+ agg .ingressRefs = mergeRefs (agg .ingressRefs , rel .ingressRefs )
1345+ agg .routeRefs = mergeRefs (agg .routeRefs , rel .routeRefs )
1346+ agg .configRefs = mergeRefs (agg .configRefs , rel .configRefs )
1347+ agg .scalerRefs = mergeRefs (agg .scalerRefs , rel .scalerRefs )
1348+ agg .storageRefs = mergeRefs (agg .storageRefs , rel .storageRefs )
1349+ agg .pdbRefs = mergeRefs (agg .pdbRefs , rel .pdbRefs )
1350+ agg .networkPolicyRefs = mergeRefs (agg .networkPolicyRefs , rel .networkPolicyRefs )
12961351 if len (rel .configRefs ) == 0 {
12971352 agg .Configs += rel .Configs
12981353 }
@@ -1305,6 +1360,9 @@ func mergeRelationships(r *appRow, rel *appRelationships) {
13051360 if len (rel .pdbRefs ) == 0 {
13061361 agg .PDBs += rel .PDBs
13071362 }
1363+ if len (rel .networkPolicyRefs ) == 0 {
1364+ agg .NetworkPolicies += rel .NetworkPolicies
1365+ }
13081366}
13091367
13101368func finalizeRelationships (r * appRow ) {
@@ -1314,44 +1372,105 @@ func finalizeRelationships(r *appRow) {
13141372 r .Relationships .Services = dedupSorted (r .Relationships .Services , 20 )
13151373 r .Relationships .Ingresses = dedupSorted (r .Relationships .Ingresses , 20 )
13161374 r .Relationships .Routes = dedupSorted (r .Relationships .Routes , 20 )
1375+ if len (r .Relationships .serviceRefs ) > 0 {
1376+ r .Relationships .ServiceRefs = sortedRefs (r .Relationships .serviceRefs , 20 )
1377+ r .Relationships .Services = refNames (r .Relationships .ServiceRefs , 20 )
1378+ }
1379+ if len (r .Relationships .ingressRefs ) > 0 {
1380+ r .Relationships .IngressRefs = sortedRefs (r .Relationships .ingressRefs , 20 )
1381+ r .Relationships .Ingresses = refNames (r .Relationships .IngressRefs , 20 )
1382+ }
1383+ if len (r .Relationships .routeRefs ) > 0 {
1384+ r .Relationships .RouteRefs = sortedRefs (r .Relationships .routeRefs , 20 )
1385+ r .Relationships .Routes = routeRefNames (r .Relationships .RouteRefs , 20 )
1386+ }
13171387 if len (r .Relationships .configRefs ) > 0 {
13181388 r .Relationships .Configs = len (r .Relationships .configRefs )
1389+ r .Relationships .ConfigRefs = sortedRefs (r .Relationships .configRefs , 20 )
13191390 }
13201391 if len (r .Relationships .scalerRefs ) > 0 {
13211392 r .Relationships .Scalers = len (r .Relationships .scalerRefs )
1393+ r .Relationships .ScalerRefs = sortedRefs (r .Relationships .scalerRefs , 20 )
13221394 }
13231395 if len (r .Relationships .storageRefs ) > 0 {
13241396 r .Relationships .Storage = len (r .Relationships .storageRefs )
1397+ r .Relationships .StorageRefs = sortedRefs (r .Relationships .storageRefs , 20 )
13251398 }
13261399 if len (r .Relationships .pdbRefs ) > 0 {
13271400 r .Relationships .PDBs = len (r .Relationships .pdbRefs )
1401+ r .Relationships .PDBRefs = sortedRefs (r .Relationships .pdbRefs , 20 )
1402+ }
1403+ if len (r .Relationships .networkPolicyRefs ) > 0 {
1404+ r .Relationships .NetworkPolicies = len (r .Relationships .networkPolicyRefs )
1405+ r .Relationships .NetworkPolicyRefs = sortedRefs (r .Relationships .networkPolicyRefs , 20 )
13281406 }
13291407}
13301408
1331- func refsSet (refs []topology.ResourceRef ) map [string ]struct {} {
1409+ func refsByKey (refs []topology.ResourceRef ) map [string ]topology. ResourceRef {
13321410 if len (refs ) == 0 {
13331411 return nil
13341412 }
1335- out := make (map [string ]struct {} , len (refs ))
1413+ out := make (map [string ]topology. ResourceRef , len (refs ))
13361414 for _ , r := range refs {
1337- out [refKey (r )] = struct {}{}
1415+ out [refKey (r )] = r
13381416 }
13391417 return out
13401418}
13411419
1342- func mergeRefSets (dst , src map [string ]struct {} ) map [string ]struct {} {
1420+ func mergeRefs (dst , src map [string ]topology. ResourceRef ) map [string ]topology. ResourceRef {
13431421 if len (src ) == 0 {
13441422 return dst
13451423 }
13461424 if dst == nil {
1347- dst = map [string ]struct {} {}
1425+ dst = map [string ]topology. ResourceRef {}
13481426 }
1349- for k := range src {
1350- dst [k ] = struct {}{}
1427+ for k , ref := range src {
1428+ dst [k ] = ref
13511429 }
13521430 return dst
13531431}
13541432
1433+ func sortedRefs (refs map [string ]topology.ResourceRef , limit int ) []topology.ResourceRef {
1434+ if len (refs ) == 0 || limit <= 0 {
1435+ return nil
1436+ }
1437+ keys := make ([]string , 0 , len (refs ))
1438+ for key := range refs {
1439+ keys = append (keys , key )
1440+ }
1441+ sort .Strings (keys )
1442+ if len (keys ) > limit {
1443+ keys = keys [:limit ]
1444+ }
1445+ out := make ([]topology.ResourceRef , 0 , len (keys ))
1446+ for _ , key := range keys {
1447+ out = append (out , refs [key ])
1448+ }
1449+ return out
1450+ }
1451+
1452+ func refNames (refs []topology.ResourceRef , limit int ) []string {
1453+ if len (refs ) == 0 || limit <= 0 {
1454+ return nil
1455+ }
1456+ names := make ([]string , 0 , len (refs ))
1457+ for _ , ref := range refs {
1458+ names = append (names , ref .Name )
1459+ }
1460+ return dedupSorted (names , limit )
1461+ }
1462+
1463+ func routeRefNames (refs []topology.ResourceRef , limit int ) []string {
1464+ if len (refs ) == 0 || limit <= 0 {
1465+ return nil
1466+ }
1467+ names := make ([]string , 0 , len (refs ))
1468+ for _ , ref := range refs {
1469+ names = append (names , ref .Kind + "/" + ref .Name )
1470+ }
1471+ return dedupSorted (names , limit )
1472+ }
1473+
13551474func refKey (r topology.ResourceRef ) string {
13561475 return r .Group + "/" + r .Kind + "/" + r .Namespace + "/" + r .Name
13571476}
@@ -1361,7 +1480,8 @@ func classifyWorkload(kind string, rels *appRelationships) string {
13611480 case "Job" , "CronJob" :
13621481 return "job"
13631482 case "Deployment" , "StatefulSet" , "DaemonSet" :
1364- if rels != nil && (len (rels .Services ) > 0 || len (rels .Ingresses ) > 0 || len (rels .Routes ) > 0 ) {
1483+ if rels != nil && (len (rels .Services ) > 0 || len (rels .Ingresses ) > 0 || len (rels .Routes ) > 0 ||
1484+ len (rels .serviceRefs ) > 0 || len (rels .ingressRefs ) > 0 || len (rels .routeRefs ) > 0 ) {
13651485 return "service"
13661486 }
13671487 return "worker"
0 commit comments