diff --git a/internal/mode/static/telemetry/collector.go b/internal/mode/static/telemetry/collector.go index 3c652255db..c16ce52ea4 100644 --- a/internal/mode/static/telemetry/collector.go +++ b/internal/mode/static/telemetry/collector.go @@ -50,13 +50,15 @@ type Data struct { // at the same index. // Each value is either 'true' or 'false' for boolean flags and 'default' or 'user-defined' for non-boolean flags. FlagValues []string - // SnippetsFiltersContextDirectives contains the context-directive strings of all applied SnippetsFilters. - // Both lists are ordered first by count, then by lexicographical order on the context-directive string. - SnippetsFiltersContextDirectives []string - // SnippetsFiltersContextDirectivesCount contains the count of the context-directive strings, where each count - // corresponds to the string from SnippetsFiltersContextDirectives at the same index. Both lists are ordered - // first by count, then by lexicographical order on the context-directive string. - SnippetsFiltersContextDirectivesCount []int64 + // SnippetsFiltersDirectiveContexts contains the context-directive strings of all applied SnippetsFilters. + // Both lists are ordered first by count, then by lexicographical order of the context string, + // then lastly by directive string. + SnippetsFiltersDirectiveContexts []string + // SnippetsFiltersDirectiveContextsCount contains the count of the directive-context strings, where each count + // corresponds to the string from SnippetsFiltersDirectiveContexts at the same index. + // Both lists are ordered first by count, then by lexicographical order of the context string, + // then lastly by directive string. + SnippetsFiltersDirectiveContextsCount []int64 NGFResourceCounts // embedding is required by the generator. // NGFReplicaCount is the number of replicas of the NGF Pod. NGFReplicaCount int64 @@ -156,8 +158,8 @@ func (c DataCollectorImpl) Collect(ctx context.Context) (Data, error) { return Data{}, fmt.Errorf("failed to get NGF deploymentID: %w", err) } - snippetsFiltersContextDirectives, - snippetsFiltersContextDirectivesCount, + snippetsFiltersDirectiveContexts, + snippetsFiltersDirectiveContextsCount, err := collectSnippetsFilterSnippetsInfo(c.cfg.GraphGetter) if err != nil { return Data{}, fmt.Errorf("failed to collect snippet filter directive info: %w", err) @@ -180,8 +182,8 @@ func (c DataCollectorImpl) Collect(ctx context.Context) (Data, error) { FlagValues: c.cfg.Flags.Values, NGFReplicaCount: int64(replicaCount), // maybe SnippetValues? - SnippetsFiltersContextDirectives: snippetsFiltersContextDirectives, - SnippetsFiltersContextDirectivesCount: snippetsFiltersContextDirectivesCount, + SnippetsFiltersDirectiveContexts: snippetsFiltersDirectiveContexts, + SnippetsFiltersDirectiveContextsCount: snippetsFiltersDirectiveContextsCount, } return data, nil @@ -403,7 +405,7 @@ func collectClusterInformation(ctx context.Context, k8sClient client.Reader) (cl return clusterInfo, nil } -type sfContextDirective struct { +type sfDirectiveContext struct { context string directive string } @@ -414,7 +416,7 @@ func collectSnippetsFilterSnippetsInfo(graphGetter GraphGetter) ([]string, []int return nil, nil, errors.New("latest graph cannot be nil") } - contextDirectiveMap := make(map[sfContextDirective]int) + directiveContextMap := make(map[sfDirectiveContext]int) for _, sf := range g.SnippetsFilters { if sf == nil { @@ -439,18 +441,18 @@ func collectSnippetsFilterSnippetsInfo(graphGetter GraphGetter) ([]string, []int directives := parseSnippetValueIntoDirectives(snippetValue) for _, directive := range directives { - contextDirective := sfContextDirective{ + directiveContext := sfDirectiveContext{ context: parsedContext, directive: directive, } - contextDirectiveMap[contextDirective]++ + directiveContextMap[directiveContext]++ } } } - contextDirectiveList, countList := parseContextDirectiveMapIntoLists(contextDirectiveMap) + directiveContextList, countList := parseDirectiveContextMapIntoLists(directiveContextMap) - return contextDirectiveList, countList, nil + return directiveContextList, countList, nil } func parseSnippetValueIntoDirectives(snippetValue string) []string { @@ -471,37 +473,41 @@ func parseSnippetValueIntoDirectives(snippetValue string) []string { return directives } -// parseContextDirectiveMapIntoLists returns two same-length lists where the elements at each corresponding index +// parseDirectiveContextMapIntoLists returns two same-length lists where the elements at each corresponding index // are paired together. -// The first list contains strings which are the NGINX context and directive of a Snippet joined with a hyphen. -// The second list contains ints which are the count of total same context-directive values of the first list. -// Both lists are ordered based off of count first, then lexicographically on the context-directive string. -func parseContextDirectiveMapIntoLists(contextDirectiveMap map[sfContextDirective]int) ([]string, []int64) { - type sfContextDirectiveCount struct { - contextDirective string - count int64 +// The first list contains strings which are the NGINX directive and context of a Snippet joined with a hyphen. +// The second list contains ints which are the count of total same directive-context values of the first list. +// Both lists are ordered first by count, then by lexicographical order of the context string, +// then lastly by directive string. +func parseDirectiveContextMapIntoLists(directiveContextMap map[sfDirectiveContext]int) ([]string, []int64) { + type sfDirectiveContextCount struct { + directive, context string + count int64 } - kvPairs := make([]sfContextDirectiveCount, 0, len(contextDirectiveMap)) + kvPairs := make([]sfDirectiveContextCount, 0, len(directiveContextMap)) - for k, v := range contextDirectiveMap { - kvPairs = append(kvPairs, sfContextDirectiveCount{k.context + "-" + k.directive, int64(v)}) + for k, v := range directiveContextMap { + kvPairs = append(kvPairs, sfDirectiveContextCount{k.directive, k.context, int64(v)}) } sort.Slice(kvPairs, func(i, j int) bool { if kvPairs[i].count == kvPairs[j].count { - return kvPairs[i].contextDirective < kvPairs[j].contextDirective + if kvPairs[i].context == kvPairs[j].context { + return kvPairs[i].directive < kvPairs[j].directive + } + return kvPairs[i].context < kvPairs[j].context } return kvPairs[i].count > kvPairs[j].count }) - contextDirectiveList := make([]string, len(kvPairs)) + directiveContextList := make([]string, len(kvPairs)) countList := make([]int64, len(kvPairs)) for i, pair := range kvPairs { - contextDirectiveList[i] = pair.contextDirective + directiveContextList[i] = pair.directive + "-" + pair.context countList[i] = pair.count } - return contextDirectiveList, countList + return directiveContextList, countList } diff --git a/internal/mode/static/telemetry/collector_test.go b/internal/mode/static/telemetry/collector_test.go index 5859328c90..9f7faea8db 100644 --- a/internal/mode/static/telemetry/collector_test.go +++ b/internal/mode/static/telemetry/collector_test.go @@ -174,8 +174,8 @@ var _ = Describe("Collector", Ordered, func() { ImageSource: "local", FlagNames: flags.Names, FlagValues: flags.Values, - SnippetsFiltersContextDirectives: []string{}, - SnippetsFiltersContextDirectivesCount: []int64{}, + SnippetsFiltersDirectiveContexts: []string{}, + SnippetsFiltersDirectiveContextsCount: []int64{}, } k8sClientReader = &eventsfakes.FakeReader{} @@ -354,6 +354,8 @@ var _ = Describe("Collector", Ordered, func() { }, {Namespace: "test", Name: "sf-3"}: { Snippets: map[ngfAPI.NginxContext]string{ + // Tests lexicographical ordering when count and context is the same + ngfAPI.NginxContextMain: "worker_rlimit_core 1m;", ngfAPI.NginxContextHTTPServer: "auth_delay 10s;", }, }, @@ -414,17 +416,18 @@ var _ = Describe("Collector", Ordered, func() { expData.ClusterVersion = "1.29.2" expData.ClusterPlatform = "kind" - expData.SnippetsFiltersContextDirectives = []string{ - "server-auth_delay", - "http-aio", - "location-keepalive_time", - "main-worker_priority", - "http-client_body_timeout", - "location-allow", - "main-worker_rlimit_nofile", - "server-ignore_invalid_headers", + expData.SnippetsFiltersDirectiveContexts = []string{ + "auth_delay-server", + "aio-http", + "keepalive_time-location", + "worker_priority-main", + "client_body_timeout-http", + "allow-location", + "worker_rlimit_core-main", + "worker_rlimit_nofile-main", + "ignore_invalid_headers-server", } - expData.SnippetsFiltersContextDirectivesCount = []int64{ + expData.SnippetsFiltersDirectiveContextsCount = []int64{ 3, 2, 2, @@ -433,6 +436,7 @@ var _ = Describe("Collector", Ordered, func() { 1, 1, 1, + 1, } data, err := dataCollector.Collect(ctx) diff --git a/internal/mode/static/telemetry/data.avdl b/internal/mode/static/telemetry/data.avdl index 675bf284f5..48c3a0a340 100644 --- a/internal/mode/static/telemetry/data.avdl +++ b/internal/mode/static/telemetry/data.avdl @@ -45,14 +45,16 @@ at the same index. Each value is either 'true' or 'false' for boolean flags and 'default' or 'user-defined' for non-boolean flags. */ union {null, array} FlagValues = null; - /** SnippetsFiltersContextDirectives contains the context-directive strings of all applied SnippetsFilters. -Both lists are ordered first by count, then by lexicographical order on the context-directive string. */ - union {null, array} SnippetsFiltersContextDirectives = null; - - /** SnippetsFiltersContextDirectivesCount contains the count of the context-directive strings, where each count -corresponds to the string from SnippetsFiltersContextDirectives at the same index. Both lists are ordered -first by count, then by lexicographical order on the context-directive string. */ - union {null, array} SnippetsFiltersContextDirectivesCount = null; + /** SnippetsFiltersDirectiveContexts contains the context-directive strings of all applied SnippetsFilters. +Both lists are ordered first by count, then by lexicographical order of the context string, +then lastly by directive string. */ + union {null, array} SnippetsFiltersDirectiveContexts = null; + + /** SnippetsFiltersDirectiveContextsCount contains the count of the directive-context strings, where each count +corresponds to the string from SnippetsFiltersDirectiveContexts at the same index. +Both lists are ordered first by count, then by lexicographical order of the context string, +then lastly by directive string. */ + union {null, array} SnippetsFiltersDirectiveContextsCount = null; /** GatewayCount is the number of relevant Gateways. */ long? GatewayCount = null; diff --git a/internal/mode/static/telemetry/data_attributes_generated.go b/internal/mode/static/telemetry/data_attributes_generated.go index 6e851e7730..b2dc22c795 100644 --- a/internal/mode/static/telemetry/data_attributes_generated.go +++ b/internal/mode/static/telemetry/data_attributes_generated.go @@ -17,8 +17,8 @@ func (d *Data) Attributes() []attribute.KeyValue { attrs = append(attrs, d.Data.Attributes()...) attrs = append(attrs, attribute.StringSlice("FlagNames", d.FlagNames)) attrs = append(attrs, attribute.StringSlice("FlagValues", d.FlagValues)) - attrs = append(attrs, attribute.StringSlice("SnippetsFiltersContextDirectives", d.SnippetsFiltersContextDirectives)) - attrs = append(attrs, attribute.Int64Slice("SnippetsFiltersContextDirectivesCount", d.SnippetsFiltersContextDirectivesCount)) + attrs = append(attrs, attribute.StringSlice("SnippetsFiltersDirectiveContexts", d.SnippetsFiltersDirectiveContexts)) + attrs = append(attrs, attribute.Int64Slice("SnippetsFiltersDirectiveContextsCount", d.SnippetsFiltersDirectiveContextsCount)) attrs = append(attrs, d.NGFResourceCounts.Attributes()...) attrs = append(attrs, attribute.Int64("NGFReplicaCount", d.NGFReplicaCount)) diff --git a/internal/mode/static/telemetry/data_test.go b/internal/mode/static/telemetry/data_test.go index 015e7f2484..9d4219a5ea 100644 --- a/internal/mode/static/telemetry/data_test.go +++ b/internal/mode/static/telemetry/data_test.go @@ -41,8 +41,8 @@ func TestDataAttributes(t *testing.T) { SnippetsFilterCount: 13, }, NGFReplicaCount: 3, - SnippetsFiltersContextDirectives: []string{"main-three-count", "http-two-count", "server-one-count"}, - SnippetsFiltersContextDirectivesCount: []int64{3, 2, 1}, + SnippetsFiltersDirectiveContexts: []string{"main-three-count", "http-two-count", "server-one-count"}, + SnippetsFiltersDirectiveContextsCount: []int64{3, 2, 1}, } expected := []attribute.KeyValue{ @@ -59,10 +59,10 @@ func TestDataAttributes(t *testing.T) { attribute.StringSlice("FlagNames", []string{"test-flag"}), attribute.StringSlice("FlagValues", []string{"test-value"}), attribute.StringSlice( - "SnippetsFiltersContextDirectives", + "SnippetsFiltersDirectiveContexts", []string{"main-three-count", "http-two-count", "server-one-count"}, ), - attribute.IntSlice("SnippetsFiltersContextDirectivesCount", []int{3, 2, 1}), + attribute.IntSlice("SnippetsFiltersDirectiveContextsCount", []int{3, 2, 1}), attribute.Int64("GatewayCount", 1), attribute.Int64("GatewayClassCount", 2), attribute.Int64("HTTPRouteCount", 3), @@ -103,8 +103,8 @@ func TestDataAttributesWithEmptyData(t *testing.T) { attribute.Int64("ClusterNodeCount", 0), attribute.StringSlice("FlagNames", nil), attribute.StringSlice("FlagValues", nil), - attribute.StringSlice("SnippetsFiltersContextDirectives", nil), - attribute.IntSlice("SnippetsFiltersContextDirectivesCount", nil), + attribute.StringSlice("SnippetsFiltersDirectiveContexts", nil), + attribute.IntSlice("SnippetsFiltersDirectiveContextsCount", nil), attribute.Int64("GatewayCount", 0), attribute.Int64("GatewayClassCount", 0), attribute.Int64("HTTPRouteCount", 0), diff --git a/site/content/overview/product-telemetry.md b/site/content/overview/product-telemetry.md index 42a9be6375..237abb1303 100644 --- a/site/content/overview/product-telemetry.md +++ b/site/content/overview/product-telemetry.md @@ -28,7 +28,7 @@ Telemetry data is collected once every 24 hours and sent to a service managed by - **Image Build Source:** whether the image was built by GitHub or locally (values are `gha`, `local`, or `unknown`). The source repository of the images is **not** collected. - **Deployment Flags:** a list of NGINX Gateway Fabric Deployment flags that are specified by a user. The actual values of non-boolean flags are **not** collected; we only record that they are either `true` or `false` for boolean flags and `default` or `user-defined` for the rest. - **Count of Resources:** the total count of resources related to NGINX Gateway Fabric. This includes `GatewayClasses`, `Gateways`, `HTTPRoutes`,`GRPCRoutes`, `TLSRoutes`, `Secrets`, `Services`, `BackendTLSPolicies`, `ClientSettingsPolicies`, `NginxProxies`, `ObservabilityPolicies`, `SnippetsFilters`, and `Endpoints`. The data within these resources is **not** collected. -- **SnippetsFilters Info**a list of context-directive strings from applied SnippetFilters and a total count per strings. The actual value of any NGINX directive is **not** collected. +- **SnippetsFilters Info**a list of directive-context strings from applied SnippetFilters and a total count per strings. The actual value of any NGINX directive is **not** collected. This data is used to identify the following information: - The flavors of Kubernetes environments that are most popular among our users. diff --git a/tests/suite/telemetry_test.go b/tests/suite/telemetry_test.go index 25c084f8c9..57cc638352 100644 --- a/tests/suite/telemetry_test.go +++ b/tests/suite/telemetry_test.go @@ -72,8 +72,8 @@ var _ = Describe("Telemetry test with OTel collector", Label("telemetry"), func( fmt.Sprintf("ClusterNodeCount: Int(%d)", info.NodeCount), "FlagNames: Slice", "FlagValues: Slice", - "SnippetsFiltersContextDirectives: Slice", - "SnippetsFiltersContextDirectivesCount: Slice", + "SnippetsFiltersDirectiveContexts: Slice", + "SnippetsFiltersDirectiveContextsCount: Slice", "GatewayCount: Int(0)", "GatewayClassCount: Int(1)", "HTTPRouteCount: Int(0)",