mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-16 03:49:52 +00:00
Merge pull request #399 from weaveworks/topology-copy
Moar functional: Topology Merge methods don't mutate the receiver
This commit is contained in:
@@ -16,18 +16,17 @@ import (
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
func fixNodeMetadatas(nodes render.RenderableNodes) render.RenderableNodes {
|
||||
result := make(render.RenderableNodes, len(nodes))
|
||||
for id, node := range nodes {
|
||||
if node.NodeMetadata.Metadata == nil {
|
||||
node.NodeMetadata.Metadata = map[string]string{}
|
||||
// A copy of sanitize from the render package.
|
||||
func sanitize(nodes render.RenderableNodes) render.RenderableNodes {
|
||||
for id, n := range nodes {
|
||||
if n.Adjacency == nil {
|
||||
n.Adjacency = report.IDList{}
|
||||
}
|
||||
if node.NodeMetadata.Counters == nil {
|
||||
node.NodeMetadata.Counters = map[string]int{}
|
||||
}
|
||||
result[id] = node
|
||||
n.NodeMetadata.Metadata = map[string]string{}
|
||||
n.NodeMetadata.Counters = map[string]int{}
|
||||
nodes[id] = n
|
||||
}
|
||||
return result
|
||||
return nodes
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
@@ -74,7 +73,7 @@ func TestAPITopologyContainers(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want, have := expected.RenderedContainers, fixNodeMetadatas(topo.Nodes); !reflect.DeepEqual(want, have) {
|
||||
if want, have := expected.RenderedContainers, sanitize(topo.Nodes); !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
@@ -122,7 +121,7 @@ func TestAPITopologyHosts(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want, have := expected.RenderedHosts, fixNodeMetadatas(topo.Nodes); !reflect.DeepEqual(want, have) {
|
||||
if want, have := expected.RenderedHosts, sanitize(topo.Nodes); !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ func NewReporter(registry Registry, scope string) *Reporter {
|
||||
// Report generates a Report containing Container and ContainerImage topologies
|
||||
func (r *Reporter) Report() (report.Report, error) {
|
||||
result := report.MakeReport()
|
||||
result.Container.Merge(r.containerTopology())
|
||||
result.ContainerImage.Merge(r.containerImageTopology())
|
||||
result.Container = result.Container.Merge(r.containerTopology())
|
||||
result.ContainerImage = result.ContainerImage.Merge(r.containerImageTopology())
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *Reporter) containerTopology() report.Topology {
|
||||
result := report.NewTopology()
|
||||
result := report.MakeTopology()
|
||||
|
||||
r.registry.WalkContainers(func(c Container) {
|
||||
nodeID := report.MakeContainerNodeID(r.scope, c.ID())
|
||||
@@ -46,7 +46,7 @@ func (r *Reporter) containerTopology() report.Topology {
|
||||
}
|
||||
|
||||
func (r *Reporter) containerImageTopology() report.Topology {
|
||||
result := report.NewTopology()
|
||||
result := report.MakeTopology()
|
||||
|
||||
r.registry.WalkImages(func(image *docker_client.APIImages) {
|
||||
nmd := report.MakeNodeMetadataWith(map[string]string{
|
||||
|
||||
@@ -83,6 +83,6 @@ func (t *Tagger) tag(tree process.Tree, topology *report.Topology) {
|
||||
ContainerID: c.ID(),
|
||||
})
|
||||
|
||||
topology.NodeMetadatas[nodeID].Merge(md)
|
||||
topology.NodeMetadatas[nodeID] = nodeMetadata.Merge(md)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ func NewTagger(hostID string) Tagger {
|
||||
|
||||
// Tag implements Tagger.
|
||||
func (t Tagger) Tag(r report.Report) (report.Report, error) {
|
||||
md := report.MakeNodeMetadataWith(map[string]string{report.HostNodeID: t.hostNodeID})
|
||||
other := report.MakeNodeMetadataWith(map[string]string{report.HostNodeID: t.hostNodeID})
|
||||
for _, topology := range r.Topologies() {
|
||||
for nodeID := range topology.NodeMetadatas {
|
||||
topology.NodeMetadatas[nodeID].Merge(md)
|
||||
for id, md := range topology.NodeMetadatas {
|
||||
topology.NodeMetadatas[id] = md.Merge(other)
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
|
||||
@@ -183,7 +183,7 @@ func main() {
|
||||
if err != nil {
|
||||
log.Printf("error generating report: %v", err)
|
||||
}
|
||||
r.Merge(newReport)
|
||||
r = r.Merge(newReport)
|
||||
}
|
||||
r = Apply(r, taggers)
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Process.Merge(processes)
|
||||
result.Process = result.Process.Merge(processes)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *Reporter) processTopology() (report.Topology, error) {
|
||||
t := report.NewTopology()
|
||||
t := report.MakeTopology()
|
||||
err := r.walker.Walk(func(p Process) {
|
||||
pidstr := strconv.Itoa(p.PID)
|
||||
nodeID := report.MakeProcessNodeID(r.scope, pidstr)
|
||||
|
||||
@@ -50,9 +50,9 @@ func (topologyTagger) Tag(r report.Report) (report.Report, error) {
|
||||
"host": &(r.Host),
|
||||
"overlay": &(r.Overlay),
|
||||
} {
|
||||
md := report.MakeNodeMetadataWith(map[string]string{Topology: val})
|
||||
for nodeID := range topology.NodeMetadatas {
|
||||
(*topology).NodeMetadatas[nodeID].Merge(md)
|
||||
other := report.MakeNodeMetadataWith(map[string]string{Topology: val})
|
||||
for id, md := range topology.NodeMetadatas {
|
||||
topology.NodeMetadatas[id] = md.Merge(other)
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
|
||||
@@ -110,9 +110,11 @@ func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
|
||||
}
|
||||
}
|
||||
|
||||
func getRenderingContext(r report.Report, n RenderableNode) (multiContainer bool, multiHost bool) {
|
||||
originHosts := make(map[string]struct{})
|
||||
originContainers := make(map[string]struct{})
|
||||
func getRenderingContext(r report.Report, n RenderableNode) (multiContainer, multiHost bool) {
|
||||
var (
|
||||
originHosts = map[string]struct{}{}
|
||||
originContainers = map[string]struct{}{}
|
||||
)
|
||||
for _, id := range n.Origins {
|
||||
for _, topology := range r.Topologies() {
|
||||
if nmd, ok := topology.NodeMetadatas[id]; ok {
|
||||
|
||||
@@ -186,6 +186,7 @@ var (
|
||||
LabelMinor: "1 process",
|
||||
Rank: "bash",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.NonContainerProcessNodeID,
|
||||
test.ServerHostNodeID,
|
||||
@@ -245,6 +246,7 @@ var (
|
||||
LabelMinor: test.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.NonContainerProcessNodeID,
|
||||
test.ServerHostNodeID,
|
||||
@@ -303,6 +305,7 @@ var (
|
||||
LabelMinor: test.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Adjacency: report.MakeIDList(),
|
||||
Origins: report.MakeIDList(
|
||||
test.NonContainerProcessNodeID,
|
||||
test.ServerHostNodeID,
|
||||
|
||||
@@ -16,26 +16,6 @@ type Renderer interface {
|
||||
// other renderers.
|
||||
type Reduce []Renderer
|
||||
|
||||
// Map is a Renderer which produces a set of RenderableNodes from the set of
|
||||
// RenderableNodes produced by another Renderer.
|
||||
type Map struct {
|
||||
MapFunc
|
||||
Renderer
|
||||
}
|
||||
|
||||
// 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
|
||||
Pseudo PseudoFunc
|
||||
}
|
||||
|
||||
// FilterUnconnected is a Renderer which filters out unconnected nodes.
|
||||
type FilterUnconnected struct {
|
||||
Renderer
|
||||
}
|
||||
|
||||
// MakeReduce is the only sane way to produce a Reduce Renderer.
|
||||
func MakeReduce(renderers ...Renderer) Renderer {
|
||||
return Reduce(renderers)
|
||||
@@ -54,11 +34,18 @@ func (r Reduce) Render(rpt report.Report) RenderableNodes {
|
||||
func (r Reduce) EdgeMetadata(rpt report.Report, localID, remoteID string) report.EdgeMetadata {
|
||||
metadata := report.EdgeMetadata{}
|
||||
for _, renderer := range r {
|
||||
metadata.Merge(renderer.EdgeMetadata(rpt, localID, remoteID))
|
||||
metadata = metadata.Merge(renderer.EdgeMetadata(rpt, localID, remoteID))
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// Map is a Renderer which produces a set of RenderableNodes from the set of
|
||||
// RenderableNodes produced by another Renderer.
|
||||
type Map struct {
|
||||
MapFunc
|
||||
Renderer
|
||||
}
|
||||
|
||||
// Render transforms a set of RenderableNodes produces by another Renderer.
|
||||
// using a map function
|
||||
func (m Map) Render(rpt report.Report) RenderableNodes {
|
||||
@@ -133,11 +120,19 @@ func (m Map) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableID st
|
||||
output := report.EdgeMetadata{}
|
||||
for _, edge := range oldEdges {
|
||||
metadata := m.Renderer.EdgeMetadata(rpt, edge.src, edge.dst)
|
||||
output.Merge(metadata)
|
||||
output = output.Merge(metadata)
|
||||
}
|
||||
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
|
||||
Pseudo PseudoFunc
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -234,8 +229,8 @@ 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.
|
||||
if md, ok := t.EdgeMetadatas[report.MakeEdgeID(srcNodeID, dstNodeID)]; ok {
|
||||
srcRenderableNode.EdgeMetadata.Merge(md)
|
||||
dstRenderableNode.EdgeMetadata.Merge(md)
|
||||
srcRenderableNode.EdgeMetadata = srcRenderableNode.EdgeMetadata.Merge(md)
|
||||
dstRenderableNode.EdgeMetadata = dstRenderableNode.EdgeMetadata.Merge(md)
|
||||
nodes[dstRenderableID] = dstRenderableNode
|
||||
}
|
||||
}
|
||||
@@ -268,12 +263,17 @@ func (m LeafMap) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableI
|
||||
dst = mapped.ID
|
||||
}
|
||||
if src == srcRenderableID && dst == dstRenderableID {
|
||||
metadata.Flatten(edgeMeta)
|
||||
metadata = metadata.Flatten(edgeMeta)
|
||||
}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// FilterUnconnected is a Renderer which filters out unconnected nodes.
|
||||
type FilterUnconnected struct {
|
||||
Renderer
|
||||
}
|
||||
|
||||
// Render produces a set of RenderableNodes given a Report
|
||||
func (f FilterUnconnected) Render(rpt report.Report) RenderableNodes {
|
||||
return OnlyConnected(f.Renderer.Render(rpt))
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
type mockRenderer struct {
|
||||
@@ -76,12 +77,12 @@ func TestMapRender2(t *testing.T) {
|
||||
"baz": {ID: "baz"},
|
||||
}},
|
||||
}
|
||||
want := render.RenderableNodes{
|
||||
want := sterilize(render.RenderableNodes{
|
||||
"bar": render.RenderableNode{ID: "bar"},
|
||||
}
|
||||
}, false)
|
||||
have := mapper.Render(report.MakeReport())
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %+v, have %+v", want, have)
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +130,8 @@ func TestMapEdge(t *testing.T) {
|
||||
}
|
||||
|
||||
mapper := render.Map{
|
||||
MapFunc: func(nodes render.RenderableNode) (render.RenderableNode, bool) {
|
||||
return render.RenderableNode{ID: "_" + nodes.ID}, true
|
||||
MapFunc: func(n render.RenderableNode) (render.RenderableNode, bool) {
|
||||
return render.RenderableNode{ID: "_" + n.ID}, true
|
||||
},
|
||||
Renderer: render.LeafMap{
|
||||
Selector: selector,
|
||||
@@ -143,7 +144,7 @@ func TestMapEdge(t *testing.T) {
|
||||
EgressPacketCount: newu64(1),
|
||||
EgressByteCount: newu64(2),
|
||||
}), mapper.EdgeMetadata(report.MakeReport(), "_foo", "_bar"); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %+v, have %+v", want, have)
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ type RenderableNode struct {
|
||||
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
|
||||
report.EdgeMetadata `json:"metadata"` // Numeric sums
|
||||
report.NodeMetadata `json:"XXXNODEMETADATA"` // merged NodeMetadata of the nodes used to build this // TODO ###
|
||||
}
|
||||
|
||||
// RenderableNodes is a set of RenderableNodes
|
||||
@@ -56,8 +56,8 @@ func (rn *RenderableNode) Merge(other RenderableNode) {
|
||||
rn.Adjacency = rn.Adjacency.Merge(other.Adjacency)
|
||||
rn.Origins = rn.Origins.Merge(other.Origins)
|
||||
|
||||
rn.EdgeMetadata.Merge(other.EdgeMetadata)
|
||||
rn.NodeMetadata.Merge(other.NodeMetadata)
|
||||
rn.EdgeMetadata = rn.EdgeMetadata.Merge(other.EdgeMetadata)
|
||||
rn.NodeMetadata = rn.NodeMetadata.Merge(other.NodeMetadata)
|
||||
}
|
||||
|
||||
// NewRenderableNode makes a new RenderableNode
|
||||
@@ -80,8 +80,8 @@ func newDerivedNode(id string, node RenderableNode) RenderableNode {
|
||||
LabelMinor: "",
|
||||
Rank: "",
|
||||
Pseudo: node.Pseudo,
|
||||
EdgeMetadata: node.EdgeMetadata,
|
||||
Origins: node.Origins,
|
||||
Origins: node.Origins.Copy(),
|
||||
EdgeMetadata: node.EdgeMetadata.Copy(),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,8 @@ func newDerivedPseudoNode(id, major string, node RenderableNode) RenderableNode
|
||||
LabelMinor: "",
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
EdgeMetadata: node.EdgeMetadata,
|
||||
Origins: node.Origins,
|
||||
Origins: node.Origins.Copy(),
|
||||
EdgeMetadata: node.EdgeMetadata.Copy(),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
func TestMergeRenderableNodes(t *testing.T) {
|
||||
@@ -17,19 +18,45 @@ func TestMergeRenderableNodes(t *testing.T) {
|
||||
"bar": render.RenderableNode{ID: "bar"},
|
||||
"baz": render.RenderableNode{ID: "baz"},
|
||||
}
|
||||
|
||||
want := render.RenderableNodes{
|
||||
want := sterilize(render.RenderableNodes{
|
||||
"foo": render.RenderableNode{ID: "foo"},
|
||||
"bar": render.RenderableNode{ID: "bar"},
|
||||
"baz": render.RenderableNode{ID: "baz"},
|
||||
}
|
||||
}, false)
|
||||
nodes1.Merge(nodes2)
|
||||
|
||||
if !reflect.DeepEqual(want, nodes1) {
|
||||
t.Errorf("want %+v, have %+v", want, nodes1)
|
||||
if have := sterilize(nodes1, false); !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func sterilize(r render.RenderableNodes, destructive bool) 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. So this function sterilizes invalid
|
||||
// RenderableNodes by fixing all nil Metadata fields. The proper fix
|
||||
// involves lots of annoying changes to instantiation.
|
||||
//
|
||||
// The extra destructive parameter is to support a historical test use
|
||||
// case where we explicitly don't compare node metadata.
|
||||
for id, n := range r {
|
||||
if n.Adjacency == nil {
|
||||
n.Adjacency = report.IDList{}
|
||||
}
|
||||
if destructive || n.NodeMetadata.Metadata == nil {
|
||||
n.NodeMetadata.Metadata = map[string]string{}
|
||||
}
|
||||
if destructive || n.NodeMetadata.Counters == nil {
|
||||
n.NodeMetadata.Counters = map[string]int{}
|
||||
}
|
||||
r[id] = n
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestMergeRenderableNode(t *testing.T) {
|
||||
node1 := render.RenderableNode{
|
||||
ID: "foo",
|
||||
@@ -49,19 +76,19 @@ func TestMergeRenderableNode(t *testing.T) {
|
||||
Adjacency: report.MakeIDList("a2"),
|
||||
Origins: report.MakeIDList("o2"),
|
||||
}
|
||||
|
||||
want := render.RenderableNode{
|
||||
ID: "foo",
|
||||
LabelMajor: "major",
|
||||
LabelMinor: "minor",
|
||||
Rank: "rank",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList("a1", "a2"),
|
||||
Origins: report.MakeIDList("o1", "o2"),
|
||||
ID: "foo",
|
||||
LabelMajor: "major",
|
||||
LabelMinor: "minor",
|
||||
Rank: "rank",
|
||||
Pseudo: false,
|
||||
Adjacency: report.MakeIDList("a1", "a2"),
|
||||
Origins: report.MakeIDList("o1", "o2"),
|
||||
NodeMetadata: report.MakeNodeMetadata(),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
}
|
||||
node1.Merge(node2)
|
||||
|
||||
if !reflect.DeepEqual(want, node1) {
|
||||
t.Errorf("want %+v, have %+v", want, node1)
|
||||
if have := node1; !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,16 @@ import (
|
||||
)
|
||||
|
||||
func TestReportLocalNetworks(t *testing.T) {
|
||||
r := report.MakeReport()
|
||||
r.Merge(report.Report{Host: report.Topology{NodeMetadatas: report.NodeMetadatas{
|
||||
"nonets": report.MakeNodeMetadata(),
|
||||
"foo": report.MakeNodeMetadataWith(map[string]string{host.LocalNetworks: "10.0.0.1/8 192.168.1.1/24 10.0.0.1/8 badnet/33"}),
|
||||
}}})
|
||||
r := report.MakeReport().Merge(report.Report{
|
||||
Host: report.Topology{
|
||||
NodeMetadatas: report.NodeMetadatas{
|
||||
"nonets": report.MakeNodeMetadata(),
|
||||
"foo": report.MakeNodeMetadataWith(map[string]string{
|
||||
host.LocalNetworks: "10.0.0.1/8 192.168.1.1/24 10.0.0.1/8 badnet/33",
|
||||
}),
|
||||
},
|
||||
},
|
||||
})
|
||||
want := report.Networks([]*net.IPNet{
|
||||
mustParseCIDR("10.0.0.1/8"),
|
||||
mustParseCIDR("192.168.1.1/24"),
|
||||
|
||||
@@ -6,55 +6,45 @@ import (
|
||||
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/render/expected"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
func trimNodeMetadata(rns render.RenderableNodes) render.RenderableNodes {
|
||||
result := render.RenderableNodes{}
|
||||
for id, rn := range rns {
|
||||
rn.NodeMetadata = report.MakeNodeMetadata()
|
||||
result[id] = rn
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TestProcessRenderer(t *testing.T) {
|
||||
have := render.ProcessRenderer.Render(test.Report)
|
||||
have = trimNodeMetadata(have)
|
||||
if !reflect.DeepEqual(expected.RenderedProcesses, have) {
|
||||
t.Error(test.Diff(expected.RenderedProcesses, have))
|
||||
have := sterilize(render.ProcessRenderer.Render(test.Report), true)
|
||||
want := expected.RenderedProcesses
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessNameRenderer(t *testing.T) {
|
||||
have := render.ProcessNameRenderer.Render(test.Report)
|
||||
have = trimNodeMetadata(have)
|
||||
if !reflect.DeepEqual(expected.RenderedProcessNames, have) {
|
||||
t.Error(test.Diff(expected.RenderedProcessNames, have))
|
||||
have := sterilize(render.ProcessNameRenderer.Render(test.Report), true)
|
||||
want := expected.RenderedProcessNames
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerRenderer(t *testing.T) {
|
||||
have := render.ContainerRenderer.Render(test.Report)
|
||||
have = trimNodeMetadata(have)
|
||||
if !reflect.DeepEqual(expected.RenderedContainers, have) {
|
||||
t.Error(test.Diff(expected.RenderedContainers, have))
|
||||
have := sterilize(render.ContainerRenderer.Render(test.Report), true)
|
||||
want := expected.RenderedContainers
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerImageRenderer(t *testing.T) {
|
||||
have := render.ContainerImageRenderer.Render(test.Report)
|
||||
have = trimNodeMetadata(have)
|
||||
if !reflect.DeepEqual(expected.RenderedContainerImages, have) {
|
||||
t.Error(test.Diff(expected.RenderedContainerImages, have))
|
||||
have := sterilize(render.ContainerImageRenderer.Render(test.Report), true)
|
||||
want := expected.RenderedContainerImages
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostRenderer(t *testing.T) {
|
||||
have := render.HostRenderer.Render(test.Report)
|
||||
have = trimNodeMetadata(have)
|
||||
if !reflect.DeepEqual(expected.RenderedHosts, have) {
|
||||
t.Error(test.Diff(expected.RenderedHosts, have))
|
||||
have := sterilize(render.HostRenderer.Render(test.Report), true)
|
||||
want := expected.RenderedHosts
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ type IDList []string
|
||||
|
||||
// MakeIDList makes a new IDList.
|
||||
func MakeIDList(ids ...string) IDList {
|
||||
if len(ids) <= 0 {
|
||||
return IDList{}
|
||||
}
|
||||
sort.Strings(ids)
|
||||
for i := 1; i < len(ids); { // shuffle down any duplicates
|
||||
if ids[i-1] == ids[i] {
|
||||
@@ -18,6 +21,15 @@ func MakeIDList(ids ...string) IDList {
|
||||
return IDList(ids)
|
||||
}
|
||||
|
||||
// Copy returns a copy of the IDList.
|
||||
func (a IDList) Copy() IDList {
|
||||
cp := make(IDList, len(a))
|
||||
for i, s := range a {
|
||||
cp[i] = s
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
// Add is the only correct way to add ids to an IDList.
|
||||
func (a IDList) Add(ids ...string) IDList {
|
||||
for _, s := range ids {
|
||||
|
||||
116
report/merge.go
116
report/merge.go
@@ -1,116 +0,0 @@
|
||||
package report
|
||||
|
||||
// Merge functions for all topology datatypes. The general semantics are that
|
||||
// the receiver is modified, and what's merged in isn't.
|
||||
|
||||
// Merge merges another Report into the receiver. Pass addWindows true if the
|
||||
// reports represent distinct (non-overlapping) periods of time.
|
||||
func (r *Report) Merge(other Report) {
|
||||
r.Endpoint.Merge(other.Endpoint)
|
||||
r.Address.Merge(other.Address)
|
||||
r.Process.Merge(other.Process)
|
||||
r.Container.Merge(other.Container)
|
||||
r.ContainerImage.Merge(other.ContainerImage)
|
||||
r.Host.Merge(other.Host)
|
||||
r.Overlay.Merge(other.Overlay)
|
||||
r.Sampling.Merge(other.Sampling)
|
||||
r.Window += other.Window
|
||||
}
|
||||
|
||||
// Merge merges another Topology into the receiver.
|
||||
func (t *Topology) Merge(other Topology) {
|
||||
t.Adjacency.Merge(other.Adjacency)
|
||||
t.EdgeMetadatas.Merge(other.EdgeMetadatas)
|
||||
t.NodeMetadatas.Merge(other.NodeMetadatas)
|
||||
}
|
||||
|
||||
// Merge merges another Adjacency list into the receiver.
|
||||
func (a *Adjacency) Merge(other Adjacency) {
|
||||
for addr, adj := range other {
|
||||
(*a)[addr] = (*a)[addr].Merge(adj)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges another NodeMetadatas into the receiver.
|
||||
func (m *NodeMetadatas) Merge(other NodeMetadatas) {
|
||||
for id, meta := range other {
|
||||
if _, ok := (*m)[id]; !ok {
|
||||
(*m)[id] = meta // not a copy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 is defined on the value-type, but node metadata map is
|
||||
// itself a reference type, so if you want to maintain immutability, use copy.
|
||||
func (nm NodeMetadata) Merge(other NodeMetadata) NodeMetadata {
|
||||
for k, v := range other.Metadata {
|
||||
nm.Metadata[k] = v // other takes precedence
|
||||
}
|
||||
for k, v := range other.Counters {
|
||||
nm.Counters[k] = nm.Counters[k] + v
|
||||
}
|
||||
return nm
|
||||
}
|
||||
|
||||
// Merge merges another EdgeMetadatas into the receiver. If other is from
|
||||
// another probe this is the union of both metadatas. Keys present in both are
|
||||
// summed.
|
||||
func (e *EdgeMetadatas) Merge(other EdgeMetadatas) {
|
||||
for id, edgemeta := range other {
|
||||
local := (*e)[id]
|
||||
local.Merge(edgemeta)
|
||||
(*e)[id] = local
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges another EdgeMetadata into the receiver. The two edge metadatas
|
||||
// should represent the same edge on different times.
|
||||
func (m *EdgeMetadata) Merge(other EdgeMetadata) {
|
||||
m.EgressPacketCount = merge(m.EgressPacketCount, other.EgressPacketCount, sum)
|
||||
m.IngressPacketCount = merge(m.IngressPacketCount, other.IngressPacketCount, sum)
|
||||
m.EgressByteCount = merge(m.EgressByteCount, other.EgressByteCount, sum)
|
||||
m.IngressByteCount = merge(m.IngressByteCount, other.IngressByteCount, sum)
|
||||
m.MaxConnCountTCP = merge(m.MaxConnCountTCP, other.MaxConnCountTCP, max)
|
||||
}
|
||||
|
||||
// Flatten sums two EdgeMetadatas. Their windows should be the same duration;
|
||||
// they should represent different edges at the same time.
|
||||
func (m *EdgeMetadata) Flatten(other EdgeMetadata) {
|
||||
m.EgressPacketCount = merge(m.EgressPacketCount, other.EgressPacketCount, sum)
|
||||
m.IngressPacketCount = merge(m.IngressPacketCount, other.IngressPacketCount, sum)
|
||||
m.EgressByteCount = merge(m.EgressByteCount, other.EgressByteCount, sum)
|
||||
m.IngressByteCount = merge(m.IngressByteCount, other.IngressByteCount, sum)
|
||||
// Note that summing of two maximums doesn't always give us the true
|
||||
// maximum. But it's a best effort.
|
||||
m.MaxConnCountTCP = merge(m.MaxConnCountTCP, other.MaxConnCountTCP, sum)
|
||||
}
|
||||
|
||||
// Merge combines two sampling structures via simple addition.
|
||||
func (s *Sampling) Merge(other Sampling) {
|
||||
s.Count += other.Count
|
||||
s.Total += other.Total
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -96,9 +97,7 @@ func TestMergeAdjacency(t *testing.T) {
|
||||
},
|
||||
},
|
||||
} {
|
||||
have := c.a
|
||||
have.Merge(c.b)
|
||||
if !reflect.DeepEqual(c.want, have) {
|
||||
if have := c.a.Merge(c.b); !reflect.DeepEqual(c.want, have) {
|
||||
t.Errorf("%s: want\n\t%#v\nhave\n\t%#v", name, c.want, have)
|
||||
}
|
||||
}
|
||||
@@ -192,15 +191,31 @@ func TestMergeEdgeMetadatas(t *testing.T) {
|
||||
},
|
||||
},
|
||||
} {
|
||||
have := c.a
|
||||
have.Merge(c.b)
|
||||
|
||||
if !reflect.DeepEqual(c.want, have) {
|
||||
t.Errorf("%s: want\n\t%#v, have\n\t%#v", name, c.want, have)
|
||||
if have := c.a.Merge(c.b); !reflect.DeepEqual(c.want, have) {
|
||||
t.Errorf("%s:\n%s", name, test.Diff(c.want, have))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlattenEdgeMetadata(t *testing.T) {
|
||||
have := (report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(1),
|
||||
MaxConnCountTCP: newu64(2),
|
||||
}).Flatten(report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(4),
|
||||
EgressByteCount: newu64(8),
|
||||
MaxConnCountTCP: newu64(16),
|
||||
})
|
||||
want := report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(1 + 4),
|
||||
EgressByteCount: newu64(8),
|
||||
MaxConnCountTCP: newu64(2 + 16), // flatten should sum MaxConnCountTCP
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeNodeMetadatas(t *testing.T) {
|
||||
for name, c := range map[string]struct {
|
||||
a, b, want report.NodeMetadatas
|
||||
@@ -291,10 +306,7 @@ func TestMergeNodeMetadatas(t *testing.T) {
|
||||
},
|
||||
},
|
||||
} {
|
||||
have := c.a
|
||||
have.Merge(c.b)
|
||||
|
||||
if !reflect.DeepEqual(c.want, have) {
|
||||
if have := c.a.Merge(c.b); !reflect.DeepEqual(c.want, have) {
|
||||
t.Errorf("%s: want\n\t%#v, have\n\t%#v", name, c.want, have)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,18 +58,49 @@ type Report struct {
|
||||
// MakeReport makes a clean report, ready to Merge() other reports into.
|
||||
func MakeReport() Report {
|
||||
return Report{
|
||||
Endpoint: NewTopology(),
|
||||
Address: NewTopology(),
|
||||
Process: NewTopology(),
|
||||
Container: NewTopology(),
|
||||
ContainerImage: NewTopology(),
|
||||
Host: NewTopology(),
|
||||
Overlay: NewTopology(),
|
||||
Endpoint: MakeTopology(),
|
||||
Address: MakeTopology(),
|
||||
Process: MakeTopology(),
|
||||
Container: MakeTopology(),
|
||||
ContainerImage: MakeTopology(),
|
||||
Host: MakeTopology(),
|
||||
Overlay: MakeTopology(),
|
||||
Sampling: Sampling{},
|
||||
Window: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Copy returns a value copy of the report.
|
||||
func (r Report) Copy() Report {
|
||||
return Report{
|
||||
Endpoint: r.Endpoint.Copy(),
|
||||
Address: r.Address.Copy(),
|
||||
Process: r.Process.Copy(),
|
||||
Container: r.Container.Copy(),
|
||||
ContainerImage: r.ContainerImage.Copy(),
|
||||
Host: r.Host.Copy(),
|
||||
Overlay: r.Overlay.Copy(),
|
||||
Sampling: r.Sampling,
|
||||
Window: r.Window,
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges another Report into the receiver and returns the result. The
|
||||
// original is not modified.
|
||||
func (r Report) Merge(other Report) Report {
|
||||
cp := r.Copy()
|
||||
cp.Endpoint = r.Endpoint.Merge(other.Endpoint)
|
||||
cp.Address = r.Address.Merge(other.Address)
|
||||
cp.Process = r.Process.Merge(other.Process)
|
||||
cp.Container = r.Container.Merge(other.Container)
|
||||
cp.ContainerImage = r.ContainerImage.Merge(other.ContainerImage)
|
||||
cp.Host = r.Host.Merge(other.Host)
|
||||
cp.Overlay = r.Overlay.Merge(other.Overlay)
|
||||
cp.Sampling = r.Sampling.Merge(other.Sampling)
|
||||
cp.Window += other.Window
|
||||
return cp
|
||||
}
|
||||
|
||||
// Topologies returns a slice of Topologies in this report
|
||||
func (r Report) Topologies() []Topology {
|
||||
return []Topology{
|
||||
@@ -118,6 +149,15 @@ func (s Sampling) Rate() float64 {
|
||||
return float64(s.Count) / float64(s.Total)
|
||||
}
|
||||
|
||||
// Merge combines two sampling structures via simple addition and returns the
|
||||
// result. The original is not modified.
|
||||
func (s Sampling) Merge(other Sampling) Sampling {
|
||||
return Sampling{
|
||||
Count: s.Count + other.Count,
|
||||
Total: s.Total + other.Total,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// HostNodeID is a metadata foreign key, linking a node in any topology to
|
||||
// a node in the host topology. That host node is the origin host, where
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
func TestReportTopologies(t *testing.T) {
|
||||
var (
|
||||
reportType = reflect.TypeOf(report.MakeReport())
|
||||
topologyType = reflect.TypeOf(report.NewTopology())
|
||||
topologyType = reflect.TypeOf(report.MakeTopology())
|
||||
)
|
||||
|
||||
var want int
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const localUnknown = "localUnknown"
|
||||
|
||||
// Topology describes a specific view of a network. It consists of nodes and
|
||||
// edges, represented by Adjacency, and metadata about those nodes and edges,
|
||||
// represented by EdgeMetadatas and NodeMetadatas respectively.
|
||||
@@ -16,18 +14,105 @@ type Topology struct {
|
||||
NodeMetadatas
|
||||
}
|
||||
|
||||
// Adjacency is an adjacency-list encoding of the topology. Keys are node IDs,
|
||||
// as produced by the relevant MappingFunc for the topology.
|
||||
// MakeTopology gives you a Topology.
|
||||
func MakeTopology() Topology {
|
||||
return Topology{
|
||||
Adjacency: map[string]IDList{},
|
||||
EdgeMetadatas: map[string]EdgeMetadata{},
|
||||
NodeMetadatas: map[string]NodeMetadata{},
|
||||
}
|
||||
}
|
||||
|
||||
// Copy returns a value copy of the Topology.
|
||||
func (t Topology) Copy() Topology {
|
||||
return Topology{
|
||||
Adjacency: t.Adjacency.Copy(),
|
||||
EdgeMetadatas: t.EdgeMetadatas.Copy(),
|
||||
NodeMetadatas: t.NodeMetadatas.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges the other object into this one, and returns the result object.
|
||||
// The original is not modified.
|
||||
func (t Topology) Merge(other Topology) Topology {
|
||||
return Topology{
|
||||
Adjacency: t.Adjacency.Merge(other.Adjacency),
|
||||
EdgeMetadatas: t.EdgeMetadatas.Merge(other.EdgeMetadatas),
|
||||
NodeMetadatas: t.NodeMetadatas.Merge(other.NodeMetadatas),
|
||||
}
|
||||
}
|
||||
|
||||
// Adjacency is an adjacency-list encoding of the topology. Keys are adjacency
|
||||
// IDs, values are lists of node IDs.
|
||||
type Adjacency map[string]IDList
|
||||
|
||||
// Copy returns a value copy of the adjacency.
|
||||
func (a Adjacency) Copy() Adjacency {
|
||||
cp := make(Adjacency, len(a))
|
||||
for k, v := range a {
|
||||
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 (a Adjacency) Merge(other Adjacency) Adjacency {
|
||||
cp := a.Copy()
|
||||
for k, v := range other {
|
||||
cp[k] = cp[k].Merge(v)
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Copy returns a value copy of the NodeMetadatas.
|
||||
func (n NodeMetadatas) Copy() NodeMetadatas {
|
||||
cp := make(NodeMetadatas, len(n))
|
||||
for k, v := range n {
|
||||
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 (n NodeMetadatas) Merge(other NodeMetadatas) NodeMetadatas {
|
||||
cp := n.Copy()
|
||||
for k, v := range other {
|
||||
if _, ok := cp[k]; !ok { // don't overwrite
|
||||
cp[k] = v.Copy()
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@@ -38,6 +123,53 @@ type EdgeMetadata struct {
|
||||
MaxConnCountTCP *uint64 `json:"max_conn_count_tcp,omitempty"`
|
||||
}
|
||||
|
||||
// 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),
|
||||
MaxConnCountTCP: cpu64ptr(e.MaxConnCountTCP),
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
cp.MaxConnCountTCP = merge(cp.MaxConnCountTCP, other.MaxConnCountTCP, max)
|
||||
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)
|
||||
// Note that summing of two maximums doesn't always give us the true
|
||||
// maximum. But it's a best effort.
|
||||
cp.MaxConnCountTCP = merge(cp.MaxConnCountTCP, other.MaxConnCountTCP, sum)
|
||||
return cp
|
||||
}
|
||||
|
||||
// NodeMetadata describes a superset of the metadata that probes can collect
|
||||
// about a given node in a given topology.
|
||||
type NodeMetadata struct {
|
||||
@@ -58,25 +190,30 @@ func MakeNodeMetadataWith(m map[string]string) NodeMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy returns a value copy, useful for tests.
|
||||
func (nm NodeMetadata) Copy() NodeMetadata {
|
||||
// Copy returns a value copy of the NodeMetadata.
|
||||
func (n NodeMetadata) Copy() NodeMetadata {
|
||||
cp := MakeNodeMetadata()
|
||||
for k, v := range nm.Metadata {
|
||||
for k, v := range n.Metadata {
|
||||
cp.Metadata[k] = v
|
||||
}
|
||||
for k, v := range nm.Counters {
|
||||
for k, v := range n.Counters {
|
||||
cp.Counters[k] = v
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
// NewTopology gives you a Topology.
|
||||
func NewTopology() Topology {
|
||||
return Topology{
|
||||
Adjacency: map[string]IDList{},
|
||||
EdgeMetadatas: map[string]EdgeMetadata{},
|
||||
NodeMetadatas: map[string]NodeMetadata{},
|
||||
// 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
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
// Validate checks the topology for various inconsistencies.
|
||||
@@ -140,3 +277,25 @@ func (t Topology) Validate() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -52,11 +52,11 @@ func (c *Collector) Report() report.Report {
|
||||
|
||||
c.reports = clean(c.reports, c.window)
|
||||
|
||||
report := report.MakeReport()
|
||||
rpt := report.MakeReport()
|
||||
for _, tr := range c.reports {
|
||||
report.Merge(tr.report)
|
||||
rpt = rpt.Merge(tr.report)
|
||||
}
|
||||
return report
|
||||
return rpt
|
||||
}
|
||||
|
||||
type timestampReport struct {
|
||||
|
||||
@@ -32,9 +32,10 @@ func TestCollector(t *testing.T) {
|
||||
}
|
||||
|
||||
c.Add(r2)
|
||||
|
||||
merged := report.MakeReport()
|
||||
merged.Merge(r1)
|
||||
merged.Merge(r2)
|
||||
merged = merged.Merge(r1)
|
||||
merged = merged.Merge(r2)
|
||||
if want, have := merged, c.Report(); !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user