mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 02:00:43 +00:00
Merge pull request #1017 from weaveworks/680-connections-table
Add connection tables to details panel
This commit is contained in:
@@ -51,17 +51,18 @@ func handleWs(ctx context.Context, rep Reporter, renderer render.Renderer, w htt
|
||||
}
|
||||
|
||||
// Individual nodes.
|
||||
func handleNode(nodeID string) func(context.Context, Reporter, render.Renderer, http.ResponseWriter, *http.Request) {
|
||||
func handleNode(topologyID, nodeID string) func(context.Context, Reporter, render.Renderer, http.ResponseWriter, *http.Request) {
|
||||
return func(ctx context.Context, rep Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
rpt = rep.Report(ctx)
|
||||
node, ok = renderer.Render(rep.Report(ctx))[nodeID]
|
||||
rendered = renderer.Render(rep.Report(ctx))
|
||||
node, ok = rendered[nodeID]
|
||||
)
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
respondWith(w, http.StatusOK, APINode{Node: detailed.MakeNode(rpt, node)})
|
||||
respondWith(w, http.StatusOK, APINode{Node: detailed.MakeNode(topologyID, rpt, rendered, node)})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,27 +54,6 @@ func TestAll(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPITopologyContainers(t *testing.T) {
|
||||
ts := topologyServer()
|
||||
{
|
||||
body := getRawJSON(t, ts, "/api/topology/containers")
|
||||
var topo app.APITopology
|
||||
decoder := codec.NewDecoderBytes(body, &codec.JsonHandle{})
|
||||
if err := decoder.Decode(&topo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := expected.RenderedContainers.Copy()
|
||||
for id, node := range want {
|
||||
node.ControlNode = ""
|
||||
want[id] = node
|
||||
}
|
||||
|
||||
if have := topo.Nodes.Prune(); !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPITopologyProcesses(t *testing.T) {
|
||||
ts := topologyServer()
|
||||
defer ts.Close()
|
||||
@@ -124,13 +103,13 @@ func TestAPITopologyHosts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
{
|
||||
body := getRawJSON(t, ts, "/api/topology/hosts/"+expected.ServerHostRenderedID)
|
||||
body := getRawJSON(t, ts, "/api/topology/hosts/"+expected.ServerHostID)
|
||||
var node app.APINode
|
||||
decoder := codec.NewDecoderBytes(body, &codec.JsonHandle{})
|
||||
if err := decoder.Decode(&node); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
equals(t, expected.ServerHostRenderedID, node.Node.ID)
|
||||
equals(t, expected.ServerHostID, node.Node.ID)
|
||||
equals(t, "server", node.Node.Label)
|
||||
equals(t, false, node.Node.Pseudo)
|
||||
// Let's not unit-test the specific content of the detail tables
|
||||
|
||||
@@ -122,7 +122,7 @@ func TopologyHandler(c Reporter, preRoutes *mux.Router, postRoutes http.Handler)
|
||||
}
|
||||
|
||||
handler := gzipHandler(requestContextDecorator(topologyRegistry.captureRendererWithoutFilters(
|
||||
c, topologyID, handleNode(nodeID),
|
||||
c, topologyID, handleNode(topologyID, nodeID),
|
||||
)))
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
@@ -174,6 +174,14 @@ export default class NodeDetails extends React.Component {
|
||||
<NodeDetailsInfo rows={details.metadata} />
|
||||
</div>}
|
||||
|
||||
{details.connections && details.connections.map(connections => {
|
||||
return (
|
||||
<div className="node-details-content-section" key={connections.id}>
|
||||
<NodeDetailsTable {...connections} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{details.children && details.children.map(children => {
|
||||
return (
|
||||
<div className="node-details-content-section" key={children.topologyId}>
|
||||
|
||||
@@ -5,6 +5,10 @@ import ShowMore from '../show-more';
|
||||
import NodeDetailsTableNodeLink from './node-details-table-node-link';
|
||||
import NodeDetailsTableNodeMetric from './node-details-table-node-metric';
|
||||
|
||||
function isNumberField(field) {
|
||||
return field.dataType && field.dataType === 'number';
|
||||
}
|
||||
|
||||
export default class NodeDetailsTable extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -32,14 +36,28 @@ export default class NodeDetailsTable extends React.Component {
|
||||
}
|
||||
|
||||
getDefaultSortBy() {
|
||||
// first metric
|
||||
// default sorter specified by columns
|
||||
const defaultSortColumn = _.find(this.props.columns, {defaultSort: true});
|
||||
if (defaultSortColumn) {
|
||||
return defaultSortColumn.id;
|
||||
}
|
||||
// otherwise choose first metric
|
||||
return _.get(this.props.nodes, [0, 'metrics', 0, 'id']);
|
||||
}
|
||||
|
||||
getMetaDataSorters() {
|
||||
// returns an array of sorters that will take a node
|
||||
return _.get(this.props.nodes, [0, 'metadata'], []).map((field, index) => {
|
||||
return node => node.metadata[index] ? node.metadata[index].value : null;
|
||||
return node => {
|
||||
const nodeMetadataField = node.metadata[index];
|
||||
if (nodeMetadataField) {
|
||||
if (isNumberField(nodeMetadataField)) {
|
||||
return parseFloat(nodeMetadataField.value);
|
||||
}
|
||||
return nodeMetadataField.value;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,6 +67,9 @@ export default class NodeDetailsTable extends React.Component {
|
||||
if (sortBy !== null) {
|
||||
const field = _.union(node.metrics, node.metadata).find(f => f.id === sortBy);
|
||||
if (field) {
|
||||
if (isNumberField(field)) {
|
||||
return parseFloat(field.value);
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,9 @@ do_connections() {
|
||||
}
|
||||
do_connections&
|
||||
|
||||
wait_for_containers $HOST1 60 nginx "Inbound"
|
||||
wait_for_containers $HOST1 60 nginx "The Internet"
|
||||
|
||||
has_container $HOST1 nginx
|
||||
has_container $HOST1 "Inbound"
|
||||
has_connection containers $HOST1 "Inbound" nginx
|
||||
has_connection_by_id containers $HOST1 "in-theinternet" $(node_id containers $HOST1 nginx)
|
||||
|
||||
kill %do_connections
|
||||
|
||||
|
||||
@@ -59,14 +59,13 @@ container_id() {
|
||||
}
|
||||
|
||||
# this checks we have an edge from container 1 to container 2
|
||||
has_connection() {
|
||||
has_connection_by_id() {
|
||||
local view="$1"
|
||||
local host="$2"
|
||||
local from="$3"
|
||||
local to="$4"
|
||||
local from_id="$3"
|
||||
local to_id="$4"
|
||||
local timeout="${5:-60}"
|
||||
local from_id=$(node_id "${view}" "${host}" "${from}")
|
||||
local to_id=$(node_id "${view}" "${host}" "${to}")
|
||||
|
||||
for i in $(seq $timeout); do
|
||||
local nodes="$(curl -s http://$host:4040/api/topology/${view}?system=show)"
|
||||
local edge=$(echo "$nodes" | jq -r ".nodes[\"$from_id\"].adjacency | contains([\"$to_id\"])" 2>/dev/null)
|
||||
@@ -82,6 +81,18 @@ has_connection() {
|
||||
assert "curl -s http://$host:4040/api/topology/${view}?system=show | jq -r '.nodes[\"$from_id\"].adjacency | contains([\"$to_id\"])'" true
|
||||
}
|
||||
|
||||
has_connection() {
|
||||
local view="$1"
|
||||
local host="$2"
|
||||
local from="$3"
|
||||
local to="$4"
|
||||
local timeout="${5:-60}"
|
||||
local from_id="$(node_id "${view}" "${host}" "${from}")"
|
||||
local to_id="$(node_id "${view}" "${host}" "${to}")"
|
||||
|
||||
has_connection_by_id "${view}" "${host}" "${from_id}" "${to_id}" "${timeout}"
|
||||
}
|
||||
|
||||
wait_for() {
|
||||
local view="$1"
|
||||
local host="$2"
|
||||
|
||||
197
render/detailed/connections.go
Normal file
197
render/detailed/connections.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package detailed
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
const (
|
||||
portKey = "port"
|
||||
portLabel = "Port"
|
||||
countKey = "count"
|
||||
countLabel = "Count"
|
||||
number = "number"
|
||||
)
|
||||
|
||||
// Exported for testing
|
||||
var (
|
||||
NormalColumns = []Column{
|
||||
{ID: portKey, Label: portLabel},
|
||||
{ID: countKey, Label: countLabel, DefaultSort: true},
|
||||
}
|
||||
InternetColumns = []Column{
|
||||
{ID: "foo", Label: "Remote"},
|
||||
{ID: portKey, Label: portLabel},
|
||||
{ID: countKey, Label: countLabel, DefaultSort: true},
|
||||
}
|
||||
)
|
||||
|
||||
type connectionsRow struct {
|
||||
remoteNode, localNode *render.RenderableNode
|
||||
remoteAddr, localAddr string
|
||||
port string // always the server-side port
|
||||
}
|
||||
|
||||
func incomingConnectionsTable(topologyID string, n render.RenderableNode, ns render.RenderableNodes) NodeSummaryGroup {
|
||||
localEndpointIDs := endpointChildIDsOf(n)
|
||||
|
||||
// For each node which has an edge TO me
|
||||
counts := map[connectionsRow]int{}
|
||||
for _, node := range ns {
|
||||
if !node.Adjacency.Contains(n.ID) {
|
||||
continue
|
||||
}
|
||||
remoteNode := node.Copy()
|
||||
|
||||
// Work out what port they are talking to, and count the number of
|
||||
// connections to that port.
|
||||
// This is complicated as for internet nodes we break out individual
|
||||
// address, both when the internet node is remote (an incoming
|
||||
// connection from the internet) and 'local' (ie you are loading
|
||||
// details on the internet node)
|
||||
for _, child := range endpointChildrenOf(node) {
|
||||
for _, localEndpointID := range child.Adjacency.Intersection(localEndpointIDs) {
|
||||
_, localAddr, port, ok := render.ParseEndpointID(localEndpointID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key := connectionsRow{
|
||||
localNode: &n,
|
||||
remoteNode: &remoteNode,
|
||||
port: port,
|
||||
}
|
||||
if isInternetNode(n) {
|
||||
key.localAddr = localAddr
|
||||
}
|
||||
counts[key] = counts[key] + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
columnHeaders := NormalColumns
|
||||
if isInternetNode(n) {
|
||||
columnHeaders = InternetColumns
|
||||
}
|
||||
return NodeSummaryGroup{
|
||||
ID: "incoming-connections",
|
||||
TopologyID: topologyID,
|
||||
Label: "Inbound",
|
||||
Columns: columnHeaders,
|
||||
Nodes: connectionRows(counts, isInternetNode(n)),
|
||||
}
|
||||
}
|
||||
|
||||
func outgoingConnectionsTable(topologyID string, n render.RenderableNode, ns render.RenderableNodes) NodeSummaryGroup {
|
||||
localEndpoints := endpointChildrenOf(n)
|
||||
|
||||
// For each node which has an edge FROM me
|
||||
counts := map[connectionsRow]int{}
|
||||
for _, node := range ns {
|
||||
if !n.Adjacency.Contains(node.ID) {
|
||||
continue
|
||||
}
|
||||
remoteNode := node.Copy()
|
||||
remoteEndpointIDs := endpointChildIDsOf(remoteNode)
|
||||
|
||||
for _, localEndpoint := range localEndpoints {
|
||||
_, localAddr, _, ok := render.ParseEndpointID(localEndpoint.ID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, remoteEndpointID := range localEndpoint.Adjacency.Intersection(remoteEndpointIDs) {
|
||||
_, _, port, ok := render.ParseEndpointID(remoteEndpointID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key := connectionsRow{
|
||||
localNode: &n,
|
||||
remoteNode: &remoteNode,
|
||||
port: port,
|
||||
}
|
||||
if isInternetNode(n) {
|
||||
key.localAddr = localAddr
|
||||
}
|
||||
counts[key] = counts[key] + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
columnHeaders := NormalColumns
|
||||
if isInternetNode(n) {
|
||||
columnHeaders = InternetColumns
|
||||
}
|
||||
return NodeSummaryGroup{
|
||||
ID: "outgoing-connections",
|
||||
TopologyID: topologyID,
|
||||
Label: "Outbound",
|
||||
Columns: columnHeaders,
|
||||
Nodes: connectionRows(counts, isInternetNode(n)),
|
||||
}
|
||||
}
|
||||
|
||||
func endpointChildrenOf(n render.RenderableNode) []render.RenderableNode {
|
||||
result := []render.RenderableNode{}
|
||||
n.Children.ForEach(func(child render.RenderableNode) {
|
||||
if _, _, _, ok := render.ParseEndpointID(child.ID); ok {
|
||||
result = append(result, child)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func endpointChildIDsOf(n render.RenderableNode) report.IDList {
|
||||
result := report.MakeIDList()
|
||||
n.Children.ForEach(func(child render.RenderableNode) {
|
||||
if _, _, _, ok := render.ParseEndpointID(child.ID); ok {
|
||||
result = append(result, child.ID)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func isInternetNode(n render.RenderableNode) bool {
|
||||
return n.ID == render.IncomingInternetID || n.ID == render.OutgoingInternetID
|
||||
}
|
||||
|
||||
func connectionRows(in map[connectionsRow]int, includeLocal bool) []NodeSummary {
|
||||
nodes := []NodeSummary{}
|
||||
for row, count := range in {
|
||||
id, label, linkable := row.remoteNode.ID, row.remoteNode.LabelMajor, true
|
||||
if row.remoteAddr != "" {
|
||||
id, label, linkable = row.remoteAddr+":"+row.port, row.remoteAddr, false
|
||||
}
|
||||
metadata := []MetadataRow{}
|
||||
if includeLocal {
|
||||
metadata = append(metadata,
|
||||
MetadataRow{
|
||||
ID: "foo",
|
||||
Value: row.localAddr,
|
||||
Datatype: number,
|
||||
})
|
||||
}
|
||||
metadata = append(metadata,
|
||||
MetadataRow{
|
||||
ID: portKey,
|
||||
Value: row.port,
|
||||
Datatype: number,
|
||||
},
|
||||
MetadataRow{
|
||||
ID: countKey,
|
||||
Value: strconv.Itoa(count),
|
||||
Datatype: number,
|
||||
},
|
||||
)
|
||||
nodes = append(nodes, NodeSummary{
|
||||
ID: id,
|
||||
Label: label,
|
||||
Linkable: linkable,
|
||||
Metadata: metadata,
|
||||
})
|
||||
}
|
||||
sort.Sort(nodeSummariesByID(nodes))
|
||||
return nodes
|
||||
}
|
||||
@@ -98,16 +98,22 @@ type Counter struct {
|
||||
// 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}}
|
||||
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
|
||||
ID string
|
||||
Value string
|
||||
Prime bool
|
||||
Datatype string
|
||||
}
|
||||
|
||||
// Copy returns a value copy of a metadata row.
|
||||
@@ -129,20 +135,22 @@ func (*MetadataRow) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
type labelledMetadataRow struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
Prime bool `json:"prime,omitempty"`
|
||||
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,
|
||||
ID: m.ID,
|
||||
Label: Label(m.ID),
|
||||
Value: m.Value,
|
||||
Prime: m.Prime,
|
||||
Datatype: m.Datatype,
|
||||
}
|
||||
encoder.Encode(in)
|
||||
}
|
||||
@@ -152,9 +160,10 @@ func (m *MetadataRow) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
var in labelledMetadataRow
|
||||
decoder.Decode(&in)
|
||||
*m = MetadataRow{
|
||||
ID: in.ID,
|
||||
Value: in.Value,
|
||||
Prime: in.Prime,
|
||||
ID: in.ID,
|
||||
Value: in.Value,
|
||||
Prime: in.Prime,
|
||||
Datatype: in.Datatype,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@ import (
|
||||
// we want deep information about an individual node.
|
||||
type Node struct {
|
||||
NodeSummary
|
||||
Rank string `json:"rank,omitempty"`
|
||||
Pseudo bool `json:"pseudo,omitempty"`
|
||||
Controls []ControlInstance `json:"controls"`
|
||||
Children []NodeSummaryGroup `json:"children,omitempty"`
|
||||
Parents []Parent `json:"parents,omitempty"`
|
||||
Rank string `json:"rank,omitempty"`
|
||||
Pseudo bool `json:"pseudo,omitempty"`
|
||||
Controls []ControlInstance `json:"controls"`
|
||||
Children []NodeSummaryGroup `json:"children,omitempty"`
|
||||
Parents []Parent `json:"parents,omitempty"`
|
||||
Connections []NodeSummaryGroup `json:"connections,omitempty"`
|
||||
}
|
||||
|
||||
// ControlInstance contains a control description, and all the info
|
||||
@@ -78,10 +79,11 @@ 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(r report.Report, n render.RenderableNode) Node {
|
||||
summary, _ := MakeNodeSummary(n.Node)
|
||||
func MakeNode(topologyID string, r report.Report, ns render.RenderableNodes, n render.RenderableNode) Node {
|
||||
summary, _ := MakeNodeSummary(n)
|
||||
summary.ID = n.ID
|
||||
summary.Label = n.LabelMajor
|
||||
|
||||
return Node{
|
||||
NodeSummary: summary,
|
||||
Rank: n.Rank,
|
||||
@@ -89,6 +91,10 @@ func MakeNode(r report.Report, n render.RenderableNode) Node {
|
||||
Controls: controls(r, n),
|
||||
Children: children(n),
|
||||
Parents: Parents(r, n),
|
||||
Connections: []NodeSummaryGroup{
|
||||
incomingConnectionsTable(topologyID, n, ns),
|
||||
outgoingConnectionsTable(topologyID, n, ns),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,17 +139,25 @@ var (
|
||||
topologyID string
|
||||
NodeSummaryGroup
|
||||
}{
|
||||
{report.Host, NodeSummaryGroup{TopologyID: "hosts", Label: "Hosts", Columns: []Column{host.CPUUsage, host.MemoryUsage}}},
|
||||
{report.Host, NodeSummaryGroup{TopologyID: "hosts", Label: "Hosts", Columns: []Column{
|
||||
MakeColumn(host.CPUUsage), MakeColumn(host.MemoryUsage),
|
||||
}}},
|
||||
{report.Pod, NodeSummaryGroup{TopologyID: "pods", Label: "Pods"}},
|
||||
{report.Container, NodeSummaryGroup{TopologyID: "containers", Label: "Containers", Columns: []Column{docker.CPUTotalUsage, docker.MemoryUsage}}},
|
||||
{report.Process, NodeSummaryGroup{TopologyID: "processes", Label: "Processes", Columns: []Column{process.PID, process.CPUUsage, process.MemoryUsage}}},
|
||||
{report.ContainerImage, NodeSummaryGroup{TopologyID: "containers-by-image", Label: "Container Images", Columns: []Column{render.ContainersKey}}},
|
||||
{report.Container, NodeSummaryGroup{TopologyID: "containers", Label: "Containers", Columns: []Column{
|
||||
MakeColumn(docker.CPUTotalUsage), MakeColumn(docker.MemoryUsage),
|
||||
}}},
|
||||
{report.Process, NodeSummaryGroup{TopologyID: "processes", Label: "Processes", Columns: []Column{
|
||||
MakeColumn(process.PID), MakeColumn(process.CPUUsage), MakeColumn(process.MemoryUsage),
|
||||
}}},
|
||||
{report.ContainerImage, NodeSummaryGroup{TopologyID: "containers-by-image", Label: "Container Images", Columns: []Column{
|
||||
MakeColumn(render.ContainersKey),
|
||||
}}},
|
||||
}
|
||||
)
|
||||
|
||||
func children(n render.RenderableNode) []NodeSummaryGroup {
|
||||
summaries := map[string][]NodeSummary{}
|
||||
n.Children.ForEach(func(child report.Node) {
|
||||
n.Children.ForEach(func(child render.RenderableNode) {
|
||||
if child.ID != n.ID {
|
||||
if summary, ok := MakeNodeSummary(child); ok {
|
||||
summaries[child.Topology] = append(summaries[child.Topology], summary)
|
||||
@@ -160,5 +174,6 @@ func children(n render.RenderableNode) []NodeSummaryGroup {
|
||||
nodeSummaryGroups = append(nodeSummaryGroups, group)
|
||||
}
|
||||
}
|
||||
|
||||
return nodeSummaryGroups
|
||||
}
|
||||
|
||||
@@ -10,21 +10,29 @@ import (
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/render/detailed"
|
||||
"github.com/weaveworks/scope/render/expected"
|
||||
"github.com/weaveworks/scope/test"
|
||||
"github.com/weaveworks/scope/test/fixture"
|
||||
)
|
||||
|
||||
func TestMakeDetailedHostNode(t *testing.T) {
|
||||
renderableNode := render.HostRenderer.Render(fixture.Report)[render.MakeHostID(fixture.ClientHostID)]
|
||||
have := detailed.MakeNode(fixture.Report, renderableNode)
|
||||
renderableNodes := render.HostRenderer.Render(fixture.Report)
|
||||
renderableNode := renderableNodes[render.MakeHostID(fixture.ClientHostID)]
|
||||
have := detailed.MakeNode("hosts", fixture.Report, renderableNodes, renderableNode)
|
||||
|
||||
containerImageNodeSummary, _ := detailed.MakeNodeSummary(
|
||||
render.ContainerImageRenderer.Render(fixture.Report)[render.MakeContainerImageID(fixture.ClientContainerImageName)].Node,
|
||||
render.ContainerImageRenderer.Render(fixture.Report)[expected.ClientContainerImageID],
|
||||
)
|
||||
containerNodeSummary, _ := detailed.MakeNodeSummary(
|
||||
render.ContainerRenderer.Render(fixture.Report)[expected.ClientContainerID],
|
||||
)
|
||||
process1NodeSummary, _ := detailed.MakeNodeSummary(
|
||||
render.ProcessRenderer.Render(fixture.Report)[expected.ClientProcess1ID],
|
||||
)
|
||||
containerNodeSummary, _ := detailed.MakeNodeSummary(fixture.Report.Container.Nodes[fixture.ClientContainerNodeID])
|
||||
process1NodeSummary, _ := detailed.MakeNodeSummary(fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID])
|
||||
process1NodeSummary.Linkable = true
|
||||
process2NodeSummary, _ := detailed.MakeNodeSummary(fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID])
|
||||
process2NodeSummary, _ := detailed.MakeNodeSummary(
|
||||
render.ProcessRenderer.Render(fixture.Report)[expected.ClientProcess2ID],
|
||||
)
|
||||
process2NodeSummary.Linkable = true
|
||||
want := detailed.Node{
|
||||
NodeSummary: detailed.NodeSummary{
|
||||
@@ -85,22 +93,56 @@ func TestMakeDetailedHostNode(t *testing.T) {
|
||||
{
|
||||
Label: "Containers",
|
||||
TopologyID: "containers",
|
||||
Columns: []detailed.Column{docker.CPUTotalUsage, docker.MemoryUsage},
|
||||
Columns: []detailed.Column{detailed.MakeColumn(docker.CPUTotalUsage), detailed.MakeColumn(docker.MemoryUsage)},
|
||||
Nodes: []detailed.NodeSummary{containerNodeSummary},
|
||||
},
|
||||
{
|
||||
Label: "Processes",
|
||||
TopologyID: "processes",
|
||||
Columns: []detailed.Column{process.PID, process.CPUUsage, process.MemoryUsage},
|
||||
Columns: []detailed.Column{detailed.MakeColumn(process.PID), detailed.MakeColumn(process.CPUUsage), detailed.MakeColumn(process.MemoryUsage)},
|
||||
Nodes: []detailed.NodeSummary{process1NodeSummary, process2NodeSummary},
|
||||
},
|
||||
{
|
||||
Label: "Container Images",
|
||||
TopologyID: "containers-by-image",
|
||||
Columns: []detailed.Column{render.ContainersKey},
|
||||
Columns: []detailed.Column{detailed.MakeColumn(render.ContainersKey)},
|
||||
Nodes: []detailed.NodeSummary{containerImageNodeSummary},
|
||||
},
|
||||
},
|
||||
Connections: []detailed.NodeSummaryGroup{
|
||||
{
|
||||
ID: "incoming-connections",
|
||||
TopologyID: "hosts",
|
||||
Label: "Inbound",
|
||||
Columns: detailed.NormalColumns,
|
||||
Nodes: []detailed.NodeSummary{},
|
||||
},
|
||||
{
|
||||
ID: "outgoing-connections",
|
||||
TopologyID: "hosts",
|
||||
Label: "Outbound",
|
||||
Columns: detailed.NormalColumns,
|
||||
Nodes: []detailed.NodeSummary{
|
||||
{
|
||||
ID: "host:server.hostname.com",
|
||||
Label: "server",
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
Datatype: "number",
|
||||
},
|
||||
{
|
||||
ID: "count",
|
||||
Value: "2",
|
||||
Datatype: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%s", test.Diff(want, have))
|
||||
@@ -109,11 +151,12 @@ func TestMakeDetailedHostNode(t *testing.T) {
|
||||
|
||||
func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
id := render.MakeContainerID(fixture.ServerContainerID)
|
||||
renderableNode, ok := render.ContainerRenderer.Render(fixture.Report)[id]
|
||||
renderableNodes := render.ContainerRenderer.Render(fixture.Report)
|
||||
renderableNode, ok := renderableNodes[id]
|
||||
if !ok {
|
||||
t.Fatalf("Node not found: %s", id)
|
||||
}
|
||||
have := detailed.MakeNode(fixture.Report, renderableNode)
|
||||
have := detailed.MakeNode("containers", fixture.Report, renderableNodes, renderableNode)
|
||||
want := detailed.Node{
|
||||
NodeSummary: detailed.NodeSummary{
|
||||
ID: id,
|
||||
@@ -145,14 +188,13 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Rank: "imageid456",
|
||||
Pseudo: false,
|
||||
Controls: []detailed.ControlInstance{},
|
||||
Children: []detailed.NodeSummaryGroup{
|
||||
{
|
||||
Label: "Processes",
|
||||
TopologyID: "processes",
|
||||
Columns: []detailed.Column{process.PID, process.CPUUsage, process.MemoryUsage},
|
||||
Columns: []detailed.Column{detailed.MakeColumn(process.PID), detailed.MakeColumn(process.CPUUsage), detailed.MakeColumn(process.MemoryUsage)},
|
||||
Nodes: []detailed.NodeSummary{
|
||||
{
|
||||
ID: fmt.Sprintf("process:%s:%s", "server.hostname.com", fixture.ServerPID),
|
||||
@@ -178,6 +220,57 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
TopologyID: "hosts",
|
||||
},
|
||||
},
|
||||
Connections: []detailed.NodeSummaryGroup{
|
||||
{
|
||||
ID: "incoming-connections",
|
||||
TopologyID: "containers",
|
||||
Label: "Inbound",
|
||||
Columns: detailed.NormalColumns,
|
||||
Nodes: []detailed.NodeSummary{
|
||||
{
|
||||
ID: "container:a1b2c3d4e5",
|
||||
Label: "client",
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
Datatype: "number",
|
||||
},
|
||||
{
|
||||
ID: "count",
|
||||
Value: "2",
|
||||
Datatype: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "in-theinternet",
|
||||
Label: "The Internet",
|
||||
Linkable: true,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
Datatype: "number",
|
||||
},
|
||||
{
|
||||
ID: "count",
|
||||
Value: "1",
|
||||
Datatype: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "outgoing-connections",
|
||||
TopologyID: "containers",
|
||||
Label: "Outbound",
|
||||
Columns: detailed.NormalColumns,
|
||||
Nodes: []detailed.NodeSummary{},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%s", test.Diff(want, have))
|
||||
|
||||
@@ -3,8 +3,6 @@ package detailed
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ugorji/go/codec"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/probe/kubernetes"
|
||||
@@ -15,6 +13,7 @@ import (
|
||||
|
||||
// NodeSummaryGroup is a topology-typed group of children for a Node.
|
||||
type NodeSummaryGroup struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Nodes []NodeSummary `json:"nodes"`
|
||||
TopologyID string `json:"topologyId"`
|
||||
@@ -36,29 +35,18 @@ func (g NodeSummaryGroup) Copy() NodeSummaryGroup {
|
||||
|
||||
// Column provides special json serialization for column ids, so they include
|
||||
// their label for the frontend.
|
||||
type Column string
|
||||
|
||||
// CodecEncodeSelf implements codec.Selfer
|
||||
func (c *Column) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
in := map[string]string{"id": string(*c), "label": Label(string(*c))}
|
||||
encoder.Encode(in)
|
||||
type Column struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
DefaultSort bool `json:"defaultSort"`
|
||||
}
|
||||
|
||||
// CodecDecodeSelf implements codec.Selfer
|
||||
func (c *Column) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
m := map[string]string{}
|
||||
decoder.Decode(&m)
|
||||
*c = Column(m["id"])
|
||||
}
|
||||
|
||||
// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
|
||||
func (Column) MarshalJSON() ([]byte, error) {
|
||||
panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
|
||||
}
|
||||
|
||||
// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
|
||||
func (*Column) UnmarshalJSON(b []byte) error {
|
||||
panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
|
||||
// MakeColumn makes a Column by looking up the label by id.
|
||||
func MakeColumn(id string) Column {
|
||||
return Column{
|
||||
ID: id,
|
||||
Label: Label(id),
|
||||
}
|
||||
}
|
||||
|
||||
// NodeSummary is summary information about a child for a Node.
|
||||
@@ -72,7 +60,7 @@ type NodeSummary struct {
|
||||
}
|
||||
|
||||
// MakeNodeSummary summarizes a node, if possible.
|
||||
func MakeNodeSummary(n report.Node) (NodeSummary, bool) {
|
||||
func MakeNodeSummary(n render.RenderableNode) (NodeSummary, bool) {
|
||||
renderers := map[string]func(report.Node) NodeSummary{
|
||||
report.Process: processNodeSummary,
|
||||
report.Container: containerNodeSummary,
|
||||
@@ -81,7 +69,7 @@ func MakeNodeSummary(n report.Node) (NodeSummary, bool) {
|
||||
report.Host: hostNodeSummary,
|
||||
}
|
||||
if renderer, ok := renderers[n.Topology]; ok {
|
||||
return renderer(n), true
|
||||
return renderer(n.Node), true
|
||||
}
|
||||
return NodeSummary{}, false
|
||||
}
|
||||
|
||||
@@ -16,16 +16,111 @@ var (
|
||||
hexagon = "hexagon"
|
||||
cloud = "cloud"
|
||||
|
||||
uncontainedServerID = render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostName)
|
||||
unknownPseudoNode1ID = render.MakePseudoNodeID("10.10.10.10", fixture.ServerIP, "80")
|
||||
unknownPseudoNode2ID = render.MakePseudoNodeID("10.10.10.11", fixture.ServerIP, "80")
|
||||
unknownPseudoNode1 = func(adjacent string) render.RenderableNode {
|
||||
Client54001EndpointID = render.MakeEndpointID(fixture.ClientHostID, fixture.ClientIP, fixture.ClientPort54001)
|
||||
Client54002EndpointID = render.MakeEndpointID(fixture.ClientHostID, fixture.ClientIP, fixture.ClientPort54002)
|
||||
ServerEndpointID = render.MakeEndpointID(fixture.ServerHostID, fixture.ServerIP, fixture.ServerPort)
|
||||
UnknownClient1EndpointID = render.MakeEndpointID("", fixture.UnknownClient1IP, fixture.UnknownClient1Port)
|
||||
UnknownClient2EndpointID = render.MakeEndpointID("", fixture.UnknownClient2IP, fixture.UnknownClient2Port)
|
||||
UnknownClient3EndpointID = render.MakeEndpointID("", fixture.UnknownClient3IP, fixture.UnknownClient3Port)
|
||||
RandomClientEndpointID = render.MakeEndpointID("", fixture.RandomClientIP, fixture.RandomClientPort)
|
||||
NonContainerEndpointID = render.MakeEndpointID(fixture.ServerHostID, fixture.ServerIP, fixture.NonContainerClientPort)
|
||||
GoogleEndpointID = render.MakeEndpointID("", fixture.GoogleIP, fixture.GooglePort)
|
||||
|
||||
RenderedEndpoints = (render.RenderableNodes{
|
||||
Client54001EndpointID: {
|
||||
ID: Client54001EndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerEndpointID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(10),
|
||||
EgressByteCount: newu64(100),
|
||||
},
|
||||
},
|
||||
Client54002EndpointID: {
|
||||
ID: Client54002EndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerEndpointID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(20),
|
||||
EgressByteCount: newu64(200),
|
||||
},
|
||||
},
|
||||
ServerEndpointID: {
|
||||
ID: ServerEndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
IngressPacketCount: newu64(210),
|
||||
IngressByteCount: newu64(2100),
|
||||
},
|
||||
},
|
||||
UnknownClient1EndpointID: {
|
||||
ID: UnknownClient1EndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerEndpointID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
},
|
||||
},
|
||||
UnknownClient2EndpointID: {
|
||||
ID: UnknownClient2EndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerEndpointID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(40),
|
||||
EgressByteCount: newu64(400),
|
||||
},
|
||||
},
|
||||
UnknownClient3EndpointID: {
|
||||
ID: UnknownClient3EndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerEndpointID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(50),
|
||||
EgressByteCount: newu64(500),
|
||||
},
|
||||
},
|
||||
RandomClientEndpointID: {
|
||||
ID: RandomClientEndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerEndpointID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(60),
|
||||
EgressByteCount: newu64(600),
|
||||
},
|
||||
},
|
||||
NonContainerEndpointID: {
|
||||
ID: NonContainerEndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(GoogleEndpointID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
GoogleEndpointID: {
|
||||
ID: GoogleEndpointID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode(),
|
||||
},
|
||||
}).Prune()
|
||||
|
||||
ClientProcess1ID = render.MakeProcessID(fixture.ClientHostID, fixture.Client1PID)
|
||||
ClientProcess2ID = render.MakeProcessID(fixture.ClientHostID, fixture.Client2PID)
|
||||
ServerProcessID = render.MakeProcessID(fixture.ServerHostID, fixture.ServerPID)
|
||||
nonContainerProcessID = render.MakeProcessID(fixture.ServerHostID, fixture.NonContainerPID)
|
||||
unknownPseudoNode1ID = render.MakePseudoNodeID(fixture.UnknownClient1IP, fixture.ServerIP, fixture.ServerPort)
|
||||
unknownPseudoNode2ID = render.MakePseudoNodeID(fixture.UnknownClient3IP, fixture.ServerIP, fixture.ServerPort)
|
||||
|
||||
unknownPseudoNode1 = func(adjacent string) render.RenderableNode {
|
||||
return render.RenderableNode{
|
||||
ID: unknownPseudoNode1ID,
|
||||
LabelMajor: "10.10.10.10",
|
||||
LabelMajor: fixture.UnknownClient1IP,
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(adjacent),
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[UnknownClient1EndpointID],
|
||||
RenderedEndpoints[UnknownClient2EndpointID],
|
||||
),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(70),
|
||||
EgressByteCount: newu64(700),
|
||||
@@ -35,10 +130,13 @@ var (
|
||||
unknownPseudoNode2 = func(adjacent string) render.RenderableNode {
|
||||
return render.RenderableNode{
|
||||
ID: unknownPseudoNode2ID,
|
||||
LabelMajor: "10.10.10.11",
|
||||
LabelMajor: fixture.UnknownClient3IP,
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(adjacent),
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[UnknownClient3EndpointID],
|
||||
),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(50),
|
||||
EgressByteCount: newu64(500),
|
||||
@@ -49,10 +147,13 @@ var (
|
||||
return render.RenderableNode{
|
||||
ID: render.IncomingInternetID,
|
||||
LabelMajor: render.InboundMajor,
|
||||
LabelMinor: render.RequestsMinor,
|
||||
LabelMinor: render.InboundMinor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode().WithAdjacent(adjacent),
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[RandomClientEndpointID],
|
||||
),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(60),
|
||||
EgressByteCount: newu64(600),
|
||||
@@ -62,16 +163,15 @@ var (
|
||||
theOutgoingInternetNode = render.RenderableNode{
|
||||
ID: render.OutgoingInternetID,
|
||||
LabelMajor: render.OutboundMajor,
|
||||
LabelMinor: render.RequestsMinor,
|
||||
LabelMinor: render.OutboundMinor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[GoogleEndpointID],
|
||||
),
|
||||
}
|
||||
ClientProcess1ID = render.MakeProcessID(fixture.ClientHostID, fixture.Client1PID)
|
||||
ClientProcess2ID = render.MakeProcessID(fixture.ClientHostID, fixture.Client2PID)
|
||||
ServerProcessID = render.MakeProcessID(fixture.ServerHostID, fixture.ServerPID)
|
||||
nonContainerProcessID = render.MakeProcessID(fixture.ServerHostID, fixture.NonContainerPID)
|
||||
|
||||
RenderedProcesses = (render.RenderableNodes{
|
||||
ClientProcess1ID: {
|
||||
@@ -79,9 +179,11 @@ var (
|
||||
LabelMajor: fixture.Client1Name,
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ClientHostID, fixture.Client1PID),
|
||||
Rank: fixture.Client1Name,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode().WithAdjacent(ServerProcessID),
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54001EndpointID],
|
||||
),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(10),
|
||||
EgressByteCount: newu64(100),
|
||||
@@ -92,9 +194,11 @@ var (
|
||||
LabelMajor: fixture.Client2Name,
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ClientHostID, fixture.Client2PID),
|
||||
Rank: fixture.Client2Name,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode().WithAdjacent(ServerProcessID),
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54002EndpointID],
|
||||
),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(20),
|
||||
EgressByteCount: newu64(200),
|
||||
@@ -105,22 +209,26 @@ var (
|
||||
LabelMajor: fixture.ServerName,
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ServerHostID, fixture.ServerPID),
|
||||
Rank: fixture.ServerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode(),
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[ServerEndpointID],
|
||||
),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
IngressPacketCount: newu64(210),
|
||||
IngressByteCount: newu64(2100),
|
||||
},
|
||||
},
|
||||
nonContainerProcessID: {
|
||||
ID: nonContainerProcessID,
|
||||
LabelMajor: fixture.NonContainerName,
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ServerHostID, fixture.NonContainerPID),
|
||||
Rank: fixture.NonContainerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode().WithAdjacent(render.OutgoingInternetID),
|
||||
ID: nonContainerProcessID,
|
||||
LabelMajor: fixture.NonContainerName,
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ServerHostID, fixture.NonContainerPID),
|
||||
Rank: fixture.NonContainerName,
|
||||
Shape: square,
|
||||
Node: report.MakeNode().WithAdjacent(render.OutgoingInternetID),
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[NonContainerEndpointID],
|
||||
),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
unknownPseudoNode1ID: unknownPseudoNode1(ServerProcessID),
|
||||
@@ -129,22 +237,19 @@ var (
|
||||
render.OutgoingInternetID: theOutgoingInternetNode,
|
||||
}).Prune()
|
||||
|
||||
ServerProcessRenderedID = render.MakeProcessID(fixture.ServerHostID, fixture.ServerPID)
|
||||
ClientProcess1RenderedID = render.MakeProcessID(fixture.ClientHostID, fixture.Client1PID)
|
||||
ClientProcess2RenderedID = render.MakeProcessID(fixture.ClientHostID, fixture.Client2PID)
|
||||
|
||||
RenderedProcessNames = (render.RenderableNodes{
|
||||
fixture.Client1Name: {
|
||||
ID: fixture.Client1Name,
|
||||
LabelMajor: fixture.Client1Name,
|
||||
LabelMinor: "2 processes",
|
||||
Rank: fixture.Client1Name,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54001EndpointID],
|
||||
RenderedEndpoints[Client54002EndpointID],
|
||||
RenderedProcesses[ClientProcess1ID],
|
||||
RenderedProcesses[ClientProcess2ID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(fixture.ServerName),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -157,11 +262,11 @@ var (
|
||||
LabelMajor: fixture.ServerName,
|
||||
LabelMinor: "1 process",
|
||||
Rank: fixture.ServerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[ServerEndpointID],
|
||||
RenderedProcesses[ServerProcessID],
|
||||
),
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -174,11 +279,11 @@ var (
|
||||
LabelMajor: fixture.NonContainerName,
|
||||
LabelMinor: "1 process",
|
||||
Rank: fixture.NonContainerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[NonContainerEndpointID],
|
||||
RenderedProcesses[nonContainerProcessID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(render.OutgoingInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
@@ -189,37 +294,37 @@ var (
|
||||
render.OutgoingInternetID: theOutgoingInternetNode,
|
||||
}).Prune()
|
||||
|
||||
ServerContainerRenderedID = render.MakeContainerID(fixture.ServerContainerID)
|
||||
ClientContainerRenderedID = render.MakeContainerID(fixture.ClientContainerID)
|
||||
ClientContainerID = render.MakeContainerID(fixture.ClientContainerID)
|
||||
ServerContainerID = render.MakeContainerID(fixture.ServerContainerID)
|
||||
uncontainedServerID = render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID)
|
||||
|
||||
RenderedContainers = (render.RenderableNodes{
|
||||
ClientContainerRenderedID: {
|
||||
ID: ClientContainerRenderedID,
|
||||
ClientContainerID: {
|
||||
ID: ClientContainerID,
|
||||
LabelMajor: "client",
|
||||
LabelMinor: fixture.ClientHostName,
|
||||
Rank: fixture.ClientContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54001EndpointID],
|
||||
RenderedEndpoints[Client54002EndpointID],
|
||||
RenderedProcesses[ClientProcess1ID],
|
||||
RenderedProcesses[ClientProcess2ID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(ServerContainerRenderedID),
|
||||
Node: report.MakeNode().WithAdjacent(ServerContainerID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
},
|
||||
ControlNode: fixture.ClientContainerNodeID,
|
||||
},
|
||||
ServerContainerRenderedID: {
|
||||
ID: ServerContainerRenderedID,
|
||||
ServerContainerID: {
|
||||
ID: ServerContainerID,
|
||||
LabelMajor: "server",
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: fixture.ServerContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[ServerEndpointID],
|
||||
RenderedProcesses[ServerProcessID],
|
||||
),
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -232,54 +337,57 @@ var (
|
||||
ID: uncontainedServerID,
|
||||
LabelMajor: render.UncontainedMajor,
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
Pseudo: true,
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[NonContainerEndpointID],
|
||||
RenderedProcesses[nonContainerProcessID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(render.OutgoingInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
render.IncomingInternetID: theIncomingInternetNode(ServerContainerRenderedID),
|
||||
// unknownPseudoNode1ID: unknownPseudoNode1(ServerContainerID),
|
||||
// unknownPseudoNode2ID: unknownPseudoNode2(ServerContainerID),
|
||||
render.IncomingInternetID: theIncomingInternetNode(ServerContainerID),
|
||||
render.OutgoingInternetID: theOutgoingInternetNode,
|
||||
}).Prune()
|
||||
|
||||
ClientContainerImageRenderedName = render.MakeContainerImageID(fixture.ClientContainerImageName)
|
||||
ServerContainerImageRenderedName = render.MakeContainerImageID(fixture.ServerContainerImageName)
|
||||
ClientContainerImageID = render.MakeContainerImageID(fixture.ClientContainerImageName)
|
||||
ServerContainerImageID = render.MakeContainerImageID(fixture.ServerContainerImageName)
|
||||
|
||||
RenderedContainerImages = (render.RenderableNodes{
|
||||
ClientContainerImageRenderedName: {
|
||||
ID: ClientContainerImageRenderedName,
|
||||
ClientContainerImageID: {
|
||||
ID: ClientContainerImageID,
|
||||
LabelMajor: fixture.ClientContainerImageName,
|
||||
LabelMinor: "1 container",
|
||||
Rank: fixture.ClientContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ClientContainerNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54001EndpointID],
|
||||
RenderedEndpoints[Client54002EndpointID],
|
||||
RenderedProcesses[ClientProcess1ID],
|
||||
RenderedProcesses[ClientProcess2ID],
|
||||
RenderedContainers[ClientContainerID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(ServerContainerImageRenderedName),
|
||||
Node: report.MakeNode().WithAdjacent(ServerContainerImageID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
},
|
||||
},
|
||||
ServerContainerImageRenderedName: {
|
||||
ID: ServerContainerImageRenderedName,
|
||||
ServerContainerImageID: {
|
||||
ID: ServerContainerImageID,
|
||||
LabelMajor: fixture.ServerContainerImageName,
|
||||
LabelMinor: "1 container",
|
||||
Rank: fixture.ServerContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[ServerEndpointID],
|
||||
RenderedProcesses[ServerProcessID],
|
||||
RenderedContainers[ServerContainerID],
|
||||
),
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -291,36 +399,113 @@ var (
|
||||
ID: uncontainedServerID,
|
||||
LabelMajor: render.UncontainedMajor,
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
Pseudo: true,
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[NonContainerEndpointID],
|
||||
RenderedProcesses[nonContainerProcessID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(render.OutgoingInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
render.IncomingInternetID: theIncomingInternetNode(ServerContainerImageRenderedName),
|
||||
// unknownPseudoNode1ID: unknownPseudoNode1(ServerContainerImageID),
|
||||
// unknownPseudoNode2ID: unknownPseudoNode2(ServerContainerImageID),
|
||||
render.IncomingInternetID: theIncomingInternetNode(ServerContainerImageID),
|
||||
render.OutgoingInternetID: theOutgoingInternetNode,
|
||||
}).Prune()
|
||||
|
||||
ServerHostRenderedID = render.MakeHostID(fixture.ServerHostID)
|
||||
ClientHostRenderedID = render.MakeHostID(fixture.ClientHostID)
|
||||
pseudoHostID1 = render.MakePseudoNodeID(fixture.UnknownClient1IP, fixture.ServerIP)
|
||||
pseudoHostID2 = render.MakePseudoNodeID(fixture.UnknownClient3IP, fixture.ServerIP)
|
||||
ClientAddressID = render.MakeAddressID(fixture.ClientHostID, fixture.ClientIP)
|
||||
ServerAddressID = render.MakeAddressID(fixture.ServerHostID, fixture.ServerIP)
|
||||
unknownPseudoAddress1ID = render.MakePseudoNodeID("10.10.10.10", fixture.ServerIP)
|
||||
unknownPseudoAddress2ID = render.MakePseudoNodeID("10.10.10.11", fixture.ServerIP)
|
||||
|
||||
RenderedAddresses = (render.RenderableNodes{
|
||||
ClientAddressID: {
|
||||
ID: ClientAddressID,
|
||||
LabelMajor: fixture.ClientIP,
|
||||
LabelMinor: fixture.ClientHostID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerAddressID),
|
||||
},
|
||||
ServerAddressID: {
|
||||
ID: ServerAddressID,
|
||||
LabelMajor: fixture.ServerIP,
|
||||
LabelMinor: fixture.ServerHostID,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode(),
|
||||
},
|
||||
unknownPseudoAddress1ID: {
|
||||
ID: unknownPseudoAddress1ID,
|
||||
LabelMajor: "10.10.10.10",
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerAddressID),
|
||||
},
|
||||
unknownPseudoAddress2ID: {
|
||||
ID: unknownPseudoAddress2ID,
|
||||
LabelMajor: "10.10.10.11",
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerAddressID),
|
||||
},
|
||||
render.IncomingInternetID: {
|
||||
ID: render.IncomingInternetID,
|
||||
LabelMajor: render.InboundMajor,
|
||||
LabelMinor: render.InboundMinor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode().WithAdjacent(ServerAddressID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
}).Prune()
|
||||
|
||||
ServerHostID = render.MakeHostID(fixture.ServerHostID)
|
||||
ClientHostID = render.MakeHostID(fixture.ClientHostID)
|
||||
pseudoHostID1 = render.MakePseudoNodeID(fixture.UnknownClient1IP, fixture.ServerIP)
|
||||
pseudoHostID2 = render.MakePseudoNodeID(fixture.UnknownClient3IP, fixture.ServerIP)
|
||||
|
||||
RenderedHosts = (render.RenderableNodes{
|
||||
ServerHostRenderedID: {
|
||||
ID: ServerHostRenderedID,
|
||||
ClientHostID: {
|
||||
ID: ClientHostID,
|
||||
LabelMajor: "client", // before first .
|
||||
LabelMinor: "hostname.com", // after first .
|
||||
Rank: "hostname.com",
|
||||
Shape: circle,
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54001EndpointID],
|
||||
RenderedEndpoints[Client54002EndpointID],
|
||||
RenderedProcesses[ClientProcess1ID],
|
||||
RenderedProcesses[ClientProcess2ID],
|
||||
RenderedContainers[ClientContainerID],
|
||||
RenderedContainerImages[ClientContainerImageID],
|
||||
RenderedAddresses[ClientAddressID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
},
|
||||
},
|
||||
ServerHostID: {
|
||||
ID: ServerHostID,
|
||||
LabelMajor: "server", // before first .
|
||||
LabelMinor: "hostname.com", // after first .
|
||||
Rank: "hostname.com",
|
||||
Pseudo: false,
|
||||
Shape: circle,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ServerProcessNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[ServerEndpointID],
|
||||
|
||||
RenderedProcesses[ServerProcessID],
|
||||
RenderedContainers[ServerContainerID],
|
||||
RenderedContainerImages[ServerContainerImageID],
|
||||
|
||||
RenderedAddresses[ServerAddressID],
|
||||
|
||||
// See #1102
|
||||
// RemappedEndpoints[NonContainerEndpointID],
|
||||
// RenderedEndpoints[NonContainerPseudoEndpointID],
|
||||
// RenderedProcesses[nonContainerProcessID],
|
||||
),
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -328,34 +513,17 @@ var (
|
||||
IngressByteCount: newu64(2100),
|
||||
},
|
||||
},
|
||||
ClientHostRenderedID: {
|
||||
ID: ClientHostRenderedID,
|
||||
LabelMajor: "client", // before first .
|
||||
LabelMinor: "hostname.com", // after first .
|
||||
Rank: "hostname.com",
|
||||
Pseudo: false,
|
||||
Shape: circle,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Container.Nodes[fixture.ClientContainerNodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(30),
|
||||
EgressByteCount: newu64(300),
|
||||
},
|
||||
},
|
||||
|
||||
pseudoHostID1: {
|
||||
ID: pseudoHostID1,
|
||||
LabelMajor: fixture.UnknownClient1IP,
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostRenderedID),
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
// RenderedEndpoints[unknownPseudoNode2ID],
|
||||
// RenderedAddresses[unknownPseudoAddress1ID],
|
||||
),
|
||||
},
|
||||
pseudoHostID2: {
|
||||
@@ -363,17 +531,24 @@ var (
|
||||
LabelMajor: fixture.UnknownClient3IP,
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostRenderedID),
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
// RenderedEndpoints[unknownPseudoNode2ID],
|
||||
// RenderedAddresses[unknownPseudoAddress2ID],
|
||||
),
|
||||
},
|
||||
render.IncomingInternetID: {
|
||||
ID: render.IncomingInternetID,
|
||||
LabelMajor: render.InboundMajor,
|
||||
LabelMinor: render.RequestsMinor,
|
||||
LabelMinor: render.InboundMinor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostRenderedID),
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
// RenderedEndpoints[render.TheInternetID],
|
||||
),
|
||||
},
|
||||
}).Prune()
|
||||
|
||||
@@ -386,14 +561,13 @@ var (
|
||||
LabelMajor: "pong-a",
|
||||
LabelMinor: "1 container",
|
||||
Rank: "ping/pong-a",
|
||||
Pseudo: false,
|
||||
Shape: heptagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ClientContainerNodeID],
|
||||
fixture.Report.ContainerImage.Nodes[fixture.ClientContainerImageNodeID],
|
||||
fixture.Report.Pod.Nodes[fixture.ClientPodNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54001EndpointID],
|
||||
RenderedEndpoints[Client54002EndpointID],
|
||||
RenderedProcesses[ClientProcess1ID],
|
||||
RenderedProcesses[ClientProcess2ID],
|
||||
RenderedContainers[ClientContainerID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(ServerPodRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -406,13 +580,11 @@ var (
|
||||
LabelMajor: "pong-b",
|
||||
LabelMinor: "1 container",
|
||||
Rank: "ping/pong-b",
|
||||
Pseudo: false,
|
||||
Shape: heptagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
fixture.Report.ContainerImage.Nodes[fixture.ServerContainerImageNodeID],
|
||||
fixture.Report.Pod.Nodes[fixture.ServerPodNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[ServerEndpointID],
|
||||
RenderedProcesses[ServerProcessID],
|
||||
RenderedContainers[ServerContainerID],
|
||||
),
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -424,16 +596,18 @@ var (
|
||||
ID: uncontainedServerID,
|
||||
LabelMajor: render.UncontainedMajor,
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[NonContainerEndpointID],
|
||||
RenderedProcesses[nonContainerProcessID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(render.OutgoingInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
// unknownPseudoNode1ID: unknownPseudoNode1(ServerPodRenderedID),
|
||||
// unknownPseudoNode2ID: unknownPseudoNode2(ServerPodRenderedID),
|
||||
render.IncomingInternetID: theIncomingInternetNode(ServerPodRenderedID),
|
||||
render.OutgoingInternetID: theOutgoingInternetNode,
|
||||
}).Prune()
|
||||
@@ -446,19 +620,19 @@ var (
|
||||
LabelMajor: "pongservice",
|
||||
LabelMinor: "2 pods",
|
||||
Rank: fixture.ServiceID,
|
||||
Pseudo: false,
|
||||
Shape: heptagon,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ClientContainerNodeID],
|
||||
fixture.Report.ContainerImage.Nodes[fixture.ClientContainerImageNodeID],
|
||||
fixture.Report.Pod.Nodes[fixture.ClientPodNodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
fixture.Report.ContainerImage.Nodes[fixture.ServerContainerImageNodeID],
|
||||
fixture.Report.Pod.Nodes[fixture.ServerPodNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[Client54001EndpointID],
|
||||
RenderedEndpoints[Client54002EndpointID],
|
||||
RenderedEndpoints[ServerEndpointID],
|
||||
RenderedProcesses[ClientProcess1ID],
|
||||
RenderedProcesses[ClientProcess2ID],
|
||||
RenderedProcesses[ServerProcessID],
|
||||
RenderedContainers[ClientContainerID],
|
||||
RenderedContainers[ServerContainerID],
|
||||
RenderedPods[ClientPodRenderedID],
|
||||
RenderedPods[ServerPodRenderedID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(ServiceRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
@@ -472,16 +646,18 @@ var (
|
||||
ID: uncontainedServerID,
|
||||
LabelMajor: render.UncontainedMajor,
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
Children: render.MakeRenderableNodeSet(
|
||||
RenderedEndpoints[NonContainerEndpointID],
|
||||
RenderedProcesses[nonContainerProcessID],
|
||||
),
|
||||
Node: report.MakeNode().WithAdjacent(render.OutgoingInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
// unknownPseudoNode1ID: unknownPseudoNode1(ServiceRenderedID),
|
||||
// unknownPseudoNode2ID: unknownPseudoNode2(ServiceRenderedID),
|
||||
render.IncomingInternetID: theIncomingInternetNode(ServiceRenderedID),
|
||||
render.OutgoingInternetID: theOutgoingInternetNode,
|
||||
}).Prune()
|
||||
|
||||
10
render/id.go
10
render/id.go
@@ -4,6 +4,16 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseEndpointID parses endpoint IDs
|
||||
func ParseEndpointID(id string) (host, ip, port string, ok bool) {
|
||||
parts := strings.SplitN(id, ":", 4)
|
||||
if len(parts) != 4 || parts[0] != "endpoint" {
|
||||
return
|
||||
}
|
||||
host, ip, port, ok = parts[1], parts[2], parts[3], true
|
||||
return
|
||||
}
|
||||
|
||||
// makeID is the generic ID maker
|
||||
func makeID(prefix string, parts ...string) string {
|
||||
return strings.Join(append([]string{prefix}, parts...), ":")
|
||||
|
||||
@@ -23,9 +23,10 @@ const (
|
||||
TheInternetID = "theinternet"
|
||||
IncomingInternetID = "in-" + TheInternetID
|
||||
OutgoingInternetID = "out-" + TheInternetID
|
||||
InboundMajor = "Inbound"
|
||||
OutboundMajor = "Outbound"
|
||||
RequestsMinor = "Requests"
|
||||
InboundMajor = "The Internet"
|
||||
OutboundMajor = "The Internet"
|
||||
InboundMinor = "Inbound connections"
|
||||
OutboundMinor = "Outbound connections"
|
||||
|
||||
ContainersKey = "containers"
|
||||
ipsKey = "ips"
|
||||
@@ -50,19 +51,20 @@ func theInternetNode(m RenderableNode) RenderableNode {
|
||||
if len(m.Adjacency) > 0 {
|
||||
node.ID = IncomingInternetID
|
||||
node.LabelMajor = InboundMajor
|
||||
node.LabelMinor = RequestsMinor
|
||||
node.LabelMinor = InboundMinor
|
||||
} else {
|
||||
node.ID = OutgoingInternetID
|
||||
node.LabelMajor = OutboundMajor
|
||||
node.LabelMinor = RequestsMinor
|
||||
node.LabelMinor = OutboundMinor
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// MapEndpointIdentity maps an endpoint topology node to a single endpoint
|
||||
// renderable node. As it is only ever run on endpoint topology nodes, we
|
||||
// expect that certain keys are present.
|
||||
func MapEndpointIdentity(m RenderableNode, local report.Networks) RenderableNodes {
|
||||
// MapEndpointIdentity remaps endpoints to have an id format consistent
|
||||
// with render/id.go; no pseudo nodes are introduced in this step, so
|
||||
// that pseudo nodes introduces later are guaranteed to have endpoints
|
||||
// as children. This is needed to construct the connection details tables.
|
||||
func MapEndpointIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
addr, ok := m.Latest.Lookup(endpoint.Addr)
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
@@ -79,46 +81,8 @@ func MapEndpointIdentity(m RenderableNode, local report.Networks) RenderableNode
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
// Nodes without a hostid are treated as psuedo nodes
|
||||
if _, ok = m.Latest.Lookup(report.HostNodeID); !ok {
|
||||
// If the dstNodeAddr is not in a network local to this report, we emit an
|
||||
// internet node
|
||||
if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
|
||||
return RenderableNodes{TheInternetID: theInternetNode(m)}
|
||||
}
|
||||
|
||||
// We are a 'client' pseudo node if the port is in the ephemeral port range.
|
||||
// Linux uses 32768 to 61000, IANA suggests 49152 to 65535.
|
||||
if p, err := strconv.Atoi(port); err == nil && len(m.Adjacency) > 0 && p >= 32768 && p < 65535 {
|
||||
// We only exist if there is something in our adjacency
|
||||
// Generate a single pseudo node for every (client ip, server ip, server port)
|
||||
dstNodeID := m.Adjacency[0]
|
||||
serverIP, serverPort := trySplitAddr(dstNodeID)
|
||||
outputID := MakePseudoNodeID(addr, serverIP, serverPort)
|
||||
return RenderableNodes{outputID: newDerivedPseudoNode(outputID, addr, m)}
|
||||
}
|
||||
|
||||
// Otherwise (the server node is missing), generate a pseudo node for every (server ip, server port)
|
||||
outputID := MakePseudoNodeID(addr, port)
|
||||
if port != "" {
|
||||
return RenderableNodes{outputID: newDerivedPseudoNode(outputID, addr+":"+port, m)}
|
||||
}
|
||||
return RenderableNodes{outputID: newDerivedPseudoNode(outputID, addr, m)}
|
||||
}
|
||||
|
||||
var (
|
||||
id = MakeEndpointID(report.ExtractHostID(m.Node), addr, port)
|
||||
major = fmt.Sprintf("%s:%s", addr, port)
|
||||
minor = report.ExtractHostID(m.Node)
|
||||
rank = major
|
||||
)
|
||||
|
||||
pid, pidOK := m.Latest.Lookup(process.PID)
|
||||
if pidOK {
|
||||
minor = fmt.Sprintf("%s (%s)", minor, pid)
|
||||
}
|
||||
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
id := MakeEndpointID(report.ExtractHostID(m.Node), addr, port)
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, "", "", "", m)}
|
||||
}
|
||||
|
||||
// MapProcessIdentity maps a process topology node to a process renderable
|
||||
@@ -155,10 +119,9 @@ func MapContainerIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
id = MakeContainerID(containerID)
|
||||
major, _ = GetRenderableContainerName(m.Node)
|
||||
minor = report.ExtractHostID(m.Node)
|
||||
rank, _ = m.Latest.Lookup(docker.ImageID)
|
||||
)
|
||||
|
||||
node := NewRenderableNodeWith(id, major, minor, rank, m)
|
||||
node := NewRenderableNodeWith(id, major, minor, "", m)
|
||||
node.ControlNode = m.ID
|
||||
node.Shape = Hexagon
|
||||
return RenderableNodes{id: node}
|
||||
@@ -289,10 +252,9 @@ func MapAddressIdentity(m RenderableNode, local report.Networks) RenderableNodes
|
||||
id = MakeAddressID(report.ExtractHostID(m.Node), addr)
|
||||
major = addr
|
||||
minor = report.ExtractHostID(m.Node)
|
||||
rank = major
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, "", m)}
|
||||
}
|
||||
|
||||
// MapHostIdentity maps a host topology node to a host renderable node. As it
|
||||
@@ -395,7 +357,7 @@ func MapIP2Container(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
// Propogate the internet pseudo node
|
||||
// Propagate the internet pseudo node
|
||||
if strings.HasSuffix(n.ID, TheInternetID) {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
@@ -425,9 +387,45 @@ func MapIP2Container(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
// format for a process, but without any Major or Minor labels.
|
||||
// It does not have enough info to do that, and the resulting graph
|
||||
// must be merged with a process graph to get that info.
|
||||
func MapEndpoint2Process(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
func MapEndpoint2Process(n RenderableNode, local report.Networks) RenderableNodes {
|
||||
// Nodes without a hostid are treated as psuedo nodes
|
||||
if _, ok := n.Latest.Lookup(report.HostNodeID); !ok {
|
||||
var node RenderableNode
|
||||
|
||||
addr, ok := n.Latest.Lookup(endpoint.Addr)
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
port, ok := n.Latest.Lookup(endpoint.Port)
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
|
||||
// If the dstNodeAddr is not in a network local to this report, we emit an
|
||||
// internet node
|
||||
node = theInternetNode(n)
|
||||
|
||||
} else if p, err := strconv.Atoi(port); err == nil && len(n.Adjacency) > 0 && p >= 32768 && p < 65535 {
|
||||
// We are a 'client' pseudo node if the port is in the ephemeral port range.
|
||||
// Linux uses 32768 to 61000, IANA suggests 49152 to 65535.
|
||||
// We only exist if there is something in our adjacency
|
||||
// Generate a single pseudo node for every (client ip, server ip, server port)
|
||||
_, serverIP, serverPort, _ := ParseEndpointID(n.Adjacency[0])
|
||||
node = newDerivedPseudoNode(MakePseudoNodeID(addr, serverIP, serverPort), addr, n)
|
||||
|
||||
} else if port != "" {
|
||||
// Otherwise (the server node is missing), generate a pseudo node for every (server ip, server port)
|
||||
node = newDerivedPseudoNode(MakePseudoNodeID(addr, port), addr+":"+port, n)
|
||||
|
||||
} else {
|
||||
// Empty port for some reason...
|
||||
node = newDerivedPseudoNode(MakePseudoNodeID(addr, port), addr, n)
|
||||
}
|
||||
|
||||
node.Children = node.Children.Add(n)
|
||||
return RenderableNodes{node.ID: node}
|
||||
}
|
||||
|
||||
pid, ok := n.Node.Latest.Lookup(process.PID)
|
||||
@@ -438,6 +436,7 @@ func MapEndpoint2Process(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
id := MakeProcessID(report.ExtractHostID(n.Node), pid)
|
||||
node := NewDerivedNode(id, n.WithParents(report.EmptySets))
|
||||
node.Shape = Square
|
||||
node.Children = node.Children.Add(n)
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
@@ -487,7 +486,7 @@ func MapProcess2Container(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
node.Stack = true
|
||||
}
|
||||
|
||||
node.Children = node.Children.Add(n.Node)
|
||||
node.Children = node.Children.Add(n)
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
@@ -513,7 +512,7 @@ func MapProcess2Name(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
node.Counters = node.Node.Counters.Add(processesKey, 1)
|
||||
node.Node.Topology = "process_name"
|
||||
node.Node.ID = name
|
||||
node.Children = node.Children.Add(n.Node)
|
||||
node.Children = node.Children.Add(n)
|
||||
node.Shape = Square
|
||||
node.Stack = true
|
||||
return RenderableNodes{name: node}
|
||||
@@ -567,7 +566,7 @@ func MapContainer2ContainerImage(n RenderableNode, _ report.Networks) Renderable
|
||||
result.Node.Counters = result.Node.Counters.Add(ContainersKey, 1)
|
||||
|
||||
// Add the container as a child of the new image node
|
||||
result.Children = result.Children.Add(n.Node)
|
||||
result.Children = result.Children.Add(n)
|
||||
|
||||
result.Node.Topology = "container_image"
|
||||
result.Node.ID = report.MakeContainerImageNodeID(imageID)
|
||||
@@ -576,42 +575,6 @@ func MapContainer2ContainerImage(n RenderableNode, _ report.Networks) Renderable
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
|
||||
// MapPod2Service maps pod RenderableNodes to service RenderableNodes.
|
||||
//
|
||||
// If this function is given a node without a kubernetes_pod_id
|
||||
// (including other pseudo nodes), it will produce an "Uncontained"
|
||||
// pseudo node.
|
||||
//
|
||||
// Otherwise, this function will produce a node with the correct ID
|
||||
// format for a container, but without any Major or Minor labels.
|
||||
// It does not have enough info to do that, and the resulting graph
|
||||
// must be merged with a pod graph to get that info.
|
||||
func MapPod2Service(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
// Propogate all pseudo nodes
|
||||
if n.Pseudo {
|
||||
return RenderableNodes{n.ID: n}
|
||||
}
|
||||
|
||||
// Otherwise, if some some reason the pod doesn't have a service_ids (maybe
|
||||
// slightly out of sync reports, or its not in a service), just drop it
|
||||
ids, ok := n.Node.Latest.Lookup(kubernetes.ServiceIDs)
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
result := RenderableNodes{}
|
||||
for _, serviceID := range strings.Fields(ids) {
|
||||
id := MakeServiceID(serviceID)
|
||||
n := NewDerivedNode(id, n.WithParents(report.EmptySets))
|
||||
n.Node.Counters = n.Node.Counters.Add(podsKey, 1)
|
||||
n.Children = n.Children.Add(n.Node)
|
||||
n.Shape = Heptagon
|
||||
n.Stack = true
|
||||
result[id] = n
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ImageNameWithoutVersion splits the image name apart, returning the name
|
||||
// without the version, if possible
|
||||
func ImageNameWithoutVersion(name string) string {
|
||||
@@ -671,7 +634,7 @@ func MapX2Host(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
}
|
||||
id := MakeHostID(report.ExtractHostID(n.Node))
|
||||
result := NewDerivedNode(id, n.WithParents(report.EmptySets))
|
||||
result.Children = result.Children.Add(n.Node)
|
||||
result.Children = result.Children.Add(n)
|
||||
result.Shape = Circle
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
@@ -717,11 +680,47 @@ func MapContainer2Pod(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
})
|
||||
}
|
||||
|
||||
result.Children = result.Children.Add(n.Node)
|
||||
result.Shape = Heptagon
|
||||
result.Children = result.Children.Add(n)
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
|
||||
// MapPod2Service maps pod RenderableNodes to service RenderableNodes.
|
||||
//
|
||||
// If this function is given a node without a kubernetes_pod_id
|
||||
// (including other pseudo nodes), it will produce an "Uncontained"
|
||||
// pseudo node.
|
||||
//
|
||||
// Otherwise, this function will produce a node with the correct ID
|
||||
// format for a container, but without any Major or Minor labels.
|
||||
// It does not have enough info to do that, and the resulting graph
|
||||
// must be merged with a pod graph to get that info.
|
||||
func MapPod2Service(pod RenderableNode, _ report.Networks) RenderableNodes {
|
||||
// Propogate all pseudo nodes
|
||||
if pod.Pseudo {
|
||||
return RenderableNodes{pod.ID: pod}
|
||||
}
|
||||
|
||||
// Otherwise, if some some reason the pod doesn't have a service_ids (maybe
|
||||
// slightly out of sync reports, or its not in a service), just drop it
|
||||
ids, ok := pod.Node.Latest.Lookup(kubernetes.ServiceIDs)
|
||||
if !ok {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
|
||||
result := RenderableNodes{}
|
||||
for _, serviceID := range strings.Fields(ids) {
|
||||
id := MakeServiceID(serviceID)
|
||||
node := NewDerivedNode(id, pod.WithParents(report.EmptySets))
|
||||
node.Node.Counters = node.Node.Counters.Add(podsKey, 1)
|
||||
node.Children = node.Children.Add(pod)
|
||||
node.Shape = Heptagon
|
||||
node.Stack = true
|
||||
result[id] = node
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MapContainer2Hostname maps container RenderableNodes to 'hostname' renderabled nodes..
|
||||
func MapContainer2Hostname(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
// Propogate all pseudo nodes
|
||||
@@ -744,7 +743,7 @@ func MapContainer2Hostname(n RenderableNode, _ report.Networks) RenderableNodes
|
||||
result.Counters = result.Counters.Add(ContainersKey, 1)
|
||||
result.Node.Topology = "container_hostname"
|
||||
result.Node.ID = id
|
||||
result.Children = result.Children.Add(n.Node)
|
||||
result.Children = result.Children.Add(n)
|
||||
result.Shape = Hexagon
|
||||
result.Stack = true
|
||||
return RenderableNodes{id: result}
|
||||
@@ -784,20 +783,3 @@ func MapCountPods(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
}
|
||||
return RenderableNodes{output.ID: output}
|
||||
}
|
||||
|
||||
// trySplitAddr is basically ParseArbitraryNodeID, since its callsites
|
||||
// (pseudo funcs) just have opaque node IDs and don't know what topology they
|
||||
// come from. Without changing how pseudo funcs work, we can't make it much
|
||||
// smarter.
|
||||
//
|
||||
// TODO change how pseudofuncs work, and eliminate this helper.
|
||||
func trySplitAddr(addr string) (string, string) {
|
||||
fields := strings.SplitN(addr, report.ScopeDelim, 3)
|
||||
if len(fields) == 3 {
|
||||
return fields[1], fields[2]
|
||||
}
|
||||
if len(fields) == 2 {
|
||||
return fields[1], ""
|
||||
}
|
||||
panic(addr)
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@ import (
|
||||
// an element of a topology. It should contain information that's relevant
|
||||
// to rendering a node when there are many nodes visible at once.
|
||||
type RenderableNode struct {
|
||||
ID string `json:"id"` //
|
||||
LabelMajor string `json:"label_major"` // e.g. "process", human-readable
|
||||
LabelMinor string `json:"label_minor,omitempty"` // e.g. "hostname", human-readable, optional
|
||||
Rank string `json:"rank"` // to help the layout engine
|
||||
Pseudo bool `json:"pseudo,omitempty"` // sort-of a placeholder node, for rendering purposes
|
||||
Children report.NodeSet `json:"children,omitempty"` // Nodes which have been grouped into this one
|
||||
ControlNode string `json:"-"` // ID of node from which to show the controls in the UI
|
||||
Shape string `json:"shape"` // Shape node should be rendered as
|
||||
Stack bool `json:"stack"` // Should UI render this node as a stack?
|
||||
ID string `json:"id"` //
|
||||
LabelMajor string `json:"label_major"` // e.g. "process", human-readable
|
||||
LabelMinor string `json:"label_minor,omitempty"` // e.g. "hostname", human-readable, optional
|
||||
Rank string `json:"rank"` // to help the layout engine
|
||||
Pseudo bool `json:"pseudo,omitempty"` // sort-of a placeholder node, for rendering purposes
|
||||
Children RenderableNodeSet `json:"children,omitempty"` // Nodes which have been grouped into this one
|
||||
ControlNode string `json:"control_node"` // ID of node from which to show the controls in the UI
|
||||
Shape string `json:"shape"` // Shape node should be rendered as
|
||||
Stack bool `json:"stack"` // Should UI render this node as a stack?
|
||||
|
||||
report.EdgeMetadata `json:"metadata"` // Numeric sums
|
||||
report.Node
|
||||
@@ -159,7 +159,10 @@ func (rn RenderableNode) Copy() RenderableNode {
|
||||
func (rn RenderableNode) Prune() RenderableNode {
|
||||
cp := rn.Copy()
|
||||
cp.Node = report.MakeNode().WithAdjacent(cp.Node.Adjacency...)
|
||||
cp.Children = report.EmptyNodeSet
|
||||
cp.Children = MakeRenderableNodeSet()
|
||||
rn.Children.ForEach(func(n RenderableNode) {
|
||||
cp.Children = cp.Children.Add(n.Prune())
|
||||
})
|
||||
return cp
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
package report
|
||||
package render
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/mndrix/ps"
|
||||
"github.com/ugorji/go/codec"
|
||||
|
||||
"github.com/weaveworks/scope/test/reflect"
|
||||
)
|
||||
|
||||
// NodeSet is a set of nodes keyed on (Topology, ID). Clients must use
|
||||
// RenderableNodeSet is a set of nodes keyed on (Topology, ID). Clients must use
|
||||
// the Add method to add nodes
|
||||
type NodeSet struct {
|
||||
type RenderableNodeSet struct {
|
||||
psMap ps.Map
|
||||
}
|
||||
|
||||
// EmptyNodeSet is the empty set of nodes.
|
||||
var EmptyNodeSet = NodeSet{ps.NewMap()}
|
||||
// EmptyRenderableNodeSet is the empty set of nodes.
|
||||
var EmptyRenderableNodeSet = RenderableNodeSet{ps.NewMap()}
|
||||
|
||||
// MakeNodeSet makes a new NodeSet with the given nodes.
|
||||
func MakeNodeSet(nodes ...Node) NodeSet {
|
||||
return EmptyNodeSet.Add(nodes...)
|
||||
// MakeRenderableNodeSet makes a new RenderableNodeSet with the given nodes.
|
||||
func MakeRenderableNodeSet(nodes ...RenderableNode) RenderableNodeSet {
|
||||
return EmptyRenderableNodeSet.Add(nodes...)
|
||||
}
|
||||
|
||||
// Add adds the nodes to the NodeSet. Add is the only valid way to grow a
|
||||
// NodeSet. Add returns the NodeSet to enable chaining.
|
||||
func (n NodeSet) Add(nodes ...Node) NodeSet {
|
||||
// Add adds the nodes to the RenderableNodeSet. Add is the only valid way to grow a
|
||||
// RenderableNodeSet. Add returns the RenderableNodeSet to enable chaining.
|
||||
func (n RenderableNodeSet) Add(nodes ...RenderableNode) RenderableNodeSet {
|
||||
result := n.psMap
|
||||
if result == nil {
|
||||
result = ps.NewMap()
|
||||
@@ -35,11 +37,11 @@ func (n NodeSet) Add(nodes ...Node) NodeSet {
|
||||
for _, node := range nodes {
|
||||
result = result.Set(fmt.Sprintf("%s|%s", node.Topology, node.ID), node)
|
||||
}
|
||||
return NodeSet{result}
|
||||
return RenderableNodeSet{result}
|
||||
}
|
||||
|
||||
// Merge combines the two NodeSets and returns a new result.
|
||||
func (n NodeSet) Merge(other NodeSet) NodeSet {
|
||||
// Merge combines the two RenderableNodeSets and returns a new result.
|
||||
func (n RenderableNodeSet) Merge(other RenderableNodeSet) RenderableNodeSet {
|
||||
nSize, otherSize := n.Size(), other.Size()
|
||||
if nSize == 0 {
|
||||
return other
|
||||
@@ -54,22 +56,22 @@ func (n NodeSet) Merge(other NodeSet) NodeSet {
|
||||
iter.ForEach(func(key string, otherVal interface{}) {
|
||||
result = result.Set(key, otherVal)
|
||||
})
|
||||
return NodeSet{result}
|
||||
return RenderableNodeSet{result}
|
||||
}
|
||||
|
||||
// Lookup the node 'key'
|
||||
func (n NodeSet) Lookup(key string) (Node, bool) {
|
||||
func (n RenderableNodeSet) Lookup(key string) (RenderableNode, bool) {
|
||||
if n.psMap != nil {
|
||||
value, ok := n.psMap.Lookup(key)
|
||||
if ok {
|
||||
return value.(Node), true
|
||||
return value.(RenderableNode), true
|
||||
}
|
||||
}
|
||||
return Node{}, false
|
||||
return RenderableNode{}, false
|
||||
}
|
||||
|
||||
// Keys is a list of all the keys in this set.
|
||||
func (n NodeSet) Keys() []string {
|
||||
func (n RenderableNodeSet) Keys() []string {
|
||||
if n.psMap == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -79,7 +81,7 @@ func (n NodeSet) Keys() []string {
|
||||
}
|
||||
|
||||
// Size is the number of nodes in the set
|
||||
func (n NodeSet) Size() int {
|
||||
func (n RenderableNodeSet) Size() int {
|
||||
if n.psMap == nil {
|
||||
return 0
|
||||
}
|
||||
@@ -88,23 +90,23 @@ func (n NodeSet) Size() int {
|
||||
|
||||
// ForEach executes f for each node in the set. Nodes are traversed in sorted
|
||||
// order.
|
||||
func (n NodeSet) ForEach(f func(Node)) {
|
||||
func (n RenderableNodeSet) ForEach(f func(RenderableNode)) {
|
||||
for _, key := range n.Keys() {
|
||||
if val, ok := n.psMap.Lookup(key); ok {
|
||||
f(val.(Node))
|
||||
f(val.(RenderableNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy is a noop
|
||||
func (n NodeSet) Copy() NodeSet {
|
||||
func (n RenderableNodeSet) Copy() RenderableNodeSet {
|
||||
return n
|
||||
}
|
||||
|
||||
func (n NodeSet) String() string {
|
||||
func (n RenderableNodeSet) String() string {
|
||||
keys := []string{}
|
||||
if n.psMap == nil {
|
||||
n = EmptyNodeSet
|
||||
n = EmptyRenderableNodeSet
|
||||
}
|
||||
psMap := n.psMap
|
||||
if psMap == nil {
|
||||
@@ -118,15 +120,15 @@ func (n NodeSet) String() string {
|
||||
buf := bytes.NewBufferString("{")
|
||||
for _, key := range keys {
|
||||
val, _ := psMap.Lookup(key)
|
||||
fmt.Fprintf(buf, "%s: %v, ", key, val)
|
||||
fmt.Fprintf(buf, "%s: %s, ", key, spew.Sdump(val))
|
||||
}
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
fmt.Fprintf(buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// DeepEqual tests equality with other NodeSets
|
||||
func (n NodeSet) DeepEqual(i interface{}) bool {
|
||||
d, ok := i.(NodeSet)
|
||||
// DeepEqual tests equality with other RenderableNodeSets
|
||||
func (n RenderableNodeSet) DeepEqual(i interface{}) bool {
|
||||
d, ok := i.(RenderableNodeSet)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@@ -149,20 +151,20 @@ func (n NodeSet) DeepEqual(i interface{}) bool {
|
||||
return equal
|
||||
}
|
||||
|
||||
func (n NodeSet) toIntermediate() []Node {
|
||||
intermediate := make([]Node, 0, n.Size())
|
||||
n.ForEach(func(node Node) {
|
||||
func (n RenderableNodeSet) toIntermediate() []RenderableNode {
|
||||
intermediate := make([]RenderableNode, 0, n.Size())
|
||||
n.ForEach(func(node RenderableNode) {
|
||||
intermediate = append(intermediate, node)
|
||||
})
|
||||
return intermediate
|
||||
}
|
||||
|
||||
func (n NodeSet) fromIntermediate(nodes []Node) NodeSet {
|
||||
return MakeNodeSet(nodes...)
|
||||
func (n RenderableNodeSet) fromIntermediate(nodes []RenderableNode) RenderableNodeSet {
|
||||
return MakeRenderableNodeSet(nodes...)
|
||||
}
|
||||
|
||||
// CodecEncodeSelf implements codec.Selfer
|
||||
func (n *NodeSet) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
func (n *RenderableNodeSet) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
if n.psMap != nil {
|
||||
encoder.Encode(n.toIntermediate())
|
||||
} else {
|
||||
@@ -171,37 +173,37 @@ func (n *NodeSet) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
}
|
||||
|
||||
// CodecDecodeSelf implements codec.Selfer
|
||||
func (n *NodeSet) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
in := []Node{}
|
||||
func (n *RenderableNodeSet) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
in := []RenderableNode{}
|
||||
if err := decoder.Decode(&in); err != nil {
|
||||
return
|
||||
}
|
||||
*n = NodeSet{}.fromIntermediate(in)
|
||||
*n = RenderableNodeSet{}.fromIntermediate(in)
|
||||
}
|
||||
|
||||
// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
|
||||
func (NodeSet) MarshalJSON() ([]byte, error) {
|
||||
func (RenderableNodeSet) MarshalJSON() ([]byte, error) {
|
||||
panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
|
||||
}
|
||||
|
||||
// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
|
||||
func (*NodeSet) UnmarshalJSON(b []byte) error {
|
||||
func (*RenderableNodeSet) UnmarshalJSON(b []byte) error {
|
||||
panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
|
||||
}
|
||||
|
||||
// GobEncode implements gob.Marshaller
|
||||
func (n NodeSet) GobEncode() ([]byte, error) {
|
||||
func (n RenderableNodeSet) GobEncode() ([]byte, error) {
|
||||
buf := bytes.Buffer{}
|
||||
err := gob.NewEncoder(&buf).Encode(n.toIntermediate())
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// GobDecode implements gob.Unmarshaller
|
||||
func (n *NodeSet) GobDecode(input []byte) error {
|
||||
in := []Node{}
|
||||
func (n *RenderableNodeSet) GobDecode(input []byte) error {
|
||||
in := []RenderableNode{}
|
||||
if err := gob.NewDecoder(bytes.NewBuffer(input)).Decode(&in); err != nil {
|
||||
return err
|
||||
}
|
||||
*n = NodeSet{}.fromIntermediate(in)
|
||||
*n = RenderableNodeSet{}.fromIntermediate(in)
|
||||
return nil
|
||||
}
|
||||
272
render/renderable_node_set_test.go
Normal file
272
render/renderable_node_set_test.go
Normal file
@@ -0,0 +1,272 @@
|
||||
package render_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test/reflect"
|
||||
)
|
||||
|
||||
var benchmarkResult render.RenderableNodeSet
|
||||
|
||||
type nodeSpec struct {
|
||||
topology string
|
||||
id string
|
||||
}
|
||||
|
||||
func renderableNode(n report.Node) render.RenderableNode {
|
||||
node := render.NewRenderableNode(n.ID)
|
||||
node.Topology = n.Topology
|
||||
return node
|
||||
}
|
||||
|
||||
func TestMakeRenderableNodeSet(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
inputs []nodeSpec
|
||||
wants []nodeSpec
|
||||
}{
|
||||
{inputs: nil, wants: nil},
|
||||
{inputs: []nodeSpec{}, wants: []nodeSpec{}},
|
||||
{
|
||||
inputs: []nodeSpec{{"", "a"}},
|
||||
wants: []nodeSpec{{"", "a"}},
|
||||
},
|
||||
{
|
||||
inputs: []nodeSpec{{"", "a"}, {"", "a"}, {"1", "a"}},
|
||||
wants: []nodeSpec{{"", "a"}, {"1", "a"}},
|
||||
},
|
||||
{
|
||||
inputs: []nodeSpec{{"", "b"}, {"", "c"}, {"", "a"}},
|
||||
wants: []nodeSpec{{"", "a"}, {"", "b"}, {"", "c"}},
|
||||
},
|
||||
{
|
||||
inputs: []nodeSpec{{"2", "a"}, {"3", "a"}, {"1", "a"}},
|
||||
wants: []nodeSpec{{"1", "a"}, {"2", "a"}, {"3", "a"}},
|
||||
},
|
||||
} {
|
||||
var (
|
||||
inputs []render.RenderableNode
|
||||
wants []render.RenderableNode
|
||||
)
|
||||
for _, spec := range testcase.inputs {
|
||||
node := render.NewRenderableNode(spec.id)
|
||||
node.Topology = spec.topology
|
||||
inputs = append(inputs, node)
|
||||
}
|
||||
for _, spec := range testcase.wants {
|
||||
node := render.NewRenderableNode(spec.id)
|
||||
node.Topology = spec.topology
|
||||
wants = append(wants, node)
|
||||
}
|
||||
if want, have := render.MakeRenderableNodeSet(wants...), render.MakeRenderableNodeSet(inputs...); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%#v: want %#v, have %#v", inputs, wants, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMakeRenderableNodeSet(b *testing.B) {
|
||||
nodes := []render.RenderableNode{}
|
||||
for i := 1000; i >= 0; i-- {
|
||||
node := report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
})
|
||||
rn := render.NewRenderableNode(node.ID)
|
||||
rn.Node = node
|
||||
nodes = append(nodes, rn)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkResult = render.MakeRenderableNodeSet(nodes...)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderableNodeSetAdd(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
input render.RenderableNodeSet
|
||||
nodes []render.RenderableNode
|
||||
want render.RenderableNodeSet
|
||||
}{
|
||||
{
|
||||
input: render.RenderableNodeSet{},
|
||||
nodes: []render.RenderableNode{},
|
||||
want: render.RenderableNodeSet{},
|
||||
},
|
||||
{
|
||||
input: render.EmptyRenderableNodeSet,
|
||||
nodes: []render.RenderableNode{},
|
||||
want: render.EmptyRenderableNodeSet,
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(renderableNode(report.MakeNode().WithID("a"))),
|
||||
nodes: []render.RenderableNode{},
|
||||
want: render.MakeRenderableNodeSet(renderableNode(report.MakeNode().WithID("a"))),
|
||||
},
|
||||
{
|
||||
input: render.EmptyRenderableNodeSet,
|
||||
nodes: []render.RenderableNode{renderableNode(report.MakeNode().WithID("a"))},
|
||||
want: render.MakeRenderableNodeSet(renderableNode(report.MakeNode().WithID("a"))),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(renderableNode(report.MakeNode().WithID("a"))),
|
||||
nodes: []render.RenderableNode{renderableNode(report.MakeNode().WithID("a"))},
|
||||
want: render.MakeRenderableNodeSet(renderableNode(report.MakeNode().WithID("a"))),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(renderableNode(report.MakeNode().WithID("b"))),
|
||||
nodes: []render.RenderableNode{
|
||||
renderableNode(report.MakeNode().WithID("a")),
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
},
|
||||
want: render.MakeRenderableNodeSet(
|
||||
renderableNode(report.MakeNode().WithID("a")),
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(renderableNode(report.MakeNode().WithID("a"))),
|
||||
nodes: []render.RenderableNode{
|
||||
renderableNode(report.MakeNode().WithID("c")),
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
},
|
||||
want: render.MakeRenderableNodeSet(
|
||||
renderableNode(report.MakeNode().WithID("a")),
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
renderableNode(report.MakeNode().WithID("c")),
|
||||
),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(
|
||||
renderableNode(report.MakeNode().WithID("a")),
|
||||
renderableNode(report.MakeNode().WithID("c")),
|
||||
),
|
||||
nodes: []render.RenderableNode{
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
},
|
||||
want: render.MakeRenderableNodeSet(
|
||||
renderableNode(report.MakeNode().WithID("a")),
|
||||
renderableNode(report.MakeNode().WithID("b")),
|
||||
renderableNode(report.MakeNode().WithID("c")),
|
||||
),
|
||||
},
|
||||
} {
|
||||
originalLen := testcase.input.Size()
|
||||
if want, have := testcase.want, testcase.input.Add(testcase.nodes...); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%v + %v: want %v, have %v", testcase.input, testcase.nodes, want, have)
|
||||
}
|
||||
if testcase.input.Size() != originalLen {
|
||||
t.Errorf("%v + %v: modified the original input!", testcase.input, testcase.nodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRenderableNodeSetAdd(b *testing.B) {
|
||||
n := render.EmptyRenderableNodeSet
|
||||
for i := 0; i < 600; i++ {
|
||||
n = n.Add(
|
||||
renderableNode(report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
node := renderableNode(report.MakeNode().WithID("401.5").WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
}))
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkResult = n.Add(node)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderableNodeSetMerge(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
input render.RenderableNodeSet
|
||||
other render.RenderableNodeSet
|
||||
want render.RenderableNodeSet
|
||||
}{
|
||||
{input: render.RenderableNodeSet{}, other: render.RenderableNodeSet{}, want: render.RenderableNodeSet{}},
|
||||
{input: render.EmptyRenderableNodeSet, other: render.EmptyRenderableNodeSet, want: render.EmptyRenderableNodeSet},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
other: render.EmptyRenderableNodeSet,
|
||||
want: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
},
|
||||
{
|
||||
input: render.EmptyRenderableNodeSet,
|
||||
other: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
want: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
other: render.MakeRenderableNodeSet(render.NewRenderableNode("b")),
|
||||
want: render.MakeRenderableNodeSet(render.NewRenderableNode("a"), render.NewRenderableNode("b")),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(render.NewRenderableNode("b")),
|
||||
other: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
want: render.MakeRenderableNodeSet(render.NewRenderableNode("a"), render.NewRenderableNode("b")),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
other: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
want: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(render.NewRenderableNode("a"), render.NewRenderableNode("c")),
|
||||
other: render.MakeRenderableNodeSet(render.NewRenderableNode("a"), render.NewRenderableNode("b")),
|
||||
want: render.MakeRenderableNodeSet(render.NewRenderableNode("a"), render.NewRenderableNode("b"), render.NewRenderableNode("c")),
|
||||
},
|
||||
{
|
||||
input: render.MakeRenderableNodeSet(render.NewRenderableNode("b")),
|
||||
other: render.MakeRenderableNodeSet(render.NewRenderableNode("a")),
|
||||
want: render.MakeRenderableNodeSet(render.NewRenderableNode("a"), render.NewRenderableNode("b")),
|
||||
},
|
||||
} {
|
||||
originalLen := testcase.input.Size()
|
||||
if want, have := testcase.want, testcase.input.Merge(testcase.other); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%v + %v: want %v, have %v", testcase.input, testcase.other, want, have)
|
||||
}
|
||||
if testcase.input.Size() != originalLen {
|
||||
t.Errorf("%v + %v: modified the original input!", testcase.input, testcase.other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRenderableNodeSetMerge(b *testing.B) {
|
||||
n, other := render.RenderableNodeSet{}, render.RenderableNodeSet{}
|
||||
for i := 0; i < 600; i++ {
|
||||
n = n.Add(
|
||||
renderableNode(report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
for i := 400; i < 1000; i++ {
|
||||
other = other.Add(
|
||||
renderableNode(report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"c": "1",
|
||||
"d": "2",
|
||||
})),
|
||||
)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkResult = n.Merge(other)
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func TestMergeRenderableNode(t *testing.T) {
|
||||
Rank: "",
|
||||
Pseudo: false,
|
||||
Node: report.MakeNode().WithAdjacent("a1"),
|
||||
Children: report.MakeNodeSet(report.MakeNode().WithID("child1")),
|
||||
Children: render.MakeRenderableNodeSet(render.NewRenderableNode("child1")),
|
||||
}
|
||||
node2 := render.RenderableNode{
|
||||
ID: "foo",
|
||||
@@ -46,7 +46,7 @@ func TestMergeRenderableNode(t *testing.T) {
|
||||
Rank: "rank",
|
||||
Pseudo: false,
|
||||
Node: report.MakeNode().WithAdjacent("a2"),
|
||||
Children: report.MakeNodeSet(report.MakeNode().WithID("child2")),
|
||||
Children: render.MakeRenderableNodeSet(render.NewRenderableNode("child2")),
|
||||
}
|
||||
want := render.RenderableNode{
|
||||
ID: "foo",
|
||||
@@ -55,7 +55,7 @@ func TestMergeRenderableNode(t *testing.T) {
|
||||
Rank: "rank",
|
||||
Pseudo: false,
|
||||
Node: report.MakeNode().WithID("foo").WithAdjacent("a1").WithAdjacent("a2"),
|
||||
Children: report.MakeNodeSet(report.MakeNode().WithID("child1"), report.MakeNode().WithID("child2")),
|
||||
Children: render.MakeRenderableNodeSet(render.NewRenderableNode("child1"), render.NewRenderableNode("child2")),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
}.Prune()
|
||||
have := node1.Merge(node2).Prune()
|
||||
|
||||
@@ -73,7 +73,7 @@ var (
|
||||
render.IncomingInternetID: {
|
||||
ID: render.IncomingInternetID,
|
||||
LabelMajor: render.InboundMajor,
|
||||
LabelMinor: render.RequestsMinor,
|
||||
LabelMinor: render.InboundMinor,
|
||||
Pseudo: true,
|
||||
Shape: "cloud",
|
||||
Node: report.MakeNode().WithAdjacent(render.MakeContainerID(containerID)),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package render_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
@@ -11,8 +10,17 @@ import (
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
"github.com/weaveworks/scope/test/fixture"
|
||||
"github.com/weaveworks/scope/test/reflect"
|
||||
)
|
||||
|
||||
func TestEndpointRenderer(t *testing.T) {
|
||||
have := render.EndpointRenderer.Render(fixture.Report).Prune()
|
||||
want := expected.RenderedEndpoints
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessRenderer(t *testing.T) {
|
||||
have := render.ProcessRenderer.Render(fixture.Report).Prune()
|
||||
want := expected.RenderedProcesses
|
||||
@@ -30,7 +38,7 @@ func TestProcessNameRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerRenderer(t *testing.T) {
|
||||
have := (render.ContainerWithImageNameRenderer.Render(fixture.Report)).Prune()
|
||||
have := (render.ContainerRenderer.Render(fixture.Report)).Prune()
|
||||
want := expected.RenderedContainers
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -44,9 +52,9 @@ func TestContainerFilterRenderer(t *testing.T) {
|
||||
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
|
||||
docker.LabelPrefix + "works.weave.role": "system",
|
||||
})
|
||||
have := render.FilterSystem(render.ContainerWithImageNameRenderer).Render(input).Prune()
|
||||
have := render.FilterSystem(render.ContainerRenderer).Render(input).Prune()
|
||||
want := expected.RenderedContainers.Copy()
|
||||
delete(want, expected.ClientContainerRenderedID)
|
||||
delete(want, expected.ClientContainerID)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
@@ -71,23 +79,17 @@ func TestContainerWithHostIPsRenderer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerFilterRendererImageName(t *testing.T) {
|
||||
// Test nodes are filtered by image name as well.
|
||||
input := fixture.Report.Copy()
|
||||
input.ContainerImage.Nodes[fixture.ClientContainerImageNodeID] = input.ContainerImage.Nodes[fixture.ClientContainerImageNodeID].WithLatests(map[string]string{
|
||||
docker.ImageName: "beta.gcr.io/google_containers/pause",
|
||||
})
|
||||
have := render.FilterSystem(render.ContainerWithImageNameRenderer).Render(input).Prune()
|
||||
want := expected.RenderedContainers.Copy()
|
||||
delete(want, expected.ClientContainerRenderedID)
|
||||
func TestContainerImageRenderer(t *testing.T) {
|
||||
have := render.ContainerImageRenderer.Render(fixture.Report).Prune()
|
||||
want := expected.RenderedContainerImages
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerImageRenderer(t *testing.T) {
|
||||
have := render.ContainerImageRenderer.Render(fixture.Report).Prune()
|
||||
want := expected.RenderedContainerImages
|
||||
func TestAddressRenderer(t *testing.T) {
|
||||
have := render.AddressRenderer.Render(fixture.Report).Prune()
|
||||
want := expected.RenderedAddresses
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
@@ -124,7 +126,7 @@ func TestPodFilterRenderer(t *testing.T) {
|
||||
have := render.FilterSystem(render.PodRenderer).Render(input).Prune()
|
||||
want := expected.RenderedPods.Copy()
|
||||
delete(want, expected.ClientPodRenderedID)
|
||||
delete(want, expected.ClientContainerRenderedID)
|
||||
delete(want, expected.ClientContainerID)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ func (c Counters) String() string {
|
||||
val, _ := c.psMap.Lookup(key)
|
||||
fmt.Fprintf(buf, "%s: %d, ", key, val)
|
||||
}
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
fmt.Fprintf(buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/mndrix/ps"
|
||||
"github.com/ugorji/go/codec"
|
||||
@@ -123,7 +124,7 @@ func (c EdgeMetadatas) String() string {
|
||||
val, _ := c.psMap.Lookup(key)
|
||||
fmt.Fprintf(buf, "%s: %v, ", key, val)
|
||||
}
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
fmt.Fprintf(buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -219,6 +220,28 @@ type EdgeMetadata struct {
|
||||
IngressByteCount *uint64 `json:"ingress_byte_count,omitempty"` // Transport layer
|
||||
}
|
||||
|
||||
// String returns a string representation of this EdgeMetadata
|
||||
// Helps with our use of Spew and diff.
|
||||
func (e EdgeMetadata) String() string {
|
||||
f := func(i *uint64) string {
|
||||
if i == nil {
|
||||
return "nil"
|
||||
}
|
||||
return strconv.FormatUint(*i, 10)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`{
|
||||
EgressPacketCount: %v,
|
||||
IngressPacketCount: %v,
|
||||
EgressByteCount: %v,
|
||||
IngressByteCount: %v,
|
||||
}`,
|
||||
f(e.EgressPacketCount),
|
||||
f(e.IngressPacketCount),
|
||||
f(e.EgressByteCount),
|
||||
f(e.IngressByteCount))
|
||||
}
|
||||
|
||||
// Copy returns a value copy of the EdgeMetadata.
|
||||
func (e EdgeMetadata) Copy() EdgeMetadata {
|
||||
return EdgeMetadata{
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package report
|
||||
|
||||
import "sort"
|
||||
|
||||
// IDList is a list of string IDs, which are always sorted and unique.
|
||||
type IDList StringSet
|
||||
|
||||
@@ -35,6 +33,10 @@ func (a IDList) Merge(b IDList) IDList {
|
||||
|
||||
// Contains returns true if id is in the list.
|
||||
func (a IDList) Contains(id string) bool {
|
||||
i := sort.Search(len(a), func(i int) bool { return a[i] >= id })
|
||||
return i < len(a) && a[i] == id
|
||||
return StringSet(a).Contains(id)
|
||||
}
|
||||
|
||||
// Intersection returns the intersection of a and b
|
||||
func (a IDList) Intersection(b IDList) IDList {
|
||||
return IDList(StringSet(a).Intersection(StringSet(b)))
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func (m LatestMap) String() string {
|
||||
val, _ := m.Map.Lookup(key)
|
||||
fmt.Fprintf(buf, "%s: %s,\n", key, val)
|
||||
}
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
fmt.Fprintf(buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
package report_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test/reflect"
|
||||
)
|
||||
|
||||
var benchmarkResult report.NodeSet
|
||||
|
||||
type nodeSpec struct {
|
||||
topology string
|
||||
id string
|
||||
}
|
||||
|
||||
func TestMakeNodeSet(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
inputs []nodeSpec
|
||||
wants []nodeSpec
|
||||
}{
|
||||
{inputs: nil, wants: nil},
|
||||
{inputs: []nodeSpec{}, wants: []nodeSpec{}},
|
||||
{
|
||||
inputs: []nodeSpec{{"", "a"}},
|
||||
wants: []nodeSpec{{"", "a"}},
|
||||
},
|
||||
{
|
||||
inputs: []nodeSpec{{"", "a"}, {"", "a"}, {"1", "a"}},
|
||||
wants: []nodeSpec{{"", "a"}, {"1", "a"}},
|
||||
},
|
||||
{
|
||||
inputs: []nodeSpec{{"", "b"}, {"", "c"}, {"", "a"}},
|
||||
wants: []nodeSpec{{"", "a"}, {"", "b"}, {"", "c"}},
|
||||
},
|
||||
{
|
||||
inputs: []nodeSpec{{"2", "a"}, {"3", "a"}, {"1", "a"}},
|
||||
wants: []nodeSpec{{"1", "a"}, {"2", "a"}, {"3", "a"}},
|
||||
},
|
||||
} {
|
||||
var (
|
||||
inputs []report.Node
|
||||
wants []report.Node
|
||||
)
|
||||
for _, spec := range testcase.inputs {
|
||||
inputs = append(inputs, report.MakeNode().WithTopology(spec.topology).WithID(spec.id))
|
||||
}
|
||||
for _, spec := range testcase.wants {
|
||||
wants = append(wants, report.MakeNode().WithTopology(spec.topology).WithID(spec.id))
|
||||
}
|
||||
if want, have := report.MakeNodeSet(wants...), report.MakeNodeSet(inputs...); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%#v: want %#v, have %#v", inputs, wants, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMakeNodeSet(b *testing.B) {
|
||||
nodes := []report.Node{}
|
||||
for i := 1000; i >= 0; i-- {
|
||||
node := report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
})
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkResult = report.MakeNodeSet(nodes...)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSetAdd(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
input report.NodeSet
|
||||
nodes []report.Node
|
||||
want report.NodeSet
|
||||
}{
|
||||
{input: report.NodeSet{}, nodes: []report.Node{}, want: report.NodeSet{}},
|
||||
{
|
||||
input: report.EmptyNodeSet,
|
||||
nodes: []report.Node{},
|
||||
want: report.EmptyNodeSet,
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
nodes: []report.Node{},
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
},
|
||||
{
|
||||
input: report.EmptyNodeSet,
|
||||
nodes: []report.Node{report.MakeNode().WithID("a")},
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
nodes: []report.Node{report.MakeNode().WithID("a")},
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("b")),
|
||||
nodes: []report.Node{report.MakeNode().WithID("a"), report.MakeNode().WithID("b")},
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
nodes: []report.Node{report.MakeNode().WithID("c"), report.MakeNode().WithID("b")},
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b"), report.MakeNode().WithID("c")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("c")),
|
||||
nodes: []report.Node{report.MakeNode().WithID("b"), report.MakeNode().WithID("b"), report.MakeNode().WithID("b")},
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b"), report.MakeNode().WithID("c")),
|
||||
},
|
||||
} {
|
||||
originalLen := testcase.input.Size()
|
||||
if want, have := testcase.want, testcase.input.Add(testcase.nodes...); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%v + %v: want %v, have %v", testcase.input, testcase.nodes, want, have)
|
||||
}
|
||||
if testcase.input.Size() != originalLen {
|
||||
t.Errorf("%v + %v: modified the original input!", testcase.input, testcase.nodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNodeSetAdd(b *testing.B) {
|
||||
n := report.EmptyNodeSet
|
||||
for i := 0; i < 600; i++ {
|
||||
n = n.Add(
|
||||
report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
node := report.MakeNode().WithID("401.5").WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
})
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkResult = n.Add(node)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSetMerge(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
input report.NodeSet
|
||||
other report.NodeSet
|
||||
want report.NodeSet
|
||||
}{
|
||||
{input: report.NodeSet{}, other: report.NodeSet{}, want: report.NodeSet{}},
|
||||
{input: report.EmptyNodeSet, other: report.EmptyNodeSet, want: report.EmptyNodeSet},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
other: report.EmptyNodeSet,
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
},
|
||||
{
|
||||
input: report.EmptyNodeSet,
|
||||
other: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
other: report.MakeNodeSet(report.MakeNode().WithID("b")),
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("b")),
|
||||
other: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
other: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("c")),
|
||||
other: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b")),
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b"), report.MakeNode().WithID("c")),
|
||||
},
|
||||
{
|
||||
input: report.MakeNodeSet(report.MakeNode().WithID("b")),
|
||||
other: report.MakeNodeSet(report.MakeNode().WithID("a")),
|
||||
want: report.MakeNodeSet(report.MakeNode().WithID("a"), report.MakeNode().WithID("b")),
|
||||
},
|
||||
} {
|
||||
originalLen := testcase.input.Size()
|
||||
if want, have := testcase.want, testcase.input.Merge(testcase.other); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%v + %v: want %v, have %v", testcase.input, testcase.other, want, have)
|
||||
}
|
||||
if testcase.input.Size() != originalLen {
|
||||
t.Errorf("%v + %v: modified the original input!", testcase.input, testcase.other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNodeSetMerge(b *testing.B) {
|
||||
n, other := report.NodeSet{}, report.NodeSet{}
|
||||
for i := 0; i < 600; i++ {
|
||||
n = n.Add(
|
||||
report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
for i := 400; i < 1000; i++ {
|
||||
other = other.Add(
|
||||
report.MakeNode().WithID(fmt.Sprint(i)).WithLatests(map[string]string{
|
||||
"c": "1",
|
||||
"d": "2",
|
||||
}),
|
||||
)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkResult = n.Merge(other)
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func (s Sets) String() string {
|
||||
val, _ := s.psMap.Lookup(key)
|
||||
fmt.Fprintf(buf, "%s: %v, ", key, val)
|
||||
}
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
fmt.Fprintf(buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,22 @@ func (s StringSet) Contains(str string) bool {
|
||||
return i < len(s) && s[i] == str
|
||||
}
|
||||
|
||||
// Intersection returns the intersections of a and b
|
||||
func (s StringSet) Intersection(b StringSet) StringSet {
|
||||
result, i, j := EmptyStringSet, 0, 0
|
||||
for i < len(s) && j < len(b) {
|
||||
if s[i] == b[j] {
|
||||
result = result.Add(s[i])
|
||||
}
|
||||
if s[i] < b[j] {
|
||||
i++
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Add adds the strings to the StringSet. Add is the only valid way to grow a
|
||||
// StringSet. Add returns the StringSet to enable chaining.
|
||||
func (s StringSet) Add(strs ...string) StringSet {
|
||||
|
||||
Reference in New Issue
Block a user