package app_test import ( "bytes" "context" "net/http/httptest" "net/url" "testing" "time" "github.com/gorilla/mux" "github.com/ugorji/go/codec" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "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 ( systemGroupID = "system" customAPITopologyOptionFilterID = "containerLabelFilter0" ) func TestAPITopology(t *testing.T) { ts := topologyServer() defer ts.Close() body := getRawJSON(t, ts, "/api/topology") var topologies []app.APITopologyDesc decoder := codec.NewDecoderBytes(body, &codec.JsonHandle{}) if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } equals(t, 6, len(topologies)) for _, topology := range topologies { is200(t, ts, topology.URL) for _, subTopology := range topology.SubTopologies { is200(t, ts, subTopology.URL) } // TODO: add ECS nodes in report fixture if topology.Name == "Tasks" || topology.Name == "Services" { continue } if have := topology.Stats.EdgeCount; have <= 0 { t.Errorf("EdgeCount isn't positive for %s: %d", topology.Name, have) } if have := topology.Stats.NodeCount; have <= 0 { t.Errorf("NodeCount isn't positive for %s: %d", topology.Name, have) } if have := topology.Stats.NonpseudoNodeCount; have <= 0 { t.Errorf("NonpseudoNodeCount isn't positive for %s: %d", topology.Name, have) } } } func TestContainerLabelFilter(t *testing.T) { topologySummaries, err := getTestContainerLabelFilterTopologySummary(t, false) if err != nil { t.Fatalf("Topology Registry Report error: %s", err) } // only the filtered container with fixture.TestLabelKey1 should be present equals(t, 1, len(topologySummaries)) for key := range topologySummaries { equals(t, report.MakeContainerNodeID(fixture.ClientContainerID), key) } } func TestContainerLabelFilterExclude(t *testing.T) { topologySummaries, err := getTestContainerLabelFilterTopologySummary(t, true) if err != nil { t.Fatalf("Topology Registry Report error: %s", err) } // all containers but the excluded container should be present for key := range topologySummaries { id := report.MakeContainerNodeID(fixture.ServerContainerNodeID) if id == key { t.Errorf("Didn't expect to find %q in report", id) } } } 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) urlvalues.Set("stopped", "running") urlvalues.Set("pseudo", "hide") renderer, filter, 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(render.Render(context.Background(), input, renderer, filter).Nodes) 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) urlvalues.Set("stopped", "running") urlvalues.Set("pseudo", "hide") renderer, filter, 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(render.Render(context.Background(), input, renderer, filter).Nodes) 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() var ( topologyRegistry = app.MakeRegistry() filterFunc render.FilterFunc ) if exclude == true { filterFunc = render.DoesNotHaveLabel(fixture.TestLabelKey2, fixture.ApplicationLabelValue2) } else { filterFunc = render.HasLabel(fixture.TestLabelKey1, fixture.ApplicationLabelValue1) } option := app.MakeAPITopologyOption(customAPITopologyOptionFilterID, "title", filterFunc, false) topologyRegistry.AddContainerFilters(option) urlvalues := url.Values{} urlvalues.Set(systemGroupID, customAPITopologyOptionFilterID) urlvalues.Set("stopped", "running") urlvalues.Set("pseudo", "hide") renderer, filter, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report) if err != nil { return nil, err } ctx := context.Background() return detailed.Summaries(ctx, detailed.RenderContext{Report: fixture.Report}, render.Render(ctx, fixture.Report, renderer, filter).Nodes), nil } func TestAPITopologyAddsKubernetes(t *testing.T) { router := mux.NewRouter() c := app.NewCollector(1 * time.Minute) app.RegisterReportPostHandler(c, router) app.RegisterTopologyRoutes(router, c, map[string]bool{"foo_capability": true}) ts := httptest.NewServer(router) defer ts.Close() body := getRawJSON(t, ts, "/api/topology") var topologies []app.APITopologyDesc decoder := codec.NewDecoderBytes(body, &codec.JsonHandle{}) if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } equals(t, 6, len(topologies)) // Enable the kubernetes topologies rpt := report.MakeReport() rpt.Pod = report.MakeTopology() rpt.Pod.Nodes[fixture.ClientPodNodeID] = kubernetes.NewPod(&apiv1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pong-a", Namespace: "ping", Labels: map[string]string{"ponger": "true"}, }, Status: apiv1.PodStatus{ HostIP: "1.2.3.4", ContainerStatuses: []apiv1.ContainerStatus{ {ContainerID: "container1"}, {ContainerID: "container2"}, }, }, Spec: apiv1.PodSpec{ SecurityContext: &apiv1.PodSecurityContext{}, }, }).GetNode("") buf := &bytes.Buffer{} encoder := codec.NewEncoder(buf, &codec.MsgpackHandle{}) if err := encoder.Encode(rpt); err != nil { t.Fatalf("Msgpack encoding error: %s", err) } checkRequest(t, ts, "POST", "/api/report", buf.Bytes()) body = getRawJSON(t, ts, "/api/topology") decoder = codec.NewDecoderBytes(body, &codec.JsonHandle{}) if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } equals(t, 6, len(topologies)) found := false for _, topology := range topologies { if topology.Name == "Pods" { found = true break } } if !found { t.Error("Could not find pods topology") } }