package render import ( "strings" "github.com/weaveworks/scope/probe/docker" "github.com/weaveworks/scope/probe/kubernetes" "github.com/weaveworks/scope/report" ) // Constants are used in the tests. const ( UnmanagedID = "unmanaged" UnmanagedMajor = "Unmanaged" ) // UnmanagedIDPrefix is the prefix of unmanaged pseudo nodes var UnmanagedIDPrefix = MakePseudoNodeID(UnmanagedID, "") func renderKubernetesTopologies(rpt report.Report) bool { // Render if any k8s topology has any nodes topologies := []*report.Topology{ &rpt.Pod, &rpt.Service, &rpt.Deployment, &rpt.DaemonSet, &rpt.StatefulSet, &rpt.CronJob, } for _, t := range topologies { if len(t.Nodes) > 0 { return true } } return false } func isPauseContainer(n report.Node) bool { image, ok := n.Latest.Lookup(docker.ImageName) return ok && kubernetes.IsPauseImageName(image) } // PodRenderer is a Renderer which produces a renderable kubernetes // graph by merging the container graph and the pods topology. var PodRenderer = Memoise(ConditionalRenderer(renderKubernetesTopologies, MakeFilter( func(n report.Node) bool { state, ok := n.Latest.Lookup(kubernetes.State) return (!ok || state != kubernetes.StateDeleted) }, MakeReduce( PropagateSingleMetrics(report.Container, MakeMap( Map2Parent([]string{report.Pod}, UnmanagedID), MakeFilter( ComposeFilterFuncs( IsRunning, Complement(isPauseContainer), ), ContainerWithImageNameRenderer, ), ), ), ConnectionJoin(MapPod2IP, report.Pod), ), ), )) // PodServiceRenderer is a Renderer which produces a renderable kubernetes services // graph by merging the pods graph and the services topology. // // not memoised var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies, renderParents( report.Pod, []string{report.Service}, "", PodRenderer, ), ) // KubeControllerRenderer is a Renderer which combines all the 'controller' topologies. // 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. // // not memoised var KubeControllerRenderer = ConditionalRenderer(renderKubernetesTopologies, renderParents( report.Pod, []string{report.Deployment, report.DaemonSet, report.StatefulSet, report.CronJob}, UnmanagedID, PodRenderer, ), ) // renderParents produces a 'standard' renderer for mapping from some child topology to some parent topologies, // by taking a child renderer, mapping to parents, propagating single metrics, and joining with full parent topology. // Other options are as per Map2Parent. func renderParents(childTopology string, parentTopologies []string, noParentsPseudoID string, childRenderer Renderer) Renderer { selectors := make([]Renderer, len(parentTopologies)) for i, topology := range parentTopologies { selectors[i] = TopologySelector(topology) } return MakeReduce(append( selectors, PropagateSingleMetrics(childTopology, MakeMap( Map2Parent(parentTopologies, noParentsPseudoID), childRenderer, ), ), )...) } // MapPod2IP maps pod nodes to their IP address. This allows pods to // be joined directly with the endpoint topology. func MapPod2IP(m report.Node) []string { // if this pod belongs to the host's networking namespace // we cannot use its IP to attribute connections // (they could come from any other process on the host or DNAT-ed IPs) if _, ok := m.Latest.Lookup(kubernetes.IsInHostNetwork); ok { return nil } ip, ok := m.Latest.Lookup(kubernetes.IP) if !ok { return nil } return []string{report.MakeScopedEndpointNodeID("", ip, "")} } // Map2Parent returns a MapFunc which maps Nodes to some parent grouping. func Map2Parent( // The topology IDs to look for parents in topologies []string, // Either the ID prefix of the pseudo node to use for nodes without // any parents in the group, eg. UnmanagedID, or "" to drop nodes without any parents. noParentsPseudoID string, ) MapFunc { return func(n report.Node) report.Nodes { // Uncontained becomes Unmanaged/whatever if noParentsPseudoID is set if noParentsPseudoID != "" && strings.HasPrefix(n.ID, UncontainedIDPrefix) { id := MakePseudoNodeID(noParentsPseudoID, n.ID[len(UncontainedIDPrefix):]) node := NewDerivedPseudoNode(id, n) return report.Nodes{id: node} } // Propagate all pseudo nodes if n.Topology == Pseudo { return report.Nodes{n.ID: n} } // For each topology, map to any parents we can find result := report.Nodes{} for _, topology := range topologies { if groupIDs, ok := n.Parents.Lookup(topology); ok { for _, id := range groupIDs { node := NewDerivedNode(id, n).WithTopology(topology) node.Counters = node.Counters.Add(n.Topology, 1) result[id] = node } } } if len(result) == 0 && noParentsPseudoID != "" { // Map to pseudo node id := MakePseudoNodeID(noParentsPseudoID, report.ExtractHostID(n)) node := NewDerivedPseudoNode(id, n) result[id] = node } return result } }