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