forked from weaveworks/scope
-
Notifications
You must be signed in to change notification settings - Fork 0
/
render.go
199 lines (169 loc) · 5.39 KB
/
render.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package render
import (
"github.com/weaveworks/scope/report"
)
// MapFunc is anything which can take an arbitrary Node and
// return a set of other Nodes.
//
// If the output is empty, the node shall be omitted from the rendered topology.
type MapFunc func(report.Node, report.Networks) report.Nodes
// Renderer is something that can render a report to a set of Nodes.
type Renderer interface {
Render(report.Report, Decorator) report.Nodes
Stats(report.Report, Decorator) Stats
}
// Stats is the type returned by Renderer.Stats
type Stats struct {
FilteredNodes int
}
func (s Stats) merge(other Stats) Stats {
return Stats{
FilteredNodes: s.FilteredNodes + other.FilteredNodes,
}
}
// Reduce renderer is a Renderer which merges together the output of several
// other renderers.
type Reduce []Renderer
// MakeReduce is the only sane way to produce a Reduce Renderer.
func MakeReduce(renderers ...Renderer) Renderer {
r := Reduce(renderers)
return Memoise(&r)
}
// Render produces a set of Nodes given a Report.
func (r *Reduce) Render(rpt report.Report, dct Decorator) report.Nodes {
result := report.Nodes{}
for _, renderer := range *r {
result = result.Merge(renderer.Render(rpt, dct))
}
return result
}
// Stats implements Renderer
func (r *Reduce) Stats(rpt report.Report, dct Decorator) Stats {
var result Stats
for _, renderer := range *r {
result = result.merge(renderer.Stats(rpt, dct))
}
return result
}
// Map is a Renderer which produces a set of Nodes from the set of
// Nodes produced by another Renderer.
type Map struct {
MapFunc
Renderer
}
// MakeMap makes a new Map
func MakeMap(f MapFunc, r Renderer) Renderer {
return Memoise(&Map{f, r})
}
// Render transforms a set of Nodes produces by another Renderer.
// using a map function
func (m *Map) Render(rpt report.Report, dct Decorator) report.Nodes {
var (
input = m.Renderer.Render(rpt, dct)
output = report.Nodes{}
mapped = map[string]report.IDList{} // input node ID -> output node IDs
adjacencies = map[string]report.IDList{} // output node ID -> input node Adjacencies
localNetworks = LocalNetworks(rpt)
)
// Rewrite all the nodes according to the map function
for _, inRenderable := range input {
for _, outRenderable := range m.MapFunc(inRenderable, localNetworks) {
existing, ok := output[outRenderable.ID]
if ok {
outRenderable = outRenderable.Merge(existing)
}
output[outRenderable.ID] = outRenderable
mapped[inRenderable.ID] = mapped[inRenderable.ID].Add(outRenderable.ID)
adjacencies[outRenderable.ID] = adjacencies[outRenderable.ID].Merge(inRenderable.Adjacency)
}
}
// Rewrite Adjacency for new node IDs.
for outNodeID, inAdjacency := range adjacencies {
outAdjacency := report.MakeIDList()
for _, inAdjacent := range inAdjacency {
for _, outAdjacent := range mapped[inAdjacent] {
outAdjacency = outAdjacency.Add(outAdjacent)
}
}
outNode := output[outNodeID]
outNode.Adjacency = outAdjacency
output[outNodeID] = outNode
}
return output
}
// Stats implements Renderer
func (m *Map) Stats(_ report.Report, _ Decorator) Stats {
// There doesn't seem to be an instance where we want stats to recurse
// through Maps - for instance we don't want to see the number of filtered
// processes in the container renderer.
return Stats{}
}
// Decorator transforms one renderer to another. e.g. Filters.
type Decorator func(Renderer) Renderer
// ComposeDecorators composes decorators into one.
func ComposeDecorators(decorators ...Decorator) Decorator {
return func(r Renderer) Renderer {
for _, decorator := range decorators {
r = decorator(r)
}
return r
}
}
type applyDecorator struct {
Renderer
}
func (ad applyDecorator) Render(rpt report.Report, dct Decorator) report.Nodes {
if dct != nil {
return dct(ad.Renderer).Render(rpt, nil)
}
return ad.Renderer.Render(rpt, nil)
}
func (ad applyDecorator) Stats(rpt report.Report, dct Decorator) Stats {
if dct != nil {
return dct(ad.Renderer).Stats(rpt, nil)
}
return Stats{}
}
// ApplyDecorator returns a renderer which will apply the given decorator to the child render.
func ApplyDecorator(renderer Renderer) Renderer {
return applyDecorator{renderer}
}
func propagateLatest(key string, from, to report.Node) report.Node {
if value, timestamp, ok := from.Latest.LookupEntry(key); ok {
to.Latest = to.Latest.Set(key, timestamp, value)
}
return to
}
// Condition is a predecate over the entire report that can evaluate to true or false.
type Condition func(report.Report) bool
type conditionalRenderer struct {
Condition
Renderer
}
// ConditionalRenderer renders nothing if the condition is false, otherwise it defers
// to the wrapped Renderer.
func ConditionalRenderer(c Condition, r Renderer) Renderer {
return Memoise(conditionalRenderer{c, r})
}
func (cr conditionalRenderer) Render(rpt report.Report, dct Decorator) report.Nodes {
if cr.Condition(rpt) {
return cr.Renderer.Render(rpt, dct)
}
return report.Nodes{}
}
func (cr conditionalRenderer) Stats(rpt report.Report, dct Decorator) Stats {
if cr.Condition(rpt) {
return cr.Renderer.Stats(rpt, dct)
}
return Stats{}
}
// ConstantRenderer renders a fixed set of nodes
type ConstantRenderer report.Nodes
// Render implements Renderer
func (c ConstantRenderer) Render(_ report.Report, _ Decorator) report.Nodes {
return report.Nodes(c)
}
// Stats implements Renderer
func (c ConstantRenderer) Stats(_ report.Report, _ Decorator) Stats {
return Stats{}
}