Skip to content

Commit f7b2ec9

Browse files
committed
Refine app overview dependencies and history
1 parent f4c0ee4 commit f7b2ec9

5 files changed

Lines changed: 428 additions & 137 deletions

File tree

internal/server/applications.go

Lines changed: 165 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
173172
type 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

13101368
func 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+
13551474
func 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

Comments
 (0)