@@ -167,21 +167,33 @@ type appHistoryIncident struct {
167167}
168168
169169// appRelationships is the structural neighborhood of an app, derived from the
170- // topology graph: what fronts it (Services/Ingress/Routes) and what supports it
171- // (config, autoscalers, disruption budgets). Counts where names add no value.
170+ // topology graph: what fronts it (Services/Ingress/Routes) and what supports it.
172171type appRelationships 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-
181- configRefs map [string ]struct {}
182- scalerRefs map [string ]struct {}
183- storageRefs map [string ]struct {}
184- pdbRefs map [string ]struct {}
172+ Services []string `json:"services,omitempty"`
173+ Ingresses []string `json:"ingresses,omitempty"`
174+ Routes []string `json:"routes,omitempty"`
175+ Configs int `json:"configs,omitempty"`
176+ Scalers int `json:"scalers,omitempty"`
177+ Storage int `json:"storage,omitempty"`
178+ PDBs int `json:"pdbs,omitempty"`
179+ NetworkPolicies int `json:"networkPolicies,omitempty"`
180+ ServiceRefs []topology.ResourceRef `json:"serviceRefs,omitempty"`
181+ IngressRefs []topology.ResourceRef `json:"ingressRefs,omitempty"`
182+ RouteRefs []topology.ResourceRef `json:"routeRefs,omitempty"`
183+ ConfigRefs []topology.ResourceRef `json:"configRefs,omitempty"`
184+ ScalerRefs []topology.ResourceRef `json:"scalerRefs,omitempty"`
185+ StorageRefs []topology.ResourceRef `json:"storageRefs,omitempty"`
186+ PDBRefs []topology.ResourceRef `json:"pdbRefs,omitempty"`
187+ NetworkPolicyRefs []topology.ResourceRef `json:"networkPolicyRefs,omitempty"`
188+
189+ serviceRefs map [string ]topology.ResourceRef
190+ ingressRefs map [string ]topology.ResourceRef
191+ routeRefs map [string ]topology.ResourceRef
192+ configRefs map [string ]topology.ResourceRef
193+ scalerRefs map [string ]topology.ResourceRef
194+ storageRefs map [string ]topology.ResourceRef
195+ pdbRefs map [string ]topology.ResourceRef
196+ networkPolicyRefs map [string ]topology.ResourceRef
185197}
186198
187199// appEvent is a recent k8s Warning event correlated to an app's workloads/pods
@@ -685,27 +697,68 @@ func (g *appGraph) relationshipsFor(kind, ns, name string) *appRelationships {
685697 if rel == nil {
686698 return nil
687699 }
688- out := & appRelationships {Configs : len (rel .ConfigRefs ), Scalers : len (rel .Scalers ), Storage : len (rel .StorageRefs ), PDBs : len (rel .PDBs )}
689- out .configRefs = refsSet (rel .ConfigRefs )
690- out .scalerRefs = refsSet (rel .Scalers )
691- out .storageRefs = refsSet (rel .StorageRefs )
692- out .pdbRefs = refsSet (rel .PDBs )
693- for _ , s := range rel .Services {
694- out .Services = append (out .Services , s .Name )
700+ out := & appRelationships {
701+ Configs : len (rel .ConfigRefs ),
702+ Scalers : len (rel .Scalers ),
703+ Storage : len (rel .StorageRefs ),
704+ PDBs : len (rel .PDBs ),
705+ NetworkPolicies : len (rel .NetworkPolicies ),
706+ serviceRefs : refsByKey (rel .Services ),
707+ ingressRefs : refsByKey (rel .Ingresses ),
708+ routeRefs : refsByKey (appRouteRefs (rel .Routes , rel .Gateways )),
709+ configRefs : refsByKey (rel .ConfigRefs ),
710+ scalerRefs : refsByKey (rel .Scalers ),
711+ storageRefs : refsByKey (rel .StorageRefs ),
712+ pdbRefs : refsByKey (rel .PDBs ),
713+ networkPolicyRefs : refsByKey (rel .NetworkPolicies ),
714+ }
715+ g .addServiceEntrypoints (out , rel .Services )
716+ if len (out .Services ) == 0 && len (out .Ingresses ) == 0 && len (out .Routes ) == 0 &&
717+ len (out .serviceRefs ) == 0 && len (out .ingressRefs ) == 0 && len (out .routeRefs ) == 0 &&
718+ out .Configs == 0 && out .Scalers == 0 && out .Storage == 0 && out .PDBs == 0 && out .NetworkPolicies == 0 {
719+ return nil
695720 }
696- for _ , i := range rel .Ingresses {
697- out .Ingresses = append (out .Ingresses , i .Name )
721+ return out
722+ }
723+
724+ func (g * appGraph ) addServiceEntrypoints (out * appRelationships , services []topology.ResourceRef ) {
725+ if g == nil || g .topo == nil || out == nil {
726+ return
698727 }
699- for _ , r := range rel .Routes {
700- out .Routes = append (out .Routes , r .Name )
728+ for _ , svc := range services {
729+ if ! strings .EqualFold (svc .Kind , "Service" ) {
730+ continue
731+ }
732+ rel := topology .GetRelationshipsWithIndex (svc .Kind , svc .Namespace , svc .Name , g .topo , g .provider , g .dp , g .idx )
733+ if rel == nil {
734+ continue
735+ }
736+ out .ingressRefs = mergeRefs (out .ingressRefs , refsByKey (rel .Ingresses ))
737+ out .routeRefs = mergeRefs (out .routeRefs , refsByKey (appRouteRefs (rel .Routes , rel .Gateways , rel .Services )))
701738 }
702- if len (out .Services ) == 0 && len (out .Ingresses ) == 0 && len (out .Routes ) == 0 &&
703- out .Configs == 0 && out .Scalers == 0 && out .Storage == 0 && out .PDBs == 0 {
704- return nil
739+ }
740+
741+ func appRouteRefs (routes []topology.ResourceRef , routeLikeGroups ... []topology.ResourceRef ) []topology.ResourceRef {
742+ out := append ([]topology.ResourceRef {}, routes ... )
743+ for _ , group := range routeLikeGroups {
744+ for _ , ref := range group {
745+ if isAppRouteKind (ref .Kind ) {
746+ out = append (out , ref )
747+ }
748+ }
705749 }
706750 return out
707751}
708752
753+ func isAppRouteKind (kind string ) bool {
754+ switch strings .ToLower (kind ) {
755+ case "httproute" , "grpcroute" , "tcproute" , "tlsroute" , "route" , "ingressroute" , "ingressroutetcp" , "ingressrouteudp" , "virtualservice" , "httpproxy" :
756+ return true
757+ default :
758+ return false
759+ }
760+ }
761+
709762// appWorkloadInput is the pre-grouping shape: one workload plus the signals
710763// that decide which app it belongs to (structural root + label overlay) and how
711764// it is classified.
@@ -1208,10 +1261,14 @@ func mergeRelationships(r *appRow, rel *appRelationships) {
12081261 agg .Services = append (agg .Services , rel .Services ... )
12091262 agg .Ingresses = append (agg .Ingresses , rel .Ingresses ... )
12101263 agg .Routes = append (agg .Routes , rel .Routes ... )
1211- agg .configRefs = mergeRefSets (agg .configRefs , rel .configRefs )
1212- agg .scalerRefs = mergeRefSets (agg .scalerRefs , rel .scalerRefs )
1213- agg .storageRefs = mergeRefSets (agg .storageRefs , rel .storageRefs )
1214- agg .pdbRefs = mergeRefSets (agg .pdbRefs , rel .pdbRefs )
1264+ agg .serviceRefs = mergeRefs (agg .serviceRefs , rel .serviceRefs )
1265+ agg .ingressRefs = mergeRefs (agg .ingressRefs , rel .ingressRefs )
1266+ agg .routeRefs = mergeRefs (agg .routeRefs , rel .routeRefs )
1267+ agg .configRefs = mergeRefs (agg .configRefs , rel .configRefs )
1268+ agg .scalerRefs = mergeRefs (agg .scalerRefs , rel .scalerRefs )
1269+ agg .storageRefs = mergeRefs (agg .storageRefs , rel .storageRefs )
1270+ agg .pdbRefs = mergeRefs (agg .pdbRefs , rel .pdbRefs )
1271+ agg .networkPolicyRefs = mergeRefs (agg .networkPolicyRefs , rel .networkPolicyRefs )
12151272 if len (rel .configRefs ) == 0 {
12161273 agg .Configs += rel .Configs
12171274 }
@@ -1224,6 +1281,9 @@ func mergeRelationships(r *appRow, rel *appRelationships) {
12241281 if len (rel .pdbRefs ) == 0 {
12251282 agg .PDBs += rel .PDBs
12261283 }
1284+ if len (rel .networkPolicyRefs ) == 0 {
1285+ agg .NetworkPolicies += rel .NetworkPolicies
1286+ }
12271287}
12281288
12291289func finalizeRelationships (r * appRow ) {
@@ -1233,44 +1293,94 @@ func finalizeRelationships(r *appRow) {
12331293 r .Relationships .Services = dedupSorted (r .Relationships .Services , 20 )
12341294 r .Relationships .Ingresses = dedupSorted (r .Relationships .Ingresses , 20 )
12351295 r .Relationships .Routes = dedupSorted (r .Relationships .Routes , 20 )
1296+ if len (r .Relationships .serviceRefs ) > 0 {
1297+ r .Relationships .ServiceRefs = sortedRefs (r .Relationships .serviceRefs , 20 )
1298+ r .Relationships .Services = refNames (r .Relationships .ServiceRefs , 20 )
1299+ }
1300+ if len (r .Relationships .ingressRefs ) > 0 {
1301+ r .Relationships .IngressRefs = sortedRefs (r .Relationships .ingressRefs , 20 )
1302+ r .Relationships .Ingresses = refNames (r .Relationships .IngressRefs , 20 )
1303+ }
1304+ if len (r .Relationships .routeRefs ) > 0 {
1305+ r .Relationships .RouteRefs = sortedRefs (r .Relationships .routeRefs , 20 )
1306+ r .Relationships .Routes = refNames (r .Relationships .RouteRefs , 20 )
1307+ }
12361308 if len (r .Relationships .configRefs ) > 0 {
12371309 r .Relationships .Configs = len (r .Relationships .configRefs )
1310+ r .Relationships .ConfigRefs = sortedRefs (r .Relationships .configRefs , 20 )
12381311 }
12391312 if len (r .Relationships .scalerRefs ) > 0 {
12401313 r .Relationships .Scalers = len (r .Relationships .scalerRefs )
1314+ r .Relationships .ScalerRefs = sortedRefs (r .Relationships .scalerRefs , 20 )
12411315 }
12421316 if len (r .Relationships .storageRefs ) > 0 {
12431317 r .Relationships .Storage = len (r .Relationships .storageRefs )
1318+ r .Relationships .StorageRefs = sortedRefs (r .Relationships .storageRefs , 20 )
12441319 }
12451320 if len (r .Relationships .pdbRefs ) > 0 {
12461321 r .Relationships .PDBs = len (r .Relationships .pdbRefs )
1322+ r .Relationships .PDBRefs = sortedRefs (r .Relationships .pdbRefs , 20 )
1323+ }
1324+ if len (r .Relationships .networkPolicyRefs ) > 0 {
1325+ r .Relationships .NetworkPolicies = len (r .Relationships .networkPolicyRefs )
1326+ r .Relationships .NetworkPolicyRefs = sortedRefs (r .Relationships .networkPolicyRefs , 20 )
12471327 }
12481328}
12491329
1250- func refsSet (refs []topology.ResourceRef ) map [string ]struct {} {
1330+ func refsByKey (refs []topology.ResourceRef ) map [string ]topology. ResourceRef {
12511331 if len (refs ) == 0 {
12521332 return nil
12531333 }
1254- out := make (map [string ]struct {} , len (refs ))
1334+ out := make (map [string ]topology. ResourceRef , len (refs ))
12551335 for _ , r := range refs {
1256- out [refKey (r )] = struct {}{}
1336+ out [refKey (r )] = r
12571337 }
12581338 return out
12591339}
12601340
1261- func mergeRefSets (dst , src map [string ]struct {} ) map [string ]struct {} {
1341+ func mergeRefs (dst , src map [string ]topology. ResourceRef ) map [string ]topology. ResourceRef {
12621342 if len (src ) == 0 {
12631343 return dst
12641344 }
12651345 if dst == nil {
1266- dst = map [string ]struct {} {}
1346+ dst = map [string ]topology. ResourceRef {}
12671347 }
1268- for k := range src {
1269- dst [k ] = struct {}{}
1348+ for k , ref := range src {
1349+ dst [k ] = ref
12701350 }
12711351 return dst
12721352}
12731353
1354+ func sortedRefs (refs map [string ]topology.ResourceRef , limit int ) []topology.ResourceRef {
1355+ if len (refs ) == 0 || limit <= 0 {
1356+ return nil
1357+ }
1358+ keys := make ([]string , 0 , len (refs ))
1359+ for key := range refs {
1360+ keys = append (keys , key )
1361+ }
1362+ sort .Strings (keys )
1363+ if len (keys ) > limit {
1364+ keys = keys [:limit ]
1365+ }
1366+ out := make ([]topology.ResourceRef , 0 , len (keys ))
1367+ for _ , key := range keys {
1368+ out = append (out , refs [key ])
1369+ }
1370+ return out
1371+ }
1372+
1373+ func refNames (refs []topology.ResourceRef , limit int ) []string {
1374+ if len (refs ) == 0 || limit <= 0 {
1375+ return nil
1376+ }
1377+ names := make ([]string , 0 , len (refs ))
1378+ for _ , ref := range refs {
1379+ names = append (names , ref .Name )
1380+ }
1381+ return dedupSorted (names , limit )
1382+ }
1383+
12741384func refKey (r topology.ResourceRef ) string {
12751385 return r .Group + "/" + r .Kind + "/" + r .Namespace + "/" + r .Name
12761386}
@@ -1280,7 +1390,8 @@ func classifyWorkload(kind string, rels *appRelationships) string {
12801390 case "Job" , "CronJob" :
12811391 return "job"
12821392 case "Deployment" , "StatefulSet" , "DaemonSet" :
1283- if rels != nil && (len (rels .Services ) > 0 || len (rels .Ingresses ) > 0 || len (rels .Routes ) > 0 ) {
1393+ if rels != nil && (len (rels .Services ) > 0 || len (rels .Ingresses ) > 0 || len (rels .Routes ) > 0 ||
1394+ len (rels .serviceRefs ) > 0 || len (rels .ingressRefs ) > 0 || len (rels .routeRefs ) > 0 ) {
12841395 return "service"
12851396 }
12861397 return "worker"
0 commit comments