mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-21 22:36:39 +00:00
Merge pull request #425 from weaveworks/357-no-more-leaf-map
Remove LeafMap - have a generic function which converts a Topology to RenderableNodes
This commit is contained in:
@@ -10,22 +10,14 @@ import (
|
||||
|
||||
// Sterilize cleans up RenderableNodes test fixtures
|
||||
func Sterilize(r render.RenderableNodes) render.RenderableNodes {
|
||||
// Since introducing new map fields to the report.NodeMetadata type, its
|
||||
// zero value is •not valid• -- every time you need one, you need to use
|
||||
// the report.MakeNodeMetadata constructor. (Similarly, but not exactly
|
||||
// the same, is that a zero-value Adjacency is not the same as a created
|
||||
// but empty Adjacency.)
|
||||
//
|
||||
// But we're not doing this in tests. We also don't particularly care
|
||||
// about the output of NodeMetadata for the rendering pipeline, as this
|
||||
// is never serialised to json. So this function sterilizes invalid
|
||||
// RenderableNodes by setting an empty NodeMetadata from the proper
|
||||
// constructor.
|
||||
// RenderableNodes contain NodeMetadatas, but generally we
|
||||
// only care about the Adjacency field - the rest is internal
|
||||
// state not sent to the client. So in the tests we ignore
|
||||
// this state.
|
||||
for id, n := range r {
|
||||
if n.Adjacency == nil {
|
||||
n.Adjacency = report.MakeIDList()
|
||||
}
|
||||
n.NodeMetadata = report.MakeNodeMetadata()
|
||||
n.NodeMetadata.Metadata = report.Metadata{}
|
||||
n.NodeMetadata.Counters = report.Counters{}
|
||||
n.NodeMetadata.Edges = report.EdgeMetadatas{}
|
||||
r[id] = n
|
||||
}
|
||||
return r
|
||||
@@ -41,12 +33,11 @@ var (
|
||||
ID: unknownPseudoNode1ID,
|
||||
LabelMajor: "10.10.10.10",
|
||||
Pseudo: true,
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacency(adjacency),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(70),
|
||||
EgressByteCount: newu64(700),
|
||||
},
|
||||
Adjacency: adjacency,
|
||||
Origins: report.MakeIDList(
|
||||
test.UnknownClient1NodeID,
|
||||
test.UnknownClient2NodeID,
|
||||
@@ -58,12 +49,11 @@ var (
|
||||
ID: unknownPseudoNode2ID,
|
||||
LabelMajor: "10.10.10.11",
|
||||
Pseudo: true,
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacency(adjacency),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(50),
|
||||
EgressByteCount: newu64(500),
|
||||
},
|
||||
Adjacency: adjacency,
|
||||
Origins: report.MakeIDList(
|
||||
test.UnknownClient3NodeID,
|
||||
),
|
||||
@@ -74,12 +64,11 @@ var (
|
||||
ID: render.TheInternetID,
|
||||
LabelMajor: render.TheInternetMajor,
|
||||
Pseudo: true,
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacency(adjacency),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(60),
|
||||
EgressByteCount: newu64(600),
|
||||
},
|
||||
Adjacency: adjacency,
|
||||
Origins: report.MakeIDList(
|
||||
test.RandomClientNodeID,
|
||||
test.GoogleEndpointNodeID,
|
||||
@@ -98,13 +87,12 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", test.ClientHostID, test.Client1PID),
|
||||
Rank: test.Client1Comm,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(ServerProcessID),
|
||||
Origins: report.MakeIDList(
|
||||
test.Client54001NodeID,
|
||||
test.ClientProcess1NodeID,
|
||||
test.ClientHostNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(ServerProcessID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(10),
|
||||
EgressByteCount: newu64(100),
|
||||
@@ -116,13 +104,12 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", test.ClientHostID, test.Client2PID),
|
||||
Rank: test.Client2Comm,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(ServerProcessID),
|
||||
Origins: report.MakeIDList(
|
||||
test.Client54002NodeID,
|
||||
test.ClientProcess2NodeID,
|
||||
test.ClientHostNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(ServerProcessID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(20),
|
||||
EgressByteCount: newu64(200),
|
||||
@@ -134,7 +121,6 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", test.ServerHostID, test.ServerPID),
|
||||
Rank: test.ServerComm,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.Server80NodeID,
|
||||
test.ServerProcessNodeID,
|
||||
@@ -152,13 +138,12 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", test.ServerHostID, test.NonContainerPID),
|
||||
Rank: test.NonContainerComm,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(render.TheInternetID),
|
||||
Origins: report.MakeIDList(
|
||||
test.NonContainerProcessNodeID,
|
||||
test.ServerHostNodeID,
|
||||
test.NonContainerNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(render.TheInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
unknownPseudoNode1ID: unknownPseudoNode1(report.MakeIDList(ServerProcessID)),
|
||||
@@ -173,7 +158,6 @@ var (
|
||||
LabelMinor: "2 processes",
|
||||
Rank: "curl",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList("apache"),
|
||||
Origins: report.MakeIDList(
|
||||
test.Client54001NodeID,
|
||||
test.Client54002NodeID,
|
||||
@@ -181,7 +165,7 @@ var (
|
||||
test.ClientProcess2NodeID,
|
||||
test.ClientHostNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent("apache"),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
@@ -193,7 +177,6 @@ var (
|
||||
LabelMinor: "1 process",
|
||||
Rank: "apache",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.Server80NodeID,
|
||||
test.ServerProcessNodeID,
|
||||
@@ -211,13 +194,12 @@ var (
|
||||
LabelMinor: "1 process",
|
||||
Rank: test.NonContainerComm,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(render.TheInternetID),
|
||||
Origins: report.MakeIDList(
|
||||
test.NonContainerProcessNodeID,
|
||||
test.ServerHostNodeID,
|
||||
test.NonContainerNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(render.TheInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
unknownPseudoNode1ID: unknownPseudoNode1(report.MakeIDList("apache")),
|
||||
@@ -232,7 +214,6 @@ var (
|
||||
LabelMinor: test.ClientHostName,
|
||||
Rank: test.ClientContainerImageID,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(test.ServerContainerID),
|
||||
Origins: report.MakeIDList(
|
||||
test.ClientContainerNodeID,
|
||||
test.Client54001NodeID,
|
||||
@@ -241,7 +222,7 @@ var (
|
||||
test.ClientProcess2NodeID,
|
||||
test.ClientHostNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(test.ServerContainerID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
@@ -253,7 +234,6 @@ var (
|
||||
LabelMinor: test.ServerHostName,
|
||||
Rank: test.ServerContainerImageID,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.ServerContainerNodeID,
|
||||
test.Server80NodeID,
|
||||
@@ -272,13 +252,12 @@ var (
|
||||
LabelMinor: test.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Adjacency: report.MakeIDList(render.TheInternetID),
|
||||
Origins: report.MakeIDList(
|
||||
test.NonContainerProcessNodeID,
|
||||
test.ServerHostNodeID,
|
||||
test.NonContainerNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(render.TheInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
render.TheInternetID: theInternetNode(report.MakeIDList(test.ServerContainerID)),
|
||||
@@ -291,7 +270,6 @@ var (
|
||||
LabelMinor: "1 container",
|
||||
Rank: test.ClientContainerImageName,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(test.ServerContainerImageName),
|
||||
Origins: report.MakeIDList(
|
||||
test.ClientContainerImageNodeID,
|
||||
test.ClientContainerNodeID,
|
||||
@@ -301,7 +279,7 @@ var (
|
||||
test.ClientProcess2NodeID,
|
||||
test.ClientHostNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(test.ServerContainerImageName),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
@@ -313,7 +291,6 @@ var (
|
||||
LabelMinor: "1 container",
|
||||
Rank: test.ServerContainerImageName,
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.ServerContainerImageNodeID,
|
||||
test.ServerContainerNodeID,
|
||||
@@ -332,13 +309,12 @@ var (
|
||||
LabelMinor: test.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Adjacency: report.MakeIDList(render.TheInternetID),
|
||||
Origins: report.MakeIDList(
|
||||
test.NonContainerNodeID,
|
||||
test.NonContainerProcessNodeID,
|
||||
test.ServerHostNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(render.TheInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
render.TheInternetID: theInternetNode(report.MakeIDList(test.ServerContainerImageName)),
|
||||
@@ -356,7 +332,6 @@ var (
|
||||
LabelMinor: "hostname.com", // after first .
|
||||
Rank: "hostname.com",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.ServerHostNodeID,
|
||||
test.ServerAddressNodeID,
|
||||
@@ -372,12 +347,11 @@ var (
|
||||
LabelMinor: "hostname.com", // after first .
|
||||
Rank: "hostname.com",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(ServerHostRenderedID),
|
||||
Origins: report.MakeIDList(
|
||||
test.ClientHostNodeID,
|
||||
test.ClientAddressNodeID,
|
||||
),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
MaxConnCountTCP: newu64(3),
|
||||
},
|
||||
@@ -386,8 +360,7 @@ var (
|
||||
ID: pseudoHostID1,
|
||||
LabelMajor: test.UnknownClient1IP,
|
||||
Pseudo: true,
|
||||
Adjacency: report.MakeIDList(ServerHostRenderedID),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Origins: report.MakeIDList(test.UnknownAddress1NodeID, test.UnknownAddress2NodeID),
|
||||
},
|
||||
@@ -395,8 +368,7 @@ var (
|
||||
ID: pseudoHostID2,
|
||||
LabelMajor: test.UnknownClient3IP,
|
||||
Pseudo: true,
|
||||
Adjacency: report.MakeIDList(ServerHostRenderedID),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Origins: report.MakeIDList(test.UnknownAddress3NodeID),
|
||||
},
|
||||
@@ -404,8 +376,7 @@ var (
|
||||
ID: render.TheInternetID,
|
||||
LabelMajor: render.TheInternetMajor,
|
||||
Pseudo: true,
|
||||
Adjacency: report.MakeIDList(ServerHostRenderedID),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Origins: report.MakeIDList(test.RandomAddressNodeID),
|
||||
},
|
||||
|
||||
@@ -25,26 +25,16 @@ const (
|
||||
processesKey = "processes"
|
||||
)
|
||||
|
||||
// LeafMapFunc is anything which can take an arbitrary NodeMetadata, which is
|
||||
// always one-to-one with nodes in a topology, and return a set of RenderableNodes
|
||||
// - specific representations of the referenced node, in the form of a map of node
|
||||
// ID to a human-readable major and minor labels.
|
||||
//
|
||||
// A single NodeMetadata can yield arbitrary many representations, including
|
||||
// representations that reduce (or even increase) the cardinality of the set of nodes.
|
||||
type LeafMapFunc func(report.NodeMetadata, report.Networks) RenderableNodes
|
||||
|
||||
// MapFunc is anything which can take an arbitrary RenderableNode and
|
||||
// return a set of other RenderableNodes.
|
||||
//
|
||||
// As with LeafMapFunc, if the output is empty, the node
|
||||
// shall be omitted from the rendered topology.
|
||||
type MapFunc func(RenderableNode) RenderableNodes
|
||||
// If the output is empty, the node shall be omitted from the rendered topology.
|
||||
type MapFunc func(RenderableNode, report.Networks) RenderableNodes
|
||||
|
||||
// MapEndpointIdentity maps an endpoint topology node to a single endpoint
|
||||
// renderable node. As it is only ever run on endpoint topology nodes, we
|
||||
// expect that certain keys are present.
|
||||
func MapEndpointIdentity(m report.NodeMetadata, local report.Networks) RenderableNodes {
|
||||
func MapEndpointIdentity(m RenderableNode, local report.Networks) RenderableNodes {
|
||||
addr, ok := m.Metadata[endpoint.Addr]
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
@@ -60,8 +50,8 @@ func MapEndpointIdentity(m report.NodeMetadata, local report.Networks) Renderabl
|
||||
if !ok {
|
||||
// If the dstNodeAddr is not in a network local to this report, we emit an
|
||||
// internet node
|
||||
if !local.Contains(net.ParseIP(addr)) {
|
||||
return RenderableNodes{TheInternetID: newPseudoNode(TheInternetID, TheInternetMajor, "")}
|
||||
if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
|
||||
return RenderableNodes{TheInternetID: newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)}
|
||||
}
|
||||
|
||||
// We are a 'client' pseudo node if the port is in the ephemeral port range.
|
||||
@@ -72,21 +62,21 @@ func MapEndpointIdentity(m report.NodeMetadata, local report.Networks) Renderabl
|
||||
dstNodeID := m.Adjacency[0]
|
||||
serverIP, serverPort := trySplitAddr(dstNodeID)
|
||||
outputID := MakePseudoNodeID(addr, serverIP, serverPort)
|
||||
return RenderableNodes{outputID: newPseudoNode(outputID, addr, "")}
|
||||
return RenderableNodes{outputID: newDerivedPseudoNode(outputID, addr, m)}
|
||||
}
|
||||
|
||||
// Otherwise (the server node is missing), generate a pseudo node for every (server ip, server port)
|
||||
outputID := MakePseudoNodeID(addr, port)
|
||||
if port != "" {
|
||||
return RenderableNodes{outputID: newPseudoNode(outputID, addr+":"+port, "")}
|
||||
return RenderableNodes{outputID: newDerivedPseudoNode(outputID, addr+":"+port, m)}
|
||||
}
|
||||
return RenderableNodes{outputID: newPseudoNode(outputID, addr, "")}
|
||||
return RenderableNodes{outputID: newDerivedPseudoNode(outputID, addr, m)}
|
||||
}
|
||||
|
||||
var (
|
||||
id = MakeEndpointID(report.ExtractHostID(m), addr, port)
|
||||
id = MakeEndpointID(report.ExtractHostID(m.NodeMetadata), addr, port)
|
||||
major = fmt.Sprintf("%s:%s", addr, port)
|
||||
minor = report.ExtractHostID(m)
|
||||
minor = report.ExtractHostID(m.NodeMetadata)
|
||||
rank = major
|
||||
)
|
||||
|
||||
@@ -94,32 +84,32 @@ func MapEndpointIdentity(m report.NodeMetadata, local report.Networks) Renderabl
|
||||
minor = fmt.Sprintf("%s (%s)", minor, pid)
|
||||
}
|
||||
|
||||
return RenderableNodes{id: NewRenderableNode(id, major, minor, rank, m)}
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
}
|
||||
|
||||
// MapProcessIdentity maps a process topology node to a process renderable
|
||||
// node. As it is only ever run on process topology nodes, we expect that
|
||||
// certain keys are present.
|
||||
func MapProcessIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes {
|
||||
func MapProcessIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
pid, ok := m.Metadata[process.PID]
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
var (
|
||||
id = MakeProcessID(report.ExtractHostID(m), pid)
|
||||
id = MakeProcessID(report.ExtractHostID(m.NodeMetadata), pid)
|
||||
major = m.Metadata["comm"]
|
||||
minor = fmt.Sprintf("%s (%s)", report.ExtractHostID(m), pid)
|
||||
minor = fmt.Sprintf("%s (%s)", report.ExtractHostID(m.NodeMetadata), pid)
|
||||
rank = m.Metadata["comm"]
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNode(id, major, minor, rank, m)}
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
}
|
||||
|
||||
// MapContainerIdentity maps a container topology node to a container
|
||||
// renderable node. As it is only ever run on container topology nodes, we
|
||||
// expect that certain keys are present.
|
||||
func MapContainerIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes {
|
||||
func MapContainerIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
id, ok := m.Metadata[docker.ContainerID]
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
@@ -127,17 +117,17 @@ func MapContainerIdentity(m report.NodeMetadata, _ report.Networks) RenderableNo
|
||||
|
||||
var (
|
||||
major = m.Metadata[docker.ContainerName]
|
||||
minor = report.ExtractHostID(m)
|
||||
minor = report.ExtractHostID(m.NodeMetadata)
|
||||
rank = m.Metadata[docker.ImageID]
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNode(id, major, minor, rank, m)}
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
}
|
||||
|
||||
// MapContainerImageIdentity maps a container image topology node to container
|
||||
// image renderable node. As it is only ever run on container image topology
|
||||
// nodes, we expect that certain keys are present.
|
||||
func MapContainerImageIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes {
|
||||
func MapContainerImageIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
id, ok := m.Metadata[docker.ImageID]
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
@@ -148,13 +138,13 @@ func MapContainerImageIdentity(m report.NodeMetadata, _ report.Networks) Rendera
|
||||
rank = m.Metadata[docker.ImageID]
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNode(id, major, "", rank, m)}
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, "", rank, m)}
|
||||
}
|
||||
|
||||
// MapAddressIdentity maps an address topology node to an address renderable
|
||||
// node. As it is only ever run on address topology nodes, we expect that
|
||||
// certain keys are present.
|
||||
func MapAddressIdentity(m report.NodeMetadata, local report.Networks) RenderableNodes {
|
||||
func MapAddressIdentity(m RenderableNode, local report.Networks) RenderableNodes {
|
||||
addr, ok := m.Metadata[endpoint.Addr]
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
@@ -166,7 +156,7 @@ func MapAddressIdentity(m report.NodeMetadata, local report.Networks) Renderable
|
||||
// If the addr is not in a network local to this report, we emit an
|
||||
// internet node
|
||||
if !local.Contains(net.ParseIP(addr)) {
|
||||
return RenderableNodes{TheInternetID: newPseudoNode(TheInternetID, TheInternetMajor, "")}
|
||||
return RenderableNodes{TheInternetID: newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)}
|
||||
}
|
||||
|
||||
// Otherwise generate a pseudo node for every
|
||||
@@ -175,25 +165,25 @@ func MapAddressIdentity(m report.NodeMetadata, local report.Networks) Renderable
|
||||
_, dstAddr, _ := report.ParseAddressNodeID(m.Adjacency[0])
|
||||
outputID = MakePseudoNodeID(addr, dstAddr)
|
||||
}
|
||||
return RenderableNodes{outputID: newPseudoNode(outputID, addr, "")}
|
||||
return RenderableNodes{outputID: newDerivedPseudoNode(outputID, addr, m)}
|
||||
}
|
||||
|
||||
var (
|
||||
id = MakeAddressID(report.ExtractHostID(m), addr)
|
||||
id = MakeAddressID(report.ExtractHostID(m.NodeMetadata), addr)
|
||||
major = addr
|
||||
minor = report.ExtractHostID(m)
|
||||
minor = report.ExtractHostID(m.NodeMetadata)
|
||||
rank = major
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNode(id, major, minor, rank, m)}
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
}
|
||||
|
||||
// MapHostIdentity maps a host topology node to a host renderable node. As it
|
||||
// is only ever run on host topology nodes, we expect that certain keys are
|
||||
// present.
|
||||
func MapHostIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes {
|
||||
func MapHostIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
var (
|
||||
id = MakeHostID(report.ExtractHostID(m))
|
||||
id = MakeHostID(report.ExtractHostID(m.NodeMetadata))
|
||||
hostname = m.Metadata[host.HostName]
|
||||
parts = strings.SplitN(hostname, ".", 2)
|
||||
major, minor, rank = "", "", ""
|
||||
@@ -205,14 +195,14 @@ func MapHostIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes {
|
||||
major = hostname
|
||||
}
|
||||
|
||||
return RenderableNodes{id: NewRenderableNode(id, major, minor, rank, m)}
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
}
|
||||
|
||||
// MapEndpoint2IP maps endpoint nodes to their IP address, for joining
|
||||
// with container nodes. We drop endpoint nodes with pids, as they
|
||||
// will be joined to containers through the process topology, and we
|
||||
// don't want to double count edges.
|
||||
func MapEndpoint2IP(m report.NodeMetadata, local report.Networks) RenderableNodes {
|
||||
func MapEndpoint2IP(m RenderableNode, local report.Networks) RenderableNodes {
|
||||
_, ok := m.Metadata[process.PID]
|
||||
if ok {
|
||||
return RenderableNodes{}
|
||||
@@ -222,22 +212,22 @@ func MapEndpoint2IP(m report.NodeMetadata, local report.Networks) RenderableNode
|
||||
return RenderableNodes{}
|
||||
}
|
||||
if !local.Contains(net.ParseIP(addr)) {
|
||||
return RenderableNodes{TheInternetID: newPseudoNode(TheInternetID, TheInternetMajor, "")}
|
||||
return RenderableNodes{TheInternetID: newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)}
|
||||
}
|
||||
return RenderableNodes{addr: NewRenderableNode(addr, "", "", "", m)}
|
||||
return RenderableNodes{addr: NewRenderableNodeWith(addr, "", "", "", m)}
|
||||
}
|
||||
|
||||
// MapContainer2IP maps container nodes to their IP addresses (outputs
|
||||
// multiple nodes). This allows container to be joined directly with
|
||||
// the endpoint topology.
|
||||
func MapContainer2IP(m report.NodeMetadata, _ report.Networks) RenderableNodes {
|
||||
func MapContainer2IP(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
result := RenderableNodes{}
|
||||
addrs, ok := m.Metadata[docker.ContainerIPs]
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
for _, addr := range strings.Fields(addrs) {
|
||||
n := NewRenderableNode(addr, "", "", "", m)
|
||||
n := NewRenderableNodeWith(addr, "", "", "", m)
|
||||
n.NodeMetadata.Counters[containersKey] = 1
|
||||
result[addr] = n
|
||||
}
|
||||
@@ -247,7 +237,7 @@ func MapContainer2IP(m report.NodeMetadata, _ report.Networks) RenderableNodes {
|
||||
// MapIP2Container maps IP nodes produced from MapContainer2IP back to
|
||||
// container nodes. If there is more than one container with a given
|
||||
// IP, it is dropped.
|
||||
func MapIP2Container(n RenderableNode) RenderableNodes {
|
||||
func MapIP2Container(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
// If an IP is shared between multiple containers, we can't
|
||||
// reliably attribute an connection based on its IP
|
||||
if n.NodeMetadata.Counters[containersKey] > 1 {
|
||||
@@ -267,7 +257,7 @@ func MapIP2Container(n RenderableNode) RenderableNodes {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
return RenderableNodes{id: newDerivedNode(id, n)}
|
||||
return RenderableNodes{id: NewDerivedNode(id, n)}
|
||||
}
|
||||
|
||||
// MapEndpoint2Process maps endpoint RenderableNodes to process
|
||||
@@ -281,7 +271,7 @@ func MapIP2Container(n RenderableNode) RenderableNodes {
|
||||
// format for a process, but without any Major or Minor labels.
|
||||
// It does not have enough info to do that, and the resulting graph
|
||||
// must be merged with a process graph to get that info.
|
||||
func MapEndpoint2Process(n RenderableNode) RenderableNodes {
|
||||
func MapEndpoint2Process(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
@@ -292,7 +282,7 @@ func MapEndpoint2Process(n RenderableNode) RenderableNodes {
|
||||
}
|
||||
|
||||
id := MakeProcessID(report.ExtractHostID(n.NodeMetadata), pid)
|
||||
return RenderableNodes{id: newDerivedNode(id, n)}
|
||||
return RenderableNodes{id: NewDerivedNode(id, n)}
|
||||
}
|
||||
|
||||
// MapProcess2Container maps process RenderableNodes to container
|
||||
@@ -306,7 +296,7 @@ func MapEndpoint2Process(n RenderableNode) RenderableNodes {
|
||||
// format for a container, but without any Major or Minor labels.
|
||||
// It does not have enough info to do that, and the resulting graph
|
||||
// must be merged with a container graph to get that info.
|
||||
func MapProcess2Container(n RenderableNode) RenderableNodes {
|
||||
func MapProcess2Container(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
// Propogate the internet pseudo node
|
||||
if n.ID == TheInternetID {
|
||||
return RenderableNodes{n.ID: n}
|
||||
@@ -330,7 +320,7 @@ func MapProcess2Container(n RenderableNode) RenderableNodes {
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
return RenderableNodes{id: newDerivedNode(id, n)}
|
||||
return RenderableNodes{id: NewDerivedNode(id, n)}
|
||||
}
|
||||
|
||||
// MapProcess2Name maps process RenderableNodes to RenderableNodes
|
||||
@@ -339,7 +329,7 @@ func MapProcess2Container(n RenderableNode) RenderableNodes {
|
||||
// This mapper is unlike the other foo2bar mappers as the intention
|
||||
// is not to join the information with another topology. Therefore
|
||||
// it outputs a properly-formed node with labels etc.
|
||||
func MapProcess2Name(n RenderableNode) RenderableNodes {
|
||||
func MapProcess2Name(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
@@ -349,7 +339,7 @@ func MapProcess2Name(n RenderableNode) RenderableNodes {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
node := newDerivedNode(name, n)
|
||||
node := NewDerivedNode(name, n)
|
||||
node.LabelMajor = name
|
||||
node.Rank = name
|
||||
node.NodeMetadata.Counters[processesKey] = 1
|
||||
@@ -359,7 +349,7 @@ func MapProcess2Name(n RenderableNode) RenderableNodes {
|
||||
// MapCountProcessName maps 1:1 process name nodes, counting
|
||||
// the number of processes grouped together and putting
|
||||
// that info in the minor label.
|
||||
func MapCountProcessName(n RenderableNode) RenderableNodes {
|
||||
func MapCountProcessName(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
@@ -384,7 +374,7 @@ func MapCountProcessName(n RenderableNode) RenderableNodes {
|
||||
// format for a container, but without any Major or Minor labels.
|
||||
// It does not have enough info to do that, and the resulting graph
|
||||
// must be merged with a container graph to get that info.
|
||||
func MapContainer2ContainerImage(n RenderableNode) RenderableNodes {
|
||||
func MapContainer2ContainerImage(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
// Propogate all pseudo nodes
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
@@ -398,7 +388,7 @@ func MapContainer2ContainerImage(n RenderableNode) RenderableNodes {
|
||||
}
|
||||
|
||||
// Add container-<id> key to NMD, which will later be counted to produce the minor label
|
||||
result := newDerivedNode(id, n)
|
||||
result := NewDerivedNode(id, n)
|
||||
result.NodeMetadata.Counters[containersKey] = 1
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
@@ -409,7 +399,7 @@ func MapContainer2ContainerImage(n RenderableNode) RenderableNodes {
|
||||
// This mapper is unlike the other foo2bar mappers as the intention
|
||||
// is not to join the information with another topology. Therefore
|
||||
// it outputs a properly-formed node with labels etc.
|
||||
func MapContainerImage2Name(n RenderableNode) RenderableNodes {
|
||||
func MapContainerImage2Name(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
@@ -424,7 +414,7 @@ func MapContainerImage2Name(n RenderableNode) RenderableNodes {
|
||||
name = parts[0]
|
||||
}
|
||||
|
||||
node := newDerivedNode(name, n)
|
||||
node := NewDerivedNode(name, n)
|
||||
node.LabelMajor = name
|
||||
node.Rank = name
|
||||
node.NodeMetadata = n.NodeMetadata.Copy() // Propagate NMD for container counting.
|
||||
@@ -434,7 +424,7 @@ func MapContainerImage2Name(n RenderableNode) RenderableNodes {
|
||||
// MapCountContainers maps 1:1 container image nodes, counting
|
||||
// the number of containers grouped together and putting
|
||||
// that info in the minor label.
|
||||
func MapCountContainers(n RenderableNode) RenderableNodes {
|
||||
func MapCountContainers(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
@@ -451,13 +441,13 @@ func MapCountContainers(n RenderableNode) RenderableNodes {
|
||||
// MapAddress2Host maps address RenderableNodes to host RenderableNodes.
|
||||
//
|
||||
// Otherthan pseudo nodes, we can assume all nodes have a HostID
|
||||
func MapAddress2Host(n RenderableNode) RenderableNodes {
|
||||
func MapAddress2Host(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
|
||||
id := MakeHostID(report.ExtractHostID(n.NodeMetadata))
|
||||
return RenderableNodes{id: newDerivedNode(id, n)}
|
||||
return RenderableNodes{id: NewDerivedNode(id, n)}
|
||||
}
|
||||
|
||||
// trySplitAddr is basically ParseArbitraryNodeID, since its callsites
|
||||
|
||||
@@ -11,14 +11,18 @@ import (
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
func nrn(nmd report.NodeMetadata) render.RenderableNode {
|
||||
return render.NewRenderableNode("").WithNodeMetadata(nmd)
|
||||
}
|
||||
|
||||
func TestMapEndpointIdentity(t *testing.T) {
|
||||
for _, input := range []testcase{
|
||||
{report.MakeNodeMetadata(), false},
|
||||
{report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "1.2.3.4"}), false},
|
||||
{report.MakeNodeMetadataWith(map[string]string{endpoint.Port: "1234"}), false},
|
||||
{report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "1.2.3.4", endpoint.Port: "1234"}), true},
|
||||
{report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "1.2.3.4", endpoint.Port: "40000"}), true},
|
||||
{report.MakeNodeMetadataWith(map[string]string{report.HostNodeID: report.MakeHostNodeID("foo"), endpoint.Addr: "10.0.0.1", endpoint.Port: "20001"}), true},
|
||||
{nrn(report.MakeNodeMetadata()), false},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "1.2.3.4"})), false},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{endpoint.Port: "1234"})), false},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "1.2.3.4", endpoint.Port: "1234"})), true},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "1.2.3.4", endpoint.Port: "40000"})), true},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{report.HostNodeID: report.MakeHostNodeID("foo"), endpoint.Addr: "10.0.0.1", endpoint.Port: "20001"})), true},
|
||||
} {
|
||||
testMap(t, render.MapEndpointIdentity, input)
|
||||
}
|
||||
@@ -26,8 +30,8 @@ func TestMapEndpointIdentity(t *testing.T) {
|
||||
|
||||
func TestMapProcessIdentity(t *testing.T) {
|
||||
for _, input := range []testcase{
|
||||
{report.MakeNodeMetadata(), false},
|
||||
{report.MakeNodeMetadataWith(map[string]string{process.PID: "201"}), true},
|
||||
{nrn(report.MakeNodeMetadata()), false},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{process.PID: "201"})), true},
|
||||
} {
|
||||
testMap(t, render.MapProcessIdentity, input)
|
||||
}
|
||||
@@ -35,8 +39,8 @@ func TestMapProcessIdentity(t *testing.T) {
|
||||
|
||||
func TestMapContainerIdentity(t *testing.T) {
|
||||
for _, input := range []testcase{
|
||||
{report.MakeNodeMetadata(), false},
|
||||
{report.MakeNodeMetadataWith(map[string]string{docker.ContainerID: "a1b2c3"}), true},
|
||||
{nrn(report.MakeNodeMetadata()), false},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{docker.ContainerID: "a1b2c3"})), true},
|
||||
} {
|
||||
testMap(t, render.MapContainerIdentity, input)
|
||||
}
|
||||
@@ -44,8 +48,8 @@ func TestMapContainerIdentity(t *testing.T) {
|
||||
|
||||
func TestMapContainerImageIdentity(t *testing.T) {
|
||||
for _, input := range []testcase{
|
||||
{report.MakeNodeMetadata(), false},
|
||||
{report.MakeNodeMetadataWith(map[string]string{docker.ImageID: "a1b2c3"}), true},
|
||||
{nrn(report.MakeNodeMetadata()), false},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{docker.ImageID: "a1b2c3"})), true},
|
||||
} {
|
||||
testMap(t, render.MapContainerImageIdentity, input)
|
||||
}
|
||||
@@ -53,8 +57,8 @@ func TestMapContainerImageIdentity(t *testing.T) {
|
||||
|
||||
func TestMapAddressIdentity(t *testing.T) {
|
||||
for _, input := range []testcase{
|
||||
{report.MakeNodeMetadata(), false},
|
||||
{report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "192.168.1.1"}), true},
|
||||
{nrn(report.MakeNodeMetadata()), false},
|
||||
{nrn(report.MakeNodeMetadataWith(map[string]string{endpoint.Addr: "192.168.1.1"})), true},
|
||||
} {
|
||||
testMap(t, render.MapAddressIdentity, input)
|
||||
}
|
||||
@@ -62,18 +66,18 @@ func TestMapAddressIdentity(t *testing.T) {
|
||||
|
||||
func TestMapHostIdentity(t *testing.T) {
|
||||
for _, input := range []testcase{
|
||||
{report.MakeNodeMetadata(), true}, // TODO it's questionable if this is actually correct
|
||||
{nrn(report.MakeNodeMetadata()), true}, // TODO it's questionable if this is actually correct
|
||||
} {
|
||||
testMap(t, render.MapHostIdentity, input)
|
||||
}
|
||||
}
|
||||
|
||||
type testcase struct {
|
||||
md report.NodeMetadata
|
||||
md render.RenderableNode
|
||||
ok bool
|
||||
}
|
||||
|
||||
func testMap(t *testing.T, f render.LeafMapFunc, input testcase) {
|
||||
func testMap(t *testing.T, f render.MapFunc, input testcase) {
|
||||
_, ipNet, err := net.ParseCIDR("1.2.3.0/16")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
|
||||
144
render/render.go
144
render/render.go
@@ -23,7 +23,7 @@ func MakeReduce(renderers ...Renderer) Renderer {
|
||||
func (r Reduce) Render(rpt report.Report) RenderableNodes {
|
||||
result := RenderableNodes{}
|
||||
for _, renderer := range r {
|
||||
result.Merge(renderer.Render(rpt))
|
||||
result = result.Merge(renderer.Render(rpt))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -52,17 +52,20 @@ func (m Map) Render(rpt report.Report) RenderableNodes {
|
||||
}
|
||||
|
||||
func (m Map) render(rpt report.Report) (RenderableNodes, map[string]report.IDList) {
|
||||
input := m.Renderer.Render(rpt)
|
||||
output := RenderableNodes{}
|
||||
mapped := map[string]report.IDList{} // input node ID -> output node IDs
|
||||
adjacencies := map[string]report.IDList{} // output node ID -> input node Adjacencies
|
||||
var (
|
||||
input = m.Renderer.Render(rpt)
|
||||
output = RenderableNodes{}
|
||||
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 {
|
||||
outRenderables := m.MapFunc(inRenderable)
|
||||
for _, outRenderable := range outRenderables {
|
||||
for _, outRenderable := range m.MapFunc(inRenderable, localNetworks) {
|
||||
existing, ok := output[outRenderable.ID]
|
||||
if ok {
|
||||
outRenderable.Merge(existing)
|
||||
outRenderable = outRenderable.Merge(existing)
|
||||
}
|
||||
|
||||
output[outRenderable.ID] = outRenderable
|
||||
@@ -72,9 +75,6 @@ func (m Map) render(rpt report.Report) (RenderableNodes, map[string]report.IDLis
|
||||
}
|
||||
|
||||
// Rewrite Adjacency for new node IDs.
|
||||
// NB we don't do pseudo nodes here; we assume the input graph
|
||||
// is properly-connected, and if the map func dropped a node,
|
||||
// we drop links to it.
|
||||
for outNodeID, inAdjacency := range adjacencies {
|
||||
outAdjacency := report.MakeIDList()
|
||||
for _, inAdjacent := range inAdjacency {
|
||||
@@ -123,128 +123,6 @@ func (m Map) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableID st
|
||||
return output
|
||||
}
|
||||
|
||||
// LeafMap is a Renderer which produces a set of RenderableNodes from a report.Topology
|
||||
// by using a map function and topology selector.
|
||||
type LeafMap struct {
|
||||
Selector report.TopologySelector
|
||||
Mapper LeafMapFunc
|
||||
}
|
||||
|
||||
// Render transforms a given Report into a set of RenderableNodes, which
|
||||
// the UI will render collectively as a graph. Note that a RenderableNode will
|
||||
// always be rendered with other nodes, and therefore contains limited detail.
|
||||
//
|
||||
// Nodes with the same mapped IDs will be merged.
|
||||
func (m LeafMap) Render(rpt report.Report) RenderableNodes {
|
||||
var (
|
||||
t = m.Selector(rpt)
|
||||
nodes = RenderableNodes{}
|
||||
source2mapped = map[string]report.IDList{} // input node ID -> output node IDs
|
||||
adjacencies = map[string]report.IDList{} // input node ID -> input node Adjacencies
|
||||
localNetworks = LocalNetworks(rpt)
|
||||
)
|
||||
|
||||
// Build a set of RenderableNodes for all non-pseudo probes, and an
|
||||
// addressID to nodeID lookup map. Multiple addressIDs can map to the same
|
||||
// RenderableNodes.
|
||||
for nodeID, metadata := range t.NodeMetadatas {
|
||||
for _, mapped := range m.Mapper(metadata, localNetworks) {
|
||||
// mapped.ID needs not be unique over all addressIDs. If not, we merge with
|
||||
// the existing data, on the assumption that the MapFunc returns the same
|
||||
// data.
|
||||
existing, ok := nodes[mapped.ID]
|
||||
if ok {
|
||||
mapped.Merge(existing)
|
||||
}
|
||||
|
||||
origins := mapped.Origins
|
||||
origins = origins.Add(nodeID)
|
||||
if hostNodeID, ok := metadata.Metadata[report.HostNodeID]; ok {
|
||||
origins = origins.Add(hostNodeID)
|
||||
}
|
||||
mapped.Origins = origins
|
||||
nodes[mapped.ID] = mapped
|
||||
source2mapped[nodeID] = source2mapped[nodeID].Add(mapped.ID)
|
||||
adjacencies[nodeID] = metadata.Adjacency
|
||||
}
|
||||
}
|
||||
|
||||
// We propagate edge metadata to nodes on both ends of the edges.
|
||||
// TODO we should 'reverse' one end of the edge meta data - ingress -> egress etc.
|
||||
for srcNodeID, nmd := range t.NodeMetadatas {
|
||||
for _, srcRenderableID := range source2mapped[srcNodeID] {
|
||||
srcRenderableNode := nodes[srcRenderableID]
|
||||
|
||||
for dstNodeID, emd := range nmd.Edges {
|
||||
for _, dstRenderableID := range source2mapped[dstNodeID] {
|
||||
dstRenderableNode := nodes[dstRenderableID]
|
||||
|
||||
srcRenderableNode.EdgeMetadata = srcRenderableNode.EdgeMetadata.Merge(emd)
|
||||
dstRenderableNode.EdgeMetadata = dstRenderableNode.EdgeMetadata.Merge(emd)
|
||||
|
||||
nodes[dstRenderableID] = dstRenderableNode
|
||||
}
|
||||
}
|
||||
|
||||
nodes[srcRenderableID] = srcRenderableNode
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the graph and make connections.
|
||||
for srcNodeID, dstNodeIDs := range adjacencies {
|
||||
for _, srcRenderableID := range source2mapped[srcNodeID] {
|
||||
srcRenderableNode := nodes[srcRenderableID]
|
||||
|
||||
for _, dstNodeID := range dstNodeIDs {
|
||||
for _, dstRenderableID := range source2mapped[dstNodeID] {
|
||||
srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID)
|
||||
}
|
||||
}
|
||||
|
||||
nodes[srcRenderableID] = srcRenderableNode
|
||||
}
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
func ids(nodes RenderableNodes) report.IDList {
|
||||
result := report.MakeIDList()
|
||||
for id := range nodes {
|
||||
result = result.Add(id)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// EdgeMetadata gives the metadata of an edge from the perspective of the
|
||||
// srcRenderableID. Since an edgeID can have multiple edges on the address
|
||||
// level, it uses the supplied mapping function to translate address IDs to
|
||||
// renderable node (mapped) IDs.
|
||||
func (m LeafMap) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableID string) report.EdgeMetadata {
|
||||
var (
|
||||
t = m.Selector(rpt)
|
||||
localNetworks = LocalNetworks(rpt)
|
||||
metadata = report.EdgeMetadata{}
|
||||
)
|
||||
for src, nmd := range t.NodeMetadatas {
|
||||
for dst, edgeMeta := range nmd.Edges {
|
||||
srcs, dsts := report.MakeIDList(src), report.MakeIDList(dst)
|
||||
if src != report.TheInternet {
|
||||
mapped := m.Mapper(t.NodeMetadatas[src], localNetworks)
|
||||
srcs = ids(mapped)
|
||||
}
|
||||
if dst != report.TheInternet {
|
||||
mapped := m.Mapper(t.NodeMetadatas[dst], localNetworks)
|
||||
dsts = ids(mapped)
|
||||
}
|
||||
if srcs.Contains(srcRenderableID) && dsts.Contains(dstRenderableID) {
|
||||
metadata = metadata.Flatten(edgeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// CustomRenderer allow for mapping functions that recived the entire topology
|
||||
// in one call - useful for functions that need to consider the entire graph
|
||||
type CustomRenderer struct {
|
||||
|
||||
@@ -24,13 +24,15 @@ func (m mockRenderer) EdgeMetadata(rpt report.Report, localID, remoteID string)
|
||||
|
||||
func TestReduceRender(t *testing.T) {
|
||||
renderer := render.Reduce([]render.Renderer{
|
||||
mockRenderer{RenderableNodes: render.RenderableNodes{"foo": {ID: "foo"}}},
|
||||
mockRenderer{RenderableNodes: render.RenderableNodes{"bar": {ID: "bar"}}},
|
||||
mockRenderer{RenderableNodes: render.RenderableNodes{"foo": render.NewRenderableNode("foo")}},
|
||||
mockRenderer{RenderableNodes: render.RenderableNodes{"bar": render.NewRenderableNode("bar")}},
|
||||
})
|
||||
|
||||
want := render.RenderableNodes{"foo": {ID: "foo"}, "bar": {ID: "bar"}}
|
||||
want := render.RenderableNodes{
|
||||
"foo": render.NewRenderableNode("foo"),
|
||||
"bar": render.NewRenderableNode("bar"),
|
||||
}
|
||||
have := renderer.Render(report.MakeReport())
|
||||
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %+v, have %+v", want, have)
|
||||
}
|
||||
@@ -44,7 +46,6 @@ func TestReduceEdge(t *testing.T) {
|
||||
|
||||
want := report.EdgeMetadata{EgressPacketCount: newu64(3)}
|
||||
have := renderer.EdgeMetadata(report.MakeReport(), "", "")
|
||||
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %+v, have %+v", want, have)
|
||||
}
|
||||
@@ -53,11 +54,11 @@ func TestReduceEdge(t *testing.T) {
|
||||
func TestMapRender1(t *testing.T) {
|
||||
// 1. Check when we return false, the node gets filtered out
|
||||
mapper := render.Map{
|
||||
MapFunc: func(nodes render.RenderableNode) render.RenderableNodes {
|
||||
MapFunc: func(nodes render.RenderableNode, _ report.Networks) render.RenderableNodes {
|
||||
return render.RenderableNodes{}
|
||||
},
|
||||
Renderer: mockRenderer{RenderableNodes: render.RenderableNodes{
|
||||
"foo": {ID: "foo"},
|
||||
"foo": render.NewRenderableNode("foo"),
|
||||
}},
|
||||
}
|
||||
want := render.RenderableNodes{}
|
||||
@@ -70,17 +71,19 @@ func TestMapRender1(t *testing.T) {
|
||||
func TestMapRender2(t *testing.T) {
|
||||
// 2. Check we can remap two nodes into one
|
||||
mapper := render.Map{
|
||||
MapFunc: func(nodes render.RenderableNode) render.RenderableNodes {
|
||||
return render.RenderableNodes{"bar": render.RenderableNode{ID: "bar"}}
|
||||
MapFunc: func(nodes render.RenderableNode, _ report.Networks) render.RenderableNodes {
|
||||
return render.RenderableNodes{
|
||||
"bar": render.NewRenderableNode("bar"),
|
||||
}
|
||||
},
|
||||
Renderer: mockRenderer{RenderableNodes: render.RenderableNodes{
|
||||
"foo": {ID: "foo"},
|
||||
"baz": {ID: "baz"},
|
||||
"foo": render.NewRenderableNode("foo"),
|
||||
"baz": render.NewRenderableNode("baz"),
|
||||
}},
|
||||
}
|
||||
want := expected.Sterilize(render.RenderableNodes{
|
||||
"bar": render.RenderableNode{ID: "bar"},
|
||||
})
|
||||
want := render.RenderableNodes{
|
||||
"bar": render.NewRenderableNode("bar"),
|
||||
}
|
||||
have := mapper.Render(report.MakeReport())
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -90,28 +93,28 @@ func TestMapRender2(t *testing.T) {
|
||||
func TestMapRender3(t *testing.T) {
|
||||
// 3. Check we can remap adjacencies
|
||||
mapper := render.Map{
|
||||
MapFunc: func(nodes render.RenderableNode) render.RenderableNodes {
|
||||
MapFunc: func(nodes render.RenderableNode, _ report.Networks) render.RenderableNodes {
|
||||
id := "_" + nodes.ID
|
||||
return render.RenderableNodes{id: render.RenderableNode{ID: id}}
|
||||
return render.RenderableNodes{id: render.NewRenderableNode(id)}
|
||||
},
|
||||
Renderer: mockRenderer{RenderableNodes: render.RenderableNodes{
|
||||
"foo": {ID: "foo", Adjacency: report.MakeIDList("baz")},
|
||||
"baz": {ID: "baz", Adjacency: report.MakeIDList("foo")},
|
||||
"foo": render.NewRenderableNode("foo").WithNodeMetadata(report.MakeNodeMetadata().WithAdjacent("baz")),
|
||||
"baz": render.NewRenderableNode("baz").WithNodeMetadata(report.MakeNodeMetadata().WithAdjacent("foo")),
|
||||
}},
|
||||
}
|
||||
want := render.RenderableNodes{
|
||||
"_foo": {ID: "_foo", Adjacency: report.MakeIDList("_baz")},
|
||||
"_baz": {ID: "_baz", Adjacency: report.MakeIDList("_foo")},
|
||||
"_foo": render.NewRenderableNode("_foo").WithNodeMetadata(report.MakeNodeMetadata().WithAdjacent("_baz")),
|
||||
"_baz": render.NewRenderableNode("_baz").WithNodeMetadata(report.MakeNodeMetadata().WithAdjacent("_foo")),
|
||||
}
|
||||
have := mapper.Render(report.MakeReport())
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %+v, have %+v", want, have)
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapEdge(t *testing.T) {
|
||||
selector := func(_ report.Report) report.Topology {
|
||||
return report.Topology{
|
||||
selector := render.TopologySelector(func(_ report.Report) render.RenderableNodes {
|
||||
return render.MakeRenderableNodes(report.Topology{
|
||||
NodeMetadatas: report.NodeMetadatas{
|
||||
"foo": report.MakeNodeMetadata().WithMetadata(map[string]string{
|
||||
"id": "foo",
|
||||
@@ -127,29 +130,38 @@ func TestMapEdge(t *testing.T) {
|
||||
EgressByteCount: newu64(4),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
identity := func(nmd report.NodeMetadata, _ report.Networks) render.RenderableNodes {
|
||||
return render.RenderableNodes{nmd.Metadata["id"]: render.NewRenderableNode(nmd.Metadata["id"], "", "", "", nmd)}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
mapper := render.Map{
|
||||
MapFunc: func(nodes render.RenderableNode) render.RenderableNodes {
|
||||
id := "_" + nodes.ID
|
||||
return render.RenderableNodes{id: render.RenderableNode{ID: id}}
|
||||
},
|
||||
Renderer: render.LeafMap{
|
||||
Selector: selector,
|
||||
Mapper: identity,
|
||||
MapFunc: func(node render.RenderableNode, _ report.Networks) render.RenderableNodes {
|
||||
id := "_" + node.ID
|
||||
return render.RenderableNodes{id: render.NewDerivedNode(id, node)}
|
||||
},
|
||||
Renderer: selector,
|
||||
}
|
||||
|
||||
have := mapper.Render(report.MakeReport())
|
||||
want := render.RenderableNodes{
|
||||
"_foo": {ID: "_foo", Adjacency: report.MakeIDList("_bar")},
|
||||
"_bar": {ID: "_bar", Adjacency: report.MakeIDList("_foo")},
|
||||
}
|
||||
have := expected.Sterilize(mapper.Render(report.MakeReport()))
|
||||
want := expected.Sterilize(render.RenderableNodes{
|
||||
"_foo": {
|
||||
ID: "_foo",
|
||||
Origins: report.MakeIDList("foo"),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent("_bar"),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(4),
|
||||
EgressByteCount: newu64(6),
|
||||
},
|
||||
},
|
||||
"_bar": {
|
||||
ID: "_bar",
|
||||
Origins: report.MakeIDList("bar"),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent("_foo"),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(4),
|
||||
EgressByteCount: newu64(6),
|
||||
},
|
||||
},
|
||||
})
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
@@ -165,13 +177,13 @@ func TestMapEdge(t *testing.T) {
|
||||
func TestFilterRender(t *testing.T) {
|
||||
renderer := render.FilterUnconnected(
|
||||
mockRenderer{RenderableNodes: render.RenderableNodes{
|
||||
"foo": {ID: "foo", Adjacency: report.MakeIDList("bar"), NodeMetadata: report.MakeNodeMetadata()},
|
||||
"bar": {ID: "bar", Adjacency: report.MakeIDList("foo"), NodeMetadata: report.MakeNodeMetadata()},
|
||||
"baz": {ID: "baz", Adjacency: report.MakeIDList(), NodeMetadata: report.MakeNodeMetadata()},
|
||||
"foo": {ID: "foo", NodeMetadata: report.MakeNodeMetadata().WithAdjacent("bar")},
|
||||
"bar": {ID: "bar", NodeMetadata: report.MakeNodeMetadata().WithAdjacent("foo")},
|
||||
"baz": {ID: "baz", NodeMetadata: report.MakeNodeMetadata()},
|
||||
}})
|
||||
want := render.RenderableNodes{
|
||||
"foo": {ID: "foo", Adjacency: report.MakeIDList("bar"), NodeMetadata: report.MakeNodeMetadata()},
|
||||
"bar": {ID: "bar", Adjacency: report.MakeIDList("foo"), NodeMetadata: report.MakeNodeMetadata()},
|
||||
"foo": {ID: "foo", NodeMetadata: report.MakeNodeMetadata().WithAdjacent("bar")},
|
||||
"bar": {ID: "bar", NodeMetadata: report.MakeNodeMetadata().WithAdjacent("foo")},
|
||||
}
|
||||
have := expected.Sterilize(renderer.Render(report.MakeReport()))
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
|
||||
@@ -13,67 +13,42 @@ type RenderableNode struct {
|
||||
LabelMinor string `json:"label_minor,omitempty"` // e.g. "hostname", human-readable, optional
|
||||
Rank string `json:"rank"` // to help the layout engine
|
||||
Pseudo bool `json:"pseudo,omitempty"` // sort-of a placeholder node, for rendering purposes
|
||||
Adjacency report.IDList `json:"adjacency,omitempty"` // Node IDs (in the same topology domain)
|
||||
Origins report.IDList `json:"origins,omitempty"` // Core node IDs that contributed information
|
||||
|
||||
report.EdgeMetadata `json:"metadata"` // Numeric sums
|
||||
report.NodeMetadata `json:"-"` // merged NodeMetadata of the nodes used to build this
|
||||
}
|
||||
|
||||
// RenderableNodes is a set of RenderableNodes
|
||||
type RenderableNodes map[string]RenderableNode
|
||||
|
||||
// Merge merges two sets of RenderableNodes
|
||||
func (rns RenderableNodes) Merge(other RenderableNodes) {
|
||||
for key, value := range other {
|
||||
if existing, ok := rns[key]; ok {
|
||||
existing.Merge(value)
|
||||
rns[key] = existing
|
||||
} else {
|
||||
rns[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges in another RenderableNode
|
||||
func (rn *RenderableNode) Merge(other RenderableNode) {
|
||||
if rn.LabelMajor == "" {
|
||||
rn.LabelMajor = other.LabelMajor
|
||||
}
|
||||
|
||||
if rn.LabelMinor == "" {
|
||||
rn.LabelMinor = other.LabelMinor
|
||||
}
|
||||
|
||||
if rn.Rank == "" {
|
||||
rn.Rank = other.Rank
|
||||
}
|
||||
|
||||
if rn.Pseudo != other.Pseudo {
|
||||
panic(rn.ID)
|
||||
}
|
||||
|
||||
rn.Adjacency = rn.Adjacency.Merge(other.Adjacency)
|
||||
rn.Origins = rn.Origins.Merge(other.Origins)
|
||||
|
||||
rn.EdgeMetadata = rn.EdgeMetadata.Merge(other.EdgeMetadata)
|
||||
rn.NodeMetadata = rn.NodeMetadata.Merge(other.NodeMetadata)
|
||||
report.NodeMetadata
|
||||
}
|
||||
|
||||
// NewRenderableNode makes a new RenderableNode
|
||||
func NewRenderableNode(id, major, minor, rank string, nmd report.NodeMetadata) RenderableNode {
|
||||
func NewRenderableNode(id string) RenderableNode {
|
||||
return RenderableNode{
|
||||
ID: id,
|
||||
LabelMajor: "",
|
||||
LabelMinor: "",
|
||||
Rank: "",
|
||||
Pseudo: false,
|
||||
Origins: report.MakeIDList(),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewRenderableNodeWith makes a new RenderableNode with some fields filled in
|
||||
func NewRenderableNodeWith(id, major, minor, rank string, rn RenderableNode) RenderableNode {
|
||||
return RenderableNode{
|
||||
ID: id,
|
||||
LabelMajor: major,
|
||||
LabelMinor: minor,
|
||||
Rank: rank,
|
||||
Pseudo: false,
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
NodeMetadata: nmd.Copy(),
|
||||
Origins: rn.Origins.Copy(),
|
||||
EdgeMetadata: rn.EdgeMetadata.Copy(),
|
||||
NodeMetadata: rn.NodeMetadata.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
func newDerivedNode(id string, node RenderableNode) RenderableNode {
|
||||
// NewDerivedNode create a renderable node based on node, but with a new ID
|
||||
func NewDerivedNode(id string, node RenderableNode) RenderableNode {
|
||||
return RenderableNode{
|
||||
ID: id,
|
||||
LabelMajor: "",
|
||||
@@ -82,19 +57,7 @@ func newDerivedNode(id string, node RenderableNode) RenderableNode {
|
||||
Pseudo: node.Pseudo,
|
||||
Origins: node.Origins.Copy(),
|
||||
EdgeMetadata: node.EdgeMetadata.Copy(),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
}
|
||||
}
|
||||
|
||||
func newPseudoNode(id, major, minor string) RenderableNode {
|
||||
return RenderableNode{
|
||||
ID: id,
|
||||
LabelMajor: major,
|
||||
LabelMinor: minor,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: node.NodeMetadata.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +70,73 @@ func newDerivedPseudoNode(id, major string, node RenderableNode) RenderableNode
|
||||
Pseudo: true,
|
||||
Origins: node.Origins.Copy(),
|
||||
EdgeMetadata: node.EdgeMetadata.Copy(),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
NodeMetadata: node.NodeMetadata.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithNodeMetadata creates a new RenderableNode based on rn, with n
|
||||
func (rn RenderableNode) WithNodeMetadata(n report.NodeMetadata) RenderableNode {
|
||||
result := rn.Copy()
|
||||
result.NodeMetadata = result.NodeMetadata.Merge(n)
|
||||
return result
|
||||
}
|
||||
|
||||
// Merge merges rn with other and returns a new RenderableNode
|
||||
func (rn RenderableNode) Merge(other RenderableNode) RenderableNode {
|
||||
result := rn.Copy()
|
||||
|
||||
if result.LabelMajor == "" {
|
||||
result.LabelMajor = other.LabelMajor
|
||||
}
|
||||
|
||||
if result.LabelMinor == "" {
|
||||
result.LabelMinor = other.LabelMinor
|
||||
}
|
||||
|
||||
if result.Rank == "" {
|
||||
result.Rank = other.Rank
|
||||
}
|
||||
|
||||
if result.Pseudo != other.Pseudo {
|
||||
panic(result.ID)
|
||||
}
|
||||
|
||||
result.Origins = rn.Origins.Merge(other.Origins)
|
||||
result.EdgeMetadata = rn.EdgeMetadata.Merge(other.EdgeMetadata)
|
||||
result.NodeMetadata = rn.NodeMetadata.Merge(other.NodeMetadata)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Copy makes a deep copy of rn
|
||||
func (rn RenderableNode) Copy() RenderableNode {
|
||||
return RenderableNode{
|
||||
ID: rn.ID,
|
||||
LabelMajor: rn.LabelMajor,
|
||||
LabelMinor: rn.LabelMinor,
|
||||
Rank: rn.Rank,
|
||||
Pseudo: rn.Pseudo,
|
||||
Origins: rn.Origins.Copy(),
|
||||
EdgeMetadata: rn.EdgeMetadata.Copy(),
|
||||
NodeMetadata: rn.NodeMetadata.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
// RenderableNodes is a set of RenderableNodes
|
||||
type RenderableNodes map[string]RenderableNode
|
||||
|
||||
// Merge merges two sets of RenderableNodes, returning a new set.
|
||||
func (rns RenderableNodes) Merge(other RenderableNodes) RenderableNodes {
|
||||
result := RenderableNodes{}
|
||||
for key, value := range rns {
|
||||
result[key] = value
|
||||
}
|
||||
for key, value := range other {
|
||||
existing, ok := result[key]
|
||||
if ok {
|
||||
value = value.Merge(existing)
|
||||
}
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -12,42 +12,42 @@ import (
|
||||
|
||||
func TestMergeRenderableNodes(t *testing.T) {
|
||||
nodes1 := render.RenderableNodes{
|
||||
"foo": render.RenderableNode{ID: "foo"},
|
||||
"bar": render.RenderableNode{ID: "bar"},
|
||||
"foo": render.NewRenderableNode("foo"),
|
||||
"bar": render.NewRenderableNode("bar"),
|
||||
}
|
||||
nodes2 := render.RenderableNodes{
|
||||
"bar": render.RenderableNode{ID: "bar"},
|
||||
"baz": render.RenderableNode{ID: "baz"},
|
||||
"bar": render.NewRenderableNode("bar"),
|
||||
"baz": render.NewRenderableNode("baz"),
|
||||
}
|
||||
want := expected.Sterilize(render.RenderableNodes{
|
||||
"foo": render.RenderableNode{ID: "foo"},
|
||||
"bar": render.RenderableNode{ID: "bar"},
|
||||
"baz": render.RenderableNode{ID: "baz"},
|
||||
"foo": render.NewRenderableNode("foo"),
|
||||
"bar": render.NewRenderableNode("bar"),
|
||||
"baz": render.NewRenderableNode("baz"),
|
||||
})
|
||||
nodes1.Merge(nodes2)
|
||||
if have := expected.Sterilize(nodes1); !reflect.DeepEqual(want, have) {
|
||||
have := nodes1.Merge(nodes2)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeRenderableNode(t *testing.T) {
|
||||
node1 := render.RenderableNode{
|
||||
ID: "foo",
|
||||
LabelMajor: "",
|
||||
LabelMinor: "minor",
|
||||
Rank: "",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList("a1"),
|
||||
Origins: report.MakeIDList("o1"),
|
||||
ID: "foo",
|
||||
LabelMajor: "",
|
||||
LabelMinor: "minor",
|
||||
Rank: "",
|
||||
Pseudo: false,
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent("a1"),
|
||||
Origins: report.MakeIDList("o1"),
|
||||
}
|
||||
node2 := render.RenderableNode{
|
||||
ID: "foo",
|
||||
LabelMajor: "major",
|
||||
LabelMinor: "",
|
||||
Rank: "rank",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList("a2"),
|
||||
Origins: report.MakeIDList("o2"),
|
||||
ID: "foo",
|
||||
LabelMajor: "major",
|
||||
LabelMinor: "",
|
||||
Rank: "rank",
|
||||
Pseudo: false,
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent("a2"),
|
||||
Origins: report.MakeIDList("o2"),
|
||||
}
|
||||
want := render.RenderableNode{
|
||||
ID: "foo",
|
||||
@@ -55,13 +55,12 @@ func TestMergeRenderableNode(t *testing.T) {
|
||||
LabelMinor: "minor",
|
||||
Rank: "rank",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList("a1", "a2"),
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacency(report.MakeIDList("a1", "a2")),
|
||||
Origins: report.MakeIDList("o1", "o2"),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
}
|
||||
node1.Merge(node2)
|
||||
if have := node1; !reflect.DeepEqual(want, have) {
|
||||
have := node1.Merge(node2)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
89
render/selectors.go
Normal file
89
render/selectors.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
// TopologySelector selects a single topology from a report.
|
||||
// NB it is also a Renderer!
|
||||
type TopologySelector func(r report.Report) RenderableNodes
|
||||
|
||||
// Render implements Renderer
|
||||
func (t TopologySelector) Render(r report.Report) RenderableNodes {
|
||||
return t(r)
|
||||
}
|
||||
|
||||
// EdgeMetadata implements Renderer
|
||||
func (t TopologySelector) EdgeMetadata(rpt report.Report, srcID, dstID string) report.EdgeMetadata {
|
||||
var (
|
||||
nodes = t(rpt)
|
||||
metadata = report.EdgeMetadata{}
|
||||
)
|
||||
for src, node := range nodes {
|
||||
for dst, edgeMeta := range node.Edges {
|
||||
if src == srcID && dst == dstID {
|
||||
return edgeMeta
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// MakeRenderableNodes converts a topology to a set of RenderableNodes
|
||||
func MakeRenderableNodes(t report.Topology) RenderableNodes {
|
||||
result := RenderableNodes{}
|
||||
for id, nmd := range t.NodeMetadatas {
|
||||
rn := NewRenderableNode(id).WithNodeMetadata(nmd)
|
||||
rn.Origins = report.MakeIDList(id)
|
||||
if hostNodeID, ok := nmd.Metadata[report.HostNodeID]; ok {
|
||||
rn.Origins = rn.Origins.Add(hostNodeID)
|
||||
}
|
||||
result[id] = rn
|
||||
}
|
||||
|
||||
// Push EdgeMetadata to both ends of the edges
|
||||
for srcID, srcNode := range result {
|
||||
for dstID, emd := range srcNode.Edges {
|
||||
srcNode.EdgeMetadata = srcNode.EdgeMetadata.Flatten(emd)
|
||||
|
||||
dstNode := result[dstID]
|
||||
dstNode.EdgeMetadata = dstNode.EdgeMetadata.Flatten(emd)
|
||||
result[dstID] = dstNode
|
||||
}
|
||||
|
||||
result[srcID] = srcNode
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var (
|
||||
// SelectEndpoint selects the endpoint topology.
|
||||
SelectEndpoint = TopologySelector(func(r report.Report) RenderableNodes {
|
||||
return MakeRenderableNodes(r.Endpoint)
|
||||
})
|
||||
|
||||
// SelectProcess selects the process topology.
|
||||
SelectProcess = TopologySelector(func(r report.Report) RenderableNodes {
|
||||
return MakeRenderableNodes(r.Process)
|
||||
})
|
||||
|
||||
// SelectContainer selects the container topology.
|
||||
SelectContainer = TopologySelector(func(r report.Report) RenderableNodes {
|
||||
return MakeRenderableNodes(r.Container)
|
||||
})
|
||||
|
||||
// SelectContainerImage selects the container image topology.
|
||||
SelectContainerImage = TopologySelector(func(r report.Report) RenderableNodes {
|
||||
return MakeRenderableNodes(r.ContainerImage)
|
||||
})
|
||||
|
||||
// SelectAddress selects the address topology.
|
||||
SelectAddress = TopologySelector(func(r report.Report) RenderableNodes {
|
||||
return MakeRenderableNodes(r.Address)
|
||||
})
|
||||
|
||||
// SelectHost selects the address topology.
|
||||
SelectHost = TopologySelector(func(r report.Report) RenderableNodes {
|
||||
return MakeRenderableNodes(r.Host)
|
||||
})
|
||||
)
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
)
|
||||
|
||||
// EndpointRenderer is a Renderer which produces a renderable endpoint graph.
|
||||
var EndpointRenderer = LeafMap{
|
||||
Selector: report.SelectEndpoint,
|
||||
Mapper: MapEndpointIdentity,
|
||||
var EndpointRenderer = Map{
|
||||
MapFunc: MapEndpointIdentity,
|
||||
Renderer: SelectEndpoint,
|
||||
}
|
||||
|
||||
// ProcessRenderer is a Renderer which produces a renderable process
|
||||
@@ -21,9 +21,9 @@ var ProcessRenderer = MakeReduce(
|
||||
MapFunc: MapEndpoint2Process,
|
||||
Renderer: EndpointRenderer,
|
||||
},
|
||||
LeafMap{
|
||||
Selector: report.SelectProcess,
|
||||
Mapper: MapProcessIdentity,
|
||||
Map{
|
||||
MapFunc: MapProcessIdentity,
|
||||
Renderer: SelectProcess,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -35,9 +35,9 @@ type ProcessWithContainerNameRenderer struct{}
|
||||
// container name, if found.
|
||||
func (r ProcessWithContainerNameRenderer) Render(rpt report.Report) RenderableNodes {
|
||||
processes := ProcessRenderer.Render(rpt)
|
||||
containers := LeafMap{
|
||||
Selector: report.SelectContainer,
|
||||
Mapper: MapContainerIdentity,
|
||||
containers := Map{
|
||||
MapFunc: MapContainerIdentity,
|
||||
Renderer: SelectContainer,
|
||||
}.Render(rpt)
|
||||
|
||||
for id, p := range processes {
|
||||
@@ -97,9 +97,9 @@ var ContainerRenderer = MakeReduce(
|
||||
},
|
||||
},
|
||||
|
||||
LeafMap{
|
||||
Selector: report.SelectContainer,
|
||||
Mapper: MapContainerIdentity,
|
||||
Map{
|
||||
MapFunc: MapContainerIdentity,
|
||||
Renderer: SelectContainer,
|
||||
},
|
||||
|
||||
// This mapper brings in short lived connections by joining with container IPs.
|
||||
@@ -110,13 +110,13 @@ var ContainerRenderer = MakeReduce(
|
||||
MapFunc: MapIP2Container,
|
||||
Renderer: FilterUnconnected(
|
||||
MakeReduce(
|
||||
LeafMap{
|
||||
Selector: report.SelectContainer,
|
||||
Mapper: MapContainer2IP,
|
||||
Map{
|
||||
MapFunc: MapContainer2IP,
|
||||
Renderer: SelectContainer,
|
||||
},
|
||||
LeafMap{
|
||||
Selector: report.SelectEndpoint,
|
||||
Mapper: MapEndpoint2IP,
|
||||
Map{
|
||||
MapFunc: MapEndpoint2IP,
|
||||
Renderer: SelectEndpoint,
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -134,9 +134,9 @@ var ContainerImageRenderer = Map{
|
||||
MapFunc: MapContainer2ContainerImage,
|
||||
Renderer: ContainerRenderer,
|
||||
},
|
||||
LeafMap{
|
||||
Selector: report.SelectContainerImage,
|
||||
Mapper: MapContainerImageIdentity,
|
||||
Map{
|
||||
MapFunc: MapContainerImageIdentity,
|
||||
Renderer: SelectContainerImage,
|
||||
},
|
||||
),
|
||||
},
|
||||
@@ -144,9 +144,9 @@ var ContainerImageRenderer = Map{
|
||||
|
||||
// AddressRenderer is a Renderer which produces a renderable address
|
||||
// graph from the address topology.
|
||||
var AddressRenderer = LeafMap{
|
||||
Selector: report.SelectAddress,
|
||||
Mapper: MapAddressIdentity,
|
||||
var AddressRenderer = Map{
|
||||
MapFunc: MapAddressIdentity,
|
||||
Renderer: SelectAddress,
|
||||
}
|
||||
|
||||
// HostRenderer is a Renderer which produces a renderable host
|
||||
@@ -156,8 +156,8 @@ var HostRenderer = MakeReduce(
|
||||
MapFunc: MapAddress2Host,
|
||||
Renderer: AddressRenderer,
|
||||
},
|
||||
LeafMap{
|
||||
Selector: report.SelectHost,
|
||||
Mapper: MapHostIdentity,
|
||||
Map{
|
||||
MapFunc: MapHostIdentity,
|
||||
Renderer: SelectHost,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
@@ -18,13 +19,11 @@ func (r ByID) Less(i, j int) bool { return r[i].ID < r[j].ID }
|
||||
|
||||
func TestTopoDiff(t *testing.T) {
|
||||
nodea := render.RenderableNode{
|
||||
ID: "nodea",
|
||||
LabelMajor: "Node A",
|
||||
LabelMinor: "'ts an a",
|
||||
Pseudo: false,
|
||||
Adjacency: []string{
|
||||
"nodeb",
|
||||
},
|
||||
ID: "nodea",
|
||||
LabelMajor: "Node A",
|
||||
LabelMinor: "'ts an a",
|
||||
Pseudo: false,
|
||||
NodeMetadata: report.MakeNodeMetadata().WithAdjacent("nodeb"),
|
||||
}
|
||||
nodeap := nodea
|
||||
nodeap.Adjacency = []string{
|
||||
|
||||
@@ -164,36 +164,3 @@ const (
|
||||
// the node was originally detected.
|
||||
HostNodeID = "host_node_id"
|
||||
)
|
||||
|
||||
// TopologySelector selects a single topology from a report.
|
||||
type TopologySelector func(r Report) Topology
|
||||
|
||||
// SelectEndpoint selects the endpoint topology.
|
||||
func SelectEndpoint(r Report) Topology {
|
||||
return r.Endpoint
|
||||
}
|
||||
|
||||
// SelectProcess selects the process topology.
|
||||
func SelectProcess(r Report) Topology {
|
||||
return r.Process
|
||||
}
|
||||
|
||||
// SelectContainer selects the container topology.
|
||||
func SelectContainer(r Report) Topology {
|
||||
return r.Container
|
||||
}
|
||||
|
||||
// SelectContainerImage selects the container image topology.
|
||||
func SelectContainerImage(r Report) Topology {
|
||||
return r.ContainerImage
|
||||
}
|
||||
|
||||
// SelectAddress selects the address topology.
|
||||
func SelectAddress(r Report) Topology {
|
||||
return r.Address
|
||||
}
|
||||
|
||||
// SelectHost selects the address topology.
|
||||
func SelectHost(r Report) Topology {
|
||||
return r.Host
|
||||
}
|
||||
|
||||
@@ -74,10 +74,10 @@ func (n NodeMetadatas) Merge(other NodeMetadatas) NodeMetadatas {
|
||||
// NodeMetadata describes a superset of the metadata that probes can collect
|
||||
// about a given node in a given topology.
|
||||
type NodeMetadata struct {
|
||||
Metadata
|
||||
Counters
|
||||
Adjacency IDList
|
||||
Edges EdgeMetadatas
|
||||
Metadata `json:"-"`
|
||||
Counters `json:"-"`
|
||||
Adjacency IDList `json:"adjacency"`
|
||||
Edges EdgeMetadatas `json:"-"`
|
||||
}
|
||||
|
||||
// MakeNodeMetadata creates a new NodeMetadata with no initial metadata.
|
||||
@@ -148,7 +148,7 @@ func (n NodeMetadata) Merge(other NodeMetadata) NodeMetadata {
|
||||
cp.Metadata = cp.Metadata.Merge(other.Metadata)
|
||||
cp.Counters = cp.Counters.Merge(other.Counters)
|
||||
cp.Adjacency = cp.Adjacency.Merge(other.Adjacency)
|
||||
cp.Edges = cp.Edges.Merge(n.Edges)
|
||||
cp.Edges = cp.Edges.Merge(other.Edges)
|
||||
return cp
|
||||
}
|
||||
|
||||
@@ -220,6 +220,16 @@ func (e EdgeMetadatas) Merge(other EdgeMetadatas) EdgeMetadatas {
|
||||
return cp
|
||||
}
|
||||
|
||||
// Flatten flattens all the EdgeMetadatas in this set and returns the result.
|
||||
// The original is not modified.
|
||||
func (e EdgeMetadatas) Flatten() EdgeMetadata {
|
||||
result := EdgeMetadata{}
|
||||
for _, v := range e {
|
||||
result = result.Flatten(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// EdgeMetadata describes a superset of the metadata that probes can possibly
|
||||
// collect about a directed edge between two nodes in any topology.
|
||||
type EdgeMetadata struct {
|
||||
|
||||
Reference in New Issue
Block a user