diff --git a/render/detailed/connections.go b/render/detailed/connections.go new file mode 100644 index 0000000000..1b7179880a --- /dev/null +++ b/render/detailed/connections.go @@ -0,0 +1,128 @@ +package detailed + +import ( + "strconv" + "strings" + + "github.com/weaveworks/scope/render" + "github.com/weaveworks/scope/report" +) + +const ( + remotePortKey = "remote_port" + localPortKey = "local_port" + countKey = "count" + number = "number" +) + +func makeIncomingConnectionsTable(n render.RenderableNode, ns render.RenderableNodes) NodeSummaryGroup { + // Get all endpoint ids which are children of this node + myEndpoints := report.MakeIDList() + n.Children.ForEach(func(child render.RenderableNode) { + if child.Topology == report.Endpoint { + myEndpoints = append(myEndpoints, child.ID) + } + }) + + // Get the endpoint children of all nodes which point to one of my endpoint children + endpoints := map[string][]render.RenderableNode{} + for _, node := range ns { + if !node.Adjacency.Contains(n.ID) { + continue + } + + node.Children.ForEach(func(child render.RenderableNode) { + if child.Topology != report.Endpoint { + return + } + + for _, endpoint := range child.Adjacency.Intersection(myEndpoints) { + endpoints[endpoint] = append(endpoints[endpoint], child) + } + }) + } + + // Dedupe nodes talking to same port multiple times + remotes := map[string]int{} + for myend, nodes := range endpoints { + // what port are they talking to? + parts := strings.SplitN(myend, ":", 4) + if len(parts) != 4 { + continue + } + port := parts[3] + + for _, node := range nodes { + // what is their IP address? + if parts := strings.SplitN(node.ID, ":", 4); len(parts) == 4 { + key := parts[2] + "|" + port + remotes[key] = remotes[key] + 1 + } + } + } + + return NodeSummaryGroup{ + ID: "incoming-connections", + Label: "Inbound", + Columns: []Column{ + {ID: localPortKey, Label: "Port"}, + {ID: countKey, Label: "Count", DefaultSort: true}, + }, + Nodes: buildConnectionNodes(remotes, localPortKey), + } +} + +func makeOutgoingConnectionsTable(n render.RenderableNode, ns render.RenderableNodes) NodeSummaryGroup { + // Get all endpoints which are children of this node + endpoints := []render.RenderableNode{} + n.Children.ForEach(func(child render.RenderableNode) { + if child.Topology == report.Endpoint { + endpoints = append(endpoints, child) + } + }) + + // Dedupe children talking to same port multiple times + remotes := map[string]int{} + for _, node := range endpoints { + for _, adjacent := range node.Adjacency { + if parts := strings.SplitN(adjacent, ":", 4); len(parts) == 4 { + key := parts[2] + "|" + parts[3] + remotes[key] = remotes[key] + 1 + } + } + } + + return NodeSummaryGroup{ + ID: "outgoing-connections", + Label: "Outbound", + Columns: []Column{ + {ID: remotePortKey, Label: "Port"}, + {ID: countKey, Label: "Count", DefaultSort: true}, + }, + Nodes: buildConnectionNodes(remotes, remotePortKey), + } +} + +func buildConnectionNodes(in map[string]int, columnKey string) []NodeSummary { + nodes := []NodeSummary{} + for key, count := range in { + parts := strings.SplitN(key, "|", 2) + nodes = append(nodes, NodeSummary{ + ID: key, + Label: parts[0], + Metadata: []MetadataRow{ + { + ID: columnKey, + Value: parts[1], + Datatype: number, + }, + { + ID: countKey, + Value: strconv.Itoa(count), + Datatype: number, + }, + }, + }) + } + return nodes +} diff --git a/render/detailed/metadata.go b/render/detailed/metadata.go index b17ad662ab..c04d6c2da0 100644 --- a/render/detailed/metadata.go +++ b/render/detailed/metadata.go @@ -102,7 +102,7 @@ func (c Counter) MetadataRows(n report.Node) []MetadataRow { ID: c.ID, Value: strconv.Itoa(val), Prime: c.Prime, - Datatype: numberWang, + Datatype: number, }} } return nil diff --git a/render/detailed/node.go b/render/detailed/node.go index ed57371346..15233bb9ec 100644 --- a/render/detailed/node.go +++ b/render/detailed/node.go @@ -2,8 +2,6 @@ package detailed import ( "sort" - "strconv" - "strings" "github.com/ugorji/go/codec" @@ -138,11 +136,19 @@ 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), + }}}, } ) @@ -168,141 +174,3 @@ func children(n render.RenderableNode) []NodeSummaryGroup { return nodeSummaryGroups } - -const ( - remotePortKey = "Remote Port" - localPortKey = "Local Port" - countKey = "Count" - numberWang = "number" -) - -func makeIncomingConnectionsTable(n render.RenderableNode, ns render.RenderableNodes) NodeSummaryGroup { - // Get all endpoint ids which are children of this node - myEndpoints := report.MakeIDList() - n.Children.ForEach(func(child render.RenderableNode) { - if child.Topology == report.Endpoint { - myEndpoints = append(myEndpoints, child.ID) - } - }) - - // Get the endpoint children of all nodes which point to one of my endpoint children - endpoints := map[string][]render.RenderableNode{} - for _, node := range ns { - if !node.Adjacency.Contains(n.ID) { - continue - } - - node.Children.ForEach(func(child render.RenderableNode) { - if child.Topology != report.Endpoint { - return - } - - intersection := child.Adjacency.Intersection(myEndpoints) - if len(intersection) <= 0 { - return - } - - for _, endpoint := range intersection { - endpoints[endpoint] = append(endpoints[endpoint], child) - } - }) - } - - // Dedupe nodes talking to same port multiple times - remotes := map[string]int{} - for myend, nodes := range endpoints { - // what port are they talking to? - parts := strings.SplitN(myend, ":", 4) - if len(parts) != 4 { - continue - } - port := parts[3] - - for _, node := range nodes { - // what is their IP address? - if parts := strings.SplitN(node.ID, ":", 4); len(parts) == 4 { - key := parts[2] + "|" + port - remotes[key] = remotes[key] + 1 - } - } - } - - // Build rows for these deduped children - nodes := []NodeSummary{} - for key, count := range remotes { - parts := strings.SplitN(key, "|", 2) - nodes = append(nodes, NodeSummary{ - ID: key, - Label: parts[0], - Metadata: []MetadataRow{ - { - ID: localPortKey, - Value: parts[1], - Datatype: numberWang, - }, - { - ID: countKey, - Value: strconv.Itoa(count), - Datatype: numberWang, - }, - }, - }) - } - - return NodeSummaryGroup{ - TopologyID: report.Endpoint + "-incoming", - Label: "Connections From", - Columns: []Column{localPortKey, countKey}, - Nodes: nodes, - } -} - -func makeOutgoingConnectionsTable(n render.RenderableNode, ns render.RenderableNodes) NodeSummaryGroup { - // Get all endpoints which are children of this node - endpoints := []render.RenderableNode{} - n.Children.ForEach(func(child render.RenderableNode) { - if child.Topology == report.Endpoint { - endpoints = append(endpoints, child) - } - }) - - // Dedupe children talking to same port multiple times - remotes := map[string]int{} - for _, node := range endpoints { - for _, adjacent := range node.Adjacency { - if parts := strings.SplitN(adjacent, ":", 4); len(parts) == 4 { - key := parts[2] + "|" + parts[3] - remotes[key] = remotes[key] + 1 - } - } - } - - // Build rows for these deduped children - nodes := []NodeSummary{} - for key, count := range remotes { - parts := strings.SplitN(key, "|", 2) - nodes = append(nodes, NodeSummary{ - ID: key, - Label: parts[0], - Metadata: []MetadataRow{ - { - ID: remotePortKey, - Value: parts[1], - Datatype: numberWang, - }, - { - ID: countKey, - Value: strconv.Itoa(count), - Datatype: numberWang, - }, - }, - }) - } - - return NodeSummaryGroup{ - TopologyID: report.Endpoint + "-outgoing", - Label: "Connections To", - Columns: []Column{remotePortKey, countKey}, - Nodes: nodes, - } -} diff --git a/render/detailed/node_test.go b/render/detailed/node_test.go index 5e23dbfca9..dcdb27e785 100644 --- a/render/detailed/node_test.go +++ b/render/detailed/node_test.go @@ -93,34 +93,40 @@ 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{ { - Label: "Connections From", - TopologyID: "endpoint-incoming", - Columns: []detailed.Column{"Local Port", "Count"}, - Nodes: []detailed.NodeSummary{}, + ID: "incoming-connections", + Label: "Inbound", + Columns: []detailed.Column{ + {ID: "local_port", Label: "Port"}, + {ID: "count", Label: "Count", DefaultSort: true}, + }, + Nodes: []detailed.NodeSummary{}, }, { - Label: "Connections To", - TopologyID: "endpoint-outgoing", - Columns: []detailed.Column{"Remote Port", "Count"}, - Nodes: []detailed.NodeSummary{}, + ID: "outgoing-connections", + Label: "Outbound", + Columns: []detailed.Column{ + {ID: "remote_port", Label: "Port"}, + {ID: "count", Label: "Count", DefaultSort: true}, + }, + Nodes: []detailed.NodeSummary{}, }, }, } @@ -174,7 +180,7 @@ func TestMakeDetailedContainerNode(t *testing.T) { { 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), @@ -202,16 +208,22 @@ func TestMakeDetailedContainerNode(t *testing.T) { }, Connections: []detailed.NodeSummaryGroup{ { - Label: "Connections From", - TopologyID: "endpoint-incoming", - Columns: []detailed.Column{"Local Port", "Count"}, - Nodes: []detailed.NodeSummary{}, + ID: "incoming-connections", + Label: "Inbound", + Columns: []detailed.Column{ + {ID: "local_port", Label: "Port"}, + {ID: "count", Label: "Count", DefaultSort: true}, + }, + Nodes: []detailed.NodeSummary{}, }, { - Label: "Connections To", - TopologyID: "endpoint-outgoing", - Columns: []detailed.Column{"Remote Port", "Count"}, - Nodes: []detailed.NodeSummary{}, + ID: "outgoing-connections", + Label: "Outbound", + Columns: []detailed.Column{ + {ID: "remote_port", Label: "Port"}, + {ID: "count", Label: "Count", DefaultSort: true}, + }, + Nodes: []detailed.NodeSummary{}, }, }, } diff --git a/render/detailed/summary.go b/render/detailed/summary.go index 7e0c1bca4e..473db69c68 100644 --- a/render/detailed/summary.go +++ b/render/detailed/summary.go @@ -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) -} - -// CodecDecodeSelf implements codec.Selfer -func (c *Column) CodecDecodeSelf(decoder *codec.Decoder) { - m := map[string]string{} - decoder.Decode(&m) - *c = Column(m["id"]) +type Column struct { + ID string `json:"id"` + Label string `json:"label"` + DefaultSort bool `json:"defaultSort"` } -// 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.