Merge pull request #2992 from weaveworks/remove-node-edges

remove Node.Edges
This commit is contained in:
Matthias Radestock
2017-12-17 18:44:03 +00:00
committed by GitHub
8 changed files with 13 additions and 599 deletions

View File

@@ -217,7 +217,7 @@ func (t *connectionTracker) addConnection(rpt *report.Report, incoming bool, ft
fromNode = t.makeEndpointNode(namespaceID, ft.fromAddr, ft.fromPort, extraFromNode)
toNode = t.makeEndpointNode(namespaceID, ft.toAddr, ft.toPort, extraToNode)
)
rpt.Endpoint = rpt.Endpoint.AddNode(fromNode.WithEdge(toNode.ID, report.EdgeMetadata{}))
rpt.Endpoint = rpt.Endpoint.AddNode(fromNode.WithAdjacent(toNode.ID))
rpt.Endpoint = rpt.Endpoint.AddNode(toNode)
}

View File

@@ -1,246 +0,0 @@
package report
import (
"fmt"
"reflect"
"strconv"
"github.com/ugorji/go/codec"
"github.com/weaveworks/ps"
)
// EdgeMetadatas collect metadata about each edge in a topology. Keys are the
// remote node IDs, as in Adjacency.
type EdgeMetadatas struct {
psMap ps.Map
}
var emptyEdgeMetadatas = EdgeMetadatas{ps.NewMap()}
// MakeEdgeMetadatas returns EmptyEdgeMetadatas
func MakeEdgeMetadatas() EdgeMetadatas {
return emptyEdgeMetadatas
}
// Add value to the counter 'key'
func (c EdgeMetadatas) Add(key string, value EdgeMetadata) EdgeMetadatas {
if c.psMap == nil {
c = emptyEdgeMetadatas
}
if existingValue, ok := c.psMap.Lookup(key); ok {
value = value.Merge(existingValue.(EdgeMetadata))
}
return EdgeMetadatas{
c.psMap.Set(key, value),
}
}
// Lookup the counter 'key'
func (c EdgeMetadatas) Lookup(key string) (EdgeMetadata, bool) {
if c.psMap != nil {
existingValue, ok := c.psMap.Lookup(key)
if ok {
return existingValue.(EdgeMetadata), true
}
}
return EdgeMetadata{}, false
}
// Size is the number of elements
func (c EdgeMetadatas) Size() int {
if c.psMap == nil {
return 0
}
return c.psMap.Size()
}
// Merge produces a fresh Counters, container the keys from both inputs. When
// both inputs container the same key, the latter value is used.
func (c EdgeMetadatas) Merge(other EdgeMetadatas) EdgeMetadatas {
var (
cSize = c.Size()
otherSize = other.Size()
output = c.psMap
iter = other.psMap
)
switch {
case cSize == 0:
return other
case otherSize == 0:
return c
case cSize < otherSize:
output, iter = iter, output
}
iter.ForEach(func(key string, otherVal interface{}) {
if val, ok := output.Lookup(key); ok {
output = output.Set(key, otherVal.(EdgeMetadata).Merge(val.(EdgeMetadata)))
} else {
output = output.Set(key, otherVal)
}
})
return EdgeMetadatas{output}
}
// Flatten flattens all the EdgeMetadatas in this set and returns the result.
// The original is not modified.
func (c EdgeMetadatas) Flatten() EdgeMetadata {
result := EdgeMetadata{}
c.ForEach(func(_ string, e EdgeMetadata) {
result = result.Flatten(e)
})
return result
}
// ForEach executes f on each key value pair in the map
func (c EdgeMetadatas) ForEach(fn func(k string, v EdgeMetadata)) {
if c.psMap != nil {
c.psMap.ForEach(func(key string, value interface{}) {
fn(key, value.(EdgeMetadata))
})
}
}
func (c EdgeMetadatas) String() string {
return mapToString(c.psMap)
}
// DeepEqual tests equality with other Counters
func (c EdgeMetadatas) DeepEqual(d EdgeMetadatas) bool {
return mapEqual(c.psMap, d.psMap, reflect.DeepEqual)
}
// CodecEncodeSelf implements codec.Selfer
func (c *EdgeMetadatas) CodecEncodeSelf(encoder *codec.Encoder) {
mapWrite(c.psMap, encoder, func(encoder *codec.Encoder, val interface{}) {
e := val.(EdgeMetadata)
(&e).CodecEncodeSelf(encoder)
})
}
// CodecDecodeSelf implements codec.Selfer
func (c *EdgeMetadatas) CodecDecodeSelf(decoder *codec.Decoder) {
out := mapRead(decoder, func(isNil bool) interface{} {
var value EdgeMetadata
if !isNil {
value.CodecDecodeSelf(decoder)
}
return value
})
*c = EdgeMetadatas{out}
}
// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
func (EdgeMetadatas) MarshalJSON() ([]byte, error) {
panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
}
// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
func (*EdgeMetadatas) UnmarshalJSON(b []byte) error {
panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
}
// 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 {
EgressPacketCount *uint64 `json:"egress_packet_count,omitempty"`
IngressPacketCount *uint64 `json:"ingress_packet_count,omitempty"`
EgressByteCount *uint64 `json:"egress_byte_count,omitempty"` // Transport layer
IngressByteCount *uint64 `json:"ingress_byte_count,omitempty"` // Transport layer
dummySelfer
}
// String returns a string representation of this EdgeMetadata
// Helps with our use of Spew and diff.
func (e EdgeMetadata) String() string {
f := func(i *uint64) string {
if i == nil {
return "nil"
}
return strconv.FormatUint(*i, 10)
}
return fmt.Sprintf(`{
EgressPacketCount: %v,
IngressPacketCount: %v,
EgressByteCount: %v,
IngressByteCount: %v,
}`,
f(e.EgressPacketCount),
f(e.IngressPacketCount),
f(e.EgressByteCount),
f(e.IngressByteCount))
}
// Copy returns a value copy of the EdgeMetadata.
func (e EdgeMetadata) Copy() EdgeMetadata {
return EdgeMetadata{
EgressPacketCount: cpu64ptr(e.EgressPacketCount),
IngressPacketCount: cpu64ptr(e.IngressPacketCount),
EgressByteCount: cpu64ptr(e.EgressByteCount),
IngressByteCount: cpu64ptr(e.IngressByteCount),
}
}
// Reversed returns a value copy of the EdgeMetadata, with the direction reversed.
func (e EdgeMetadata) Reversed() EdgeMetadata {
return EdgeMetadata{
EgressPacketCount: cpu64ptr(e.IngressPacketCount),
IngressPacketCount: cpu64ptr(e.EgressPacketCount),
EgressByteCount: cpu64ptr(e.IngressByteCount),
IngressByteCount: cpu64ptr(e.EgressByteCount),
}
}
func cpu64ptr(u *uint64) *uint64 {
if u == nil {
return nil
}
value := *u // oh man
return &value // this sucks
}
// Merge merges another EdgeMetadata into the receiver and returns the result.
// The receiver is not modified. The two edge metadatas should represent the
// same edge on different times.
func (e EdgeMetadata) Merge(other EdgeMetadata) EdgeMetadata {
cp := e.Copy()
cp.EgressPacketCount = merge(cp.EgressPacketCount, other.EgressPacketCount, sum)
cp.IngressPacketCount = merge(cp.IngressPacketCount, other.IngressPacketCount, sum)
cp.EgressByteCount = merge(cp.EgressByteCount, other.EgressByteCount, sum)
cp.IngressByteCount = merge(cp.IngressByteCount, other.IngressByteCount, sum)
return cp
}
// Flatten sums two EdgeMetadatas and returns the result. The receiver is not
// modified. The two edge metadata windows should be the same duration; they
// should represent different edges at the same time.
func (e EdgeMetadata) Flatten(other EdgeMetadata) EdgeMetadata {
cp := e.Copy()
cp.EgressPacketCount = merge(cp.EgressPacketCount, other.EgressPacketCount, sum)
cp.IngressPacketCount = merge(cp.IngressPacketCount, other.IngressPacketCount, sum)
cp.EgressByteCount = merge(cp.EgressByteCount, other.EgressByteCount, sum)
cp.IngressByteCount = merge(cp.IngressByteCount, other.IngressByteCount, sum)
return cp
}
func merge(dst, src *uint64, op func(uint64, uint64) uint64) *uint64 {
if src == nil {
return dst
}
if dst == nil {
dst = new(uint64)
}
(*dst) = op(*dst, *src)
return dst
}
func sum(dst, src uint64) uint64 {
return dst + src
}
func max(dst, src uint64) uint64 {
if dst > src {
return dst
}
return src
}

View File

@@ -1,288 +0,0 @@
package report
import (
"bytes"
"testing"
"github.com/ugorji/go/codec"
"github.com/weaveworks/common/test"
"github.com/weaveworks/scope/test/reflect"
)
func TestEdgeMetadatasAdd(t *testing.T) {
have := MakeEdgeMetadatas().
Add("foo",
EdgeMetadata{
EgressPacketCount: newu64(1),
}).
Add("foo",
EdgeMetadata{
EgressPacketCount: newu64(2),
})
if emd, ok := have.Lookup("foo"); !ok || *emd.EgressPacketCount != 3 {
t.Errorf("foo.EgressPacketCount != 3")
}
if emd, ok := have.Lookup("bar"); ok || emd.EgressPacketCount != nil {
t.Errorf("bar.EgressPacketCount != nil")
}
have.ForEach(func(k string, emd EdgeMetadata) {
if k != "foo" || *emd.EgressPacketCount != 3 {
t.Errorf("foo.EgressPacketCount != 3")
}
})
}
func TestEdgeMetadatasAddNil(t *testing.T) {
have := EdgeMetadatas{}.Add("foo", EdgeMetadata{EgressPacketCount: newu64(1)})
if have.Size() != 1 {
t.Errorf("Adding to a zero-value EdgeMetadatas failed, got: %v", have)
}
}
func TestEdgeMetadatasDeepEquals(t *testing.T) {
want := MakeEdgeMetadatas().
Add("foo",
EdgeMetadata{
EgressPacketCount: newu64(3),
})
have := MakeEdgeMetadatas().
Add("foo",
EdgeMetadata{
EgressPacketCount: newu64(3),
})
if !reflect.DeepEqual(want, have) {
t.Errorf(test.Diff(want, have))
}
}
func TestEdgeMetadatasMerge(t *testing.T) {
for name, c := range map[string]struct {
a, b, want EdgeMetadatas
}{
"nils": {
a: EdgeMetadatas{},
b: EdgeMetadatas{},
want: EdgeMetadatas{},
},
"Empty a": {
a: MakeEdgeMetadatas(),
b: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(1),
}),
want: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(1),
}),
},
"Empty b": {
a: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(12),
EgressByteCount: newu64(999),
}),
b: MakeEdgeMetadatas(),
want: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(12),
EgressByteCount: newu64(999),
}),
},
"Disjoint a & b": {
a: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(12),
EgressByteCount: newu64(500),
}),
b: MakeEdgeMetadatas().
Add("hostQ|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(1),
EgressByteCount: newu64(2),
}),
want: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(12),
EgressByteCount: newu64(500),
}).
Add("hostQ|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(1),
EgressByteCount: newu64(2),
}),
},
"Overlapping a & b": {
a: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(12),
EgressByteCount: newu64(1000),
}),
b: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(1),
IngressByteCount: newu64(123),
EgressByteCount: newu64(2),
}),
want: MakeEdgeMetadatas().
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
EdgeMetadata{
EgressPacketCount: newu64(13),
IngressByteCount: newu64(123),
EgressByteCount: newu64(1002),
}),
},
} {
if have := c.a.Merge(c.b); !reflect.DeepEqual(c.want, have) {
t.Errorf("%s:\n%s", name, test.Diff(c.want, have))
}
}
}
func TestEdgeMetadataFlatten(t *testing.T) {
// Test two EdgeMetadatas flatten to the correct values
{
have := (EdgeMetadata{
EgressPacketCount: newu64(1),
}).Flatten(EdgeMetadata{
EgressPacketCount: newu64(4),
EgressByteCount: newu64(8),
})
want := EdgeMetadata{
EgressPacketCount: newu64(1 + 4),
EgressByteCount: newu64(8),
}
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
// Test an EdgeMetadatas flatten to the correct value (should
// just sum)
{
have := MakeEdgeMetadatas().
Add("foo", EdgeMetadata{
EgressPacketCount: newu64(1),
}).
Add("bar", EdgeMetadata{
EgressPacketCount: newu64(3),
}).Flatten()
want := EdgeMetadata{
EgressPacketCount: newu64(1 + 3),
}
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
{
// Should not panic on nil
have := EdgeMetadatas{}.Flatten()
want := EdgeMetadata{}
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
}
func TestEdgeMetadataReversed(t *testing.T) {
have := EdgeMetadata{
EgressPacketCount: newu64(1),
}.Reversed()
want := EdgeMetadata{
IngressPacketCount: newu64(1),
}
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func TestEdgeMetadatasEncoding(t *testing.T) {
want := MakeEdgeMetadatas().
Add("foo", EdgeMetadata{
EgressPacketCount: newu64(1),
}).
Add("bar", EdgeMetadata{
EgressPacketCount: newu64(3),
})
{
for _, h := range []codec.Handle{
codec.Handle(&codec.MsgpackHandle{}),
codec.Handle(&codec.JsonHandle{}),
} {
buf := &bytes.Buffer{}
encoder := codec.NewEncoder(buf, h)
want.CodecEncodeSelf(encoder)
decoder := codec.NewDecoder(buf, h)
have := MakeEdgeMetadatas()
have.CodecDecodeSelf(decoder)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
}
}
func TestEdgeMetadatasEncodingNil(t *testing.T) {
want := EdgeMetadatas{}
{
for _, h := range []codec.Handle{
codec.Handle(&codec.MsgpackHandle{}),
codec.Handle(&codec.JsonHandle{}),
} {
buf := &bytes.Buffer{}
encoder := codec.NewEncoder(buf, h)
want.CodecEncodeSelf(encoder)
decoder := codec.NewDecoder(buf, h)
have := MakeEdgeMetadatas()
have.CodecDecodeSelf(decoder)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
}
}
func newu64(value uint64) *uint64 { return &value }
func TestEdgeMetadataDeepEquals(t *testing.T) {
for _, c := range []struct {
name string
a, b interface{}
want bool
}{
{
name: "zero values",
a: EdgeMetadata{},
b: EdgeMetadata{},
want: true,
},
{
name: "matching, but different pointers",
a: EdgeMetadata{EgressPacketCount: newu64(3)},
b: EdgeMetadata{EgressPacketCount: newu64(3)},
want: true,
},
{
name: "mismatching",
a: EdgeMetadata{EgressPacketCount: newu64(3)},
b: EdgeMetadata{EgressPacketCount: newu64(4)},
want: false,
},
} {
if have := reflect.DeepEqual(c.a, c.b); have != c.want {
t.Errorf("reflect.DeepEqual(%v, %v) != %v", c.a, c.b, c.want)
}
}
}

View File

@@ -6,16 +6,15 @@ import (
"github.com/weaveworks/common/mtime"
)
// Node describes a superset of the metadata that probes can collect about a
// given node in a given topology, along with the edges emanating from the
// node and metadata about those edges.
// Node describes a superset of the metadata that probes can collect
// about a given node in a given topology, along with the edges (aka
// adjacency) emanating from the node.
type Node struct {
ID string `json:"id,omitempty"`
Topology string `json:"topology,omitempty"`
Counters Counters `json:"counters,omitempty"`
Sets Sets `json:"sets,omitempty"`
Adjacency IDList `json:"adjacency"`
Edges EdgeMetadatas `json:"edges,omitempty"`
Controls NodeControls `json:"controls,omitempty"`
LatestControls NodeControlDataLatestMap `json:"latestControls,omitempty"`
Latest StringLatestMap `json:"latest,omitempty"`
@@ -31,7 +30,6 @@ func MakeNode(id string) Node {
Counters: MakeCounters(),
Sets: MakeSets(),
Adjacency: MakeIDList(),
Edges: MakeEdgeMetadatas(),
Controls: MakeNodeControls(),
LatestControls: MakeNodeControlDataLatestMap(),
Latest: MakeStringLatestMap(),
@@ -124,14 +122,6 @@ func (n Node) WithAdjacent(a ...string) Node {
return n
}
// WithEdge returns a fresh copy of n, with 'dst' added to Adjacency and md
// added to EdgeMetadata.
func (n Node) WithEdge(dst string, md EdgeMetadata) Node {
n.Adjacency = n.Adjacency.Add(dst)
n.Edges = n.Edges.Add(dst, md)
return n
}
// WithControls returns a fresh copy of n, with cs added to Controls.
func (n Node) WithControls(cs ...string) Node {
n.Controls = n.Controls.Add(cs...)
@@ -205,7 +195,6 @@ func (n Node) Merge(other Node) Node {
Counters: n.Counters.Merge(other.Counters),
Sets: n.Sets.Merge(other.Sets),
Adjacency: n.Adjacency.Merge(other.Adjacency),
Edges: n.Edges.Merge(other.Edges),
Controls: n.Controls.Merge(other.Controls),
LatestControls: n.LatestControls.Merge(other.LatestControls),
Latest: n.Latest.Merge(other.Latest),

View File

@@ -121,7 +121,7 @@ type Report struct {
// Overlay nodes are active peers in any software-defined network that's
// overlaid on the infrastructure. The information is scraped by polling
// their status endpoints. Edges could be present, but aren't currently.
// their status endpoints. Edges are present.
Overlay Topology
// Sampling data for this report.

View File

@@ -66,17 +66,6 @@ func TestNode(t *testing.T) {
t.Errorf("want foo, have %v", node.Adjacency)
}
}
{
node := report.MakeNode("foo").WithEdge("foo", report.EdgeMetadata{
EgressPacketCount: newu64(13),
})
if node.Adjacency[0] != "foo" {
t.Errorf("want foo, have %v", node.Adjacency)
}
if v, ok := node.Edges.Lookup("foo"); ok && *v.EgressPacketCount != 13 {
t.Errorf("want 13, have %v", node.Edges)
}
}
}
func TestReportBackwardCompatibility(t *testing.T) {

View File

@@ -5,9 +5,8 @@ import (
"strings"
)
// Topology describes a specific view of a network. It consists of nodes and
// edges, and metadata about those nodes and edges, represented by
// EdgeMetadatas and Nodes respectively. Edges are directional, and embedded
// Topology describes a specific view of a network. It consists of
// nodes with metadata, and edges. Edges are directional, and embedded
// in the Node struct.
type Topology struct {
Shape string `json:"shape,omitempty"`
@@ -206,13 +205,6 @@ func (t Topology) Validate() error {
errs = append(errs, fmt.Sprintf("node missing from adjacency %q -> %q", nodeID, dstNodeID))
}
}
// Check all the edge metadatas have entries in adjacencies
nmd.Edges.ForEach(func(dstNodeID string, _ EdgeMetadata) {
if _, ok := t.Nodes[dstNodeID]; !ok {
errs = append(errs, fmt.Sprintf("node %s missing for edge %q", dstNodeID, nodeID))
}
})
}
if len(errs) > 0 {

View File

@@ -129,18 +129,12 @@ var (
Client54001NodeID: report.MakeNode(Client54001NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
process.PID: Client1PID,
report.HostNodeID: ClientHostNodeID,
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(10),
EgressByteCount: newu64(100),
}),
}).WithAdjacent(Server80NodeID),
Client54002NodeID: report.MakeNode(Client54002NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
process.PID: Client2PID,
report.HostNodeID: ClientHostNodeID,
}).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(20),
EgressByteCount: newu64(200),
}),
}).WithAdjacent(Server80NodeID),
Server80NodeID: report.MakeNode(Server80NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
process.PID: ServerPID,
@@ -153,26 +147,10 @@ var (
}).WithAdjacent(GoogleEndpointNodeID),
// Probe pseudo nodes
UnknownClient1NodeID: report.MakeNode(UnknownClient1NodeID).WithTopology(report.Endpoint).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(30),
EgressByteCount: newu64(300),
}),
UnknownClient2NodeID: report.MakeNode(UnknownClient2NodeID).WithTopology(report.Endpoint).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(40),
EgressByteCount: newu64(400),
}),
UnknownClient3NodeID: report.MakeNode(UnknownClient3NodeID).WithTopology(report.Endpoint).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(50),
EgressByteCount: newu64(500),
}),
RandomClientNodeID: report.MakeNode(RandomClientNodeID).WithTopology(report.Endpoint).WithEdge(Server80NodeID, report.EdgeMetadata{
EgressPacketCount: newu64(60),
EgressByteCount: newu64(600),
}),
UnknownClient1NodeID: report.MakeNode(UnknownClient1NodeID).WithTopology(report.Endpoint).WithAdjacent(Server80NodeID),
UnknownClient2NodeID: report.MakeNode(UnknownClient2NodeID).WithTopology(report.Endpoint).WithAdjacent(Server80NodeID),
UnknownClient3NodeID: report.MakeNode(UnknownClient3NodeID).WithTopology(report.Endpoint).WithAdjacent(Server80NodeID),
RandomClientNodeID: report.MakeNode(RandomClientNodeID).WithTopology(report.Endpoint).WithAdjacent(Server80NodeID),
GoogleEndpointNodeID: report.MakeNode(GoogleEndpointNodeID).WithTopology(report.Endpoint),
},
},