From ef2b6f6c55559ce96713fbbb39d5181355c5d383 Mon Sep 17 00:00:00 2001 From: Mike Lang Date: Tue, 27 Jun 2017 17:28:34 -0700 Subject: [PATCH] Remove replica sets Use a special kind of selector renderer to elide replica sets from pod nodes and directly reference deployment parents instead. Do the inverse (replace replica sets with pods) during the mapping from pod to deployment. Note we can no longer use renderParents since we're using a non-standard Selector --- app/api_topologies.go | 13 +---- render/detailed/node.go | 10 ---- render/detailed/parents.go | 1 - render/detailed/summary.go | 2 - render/pod.go | 97 ++++++++++++++++++++++++-------------- render/selectors.go | 1 - 6 files changed, 64 insertions(+), 60 deletions(-) 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)