Files
weave-scope/render/process.go
Matthias Radestock 956303694a always apply ColorConnected in process renderers
This eliminates the awkward distinction between ProcessRenderer and
ColorConnectedProcessRenderer.

It also ensures that processes resulting from direct rendering of the
process topology (/api/topology/processes is invoking
ProcessWithContainerNameRenderer and /api/topology/processes-by-name
is invoking ProcessNameRenderer) are colored and hence summarising
them correctly sets the 'linkable' property. This was the behaviour
prior to the revamping of the rendering pipeline. However, it doesn't
actually make a practical difference since process detail panels only
show other processes as connection endpoints, and these are always
marked linkable anyway.
2017-11-28 06:54:15 +00:00

147 lines
4.5 KiB
Go

package render
import (
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/process"
"github.com/weaveworks/scope/report"
)
// Constants are used in the tests.
const (
InboundMajor = "The Internet"
OutboundMajor = "The Internet"
InboundMinor = "Inbound connections"
OutboundMinor = "Outbound connections"
// Topology for pseudo-nodes and IPs so we can differentiate them at the end
Pseudo = "pseudo"
)
func renderProcesses(rpt report.Report) bool {
return len(rpt.Process.Nodes) >= 1
}
// EndpointRenderer is a Renderer which produces a renderable endpoint graph.
var EndpointRenderer = SelectEndpoint
// ProcessRenderer is a Renderer which produces a renderable process
// graph by merging the endpoint graph and the process topology. It
// also colors connected nodes. Since the process topology views only
// show connected processes, we need this info to determine whether
// processes appearing in a details panel are linkable.
var ProcessRenderer = Memoise(ColorConnected(endpoints2Processes{}))
// processWithContainerNameRenderer is a Renderer which produces a process
// graph enriched with container names where appropriate
type processWithContainerNameRenderer struct {
Renderer
}
func (r processWithContainerNameRenderer) Render(rpt report.Report) Nodes {
processes := r.Renderer.Render(rpt)
containers := SelectContainer.Render(rpt)
outputs := report.Nodes{}
for id, p := range processes.Nodes {
outputs[id] = p
containerID, timestamp, ok := p.Latest.LookupEntry(docker.ContainerID)
if !ok {
continue
}
container, ok := containers.Nodes[report.MakeContainerNodeID(containerID)]
if !ok {
continue
}
p.Latest = p.Latest.Set(docker.ContainerID, timestamp, containerID)
if containerName, timestamp, ok := container.Latest.LookupEntry(docker.ContainerName); ok {
p.Latest = p.Latest.Set(docker.ContainerName, timestamp, containerName)
}
outputs[id] = p
}
return Nodes{Nodes: outputs, Filtered: processes.Filtered}
}
// ProcessWithContainerNameRenderer is a Renderer which produces a process
// graph enriched with container names where appropriate
//
// not memoised
var ProcessWithContainerNameRenderer = processWithContainerNameRenderer{ProcessRenderer}
// ProcessNameRenderer is a Renderer which produces a renderable process
// name graph by munging the progess graph.
//
// not memoised
var ProcessNameRenderer = CustomRenderer{RenderFunc: processes2Names, Renderer: ProcessRenderer}
// endpoints2Processes joins the endpoint topology to the process
// topology, matching on hostID and pid.
type endpoints2Processes struct {
}
func (e endpoints2Processes) Render(rpt report.Report) Nodes {
if len(rpt.Process.Nodes) == 0 {
return Nodes{}
}
local := LocalNetworks(rpt)
processes := SelectProcess.Render(rpt)
endpoints := SelectEndpoint.Render(rpt)
ret := newJoinResults()
for _, n := range endpoints.Nodes {
// Nodes without a hostid are treated as pseudo nodes
if hostNodeID, ok := n.Latest.Lookup(report.HostNodeID); !ok {
if id, ok := pseudoNodeID(n, local); ok {
ret.addChild(n, id, newPseudoNode)
}
} else {
pid, timestamp, ok := n.Latest.LookupEntry(process.PID)
if !ok {
continue
}
if len(n.Adjacency) > 1 {
// We cannot be sure that the pid is associated with all the
// connections. It is better to drop such an endpoint than
// risk rendering bogus connections.
continue
}
hostID, _, _ := report.ParseNodeID(hostNodeID)
id := report.MakeProcessNodeID(hostID, pid)
ret.addChild(n, id, func(id string) report.Node {
if processNode, found := processes.Nodes[id]; found {
return processNode
}
// we have a pid, but no matching process node; create a new one rather than dropping the data
return report.MakeNode(id).WithTopology(report.Process).
WithLatest(process.PID, timestamp, pid)
})
}
}
ret.copyUnmatched(processes)
ret.fixupAdjacencies(processes)
ret.fixupAdjacencies(endpoints)
return ret.result()
}
// processes2Names maps process Nodes to Nodes for each process name.
func processes2Names(processes Nodes) Nodes {
ret := newJoinResults()
for _, n := range processes.Nodes {
if n.Topology == Pseudo {
ret.passThrough(n)
} else {
name, timestamp, ok := n.Latest.LookupEntry(process.Name)
if ok {
ret.addChildAndChildren(n, name, func(id string) report.Node {
return report.MakeNode(id).WithTopology(MakeGroupNodeTopology(n.Topology, process.Name)).
WithLatest(process.Name, timestamp, name)
})
}
}
}
ret.fixupAdjacencies(processes)
return ret.result()
}