Remove MappedNode type; mappers return RenderableNodes

This commit is contained in:
Tom Wilkie
2015-06-15 12:49:35 +00:00
parent 8e0395e073
commit d71f10773f
3 changed files with 58 additions and 90 deletions

View File

@@ -7,12 +7,26 @@ import (
const humanTheInternet = "the Internet"
// MappedNode is returned by the MapFuncs.
type MappedNode struct {
ID string
Major string
Minor string
Rank string
func newRenderableNode(id, major, minor, rank string) RenderableNode {
return RenderableNode{
ID: id,
LabelMajor: major,
LabelMinor: minor,
Rank: rank,
Pseudo: false,
Metadata: AggregateMetadata{},
}
}
func newPseudoNode(id, major, minor string) RenderableNode {
return RenderableNode{
ID: id,
LabelMajor: major,
LabelMinor: minor,
Rank: "",
Pseudo: true,
Metadata: AggregateMetadata{},
}
}
// MapFunc is anything which can take an arbitrary NodeMetadata, which is
@@ -25,13 +39,13 @@ type MappedNode struct {
//
// If the final output parameter is false, the node shall be omitted from the
// rendered topology.
type MapFunc func(string, NodeMetadata) (MappedNode, bool)
type MapFunc func(NodeMetadata) (RenderableNode, bool)
// PseudoFunc creates MappedNode representing pseudo nodes given the dstNodeID.
// 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 RenderableNode, dstNodeID string) (MappedNode, bool)
type PseudoFunc func(srcNodeID string, srcNode RenderableNode, dstNodeID string) (RenderableNode, bool)
// TopologySelector selects a single topology from a report.
type TopologySelector func(r Report) Topology
@@ -54,39 +68,29 @@ func SelectContainer(r Report) Topology {
// 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(_ string, m NodeMetadata) (MappedNode, bool) {
func ProcessPID(m NodeMetadata) (RenderableNode, bool) {
var (
identifier = fmt.Sprintf("%s:%s:%s", "pid", m["domain"], m["pid"])
minor = fmt.Sprintf("%s (%s)", m["domain"], m["pid"])
show = m["pid"] != "" && m["name"] != ""
)
return MappedNode{
ID: identifier,
Major: m["name"],
Minor: minor,
Rank: m["pid"],
}, show
return newRenderableNode(identifier, m["name"], minor, m["pid"]), show
}
// 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(_ string, m NodeMetadata) (MappedNode, bool) {
func ProcessName(m NodeMetadata) (RenderableNode, bool) {
show := m["pid"] != "" && m["name"] != ""
return MappedNode{
ID: m["name"],
Major: m["name"],
Minor: "",
Rank: m["name"],
}, show
return newRenderableNode(m["name"], m["name"], "", m["name"]), show
}
// MapEndpoint2Container maps endpoint topology nodes to the containers they run
// 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(_ string, m NodeMetadata) (MappedNode, bool) {
func MapEndpoint2Container(m NodeMetadata) (RenderableNode, bool) {
var id, major, minor, rank string
if m["docker_container_id"] == "" {
id, major, minor, rank = "uncontained", "Uncontained", "", "uncontained"
@@ -94,16 +98,11 @@ func MapEndpoint2Container(_ string, m NodeMetadata) (MappedNode, bool) {
id, major, minor, rank = m["docker_container_id"], "", m["domain"], ""
}
return MappedNode{
ID: id,
Major: major,
Minor: minor,
Rank: rank,
}, true
return newRenderableNode(id, major, minor, rank), true
}
// MapContainerIdentity maps container topology node to container mapped nodes.
func MapContainerIdentity(_ string, m NodeMetadata) (MappedNode, bool) {
func MapContainerIdentity(m NodeMetadata) (RenderableNode, bool) {
var id, major, minor, rank string
if m["docker_container_id"] == "" {
id, major, minor, rank = "uncontained", "Uncontained", "", "uncontained"
@@ -111,18 +110,13 @@ func MapContainerIdentity(_ string, m NodeMetadata) (MappedNode, bool) {
id, major, minor, rank = m["docker_container_id"], m["docker_container_name"], m["domain"], m["docker_image_id"]
}
return MappedNode{
ID: id,
Major: major,
Minor: minor,
Rank: rank,
}, true
return newRenderableNode(id, major, minor, rank), true
}
// 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(_ string, m NodeMetadata) (MappedNode, bool) {
func ProcessContainerImage(m NodeMetadata) (RenderableNode, bool) {
var id, major, minor, rank string
if m["docker_image_id"] == "" {
id, major, minor, rank = "uncontained", "Uncontained", "", "uncontained"
@@ -130,18 +124,13 @@ func ProcessContainerImage(_ string, m NodeMetadata) (MappedNode, bool) {
id, major, minor, rank = m["docker_image_id"], m["docker_image_name"], "", m["docker_image_id"]
}
return MappedNode{
ID: id,
Major: major,
Minor: minor,
Rank: rank,
}, true
return newRenderableNode(id, major, minor, rank), true
}
// 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(_ string, m NodeMetadata) (MappedNode, bool) {
func NetworkHostname(m NodeMetadata) (RenderableNode, bool) {
var (
name = m["name"]
domain = ""
@@ -152,17 +141,12 @@ func NetworkHostname(_ string, m NodeMetadata) (MappedNode, bool) {
domain = parts[1]
}
return MappedNode{
ID: fmt.Sprintf("host:%s", name),
Major: parts[0],
Minor: domain,
Rank: parts[0],
}, name != ""
return newRenderableNode(fmt.Sprintf("host:%s", name), parts[0], domain, parts[0]), name != ""
}
// GenericPseudoNode contains heuristics for building sensible pseudo nodes.
// It should go away.
func GenericPseudoNode(src string, srcMapped RenderableNode, dst string) (MappedNode, bool) {
func GenericPseudoNode(src string, srcMapped RenderableNode, dst string) (RenderableNode, bool) {
var maj, min, outputID string
if dst == TheInternet {
@@ -178,16 +162,12 @@ func GenericPseudoNode(src string, srcMapped RenderableNode, dst string) (Mapped
maj, min = dstNodeAddr, ""
}
return MappedNode{
ID: outputID,
Major: maj,
Minor: min,
}, true
return newPseudoNode(outputID, maj, min), true
}
// GenericGroupedPseudoNode contains heuristics for building sensible pseudo nodes.
// It should go away.
func GenericGroupedPseudoNode(src string, srcMapped RenderableNode, dst string) (MappedNode, bool) {
func GenericGroupedPseudoNode(src string, srcMapped RenderableNode, dst string) (RenderableNode, bool) {
var maj, min, outputID string
if dst == TheInternet {
@@ -201,19 +181,15 @@ func GenericGroupedPseudoNode(src string, srcMapped RenderableNode, dst string)
maj, min = dstNodeAddr, ""
}
return MappedNode{
ID: outputID,
Major: maj,
Minor: min,
}, true
return newPseudoNode(outputID, maj, min), true
}
// InternetOnlyPseudoNode never creates a pseudo node, unless it's the Internet.
func InternetOnlyPseudoNode(_ string, _ RenderableNode, dst string) (MappedNode, bool) {
func InternetOnlyPseudoNode(_ string, _ RenderableNode, dst string) (RenderableNode, bool) {
if dst == TheInternet {
return MappedNode{ID: TheInternet, Major: humanTheInternet}, true
return newPseudoNode(TheInternet, humanTheInternet, ""), true
}
return MappedNode{}, false
return RenderableNode{}, false
}
// trySplitAddr is basically ParseArbitraryNodeID, since its callsites

View File

@@ -86,17 +86,17 @@ func TestUngroupedMapping(t *testing.T) {
} {
identity := fmt.Sprintf("(%d %s %v)", i, c.id, c.meta)
m, haveOK := c.f(c.id, c.meta)
m, haveOK := c.f(c.meta)
if want, have := c.wantOK, haveOK; want != have {
t.Errorf("%s: map OK error: want %v, have %v", identity, want, have)
}
if want, have := c.wantID, m.ID; want != have {
t.Errorf("%s: map ID error: want %#v, have %#v", identity, want, have)
}
if want, have := c.wantMajor, m.Major; want != have {
if want, have := c.wantMajor, m.LabelMajor; want != have {
t.Errorf("%s: map major label: want %#v, have %#v", identity, want, have)
}
if want, have := c.wantMinor, m.Minor; want != have {
if want, have := c.wantMinor, m.LabelMinor; want != have {
t.Errorf("%s: map minor label: want %#v, have %#v", identity, want, have)
}
if want, have := c.wantRank, m.Rank; want != have {

View File

@@ -93,23 +93,21 @@ func (t Topology) RenderBy(mapFunc MapFunc, pseudoFunc PseudoFunc) RenderableNod
source2host = map[string]string{} // source node ID -> origin host ID
)
for nodeID, metadata := range t.NodeMetadatas {
mapped, ok := mapFunc(nodeID, metadata)
mapped, ok := mapFunc(metadata)
if !ok {
continue
}
// mapped.ID needs not be unique over all addressIDs. If not, we just
// overwrite the existing data, on the assumption that the MapFunc
// returns the same data.
nodes[mapped.ID] = RenderableNode{
ID: mapped.ID,
LabelMajor: mapped.Major,
LabelMinor: mapped.Minor,
Rank: mapped.Rank,
Pseudo: false,
Origins: IDList{nodeID},
Metadata: AggregateMetadata{}, // later
// mapped.ID needs not be unique over all addressIDs. If not, we merge with
// the existing data, on the assumption that the MapFunc returns the same
// data.
existing, ok := nodes[mapped.ID]
if ok {
mapped.Merge(existing)
}
mapped.Origins = mapped.Origins.Add(nodeID)
nodes[mapped.ID] = mapped
source2mapped[nodeID] = mapped.ID
source2host[nodeID] = metadata[HostNodeID]
}
@@ -136,13 +134,7 @@ func (t Topology) RenderBy(mapFunc MapFunc, pseudoFunc PseudoFunc) RenderableNod
continue
}
dstRenderableID = pseudoNode.ID
nodes[dstRenderableID] = RenderableNode{
ID: pseudoNode.ID,
LabelMajor: pseudoNode.Major,
LabelMinor: pseudoNode.Minor,
Pseudo: true,
Metadata: AggregateMetadata{}, // populated below - or not?
}
nodes[dstRenderableID] = pseudoNode
source2mapped[dstNodeID] = dstRenderableID
}
@@ -174,11 +166,11 @@ func (t Topology) EdgeMetadata(mapFunc MapFunc, srcRenderableID, dstRenderableID
continue
}
if src != TheInternet {
mapped, _ := mapFunc(src, t.NodeMetadatas[src])
mapped, _ := mapFunc(t.NodeMetadatas[src])
src = mapped.ID
}
if dst != TheInternet {
mapped, _ := mapFunc(dst, t.NodeMetadatas[dst])
mapped, _ := mapFunc(t.NodeMetadatas[dst])
dst = mapped.ID
}
if src == srcRenderableID && dst == dstRenderableID {