diff --git a/app/api_topologies.go b/app/api_topologies.go index f8b5cb7b4..7f0e205fc 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -45,13 +45,76 @@ var ( } ) -func init() { - AddInitialTopologiesToRegistry(topologyRegistry) +// kubernetesFilters generates the current kubernetes filters based on the +// available k8s topologies. +func kubernetesFilters(namespaces ...string) APITopologyOptionGroup { + options := APITopologyOptionGroup{ID: "namespace", Default: "all"} + for _, namespace := range namespaces { + if namespace == "default" { + options.Default = namespace + } + options.Options = append(options.Options, APITopologyOption{ + Value: namespace, Label: namespace, filter: render.IsNamespace(namespace), filterPseudo: false, + }) + } + options.Options = append(options.Options, APITopologyOption{Value: "all", Label: "All Namespaces", filter: nil, filterPseudo: false}) + return options } -// AddInitialTopologiesToRegistry does the initial setup for a Registry. -// This is needed for testing. -func AddInitialTopologiesToRegistry(registry *Registry) { +// updateFilters updates the available filters based on the current report. +// Currently only kubernetes changes. +func updateFilters(rpt report.Report, topologies []APITopologyDesc) []APITopologyDesc { + namespaces := map[string]struct{}{} + for _, t := range []report.Topology{rpt.Pod, rpt.Service, rpt.Deployment, rpt.ReplicaSet} { + for _, n := range t.Nodes { + if state, ok := n.Latest.Lookup(kubernetes.State); ok && state == kubernetes.StateDeleted { + continue + } + if namespace, ok := n.Latest.Lookup(kubernetes.Namespace); ok { + namespaces[namespace] = struct{}{} + } + } + } + var ns []string + for namespace := range namespaces { + ns = append(ns, namespace) + } + sort.Strings(ns) + for i, t := range topologies { + if t.id == podsID || t.id == servicesID || t.id == deploymentsID || t.id == replicaSetsID { + topologies[i] = updateTopologyFilters(t, []APITopologyOptionGroup{ + kubernetesFilters(ns...), unmanagedFilter, + }) + } + } + return topologies +} + +// updateTopologyFilters recursively sets the options on a topology description +func updateTopologyFilters(t APITopologyDesc, options []APITopologyOptionGroup) APITopologyDesc { + t.Options = options + for i, sub := range t.SubTopologies { + t.SubTopologies[i] = updateTopologyFilters(sub, options) + } + return t +} + +// MakeAPITopologyOption provides an external interface to the package for creating an APITopologyOption. +func MakeAPITopologyOption(value string, label string, filterFunc render.FilterFunc, pseudo bool) APITopologyOption { + return APITopologyOption{Value: value, Label: label, filter: filterFunc, filterPseudo: pseudo} +} + +// Registry is a threadsafe store of the available topologies +type Registry struct { + sync.RWMutex + items map[string]APITopologyDesc +} + +// MakeRegistry returns a new Registry +func MakeRegistry() *Registry { + registry := &Registry{ + items: map[string]APITopologyDesc{}, + } containerFilters := []APITopologyOptionGroup{ { ID: containerLabelFiltersGroupID, @@ -189,79 +252,8 @@ func AddInitialTopologiesToRegistry(registry *Registry) { Name: "Weave Net", }, ) -} -// kubernetesFilters generates the current kubernetes filters based on the -// available k8s topologies. -func kubernetesFilters(namespaces ...string) APITopologyOptionGroup { - options := APITopologyOptionGroup{ID: "namespace", Default: "all"} - for _, namespace := range namespaces { - if namespace == "default" { - options.Default = namespace - } - options.Options = append(options.Options, APITopologyOption{ - Value: namespace, Label: namespace, filter: render.IsNamespace(namespace), filterPseudo: false, - }) - } - options.Options = append(options.Options, APITopologyOption{Value: "all", Label: "All Namespaces", filter: nil, filterPseudo: false}) - return options -} - -// updateFilters updates the available filters based on the current report. -// Currently only kubernetes changes. -func updateFilters(rpt report.Report, topologies []APITopologyDesc) []APITopologyDesc { - namespaces := map[string]struct{}{} - for _, t := range []report.Topology{rpt.Pod, rpt.Service, rpt.Deployment, rpt.ReplicaSet} { - for _, n := range t.Nodes { - if state, ok := n.Latest.Lookup(kubernetes.State); ok && state == kubernetes.StateDeleted { - continue - } - if namespace, ok := n.Latest.Lookup(kubernetes.Namespace); ok { - namespaces[namespace] = struct{}{} - } - } - } - var ns []string - for namespace := range namespaces { - ns = append(ns, namespace) - } - sort.Strings(ns) - for i, t := range topologies { - if t.id == podsID || t.id == servicesID || t.id == deploymentsID || t.id == replicaSetsID { - topologies[i] = updateTopologyFilters(t, []APITopologyOptionGroup{ - kubernetesFilters(ns...), unmanagedFilter, - }) - } - } - return topologies -} - -// updateTopologyFilters recursively sets the options on a topology description -func updateTopologyFilters(t APITopologyDesc, options []APITopologyOptionGroup) APITopologyDesc { - t.Options = options - for i, sub := range t.SubTopologies { - t.SubTopologies[i] = updateTopologyFilters(sub, options) - } - return t -} - -// MakeAPITopologyOption provides an external interface to the package for creating an APITopologyOption. -func MakeAPITopologyOption(value string, label string, filterFunc render.FilterFunc, pseudo bool) APITopologyOption { - return APITopologyOption{Value: value, Label: label, filter: filterFunc, filterPseudo: pseudo} -} - -// Registry is a threadsafe store of the available topologies -type Registry struct { - sync.RWMutex - items map[string]APITopologyDesc -} - -// MakeRegistry returns a new Registry -func MakeRegistry() *Registry { - newRegistry := &Registry{ - items: map[string]APITopologyDesc{}, - } - return newRegistry + return registry } // APITopologyDesc is returned in a list by the /api/topology handler. diff --git a/app/api_topologies_test.go b/app/api_topologies_test.go index ea23b7945..63e214662 100644 --- a/app/api_topologies_test.go +++ b/app/api_topologies_test.go @@ -20,7 +20,7 @@ import ( ) const ( - containerLabelFiltersGroupID = "container_label_filters_group" + containerLabelFiltersGroupID = "container-label-filters-group" customAPITopologyOptionFilterID = "containerLabelFilter0" ) @@ -35,7 +35,7 @@ func TestAPITopology(t *testing.T) { if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } - equals(t, 4, len(topologies)) + equals(t, 5, len(topologies)) for _, topology := range topologies { is200(t, ts, topology.URL) @@ -44,6 +44,11 @@ func TestAPITopology(t *testing.T) { is200(t, ts, subTopology.URL) } + // TODO: add ECS nodes in report fixture + if topology.Name == "Tasks" { + continue + } + if have := topology.Stats.EdgeCount; have <= 0 { t.Errorf("EdgeCount isn't positive for %s: %d", topology.Name, have) } @@ -79,8 +84,9 @@ func TestContainerLabelFilterExclude(t *testing.T) { // all containers but the excluded container should be present for key := range topologySummaries { - if report.MakeContainerNodeID(fixture.ServerContainerNodeID) == key { - t.Errorf("TestAPITopologyNegativeContainerLabelFilter Failed. Expected to not find " + report.MakeContainerNodeID(fixture.ServerContainerNodeID) + " in report") + id := report.MakeContainerNodeID(fixture.ServerContainerNodeID) + if id == key { + t.Errorf("Didn't expect to find %q in report", id) } } } @@ -89,14 +95,17 @@ func getTestContainerLabelFilterTopologySummary(t *testing.T, exclude bool) (det ts := topologyServer() defer ts.Close() - topologyRegistry := app.MakeRegistry() - app.AddInitialTopologiesToRegistry(topologyRegistry) - + var ( + topologyRegistry = app.MakeRegistry() + filter render.FilterFunc + ) if exclude == true { - topologyRegistry.AddContainerFilters(app.MakeAPITopologyOption(customAPITopologyOptionFilterID, "title", render.DoesNotHaveLabel(fixture.TestLabelKey2, fixture.ApplicationLabelValue2), false)) + filter = render.DoesNotHaveLabel(fixture.TestLabelKey2, fixture.ApplicationLabelValue2) } else { - topologyRegistry.AddContainerFilters(app.MakeAPITopologyOption(customAPITopologyOptionFilterID, "title", render.HasLabel(fixture.TestLabelKey1, fixture.ApplicationLabelValue1), false)) + filter = render.HasLabel(fixture.TestLabelKey1, fixture.ApplicationLabelValue1) } + option := app.MakeAPITopologyOption(customAPITopologyOptionFilterID, "title", filter, false) + topologyRegistry.AddContainerFilters(option) urlvalues := url.Values{} urlvalues.Set(containerLabelFiltersGroupID, customAPITopologyOptionFilterID) @@ -123,7 +132,7 @@ func TestAPITopologyAddsKubernetes(t *testing.T) { if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } - equals(t, 4, len(topologies)) + equals(t, 5, len(topologies)) // Enable the kubernetes topologies rpt := report.MakeReport() @@ -157,7 +166,7 @@ func TestAPITopologyAddsKubernetes(t *testing.T) { if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } - equals(t, 4, len(topologies)) + equals(t, 5, len(topologies)) found := false for _, topology := range topologies { diff --git a/probe/appclient/app_client_internal_test.go b/probe/appclient/app_client_internal_test.go index e29a5bc75..79df715e2 100644 --- a/probe/appclient/app_client_internal_test.go +++ b/probe/appclient/app_client_internal_test.go @@ -83,6 +83,8 @@ func TestAppClientPublish(t *testing.T) { rpt.ReplicaSet = report.MakeTopology() rpt.Host = report.MakeTopology() rpt.Overlay = report.MakeTopology() + rpt.ECSTask = report.MakeTopology() + rpt.ECSService = report.MakeTopology() rpt.Endpoint.Controls = nil rpt.Process.Controls = nil rpt.Container.Controls = nil @@ -93,6 +95,8 @@ func TestAppClientPublish(t *testing.T) { rpt.ReplicaSet.Controls = nil rpt.Host.Controls = nil rpt.Overlay.Controls = nil + rpt.ECSTask.Controls = nil + rpt.ECSService.Controls = nil s := dummyServer(t, token, id, version, rpt, done) defer s.Close() diff --git a/probe/probe_internal_test.go b/probe/probe_internal_test.go index 55cd52e68..0202ad36c 100644 --- a/probe/probe_internal_test.go +++ b/probe/probe_internal_test.go @@ -91,6 +91,8 @@ func TestProbe(t *testing.T) { want.ReplicaSet.Controls = nil want.Host.Controls = nil want.Overlay.Controls = nil + want.ECSTask.Controls = nil + want.ECSService.Controls = nil want.Endpoint.AddNode(node) pub := mockPublisher{make(chan report.Report, 10)}