mirror of
https://github.com/weaveworks/scope.git
synced 2026-08-18 03:46:45 +00:00
Adding support for plugins, with basic example of iowait, and ebpf
Squash of: * Include plugins in the report * show plugin list in the UI * moving metric and metadata templates into the probe reports * update js for prime -> priority * added retry to plugin handshake * added iowait plugin * review feedback * plugin documentation
This commit is contained in:
@@ -65,7 +65,7 @@ func (row connection) ID() string {
|
||||
return fmt.Sprintf("%s:%s-%s:%s-%s", row.remoteNode.ID, row.remoteAddr, row.localNode.ID, row.localAddr, row.port)
|
||||
}
|
||||
|
||||
func incomingConnectionsSummary(topologyID string, n report.Node, ns report.Nodes) ConnectionsSummary {
|
||||
func incomingConnectionsSummary(topologyID string, r report.Report, n report.Node, ns report.Nodes) ConnectionsSummary {
|
||||
localEndpointIDs := endpointChildIDsOf(n)
|
||||
|
||||
// For each node which has an edge TO me
|
||||
@@ -110,11 +110,11 @@ func incomingConnectionsSummary(topologyID string, n report.Node, ns report.Node
|
||||
TopologyID: topologyID,
|
||||
Label: "Inbound",
|
||||
Columns: columnHeaders,
|
||||
Connections: connectionRows(counts, isInternetNode(n)),
|
||||
Connections: connectionRows(r, counts, isInternetNode(n)),
|
||||
}
|
||||
}
|
||||
|
||||
func outgoingConnectionsSummary(topologyID string, n report.Node, ns report.Nodes) ConnectionsSummary {
|
||||
func outgoingConnectionsSummary(topologyID string, r report.Report, n report.Node, ns report.Nodes) ConnectionsSummary {
|
||||
localEndpoints := endpointChildrenOf(n)
|
||||
|
||||
// For each node which has an edge FROM me
|
||||
@@ -160,7 +160,7 @@ func outgoingConnectionsSummary(topologyID string, n report.Node, ns report.Node
|
||||
TopologyID: topologyID,
|
||||
Label: "Outbound",
|
||||
Columns: columnHeaders,
|
||||
Connections: connectionRows(counts, isInternetNode(n)),
|
||||
Connections: connectionRows(r, counts, isInternetNode(n)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,13 +188,13 @@ func isInternetNode(n report.Node) bool {
|
||||
return n.ID == render.IncomingInternetID || n.ID == render.OutgoingInternetID
|
||||
}
|
||||
|
||||
func connectionRows(in map[connection]int, includeLocal bool) []Connection {
|
||||
func connectionRows(r report.Report, in map[connection]int, includeLocal bool) []Connection {
|
||||
output := []Connection{}
|
||||
for row, count := range in {
|
||||
// Use MakeNodeSummary to render the id and label of this node
|
||||
// TODO(paulbellamy): Would be cleaner if we hade just a
|
||||
// MakeNodeID(*row.remoteode). As we don't need the whole summary.
|
||||
summary, ok := MakeNodeSummary(*row.remoteNode)
|
||||
summary, ok := MakeNodeSummary(r, *row.remoteNode)
|
||||
connection := Connection{
|
||||
ID: row.ID(),
|
||||
NodeID: summary.ID,
|
||||
@@ -207,19 +207,19 @@ func connectionRows(in map[connection]int, includeLocal bool) []Connection {
|
||||
}
|
||||
if includeLocal {
|
||||
connection.Metadata = append(connection.Metadata,
|
||||
MetadataRow{
|
||||
report.MetadataRow{
|
||||
ID: "foo",
|
||||
Value: row.localAddr,
|
||||
Datatype: number,
|
||||
})
|
||||
}
|
||||
connection.Metadata = append(connection.Metadata,
|
||||
MetadataRow{
|
||||
report.MetadataRow{
|
||||
ID: portKey,
|
||||
Value: row.port,
|
||||
Datatype: number,
|
||||
},
|
||||
MetadataRow{
|
||||
report.MetadataRow{
|
||||
ID: countKey,
|
||||
Value: strconv.Itoa(count),
|
||||
Datatype: number,
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
// NodeDockerLabels produces a table (to be consumed directly by the UI) based
|
||||
// on an origin ID, which is (optimistically) a node ID in one of our
|
||||
// topologies.
|
||||
func NodeDockerLabels(nmd report.Node) []MetadataRow {
|
||||
func NodeDockerLabels(nmd report.Node) []report.MetadataRow {
|
||||
if _, ok := nmd.Counters.Lookup(nmd.Topology); ok {
|
||||
// This is a group of nodes, so no docker labels!
|
||||
return nil
|
||||
@@ -20,7 +20,7 @@ func NodeDockerLabels(nmd report.Node) []MetadataRow {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rows []MetadataRow
|
||||
var rows []report.MetadataRow
|
||||
// Add labels in alphabetical order
|
||||
labels := docker.ExtractLabels(nmd)
|
||||
labelKeys := make([]string, 0, len(labels))
|
||||
@@ -29,7 +29,7 @@ func NodeDockerLabels(nmd report.Node) []MetadataRow {
|
||||
}
|
||||
sort.Strings(labelKeys)
|
||||
for _, labelKey := range labelKeys {
|
||||
rows = append(rows, MetadataRow{ID: "label_" + labelKey, Value: labels[labelKey]})
|
||||
rows = append(rows, report.MetadataRow{ID: "label_" + labelKey, Value: labels[labelKey]})
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestNodeDockerLabels(t *testing.T) {
|
||||
inputs := []struct {
|
||||
name string
|
||||
node report.Node
|
||||
want []detailed.MetadataRow
|
||||
want []report.MetadataRow
|
||||
}{
|
||||
{
|
||||
name: "container",
|
||||
@@ -26,7 +26,7 @@ func TestNodeDockerLabels(t *testing.T) {
|
||||
}).WithTopology(report.Container).WithSets(report.EmptySets.
|
||||
Add(docker.ContainerIPs, report.MakeStringSet("10.10.10.0/24", "10.10.10.1/24")),
|
||||
),
|
||||
want: []detailed.MetadataRow{
|
||||
want: []report.MetadataRow{
|
||||
{
|
||||
ID: "label_label1",
|
||||
Value: "label1value",
|
||||
|
||||
+4
-175
@@ -1,190 +1,19 @@
|
||||
package detailed
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ugorji/go/codec"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/probe/kubernetes"
|
||||
"github.com/weaveworks/scope/probe/overlay"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
var (
|
||||
processNodeMetadata = []MetadataRowTemplate{
|
||||
Latest{ID: process.PID, Prime: true, Datatype: number},
|
||||
Latest{ID: process.Cmdline, Prime: true},
|
||||
Latest{ID: process.PPID, Prime: true},
|
||||
Latest{ID: process.Threads, Prime: true},
|
||||
}
|
||||
containerNodeMetadata = []MetadataRowTemplate{
|
||||
Latest{ID: docker.ContainerID, Truncate: 12, Prime: true},
|
||||
Latest{ID: docker.ContainerStateHuman, Prime: true},
|
||||
Latest{ID: docker.ContainerCommand, Prime: true},
|
||||
Latest{ID: docker.ImageID, Truncate: 12},
|
||||
Latest{ID: docker.ContainerUptime},
|
||||
Latest{ID: docker.ContainerRestartCount},
|
||||
Set{ID: docker.ContainerIPs},
|
||||
Set{ID: docker.ContainerPorts},
|
||||
Latest{ID: docker.ContainerCreated},
|
||||
Latest{ID: overlay.WeaveMACAddress},
|
||||
Latest{ID: overlay.WeaveDNSHostname},
|
||||
}
|
||||
containerImageNodeMetadata = []MetadataRowTemplate{
|
||||
Latest{ID: docker.ImageID, Truncate: 12, Prime: true},
|
||||
Counter{ID: report.Container, Prime: true},
|
||||
}
|
||||
podNodeMetadata = []MetadataRowTemplate{
|
||||
Latest{ID: kubernetes.PodID, Prime: true},
|
||||
Latest{ID: kubernetes.Namespace, Prime: true},
|
||||
Latest{ID: kubernetes.PodCreated, Prime: true},
|
||||
}
|
||||
hostNodeMetadata = []MetadataRowTemplate{
|
||||
Latest{ID: host.KernelVersion, Prime: true},
|
||||
Latest{ID: host.Uptime, Prime: true},
|
||||
Latest{ID: host.HostName},
|
||||
Latest{ID: host.OS},
|
||||
Set{ID: host.LocalNetworks},
|
||||
}
|
||||
)
|
||||
|
||||
// MetadataRowTemplate extracts some metadata rows from a node
|
||||
type MetadataRowTemplate interface {
|
||||
MetadataRows(report.Node) []MetadataRow
|
||||
}
|
||||
|
||||
// Latest extracts some metadata rows from a node's Latest
|
||||
type Latest struct {
|
||||
ID string
|
||||
Truncate int // If > 0, truncate the value to this length.
|
||||
Prime bool // Whether the row should be shown by default
|
||||
Datatype string
|
||||
}
|
||||
|
||||
// MetadataRows implements MetadataRowTemplate
|
||||
func (l Latest) MetadataRows(n report.Node) []MetadataRow {
|
||||
if val, ok := n.Latest.Lookup(l.ID); ok {
|
||||
if l.Truncate > 0 && len(val) > l.Truncate {
|
||||
val = val[:l.Truncate]
|
||||
}
|
||||
return []MetadataRow{{ID: l.ID, Value: val, Prime: l.Prime, Datatype: l.Datatype}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set extracts some metadata rows from a node's Sets
|
||||
type Set struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
// MetadataRows implements MetadataRowTemplate
|
||||
func (s Set) MetadataRows(n report.Node) []MetadataRow {
|
||||
if val, ok := n.Sets.Lookup(s.ID); ok && len(val) > 0 {
|
||||
return []MetadataRow{{ID: s.ID, Value: strings.Join(val, ", ")}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Counter extracts some metadata rows from a node's Counters
|
||||
type Counter struct {
|
||||
ID string
|
||||
Prime bool
|
||||
}
|
||||
|
||||
// MetadataRows implements MetadataRowTemplate
|
||||
func (c Counter) MetadataRows(n report.Node) []MetadataRow {
|
||||
if val, ok := n.Counters.Lookup(c.ID); ok {
|
||||
return []MetadataRow{{
|
||||
ID: c.ID,
|
||||
Value: strconv.Itoa(val),
|
||||
Prime: c.Prime,
|
||||
Datatype: number,
|
||||
}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MetadataRow is a row for the metadata table.
|
||||
type MetadataRow struct {
|
||||
ID string
|
||||
Value string
|
||||
Prime bool
|
||||
Datatype string
|
||||
}
|
||||
|
||||
// Copy returns a value copy of a metadata row.
|
||||
func (m MetadataRow) Copy() MetadataRow {
|
||||
return m
|
||||
}
|
||||
|
||||
// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
|
||||
func (MetadataRow) MarshalJSON() ([]byte, error) {
|
||||
panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
|
||||
}
|
||||
|
||||
// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
|
||||
func (*MetadataRow) UnmarshalJSON(b []byte) error {
|
||||
panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
|
||||
}
|
||||
|
||||
type labelledMetadataRow struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
Prime bool `json:"prime,omitempty"`
|
||||
Datatype string `json:"dataType,omitempty"`
|
||||
}
|
||||
|
||||
// CodecEncodeSelf marshals this MetadataRow. It adds a label before
|
||||
// rendering.
|
||||
func (m *MetadataRow) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
in := labelledMetadataRow{
|
||||
ID: m.ID,
|
||||
Label: Label(m.ID),
|
||||
Value: m.Value,
|
||||
Prime: m.Prime,
|
||||
Datatype: m.Datatype,
|
||||
}
|
||||
encoder.Encode(in)
|
||||
}
|
||||
|
||||
// CodecDecodeSelf implements codec.Selfer
|
||||
func (m *MetadataRow) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
var in labelledMetadataRow
|
||||
decoder.Decode(&in)
|
||||
*m = MetadataRow{
|
||||
ID: in.ID,
|
||||
Value: in.Value,
|
||||
Prime: in.Prime,
|
||||
Datatype: in.Datatype,
|
||||
}
|
||||
}
|
||||
|
||||
// NodeMetadata produces a table (to be consumed directly by the UI) based on
|
||||
// an origin ID, which is (optimistically) a node ID in one of our topologies.
|
||||
func NodeMetadata(n report.Node) []MetadataRow {
|
||||
// an a report.Node, which is (hopefully) a node in one of our topologies.
|
||||
func NodeMetadata(r report.Report, n report.Node) []report.MetadataRow {
|
||||
if _, ok := n.Counters.Lookup(n.Topology); ok {
|
||||
// This is a group of nodes, so no metadata!
|
||||
return nil
|
||||
}
|
||||
|
||||
renderers := map[string][]MetadataRowTemplate{
|
||||
report.Process: processNodeMetadata,
|
||||
report.Container: containerNodeMetadata,
|
||||
report.ContainerImage: containerImageNodeMetadata,
|
||||
report.Pod: podNodeMetadata,
|
||||
report.Host: hostNodeMetadata,
|
||||
}
|
||||
if templates, ok := renderers[n.Topology]; ok {
|
||||
rows := []MetadataRow{}
|
||||
for _, template := range templates {
|
||||
rows = append(rows, template.MetadataRows(n)...)
|
||||
}
|
||||
return rows
|
||||
if topology, ok := r.Topology(n.Topology); ok {
|
||||
return topology.MetadataTemplates.MetadataRows(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestNodeMetadata(t *testing.T) {
|
||||
inputs := []struct {
|
||||
name string
|
||||
node report.Node
|
||||
want []detailed.MetadataRow
|
||||
want []report.MetadataRow
|
||||
}{
|
||||
{
|
||||
name: "container",
|
||||
@@ -26,10 +26,10 @@ func TestNodeMetadata(t *testing.T) {
|
||||
}).WithTopology(report.Container).WithSets(report.EmptySets.
|
||||
Add(docker.ContainerIPs, report.MakeStringSet("10.10.10.0/24", "10.10.10.1/24")),
|
||||
),
|
||||
want: []detailed.MetadataRow{
|
||||
{ID: docker.ContainerID, Value: fixture.ClientContainerID, Prime: true},
|
||||
{ID: docker.ContainerStateHuman, Value: "running", Prime: true},
|
||||
{ID: docker.ContainerIPs, Value: "10.10.10.0/24, 10.10.10.1/24"},
|
||||
want: []report.MetadataRow{
|
||||
{ID: docker.ContainerID, Label: "ID", Value: fixture.ClientContainerID, Priority: 1},
|
||||
{ID: docker.ContainerStateHuman, Label: "State", Value: "running", Priority: 2},
|
||||
{ID: docker.ContainerIPs, Label: "IPs", Value: "10.10.10.0/24, 10.10.10.1/24", Priority: 14},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -41,7 +41,7 @@ func TestNodeMetadata(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, input := range inputs {
|
||||
have := detailed.NodeMetadata(input.node)
|
||||
have := detailed.NodeMetadata(fixture.Report, input.node)
|
||||
if !reflect.DeepEqual(input.want, have) {
|
||||
t.Errorf("%s: %s", input.name, test.Diff(input.want, have))
|
||||
}
|
||||
@@ -50,10 +50,10 @@ func TestNodeMetadata(t *testing.T) {
|
||||
|
||||
func TestMetadataRowCopy(t *testing.T) {
|
||||
var (
|
||||
row = detailed.MetadataRow{
|
||||
row = report.MetadataRow{
|
||||
ID: "id",
|
||||
Value: "value",
|
||||
Prime: true,
|
||||
Priority: 1,
|
||||
Datatype: "datatype",
|
||||
}
|
||||
cp = row.Copy()
|
||||
@@ -67,9 +67,9 @@ func TestMetadataRowCopy(t *testing.T) {
|
||||
// changing the copy should not change the original
|
||||
cp.ID = ""
|
||||
cp.Value = ""
|
||||
cp.Prime = false
|
||||
cp.Priority = 2
|
||||
cp.Datatype = ""
|
||||
if row.ID != "id" || row.Value != "value" || row.Prime != true || row.Datatype != "datatype" {
|
||||
if row.ID != "id" || row.Value != "value" || row.Priority != 1 || row.Datatype != "datatype" {
|
||||
t.Errorf("Expected changing the copy not to modify the original")
|
||||
}
|
||||
}
|
||||
|
||||
+5
-162
@@ -1,177 +1,20 @@
|
||||
package detailed
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/ugorji/go/codec"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultFormat = ""
|
||||
filesizeFormat = "filesize"
|
||||
integerFormat = "integer"
|
||||
percentFormat = "percent"
|
||||
)
|
||||
|
||||
var (
|
||||
processNodeMetrics = []MetricRow{
|
||||
{ID: process.CPUUsage, Format: percentFormat},
|
||||
{ID: process.MemoryUsage, Format: filesizeFormat},
|
||||
{ID: process.OpenFilesCount, Format: integerFormat},
|
||||
}
|
||||
containerNodeMetrics = []MetricRow{
|
||||
{ID: docker.CPUTotalUsage, Format: percentFormat},
|
||||
{ID: docker.MemoryUsage, Format: filesizeFormat},
|
||||
}
|
||||
hostNodeMetrics = []MetricRow{
|
||||
{ID: host.CPUUsage, Format: percentFormat},
|
||||
{ID: host.MemoryUsage, Format: filesizeFormat},
|
||||
{ID: host.Load1, Format: defaultFormat, Group: "load"},
|
||||
}
|
||||
)
|
||||
|
||||
// MetricRow is a tuple of data used to render a metric as a sparkline and
|
||||
// accoutrements.
|
||||
type MetricRow struct {
|
||||
ID string
|
||||
Format string
|
||||
Group string
|
||||
Value float64
|
||||
Metric *report.Metric
|
||||
}
|
||||
|
||||
// Summary returns a copy of the MetricRow, without the samples, just the value if there is one.
|
||||
func (m MetricRow) Summary() MetricRow {
|
||||
row := MetricRow{
|
||||
ID: m.ID,
|
||||
Format: m.Format,
|
||||
Group: m.Group,
|
||||
Value: m.Value,
|
||||
}
|
||||
if m.Metric != nil {
|
||||
var metric = m.Metric.Copy()
|
||||
metric.Samples = nil
|
||||
row.Metric = &metric
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
// Copy returns a value copy of the MetricRow
|
||||
func (m MetricRow) Copy() MetricRow {
|
||||
row := MetricRow{
|
||||
ID: m.ID,
|
||||
Format: m.Format,
|
||||
Group: m.Group,
|
||||
Value: m.Value,
|
||||
}
|
||||
if m.Metric != nil {
|
||||
var metric = m.Metric.Copy()
|
||||
row.Metric = &metric
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
|
||||
func (MetricRow) MarshalJSON() ([]byte, error) {
|
||||
panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
|
||||
}
|
||||
|
||||
// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
|
||||
func (*MetricRow) UnmarshalJSON(b []byte) error {
|
||||
panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
|
||||
}
|
||||
|
||||
type wiredMetricRow struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Group string `json:"group,omitempty"`
|
||||
Value float64 `json:"value"`
|
||||
Samples []report.Sample `json:"samples"`
|
||||
Min float64 `json:"min"`
|
||||
Max float64 `json:"max"`
|
||||
First string `json:"first,omitempty"`
|
||||
Last string `json:"last,omitempty"`
|
||||
}
|
||||
|
||||
// CodecEncodeSelf marshals this MetricRow. It takes the basic Metric
|
||||
// rendering, then adds some row-specific fields.
|
||||
func (m *MetricRow) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
in := m.Metric.ToIntermediate()
|
||||
encoder.Encode(wiredMetricRow{
|
||||
ID: m.ID,
|
||||
Label: Label(m.ID),
|
||||
Format: m.Format,
|
||||
Group: m.Group,
|
||||
Value: m.Value,
|
||||
Samples: in.Samples,
|
||||
Min: in.Min,
|
||||
Max: in.Max,
|
||||
First: in.First,
|
||||
Last: in.Last,
|
||||
})
|
||||
}
|
||||
|
||||
// CodecDecodeSelf implements codec.Selfer
|
||||
func (m *MetricRow) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
var in wiredMetricRow
|
||||
decoder.Decode(&in)
|
||||
w := report.WireMetrics{
|
||||
Samples: in.Samples,
|
||||
Min: in.Min,
|
||||
Max: in.Max,
|
||||
First: in.First,
|
||||
Last: in.Last,
|
||||
}
|
||||
metric := w.FromIntermediate()
|
||||
*m = MetricRow{
|
||||
ID: in.ID,
|
||||
Format: in.Format,
|
||||
Group: in.Group,
|
||||
Value: in.Value,
|
||||
Metric: &metric,
|
||||
}
|
||||
}
|
||||
|
||||
// NodeMetrics produces a table (to be consumed directly by the UI) based on
|
||||
// an a report.Node, which is (hopefully) a node in one of our topologies.
|
||||
func NodeMetrics(n report.Node) []MetricRow {
|
||||
func NodeMetrics(r report.Report, n report.Node) []report.MetricRow {
|
||||
if _, ok := n.Counters.Lookup(n.Topology); ok {
|
||||
// This is a group of nodes, so no metrics!
|
||||
return nil
|
||||
}
|
||||
|
||||
renderers := map[string][]MetricRow{
|
||||
report.Process: processNodeMetrics,
|
||||
report.Container: containerNodeMetrics,
|
||||
report.Host: hostNodeMetrics,
|
||||
topology, ok := r.Topology(n.Topology)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if templates, ok := renderers[n.Topology]; ok {
|
||||
rows := []MetricRow{}
|
||||
for _, template := range templates {
|
||||
metric, ok := n.Metrics[template.ID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
t := template.Copy()
|
||||
if s := metric.LastSample(); s != nil {
|
||||
t.Value = toFixed(s.Value, 2)
|
||||
}
|
||||
t.Metric = &metric
|
||||
rows = append(rows, t)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// toFixed truncates decimals of float64 down to specified precision
|
||||
func toFixed(num float64, precision int) float64 {
|
||||
output := math.Pow(10, float64(precision))
|
||||
return float64(int64(num*output)) / output
|
||||
return topology.MetricTemplates.MetricRows(n)
|
||||
}
|
||||
|
||||
@@ -18,71 +18,85 @@ func TestNodeMetrics(t *testing.T) {
|
||||
inputs := []struct {
|
||||
name string
|
||||
node report.Node
|
||||
want []detailed.MetricRow
|
||||
want []report.MetricRow
|
||||
}{
|
||||
{
|
||||
name: "process",
|
||||
node: fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
want: []detailed.MetricRow{
|
||||
want: []report.MetricRow{
|
||||
{
|
||||
ID: process.CPUUsage,
|
||||
Format: "percent",
|
||||
Group: "",
|
||||
Value: 0.01,
|
||||
Metric: &fixture.ClientProcess1CPUMetric,
|
||||
ID: process.CPUUsage,
|
||||
Label: "CPU",
|
||||
Format: "percent",
|
||||
Group: "",
|
||||
Value: 0.01,
|
||||
Priority: 1,
|
||||
Metric: &fixture.ClientProcess1CPUMetric,
|
||||
},
|
||||
{
|
||||
ID: process.MemoryUsage,
|
||||
Format: "filesize",
|
||||
Group: "",
|
||||
Value: 0.02,
|
||||
Metric: &fixture.ClientProcess1MemoryMetric,
|
||||
ID: process.MemoryUsage,
|
||||
Label: "Memory",
|
||||
Format: "filesize",
|
||||
Group: "",
|
||||
Value: 0.02,
|
||||
Priority: 2,
|
||||
Metric: &fixture.ClientProcess1MemoryMetric,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "container",
|
||||
node: fixture.Report.Container.Nodes[fixture.ClientContainerNodeID],
|
||||
want: []detailed.MetricRow{
|
||||
want: []report.MetricRow{
|
||||
{
|
||||
ID: docker.CPUTotalUsage,
|
||||
Format: "percent",
|
||||
Group: "",
|
||||
Value: 0.03,
|
||||
Metric: &fixture.ClientContainerCPUMetric,
|
||||
ID: docker.CPUTotalUsage,
|
||||
Label: "CPU",
|
||||
Format: "percent",
|
||||
Group: "",
|
||||
Value: 0.03,
|
||||
Priority: 1,
|
||||
Metric: &fixture.ClientContainerCPUMetric,
|
||||
},
|
||||
{
|
||||
ID: docker.MemoryUsage,
|
||||
Format: "filesize",
|
||||
Group: "",
|
||||
Value: 0.04,
|
||||
Metric: &fixture.ClientContainerMemoryMetric,
|
||||
ID: docker.MemoryUsage,
|
||||
Label: "Memory",
|
||||
Format: "filesize",
|
||||
Group: "",
|
||||
Value: 0.04,
|
||||
Priority: 2,
|
||||
Metric: &fixture.ClientContainerMemoryMetric,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "host",
|
||||
node: fixture.Report.Host.Nodes[fixture.ClientHostNodeID],
|
||||
want: []detailed.MetricRow{
|
||||
want: []report.MetricRow{
|
||||
{
|
||||
ID: host.CPUUsage,
|
||||
Format: "percent",
|
||||
Group: "",
|
||||
Value: 0.07,
|
||||
Metric: &fixture.ClientHostCPUMetric,
|
||||
ID: host.CPUUsage,
|
||||
Label: "CPU",
|
||||
Format: "percent",
|
||||
Group: "",
|
||||
Value: 0.07,
|
||||
Priority: 1,
|
||||
Metric: &fixture.ClientHostCPUMetric,
|
||||
},
|
||||
{
|
||||
ID: host.MemoryUsage,
|
||||
Format: "filesize",
|
||||
Group: "",
|
||||
Value: 0.08,
|
||||
Metric: &fixture.ClientHostMemoryMetric,
|
||||
ID: host.MemoryUsage,
|
||||
Label: "Memory",
|
||||
Format: "filesize",
|
||||
Group: "",
|
||||
Value: 0.08,
|
||||
Priority: 2,
|
||||
Metric: &fixture.ClientHostMemoryMetric,
|
||||
},
|
||||
{
|
||||
ID: host.Load1,
|
||||
Group: "load",
|
||||
Value: 0.09,
|
||||
Metric: &fixture.ClientHostLoad1Metric,
|
||||
ID: host.Load1,
|
||||
Label: "Load (1m)",
|
||||
Group: "load",
|
||||
Value: 0.09,
|
||||
Priority: 11,
|
||||
Metric: &fixture.ClientHostLoad1Metric,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -93,7 +107,7 @@ func TestNodeMetrics(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, input := range inputs {
|
||||
have := detailed.NodeMetrics(input.node)
|
||||
have := detailed.NodeMetrics(fixture.Report, input.node)
|
||||
if !reflect.DeepEqual(input.want, have) {
|
||||
t.Errorf("%s: %s", input.name, test.Diff(input.want, have))
|
||||
}
|
||||
@@ -104,19 +118,16 @@ func TestMetricRowSummary(t *testing.T) {
|
||||
var (
|
||||
now = time.Now()
|
||||
metric = report.MakeMetric().Add(now, 1.234)
|
||||
row = detailed.MetricRow{
|
||||
ID: "id",
|
||||
Format: "format",
|
||||
Group: "group",
|
||||
Value: 1.234,
|
||||
Metric: &metric,
|
||||
row = report.MetricRow{
|
||||
ID: "id",
|
||||
Format: "format",
|
||||
Group: "group",
|
||||
Value: 1.234,
|
||||
Priority: 1,
|
||||
Metric: &metric,
|
||||
}
|
||||
summary = row.Summary()
|
||||
)
|
||||
// summary should have all the same fields
|
||||
if row.ID != summary.ID || row.Format != summary.Format || row.Group != summary.Group || row.Value != summary.Value {
|
||||
t.Errorf("Expected summary to have same fields as original: %#v, but had %#v", row, summary)
|
||||
}
|
||||
// summary should not have any samples
|
||||
if summary.Metric.Len() != 0 {
|
||||
t.Errorf("Expected summary to have no samples, but had %d", summary.Metric.Len())
|
||||
@@ -125,4 +136,10 @@ func TestMetricRowSummary(t *testing.T) {
|
||||
if metric.Len() != 1 {
|
||||
t.Errorf("Expected original metric to still have it's samples, but had %d", metric.Len())
|
||||
}
|
||||
// summary should have all the same fields (minus the metric)
|
||||
summary.Metric = nil
|
||||
row.Metric = nil
|
||||
if !reflect.DeepEqual(summary, row) {
|
||||
t.Errorf("Expected summary to have same fields as original: %s", test.Diff(summary, row))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ type wiredControlInstance struct {
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
// CodecEncodeSelf marshals this MetricRow. It takes the basic Metric
|
||||
// CodecEncodeSelf marshals this ControlInstance. It takes the basic Metric
|
||||
// rendering, then adds some row-specific fields.
|
||||
func (c *ControlInstance) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
encoder.Encode(wiredControlInstance{
|
||||
@@ -77,15 +77,15 @@ func (c *ControlInstance) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
// MakeNode transforms a renderable node to a detailed node. It uses
|
||||
// aggregate metadata, plus the set of origin node IDs, to produce tables.
|
||||
func MakeNode(topologyID string, r report.Report, ns report.Nodes, n report.Node) Node {
|
||||
summary, _ := MakeNodeSummary(n)
|
||||
summary, _ := MakeNodeSummary(r, n)
|
||||
return Node{
|
||||
NodeSummary: summary,
|
||||
Controls: controls(r, n),
|
||||
Children: children(n),
|
||||
Children: children(r, n),
|
||||
Parents: Parents(r, n),
|
||||
Connections: []ConnectionsSummary{
|
||||
incomingConnectionsSummary(topologyID, n, ns),
|
||||
outgoingConnectionsSummary(topologyID, n, ns),
|
||||
incomingConnectionsSummary(topologyID, r, n, ns),
|
||||
outgoingConnectionsSummary(topologyID, r, n, ns),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,6 @@ func controlsFor(topology report.Topology, nodeID string) []ControlInstance {
|
||||
}
|
||||
|
||||
func controls(r report.Report, n report.Node) []ControlInstance {
|
||||
// TODO(paulbellamy): this ID will have been munged in rendering, so we should stop doing that, so that this matches up.
|
||||
if t, ok := r.Topology(n.Topology); ok {
|
||||
return controlsFor(t, n.ID)
|
||||
}
|
||||
@@ -178,13 +177,13 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func children(n report.Node) []NodeSummaryGroup {
|
||||
func children(r report.Report, n report.Node) []NodeSummaryGroup {
|
||||
summaries := map[string][]NodeSummary{}
|
||||
n.Children.ForEach(func(child report.Node) {
|
||||
if child.ID == n.ID {
|
||||
return
|
||||
}
|
||||
summary, ok := MakeNodeSummary(child)
|
||||
summary, ok := MakeNodeSummary(r, child)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
func child(t *testing.T, r render.Renderer, id string) detailed.NodeSummary {
|
||||
s, ok := detailed.MakeNodeSummary(r.Render(fixture.Report)[id])
|
||||
s, ok := detailed.MakeNodeSummary(fixture.Report, r.Render(fixture.Report)[id])
|
||||
if !ok {
|
||||
t.Fatalf("Expected node %s to be summarizable, but wasn't", id)
|
||||
}
|
||||
@@ -44,38 +44,50 @@ func TestMakeDetailedHostNode(t *testing.T) {
|
||||
Shape: "circle",
|
||||
Linkable: true,
|
||||
Adjacency: report.MakeIDList(fixture.ServerHostNodeID),
|
||||
Metadata: []detailed.MetadataRow{
|
||||
Metadata: []report.MetadataRow{
|
||||
{
|
||||
ID: "host_name",
|
||||
Value: "client.hostname.com",
|
||||
ID: "host_name",
|
||||
Label: "Hostname",
|
||||
Value: "client.hostname.com",
|
||||
Priority: 11,
|
||||
},
|
||||
{
|
||||
ID: "os",
|
||||
Value: "Linux",
|
||||
ID: "os",
|
||||
Label: "OS",
|
||||
Value: "Linux",
|
||||
Priority: 12,
|
||||
},
|
||||
{
|
||||
ID: "local_networks",
|
||||
Value: "10.10.10.0/24",
|
||||
ID: "local_networks",
|
||||
Label: "Local Networks",
|
||||
Value: "10.10.10.0/24",
|
||||
Priority: 13,
|
||||
},
|
||||
},
|
||||
Metrics: []detailed.MetricRow{
|
||||
Metrics: []report.MetricRow{
|
||||
{
|
||||
ID: host.CPUUsage,
|
||||
Format: "percent",
|
||||
Value: 0.07,
|
||||
Metric: &fixture.ClientHostCPUMetric,
|
||||
ID: host.CPUUsage,
|
||||
Label: "CPU",
|
||||
Format: "percent",
|
||||
Value: 0.07,
|
||||
Priority: 1,
|
||||
Metric: &fixture.ClientHostCPUMetric,
|
||||
},
|
||||
{
|
||||
ID: host.MemoryUsage,
|
||||
Format: "filesize",
|
||||
Value: 0.08,
|
||||
Metric: &fixture.ClientHostMemoryMetric,
|
||||
ID: host.MemoryUsage,
|
||||
Label: "Memory",
|
||||
Format: "filesize",
|
||||
Value: 0.08,
|
||||
Priority: 2,
|
||||
Metric: &fixture.ClientHostMemoryMetric,
|
||||
},
|
||||
{
|
||||
ID: host.Load1,
|
||||
Group: "load",
|
||||
Value: 0.09,
|
||||
Metric: &fixture.ClientHostLoad1Metric,
|
||||
ID: host.Load1,
|
||||
Label: "Load (1m)",
|
||||
Group: "load",
|
||||
Value: 0.09,
|
||||
Priority: 11,
|
||||
Metric: &fixture.ClientHostLoad1Metric,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -121,7 +133,7 @@ func TestMakeDetailedHostNode(t *testing.T) {
|
||||
NodeID: fixture.ServerHostNodeID,
|
||||
Label: "server",
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
Metadata: []report.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
@@ -162,29 +174,33 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
Shape: "hexagon",
|
||||
Linkable: true,
|
||||
Pseudo: false,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{ID: "docker_container_id", Value: fixture.ServerContainerID, Prime: true},
|
||||
{ID: "docker_container_state_human", Value: "running", Prime: true},
|
||||
{ID: "docker_image_id", Value: fixture.ServerContainerImageID},
|
||||
Metadata: []report.MetadataRow{
|
||||
{ID: "docker_container_id", Label: "ID", Value: fixture.ServerContainerID, Priority: 1},
|
||||
{ID: "docker_container_state_human", Label: "State", Value: "running", Priority: 2},
|
||||
{ID: "docker_image_id", Label: "Image ID", Value: fixture.ServerContainerImageID, Priority: 11},
|
||||
},
|
||||
DockerLabels: []detailed.MetadataRow{
|
||||
DockerLabels: []report.MetadataRow{
|
||||
{ID: "label_" + detailed.AmazonECSContainerNameLabel, Value: `server`},
|
||||
{ID: "label_foo1", Value: `bar1`},
|
||||
{ID: "label_foo2", Value: `bar2`},
|
||||
{ID: "label_io.kubernetes.pod.name", Value: "ping/pong-b"},
|
||||
},
|
||||
Metrics: []detailed.MetricRow{
|
||||
Metrics: []report.MetricRow{
|
||||
{
|
||||
ID: docker.CPUTotalUsage,
|
||||
Format: "percent",
|
||||
Value: 0.05,
|
||||
Metric: &fixture.ServerContainerCPUMetric,
|
||||
ID: docker.CPUTotalUsage,
|
||||
Label: "CPU",
|
||||
Format: "percent",
|
||||
Value: 0.05,
|
||||
Priority: 1,
|
||||
Metric: &fixture.ServerContainerCPUMetric,
|
||||
},
|
||||
{
|
||||
ID: docker.MemoryUsage,
|
||||
Format: "filesize",
|
||||
Value: 0.06,
|
||||
Metric: &fixture.ServerContainerMemoryMetric,
|
||||
ID: docker.MemoryUsage,
|
||||
Label: "Memory",
|
||||
Format: "filesize",
|
||||
Value: 0.06,
|
||||
Priority: 2,
|
||||
Metric: &fixture.ServerContainerMemoryMetric,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -221,7 +237,7 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
NodeID: fixture.ClientContainerNodeID,
|
||||
Label: "client",
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
Metadata: []report.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
@@ -239,7 +255,7 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
NodeID: render.IncomingInternetID,
|
||||
Label: render.InboundMajor,
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
Metadata: []report.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
|
||||
+19
-19
@@ -68,22 +68,22 @@ func MakeColumn(id string) Column {
|
||||
|
||||
// NodeSummary is summary information about a child for a Node.
|
||||
type NodeSummary struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
LabelMinor string `json:"label_minor"`
|
||||
Rank string `json:"rank"`
|
||||
Shape string `json:"shape,omitempty"`
|
||||
Stack bool `json:"stack,omitempty"`
|
||||
Linkable bool `json:"linkable,omitempty"` // Whether this node can be linked-to
|
||||
Pseudo bool `json:"pseudo,omitempty"`
|
||||
Metadata []MetadataRow `json:"metadata,omitempty"`
|
||||
DockerLabels []MetadataRow `json:"docker_labels,omitempty"`
|
||||
Metrics []MetricRow `json:"metrics,omitempty"`
|
||||
Adjacency report.IDList `json:"adjacency,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
LabelMinor string `json:"label_minor"`
|
||||
Rank string `json:"rank"`
|
||||
Shape string `json:"shape,omitempty"`
|
||||
Stack bool `json:"stack,omitempty"`
|
||||
Linkable bool `json:"linkable,omitempty"` // Whether this node can be linked-to
|
||||
Pseudo bool `json:"pseudo,omitempty"`
|
||||
Metadata []report.MetadataRow `json:"metadata,omitempty"`
|
||||
DockerLabels []report.MetadataRow `json:"docker_labels,omitempty"`
|
||||
Metrics []report.MetricRow `json:"metrics,omitempty"`
|
||||
Adjacency report.IDList `json:"adjacency,omitempty"`
|
||||
}
|
||||
|
||||
// MakeNodeSummary summarizes a node, if possible.
|
||||
func MakeNodeSummary(n report.Node) (NodeSummary, bool) {
|
||||
func MakeNodeSummary(r report.Report, n report.Node) (NodeSummary, bool) {
|
||||
renderers := map[string]func(NodeSummary, report.Node) (NodeSummary, bool){
|
||||
render.Pseudo: pseudoNodeSummary,
|
||||
report.Process: processNodeSummary,
|
||||
@@ -94,7 +94,7 @@ func MakeNodeSummary(n report.Node) (NodeSummary, bool) {
|
||||
report.Host: hostNodeSummary,
|
||||
}
|
||||
if renderer, ok := renderers[n.Topology]; ok {
|
||||
return renderer(baseNodeSummary(n), n)
|
||||
return renderer(baseNodeSummary(r, n), n)
|
||||
}
|
||||
return NodeSummary{}, false
|
||||
}
|
||||
@@ -133,14 +133,14 @@ func (n NodeSummary) Copy() NodeSummary {
|
||||
return result
|
||||
}
|
||||
|
||||
func baseNodeSummary(n report.Node) NodeSummary {
|
||||
func baseNodeSummary(r report.Report, n report.Node) NodeSummary {
|
||||
return NodeSummary{
|
||||
ID: n.ID,
|
||||
Shape: Circle,
|
||||
Linkable: true,
|
||||
Metadata: NodeMetadata(n),
|
||||
Metadata: NodeMetadata(r, n),
|
||||
DockerLabels: NodeDockerLabels(n),
|
||||
Metrics: NodeMetrics(n),
|
||||
Metrics: NodeMetrics(r, n),
|
||||
Adjacency: n.Adjacency.Copy(),
|
||||
}
|
||||
}
|
||||
@@ -341,10 +341,10 @@ func (s nodeSummariesByID) Less(i, j int) bool { return s[i].ID < s[j].ID }
|
||||
type NodeSummaries map[string]NodeSummary
|
||||
|
||||
// Summaries converts RenderableNodes into a set of NodeSummaries
|
||||
func Summaries(rns report.Nodes) NodeSummaries {
|
||||
func Summaries(r report.Report, rns report.Nodes) NodeSummaries {
|
||||
result := NodeSummaries{}
|
||||
for id, node := range rns {
|
||||
if summary, ok := MakeNodeSummary(node); ok {
|
||||
if summary, ok := MakeNodeSummary(r, node); ok {
|
||||
for i, m := range summary.Metrics {
|
||||
summary.Metrics[i] = m.Summary()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
func TestSummaries(t *testing.T) {
|
||||
{
|
||||
// Just a convenient source of some rendered nodes
|
||||
have := detailed.Summaries(render.ProcessRenderer.Render(fixture.Report))
|
||||
have := detailed.Summaries(fixture.Report, render.ProcessRenderer.Render(fixture.Report))
|
||||
// The ids of the processes rendered above
|
||||
expectedIDs := []string{
|
||||
fixture.ClientProcess1NodeID,
|
||||
@@ -53,14 +53,14 @@ func TestSummaries(t *testing.T) {
|
||||
input := fixture.Report.Copy()
|
||||
|
||||
input.Process.Nodes[fixture.ClientProcess1NodeID] = input.Process.Nodes[fixture.ClientProcess1NodeID].WithMetrics(report.Metrics{process.CPUUsage: metric})
|
||||
have := detailed.Summaries(render.ProcessRenderer.Render(input))
|
||||
have := detailed.Summaries(input, render.ProcessRenderer.Render(input))
|
||||
|
||||
node, ok := have[fixture.ClientProcess1NodeID]
|
||||
if !ok {
|
||||
t.Fatalf("Expected output to have the node we added the metric to")
|
||||
}
|
||||
|
||||
var row detailed.MetricRow
|
||||
var row report.MetricRow
|
||||
ok = false
|
||||
for _, metric := range node.Metrics {
|
||||
if metric.ID == process.CPUUsage {
|
||||
@@ -74,10 +74,12 @@ func TestSummaries(t *testing.T) {
|
||||
}
|
||||
|
||||
// Our summarized MetricRow
|
||||
want := detailed.MetricRow{
|
||||
ID: process.CPUUsage,
|
||||
Format: "percent",
|
||||
Value: 2,
|
||||
want := report.MetricRow{
|
||||
ID: process.CPUUsage,
|
||||
Label: "CPU",
|
||||
Format: "percent",
|
||||
Value: 2,
|
||||
Priority: 1,
|
||||
Metric: &report.Metric{
|
||||
Samples: nil,
|
||||
Min: metric.Min,
|
||||
@@ -109,10 +111,9 @@ func TestMakeNodeSummary(t *testing.T) {
|
||||
LabelMinor: "client.hostname.com (10001)",
|
||||
Rank: fixture.Client1Name,
|
||||
Shape: "square",
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{ID: process.PID, Value: fixture.Client1PID, Prime: true, Datatype: "number"},
|
||||
Metadata: []report.MetadataRow{
|
||||
{ID: process.PID, Label: "PID", Value: fixture.Client1PID, Priority: 1, Datatype: "number"},
|
||||
},
|
||||
Metrics: []detailed.MetricRow{},
|
||||
Adjacency: report.MakeIDList(fixture.ServerProcessNodeID),
|
||||
},
|
||||
},
|
||||
@@ -127,10 +128,9 @@ func TestMakeNodeSummary(t *testing.T) {
|
||||
Rank: fixture.ClientContainerImageName,
|
||||
Shape: "hexagon",
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{ID: docker.ContainerID, Value: fixture.ClientContainerID, Prime: true},
|
||||
Metadata: []report.MetadataRow{
|
||||
{ID: docker.ContainerID, Label: "ID", Value: fixture.ClientContainerID, Priority: 1},
|
||||
},
|
||||
Metrics: []detailed.MetricRow{},
|
||||
Adjacency: report.MakeIDList(fixture.ServerContainerNodeID),
|
||||
},
|
||||
},
|
||||
@@ -146,9 +146,9 @@ func TestMakeNodeSummary(t *testing.T) {
|
||||
Shape: "hexagon",
|
||||
Linkable: true,
|
||||
Stack: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{ID: docker.ImageID, Value: fixture.ClientContainerImageID, Prime: true},
|
||||
{ID: report.Container, Value: "1", Prime: true, Datatype: "number"},
|
||||
Metadata: []report.MetadataRow{
|
||||
{ID: docker.ImageID, Label: "Image ID", Value: fixture.ClientContainerImageID, Priority: 1},
|
||||
{ID: report.Container, Label: "# Containers", Value: "1", Priority: 2, Datatype: "number"},
|
||||
},
|
||||
Adjacency: report.MakeIDList(fixture.ServerContainerImageNodeID),
|
||||
},
|
||||
@@ -164,16 +164,15 @@ func TestMakeNodeSummary(t *testing.T) {
|
||||
Rank: "hostname.com",
|
||||
Shape: "circle",
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{ID: host.HostName, Value: fixture.ClientHostName, Prime: false},
|
||||
Metadata: []report.MetadataRow{
|
||||
{ID: host.HostName, Label: "Hostname", Value: fixture.ClientHostName, Priority: 11},
|
||||
},
|
||||
Metrics: []detailed.MetricRow{},
|
||||
Adjacency: report.MakeIDList(fixture.ServerHostNodeID),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
have, ok := detailed.MakeNodeSummary(testcase.input)
|
||||
have, ok := detailed.MakeNodeSummary(fixture.Report, testcase.input)
|
||||
if ok != testcase.ok {
|
||||
t.Errorf("%s: MakeNodeSummary failed: expected ok value to be: %v", testcase.name, testcase.ok)
|
||||
continue
|
||||
|
||||
+4
-6
@@ -26,8 +26,6 @@ const (
|
||||
InboundMinor = "Inbound connections"
|
||||
OutboundMinor = "Outbound connections"
|
||||
|
||||
ipsKey = "ips"
|
||||
|
||||
// Topology for pseudo-nodes and IPs so we can differentiate them at the end
|
||||
Pseudo = "pseudo"
|
||||
IP = "IP"
|
||||
@@ -46,7 +44,7 @@ func NewDerivedNode(id string, node report.Node) report.Node {
|
||||
|
||||
// NewDerivedPseudoNode makes a new pseudo node with the node as a child
|
||||
func NewDerivedPseudoNode(id string, node report.Node) report.Node {
|
||||
return node.WithID(id).WithTopology(Pseudo).WithChildren(report.MakeNodeSet(node)).PruneParents()
|
||||
return NewDerivedNode(id, node).WithTopology(Pseudo)
|
||||
}
|
||||
|
||||
func theInternetNode(m report.Node) report.Node {
|
||||
@@ -110,7 +108,7 @@ func MapContainer2IP(m report.Node, _ report.Networks) report.Nodes {
|
||||
result[id] = NewDerivedNode(id, m).
|
||||
WithTopology(IP).
|
||||
WithLatests(map[string]string{docker.ContainerID: containerID}).
|
||||
WithCounters(map[string]int{ipsKey: 1})
|
||||
WithCounters(map[string]int{IP: 1})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -125,7 +123,7 @@ func MapContainer2IP(m report.Node, _ report.Networks) report.Nodes {
|
||||
result[id] = NewDerivedNode(id, m).
|
||||
WithTopology(IP).
|
||||
WithLatests(map[string]string{docker.ContainerID: containerID}).
|
||||
WithCounters(map[string]int{ipsKey: 1})
|
||||
WithCounters(map[string]int{IP: 1})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -139,7 +137,7 @@ func MapContainer2IP(m report.Node, _ report.Networks) report.Nodes {
|
||||
func MapIP2Container(n report.Node, _ report.Networks) report.Nodes {
|
||||
// If an IP is shared between multiple containers, we can't
|
||||
// reliably attribute an connection based on its IP
|
||||
if count, _ := n.Counters.Lookup(ipsKey); count > 1 {
|
||||
if count, _ := n.Counters.Lookup(IP); count > 1 {
|
||||
return report.Nodes{}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user