Merge pull request #421 from weaveworks/357-edgemetadata

Move EdgeMetadata into the NodeMetadata struct.
This commit is contained in:
Tom Wilkie
2015-09-02 15:47:53 +01:00
15 changed files with 287 additions and 348 deletions

View File

@@ -88,23 +88,16 @@ func demoReport(nodeCount int) report.Report {
process.PID: "4000",
"name": c.srcProc,
"domain": "node-" + src,
}).WithAdjacent(dstPortID))
}).WithEdge(dstPortID, report.EdgeMetadata{
MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)),
}))
r.Endpoint = r.Endpoint.WithNode(dstPortID, report.MakeNodeMetadata().WithMetadata(map[string]string{
process.PID: "4000",
"name": c.dstProc,
"domain": "node-" + dst,
}).WithAdjacent(srcPortID))
var (
edgeKeyEgress = report.MakeEdgeID(srcPortID, dstPortID)
edgeKeyIngress = report.MakeEdgeID(dstPortID, srcPortID)
)
r.Endpoint.EdgeMetadatas[edgeKeyEgress] = report.EdgeMetadata{
}).WithEdge(srcPortID, report.EdgeMetadata{
MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)),
}
r.Endpoint.EdgeMetadatas[edgeKeyIngress] = report.EdgeMetadata{
MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)),
}
}))
// Address topology
r.Address = r.Address.WithNode(srcAddressID, report.MakeNodeMetadata().WithMetadata(map[string]string{

View File

@@ -68,23 +68,16 @@ func DemoReport(nodeCount int) report.Report {
"pid": "4000",
"name": c.srcProc,
"domain": "node-" + src,
}).WithAdjacent(dstPortID))
}).WithEdge(dstPortID, report.EdgeMetadata{
MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)),
}))
r.Endpoint = r.Endpoint.WithNode(dstPortID, report.MakeNodeMetadata().WithMetadata(map[string]string{
"pid": "4000",
"name": c.dstProc,
"domain": "node-" + dst,
}).WithAdjacent(srcPortID))
var (
edgeKeyEgress = report.MakeEdgeID(srcPortID, dstPortID)
edgeKeyIngress = report.MakeEdgeID(dstPortID, srcPortID)
)
r.Endpoint.EdgeMetadatas[edgeKeyEgress] = report.EdgeMetadata{
}).WithEdge(srcPortID, report.EdgeMetadata{
MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)),
}
r.Endpoint.EdgeMetadatas[edgeKeyIngress] = report.EdgeMetadata{
MaxConnCountTCP: newu64(uint64(rand.Intn(100) + 10)),
}
}))
// Address topology
r.Address = r.Address.WithNode(srcAddressID, report.MakeNodeMetadata().WithMetadata(map[string]string{

View File

@@ -50,7 +50,6 @@ var (
func TestReporter(t *testing.T) {
want := report.MakeReport()
want.Container = report.Topology{
EdgeMetadatas: report.EdgeMetadatas{},
NodeMetadatas: report.NodeMetadatas{
report.MakeContainerNodeID("", "ping"): report.MakeNodeMetadataWith(map[string]string{
docker.ContainerID: "ping",
@@ -60,7 +59,6 @@ func TestReporter(t *testing.T) {
},
}
want.ContainerImage = report.Topology{
EdgeMetadatas: report.EdgeMetadatas{},
NodeMetadatas: report.NodeMetadatas{
report.MakeContainerNodeID("", "baz"): report.MakeNodeMetadataWith(map[string]string{
docker.ImageID: "baz",

View File

@@ -135,9 +135,7 @@ func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr strin
var (
localAddressNodeID = report.MakeAddressNodeID(r.hostID, localAddr)
remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, remoteAddr)
edgeID = ""
localNode = report.MakeNodeMetadataWith(map[string]string{
localNode = report.MakeNodeMetadataWith(map[string]string{
"name": r.hostName,
Addr: localAddr,
report.HostNodeID: hostNodeID,
@@ -148,16 +146,18 @@ func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr strin
)
if localIsClient {
localNode.Adjacency = localNode.Adjacency.Add(remoteAddressNodeID)
edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID)
// New nodes are merged into the report so we don't need to do any counting here; the merge does it for us.
localNode = localNode.WithEdge(remoteAddressNodeID, report.EdgeMetadata{
MaxConnCountTCP: newu64(1),
})
} else {
remoteNode.Adjacency = localNode.Adjacency.Add(localAddressNodeID)
edgeID = report.MakeEdgeID(remoteAddressNodeID, localAddressNodeID)
remoteNode = localNode.WithEdge(localAddressNodeID, report.EdgeMetadata{
MaxConnCountTCP: newu64(1),
})
}
rpt.Address = rpt.Address.WithNode(localAddressNodeID, localNode)
rpt.Address = rpt.Address.WithNode(remoteAddressNodeID, remoteNode)
countTCPConnection(rpt.Address.EdgeMetadatas, edgeID)
}
// Update endpoint topology
@@ -165,7 +165,6 @@ func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr strin
var (
localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, localAddr, strconv.Itoa(int(localPort)))
remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, remoteAddr, strconv.Itoa(int(remotePort)))
edgeID = ""
localNode = report.MakeNodeMetadataWith(map[string]string{
Addr: localAddr,
@@ -179,11 +178,14 @@ func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr strin
)
if localIsClient {
localNode.Adjacency = localNode.Adjacency.Add(remoteEndpointNodeID)
edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID)
// New nodes are merged into the report so we don't need to do any counting here; the merge does it for us.
localNode = localNode.WithEdge(remoteEndpointNodeID, report.EdgeMetadata{
MaxConnCountTCP: newu64(1),
})
} else {
remoteNode.Adjacency = remoteNode.Adjacency.Add(localEndpointNodeID)
edgeID = report.MakeEdgeID(remoteEndpointNodeID, localEndpointNodeID)
remoteNode = remoteNode.WithEdge(localEndpointNodeID, report.EdgeMetadata{
MaxConnCountTCP: newu64(1),
})
}
if proc != nil && proc.PID > 0 {
@@ -192,15 +194,9 @@ func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr strin
rpt.Endpoint = rpt.Endpoint.WithNode(localEndpointNodeID, localNode)
rpt.Endpoint = rpt.Endpoint.WithNode(remoteEndpointNodeID, remoteNode)
countTCPConnection(rpt.Endpoint.EdgeMetadatas, edgeID)
}
}
func countTCPConnection(mds report.EdgeMetadatas, key string) {
md := mds[key]
if md.MaxConnCountTCP == nil {
md.MaxConnCountTCP = new(uint64)
}
*md.MaxConnCountTCP++
mds[key] = md
func newu64(i uint64) *uint64 {
return &i
}

View File

@@ -36,7 +36,6 @@ func TestWeaveTaggerOverlayTopology(t *testing.T) {
t.Fatal(err)
}
if want, have := (report.Topology{
EdgeMetadatas: report.EdgeMetadatas{},
NodeMetadatas: report.NodeMetadatas{
report.MakeOverlayNodeID(mockWeavePeerName): report.MakeNodeMetadataWith(map[string]string{
overlay.WeavePeerName: mockWeavePeerName,

View File

@@ -34,7 +34,6 @@ func TestReporter(t *testing.T) {
reporter := process.NewReporter(walker, "")
want := report.MakeReport()
want.Process = report.Topology{
EdgeMetadatas: report.EdgeMetadatas{},
NodeMetadatas: report.NodeMetadatas{
report.MakeProcessNodeID("", "1"): report.MakeNodeMetadataWith(map[string]string{
process.PID: "1",

View File

@@ -118,18 +118,20 @@ func interpolateCounts(r report.Report) {
}
factor := 1.0 / rate
for _, topology := range r.Topologies() {
for _, emd := range topology.EdgeMetadatas {
if emd.EgressPacketCount != nil {
*emd.EgressPacketCount = uint64(float64(*emd.EgressPacketCount) * factor)
}
if emd.IngressPacketCount != nil {
*emd.IngressPacketCount = uint64(float64(*emd.IngressPacketCount) * factor)
}
if emd.EgressByteCount != nil {
*emd.EgressByteCount = uint64(float64(*emd.EgressByteCount) * factor)
}
if emd.IngressByteCount != nil {
*emd.IngressByteCount = uint64(float64(*emd.IngressByteCount) * factor)
for _, nmd := range topology.NodeMetadatas {
for _, emd := range nmd.Edges {
if emd.EgressPacketCount != nil {
*emd.EgressPacketCount = uint64(float64(*emd.EgressPacketCount) * factor)
}
if emd.IngressPacketCount != nil {
*emd.IngressPacketCount = uint64(float64(*emd.IngressPacketCount) * factor)
}
if emd.EgressByteCount != nil {
*emd.EgressByteCount = uint64(float64(*emd.EgressByteCount) * factor)
}
if emd.IngressByteCount != nil {
*emd.IngressByteCount = uint64(float64(*emd.IngressByteCount) * factor)
}
}
}
}
@@ -261,12 +263,11 @@ func (s *Sniffer) Merge(p Packet, rpt *report.Report) {
var (
srcNodeID = report.MakeAddressNodeID(s.hostID, localIP)
dstNodeID = report.MakeAddressNodeID(s.hostID, remoteIP)
edgeID = report.MakeEdgeID(srcNodeID, dstNodeID)
)
rpt.Address = addAdjacency(rpt.Address, srcNodeID, dstNodeID)
emd := rpt.Address.EdgeMetadatas[edgeID]
emd := rpt.Address.NodeMetadatas[srcNodeID].Edges[dstNodeID]
if egress {
if emd.EgressPacketCount == nil {
emd.EgressPacketCount = new(uint64)
@@ -286,7 +287,7 @@ func (s *Sniffer) Merge(p Packet, rpt *report.Report) {
}
*emd.IngressByteCount += uint64(p.Network)
}
rpt.Address.EdgeMetadatas[edgeID] = emd
rpt.Address.NodeMetadatas[srcNodeID].Edges[dstNodeID] = emd
}
// If we have ports, we can add to the endpoint topology, too.
@@ -294,12 +295,11 @@ func (s *Sniffer) Merge(p Packet, rpt *report.Report) {
var (
srcNodeID = report.MakeEndpointNodeID(s.hostID, localIP, localPort)
dstNodeID = report.MakeEndpointNodeID(s.hostID, remoteIP, remotePort)
edgeID = report.MakeEdgeID(srcNodeID, dstNodeID)
)
rpt.Endpoint = addAdjacency(rpt.Endpoint, srcNodeID, dstNodeID)
emd := rpt.Endpoint.EdgeMetadatas[edgeID]
emd := rpt.Endpoint.NodeMetadatas[srcNodeID].Edges[dstNodeID]
if egress {
if emd.EgressPacketCount == nil {
emd.EgressPacketCount = new(uint64)
@@ -319,6 +319,6 @@ func (s *Sniffer) Merge(p Packet, rpt *report.Report) {
}
*emd.IngressByteCount += uint64(p.Transport)
}
rpt.Endpoint.EdgeMetadatas[edgeID] = emd
rpt.Endpoint.NodeMetadatas[srcNodeID].Edges[dstNodeID] = emd
}
}

View File

@@ -11,7 +11,6 @@ func TestInterpolateCounts(t *testing.T) {
hostID = "macbook-air"
srcNodeID = report.MakeEndpointNodeID(hostID, "1.2.3.4", "5678")
dstNodeID = report.MakeEndpointNodeID(hostID, "5.6.7.8", "9012")
edgeID = report.MakeEdgeID(srcNodeID, dstNodeID)
samplingCount = uint64(200)
samplingTotal = uint64(2345)
packetCount = uint64(123)
@@ -21,12 +20,12 @@ func TestInterpolateCounts(t *testing.T) {
r := report.MakeReport()
r.Sampling.Count = samplingCount
r.Sampling.Total = samplingTotal
r.Endpoint.EdgeMetadatas[edgeID] = report.EdgeMetadata{
r.Endpoint.NodeMetadatas[srcNodeID] = report.MakeNodeMetadata().WithEdge(dstNodeID, report.EdgeMetadata{
EgressPacketCount: newu64(packetCount),
IngressPacketCount: newu64(packetCount),
EgressByteCount: newu64(byteCount),
IngressByteCount: newu64(byteCount),
}
})
interpolateCounts(r)
@@ -34,7 +33,7 @@ func TestInterpolateCounts(t *testing.T) {
rate = float64(samplingCount) / float64(samplingTotal)
factor = 1.0 / rate
apply = func(v uint64) uint64 { return uint64(factor * float64(v)) }
emd = r.Endpoint.EdgeMetadatas[edgeID]
emd = r.Endpoint.NodeMetadatas[srcNodeID].Edges[dstNodeID]
)
if want, have := apply(packetCount), (*emd.EgressPacketCount); want != have {
t.Errorf("want %d packets, have %d", want, have)

View File

@@ -65,14 +65,11 @@ func TestMerge(t *testing.T) {
dstEndpointNodeID = report.MakeEndpointNodeID(hostID, p.DstIP, p.DstPort)
)
if want, have := (report.Topology{
EdgeMetadatas: report.EdgeMetadatas{
report.MakeEdgeID(srcEndpointNodeID, dstEndpointNodeID): report.EdgeMetadata{
NodeMetadatas: report.NodeMetadatas{
srcEndpointNodeID: report.MakeNodeMetadata().WithEdge(dstEndpointNodeID, report.EdgeMetadata{
EgressPacketCount: newu64(1),
EgressByteCount: newu64(256),
},
},
NodeMetadatas: report.NodeMetadatas{
srcEndpointNodeID: report.MakeNodeMetadata().WithAdjacent(dstEndpointNodeID),
}),
dstEndpointNodeID: report.MakeNodeMetadata(),
},
}), rpt.Endpoint; !reflect.DeepEqual(want, have) {
@@ -84,14 +81,11 @@ func TestMerge(t *testing.T) {
dstAddressNodeID = report.MakeAddressNodeID(hostID, p.DstIP)
)
if want, have := (report.Topology{
EdgeMetadatas: report.EdgeMetadatas{
report.MakeEdgeID(srcAddressNodeID, dstAddressNodeID): report.EdgeMetadata{
NodeMetadatas: report.NodeMetadatas{
srcAddressNodeID: report.MakeNodeMetadata().WithEdge(dstAddressNodeID, report.EdgeMetadata{
EgressPacketCount: newu64(1),
EgressByteCount: newu64(512),
},
},
NodeMetadatas: report.NodeMetadatas{
srcAddressNodeID: report.MakeNodeMetadata().WithAdjacent(dstAddressNodeID),
}),
dstAddressNodeID: report.MakeNodeMetadata(),
},
}), rpt.Address; !reflect.DeepEqual(want, have) {

View File

@@ -1,8 +1,6 @@
package render
import (
"log"
"github.com/weaveworks/scope/report"
)
@@ -171,6 +169,27 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes {
}
}
// 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] {
@@ -178,17 +197,7 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes {
for _, dstNodeID := range dstNodeIDs {
for _, dstRenderableID := range source2mapped[dstNodeID] {
dstRenderableNode := nodes[dstRenderableID]
srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID)
// 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 = srcRenderableNode.EdgeMetadata.Merge(md)
dstRenderableNode.EdgeMetadata = dstRenderableNode.EdgeMetadata.Merge(md)
nodes[dstRenderableID] = dstRenderableNode
}
}
}
@@ -217,23 +226,20 @@ func (m LeafMap) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableI
localNetworks = LocalNetworks(rpt)
metadata = report.EdgeMetadata{}
)
for edgeID, edgeMeta := range t.EdgeMetadatas {
src, dst, ok := report.ParseEdgeID(edgeID)
if !ok {
log.Printf("bad edge ID %q", edgeID)
continue
}
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)
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

View File

@@ -113,18 +113,19 @@ func TestMapEdge(t *testing.T) {
selector := func(_ report.Report) report.Topology {
return report.Topology{
NodeMetadatas: report.NodeMetadatas{
"foo": {
Metadata: map[string]string{"id": "foo"},
Adjacency: report.MakeIDList("bar"),
},
"bar": {
Metadata: map[string]string{"id": "bar"},
Adjacency: report.MakeIDList("foo"),
},
},
EdgeMetadatas: report.EdgeMetadatas{
"foo|bar": report.EdgeMetadata{EgressPacketCount: newu64(1), EgressByteCount: newu64(2)},
"bar|foo": report.EdgeMetadata{EgressPacketCount: newu64(3), EgressByteCount: newu64(4)},
"foo": report.MakeNodeMetadata().WithMetadata(map[string]string{
"id": "foo",
}).WithEdge("bar", report.EdgeMetadata{
EgressPacketCount: newu64(1),
EgressByteCount: newu64(2),
}),
"bar": report.MakeNodeMetadata().WithMetadata(map[string]string{
"id": "bar",
}).WithEdge("foo", report.EdgeMetadata{
EgressPacketCount: newu64(3),
EgressByteCount: newu64(4),
}),
},
}
}

View File

@@ -21,20 +21,6 @@ const (
EdgeDelim = "|"
)
// MakeEdgeID produces an edge ID from composite parts.
func MakeEdgeID(srcNodeID, dstNodeID string) string {
return srcNodeID + EdgeDelim + dstNodeID
}
// ParseEdgeID splits an edge ID to its composite parts.
func ParseEdgeID(edgeID string) (srcNodeID, dstNodeID string, ok bool) {
fields := strings.SplitN(edgeID, EdgeDelim, 2)
if len(fields) != 2 {
return "", "", false
}
return fields[0], fields[1], true
}
// MakeEndpointNodeID produces an endpoint node ID from its composite parts.
func MakeEndpointNodeID(hostID, address, port string) string {
return MakeAddressNodeID(hostID, address) + ScopeDelim + port

View File

@@ -65,51 +65,3 @@ func TestEndpointNodeID(t *testing.T) {
}
}
}
func TestEdgeID(t *testing.T) {
for _, bad := range []string{
client54001EndpointNodeID,
client54002EndpointNodeID,
unknown1EndpointNodeID,
unknown2EndpointNodeID,
unknown3EndpointNodeID,
clientAddressNodeID,
serverAddressNodeID,
unknownAddressNodeID,
clientHostNodeID,
serverHostNodeID,
">1.2.3.4",
">",
";",
"",
} {
if srcNodeID, dstNodeID, ok := report.ParseEdgeID(bad); ok {
t.Errorf("%q: expected failure, but got (%q, %q)", bad, srcNodeID, dstNodeID)
}
}
for input, want := range map[string]struct{ srcNodeID, dstNodeID string }{
report.MakeEdgeID("a", report.MakeEndpointNodeID("a", "b", "c")): {"a", report.MakeEndpointNodeID("a", "b", "c")},
report.MakeEdgeID("a", report.MakeAddressNodeID("a", "b")): {"a", report.MakeAddressNodeID("a", "b")},
report.MakeEdgeID("a", report.MakeProcessNodeID("a", "b")): {"a", report.MakeProcessNodeID("a", "b")},
report.MakeEdgeID("a", report.MakeHostNodeID("a")): {"a", report.MakeHostNodeID("a")},
"host.com|1.2.3.4": {"host.com", "1.2.3.4"},
"a|b;c": {"a", "b;c"},
"a|b": {"a", "b"},
"a|": {"a", ""},
"|b": {"", "b"},
"|": {"", ""},
} {
srcNodeID, dstNodeID, ok := report.ParseEdgeID(input)
if !ok {
t.Errorf("%q: not OK", input)
continue
}
if want, have := want.srcNodeID, srcNodeID; want != have {
t.Errorf("%q: want %q, have %q", input, want, have)
}
if want, have := want.dstNodeID, dstNodeID; want != have {
t.Errorf("%q: want %q, have %q", input, want, have)
}
}
}

View File

@@ -10,14 +10,12 @@ import (
// and NodeMetadatas respectively. Edges are directional, and embedded in the
// NodeMetadata.
type Topology struct {
EdgeMetadatas
NodeMetadatas
}
// MakeTopology gives you a Topology.
func MakeTopology() Topology {
return Topology{
EdgeMetadatas: map[string]EdgeMetadata{},
NodeMetadatas: map[string]NodeMetadata{},
}
}
@@ -36,7 +34,6 @@ func (t Topology) WithNode(nodeID string, nmd NodeMetadata) Topology {
// Copy returns a value copy of the Topology.
func (t Topology) Copy() Topology {
return Topology{
EdgeMetadatas: t.EdgeMetadatas.Copy(),
NodeMetadatas: t.NodeMetadatas.Copy(),
}
}
@@ -45,34 +42,10 @@ func (t Topology) Copy() Topology {
// The original is not modified.
func (t Topology) Merge(other Topology) Topology {
return Topology{
EdgeMetadatas: t.EdgeMetadatas.Merge(other.EdgeMetadatas),
NodeMetadatas: t.NodeMetadatas.Merge(other.NodeMetadatas),
}
}
// EdgeMetadatas collect metadata about each edge in a topology. Keys are a
// concatenation of node IDs.
type EdgeMetadatas map[string]EdgeMetadata
// Copy returns a value copy of the EdgeMetadatas.
func (e EdgeMetadatas) Copy() EdgeMetadatas {
cp := make(EdgeMetadatas, len(e))
for k, v := range e {
cp[k] = v.Copy()
}
return cp
}
// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (e EdgeMetadatas) Merge(other EdgeMetadatas) EdgeMetadatas {
cp := e.Copy()
for k, v := range other {
cp[k] = cp[k].Merge(v)
}
return cp
}
// NodeMetadatas collect metadata about each node in a topology. Keys are node
// IDs.
type NodeMetadatas map[string]NodeMetadata
@@ -98,6 +71,155 @@ func (n NodeMetadatas) Merge(other NodeMetadatas) NodeMetadatas {
return cp
}
// 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
}
// MakeNodeMetadata creates a new NodeMetadata with no initial metadata.
func MakeNodeMetadata() NodeMetadata {
return NodeMetadata{
Metadata: Metadata{},
Counters: Counters{},
Adjacency: MakeIDList(),
Edges: EdgeMetadatas{},
}
}
// MakeNodeMetadataWith creates a new NodeMetadata with the supplied map.
func MakeNodeMetadataWith(m map[string]string) NodeMetadata {
return MakeNodeMetadata().WithMetadata(m)
}
// WithMetadata returns a fresh copy of n, with Metadata set to m
func (n NodeMetadata) WithMetadata(m map[string]string) NodeMetadata {
result := n.Copy()
result.Metadata = m
return result
}
// WithCounters returns a fresh copy of n, with Counters set to c
func (n NodeMetadata) WithCounters(c map[string]int) NodeMetadata {
result := n.Copy()
result.Counters = c
return result
}
// WithAdjacency returns a fresh copy of n, with Adjacency set to a
func (n NodeMetadata) WithAdjacency(a IDList) NodeMetadata {
result := n.Copy()
result.Adjacency = a
return result
}
// WithAdjacent returns a fresh copy of n, with 'a' added to Adjacency
func (n NodeMetadata) WithAdjacent(a string) NodeMetadata {
result := n.Copy()
result.Adjacency = result.Adjacency.Add(a)
return result
}
// WithEdge returns a fresh copy of n, with 'dst' added to Adjacency and md added to EdgeMetadata
func (n NodeMetadata) WithEdge(dst string, md EdgeMetadata) NodeMetadata {
result := n.Copy()
result.Adjacency = result.Adjacency.Add(dst)
result.Edges[dst] = md
return result
}
// Copy returns a value copy of the NodeMetadata.
func (n NodeMetadata) Copy() NodeMetadata {
cp := MakeNodeMetadata()
cp.Metadata = n.Metadata.Copy()
cp.Counters = n.Counters.Copy()
cp.Adjacency = n.Adjacency.Copy()
cp.Edges = n.Edges.Copy()
return cp
}
// Merge mergses the individual components of a node and returns a
// fresh node.
func (n NodeMetadata) Merge(other NodeMetadata) NodeMetadata {
cp := n.Copy()
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)
return cp
}
// Metadata is a string->string map
type Metadata map[string]string
// Merge merges two node metadata maps together. In case of conflict, the
// other (right-hand) side wins. Always reassign the result of merge to the
// destination. Merge does not modify the receiver.
func (m Metadata) Merge(other Metadata) Metadata {
result := m.Copy()
for k, v := range other {
result[k] = v // other takes precedence
}
return result
}
// Copy creates a deep copy of the Metadata
func (m Metadata) Copy() Metadata {
result := Metadata{}
for k, v := range m {
result[k] = v
}
return result
}
// Counters is a string->int map
type Counters map[string]int
// Merge merges two sets of counters into a fresh set of counters,
// summing values where appropriate
func (c Counters) Merge(other Counters) Counters {
result := c.Copy()
for k, v := range other {
result[k] = result[k] + v
}
return result
}
// Copy creates a deep copy of the Counters
func (c Counters) Copy() Counters {
result := Counters{}
for k, v := range c {
result[k] = v
}
return result
}
// EdgeMetadatas collect metadata about each edge in a topology. Keys are
// the remote node IDs, as in Adjacency.
type EdgeMetadatas map[string]EdgeMetadata
// Copy returns a value copy of the EdgeMetadatas.
func (e EdgeMetadatas) Copy() EdgeMetadatas {
cp := make(EdgeMetadatas, len(e))
for k, v := range e {
cp[k] = v.Copy()
}
return cp
}
// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (e EdgeMetadatas) Merge(other EdgeMetadatas) EdgeMetadatas {
cp := e.Copy()
for k, v := range other {
cp[k] = cp[k].Merge(v)
}
return cp
}
// 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 {
@@ -155,102 +277,9 @@ func (e EdgeMetadata) Flatten(other EdgeMetadata) EdgeMetadata {
return cp
}
// NodeMetadata describes a superset of the metadata that probes can collect
// about a given node in a given topology.
type NodeMetadata struct {
Metadata map[string]string
Counters map[string]int
Adjacency IDList
}
// MakeNodeMetadata creates a new NodeMetadata with no initial metadata.
func MakeNodeMetadata() NodeMetadata {
return MakeNodeMetadataWith(map[string]string{})
}
// MakeNodeMetadataWith creates a new NodeMetadata with the supplied map.
func MakeNodeMetadataWith(m map[string]string) NodeMetadata {
return NodeMetadata{
Metadata: m,
Counters: map[string]int{},
Adjacency: MakeIDList(),
}
}
// WithMetadata returns a fresh copy of n, with Metadata set to m
func (n NodeMetadata) WithMetadata(m map[string]string) NodeMetadata {
result := n.Copy()
result.Metadata = m
return result
}
// WithCounters returns a fresh copy of n, with Counters set to c
func (n NodeMetadata) WithCounters(c map[string]int) NodeMetadata {
result := n.Copy()
result.Counters = c
return result
}
// WithAdjacency returns a fresh copy of n, with Adjacency set to a
func (n NodeMetadata) WithAdjacency(a IDList) NodeMetadata {
result := n.Copy()
result.Adjacency = a
return result
}
// WithAdjacent returns a fresh copy of n, with 'a' added to Adjacency
func (n NodeMetadata) WithAdjacent(a string) NodeMetadata {
result := n.Copy()
result.Adjacency = result.Adjacency.Add(a)
return result
}
// Copy returns a value copy of the NodeMetadata.
func (n NodeMetadata) Copy() NodeMetadata {
cp := MakeNodeMetadata()
for k, v := range n.Metadata {
cp.Metadata[k] = v
}
for k, v := range n.Counters {
cp.Counters[k] = v
}
cp.Adjacency = n.Adjacency.Copy()
return cp
}
// Merge merges two node metadata maps together. In case of conflict, the
// other (right-hand) side wins. Always reassign the result of merge to the
// destination. Merge does not modify the receiver.
func (n NodeMetadata) Merge(other NodeMetadata) NodeMetadata {
cp := n.Copy()
for k, v := range other.Metadata {
cp.Metadata[k] = v // other takes precedence
}
for k, v := range other.Counters {
cp.Counters[k] = n.Counters[k] + v
}
cp.Adjacency = cp.Adjacency.Merge(other.Adjacency)
return cp
}
// Validate checks the topology for various inconsistencies.
func (t Topology) Validate() error {
// Check all edge metadata keys must have the appropriate entries in
// adjacencies & node metadata.
var errs []string
for edgeID := range t.EdgeMetadatas {
srcNodeID, dstNodeID, ok := ParseEdgeID(edgeID)
if !ok {
errs = append(errs, fmt.Sprintf("invalid edge ID %q", edgeID))
continue
}
// For each edge, ensure they are connected in the right direction
if src, ok := t.NodeMetadatas[srcNodeID]; !ok {
errs = append(errs, fmt.Sprintf("node %s metadatas missing for edge %q", srcNodeID, edgeID))
} else if !src.Adjacency.Contains(dstNodeID) {
errs = append(errs, fmt.Sprintf("adjacency destination missing for destination node ID %q (from edge %q)", srcNodeID, edgeID))
}
}
errs := []string{}
// Check all node metadatas are valid, and the keys are parseable, i.e.
// contain a scope.
@@ -268,6 +297,13 @@ func (t Topology) Validate() error {
errs = append(errs, fmt.Sprintf("node metadata missing from adjacency %q -> %q", nodeID, dstNodeID))
}
}
// Check all the edge metadatas have entries in adjacencies
for dstNodeID := range nmd.Edges {
if _, ok := t.NodeMetadatas[dstNodeID]; !ok {
errs = append(errs, fmt.Sprintf("node %s metadatas missing for edge %q", dstNodeID, nodeID))
}
}
}
if len(errs) > 0 {

View File

@@ -95,14 +95,20 @@ var (
endpoint.Port: ClientPort54001,
process.PID: Client1PID,
report.HostNodeID: ClientHostNodeID,
}).WithAdjacency(report.MakeIDList(Server80NodeID)),
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(10),
EgressByteCount: newu64(100),
}),
Client54002NodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: ClientIP,
endpoint.Port: ClientPort54002,
process.PID: Client2PID,
report.HostNodeID: ClientHostNodeID,
}).WithAdjacency(report.MakeIDList(Server80NodeID)),
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(20),
EgressByteCount: newu64(200),
}),
Server80NodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: ServerIP,
@@ -116,60 +122,46 @@ var (
endpoint.Port: NonContainerClientPort,
process.PID: NonContainerPID,
report.HostNodeID: ServerHostNodeID,
}).WithAdjacency(report.MakeIDList(GoogleEndpointNodeID)),
}).WithAdjacent(GoogleEndpointNodeID),
// Probe pseudo nodes
UnknownClient1NodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: UnknownClient1IP,
endpoint.Port: UnknownClient1Port,
}).WithAdjacency(report.MakeIDList(Server80NodeID)),
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(30),
EgressByteCount: newu64(300),
}),
UnknownClient2NodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: UnknownClient2IP,
endpoint.Port: UnknownClient2Port,
}).WithAdjacency(report.MakeIDList(Server80NodeID)),
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(40),
EgressByteCount: newu64(400),
}),
UnknownClient3NodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: UnknownClient3IP,
endpoint.Port: UnknownClient3Port,
}).WithAdjacency(report.MakeIDList(Server80NodeID)),
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(50),
EgressByteCount: newu64(500),
}),
RandomClientNodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: RandomClientIP,
endpoint.Port: RandomClientPort,
}).WithAdjacency(report.MakeIDList(Server80NodeID)),
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(60),
EgressByteCount: newu64(600),
}),
GoogleEndpointNodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: GoogleIP,
endpoint.Port: GooglePort,
}),
},
EdgeMetadatas: report.EdgeMetadatas{
report.MakeEdgeID(Client54001NodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(10),
EgressByteCount: newu64(100),
},
report.MakeEdgeID(Client54002NodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(20),
EgressByteCount: newu64(200),
},
report.MakeEdgeID(UnknownClient1NodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(30),
EgressByteCount: newu64(300),
},
report.MakeEdgeID(UnknownClient2NodeID, Server80NodeID): report.EdgeMetadata{
EgressPacketCount: newu64(40),
EgressByteCount: newu64(400),
},
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{
NodeMetadatas: report.NodeMetadatas{
@@ -197,7 +189,6 @@ var (
report.HostNodeID: ServerHostNodeID,
}),
},
EdgeMetadatas: report.EdgeMetadatas{},
},
Container: report.Topology{
NodeMetadatas: report.NodeMetadatas{
@@ -238,7 +229,9 @@ var (
ClientAddressNodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: ClientIP,
report.HostNodeID: ClientHostNodeID,
}).WithAdjacency(report.MakeIDList(ServerAddressNodeID)),
}).WithEdge(ServerAddressNodeID, report.EdgeMetadata{
MaxConnCountTCP: newu64(3),
}),
ServerAddressNodeID: report.MakeNodeMetadata().WithMetadata(map[string]string{
endpoint.Addr: ServerIP,
@@ -261,11 +254,6 @@ var (
endpoint.Addr: RandomClientIP,
}).WithAdjacency(report.MakeIDList(ServerAddressNodeID)),
},
EdgeMetadatas: report.EdgeMetadatas{
report.MakeEdgeID(ClientAddressNodeID, ServerAddressNodeID): report.EdgeMetadata{
MaxConnCountTCP: newu64(3),
},
},
},
Host: report.Topology{
NodeMetadatas: report.NodeMetadatas{
@@ -284,7 +272,6 @@ var (
report.HostNodeID: ServerHostNodeID,
}),
},
EdgeMetadatas: report.EdgeMetadatas{},
},
Sampling: report.Sampling{
Count: 1024,