5
5
"errors"
6
6
"fmt"
7
7
"runtime"
8
+ "sort"
9
+ "strings"
8
10
9
11
tel "github.com/nginxinc/telemetry-exporter/pkg/telemetry"
10
12
appsv1 "k8s.io/api/apps/v1"
@@ -14,6 +16,7 @@ import (
14
16
k8sversion "k8s.io/apimachinery/pkg/util/version"
15
17
"sigs.k8s.io/controller-runtime/pkg/client"
16
18
19
+ ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
17
20
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds"
18
21
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/config"
19
22
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
@@ -46,8 +49,17 @@ type Data struct {
46
49
// FlagValues contains the values of the command-line flags, where each value corresponds to the flag from FlagNames
47
50
// at the same index.
48
51
// Each value is either 'true' or 'false' for boolean flags and 'default' or 'user-defined' for non-boolean flags.
49
- FlagValues []string
50
- NGFResourceCounts // embedding is required by the generator.
52
+ FlagValues []string
53
+ // SnippetsFiltersDirectives contains the directive-context strings of all applied SnippetsFilters.
54
+ // Both lists are ordered first by count, then by lexicographical order of the context string,
55
+ // then lastly by directive string.
56
+ SnippetsFiltersDirectives []string
57
+ // SnippetsFiltersDirectivesCount contains the count of the directive-context strings, where each count
58
+ // corresponds to the string from SnippetsFiltersDirectives at the same index.
59
+ // Both lists are ordered first by count, then by lexicographical order of the context string,
60
+ // then lastly by directive string.
61
+ SnippetsFiltersDirectivesCount []int64
62
+ NGFResourceCounts // embedding is required by the generator.
51
63
// NGFReplicaCount is the number of replicas of the NGF Pod.
52
64
NGFReplicaCount int64
53
65
}
@@ -83,6 +95,8 @@ type NGFResourceCounts struct {
83
95
ObservabilityPolicyCount int64
84
96
// NginxProxyCount is the number of NginxProxies.
85
97
NginxProxyCount int64
98
+ // SnippetsFilterCount is the number of SnippetsFilters.
99
+ SnippetsFilterCount int64
86
100
}
87
101
88
102
// DataCollectorConfig holds configuration parameters for DataCollectorImpl.
@@ -119,12 +133,17 @@ func NewDataCollectorImpl(
119
133
120
134
// Collect collects and returns telemetry Data.
121
135
func (c DataCollectorImpl ) Collect (ctx context.Context ) (Data , error ) {
136
+ g := c .cfg .GraphGetter .GetLatestGraph ()
137
+ if g == nil {
138
+ return Data {}, errors .New ("failed to collect telemetry data: latest graph cannot be nil" )
139
+ }
140
+
122
141
clusterInfo , err := collectClusterInformation (ctx , c .cfg .K8sClientReader )
123
142
if err != nil {
124
143
return Data {}, fmt .Errorf ("failed to collect cluster information: %w" , err )
125
144
}
126
145
127
- graphResourceCount , err := collectGraphResourceCount (c . cfg . GraphGetter , c .cfg .ConfigurationGetter )
146
+ graphResourceCount , err := collectGraphResourceCount (g , c .cfg .ConfigurationGetter )
128
147
if err != nil {
129
148
return Data {}, fmt .Errorf ("failed to collect NGF resource counts: %w" , err )
130
149
}
@@ -144,6 +163,8 @@ func (c DataCollectorImpl) Collect(ctx context.Context) (Data, error) {
144
163
return Data {}, fmt .Errorf ("failed to get NGF deploymentID: %w" , err )
145
164
}
146
165
166
+ snippetsFiltersDirectives , snippetsFiltersDirectivesCount := collectSnippetsFilterDirectives (g )
167
+
147
168
data := Data {
148
169
Data : tel.Data {
149
170
ProjectName : "NGF" ,
@@ -155,27 +176,25 @@ func (c DataCollectorImpl) Collect(ctx context.Context) (Data, error) {
155
176
InstallationID : deploymentID ,
156
177
ClusterNodeCount : int64 (clusterInfo .NodeCount ),
157
178
},
158
- NGFResourceCounts : graphResourceCount ,
159
- ImageSource : c .cfg .ImageSource ,
160
- FlagNames : c .cfg .Flags .Names ,
161
- FlagValues : c .cfg .Flags .Values ,
162
- NGFReplicaCount : int64 (replicaCount ),
179
+ NGFResourceCounts : graphResourceCount ,
180
+ ImageSource : c .cfg .ImageSource ,
181
+ FlagNames : c .cfg .Flags .Names ,
182
+ FlagValues : c .cfg .Flags .Values ,
183
+ NGFReplicaCount : int64 (replicaCount ),
184
+ SnippetsFiltersDirectives : snippetsFiltersDirectives ,
185
+ SnippetsFiltersDirectivesCount : snippetsFiltersDirectivesCount ,
163
186
}
164
187
165
188
return data , nil
166
189
}
167
190
168
191
func collectGraphResourceCount (
169
- graphGetter GraphGetter ,
192
+ g * graph. Graph ,
170
193
configurationGetter ConfigurationGetter ,
171
194
) (NGFResourceCounts , error ) {
172
195
ngfResourceCounts := NGFResourceCounts {}
173
- g := graphGetter .GetLatestGraph ()
174
196
cfg := configurationGetter .GetLatestConfiguration ()
175
197
176
- if g == nil {
177
- return ngfResourceCounts , errors .New ("latest graph cannot be nil" )
178
- }
179
198
if cfg == nil {
180
199
return ngfResourceCounts , errors .New ("latest configuration cannot be nil" )
181
200
}
@@ -227,6 +246,8 @@ func collectGraphResourceCount(
227
246
ngfResourceCounts .NginxProxyCount = 1
228
247
}
229
248
249
+ ngfResourceCounts .SnippetsFilterCount = int64 (len (g .SnippetsFilters ))
250
+
230
251
return ngfResourceCounts , nil
231
252
}
232
253
@@ -378,3 +399,103 @@ func collectClusterInformation(ctx context.Context, k8sClient client.Reader) (cl
378
399
379
400
return clusterInfo , nil
380
401
}
402
+
403
+ type sfDirectiveContext struct {
404
+ directive string
405
+ context string
406
+ }
407
+
408
+ func collectSnippetsFilterDirectives (g * graph.Graph ) ([]string , []int64 ) {
409
+ directiveContextMap := make (map [sfDirectiveContext ]int )
410
+
411
+ for _ , sf := range g .SnippetsFilters {
412
+ if sf == nil {
413
+ continue
414
+ }
415
+
416
+ for nginxContext , snippetValue := range sf .Snippets {
417
+ var parsedContext string
418
+
419
+ switch nginxContext {
420
+ case ngfAPI .NginxContextMain :
421
+ parsedContext = "main"
422
+ case ngfAPI .NginxContextHTTP :
423
+ parsedContext = "http"
424
+ case ngfAPI .NginxContextHTTPServer :
425
+ parsedContext = "server"
426
+ case ngfAPI .NginxContextHTTPServerLocation :
427
+ parsedContext = "location"
428
+ default :
429
+ parsedContext = "unknown"
430
+ }
431
+
432
+ directives := parseSnippetValueIntoDirectives (snippetValue )
433
+ for _ , directive := range directives {
434
+ directiveContext := sfDirectiveContext {
435
+ directive : directive ,
436
+ context : parsedContext ,
437
+ }
438
+ directiveContextMap [directiveContext ]++
439
+ }
440
+ }
441
+ }
442
+
443
+ return parseDirectiveContextMapIntoLists (directiveContextMap )
444
+ }
445
+
446
+ func parseSnippetValueIntoDirectives (snippetValue string ) []string {
447
+ separatedDirectives := strings .Split (snippetValue , ";" )
448
+ directives := make ([]string , 0 , len (separatedDirectives ))
449
+
450
+ for _ , directive := range separatedDirectives {
451
+ // the strings.TrimSpace is needed in the case of multi-line NGINX Snippet values
452
+ directive = strings .Split (strings .TrimSpace (directive ), " " )[0 ]
453
+
454
+ // splitting on the delimiting character can result in a directive being empty or a space/newline character,
455
+ // so we check here to ensure it's not
456
+ if directive != "" {
457
+ directives = append (directives , directive )
458
+ }
459
+ }
460
+
461
+ return directives
462
+ }
463
+
464
+ // parseDirectiveContextMapIntoLists returns two same-length lists where the elements at each corresponding index
465
+ // are paired together.
466
+ // The first list contains strings which are the NGINX directive and context of a Snippet joined with a hyphen.
467
+ // The second list contains ints which are the count of total same directive-context values of the first list.
468
+ // Both lists are ordered first by count, then by lexicographical order of the context string,
469
+ // then lastly by directive string.
470
+ func parseDirectiveContextMapIntoLists (directiveContextMap map [sfDirectiveContext ]int ) ([]string , []int64 ) {
471
+ type sfDirectiveContextCount struct {
472
+ directive , context string
473
+ count int64
474
+ }
475
+
476
+ kvPairs := make ([]sfDirectiveContextCount , 0 , len (directiveContextMap ))
477
+
478
+ for k , v := range directiveContextMap {
479
+ kvPairs = append (kvPairs , sfDirectiveContextCount {k .directive , k .context , int64 (v )})
480
+ }
481
+
482
+ sort .Slice (kvPairs , func (i , j int ) bool {
483
+ if kvPairs [i ].count == kvPairs [j ].count {
484
+ if kvPairs [i ].context == kvPairs [j ].context {
485
+ return kvPairs [i ].directive < kvPairs [j ].directive
486
+ }
487
+ return kvPairs [i ].context < kvPairs [j ].context
488
+ }
489
+ return kvPairs [i ].count > kvPairs [j ].count
490
+ })
491
+
492
+ directiveContextList := make ([]string , len (kvPairs ))
493
+ countList := make ([]int64 , len (kvPairs ))
494
+
495
+ for i , pair := range kvPairs {
496
+ directiveContextList [i ] = pair .directive + "-" + pair .context
497
+ countList [i ] = pair .count
498
+ }
499
+
500
+ return directiveContextList , countList
501
+ }
0 commit comments