mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-16 20:11:09 +00:00
The nodes returned from Map2Parent are mostly empty, and need joining with their full topologies before being useful. But we want to filter out any nodes that weren't already in the graph. We make a new Renderer type to implement this. We also add the missing types to the type option group
234 lines
6.5 KiB
Go
234 lines
6.5 KiB
Go
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
|
|
}
|
|
|
|
// ReduceFirstOnly renderer is a Renderer which merges together the output of several other renderers,
|
|
// including only the nodes present in the first render, but merging in info from the same nodes
|
|
// if present in the other renders.
|
|
type ReduceFirstOnly struct {
|
|
first Renderer
|
|
others []Renderer
|
|
}
|
|
|
|
// MakeReduceFirstOnly is the only sane way to produce a ReduceFirstOnly Renderer.
|
|
func MakeReduceFirstOnly(first Renderer, others ...Renderer) Renderer {
|
|
r := ReduceFirstOnly{first, others}
|
|
return Memoise(&r)
|
|
}
|
|
|
|
// Render produces a set of Nodes given a Report.
|
|
func (r *ReduceFirstOnly) Render(rpt report.Report, dct Decorator) report.Nodes {
|
|
result := r.first.Render(rpt, dct).Copy()
|
|
for _, other := range r.others {
|
|
for id, node := range other.Render(rpt, dct) {
|
|
if n, ok := result[id]; ok {
|
|
result[id] = n.Merge(node)
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Stats implements Renderer
|
|
func (r *ReduceFirstOnly) Stats(rpt report.Report, dct Decorator) Stats {
|
|
// Since we're just taking more detailed data from the other renderers,
|
|
// we probably only care about the filter stats for the 'main' renderer.
|
|
return r.first.Stats(rpt, dct)
|
|
}
|
|
|
|
// 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{}
|
|
}
|