Skip to content

Commit

Permalink
Merge pull request weaveworks#3135 from weaveworks/map-one
Browse files Browse the repository at this point in the history
Simplify Map.Render()
  • Loading branch information
bboreham authored Apr 10, 2018
2 parents c86e931 + 7e63d0f commit e2b4b3e
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 87 deletions.
34 changes: 17 additions & 17 deletions render/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,10 @@ func MapContainer2IP(m report.Node) []string {
// 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 container graph to get that info.
func MapProcess2Container(n report.Node) report.Nodes {
func MapProcess2Container(n report.Node) report.Node {
// Propagate pseudo nodes
if n.Topology == Pseudo {
return report.Nodes{n.ID: n}
return n
}

// Otherwise, if the process is not in a container, group it into
Expand All @@ -275,7 +275,7 @@ func MapProcess2Container(n report.Node) report.Nodes {
id = MakePseudoNodeID(UncontainedID, hostID)
node = NewDerivedPseudoNode(id, n)
}
return report.Nodes{id: node}
return node
}

// MapContainer2ContainerImage maps container Nodes to container
Expand All @@ -290,68 +290,68 @@ func MapProcess2Container(n report.Node) report.Nodes {
// format for a container image, 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 container image graph to get that info.
func MapContainer2ContainerImage(n report.Node) report.Nodes {
func MapContainer2ContainerImage(n report.Node) report.Node {
// Propagate all pseudo nodes
if n.Topology == Pseudo {
return report.Nodes{n.ID: n}
return n
}

// Otherwise, if some some reason the container doesn't have a image_id
// (maybe slightly out of sync reports), just drop it
imageID, ok := n.Latest.Lookup(docker.ImageID)
if !ok {
return report.Nodes{}
return report.Node{}
}

// Add container id key to the counters, which will later be
// counted to produce the minor label
id := report.MakeContainerImageNodeID(imageID)
result := NewDerivedNode(id, n).WithTopology(report.ContainerImage)
result.Counters = result.Counters.Add(n.Topology, 1)
return report.Nodes{id: result}
return result
}

// MapContainerImage2Name ignores image versions
func MapContainerImage2Name(n report.Node) report.Nodes {
func MapContainerImage2Name(n report.Node) report.Node {
// Propagate all pseudo nodes
if n.Topology == Pseudo {
return report.Nodes{n.ID: n}
return n
}

imageName, ok := n.Latest.Lookup(docker.ImageName)
if !ok {
return report.Nodes{}
return report.Node{}
}

imageNameWithoutVersion := docker.ImageNameWithoutVersion(imageName)
n.ID = report.MakeContainerImageNodeID(imageNameWithoutVersion)

return report.Nodes{n.ID: n}
return n
}

var containerHostnameTopology = MakeGroupNodeTopology(report.Container, docker.ContainerHostname)

// MapContainer2Hostname maps container Nodes to 'hostname' renderabled nodes..
func MapContainer2Hostname(n report.Node) report.Nodes {
func MapContainer2Hostname(n report.Node) report.Node {
// Propagate all pseudo nodes
if n.Topology == Pseudo {
return report.Nodes{n.ID: n}
return n
}

// Otherwise, if some some reason the container doesn't have a hostname
// (maybe slightly out of sync reports), just drop it
id, ok := n.Latest.Lookup(docker.ContainerHostname)
if !ok {
return report.Nodes{}
return report.Node{}
}

node := NewDerivedNode(id, n).WithTopology(containerHostnameTopology)
node.Counters = node.Counters.Add(n.Topology, 1)
return report.Nodes{id: node}
return node
}

// MapToEmpty removes all the attributes, children, etc, of a node. Useful when
// we just want to count the presence of nodes.
func MapToEmpty(n report.Node) report.Nodes {
return report.Nodes{n.ID: report.MakeNode(n.ID).WithTopology(n.Topology)}
func MapToEmpty(n report.Node) report.Node {
return report.MakeNode(n.ID).WithTopology(n.Topology)
}
2 changes: 1 addition & 1 deletion render/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type testcase struct {
}

func testMap(t *testing.T, f render.MapFunc, input testcase) {
if have := f(input.n); input.ok != (len(have) > 0) {
if have := f(input.n); input.ok != (have.ID != "") {
name := input.name
if name == "" {
name = fmt.Sprintf("%v", input.n)
Expand Down
64 changes: 33 additions & 31 deletions render/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,14 @@ var PodRenderer = Memoise(ConditionalRenderer(renderKubernetesTopologies,
},
MakeReduce(
PropagateSingleMetrics(report.Container,
MakeMap(
Map2Parent([]string{report.Pod}, UnmanagedID),
MakeFilter(
Map2Parent{topologies: []string{report.Pod}, noParentsPseudoID: UnmanagedID,
chainRenderer: MakeFilter(
ComposeFilterFuncs(
IsRunning,
Complement(isPauseContainer),
),
ContainerWithImageNameRenderer,
),
),
)},
),
ConnectionJoin(MapPod2IP, report.Pod),
),
Expand Down Expand Up @@ -101,10 +99,8 @@ func renderParents(childTopology string, parentTopologies []string, noParentsPse
return MakeReduce(append(
selectors,
PropagateSingleMetrics(childTopology,
MakeMap(
Map2Parent(parentTopologies, noParentsPseudoID),
childRenderer,
),
Map2Parent{topologies: parentTopologies, noParentsPseudoID: noParentsPseudoID,
chainRenderer: childRenderer},
),
)...)
}
Expand All @@ -126,46 +122,52 @@ func MapPod2IP(m report.Node) []string {
return []string{report.MakeScopedEndpointNodeID("", ip, "")}
}

// Map2Parent returns a MapFunc which maps Nodes to some parent grouping.
func Map2Parent(
// Map2Parent is a Renderer which maps Nodes to some parent grouping.
type Map2Parent struct {
// Renderer to chain from
chainRenderer Renderer
// The topology IDs to look for parents in
topologies []string,
topologies []string
// Either the ID prefix of the pseudo node to use for nodes without
// any parents in the group, eg. UnmanagedID, or "" to drop nodes without any parents.
noParentsPseudoID string,
) MapFunc {
return func(n report.Node) report.Nodes {
noParentsPseudoID string
}

// Render implements Renderer
func (m Map2Parent) Render(rpt report.Report) Nodes {
input := m.chainRenderer.Render(rpt)
ret := newJoinResults(nil)

for _, n := range input.Nodes {
// Uncontained becomes Unmanaged/whatever if noParentsPseudoID is set
if noParentsPseudoID != "" && strings.HasPrefix(n.ID, UncontainedIDPrefix) {
id := MakePseudoNodeID(noParentsPseudoID, n.ID[len(UncontainedIDPrefix):])
node := NewDerivedPseudoNode(id, n)
return report.Nodes{id: node}
if m.noParentsPseudoID != "" && strings.HasPrefix(n.ID, UncontainedIDPrefix) {
id := MakePseudoNodeID(m.noParentsPseudoID, n.ID[len(UncontainedIDPrefix):])
ret.addChildAndChildren(n, id, Pseudo)
continue
}

// Propagate all pseudo nodes
if n.Topology == Pseudo {
return report.Nodes{n.ID: n}
ret.passThrough(n)
continue
}

added := false
// For each topology, map to any parents we can find
result := report.Nodes{}
for _, topology := range topologies {
for _, topology := range m.topologies {
if groupIDs, ok := n.Parents.Lookup(topology); ok {
for _, id := range groupIDs {
node := NewDerivedNode(id, n).WithTopology(topology)
node.Counters = node.Counters.Add(n.Topology, 1)
result[id] = node
ret.addChildAndChildren(n, id, topology)
added = true
}
}
}

if len(result) == 0 && noParentsPseudoID != "" {
if !added && m.noParentsPseudoID != "" {
// Map to pseudo node
id := MakePseudoNodeID(noParentsPseudoID, report.ExtractHostID(n))
node := NewDerivedPseudoNode(id, n)
result[id] = node
id := MakePseudoNodeID(m.noParentsPseudoID, report.ExtractHostID(n))
ret.addChildAndChildren(n, id, Pseudo)
}

return result
}
return ret.result(input)
}
46 changes: 19 additions & 27 deletions render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
)

// MapFunc is anything which can take an arbitrary Node and
// return a set of other Nodes.
// return another Node.
//
// If the output is empty, the node shall be omitted from the rendered topology.
type MapFunc func(report.Node) report.Nodes
// If the output ID is blank, the node shall be omitted from the rendered topology.
// (we chose not to return an extra bool because it adds clutter)
type MapFunc func(report.Node) report.Node

// Renderer is something that can render a report to a set of Nodes.
type Renderer interface {
Expand Down Expand Up @@ -99,37 +100,19 @@ func MakeMap(f MapFunc, r Renderer) Renderer {
// using a map function
func (m Map) Render(rpt report.Report) Nodes {
var (
input = m.Renderer.Render(rpt)
output = report.Nodes{}
mapped = map[string]report.IDList{} // input node ID -> output node IDs
adjacencies = map[string]report.IDList{} // output node ID -> input node Adjacencies
input = m.Renderer.Render(rpt)
output = newJoinResults(nil)
)

// Rewrite all the nodes according to the map function
for _, inRenderable := range input.Nodes {
for _, outRenderable := range m.MapFunc(inRenderable) {
if existing, ok := output[outRenderable.ID]; ok {
outRenderable = outRenderable.Merge(existing)
}

output[outRenderable.ID] = outRenderable
mapped[inRenderable.ID] = mapped[inRenderable.ID].Add(outRenderable.ID)
adjacencies[outRenderable.ID] = adjacencies[outRenderable.ID].Merge(inRenderable.Adjacency)
outRenderable := m.MapFunc(inRenderable)
if outRenderable.ID != "" {
output.add(inRenderable.ID, outRenderable)
}
}

// Rewrite Adjacency for new node IDs.
for outNodeID, inAdjacency := range adjacencies {
outAdjacency := report.MakeIDList()
for _, inAdjacent := range inAdjacency {
outAdjacency = outAdjacency.Merge(mapped[inAdjacent])
}
outNode := output[outNodeID]
outNode.Adjacency = outAdjacency
output[outNodeID] = outNode
}

return Nodes{Nodes: output}
return output.result(input)
}

func propagateLatest(key string, from, to report.Node) report.Node {
Expand Down Expand Up @@ -184,6 +167,15 @@ func (ret *joinResults) mapChild(from, to string) {
}
}

// Add m into the results as a top-level node, mapped from original ID
func (ret *joinResults) add(from string, m report.Node) {
if existing, ok := ret.nodes[m.ID]; ok {
m = m.Merge(existing)
}
ret.nodes[m.ID] = m
ret.mapChild(from, m.ID)
}

// Add m as a child of the node at id, creating a new result node in
// the specified topology if not already there.
func (ret *joinResults) addUnmappedChild(m report.Node, id string, topology string) {
Expand Down
14 changes: 6 additions & 8 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ func TestReduceRender(t *testing.T) {
func TestMapRender1(t *testing.T) {
// 1. Check when we return false, the node gets filtered out
mapper := render.Map{
MapFunc: func(nodes report.Node) report.Nodes {
return report.Nodes{}
MapFunc: func(nodes report.Node) report.Node {
return report.Node{}
},
Renderer: mockRenderer{Nodes: report.Nodes{
"foo": report.MakeNode("foo"),
Expand All @@ -53,10 +53,8 @@ func TestMapRender1(t *testing.T) {
func TestMapRender2(t *testing.T) {
// 2. Check we can remap two nodes into one
mapper := render.Map{
MapFunc: func(nodes report.Node) report.Nodes {
return report.Nodes{
"bar": report.MakeNode("bar"),
}
MapFunc: func(nodes report.Node) report.Node {
return report.MakeNode("bar")
},
Renderer: mockRenderer{Nodes: report.Nodes{
"foo": report.MakeNode("foo"),
Expand All @@ -75,9 +73,9 @@ func TestMapRender2(t *testing.T) {
func TestMapRender3(t *testing.T) {
// 3. Check we can remap adjacencies
mapper := render.Map{
MapFunc: func(nodes report.Node) report.Nodes {
MapFunc: func(nodes report.Node) report.Node {
id := "_" + nodes.ID
return report.Nodes{id: report.MakeNode(id)}
return report.MakeNode(id)
},
Renderer: mockRenderer{Nodes: report.Nodes{
"foo": report.MakeNode("foo").WithAdjacent("baz"),
Expand Down
6 changes: 3 additions & 3 deletions render/weave.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ var WeaveRenderer = MakeMap(
)

// MapWeaveIdentity maps an overlay topology node to a weave topology node.
func MapWeaveIdentity(m report.Node) report.Nodes {
func MapWeaveIdentity(m report.Node) report.Node {
peerPrefix, _ := report.ParseOverlayNodeID(m.ID)
if peerPrefix != report.WeaveOverlayPeerPrefix {
return nil
return report.Node{}
}

var (
Expand All @@ -33,5 +33,5 @@ func MapWeaveIdentity(m report.Node) report.Nodes {
node = NewDerivedPseudoNode(id, m)
}

return report.Nodes{node.ID: node}
return node
}

0 comments on commit e2b4b3e

Please sign in to comment.