Files
weave-scope/render/process.go
Matthias Radestock 9bd8bd825b remove now superfluous Renderer.Stats method
step 2 (and final step) in producing stats as part of Rendering
2017-11-08 07:15:28 +00:00

155 lines
4.8 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.
var ProcessRenderer = Memoise(endpoints2Processes{})
// ColorConnectedProcessRenderer colors connected nodes from
// ProcessRenderer. 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 ColorConnectedProcessRenderer = Memoise(ColorConnected(ProcessRenderer))
// 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, dct Decorator) Nodes {
processes := r.Renderer.Render(rpt, dct)
containers := SelectContainer.Render(rpt, dct)
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 = ConditionalRenderer(renderProcesses,
MakeMap(
MapProcess2Name,
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, dct Decorator) Nodes {
if len(rpt.Process.Nodes) == 0 {
return Nodes{}
}
local := LocalNetworks(rpt)
processes := SelectProcess.Render(rpt, dct)
endpoints := SelectEndpoint.Render(rpt, dct)
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.addToResults(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.addToResults(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()
}
// MapProcess2Name maps process Nodes to Nodes
// for each process name.
//
// This mapper is unlike the other foo2bar mappers as the intention
// is not to join the information with another topology.
func MapProcess2Name(n report.Node, _ report.Networks) report.Nodes {
if n.Topology == Pseudo {
return report.Nodes{n.ID: n}
}
name, timestamp, ok := n.Latest.LookupEntry(process.Name)
if !ok {
return report.Nodes{}
}
node := NewDerivedNode(name, n).WithTopology(MakeGroupNodeTopology(n.Topology, process.Name))
node.Latest = node.Latest.Set(process.Name, timestamp, name)
node.Counters = node.Counters.Add(n.Topology, 1)
return report.Nodes{name: node}
}