Skip to content

Commit

Permalink
Move RenderableNode and DetailedNode into render/
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Wilkie committed Jun 16, 2015
1 parent 4726b48 commit 228440b
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 180 deletions.
4 changes: 2 additions & 2 deletions app/api_topologies.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"net/http"

"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/render"
)

// APITopologyDesc is returned in a list by the /api/topology handler.
Expand Down Expand Up @@ -52,7 +52,7 @@ func makeTopologyList(rep Reporter) func(w http.ResponseWriter, r *http.Request)
}
}

func stats(r report.RenderableNodes) *topologyStats {
func stats(r render.RenderableNodes) *topologyStats {
var (
nodes int
realNodes int
Expand Down
8 changes: 4 additions & 4 deletions app/api_topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ const (

// APITopology is returned by the /api/topology/{name} handler.
type APITopology struct {
Nodes report.RenderableNodes `json:"nodes"`
Nodes render.RenderableNodes `json:"nodes"`
}

// APINode is returned by the /api/topology/{name}/{id} handler.
type APINode struct {
Node report.DetailedNode `json:"node"`
Node render.DetailedNode `json:"node"`
}

// APIEdge is returned by the /api/topology/*/*/* handlers.
Expand Down Expand Up @@ -67,7 +67,7 @@ func handleNode(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Req
http.NotFound(w, r)
return
}
respondWith(w, http.StatusOK, APINode{Node: report.MakeDetailedNode(rpt, node)})
respondWith(w, http.StatusOK, APINode{Node: render.MakeDetailedNode(rpt, node)})
}

// Individual edges.
Expand Down Expand Up @@ -112,7 +112,7 @@ func handleWebsocket(
}(conn)

var (
previousTopo report.RenderableNodes
previousTopo render.RenderableNodes
tick = time.Tick(loop)
)
for {
Expand Down
2 changes: 1 addition & 1 deletion experimental/graphviz/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func handleHTML(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "</body></html>\n")
}

func dot(w io.Writer, m map[string]report.RenderableNode) {
func dot(w io.Writer, m map[string]render.RenderableNode) {
fmt.Fprintf(w, "digraph G {\n")
fmt.Fprintf(w, "\tgraph [ overlap=false ];\n")
fmt.Fprintf(w, "\tnode [ shape=circle, style=filled ];\n")
Expand Down
49 changes: 38 additions & 11 deletions report/detailed_node.go → render/detailed_node.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,50 @@
package report
package render

import (
"reflect"
"strconv"

"github.com/weaveworks/scope/report"
)

// DetailedNode is the data type that's yielded to the JavaScript layer when
// we want deep information about an individual node.
type DetailedNode struct {
ID string `json:"id"`
LabelMajor string `json:"label_major"`
LabelMinor string `json:"label_minor,omitempty"`
Pseudo bool `json:"pseudo,omitempty"`
Tables []Table `json:"tables"`
}

// Table is a dataset associated with a node. It will be displayed in the
// detail panel when a user clicks on a node.
type Table struct {
Title string `json:"title"` // e.g. Bandwidth
Numeric bool `json:"numeric"` // should the major column be right-aligned?
Rows []Row `json:"rows"`
}

// Row is a single entry in a Table dataset.
type Row struct {
Key string `json:"key"` // e.g. Ingress
ValueMajor string `json:"value_major"` // e.g. 25
ValueMinor string `json:"value_minor,omitempty"` // e.g. KB/s
}

// MakeDetailedNode transforms a renderable node to a detailed node. It uses
// aggregate metadata, plus the set of origin node IDs, to produce tables.
func MakeDetailedNode(r Report, n RenderableNode) DetailedNode {
func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
tables := []Table{}
{
rows := []Row{}
if val, ok := n.Metadata[KeyMaxConnCountTCP]; ok {
if val, ok := n.Metadata[report.KeyMaxConnCountTCP]; ok {
rows = append(rows, Row{"TCP connections", strconv.FormatInt(int64(val), 10), ""})
}
if val, ok := n.Metadata[KeyBytesIngress]; ok {
if val, ok := n.Metadata[report.KeyBytesIngress]; ok {
rows = append(rows, Row{"Bytes ingress", strconv.FormatInt(int64(val), 10), ""})
}
if val, ok := n.Metadata[KeyBytesEgress]; ok {
if val, ok := n.Metadata[report.KeyBytesEgress]; ok {
rows = append(rows, Row{"Bytes egress", strconv.FormatInt(int64(val), 10), ""})
}
if len(rows) > 0 {
Expand Down Expand Up @@ -53,7 +80,7 @@ outer:

// OriginTable produces a table (to be consumed directly by the UI) based on
// an origin ID, which is (optimistically) a node ID in one of our topologies.
func OriginTable(r Report, originID string) (Table, bool) {
func OriginTable(r report.Report, originID string) (Table, bool) {
if nmd, ok := r.Endpoint.NodeMetadatas[originID]; ok {
return endpointOriginTable(nmd)
}
Expand All @@ -72,7 +99,7 @@ func OriginTable(r Report, originID string) (Table, bool) {
return Table{}, false
}

func endpointOriginTable(nmd NodeMetadata) (Table, bool) {
func endpointOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{"endpoint", "Endpoint"},
Expand All @@ -91,7 +118,7 @@ func endpointOriginTable(nmd NodeMetadata) (Table, bool) {
}, len(rows) > 0
}

func addressOriginTable(nmd NodeMetadata) (Table, bool) {
func addressOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
if val, ok := nmd["address"]; ok {
rows = append(rows, Row{"Address", val, ""})
Expand All @@ -106,7 +133,7 @@ func addressOriginTable(nmd NodeMetadata) (Table, bool) {
}, len(rows) > 0
}

func processOriginTable(nmd NodeMetadata) (Table, bool) {
func processOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
if val, ok := nmd["comm"]; ok {
rows = append(rows, Row{"Name (comm)", val, ""})
Expand All @@ -124,7 +151,7 @@ func processOriginTable(nmd NodeMetadata) (Table, bool) {
}, len(rows) > 0
}

func containerOriginTable(nmd NodeMetadata) (Table, bool) {
func containerOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{"docker_container_id", "Container ID"},
Expand All @@ -143,7 +170,7 @@ func containerOriginTable(nmd NodeMetadata) (Table, bool) {
}, len(rows) > 0
}

func hostOriginTable(nmd NodeMetadata) (Table, bool) {
func hostOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
if val, ok := nmd["host_name"]; ok {
rows = append(rows, Row{"Host name", val, ""})
Expand Down
22 changes: 13 additions & 9 deletions report/detailed_node_test.go → render/detailed_node_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package report_test
package render

import (
"reflect"
Expand All @@ -12,41 +12,45 @@ func TestMakeDetailedNode(t *testing.T) {
}

func TestOriginTable(t *testing.T) {
if _, ok := report.OriginTable(reportFixture, "not-found"); ok {
if _, ok := OriginTable(rpt, "not-found"); ok {
t.Errorf("unknown origin ID gave unexpected success")
}
for originID, want := range map[string]report.Table{
client54001EndpointNodeID: {
for originID, want := range map[string]Table{
client54001NodeID: {
Title: "Origin Endpoint",
Numeric: false,
Rows: []report.Row{{"Host name", clientHostName, ""}},
Rows: []Row{
{"Host name", clientHostName, ""},
{"PID", "10001", ""},
{"Process name", "curl", ""},
},
},
clientAddressNodeID: {
Title: "Origin Address",
Numeric: false,
Rows: []report.Row{
Rows: []Row{
{"Host name", clientHostName, ""},
},
},
report.MakeProcessNodeID(clientHostID, "4242"): {
Title: "Origin Process",
Numeric: false,
Rows: []report.Row{
Rows: []Row{
{"Name (comm)", "curl", ""},
{"PID", "4242", ""},
},
},
serverHostNodeID: {
Title: "Origin Host",
Numeric: false,
Rows: []report.Row{
Rows: []Row{
{"Host name", serverHostName, ""},
{"Load", "0.01 0.01 0.01", ""},
{"Operating system", "Linux", ""},
},
},
} {
have, ok := report.OriginTable(reportFixture, originID)
have, ok := OriginTable(rpt, originID)
if !ok {
t.Errorf("%q: not OK", originID)
continue
Expand Down
32 changes: 16 additions & 16 deletions render/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (

const humanTheInternet = "the Internet"

func newRenderableNode(id, major, minor, rank string) report.RenderableNode {
return report.RenderableNode{
func newRenderableNode(id, major, minor, rank string) RenderableNode {
return RenderableNode{
ID: id,
LabelMajor: major,
LabelMinor: minor,
Expand All @@ -20,8 +20,8 @@ func newRenderableNode(id, major, minor, rank string) report.RenderableNode {
}
}

func newPseudoNode(id, major, minor string) report.RenderableNode {
return report.RenderableNode{
func newPseudoNode(id, major, minor string) RenderableNode {
return RenderableNode{
ID: id,
LabelMajor: major,
LabelMinor: minor,
Expand All @@ -41,18 +41,18 @@ func newPseudoNode(id, major, minor string) report.RenderableNode {
//
// If the final output parameter is false, the node shall be omitted from the
// rendered topology.
type MapFunc func(report.NodeMetadata) (report.RenderableNode, bool)
type MapFunc func(report.NodeMetadata) (RenderableNode, bool)

// PseudoFunc creates RenderableNode representing pseudo nodes given the dstNodeID.
// The srcNode renderable node is essentially from MapFunc, representing one of
// the rendered nodes this pseudo node refers to. srcNodeID and dstNodeID are
// node IDs prior to mapping.
type PseudoFunc func(srcNodeID string, srcNode report.RenderableNode, dstNodeID string) (report.RenderableNode, bool)
type PseudoFunc func(srcNodeID string, srcNode RenderableNode, dstNodeID string) (RenderableNode, bool)

// ProcessPID takes a node NodeMetadata from topology, and returns a
// representation with the ID based on the process PID and the labels based on
// the process name.
func ProcessPID(m report.NodeMetadata) (report.RenderableNode, bool) {
func ProcessPID(m report.NodeMetadata) (RenderableNode, bool) {
var (
identifier = fmt.Sprintf("%s:%s:%s", "pid", m["domain"], m["pid"])
minor = fmt.Sprintf("%s (%s)", m["domain"], m["pid"])
Expand All @@ -65,7 +65,7 @@ func ProcessPID(m report.NodeMetadata) (report.RenderableNode, bool) {
// ProcessName takes a node NodeMetadata from a topology, and returns a
// representation with the ID based on the process name (grouping all
// processes with the same name together).
func ProcessName(m report.NodeMetadata) (report.RenderableNode, bool) {
func ProcessName(m report.NodeMetadata) (RenderableNode, bool) {
show := m["pid"] != "" && m["name"] != ""
return newRenderableNode(m["name"], m["name"], "", m["name"]), show
}
Expand All @@ -74,7 +74,7 @@ func ProcessName(m report.NodeMetadata) (report.RenderableNode, bool) {
// in. We consider container and image IDs to be globally unique, and so don't
// scope them further by e.g. host. If no container metadata is found, nodes are
// grouped into the Uncontained node.
func MapEndpoint2Container(m report.NodeMetadata) (report.RenderableNode, bool) {
func MapEndpoint2Container(m report.NodeMetadata) (RenderableNode, bool) {
var id, major, minor, rank string
if m["docker_container_id"] == "" {
id, major, minor, rank = "uncontained", "Uncontained", "", "uncontained"
Expand All @@ -86,7 +86,7 @@ func MapEndpoint2Container(m report.NodeMetadata) (report.RenderableNode, bool)
}

// MapContainerIdentity maps container topology node to container mapped nodes.
func MapContainerIdentity(m report.NodeMetadata) (report.RenderableNode, bool) {
func MapContainerIdentity(m report.NodeMetadata) (RenderableNode, bool) {
var id, major, minor, rank string
if m["docker_container_id"] == "" {
id, major, minor, rank = "uncontained", "Uncontained", "", "uncontained"
Expand All @@ -100,7 +100,7 @@ func MapContainerIdentity(m report.NodeMetadata) (report.RenderableNode, bool) {
// ProcessContainerImage maps topology nodes to the container images they run
// on. If no container metadata is found, nodes are grouped into the
// Uncontained node.
func ProcessContainerImage(m report.NodeMetadata) (report.RenderableNode, bool) {
func ProcessContainerImage(m report.NodeMetadata) (RenderableNode, bool) {
var id, major, minor, rank string
if m["docker_image_id"] == "" {
id, major, minor, rank = "uncontained", "Uncontained", "", "uncontained"
Expand All @@ -114,7 +114,7 @@ func ProcessContainerImage(m report.NodeMetadata) (report.RenderableNode, bool)
// NetworkHostname takes a node NodeMetadata and returns a representation
// based on the hostname. Major label is the hostname, the minor label is the
// domain, if any.
func NetworkHostname(m report.NodeMetadata) (report.RenderableNode, bool) {
func NetworkHostname(m report.NodeMetadata) (RenderableNode, bool) {
var (
name = m["name"]
domain = ""
Expand All @@ -130,7 +130,7 @@ func NetworkHostname(m report.NodeMetadata) (report.RenderableNode, bool) {

// GenericPseudoNode contains heuristics for building sensible pseudo nodes.
// It should go away.
func GenericPseudoNode(src string, srcMapped report.RenderableNode, dst string) (report.RenderableNode, bool) {
func GenericPseudoNode(src string, srcMapped RenderableNode, dst string) (RenderableNode, bool) {
var maj, min, outputID string

if dst == report.TheInternet {
Expand All @@ -151,7 +151,7 @@ func GenericPseudoNode(src string, srcMapped report.RenderableNode, dst string)

// GenericGroupedPseudoNode contains heuristics for building sensible pseudo nodes.
// It should go away.
func GenericGroupedPseudoNode(src string, srcMapped report.RenderableNode, dst string) (report.RenderableNode, bool) {
func GenericGroupedPseudoNode(src string, srcMapped RenderableNode, dst string) (RenderableNode, bool) {
var maj, min, outputID string

if dst == report.TheInternet {
Expand All @@ -169,11 +169,11 @@ func GenericGroupedPseudoNode(src string, srcMapped report.RenderableNode, dst s
}

// InternetOnlyPseudoNode never creates a pseudo node, unless it's the Internet.
func InternetOnlyPseudoNode(_ string, _ report.RenderableNode, dst string) (report.RenderableNode, bool) {
func InternetOnlyPseudoNode(_ string, _ RenderableNode, dst string) (RenderableNode, bool) {
if dst == report.TheInternet {
return newPseudoNode(report.TheInternet, humanTheInternet, ""), true
}
return report.RenderableNode{}, false
return RenderableNode{}, false
}

// trySplitAddr is basically ParseArbitraryNodeID, since its callsites
Expand Down
12 changes: 6 additions & 6 deletions render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// Renderer is something that can render a report to a set of RenderableNodes
type Renderer interface {
Render(report.Report) report.RenderableNodes
Render(report.Report) RenderableNodes
AggregateMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata
}

Expand All @@ -17,8 +17,8 @@ type Renderer interface {
type Reduce []Renderer

// Render produces a set of RenderableNodes given a Report
func (r Reduce) Render(rpt report.Report) report.RenderableNodes {
result := report.RenderableNodes{}
func (r Reduce) Render(rpt report.Report) RenderableNodes {
result := RenderableNodes{}
for _, renderer := range r {
result.Merge(renderer.Render(rpt))
}
Expand All @@ -43,7 +43,7 @@ type Map struct {
}

// Render produces a set of RenderableNodes given a Report
func (m Map) Render(rpt report.Report) report.RenderableNodes {
func (m Map) Render(rpt report.Report) RenderableNodes {
return renderTopology(m.Selector(rpt), m.Mapper, m.Pseudo)
}

Expand All @@ -53,8 +53,8 @@ func (m Map) Render(rpt report.Report) report.RenderableNodes {
//
// RenderBy takes a a MapFunc, which defines how to group and label nodes. Npdes
// with the same mapped IDs will be merged.
func renderTopology(t report.Topology, mapFunc MapFunc, pseudoFunc PseudoFunc) report.RenderableNodes {
nodes := report.RenderableNodes{}
func renderTopology(t report.Topology, mapFunc MapFunc, pseudoFunc PseudoFunc) RenderableNodes {
nodes := RenderableNodes{}

// Build a set of RenderableNodes for all non-pseudo probes, and an
// addressID to nodeID lookup map. Multiple addressIDs can map to the same
Expand Down
Loading

0 comments on commit 228440b

Please sign in to comment.