Merge pull request #355 from weaveworks/directionality

Make edge direction flow from client->server.
This commit is contained in:
Tom Wilkie
2015-08-19 14:21:13 +01:00
10 changed files with 264 additions and 145 deletions

View File

@@ -136,7 +136,7 @@ func TestAPITopologyHosts(t *testing.T) {
// Let's not unit-test the specific content of the detail tables
}
{
body := getRawJSON(t, ts, fmt.Sprintf("/api/topology/hosts/%s/%s", expected.ServerHostRenderedID, expected.ClientHostRenderedID))
body := getRawJSON(t, ts, fmt.Sprintf("/api/topology/hosts/%s/%s", expected.ClientHostRenderedID, expected.ServerHostRenderedID))
var edge APIEdge
if err := json.Unmarshal(body, &edge); err != nil {
t.Fatalf("JSON parse error: %s", err)

View File

@@ -77,13 +77,24 @@ func (r *Reporter) Report() (report.Report, error) {
func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) {
var (
localIsClient = int(c.LocalPort) > int(c.RemotePort)
localAddressNodeID = report.MakeAddressNodeID(r.hostID, c.LocalAddress.String())
remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, c.RemoteAddress.String())
adjecencyID = report.MakeAdjacencyID(localAddressNodeID)
edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID)
adjacencyID = ""
edgeID = ""
)
rpt.Address.Adjacency[adjecencyID] = rpt.Address.Adjacency[adjecencyID].Add(remoteAddressNodeID)
if localIsClient {
adjacencyID = report.MakeAdjacencyID(localAddressNodeID)
rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(remoteAddressNodeID)
edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID)
} else {
adjacencyID = report.MakeAdjacencyID(remoteAddressNodeID)
rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(localAddressNodeID)
edgeID = report.MakeEdgeID(remoteAddressNodeID, localAddressNodeID)
}
if _, ok := rpt.Address.NodeMetadatas[localAddressNodeID]; !ok {
rpt.Address.NodeMetadatas[localAddressNodeID] = report.MakeNodeMetadataWith(map[string]string{
@@ -98,11 +109,21 @@ func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) {
var (
localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.LocalAddress.String(), strconv.Itoa(int(c.LocalPort)))
remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.RemoteAddress.String(), strconv.Itoa(int(c.RemotePort)))
adjecencyID = report.MakeAdjacencyID(localEndpointNodeID)
edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID)
adjacencyID = ""
edgeID = ""
)
rpt.Endpoint.Adjacency[adjecencyID] = rpt.Endpoint.Adjacency[adjecencyID].Add(remoteEndpointNodeID)
if localIsClient {
adjacencyID = report.MakeAdjacencyID(localEndpointNodeID)
rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(remoteEndpointNodeID)
edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID)
} else {
adjacencyID = report.MakeAdjacencyID(remoteEndpointNodeID)
rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(localEndpointNodeID)
edgeID = report.MakeEdgeID(remoteEndpointNodeID, localEndpointNodeID)
}
if _, ok := rpt.Endpoint.NodeMetadatas[localEndpointNodeID]; !ok {
// First hit establishes NodeMetadata for scoped local address + port

View File

@@ -85,14 +85,14 @@ func TestSpyNoProcesses(t *testing.T) {
var (
scopedLocal = report.MakeAddressNodeID(nodeID, fixLocalAddress.String())
scopedRemote = report.MakeAddressNodeID(nodeID, fixRemoteAddress.String())
localKey = report.MakeAdjacencyID(scopedLocal)
remoteKey = report.MakeAdjacencyID(scopedRemote)
)
if want, have := 1, len(r.Address.Adjacency[localKey]); want != have {
if want, have := 1, len(r.Address.Adjacency[remoteKey]); want != have {
t.Fatalf("want %d, have %d", want, have)
}
if want, have := scopedRemote, r.Address.Adjacency[localKey][0]; want != have {
if want, have := scopedLocal, r.Address.Adjacency[remoteKey][0]; want != have {
t.Fatalf("want %q, have %q", want, have)
}
@@ -116,14 +116,14 @@ func TestSpyWithProcesses(t *testing.T) {
var (
scopedLocal = report.MakeEndpointNodeID(nodeID, fixLocalAddress.String(), strconv.Itoa(int(fixLocalPort)))
scopedRemote = report.MakeEndpointNodeID(nodeID, fixRemoteAddress.String(), strconv.Itoa(int(fixRemotePort)))
localKey = report.MakeAdjacencyID(scopedLocal)
remoteKey = report.MakeAdjacencyID(scopedRemote)
)
if want, have := 1, len(r.Endpoint.Adjacency[localKey]); want != have {
if want, have := 1, len(r.Endpoint.Adjacency[remoteKey]); want != have {
t.Fatalf("want %d, have %d", want, have)
}
if want, have := scopedRemote, r.Endpoint.Adjacency[localKey][0]; want != have {
if want, have := scopedLocal, r.Endpoint.Adjacency[remoteKey][0]; want != have {
t.Fatalf("want %q, have %q", want, have)
}

View File

@@ -48,6 +48,23 @@ type Row struct {
ValueMinor string `json:"value_minor,omitempty"` // e.g. KB/s
}
type rows []Row
func (r rows) Len() int { return len(r) }
func (r rows) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r rows) Less(i, j int) bool {
switch {
case r[i].Key != r[j].Key:
return r[i].Key < r[j].Key
case r[i].ValueMajor != r[j].ValueMajor:
return r[i].ValueMajor < r[j].ValueMajor
default:
return r[i].ValueMinor < r[j].ValueMinor
}
}
type tables []Table
func (t tables) Len() int { return len(t) }
@@ -118,6 +135,7 @@ func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
}
}
if len(connections) > 0 {
sort.Sort(rows(connections))
tables = append(tables, connectionDetailsTable(connections))
}
@@ -166,15 +184,38 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row {
if !ok {
return rows
}
adjacencies := topology.Adjacency[report.MakeAdjacencyID(originID)]
sort.Strings(adjacencies)
for _, nodeID := range adjacencies {
if remote, ok := labeler(nodeID); ok {
rows = append(rows, Row{
Key: local,
ValueMajor: remote,
})
// Firstly, collection outgoing connections from this node.
originAdjID := report.MakeAdjacencyID(originID)
for _, serverNodeID := range topology.Adjacency[originAdjID] {
remote, ok := labeler(serverNodeID)
if !ok {
continue
}
rows = append(rows, Row{
Key: local,
ValueMajor: remote,
})
}
// Next, scan the topology for incoming connections to this node.
for clientAdjID, serverNodeIDs := range topology.Adjacency {
if clientAdjID == originAdjID {
continue
}
if !serverNodeIDs.Contains(originID) {
continue
}
clientNodeID, ok := report.ParseAdjacencyID(clientAdjID)
if !ok {
continue
}
remote, ok := labeler(clientNodeID)
if !ok {
continue
}
rows = append(rows, Row{
Key: remote,
ValueMajor: local,
})
}
return rows
}
@@ -183,7 +224,7 @@ func connectionDetailsTable(connectionRows []Row) Table {
return Table{
Title: "Connection Details",
Numeric: false,
Rows: append([]Row{{Key: "Local", ValueMajor: "Remote"}}, connectionRows...),
Rows: append([]Row{{Key: "Client", ValueMajor: "Server"}}, connectionRows...),
Rank: endpointRank,
}
}

View File

@@ -94,8 +94,8 @@ func TestMakeDetailedHostNode(t *testing.T) {
Rank: 0,
Rows: []render.Row{
{
Key: "Local",
ValueMajor: "Remote",
Key: "Client",
ValueMajor: "Server",
ValueMinor: "",
},
{
@@ -126,8 +126,8 @@ func TestMakeDetailedContainerNode(t *testing.T) {
Numeric: true,
Rank: 100,
Rows: []render.Row{
{"Egress packet rate", "75", "packets/sec"},
{"Egress byte rate", "750", "Bps"},
{"Egress packet rate", "105", "packets/sec"},
{"Egress byte rate", "1.0", "KBps"},
},
},
{
@@ -163,35 +163,35 @@ func TestMakeDetailedContainerNode(t *testing.T) {
Title: "Connection Details",
Numeric: false,
Rows: []render.Row{
{"Local", "Remote", ""},
{"Client", "Server", ""},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
},
},

View File

@@ -13,28 +13,45 @@ var (
uncontainedServerID = render.MakePseudoNodeID(render.UncontainedID, test.ServerHostName)
unknownPseudoNode1ID = render.MakePseudoNodeID("10.10.10.10", test.ServerIP, "80")
unknownPseudoNode2ID = render.MakePseudoNodeID("10.10.10.11", test.ServerIP, "80")
unknownPseudoNode1 = render.RenderableNode{
ID: unknownPseudoNode1ID,
LabelMajor: "10.10.10.10",
Pseudo: true,
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
unknownPseudoNode1 = func(adjacency report.IDList) render.RenderableNode {
return render.RenderableNode{
ID: unknownPseudoNode1ID,
LabelMajor: "10.10.10.10",
Pseudo: true,
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{
EgressPacketCount: newu64(70),
EgressByteCount: newu64(700),
},
Adjacency: adjacency,
}
}
unknownPseudoNode2 = render.RenderableNode{
ID: unknownPseudoNode2ID,
LabelMajor: "10.10.10.11",
Pseudo: true,
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
unknownPseudoNode2 = func(adjacency report.IDList) render.RenderableNode {
return render.RenderableNode{
ID: unknownPseudoNode2ID,
LabelMajor: "10.10.10.11",
Pseudo: true,
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{
EgressPacketCount: newu64(50),
EgressByteCount: newu64(500),
},
Adjacency: adjacency,
}
}
theInternetNode = render.RenderableNode{
ID: render.TheInternetID,
LabelMajor: render.TheInternetMajor,
Pseudo: true,
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
theInternetNode = func(adjacency report.IDList) render.RenderableNode {
return render.RenderableNode{
ID: render.TheInternetID,
LabelMajor: render.TheInternetMajor,
Pseudo: true,
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{
EgressPacketCount: newu64(60),
EgressByteCount: newu64(600),
},
Adjacency: adjacency,
}
}
ClientProcess1ID = render.MakeProcessID(test.ClientHostID, test.Client1PID)
ClientProcess2ID = render.MakeProcessID(test.ClientHostID, test.Client2PID)
ServerProcessID = render.MakeProcessID(test.ServerHostID, test.ServerPID)
@@ -83,13 +100,7 @@ var (
LabelMinor: fmt.Sprintf("%s (%s)", test.ServerHostID, test.ServerPID),
Rank: test.ServerComm,
Pseudo: false,
Adjacency: report.MakeIDList(
ClientProcess1ID,
ClientProcess2ID,
unknownPseudoNode1ID,
unknownPseudoNode2ID,
render.TheInternetID,
),
Adjacency: report.MakeIDList(),
Origins: report.MakeIDList(
test.Server80NodeID,
test.ServerProcessNodeID,
@@ -97,8 +108,8 @@ var (
),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{
EgressPacketCount: newu64(150),
EgressByteCount: newu64(1500),
EgressPacketCount: newu64(210),
EgressByteCount: newu64(2100),
},
},
nonContainerProcessID: {
@@ -115,9 +126,9 @@ var (
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
},
unknownPseudoNode1ID: unknownPseudoNode1,
unknownPseudoNode2ID: unknownPseudoNode2,
render.TheInternetID: theInternetNode,
unknownPseudoNode1ID: unknownPseudoNode1(report.MakeIDList(ServerProcessID)),
unknownPseudoNode2ID: unknownPseudoNode2(report.MakeIDList(ServerProcessID)),
render.TheInternetID: theInternetNode(report.MakeIDList(ServerProcessID)),
}
RenderedProcessNames = render.RenderableNodes{
@@ -147,12 +158,7 @@ var (
LabelMinor: "",
Rank: "apache",
Pseudo: false,
Adjacency: report.MakeIDList(
"curl",
unknownPseudoNode1ID,
unknownPseudoNode2ID,
render.TheInternetID,
),
Adjacency: report.MakeIDList(),
Origins: report.MakeIDList(
test.Server80NodeID,
test.ServerProcessNodeID,
@@ -160,8 +166,8 @@ var (
),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{
EgressPacketCount: newu64(150),
EgressByteCount: newu64(1500),
EgressPacketCount: newu64(210),
EgressByteCount: newu64(2100),
},
},
"bash": {
@@ -177,9 +183,9 @@ var (
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
},
unknownPseudoNode1ID: unknownPseudoNode1,
unknownPseudoNode2ID: unknownPseudoNode2,
render.TheInternetID: theInternetNode,
unknownPseudoNode1ID: unknownPseudoNode1(report.MakeIDList("apache")),
unknownPseudoNode2ID: unknownPseudoNode2(report.MakeIDList("apache")),
render.TheInternetID: theInternetNode(report.MakeIDList("apache")),
}
RenderedContainers = render.RenderableNodes{
@@ -210,7 +216,7 @@ var (
LabelMinor: test.ServerHostName,
Rank: test.ServerContainerImageID,
Pseudo: false,
Adjacency: report.MakeIDList(test.ClientContainerID, render.TheInternetID),
Adjacency: report.MakeIDList(),
Origins: report.MakeIDList(
test.ServerContainerNodeID,
test.Server80NodeID,
@@ -219,8 +225,8 @@ var (
),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{
EgressPacketCount: newu64(150),
EgressByteCount: newu64(1500),
EgressPacketCount: newu64(210),
EgressByteCount: newu64(2100),
},
},
uncontainedServerID: {
@@ -236,7 +242,7 @@ var (
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
},
render.TheInternetID: theInternetNode,
render.TheInternetID: theInternetNode(report.MakeIDList(test.ServerContainerID)),
}
RenderedContainerImages = render.RenderableNodes{
@@ -268,7 +274,7 @@ var (
LabelMinor: "",
Rank: test.ServerContainerImageName,
Pseudo: false,
Adjacency: report.MakeIDList(test.ClientContainerImageName, render.TheInternetID),
Adjacency: report.MakeIDList(),
Origins: report.MakeIDList(
test.ServerContainerImageNodeID,
test.ServerContainerNodeID,
@@ -277,8 +283,8 @@ var (
test.ServerHostNodeID),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{
EgressPacketCount: newu64(150),
EgressByteCount: newu64(1500),
EgressPacketCount: newu64(210),
EgressByteCount: newu64(2100),
},
},
uncontainedServerID: {
@@ -294,7 +300,7 @@ var (
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
},
render.TheInternetID: theInternetNode,
render.TheInternetID: theInternetNode(report.MakeIDList(test.ServerContainerImageName)),
}
ServerHostRenderedID = render.MakeHostID(test.ServerHostID)
@@ -309,7 +315,7 @@ var (
LabelMinor: "hostname.com", // after first .
Rank: "hostname.com",
Pseudo: false,
Adjacency: report.MakeIDList(ClientHostRenderedID, render.TheInternetID, pseudoHostID1, pseudoHostID2),
Adjacency: report.MakeIDList(),
Origins: report.MakeIDList(
test.ServerHostNodeID,
test.ServerAddressNodeID,
@@ -339,6 +345,7 @@ var (
ID: pseudoHostID1,
LabelMajor: "10.10.10.10",
Pseudo: true,
Adjacency: report.MakeIDList(ServerHostRenderedID),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
},
@@ -346,10 +353,18 @@ var (
ID: pseudoHostID2,
LabelMajor: "10.10.10.11",
Pseudo: true,
Adjacency: report.MakeIDList(ServerHostRenderedID),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
},
render.TheInternetID: {
ID: render.TheInternetID,
LabelMajor: render.TheInternetMajor,
Pseudo: true,
Adjacency: report.MakeIDList(ServerHostRenderedID),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
},
render.TheInternetID: theInternetNode,
}
)

View File

@@ -33,11 +33,12 @@ const (
// rendered topology.
type LeafMapFunc func(report.NodeMetadata) (RenderableNode, bool)
// PseudoFunc creates RenderableNode representing pseudo nodes given the dstNodeID.
// The srcNode renderable node is essentially from MapFunc, representing one of
// the rendered nodes this pseudo node refers to. srcNodeID and dstNodeID are
// node IDs prior to mapping.
type PseudoFunc func(srcNodeID string, srcNode RenderableNode, dstNodeID string, local report.Networks) (RenderableNode, bool)
// PseudoFunc creates RenderableNode representing pseudo nodes given the
// srcNodeID. dstNodeID is the node id of one of the nodes this node has an
// edge to. srcNodeID and dstNodeID are node IDs prior to mapping. srcIsClient
// indicates the direction of the edge to dstNodeID - true indicates srcNodeID
// is the client, false indicates dstNodeID is the server.
type PseudoFunc func(srcNodeID, dstNodeID string, srcIsClient bool, local report.Networks) (RenderableNode, bool)
// MapFunc is anything which can take an arbitrary RenderableNode and
// return another RenderableNode.
@@ -323,29 +324,36 @@ func MapAddress2Host(n RenderableNode) (RenderableNode, bool) {
// the report's local networks. Otherwise, the returned function will
// produce a single pseudo node per (dst address, src address, src port).
func GenericPseudoNode(addresser func(id string) net.IP) PseudoFunc {
return func(src string, srcMapped RenderableNode, dst string, local report.Networks) (RenderableNode, bool) {
// Use the addresser to extract the destination IP
dstNodeAddr := addresser(dst)
return func(srcNodeID, dstNodeID string, srcIsClient bool, local report.Networks) (RenderableNode, bool) {
// Use the addresser to extract the IP of the missing node
srcNodeAddr := addresser(srcNodeID)
// If the dstNodeAddr is not in a network local to this report, we emit an
// internet node
if !local.Contains(dstNodeAddr) {
if !local.Contains(srcNodeAddr) {
return newPseudoNode(TheInternetID, TheInternetMajor, ""), true
}
// Otherwise, the rule for non-internet psuedo nodes; emit 1 new node for each
// dstNodeAddr, srcNodeAddr, srcNodePort.
srcNodeAddr, srcNodePort := trySplitAddr(src)
if srcIsClient {
// If the client node is missing, generate a single pseudo node for every (client ip, server ip, server port)
serverIP, serverPort := trySplitAddr(dstNodeID)
outputID := MakePseudoNodeID(srcNodeAddr.String(), serverIP, serverPort)
major := srcNodeAddr.String()
return newPseudoNode(outputID, major, ""), true
}
outputID := MakePseudoNodeID(dstNodeAddr.String(), srcNodeAddr, srcNodePort)
major := dstNodeAddr.String()
return newPseudoNode(outputID, major, ""), true
// Otherwise (the server node is missing), generate a pseudo node for every (server ip, server port)
serverIP, serverPort := trySplitAddr(srcNodeID)
outputID := MakePseudoNodeID(serverIP, serverPort)
if serverPort != "" {
return newPseudoNode(outputID, serverIP+":"+serverPort, ""), true
}
return newPseudoNode(outputID, serverIP, ""), true
}
}
// PanicPseudoNode just panics; it is for Topologies without edges
func PanicPseudoNode(src string, srcMapped RenderableNode, dst string, local report.Networks) (RenderableNode, bool) {
panic(dst)
func PanicPseudoNode(src, dst string, isClient bool, local report.Networks) (RenderableNode, bool) {
panic(src)
}
// trySplitAddr is basically ParseArbitraryNodeID, since its callsites

View File

@@ -177,37 +177,69 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes {
source2mapped[nodeID] = mapped.ID
}
mkPseudoNode := func(srcNodeID, dstNodeID string, srcIsClient bool) (string, bool) {
pseudoNode, ok := m.Pseudo(srcNodeID, dstNodeID, srcIsClient, localNetworks)
if !ok {
return "", false
}
// TODO(tomwilkie): we should propagate origin nodes for pseudo nodes.
// Not worth doing until they are selectable in the UI
// pseudoNode.Origins = pseudoNode.Origins.Add(srcID)
existing, ok := nodes[pseudoNode.ID]
if ok {
pseudoNode.Merge(existing)
}
nodes[pseudoNode.ID] = pseudoNode
source2mapped[pseudoNode.ID] = srcNodeID
return pseudoNode.ID, true
}
// Walk the graph and make connections.
for src, dsts := range t.Adjacency {
var (
srcNodeID, ok = report.ParseAdjacencyID(src)
srcRenderableID = source2mapped[srcNodeID] // must exist
srcRenderableNode = nodes[srcRenderableID] // must exist
)
srcNodeID, ok := report.ParseAdjacencyID(src)
if !ok {
log.Printf("bad adjacency ID %q", src)
continue
}
srcRenderableID, ok := source2mapped[srcNodeID]
if !ok {
// One of the entries in dsts must be a non-pseudo node
var existingDstNodeID string
for _, dstNodeID := range dsts {
if _, ok := source2mapped[dstNodeID]; ok {
existingDstNodeID = dstNodeID
break
}
}
srcRenderableID, ok = mkPseudoNode(srcNodeID, existingDstNodeID, true)
if !ok {
continue
}
}
srcRenderableNode := nodes[srcRenderableID]
for _, dstNodeID := range dsts {
dstRenderableID, ok := source2mapped[dstNodeID]
if !ok {
pseudoNode, ok := m.Pseudo(srcNodeID, srcRenderableNode, dstNodeID, localNetworks)
dstRenderableID, ok = mkPseudoNode(dstNodeID, srcNodeID, false)
if !ok {
continue
}
dstRenderableID = pseudoNode.ID
nodes[dstRenderableID] = pseudoNode
source2mapped[dstNodeID] = dstRenderableID
}
dstRenderableNode := nodes[dstRenderableID]
srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID)
srcRenderableNode.Origins = srcRenderableNode.Origins.Add(srcNodeID)
edgeID := report.MakeEdgeID(srcNodeID, dstNodeID)
if md, ok := t.EdgeMetadatas[edgeID]; ok {
srcRenderableNode.EdgeMetadata.Merge(md)
}
// 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.
if md, ok := t.EdgeMetadatas[report.MakeEdgeID(srcNodeID, dstNodeID)]; ok {
srcRenderableNode.EdgeMetadata.Merge(md)
dstRenderableNode.EdgeMetadata.Merge(md)
nodes[dstRenderableID] = dstRenderableNode
}
}
nodes[srcRenderableID] = srcRenderableNode

View File

@@ -85,8 +85,11 @@ func (t Topology) Validate() error {
errs = append(errs, fmt.Sprintf("invalid edge ID %q", edgeID))
continue
}
// For each edge, at least one of the ends must exist in nodemetadata
if _, ok := t.NodeMetadatas[srcNodeID]; !ok {
errs = append(errs, fmt.Sprintf("node metadata missing for source node ID %q (from edge %q)", srcNodeID, edgeID))
if _, ok := t.NodeMetadatas[dstNodeID]; !ok {
errs = append(errs, fmt.Sprintf("node metadatas missing for edge %q", edgeID))
}
}
dstNodeIDs, ok := t.Adjacency[MakeAdjacencyID(srcNodeID)]
if !ok {
@@ -99,14 +102,19 @@ func (t Topology) Validate() error {
}
// Check all adjancency keys has entries in NodeMetadata.
for adjacencyID := range t.Adjacency {
nodeID, ok := ParseAdjacencyID(adjacencyID)
for adjacencyID, dsts := range t.Adjacency {
srcNodeID, ok := ParseAdjacencyID(adjacencyID)
if !ok {
errs = append(errs, fmt.Sprintf("invalid adjacency ID %q", adjacencyID))
continue
}
if _, ok := t.NodeMetadatas[nodeID]; !ok {
errs = append(errs, fmt.Sprintf("node metadata missing for source node %q (from adjacency %q)", nodeID, adjacencyID))
for _, dstNodeID := range dsts {
// For each edge, at least one of the ends must exist in nodemetadata
if _, ok := t.NodeMetadatas[srcNodeID]; !ok {
if _, ok := t.NodeMetadatas[dstNodeID]; !ok {
errs = append(errs, fmt.Sprintf("node metadata missing from adjacency %q -> %q", srcNodeID, dstNodeID))
}
}
}
}

View File

@@ -79,11 +79,12 @@ var (
Report = report.Report{
Endpoint: report.Topology{
Adjacency: report.Adjacency{
report.MakeAdjacencyID(Client54001NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(Client54002NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(Server80NodeID): report.MakeIDList(
Client54001NodeID, Client54002NodeID, UnknownClient1NodeID, UnknownClient2NodeID,
UnknownClient3NodeID, RandomClientNodeID),
report.MakeAdjacencyID(Client54001NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(Client54002NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(UnknownClient1NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(UnknownClient2NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(UnknownClient3NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(RandomClientNodeID): report.MakeIDList(Server80NodeID),
},
NodeMetadatas: report.NodeMetadatas{
// NodeMetadata is arbitrary. We're free to put only precisely what we
@@ -117,27 +118,22 @@ var (
EgressPacketCount: newu64(20),
EgressByteCount: newu64(200),
},
report.MakeEdgeID(Server80NodeID, Client54001NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(10),
EgressByteCount: newu64(100),
},
report.MakeEdgeID(Server80NodeID, Client54002NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(20),
EgressByteCount: newu64(200),
},
report.MakeEdgeID(Server80NodeID, UnknownClient1NodeID): report.EdgeMetadata{
report.MakeEdgeID(UnknownClient1NodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(30),
EgressByteCount: newu64(300),
},
report.MakeEdgeID(Server80NodeID, UnknownClient2NodeID): report.EdgeMetadata{
report.MakeEdgeID(UnknownClient2NodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(40),
EgressByteCount: newu64(400),
},
report.MakeEdgeID(Server80NodeID, UnknownClient3NodeID): report.EdgeMetadata{
report.MakeEdgeID(UnknownClient3NodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(50),
EgressByteCount: newu64(500),
},
report.MakeEdgeID(RandomClientNodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(60),
EgressByteCount: newu64(600),
},
},
},
Process: report.Topology{
@@ -201,9 +197,10 @@ var (
},
Address: report.Topology{
Adjacency: report.Adjacency{
report.MakeAdjacencyID(ClientAddressNodeID): report.MakeIDList(ServerAddressNodeID),
report.MakeAdjacencyID(ServerAddressNodeID): report.MakeIDList(
ClientAddressNodeID, UnknownAddress1NodeID, UnknownAddress2NodeID, RandomAddressNodeID), // no backlinks to unknown/random
report.MakeAdjacencyID(ClientAddressNodeID): report.MakeIDList(ServerAddressNodeID),
report.MakeAdjacencyID(UnknownAddress1NodeID): report.MakeIDList(ServerAddressNodeID),
report.MakeAdjacencyID(UnknownAddress2NodeID): report.MakeIDList(ServerAddressNodeID),
report.MakeAdjacencyID(RandomAddressNodeID): report.MakeIDList(ServerAddressNodeID),
},
NodeMetadatas: report.NodeMetadatas{
ClientAddressNodeID: report.MakeNodeMetadataWith(map[string]string{
@@ -219,9 +216,6 @@ var (
report.MakeEdgeID(ClientAddressNodeID, ServerAddressNodeID): report.EdgeMetadata{
MaxConnCountTCP: newu64(3),
},
report.MakeEdgeID(ServerAddressNodeID, ClientAddressNodeID): report.EdgeMetadata{
MaxConnCountTCP: newu64(3),
},
},
},
Host: report.Topology{