mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 10:11:03 +00:00
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.
147 lines
4.5 KiB
Go
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()
|
|
}
|