diff --git a/app/api_topologies.go b/app/api_topologies.go index 684801e45..8047961ee 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -28,7 +28,6 @@ const ( containersByHostnameID = "containers-by-hostname" containersByImageID = "containers-by-image" podsID = "pods" - replicaSetsID = "replica-sets" kubeControllersID = "kube-controllers" servicesID = "services" hostsID = "hosts" @@ -97,7 +96,7 @@ func updateSwarmFilters(rpt report.Report, topologies []APITopologyDesc) []APITo func updateKubeFilters(rpt report.Report, topologies []APITopologyDesc) []APITopologyDesc { namespaces := map[string]struct{}{} - for _, t := range []report.Topology{rpt.Pod, rpt.Service, rpt.Deployment, rpt.ReplicaSet} { + for _, t := range []report.Topology{rpt.Pod, rpt.Service, rpt.Deployment} { for _, n := range t.Nodes { if state, ok := n.Latest.Lookup(kubernetes.State); ok && state == kubernetes.StateDeleted { continue @@ -119,7 +118,7 @@ func updateKubeFilters(rpt report.Report, topologies []APITopologyDesc) []APITop sort.Strings(ns) topologies = append([]APITopologyDesc{}, topologies...) // Make a copy so we can make changes safely for i, t := range topologies { - if t.id == containersID || t.id == podsID || t.id == servicesID || t.id == replicaSetsID || t.id == kubeControllersID { + if t.id == containersID || t.id == podsID || t.id == servicesID || t.id == kubeControllersID { topologies[i] = mergeTopologyFilters(t, []APITopologyOptionGroup{ namespaceFilters(ns, "All Namespaces"), }) @@ -254,14 +253,6 @@ func MakeRegistry() *Registry { Options: []APITopologyOptionGroup{unmanagedFilter}, HideIfEmpty: true, }, - APITopologyDesc{ - id: replicaSetsID, - parent: podsID, - renderer: render.FilterUnconnectedPseudo(render.ReplicaSetRenderer), - Name: "replica sets", - Options: []APITopologyOptionGroup{unmanagedFilter}, - HideIfEmpty: true, - }, APITopologyDesc{ id: kubeControllersID, parent: podsID, diff --git a/render/detailed/node.go b/render/detailed/node.go index 5164bc5b8..1d620338a 100644 --- a/render/detailed/node.go +++ b/render/detailed/node.go @@ -131,16 +131,6 @@ var nodeSummaryGroupSpecs = []struct { topologyID string NodeSummaryGroup }{ - { - topologyID: report.ReplicaSet, - NodeSummaryGroup: NodeSummaryGroup{ - Label: "Replica Sets", - Columns: []Column{ - {ID: report.Pod, Label: "# Pods", Datatype: "number"}, - {ID: kubernetes.ObservedGeneration, Label: "Observed Gen.", Datatype: "number"}, - }, - }, - }, { topologyID: report.Pod, NodeSummaryGroup: NodeSummaryGroup{ diff --git a/render/detailed/parents.go b/render/detailed/parents.go index d51a67ca5..8e347add8 100644 --- a/render/detailed/parents.go +++ b/render/detailed/parents.go @@ -23,7 +23,6 @@ var ( getLabelForTopology = map[string]func(report.Node) string{ report.Container: getRenderableContainerName, report.Pod: kubernetesParentLabel, - report.ReplicaSet: kubernetesParentLabel, report.Deployment: kubernetesParentLabel, report.DaemonSet: kubernetesParentLabel, report.Service: kubernetesParentLabel, diff --git a/render/detailed/summary.go b/render/detailed/summary.go index b3d058b15..2ef25f264 100644 --- a/render/detailed/summary.go +++ b/render/detailed/summary.go @@ -68,7 +68,6 @@ var renderers = map[string]func(NodeSummary, report.Node) (NodeSummary, bool){ report.Service: podGroupNodeSummary, report.Deployment: podGroupNodeSummary, report.DaemonSet: podGroupNodeSummary, - report.ReplicaSet: podGroupNodeSummary, report.ECSTask: ecsTaskNodeSummary, report.ECSService: ecsServiceNodeSummary, report.SwarmService: swarmServiceNodeSummary, @@ -89,7 +88,6 @@ var primaryAPITopology = map[string]string{ report.Container: "containers", report.ContainerImage: "containers-by-image", report.Pod: "pods", - report.ReplicaSet: "replica-sets", report.Deployment: "kube-controllers", report.DaemonSet: "kube-controllers", report.Service: "services", diff --git a/render/pod.go b/render/pod.go index ccdb0dc1a..5f4994c9a 100644 --- a/render/pod.go +++ b/render/pod.go @@ -18,7 +18,7 @@ const ( var UnmanagedIDPrefix = MakePseudoNodeID(UnmanagedID) func renderKubernetesTopologies(rpt report.Report) bool { - return len(rpt.Pod.Nodes)+len(rpt.Service.Nodes)+len(rpt.Deployment.Nodes)+len(rpt.ReplicaSet.Nodes)+len(rpt.DaemonSet.Nodes) >= 1 + return len(rpt.Pod.Nodes)+len(rpt.Service.Nodes)+len(rpt.Deployment.Nodes)+len(rpt.DaemonSet.Nodes) >= 1 } func isPauseContainer(n report.Node) bool { @@ -44,16 +44,20 @@ var PodRenderer = ConditionalRenderer(renderKubernetesTopologies, return (!ok || state != kubernetes.StateDeleted) }, MakeReduce( - renderParents( - report.Container, []string{report.Pod}, NoParentsPseudo, UnmanagedID, nil, - MakeFilter( - ComposeFilterFuncs( - IsRunning, - Complement(isPauseContainer), + MakeMap( + PropagateSingleMetrics(report.Container), + MakeMap( + Map2Parent([]string{report.Pod}, NoParentsPseudo, UnmanagedID, nil), + MakeFilter( + ComposeFilterFuncs( + IsRunning, + Complement(isPauseContainer), + ), + ContainerWithImageNameRenderer, ), - ContainerWithImageNameRenderer, ), ), + selectPodsWithDeployments{}, ConnectionJoin(SelectPod, MapPod2IP), ), ), @@ -68,33 +72,15 @@ var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies, ), ) -// ReplicaSetRenderer is a Renderer which produces a renderable kubernetes replica sets -// graph by merging the pods graph and the replica sets topology. -var ReplicaSetRenderer = ConditionalRenderer(renderKubernetesTopologies, - renderParents( - report.Pod, []string{report.ReplicaSet}, NoParentsDrop, "", nil, - PodRenderer, - ), -) - // KubeControllerRenderer is a Renderer which combines all the 'controller' topologies. -// We first map pods to daemonsets and replica sets, then to deployments since replica sets -// can map to those (the rest are passed through unchanged). // Pods with no controller are mapped to 'Unmanaged' // We can't simply combine the rendered graphs of the high level objects as they would never // have connections to each other. var KubeControllerRenderer = ConditionalRenderer(renderKubernetesTopologies, - MakeFilter( - // Filter out any remaining unmatched replica sets - Complement(IsTopology(report.ReplicaSet)), - renderParents( - report.ReplicaSet, []string{report.Deployment}, NoParentsKeep, "", mapPodCounts, - renderParents( - report.Pod, []string{report.ReplicaSet, report.DaemonSet}, - NoParentsPseudo, UnmanagedID, nil, - PodRenderer, - ), - ), + renderParents( + report.Pod, []string{report.Deployment, report.DaemonSet}, + NoParentsPseudo, UnmanagedID, makePodsChildren, + PodRenderer, ), ) @@ -120,12 +106,53 @@ func renderParents(childTopology string, parentTopologies []string, noParentsAct )...) } -func mapPodCounts(parent, original report.Node) report.Node { - // When mapping ReplicaSets to Deployments, we want to propagate the Pods counter - if count, ok := original.Counters.Lookup(report.Pod); ok { - parent.Counters = parent.Counters.Add(report.Pod, count) +// Renderer to return modified Pod nodes to elide replica sets and point directly +// to deployments where applicable. +type selectPodsWithDeployments struct{} + +func (s selectPodsWithDeployments) Render(rpt report.Report, dct Decorator) report.Nodes { + result := report.Nodes{} + // For each pod, we check for any replica sets, and merge any deployments they point to + // into a replacement Parents value. + for podID, pod := range rpt.Pod.Nodes { + if replicaSetIDs, ok := pod.Parents.Lookup(report.ReplicaSet); ok { + newParents := pod.Parents.Delete(report.ReplicaSet) + for _, replicaSetID := range replicaSetIDs { + if replicaSet, ok := rpt.ReplicaSet.Nodes[replicaSetID]; ok { + if deploymentIDs, ok := replicaSet.Parents.Lookup(report.Deployment); ok { + newParents = newParents.Add(report.Deployment, deploymentIDs) + } + } + } + pod = pod.WithParents(newParents) + } + result[podID] = pod } - return parent + return result +} + +func (s selectPodsWithDeployments) Stats(rpt report.Report, _ Decorator) Stats { + return Stats{} +} + +// When mapping from pods to deployments, complete the two-way relation by making the +// mapped-from pod a child of the mapped-to deployment, and remove any replica set children. +// This is needed because pods were originally mapped to deployments via an intermediate replica set +// which we need to remove. +func makePodsChildren(parent, original report.Node) report.Node { + children := parent.Children + // Gather all the replica sets... + replicaSetIDs := []string{} + children.ForEach(func(n report.Node) { + if n.Topology == report.ReplicaSet { + replicaSetIDs = append(replicaSetIDs, n.ID) + } + }) + // ...and delete them. + children = children.Delete(replicaSetIDs...) + // Then add in the mapped-from pod. + children = children.Add(original) + return parent.WithChildren(children) } // MapPod2IP maps pod nodes to their IP address. This allows pods to diff --git a/render/selectors.go b/render/selectors.go index 483838c54..3733c3a6a 100644 --- a/render/selectors.go +++ b/render/selectors.go @@ -31,7 +31,6 @@ var ( SelectService = TopologySelector(report.Service) SelectDeployment = TopologySelector(report.Deployment) SelectDaemonSet = TopologySelector(report.DaemonSet) - SelectReplicaSet = TopologySelector(report.ReplicaSet) SelectECSTask = TopologySelector(report.ECSTask) SelectECSService = TopologySelector(report.ECSService) SelectSwarmService = TopologySelector(report.SwarmService)