mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-19 21:39:26 +00:00
Make NodeMetadatas immutable.
This commit is contained in:
@@ -27,14 +27,13 @@ func MakeRenderableNodes(t report.Topology) RenderableNodes {
|
||||
|
||||
// Push EdgeMetadata to both ends of the edges
|
||||
for srcID, srcNode := range result {
|
||||
for dstID, emd := range srcNode.Edges {
|
||||
srcNode.Edges.ForEach(func(dstID string, emd report.EdgeMetadata) {
|
||||
srcNode.EdgeMetadata = srcNode.EdgeMetadata.Flatten(emd)
|
||||
|
||||
dstNode := result[dstID]
|
||||
dstNode.EdgeMetadata = dstNode.EdgeMetadata.Flatten(emd.Reversed())
|
||||
result[dstID] = dstNode
|
||||
}
|
||||
|
||||
})
|
||||
result[srcID] = srcNode
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -1,38 +1,176 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/mndrix/ps"
|
||||
)
|
||||
|
||||
// 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
|
||||
type EdgeMetadatas struct {
|
||||
psMap ps.Map
|
||||
}
|
||||
|
||||
// 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)
|
||||
// EmptyEdgeMetadatas is the set of empty EdgeMetadatas.
|
||||
var EmptyEdgeMetadatas = EdgeMetadatas{ps.NewMap()}
|
||||
|
||||
// MakeEdgeMetadatas returns EmptyEdgeMetadatas
|
||||
func MakeEdgeMetadatas() EdgeMetadatas {
|
||||
return EmptyEdgeMetadatas
|
||||
}
|
||||
|
||||
// Copy is a noop
|
||||
func (c EdgeMetadatas) Copy() EdgeMetadatas {
|
||||
return c
|
||||
}
|
||||
|
||||
// Add value to the counter 'key'
|
||||
func (c EdgeMetadatas) Add(key string, value EdgeMetadata) EdgeMetadatas {
|
||||
if existingValue, ok := c.psMap.Lookup(key); ok {
|
||||
value.Merge(existingValue.(EdgeMetadata))
|
||||
}
|
||||
return cp
|
||||
return EdgeMetadatas{
|
||||
c.psMap.Set(key, value),
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup the counter 'key'
|
||||
func (c EdgeMetadatas) Lookup(key string) (EdgeMetadata, bool) {
|
||||
existingValue, ok := c.psMap.Lookup(key)
|
||||
if ok {
|
||||
return existingValue.(EdgeMetadata), true
|
||||
}
|
||||
return EdgeMetadata{}, false
|
||||
}
|
||||
|
||||
// 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 {
|
||||
output := c.psMap
|
||||
|
||||
other.psMap.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 (e EdgeMetadatas) Flatten() EdgeMetadata {
|
||||
func (c EdgeMetadatas) Flatten() EdgeMetadata {
|
||||
result := EdgeMetadata{}
|
||||
for _, v := range e {
|
||||
result = result.Flatten(v)
|
||||
}
|
||||
c.psMap.ForEach(func(_ string, v interface{}) {
|
||||
result = result.Flatten(v.(EdgeMetadata))
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// ForEach executes f on each key value pair in the map
|
||||
func (c EdgeMetadatas) ForEach(fn func(k string, v EdgeMetadata)) {
|
||||
c.psMap.ForEach(func(key string, value interface{}) {
|
||||
fn(key, value.(EdgeMetadata))
|
||||
})
|
||||
}
|
||||
|
||||
func (c EdgeMetadatas) String() string {
|
||||
keys := []string{}
|
||||
for _, k := range c.psMap.Keys() {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
buf := bytes.NewBufferString("{")
|
||||
for _, key := range keys {
|
||||
val, _ := c.psMap.Lookup(key)
|
||||
fmt.Fprintf(buf, "%s: %v, ", key, val)
|
||||
}
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// DeepEqual tests equality with other Counters
|
||||
func (c EdgeMetadatas) DeepEqual(i interface{}) bool {
|
||||
d, ok := i.(EdgeMetadatas)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if c.psMap.Size() != d.psMap.Size() {
|
||||
return false
|
||||
}
|
||||
|
||||
equal := true
|
||||
c.psMap.ForEach(func(k string, val interface{}) {
|
||||
if otherValue, ok := d.psMap.Lookup(k); !ok {
|
||||
equal = false
|
||||
} else {
|
||||
equal = equal && reflect.DeepEqual(val, otherValue)
|
||||
}
|
||||
})
|
||||
return equal
|
||||
}
|
||||
|
||||
func (c EdgeMetadatas) toIntermediate() map[string]EdgeMetadata {
|
||||
intermediate := map[string]EdgeMetadata{}
|
||||
c.psMap.ForEach(func(key string, val interface{}) {
|
||||
intermediate[key] = val.(EdgeMetadata)
|
||||
})
|
||||
return intermediate
|
||||
}
|
||||
|
||||
func (c EdgeMetadatas) fromIntermediate(in map[string]EdgeMetadata) EdgeMetadatas {
|
||||
out := ps.NewMap()
|
||||
for k, v := range in {
|
||||
out = out.Set(k, v)
|
||||
}
|
||||
return EdgeMetadatas{out}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaller
|
||||
func (c EdgeMetadatas) MarshalJSON() ([]byte, error) {
|
||||
if c.psMap != nil {
|
||||
return json.Marshal(c.toIntermediate())
|
||||
}
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (c *EdgeMetadatas) UnmarshalJSON(input []byte) error {
|
||||
in := map[string]EdgeMetadata{}
|
||||
if err := json.Unmarshal(input, &in); err != nil {
|
||||
return err
|
||||
}
|
||||
*c = EdgeMetadatas{}.fromIntermediate(in)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GobEncode implements gob.Marshaller
|
||||
func (c EdgeMetadatas) GobEncode() ([]byte, error) {
|
||||
buf := bytes.Buffer{}
|
||||
err := gob.NewEncoder(&buf).Encode(c.toIntermediate())
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// GobDecode implements gob.Unmarshaller
|
||||
func (c *EdgeMetadatas) GobDecode(input []byte) error {
|
||||
in := map[string]EdgeMetadata{}
|
||||
if err := gob.NewDecoder(bytes.NewBuffer(input)).Decode(&in); err != nil {
|
||||
return err
|
||||
}
|
||||
*c = EdgeMetadatas{}.fromIntermediate(in)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -5,6 +5,9 @@ import "sort"
|
||||
// IDList is a list of string IDs, which are always sorted and unique.
|
||||
type IDList StringSet
|
||||
|
||||
// EmptyIDList is an Empty ID List.
|
||||
var EmptyIDList = IDList(EmptyStringSet)
|
||||
|
||||
// MakeIDList makes a new IDList.
|
||||
func MakeIDList(ids ...string) IDList {
|
||||
return IDList(MakeStringSet(ids...))
|
||||
|
||||
@@ -21,87 +21,88 @@ func TestMergeEdgeMetadatas(t *testing.T) {
|
||||
a, b, want report.EdgeMetadatas
|
||||
}{
|
||||
"Empty a": {
|
||||
a: report.EdgeMetadatas{},
|
||||
b: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
a: report.EmptyEdgeMetadatas,
|
||||
b: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(1),
|
||||
MaxConnCountTCP: newu64(2),
|
||||
},
|
||||
},
|
||||
want: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
}),
|
||||
want: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(1),
|
||||
MaxConnCountTCP: newu64(2),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
"Empty b": {
|
||||
a: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
a: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(12),
|
||||
EgressByteCount: newu64(999),
|
||||
},
|
||||
},
|
||||
b: report.EdgeMetadatas{},
|
||||
want: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
}),
|
||||
b: report.EmptyEdgeMetadatas,
|
||||
want: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(12),
|
||||
EgressByteCount: newu64(999),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
"Host merge": {
|
||||
a: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
a: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(12),
|
||||
EgressByteCount: newu64(500),
|
||||
MaxConnCountTCP: newu64(4),
|
||||
},
|
||||
},
|
||||
b: report.EdgeMetadatas{
|
||||
"hostQ|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
}),
|
||||
b: report.EmptyEdgeMetadatas.
|
||||
Add("hostQ|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(1),
|
||||
EgressByteCount: newu64(2),
|
||||
MaxConnCountTCP: newu64(6),
|
||||
},
|
||||
},
|
||||
want: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
}),
|
||||
want: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(12),
|
||||
EgressByteCount: newu64(500),
|
||||
MaxConnCountTCP: newu64(4),
|
||||
},
|
||||
"hostQ|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
}).
|
||||
Add("hostQ|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(1),
|
||||
EgressByteCount: newu64(2),
|
||||
MaxConnCountTCP: newu64(6),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
"Edge merge": {
|
||||
a: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
a: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(12),
|
||||
EgressByteCount: newu64(1000),
|
||||
MaxConnCountTCP: newu64(7),
|
||||
},
|
||||
},
|
||||
b: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
}),
|
||||
b: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(1),
|
||||
IngressByteCount: newu64(123),
|
||||
EgressByteCount: newu64(2),
|
||||
MaxConnCountTCP: newu64(9),
|
||||
},
|
||||
},
|
||||
want: report.EdgeMetadatas{
|
||||
"hostA|:192.168.1.1:12345|:192.168.1.2:80": report.EdgeMetadata{
|
||||
}),
|
||||
want: report.EmptyEdgeMetadatas.
|
||||
Add("hostA|:192.168.1.1:12345|:192.168.1.2:80",
|
||||
report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(13),
|
||||
IngressByteCount: newu64(123),
|
||||
EgressByteCount: newu64(1002),
|
||||
MaxConnCountTCP: newu64(9),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
} {
|
||||
if have := c.a.Merge(c.b); !reflect.DeepEqual(c.want, have) {
|
||||
|
||||
@@ -27,8 +27,8 @@ func MakeNode() Node {
|
||||
return Node{
|
||||
Counters: EmptyCounters,
|
||||
Sets: EmptySets,
|
||||
Adjacency: MakeIDList(),
|
||||
Edges: EdgeMetadatas{},
|
||||
Adjacency: EmptyIDList,
|
||||
Edges: EmptyEdgeMetadatas,
|
||||
Controls: MakeNodeControls(),
|
||||
Latest: EmptyLatestMap,
|
||||
Metrics: Metrics{},
|
||||
@@ -134,7 +134,7 @@ func (n Node) WithAdjacent(a ...string) Node {
|
||||
func (n Node) WithEdge(dst string, md EdgeMetadata) Node {
|
||||
result := n.Copy()
|
||||
result.Adjacency = result.Adjacency.Add(dst)
|
||||
result.Edges[dst] = md
|
||||
result.Edges = result.Edges.Add(dst, md)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestNode(t *testing.T) {
|
||||
if node.Adjacency[0] != "foo" {
|
||||
t.Errorf("want foo, have %v", node.Adjacency)
|
||||
}
|
||||
if *node.Edges["foo"].EgressPacketCount != 13 {
|
||||
if v, ok := node.Edges.Lookup("foo"); ok && *v.EgressPacketCount != 13 {
|
||||
t.Errorf("want 13, have %v", node.Edges)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +97,11 @@ func (t Topology) Validate() error {
|
||||
}
|
||||
|
||||
// Check all the edge metadatas have entries in adjacencies
|
||||
for dstNodeID := range nmd.Edges {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user