diff --git a/app/api_topologies.go b/app/api_topologies.go index 21dee103d..5d9cb6dc5 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -204,6 +204,8 @@ func MakeRegistry() *Registry { Options: []APITopologyOption{ {Value: report.Deployment, Label: "Deployments", filter: render.IsTopology(report.Deployment), filterPseudo: false}, {Value: report.DaemonSet, Label: "Daemonsets", filter: render.IsTopology(report.DaemonSet), filterPseudo: false}, + {Value: report.ReplicaSet, Label: "Replica sets", filter: render.IsTopology(report.ReplicaSet), filterPseudo: false}, + {Value: report.Pod, Label: "Pods", filter: render.IsTopology(report.Pod), filterPseudo: false}, }, } diff --git a/render/pod.go b/render/pod.go index 6caf168b5..4707c0882 100644 --- a/render/pod.go +++ b/render/pod.go @@ -127,16 +127,25 @@ var DaemonSetRenderer = ConditionalRenderer(renderKubernetesTopologies, // unchanged). // We can't simply combine the rendered graphs of the high level objects as they would never // have connections to each other. +// We combine with all the full topologies using ReduceFirstOnly, which keeps the same +// set of nodes but merges in the full data from the other renderers. var KubeCombinedRenderer = ConditionalRenderer(renderKubernetesTopologies, - MakeMap( - Map2Parent([]string{report.Deployment}, NoParentsKeep, "", nil), + MakeReduceFirstOnly( MakeMap( - Map2Parent([]string{ - report.ReplicaSet, - report.DaemonSet, - }, NoParentsKeep, "", nil), - PodRenderer, + Map2Parent([]string{report.Deployment}, NoParentsKeep, "", nil), + MakeReduceFirstOnly( + MakeMap( + Map2Parent([]string{ + report.ReplicaSet, + report.DaemonSet, + }, NoParentsKeep, "", nil), + PodRenderer, + ), + SelectReplicaSet, + SelectDaemonSet, + ), ), + SelectDeployment, ), ) diff --git a/render/render.go b/render/render.go index 415bc3ffb..7b2582fa6 100644 --- a/render/render.go +++ b/render/render.go @@ -55,6 +55,40 @@ func (r *Reduce) Stats(rpt report.Report, dct Decorator) Stats { return result } +// ReduceFirstOnly renderer is a Renderer which merges together the output of several other renderers, +// including only the nodes present in the first render, but merging in info from the same nodes +// if present in the other renders. +type ReduceFirstOnly struct { + first Renderer + others []Renderer +} + +// MakeReduceFirstOnly is the only sane way to produce a ReduceFirstOnly Renderer. +func MakeReduceFirstOnly(first Renderer, others ...Renderer) Renderer { + r := ReduceFirstOnly{first, others} + return Memoise(&r) +} + +// Render produces a set of Nodes given a Report. +func (r *ReduceFirstOnly) Render(rpt report.Report, dct Decorator) report.Nodes { + result := r.first.Render(rpt, dct).Copy() + for _, other := range r.others { + for id, node := range other.Render(rpt, dct) { + if n, ok := result[id]; ok { + result[id] = n.Merge(node) + } + } + } + return result +} + +// Stats implements Renderer +func (r *ReduceFirstOnly) Stats(rpt report.Report, dct Decorator) Stats { + // Since we're just taking more detailed data from the other renderers, + // we probably only care about the filter stats for the 'main' renderer. + return r.first.Stats(rpt, dct) +} + // Map is a Renderer which produces a set of Nodes from the set of // Nodes produced by another Renderer. type Map struct {