pod.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package render
  2. import (
  3. "context"
  4. "strings"
  5. "github.com/weaveworks/scope/report"
  6. )
  7. // Constants are used in the tests.
  8. const (
  9. UnmanagedID = "unmanaged"
  10. UnmanagedMajor = "Unmanaged"
  11. )
  12. // UnmanagedIDPrefix is the prefix of unmanaged pseudo nodes
  13. var UnmanagedIDPrefix = MakePseudoNodeID(UnmanagedID, "")
  14. func renderKubernetesTopologies(rpt report.Report) bool {
  15. // Render if any k8s topology has any nodes
  16. topologies := []*report.Topology{
  17. &rpt.Pod,
  18. &rpt.Service,
  19. &rpt.Deployment,
  20. &rpt.DaemonSet,
  21. &rpt.StatefulSet,
  22. &rpt.CronJob,
  23. &rpt.PersistentVolume,
  24. &rpt.PersistentVolumeClaim,
  25. &rpt.StorageClass,
  26. &rpt.Job,
  27. }
  28. for _, t := range topologies {
  29. if len(t.Nodes) > 0 {
  30. return true
  31. }
  32. }
  33. return false
  34. }
  35. func isPauseContainer(n report.Node) bool {
  36. image, ok := n.Latest.Lookup(report.DockerImageName)
  37. return ok && report.IsPauseImageName(image)
  38. }
  39. // PodRenderer is a Renderer which produces a renderable kubernetes
  40. // graph by merging the container graph and the pods topology.
  41. var PodRenderer = Memoise(ConditionalRenderer(renderKubernetesTopologies,
  42. MakeFilter(
  43. func(n report.Node) bool {
  44. state, ok := n.Latest.Lookup(report.KubernetesState)
  45. return !ok || !(state == report.StateDeleted || state == report.StateFailed)
  46. },
  47. MakeReduce(
  48. PropagateSingleMetrics(report.Container,
  49. MakeMap(propagatePodHost,
  50. Map2Parent{topologies: []string{report.Pod}, noParentsPseudoID: UnmanagedID,
  51. chainRenderer: MakeFilter(
  52. ComposeFilterFuncs(
  53. IsRunning,
  54. Complement(isPauseContainer),
  55. ),
  56. ContainerWithImageNameRenderer,
  57. )},
  58. ),
  59. ),
  60. ConnectionJoin(MapPod2IP, report.Pod),
  61. KubernetesVolumesRenderer,
  62. ),
  63. ),
  64. ))
  65. // Pods are not tagged with a Host parent, but their container children are.
  66. // If n doesn't already have a host, copy it from one of the children
  67. func propagatePodHost(n report.Node) report.Node {
  68. if n.Topology != report.Pod {
  69. return n
  70. } else if _, found := n.Parents.Lookup(report.Host); found {
  71. return n
  72. }
  73. done := false
  74. n.Children.ForEach(func(child report.Node) {
  75. if !done {
  76. if hosts, found := child.Parents.Lookup(report.Host); found {
  77. for _, h := range hosts {
  78. n = n.WithParent(report.Host, h)
  79. }
  80. done = true
  81. }
  82. }
  83. })
  84. return n
  85. }
  86. // PodServiceRenderer is a Renderer which produces a renderable kubernetes services
  87. // graph by merging the pods graph and the services topology.
  88. //
  89. // not memoised
  90. var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies,
  91. renderParents(
  92. report.Pod, []string{report.Service}, "",
  93. PodRenderer,
  94. ),
  95. )
  96. // KubeControllerRenderer is a Renderer which combines all the 'controller' topologies.
  97. // Pods with no controller are mapped to 'Unmanaged'
  98. // We can't simply combine the rendered graphs of the high level objects as they would never
  99. // have connections to each other.
  100. //
  101. // not memoised
  102. var KubeControllerRenderer = ConditionalRenderer(renderKubernetesTopologies,
  103. renderParents(
  104. report.Pod, []string{report.Deployment, report.DaemonSet, report.StatefulSet, report.CronJob, report.Job}, UnmanagedID,
  105. PodRenderer,
  106. ),
  107. )
  108. // renderParents produces a 'standard' renderer for mapping from some child topology to some parent topologies,
  109. // by taking a child renderer, mapping to parents, propagating single metrics, and joining with full parent topology.
  110. // Other options are as per Map2Parent.
  111. func renderParents(childTopology string, parentTopologies []string, noParentsPseudoID string, childRenderer Renderer) Renderer {
  112. selectors := make([]Renderer, len(parentTopologies))
  113. for i, topology := range parentTopologies {
  114. selectors[i] = TopologySelector(topology)
  115. }
  116. return MakeReduce(append(
  117. selectors,
  118. PropagateSingleMetrics(childTopology,
  119. Map2Parent{topologies: parentTopologies, noParentsPseudoID: noParentsPseudoID,
  120. chainRenderer: childRenderer},
  121. ),
  122. )...)
  123. }
  124. // MapPod2IP maps pod nodes to their IP address. This allows pods to
  125. // be joined directly with the endpoint topology.
  126. func MapPod2IP(m report.Node) []string {
  127. // if this pod belongs to the host's networking namespace
  128. // we cannot use its IP to attribute connections
  129. // (they could come from any other process on the host or DNAT-ed IPs)
  130. if _, ok := m.Latest.Lookup(report.KubernetesIsInHostNetwork); ok {
  131. return nil
  132. }
  133. ip, ok := m.Latest.Lookup(report.KubernetesIP)
  134. if !ok || ip == "" {
  135. return nil
  136. }
  137. return []string{report.MakeScopedEndpointNodeID("", ip, "")}
  138. }
  139. // Map2Parent is a Renderer which maps Nodes to some parent grouping.
  140. type Map2Parent struct {
  141. // Renderer to chain from
  142. chainRenderer Renderer
  143. // The topology IDs to look for parents in
  144. topologies []string
  145. // Either the ID prefix of the pseudo node to use for nodes without
  146. // any parents in the group, eg. UnmanagedID, or "" to drop nodes without any parents.
  147. noParentsPseudoID string
  148. }
  149. // Render implements Renderer
  150. func (m Map2Parent) Render(ctx context.Context, rpt report.Report) Nodes {
  151. input := m.chainRenderer.Render(ctx, rpt)
  152. ret := newJoinResults(nil)
  153. for _, n := range input.Nodes {
  154. // Uncontained becomes Unmanaged/whatever if noParentsPseudoID is set
  155. if m.noParentsPseudoID != "" && strings.HasPrefix(n.ID, UncontainedIDPrefix) {
  156. id := MakePseudoNodeID(m.noParentsPseudoID, n.ID[len(UncontainedIDPrefix):])
  157. ret.addChildAndChildren(n, id, Pseudo)
  158. continue
  159. }
  160. // Propagate all pseudo nodes
  161. if n.Topology == Pseudo {
  162. ret.passThrough(n)
  163. continue
  164. }
  165. added := false
  166. // For each topology, map to any parents we can find
  167. for _, topology := range m.topologies {
  168. if groupIDs, ok := n.Parents.Lookup(topology); ok {
  169. for _, id := range groupIDs {
  170. ret.addChildAndChildren(n, id, topology)
  171. added = true
  172. }
  173. }
  174. }
  175. if !added && m.noParentsPseudoID != "" {
  176. // Map to pseudo node
  177. id := MakePseudoNodeID(m.noParentsPseudoID, report.ExtractHostID(n))
  178. ret.addChildAndChildren(n, id, Pseudo)
  179. }
  180. }
  181. return ret.result(ctx, input)
  182. }