Merge pull request #2244 from weaveworks/uncontained-filtering-fix

Fix filtering out uncontained nodes in DNS view
This commit is contained in:
Filip Barl
2017-02-20 12:16:59 +01:00
committed by GitHub
15 changed files with 199 additions and 149 deletions

View File

@@ -439,7 +439,10 @@ func (r *Registry) RendererForTopology(topologyID string, values url.Values, rpt
}
}
if len(decorators) > 0 {
return topology.renderer, render.ComposeDecorators(decorators...), nil
// Here we tell the topology renderer to apply the filtering decorator
// that we construct as a composition of all the selected filters.
composedFilterDecorator := render.ComposeDecorators(decorators...)
return render.ApplyDecorator(topology.renderer), composedFilterDecorator, nil
}
return topology.renderer, nil, nil
}

View File

@@ -11,12 +11,17 @@ import (
"github.com/ugorji/go/codec"
"k8s.io/kubernetes/pkg/api"
"github.com/weaveworks/common/test"
"github.com/weaveworks/scope/app"
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/kubernetes"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/render/detailed"
"github.com/weaveworks/scope/render/expected"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test/fixture"
"github.com/weaveworks/scope/test/reflect"
"github.com/weaveworks/scope/test/utils"
)
const (
@@ -91,6 +96,63 @@ func TestContainerLabelFilterExclude(t *testing.T) {
}
}
func TestRendererForTopologyWithFiltering(t *testing.T) {
ts := topologyServer()
defer ts.Close()
topologyRegistry := app.MakeRegistry()
option := app.MakeAPITopologyOption(customAPITopologyOptionFilterID, "title", render.IsApplication, false)
topologyRegistry.AddContainerFilters(option)
urlvalues := url.Values{}
urlvalues.Set(systemGroupID, customAPITopologyOptionFilterID)
renderer, decorator, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
if err != nil {
t.Fatalf("Topology Registry Report error: %s", err)
}
input := fixture.Report.Copy()
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
})
have := utils.Prune(renderer.Render(input, decorator))
want := utils.Prune(expected.RenderedContainers.Copy())
delete(want, fixture.ClientContainerNodeID)
delete(want, render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID))
delete(want, render.OutgoingInternetID)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func TestRendererForTopologyNoFiltering(t *testing.T) {
ts := topologyServer()
defer ts.Close()
topologyRegistry := app.MakeRegistry()
option := app.MakeAPITopologyOption(customAPITopologyOptionFilterID, "title", nil, false)
topologyRegistry.AddContainerFilters(option)
urlvalues := url.Values{}
urlvalues.Set(systemGroupID, customAPITopologyOptionFilterID)
renderer, decorator, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
if err != nil {
t.Fatalf("Topology Registry Report error: %s", err)
}
input := fixture.Report.Copy()
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
})
have := utils.Prune(renderer.Render(input, decorator))
want := utils.Prune(expected.RenderedContainers.Copy())
delete(want, render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID))
delete(want, render.OutgoingInternetID)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func getTestContainerLabelFilterTopologySummary(t *testing.T, exclude bool) (detailed.NodeSummaries, error) {
ts := topologyServer()
defer ts.Close()

View File

@@ -204,7 +204,7 @@ func (r containerWithImageNameRenderer) Render(rpt report.Report, dct Decorator)
// ContainerWithImageNameRenderer is a Renderer which produces a container
// graph where the ranks are the image names, not their IDs
var ContainerWithImageNameRenderer = ApplyDecorators(containerWithImageNameRenderer{ContainerRenderer})
var ContainerWithImageNameRenderer = containerWithImageNameRenderer{ContainerRenderer}
// ContainerImageRenderer is a Renderer which produces a renderable container
// image graph by merging the container graph and the container image topology.

View File

@@ -13,6 +13,7 @@ import (
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test/fixture"
"github.com/weaveworks/scope/test/reflect"
"github.com/weaveworks/scope/test/utils"
)
// FilterApplication is a Renderer which filters out application nodes.
@@ -20,6 +21,11 @@ func FilterApplication(r render.Renderer) render.Renderer {
return render.MakeFilter(render.IsApplication, r)
}
// FilterSystem is a Renderer which filters out system nodes.
func FilterSystem(r render.Renderer) render.Renderer {
return render.MakeFilter(render.IsSystem, r)
}
// FilterNoop does nothing.
func FilterNoop(r render.Renderer) render.Renderer { return r }
@@ -55,8 +61,8 @@ func testMap(t *testing.T, f render.MapFunc, input testcase) {
}
func TestContainerRenderer(t *testing.T) {
have := Prune(render.ContainerWithImageNameRenderer.Render(fixture.Report, FilterNoop))
want := Prune(expected.RenderedContainers)
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(fixture.Report, FilterNoop))
want := utils.Prune(expected.RenderedContainers)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -66,11 +72,14 @@ func TestContainerFilterRenderer(t *testing.T) {
// tag on of the containers in the topology and ensure
// it is filtered out correctly.
input := fixture.Report.Copy()
renderer := render.ApplyDecorator(render.ContainerWithImageNameRenderer)
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
})
have := Prune(render.ContainerWithImageNameRenderer.Render(input, FilterApplication))
want := Prune(expected.RenderedContainers.Copy())
have := utils.Prune(renderer.Render(input, FilterApplication))
want := utils.Prune(expected.RenderedContainers.Copy())
delete(want, fixture.ClientContainerNodeID)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -78,70 +87,42 @@ func TestContainerFilterRenderer(t *testing.T) {
}
func TestContainerHostnameRenderer(t *testing.T) {
have := Prune(render.ContainerHostnameRenderer.Render(fixture.Report, FilterNoop))
want := Prune(expected.RenderedContainerHostnames)
renderer := render.ApplyDecorator(render.ContainerHostnameRenderer)
have := utils.Prune(renderer.Render(fixture.Report, FilterNoop))
want := utils.Prune(expected.RenderedContainerHostnames)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func TestContainerHostnameFilterRenderer(t *testing.T) {
// add a system container into the topology and ensure
// it is filtered out correctly.
input := fixture.Report.Copy()
clientContainer2ID := "f6g7h8i9j1"
clientContainer2NodeID := report.MakeContainerNodeID(clientContainer2ID)
input.Container.AddNode(report.MakeNodeWith(clientContainer2NodeID, map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
docker.ContainerHostname: fixture.ClientContainerHostname,
report.HostNodeID: fixture.ClientHostNodeID,
}).
WithParents(report.EmptySets.
Add("host", report.MakeStringSet(fixture.ClientHostNodeID)),
).WithTopology(report.Container))
have := Prune(render.ContainerHostnameRenderer.Render(input, FilterApplication))
want := Prune(expected.RenderedContainerHostnames)
// Test works by virtue of the RenderedContainerHostname only having a container
// counter == 1
renderer := render.ApplyDecorator(render.ContainerHostnameRenderer)
have := utils.Prune(renderer.Render(fixture.Report, FilterSystem))
want := utils.Prune(expected.RenderedContainerHostnames.Copy())
delete(want, fixture.ClientContainerHostname)
delete(want, fixture.ServerContainerHostname)
delete(want, render.IncomingInternetID)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func TestContainerImageRenderer(t *testing.T) {
have := Prune(render.ContainerImageRenderer.Render(fixture.Report, FilterNoop))
want := Prune(expected.RenderedContainerImages)
renderer := render.ApplyDecorator(render.ContainerImageRenderer)
have := utils.Prune(renderer.Render(fixture.Report, FilterNoop))
want := utils.Prune(expected.RenderedContainerImages)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func TestContainerImageFilterRenderer(t *testing.T) {
// add a system container into the topology and ensure
// it is filtered out correctly.
input := fixture.Report.Copy()
clientContainer2ID := "f6g7h8i9j1"
clientContainer2NodeID := report.MakeContainerNodeID(clientContainer2ID)
input.Container.AddNode(report.MakeNodeWith(clientContainer2NodeID, map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
docker.ImageID: fixture.ClientContainerImageID,
docker.ImageName: fixture.ClientContainerImageName,
report.HostNodeID: fixture.ClientHostNodeID,
}).
WithParents(report.EmptySets.
Add("host", report.MakeStringSet(fixture.ClientHostNodeID)),
).WithTopology(report.ContainerImage))
have := Prune(render.ContainerImageRenderer.Render(input, FilterApplication))
want := Prune(expected.RenderedContainerImages.Copy())
// Test works by virtue of the RenderedContainerImage only having a container
// counter == 1
renderer := render.ApplyDecorator(render.ContainerImageRenderer)
have := utils.Prune(renderer.Render(fixture.Report, FilterSystem))
want := utils.Prune(expected.RenderedContainerHostnames.Copy())
delete(want, fixture.ClientContainerHostname)
delete(want, fixture.ServerContainerHostname)
delete(want, render.IncomingInternetID)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}

View File

@@ -9,32 +9,28 @@ import (
// ECSTaskRenderer is a Renderer for Amazon ECS tasks.
var ECSTaskRenderer = ConditionalRenderer(renderECSTopologies,
ApplyDecorators(
MakeMap(
PropagateSingleMetrics(report.Container),
MakeReduce(
MakeMap(
MapContainer2ECSTask,
ContainerWithImageNameRenderer,
),
SelectECSTask,
MakeMap(
PropagateSingleMetrics(report.Container),
MakeReduce(
MakeMap(
MapContainer2ECSTask,
ContainerWithImageNameRenderer,
),
SelectECSTask,
),
),
)
// ECSServiceRenderer is a Renderer for Amazon ECS services.
var ECSServiceRenderer = ConditionalRenderer(renderECSTopologies,
ApplyDecorators(
MakeMap(
PropagateSingleMetrics(report.ECSTask),
MakeReduce(
MakeMap(
Map2Parent(report.ECSService),
ECSTaskRenderer,
),
SelectECSService,
MakeMap(
PropagateSingleMetrics(report.ECSTask),
MakeReduce(
MakeMap(
Map2Parent(report.ECSService),
ECSTaskRenderer,
),
SelectECSService,
),
),
)

View File

@@ -6,7 +6,7 @@ import (
// HostRenderer is a Renderer which produces a renderable host
// graph from the host topology.
var HostRenderer = ApplyDecorators(MakeReduce(
var HostRenderer = MakeReduce(
MakeMap(
MapEndpoint2Host,
EndpointRenderer,
@@ -28,7 +28,7 @@ var HostRenderer = ApplyDecorators(MakeReduce(
PodRenderer,
),
SelectHost,
))
)
// MapX2Host maps any Nodes to host Nodes.
//

View File

@@ -8,11 +8,12 @@ import (
"github.com/weaveworks/scope/render/expected"
"github.com/weaveworks/scope/test/fixture"
"github.com/weaveworks/scope/test/reflect"
"github.com/weaveworks/scope/test/utils"
)
func TestHostRenderer(t *testing.T) {
have := Prune(render.HostRenderer.Render(fixture.Report, FilterNoop))
want := Prune(expected.RenderedHosts)
have := utils.Prune(render.HostRenderer.Render(fixture.Report, FilterNoop))
want := utils.Prune(expected.RenderedHosts)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}

View File

@@ -21,7 +21,7 @@ func renderKubernetesTopologies(rpt report.Report) bool {
// PodRenderer is a Renderer which produces a renderable kubernetes
// graph by merging the container graph and the pods topology.
var PodRenderer = ConditionalRenderer(renderKubernetesTopologies,
ApplyDecorators(MakeFilter(
MakeFilter(
func(n report.Node) bool {
state, ok := n.Latest.Lookup(kubernetes.State)
return (!ok || state != kubernetes.StateDeleted)
@@ -37,22 +37,20 @@ var PodRenderer = ConditionalRenderer(renderKubernetesTopologies,
SelectPod,
),
),
)),
),
)
// PodServiceRenderer is a Renderer which produces a renderable kubernetes services
// graph by merging the pods graph and the services topology.
var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies,
ApplyDecorators(
MakeMap(
PropagateSingleMetrics(report.Pod),
MakeReduce(
MakeMap(
Map2Service,
PodRenderer,
),
SelectService,
MakeMap(
PropagateSingleMetrics(report.Pod),
MakeReduce(
MakeMap(
Map2Service,
PodRenderer,
),
SelectService,
),
),
)
@@ -60,16 +58,14 @@ var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies,
// DeploymentRenderer is a Renderer which produces a renderable kubernetes deployments
// graph by merging the pods graph and the deployments topology.
var DeploymentRenderer = ConditionalRenderer(renderKubernetesTopologies,
ApplyDecorators(
MakeMap(
PropagateSingleMetrics(report.ReplicaSet),
MakeReduce(
MakeMap(
Map2Deployment,
ReplicaSetRenderer,
),
SelectDeployment,
MakeMap(
PropagateSingleMetrics(report.ReplicaSet),
MakeReduce(
MakeMap(
Map2Deployment,
ReplicaSetRenderer,
),
SelectDeployment,
),
),
)
@@ -77,16 +73,14 @@ var DeploymentRenderer = ConditionalRenderer(renderKubernetesTopologies,
// ReplicaSetRenderer is a Renderer which produces a renderable kubernetes replica sets
// graph by merging the pods graph and the replica sets topology.
var ReplicaSetRenderer = ConditionalRenderer(renderKubernetesTopologies,
ApplyDecorators(
MakeMap(
PropagateSingleMetrics(report.Pod),
MakeReduce(
MakeMap(
Map2ReplicaSet,
PodRenderer,
),
SelectReplicaSet,
MakeMap(
PropagateSingleMetrics(report.Pod),
MakeReduce(
MakeMap(
Map2ReplicaSet,
PodRenderer,
),
SelectReplicaSet,
),
),
)

View File

@@ -9,11 +9,12 @@ import (
"github.com/weaveworks/scope/render/expected"
"github.com/weaveworks/scope/test/fixture"
"github.com/weaveworks/scope/test/reflect"
"github.com/weaveworks/scope/test/utils"
)
func TestPodRenderer(t *testing.T) {
have := Prune(render.PodRenderer.Render(fixture.Report, nil))
want := Prune(expected.RenderedPods)
have := utils.Prune(render.PodRenderer.Render(fixture.Report, nil))
want := utils.Prune(expected.RenderedPods)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -27,11 +28,14 @@ func TestPodFilterRenderer(t *testing.T) {
// tag on containers or pod namespace in the topology and ensure
// it is filtered out correctly.
input := fixture.Report.Copy()
renderer := render.ApplyDecorator(render.PodRenderer)
input.Pod.Nodes[fixture.ClientPodNodeID] = input.Pod.Nodes[fixture.ClientPodNodeID].WithLatests(map[string]string{
kubernetes.Namespace: "kube-system",
})
have := Prune(render.PodRenderer.Render(input, filterNonKubeSystem))
want := Prune(expected.RenderedPods.Copy())
have := utils.Prune(renderer.Render(input, filterNonKubeSystem))
want := utils.Prune(expected.RenderedPods.Copy())
delete(want, fixture.ClientPodNodeID)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -39,8 +43,8 @@ func TestPodFilterRenderer(t *testing.T) {
}
func TestPodServiceRenderer(t *testing.T) {
have := Prune(render.PodServiceRenderer.Render(fixture.Report, nil))
want := Prune(expected.RenderedPodServices)
have := utils.Prune(render.PodServiceRenderer.Render(fixture.Report, nil))
want := utils.Prune(expected.RenderedPodServices)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -50,11 +54,14 @@ func TestPodServiceFilterRenderer(t *testing.T) {
// tag on containers or pod namespace in the topology and ensure
// it is filtered out correctly.
input := fixture.Report.Copy()
renderer := render.ApplyDecorator(render.PodServiceRenderer)
input.Service.Nodes[fixture.ServiceNodeID] = input.Service.Nodes[fixture.ServiceNodeID].WithLatests(map[string]string{
kubernetes.Namespace: "kube-system",
})
have := Prune(render.PodServiceRenderer.Render(input, filterNonKubeSystem))
want := Prune(expected.RenderedPodServices.Copy())
have := utils.Prune(renderer.Render(input, filterNonKubeSystem))
want := utils.Prune(expected.RenderedPodServices.Copy())
delete(want, fixture.ServiceNodeID)
delete(want, render.IncomingInternetID)
if !reflect.DeepEqual(want, have) {

View File

@@ -28,13 +28,13 @@ var EndpointRenderer = FilterNonProcspied(SelectEndpoint)
// ProcessRenderer is a Renderer which produces a renderable process
// graph by merging the endpoint graph and the process topology.
var ProcessRenderer = ConditionalRenderer(renderProcesses,
ApplyDecorators(ColorConnected(MakeReduce(
ColorConnected(MakeReduce(
MakeMap(
MapEndpoint2Process,
EndpointRenderer,
),
SelectProcess,
))),
)),
)
// processWithContainerNameRenderer is a Renderer which produces a process

View File

@@ -8,27 +8,28 @@ import (
"github.com/weaveworks/scope/render/expected"
"github.com/weaveworks/scope/test/fixture"
"github.com/weaveworks/scope/test/reflect"
"github.com/weaveworks/scope/test/utils"
)
func TestEndpointRenderer(t *testing.T) {
have := Prune(render.EndpointRenderer.Render(fixture.Report, FilterNoop))
want := Prune(expected.RenderedEndpoints)
have := utils.Prune(render.EndpointRenderer.Render(fixture.Report, FilterNoop))
want := utils.Prune(expected.RenderedEndpoints)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func TestProcessRenderer(t *testing.T) {
have := Prune(render.ProcessRenderer.Render(fixture.Report, FilterNoop))
want := Prune(expected.RenderedProcesses)
have := utils.Prune(render.ProcessRenderer.Render(fixture.Report, FilterNoop))
want := utils.Prune(expected.RenderedProcesses)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
func TestProcessNameRenderer(t *testing.T) {
have := Prune(render.ProcessNameRenderer.Render(fixture.Report, FilterNoop))
want := Prune(expected.RenderedProcessNames)
have := utils.Prune(render.ProcessNameRenderer.Render(fixture.Report, FilterNoop))
want := utils.Prune(expected.RenderedProcessNames)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}

View File

@@ -146,9 +146,8 @@ func (ad applyDecorator) Stats(rpt report.Report, dct Decorator) Stats {
return Stats{}
}
// ApplyDecorators returns a renderer which will apply the given decorators
// to the child render.
func ApplyDecorators(renderer Renderer) Renderer {
// ApplyDecorator returns a renderer which will apply the given decorator to the child render.
func ApplyDecorator(renderer Renderer) Renderer {
return applyDecorator{renderer}
}

View File

@@ -21,31 +21,6 @@ func (m mockRenderer) Render(rpt report.Report, d render.Decorator) report.Nodes
}
func (m mockRenderer) Stats(rpt report.Report, _ render.Decorator) render.Stats { return render.Stats{} }
// Prune returns a copy of the Nodes with all information not strictly
// necessary for rendering nodes and edges in the UI cut away.
func Prune(nodes report.Nodes) report.Nodes {
result := report.Nodes{}
for id, node := range nodes {
result[id] = PruneNode(node)
}
return result
}
// PruneNode returns a copy of the Node with all information not strictly
// necessary for rendering nodes and edges stripped away. Specifically, that
// means cutting out parts of the Node.
func PruneNode(node report.Node) report.Node {
prunedChildren := report.MakeNodeSet()
node.Children.ForEach(func(child report.Node) {
prunedChildren = prunedChildren.Add(PruneNode(child))
})
return report.MakeNode(
node.ID).
WithTopology(node.Topology).
WithAdjacent(node.Adjacency.Copy()...).
WithChildren(prunedChildren)
}
func TestReduceRender(t *testing.T) {
renderer := render.Reduce([]render.Renderer{
mockRenderer{Nodes: report.Nodes{"foo": report.MakeNode("foo")}},

View File

@@ -11,6 +11,7 @@ import (
"github.com/weaveworks/scope/probe/host"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test/utils"
)
var (
@@ -125,7 +126,7 @@ var (
)
func TestShortLivedInternetNodeConnections(t *testing.T) {
have := Prune(render.ContainerWithImageNameRenderer.Render(rpt, FilterNoop))
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt, FilterNoop))
// Conntracked-only connections from the internet should be assigned to the internet pseudonode
internet, ok := have[render.IncomingInternetID]
@@ -139,7 +140,7 @@ func TestShortLivedInternetNodeConnections(t *testing.T) {
}
func TestPauseContainerDiscarded(t *testing.T) {
have := Prune(render.ContainerWithImageNameRenderer.Render(rpt, FilterNoop))
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt, FilterNoop))
// There should only be a connection from container1 and the destination should be container2
container1, ok := have[container1NodeID]
if !ok {

30
test/utils/prune.go Normal file
View File

@@ -0,0 +1,30 @@
package utils
import (
"github.com/weaveworks/scope/report"
)
// Prune returns a copy of the Nodes with all information not strictly
// necessary for rendering nodes and edges in the UI cut away.
func Prune(nodes report.Nodes) report.Nodes {
result := report.Nodes{}
for id, node := range nodes {
result[id] = PruneNode(node)
}
return result
}
// PruneNode returns a copy of the Node with all information not strictly
// necessary for rendering nodes and edges stripped away. Specifically, that
// means cutting out parts of the Node.
func PruneNode(node report.Node) report.Node {
prunedChildren := report.MakeNodeSet()
node.Children.ForEach(func(child report.Node) {
prunedChildren = prunedChildren.Add(PruneNode(child))
})
return report.MakeNode(
node.ID).
WithTopology(node.Topology).
WithAdjacent(node.Adjacency.Copy()...).
WithChildren(prunedChildren)
}