Skip to content

Only group processes not in containers AND doing network IO into uncontained nodes. #401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/api_topology_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func TestAPITopologyWebsocket(t *testing.T) {
if err := json.Unmarshal(p, &d); err != nil {
t.Fatalf("JSON parse error: %s", err)
}
equals(t, 6, len(d.Add))
equals(t, 7, len(d.Add))
equals(t, 0, len(d.Update))
equals(t, 0, len(d.Remove))
}
Expand Down
4 changes: 2 additions & 2 deletions app/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,12 @@ var topologyRegistry = map[string]topologyView{
"applications": {
human: "Applications",
parent: "",
renderer: render.FilterUnconnected{Renderer: render.ProcessWithContainerNameRenderer{}},
renderer: render.FilterUnconnected(render.ProcessWithContainerNameRenderer{}),
},
"applications-by-name": {
human: "by name",
parent: "applications",
renderer: render.FilterUnconnected{Renderer: render.ProcessNameRenderer},
renderer: render.FilterUnconnected(render.ProcessNameRenderer),
},
"containers": {
human: "Containers",
Expand Down
23 changes: 14 additions & 9 deletions render/expected/expected.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ var (
Adjacency: adjacency,
Origins: report.MakeIDList(
test.RandomClientNodeID,
test.GoogleEndpointNodeID,
),
}
}
Expand Down Expand Up @@ -124,14 +125,15 @@ var (
},
nonContainerProcessID: {
ID: nonContainerProcessID,
LabelMajor: "bash",
LabelMajor: test.NonContainerComm,
LabelMinor: fmt.Sprintf("%s (%s)", test.ServerHostID, test.NonContainerPID),
Rank: test.NonContainerComm,
Pseudo: false,
Adjacency: report.MakeIDList(),
Adjacency: report.MakeIDList(render.TheInternetID),
Origins: report.MakeIDList(
test.NonContainerProcessNodeID,
test.ServerHostNodeID,
test.NonContainerNodeID,
),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
Expand Down Expand Up @@ -180,16 +182,17 @@ var (
EgressByteCount: newu64(2100),
},
},
"bash": {
ID: "bash",
LabelMajor: "bash",
test.NonContainerComm: {
ID: test.NonContainerComm,
LabelMajor: test.NonContainerComm,
LabelMinor: "1 process",
Rank: "bash",
Rank: test.NonContainerComm,
Pseudo: false,
Adjacency: report.MakeIDList(),
Adjacency: report.MakeIDList(render.TheInternetID),
Origins: report.MakeIDList(
test.NonContainerProcessNodeID,
test.ServerHostNodeID,
test.NonContainerNodeID,
),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
Expand Down Expand Up @@ -246,10 +249,11 @@ var (
LabelMinor: test.ServerHostName,
Rank: "",
Pseudo: true,
Adjacency: report.MakeIDList(),
Adjacency: report.MakeIDList(render.TheInternetID),
Origins: report.MakeIDList(
test.NonContainerProcessNodeID,
test.ServerHostNodeID,
test.NonContainerNodeID,
),
NodeMetadata: report.MakeNodeMetadata(),
EdgeMetadata: report.EdgeMetadata{},
Expand Down Expand Up @@ -305,8 +309,9 @@ var (
LabelMinor: test.ServerHostName,
Rank: "",
Pseudo: true,
Adjacency: report.MakeIDList(),
Adjacency: report.MakeIDList(render.TheInternetID),
Origins: report.MakeIDList(
test.NonContainerNodeID,
test.NonContainerProcessNodeID,
test.ServerHostNodeID,
),
Expand Down
66 changes: 59 additions & 7 deletions render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,27 +269,79 @@ func (m LeafMap) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableI
return metadata
}

// FilterUnconnected is a Renderer which filters out unconnected nodes.
type FilterUnconnected struct {
// CustomRenderer allow for mapping functions that recived the entire topology
// in one call - useful for functions that need to consider the entire graph
type CustomRenderer struct {
RenderFunc func(RenderableNodes) RenderableNodes
Renderer
}

// Render produces a set of RenderableNodes given a Report
func (f FilterUnconnected) Render(rpt report.Report) RenderableNodes {
return OnlyConnected(f.Renderer.Render(rpt))
// Render implements Renderer
func (c CustomRenderer) Render(rpt report.Report) RenderableNodes {
return c.RenderFunc(c.Renderer.Render(rpt))
}

// IsConnected is the key added to NodeMetadata by ColorConnected
// to indicate a node has an edge pointing to it or from it
const IsConnected = "is_connected"

// OnlyConnected filters out unconnected RenderedNodes
func OnlyConnected(input RenderableNodes) RenderableNodes {
output := RenderableNodes{}
for id, node := range ColorConnected(input) {
if _, ok := node.NodeMetadata.Metadata[IsConnected]; ok {
output[id] = node
}
}
return output
}

// FilterUnconnected produces a renderer that filters unconnected nodes
// from the given renderer
func FilterUnconnected(r Renderer) Renderer {
return CustomRenderer{
RenderFunc: OnlyConnected,
Renderer: r,
}
}

// ColorConnected colors nodes with the IsConnected key if
// they have edges to or from them.
func ColorConnected(input RenderableNodes) RenderableNodes {
connected := map[string]struct{}{}
void := struct{}{}

for id, node := range input {
if len(node.Adjacency) == 0 {
continue
}

output[id] = node
connected[id] = void
for _, id := range node.Adjacency {
output[id] = input[id]
connected[id] = void
}
}

for id := range connected {
node := input[id]
node.NodeMetadata.Metadata[IsConnected] = "true"
input[id] = node
}
return input
}

// Filter removes nodes from a view based on a predicate.
type Filter struct {
Renderer
f func(RenderableNode) bool
}

// Render implements Renderer
func (f Filter) Render(rpt report.Report) RenderableNodes {
output := RenderableNodes{}
for id, node := range f.Renderer.Render(rpt) {
if f.f(node) {
output[id] = node
}
}
return output
Expand Down
19 changes: 9 additions & 10 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,17 @@ func TestMapEdge(t *testing.T) {
}

func TestFilterRender(t *testing.T) {
renderer := render.FilterUnconnected{
Renderer: mockRenderer{RenderableNodes: render.RenderableNodes{
"foo": {ID: "foo", Adjacency: report.MakeIDList("bar")},
"bar": {ID: "bar", Adjacency: report.MakeIDList("foo")},
"baz": {ID: "baz", Adjacency: report.MakeIDList()},
}},
}
renderer := render.FilterUnconnected(
mockRenderer{RenderableNodes: render.RenderableNodes{
"foo": {ID: "foo", Adjacency: report.MakeIDList("bar"), NodeMetadata: report.MakeNodeMetadata()},
"bar": {ID: "bar", Adjacency: report.MakeIDList("foo"), NodeMetadata: report.MakeNodeMetadata()},
"baz": {ID: "baz", Adjacency: report.MakeIDList(), NodeMetadata: report.MakeNodeMetadata()},
}})
want := render.RenderableNodes{
"foo": {ID: "foo", Adjacency: report.MakeIDList("bar")},
"bar": {ID: "bar", Adjacency: report.MakeIDList("foo")},
"foo": {ID: "foo", Adjacency: report.MakeIDList("bar"), NodeMetadata: report.MakeNodeMetadata()},
"bar": {ID: "bar", Adjacency: report.MakeIDList("foo"), NodeMetadata: report.MakeNodeMetadata()},
}
have := renderer.Render(report.MakeReport())
have := sterilize(renderer.Render(report.MakeReport()), true)
if !reflect.DeepEqual(want, have) {
t.Errorf("want %+v, have %+v", want, have)
}
Expand Down
18 changes: 16 additions & 2 deletions render/topologies.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,22 @@ var ProcessNameRenderer = Map{
// graph by merging the process graph and the container topology.
var ContainerRenderer = MakeReduce(
Map{
MapFunc: MapProcess2Container,
Renderer: ProcessRenderer,
MapFunc: MapProcess2Container,

// We only want processes in container _or_ processes with network connections
// but we need to be careful to ensure we only include each edge once, by only
// including the ProcessRenderer once.
Renderer: Filter{
f: func(n RenderableNode) bool {
_, inContainer := n.NodeMetadata.Metadata[docker.ContainerID]
_, isConnected := n.NodeMetadata.Metadata[IsConnected]
return inContainer || isConnected
},

This comment was marked as abuse.

This comment was marked as abuse.

Renderer: CustomRenderer{
RenderFunc: ColorConnected,
Renderer: ProcessRenderer,
},
},
},
LeafMap{
Selector: report.SelectContainer,
Expand Down
10 changes: 10 additions & 0 deletions test/report_fixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var (
UnknownClient2IP = "10.10.10.10"
UnknownClient3IP = "10.10.10.11"
RandomClientIP = "51.52.53.54"
GoogleIP = "8.8.8.8"

ClientHostName = ClientHostID
ServerHostName = ServerHostID
Expand All @@ -52,6 +53,8 @@ var (
UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient2IP, "54020") // to the same server, are deduped.
UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient3IP, "54020") // Check this one isn't deduped
RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, RandomClientIP, "12345") // this should become an internet node
NonContainerNodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, "46789")
GoogleEndpointNodeID = report.MakeEndpointNodeID(ServerHostID, GoogleIP, "80")

ClientProcess1NodeID = report.MakeProcessNodeID(ClientHostID, Client1PID)
ClientProcess2NodeID = report.MakeProcessNodeID(ClientHostID, Client2PID)
Expand Down Expand Up @@ -85,6 +88,7 @@ var (
report.MakeAdjacencyID(UnknownClient2NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(UnknownClient3NodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(RandomClientNodeID): report.MakeIDList(Server80NodeID),
report.MakeAdjacencyID(NonContainerNodeID): report.MakeIDList(GoogleEndpointNodeID),
},
NodeMetadatas: report.NodeMetadatas{
// NodeMetadata is arbitrary. We're free to put only precisely what we
Expand All @@ -108,6 +112,12 @@ var (
process.PID: ServerPID,
report.HostNodeID: ServerHostNodeID,
}),
NonContainerNodeID: report.MakeNodeMetadataWith(map[string]string{
endpoint.Addr: ServerIP,
endpoint.Port: "46789",
process.PID: NonContainerPID,
report.HostNodeID: ServerHostNodeID,
}),
},
EdgeMetadatas: report.EdgeMetadatas{
report.MakeEdgeID(Client54001NodeID, Server80NodeID): report.EdgeMetadata{
Expand Down