render.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. package render
  2. import (
  3. "context"
  4. opentracing "github.com/opentracing/opentracing-go"
  5. otlog "github.com/opentracing/opentracing-go/log"
  6. "github.com/weaveworks/scope/report"
  7. )
  8. // MapFunc is anything which can take an arbitrary Node and
  9. // return another Node.
  10. //
  11. // If the output ID is blank, the node shall be omitted from the rendered topology.
  12. // (we chose not to return an extra bool because it adds clutter)
  13. type MapFunc func(report.Node) report.Node
  14. // Renderer is something that can render a report to a set of Nodes.
  15. type Renderer interface {
  16. Render(context.Context, report.Report) Nodes
  17. }
  18. // Nodes is the result of Rendering
  19. type Nodes struct {
  20. report.Nodes
  21. Filtered int
  22. }
  23. // Merge merges the results of Rendering
  24. func (r Nodes) Merge(o Nodes) Nodes {
  25. return Nodes{
  26. Nodes: r.Nodes.Merge(o.Nodes),
  27. Filtered: r.Filtered + o.Filtered,
  28. }
  29. }
  30. // Transformer is something that transforms one set of Nodes to
  31. // another set of Nodes.
  32. type Transformer interface {
  33. Transform(nodes Nodes) Nodes
  34. }
  35. // Transformers is a composition of Transformers
  36. type Transformers []Transformer
  37. // Transform implements Transformer
  38. func (ts Transformers) Transform(nodes Nodes) Nodes {
  39. for _, t := range ts {
  40. nodes = t.Transform(nodes)
  41. }
  42. return nodes
  43. }
  44. // Render renders the report and then transforms it
  45. func Render(ctx context.Context, rpt report.Report, renderer Renderer, transformer Transformer) Nodes {
  46. span, ctx := opentracing.StartSpanFromContext(ctx, "Render:"+typeName(renderer))
  47. defer span.Finish()
  48. return transformer.Transform(renderer.Render(ctx, rpt))
  49. }
  50. // Reduce renderer is a Renderer which merges together the output of several
  51. // other renderers.
  52. type Reduce []Renderer
  53. // MakeReduce is the only sane way to produce a Reduce Renderer.
  54. func MakeReduce(renderers ...Renderer) Renderer {
  55. return Reduce(renderers)
  56. }
  57. // Render produces a set of Nodes given a Report.
  58. func (r Reduce) Render(ctx context.Context, rpt report.Report) Nodes {
  59. if ctx.Err() != nil {
  60. return Nodes{}
  61. }
  62. span, ctx := opentracing.StartSpanFromContext(ctx, "Reduce.Render")
  63. defer span.Finish()
  64. l := len(r)
  65. switch l {
  66. case 0:
  67. return Nodes{}
  68. }
  69. c := make(chan Nodes, l)
  70. for _, renderer := range r {
  71. renderer := renderer // Pike!!
  72. go func() {
  73. span, ctx := opentracing.StartSpanFromContext(ctx, typeName(renderer))
  74. c <- renderer.Render(ctx, rpt)
  75. span.Finish()
  76. }()
  77. }
  78. for ; l > 1; l-- {
  79. left, right := <-c, <-c
  80. go func() {
  81. c <- left.Merge(right)
  82. }()
  83. }
  84. return <-c
  85. }
  86. // Map is a Renderer which produces a set of Nodes from the set of
  87. // Nodes produced by another Renderer.
  88. type Map struct {
  89. MapFunc
  90. Renderer
  91. }
  92. // MakeMap makes a new Map
  93. func MakeMap(f MapFunc, r Renderer) Renderer {
  94. return Map{f, r}
  95. }
  96. // Render transforms a set of Nodes produces by another Renderer.
  97. // using a map function
  98. func (m Map) Render(ctx context.Context, rpt report.Report) Nodes {
  99. span, ctx := opentracing.StartSpanFromContext(ctx, "Map.Render:"+functionName(m.MapFunc))
  100. defer span.Finish()
  101. var (
  102. input = m.Renderer.Render(ctx, rpt)
  103. output = newJoinResults(nil)
  104. )
  105. // Rewrite all the nodes according to the map function
  106. for _, inRenderable := range input.Nodes {
  107. if ctx.Err() != nil { // check if cancelled
  108. return Nodes{}
  109. }
  110. outRenderable := m.MapFunc(inRenderable)
  111. if outRenderable.ID != "" {
  112. output.add(inRenderable.ID, outRenderable)
  113. }
  114. }
  115. span.LogFields(otlog.Int("input.nodes", len(input.Nodes)),
  116. otlog.Int("ouput.nodes", len(output.nodes)))
  117. return output.result(ctx, input)
  118. }
  119. // Condition is a predecate over the entire report that can evaluate to true or false.
  120. type Condition func(report.Report) bool
  121. type conditionalRenderer struct {
  122. Condition
  123. Renderer
  124. }
  125. // ConditionalRenderer renders nothing if the condition is false, otherwise it defers
  126. // to the wrapped Renderer.
  127. func ConditionalRenderer(c Condition, r Renderer) Renderer {
  128. return conditionalRenderer{c, r}
  129. }
  130. func (cr conditionalRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
  131. if cr.Condition(rpt) {
  132. return cr.Renderer.Render(ctx, rpt)
  133. }
  134. return Nodes{}
  135. }
  136. // joinResults is used by Renderers that join sets of nodes
  137. type joinResults struct {
  138. nodes report.Nodes
  139. mapped map[string]string // input node ID -> output node ID - common case
  140. multi map[string][]string // input node ID -> output node IDs - exceptional case
  141. }
  142. func newJoinResults(inputNodes report.Nodes) joinResults {
  143. nodes := make(report.Nodes, len(inputNodes))
  144. for id, n := range inputNodes {
  145. n.Adjacency = nil // result() assumes all nodes start with no adjacencies
  146. n.Children = n.Children.Copy() // so we can do unsafe adds
  147. nodes[id] = n
  148. }
  149. return joinResults{nodes: nodes, mapped: map[string]string{}, multi: map[string][]string{}}
  150. }
  151. func (ret *joinResults) mapChild(from, to string) {
  152. if _, ok := ret.mapped[from]; !ok {
  153. ret.mapped[from] = to
  154. } else {
  155. ret.multi[from] = append(ret.multi[from], to)
  156. }
  157. }
  158. // Add m into the results as a top-level node, mapped from original ID
  159. // Note it is not safe to mix calls to add() with addChild(), addChildAndChildren() or addUnmappedChild()
  160. func (ret *joinResults) add(from string, m report.Node) {
  161. if existing, ok := ret.nodes[m.ID]; ok {
  162. m = m.Merge(existing)
  163. }
  164. ret.nodes[m.ID] = m
  165. ret.mapChild(from, m.ID)
  166. }
  167. // Add m as a child of the node at id, creating a new result node in
  168. // the specified topology if not already there.
  169. func (ret *joinResults) addUnmappedChild(m report.Node, id string, topology string) {
  170. result, exists := ret.nodes[id]
  171. if !exists {
  172. result = report.MakeNode(id).WithTopology(topology)
  173. }
  174. result.Children.UnsafeAdd(m)
  175. if m.Topology != report.Endpoint { // optimisation: we never look at endpoint counts
  176. result = result.AddCounter(m.Topology, 1)
  177. }
  178. ret.nodes[id] = result
  179. }
  180. // Add m as a child of the node at id, creating a new result node in
  181. // the specified topology if not already there, and updating the
  182. // mapping from old ID to new ID.
  183. func (ret *joinResults) addChild(m report.Node, id string, topology string) {
  184. ret.addUnmappedChild(m, id, topology)
  185. ret.mapChild(m.ID, id)
  186. }
  187. // Like addChild, but also add m's children.
  188. func (ret *joinResults) addChildAndChildren(m report.Node, id string, topology string) {
  189. ret.addUnmappedChild(m, id, topology)
  190. result := ret.nodes[id]
  191. result.Children.UnsafeMerge(m.Children)
  192. ret.nodes[id] = result
  193. ret.mapChild(m.ID, id)
  194. }
  195. // Add a copy of n straight into the results
  196. func (ret *joinResults) passThrough(n report.Node) {
  197. n.Adjacency = nil // result() assumes all nodes start with no adjacencies
  198. ret.nodes[n.ID] = n
  199. n.Children = n.Children.Copy() // so we can do unsafe adds
  200. ret.mapChild(n.ID, n.ID)
  201. }
  202. // Rewrite Adjacency of nodes in ret mapped from original nodes in
  203. // input, and return the result.
  204. func (ret *joinResults) result(ctx context.Context, input Nodes) Nodes {
  205. for _, n := range input.Nodes {
  206. if ctx.Err() != nil { // check if cancelled
  207. return Nodes{}
  208. }
  209. outID, ok := ret.mapped[n.ID]
  210. if !ok {
  211. continue
  212. }
  213. ret.rewriteAdjacency(outID, n.Adjacency)
  214. for _, outID := range ret.multi[n.ID] {
  215. ret.rewriteAdjacency(outID, n.Adjacency)
  216. }
  217. }
  218. return Nodes{Nodes: ret.nodes}
  219. }
  220. func (ret *joinResults) rewriteAdjacency(outID string, adjacency report.IDList) {
  221. out := ret.nodes[outID]
  222. // for each adjacency in the original node, find out what it maps
  223. // to (if any), and add that to the new node
  224. for _, a := range adjacency {
  225. if mappedDest, found := ret.mapped[a]; found {
  226. out.Adjacency = out.Adjacency.Add(mappedDest)
  227. out.Adjacency = out.Adjacency.Add(ret.multi[a]...)
  228. }
  229. }
  230. ret.nodes[outID] = out
  231. }
  232. // ResetCache blows away the rendered node cache, and known service
  233. // cache.
  234. func ResetCache() {
  235. renderCache.Purge()
  236. purgeKnownServiceCache()
  237. }