Skip to content

Commit ed19da4

Browse files
authored
Add ConfigMapKeys & MGMTConfigMapKeys to Telemetry (#7695)
1 parent a0e11a8 commit ed19da4

File tree

9 files changed

+666
-6
lines changed

9 files changed

+666
-6
lines changed

internal/k8s/controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,10 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc
420420
},
421421
Policies: lbc.getAllPolicies,
422422
IsPlus: lbc.isNginxPlus,
423+
MainConfigMap: lbc.configMap,
424+
MainConfigMapName: lbc.nginxConfigMapName,
425+
MGMTConfigMap: lbc.mgmtConfigMap,
426+
MGMTConfigMapName: lbc.mgmtConfigMapName,
423427
CustomResourcesEnabled: lbc.areCustomResourcesEnabled,
424428
}
425429
collector, err := telemetry.NewCollector(

internal/telemetry/cluster.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,109 @@ import (
1010
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1111
)
1212

13+
// configMapFilteredKeys and mgmtConfigMapFilteredKeys are lists containing keys from the main ConfigMap and MGMT ConfigMap that are used by NIC
14+
// These will need to be updated if new keys are added to the ConfigMap or MGMTConfigMap.
15+
var configMapFilteredKeys = []string{
16+
"external-status-address",
17+
"server-tokens",
18+
"lb-method",
19+
"proxy-connect-timeout",
20+
"proxy-read-timeout",
21+
"proxy-send-timeout",
22+
"proxy-hide-headers",
23+
"proxy-pass-headers",
24+
"client-max-body-size",
25+
"server-names-hash-bucket-size",
26+
"server-names-hash-max-size",
27+
"map-hash-bucket-size",
28+
"map-hash-max-size",
29+
"http2",
30+
"redirect-to-https",
31+
"ssl-redirect",
32+
"hsts",
33+
"hsts-max-age",
34+
"hsts-include-subdomains",
35+
"hsts-behind-proxy",
36+
"proxy-protocol",
37+
"real-ip-header",
38+
"set-real-ip-from",
39+
"real-ip-recursive",
40+
"ssl-protocols",
41+
"ssl-prefer-server-ciphers",
42+
"ssl-ciphers",
43+
"ssl-dhparam-file",
44+
"error-log-level",
45+
"access-log",
46+
"access-log-off",
47+
"log-format",
48+
"log-format-escaping",
49+
"stream-log-format",
50+
"stream-log-format-escaping",
51+
"default-server-access-log-off",
52+
"default-server-return",
53+
"proxy-buffering",
54+
"proxy-buffers",
55+
"proxy-buffer-size",
56+
"proxy-max-temp-file-size",
57+
"main-snippets",
58+
"http-snippets",
59+
"location-snippets",
60+
"server-snippets",
61+
"worker-processes",
62+
"worker-cpu-affinity",
63+
"worker-shutdown-timeout",
64+
"worker-connections",
65+
"worker-rlimit-nofile",
66+
"keepalive",
67+
"max-fails",
68+
"upstream-zone-size",
69+
"fail-timeout",
70+
"main-template",
71+
"ingress-template",
72+
"virtualserver-template",
73+
"transportserver-template",
74+
"stream-snippets",
75+
"resolver-addresses",
76+
"resolver-ipv6",
77+
"resolver-valid",
78+
"resolver-timeout",
79+
"keepalive-timeout",
80+
"keepalive-requests",
81+
"variables-hash-bucket-size",
82+
"variables-hash-max-size",
83+
"opentracing-tracer",
84+
"opentracing-tracer-config",
85+
"opentracing",
86+
"app-protect-failure-mode-action",
87+
"app-protect-compressed-requests-action",
88+
"app-protect-cookie-seed",
89+
"app-protect-cpu-thresholds",
90+
"app-protect-physical-memory-util-thresholds",
91+
"app-protect-reconnect-period-seconds",
92+
"app-protect-dos-log-format",
93+
"app-protect-dos-log-format-escaping",
94+
"app-protect-dos-arb-fqdn",
95+
"zone-sync",
96+
"zone-sync-port",
97+
"zone-sync-resolver-addresses",
98+
"zone-sync-resolver-valid",
99+
"zone-sync-resolver-ipv6",
100+
}
101+
102+
var mgmtConfigMapFilteredKeys = []string{
103+
"license-token-secret-name",
104+
"ssl-verify",
105+
"resolver-addresses",
106+
"resolver-ipv6",
107+
"resolver-valid",
108+
"enforce-initial-report",
109+
"usage-report-endpoint",
110+
"usage-report-interval",
111+
"ssl-trusted-certificate-secret-name",
112+
"ssl-certificate-secret-name",
113+
"usage-report-proxy-host",
114+
}
115+
13116
// NodeCount returns the total number of nodes in the cluster.
14117
// It returns an error if the underlying k8s API client errors.
15118
func (c *Collector) NodeCount(ctx context.Context) (int, error) {
@@ -199,6 +302,55 @@ func (c *Collector) BuildOS() string {
199302
return c.Config.BuildOS
200303
}
201304

305+
// ConfigMapKeys gets the main ConfigMap keys from the configMapKeys function that accesses the K8s API and returns keys that are filtered and used by NIC.
306+
func (c *Collector) ConfigMapKeys(ctx context.Context) ([]string, error) {
307+
return c.configMapKeys(ctx,
308+
c.Config.MainConfigMapName,
309+
configMapFilteredKeys,
310+
)
311+
}
312+
313+
// MGMTConfigMapKeys gets the MGMT ConfigMap keys from the configMapKeys function that accesses the K8s API and returns keys that are filtered and used by NIC.
314+
func (c *Collector) MGMTConfigMapKeys(ctx context.Context) ([]string, error) {
315+
return c.configMapKeys(ctx,
316+
c.Config.MGMTConfigMapName,
317+
mgmtConfigMapFilteredKeys,
318+
)
319+
}
320+
321+
// / configMapKeys is a helper function that retrieves the keys from the ConfigMap
322+
// and filters them based on the provided filteredConfigMapKeys.
323+
func (c *Collector) configMapKeys(
324+
ctx context.Context,
325+
configMapName string,
326+
filteredConfigMapKeys []string,
327+
) ([]string, error) {
328+
parts := strings.Split(configMapName, "/")
329+
if len(parts) != 2 {
330+
return nil, fmt.Errorf("invalid config map name: %s", configMapName)
331+
}
332+
namespace, name := parts[0], parts[1]
333+
334+
configMap, err := c.Config.K8sClientReader.CoreV1().ConfigMaps(namespace).Get(ctx, name, metaV1.GetOptions{})
335+
if err != nil {
336+
return nil, err
337+
}
338+
339+
filteredKeys := make(map[string]struct{}, len(filteredConfigMapKeys))
340+
for _, key := range filteredConfigMapKeys {
341+
filteredKeys[key] = struct{}{}
342+
}
343+
344+
var keys []string
345+
for k := range configMap.Data {
346+
if _, ok := filteredKeys[k]; ok {
347+
keys = append(keys, k)
348+
}
349+
}
350+
351+
return keys, nil
352+
}
353+
202354
// lookupPlatform takes a string representing a K8s PlatformID
203355
// retrieved from a cluster node and returns a string
204356
// representing the platform name.

internal/telemetry/cluster_test.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"context"
55
"testing"
66

7+
"github.com/stretchr/testify/assert"
8+
79
"github.com/google/go-cmp/cmp"
810

911
"github.com/nginx/kubernetes-ingress/internal/telemetry"
@@ -533,6 +535,210 @@ func TestGetServices(t *testing.T) {
533535
}
534536
}
535537

538+
func TestConfigMapKeys(t *testing.T) {
539+
t.Parallel()
540+
testCases := []struct {
541+
name string
542+
config telemetry.CollectorConfig
543+
want []string
544+
}{
545+
{
546+
name: "ConfigMap With some keys",
547+
config: telemetry.CollectorConfig{
548+
K8sClientReader: newTestClientset(
549+
&apiCoreV1.ConfigMap{
550+
ObjectMeta: metaV1.ObjectMeta{
551+
Name: "nginx-ingress",
552+
Namespace: "nginx-ingress",
553+
},
554+
Data: map[string]string{
555+
"proxy-buffering": "false",
556+
"zone-sync": "true",
557+
},
558+
},
559+
),
560+
MainConfigMapName: "nginx-ingress/nginx-ingress",
561+
},
562+
want: []string{"proxy-buffering", "zone-sync"},
563+
},
564+
{
565+
name: "ConfigMap With no keys",
566+
config: telemetry.CollectorConfig{
567+
K8sClientReader: newTestClientset(
568+
&apiCoreV1.ConfigMap{
569+
ObjectMeta: metaV1.ObjectMeta{
570+
Name: "nginx-ingress",
571+
Namespace: "nginx-ingress",
572+
},
573+
Data: map[string]string{},
574+
},
575+
),
576+
MainConfigMapName: "nginx-ingress/nginx-ingress",
577+
},
578+
want: nil,
579+
},
580+
{
581+
name: "ConfigMap With ignored keys",
582+
config: telemetry.CollectorConfig{
583+
K8sClientReader: newTestClientset(
584+
&apiCoreV1.ConfigMap{
585+
ObjectMeta: metaV1.ObjectMeta{
586+
Name: "nginx-ingress",
587+
Namespace: "nginx-ingress",
588+
},
589+
Data: map[string]string{
590+
"enforce-initial-report": "false",
591+
"sync": "hello",
592+
"hello": "world",
593+
},
594+
},
595+
),
596+
MainConfigMapName: "nginx-ingress/nginx-ingress",
597+
},
598+
want: nil,
599+
},
600+
{
601+
name: "ConfigMap With ignored keys and valid keys",
602+
config: telemetry.CollectorConfig{
603+
K8sClientReader: newTestClientset(
604+
&apiCoreV1.ConfigMap{
605+
ObjectMeta: metaV1.ObjectMeta{
606+
Name: "nginx-ingress",
607+
Namespace: "nginx-ingress",
608+
},
609+
Data: map[string]string{
610+
"proxy-buffering": "false",
611+
"enforce-initial-report": "false",
612+
"sync": "hello",
613+
"hello": "world",
614+
"zone-sync": "true",
615+
},
616+
},
617+
),
618+
MainConfigMapName: "nginx-ingress/nginx-ingress",
619+
},
620+
want: []string{"proxy-buffering", "zone-sync"},
621+
},
622+
}
623+
624+
for _, tc := range testCases {
625+
t.Run(tc.name, func(t *testing.T) {
626+
c, err := telemetry.NewCollector(tc.config)
627+
if err != nil {
628+
t.Fatal(err)
629+
}
630+
got, err := c.ConfigMapKeys(context.Background())
631+
if err != nil {
632+
t.Fatal(err)
633+
}
634+
if !assert.ElementsMatch(t, tc.want, got, "MGMTConfigMap keys do not match") {
635+
t.Error(cmp.Diff(tc.want, got))
636+
}
637+
})
638+
}
639+
}
640+
641+
func TestMGMTConfigMapKeys(t *testing.T) {
642+
t.Parallel()
643+
testCases := []struct {
644+
name string
645+
config telemetry.CollectorConfig
646+
want []string
647+
}{
648+
{
649+
name: "MGMTConfigMap With some keys",
650+
config: telemetry.CollectorConfig{
651+
K8sClientReader: newTestClientset(
652+
&apiCoreV1.ConfigMap{
653+
ObjectMeta: metaV1.ObjectMeta{
654+
Name: "nginx-ingress-mgmt",
655+
Namespace: "nginx-ingress",
656+
},
657+
Data: map[string]string{
658+
"enforce-initial-report": "false",
659+
"license-token-secret-name": "license-token",
660+
},
661+
},
662+
),
663+
MGMTConfigMapName: "nginx-ingress/nginx-ingress-mgmt",
664+
},
665+
want: []string{"enforce-initial-report", "license-token-secret-name"},
666+
},
667+
{
668+
name: "MGMTConfigMap With no keys",
669+
config: telemetry.CollectorConfig{
670+
K8sClientReader: newTestClientset(
671+
&apiCoreV1.ConfigMap{
672+
ObjectMeta: metaV1.ObjectMeta{
673+
Name: "nginx-ingress-mgmt",
674+
Namespace: "nginx-ingress",
675+
},
676+
Data: map[string]string{},
677+
},
678+
),
679+
MGMTConfigMapName: "nginx-ingress/nginx-ingress-mgmt",
680+
},
681+
want: nil,
682+
},
683+
{
684+
name: "MGMTConfigMap With ignored keys",
685+
config: telemetry.CollectorConfig{
686+
K8sClientReader: newTestClientset(
687+
&apiCoreV1.ConfigMap{
688+
ObjectMeta: metaV1.ObjectMeta{
689+
Name: "nginx-ingress-mgmt",
690+
Namespace: "nginx-ingress",
691+
},
692+
Data: map[string]string{
693+
"zone-sync": "false",
694+
"license": "test",
695+
},
696+
},
697+
),
698+
MGMTConfigMapName: "nginx-ingress/nginx-ingress-mgmt",
699+
},
700+
want: nil,
701+
},
702+
{
703+
name: "MGMTConfigMap With ignored keys and valid keys",
704+
config: telemetry.CollectorConfig{
705+
K8sClientReader: newTestClientset(
706+
&apiCoreV1.ConfigMap{
707+
ObjectMeta: metaV1.ObjectMeta{
708+
Name: "nginx-ingress-mgmt",
709+
Namespace: "nginx-ingress",
710+
},
711+
Data: map[string]string{
712+
"zone-sync": "false",
713+
"license": "test",
714+
"enforce-initial-report": "false",
715+
"license-token-secret-name": "license-token",
716+
},
717+
},
718+
),
719+
MGMTConfigMapName: "nginx-ingress/nginx-ingress-mgmt",
720+
},
721+
want: []string{"enforce-initial-report", "license-token-secret-name"},
722+
},
723+
}
724+
725+
for _, tc := range testCases {
726+
t.Run(tc.name, func(t *testing.T) {
727+
c, err := telemetry.NewCollector(tc.config)
728+
if err != nil {
729+
t.Fatal(err)
730+
}
731+
got, err := c.MGMTConfigMapKeys(context.Background())
732+
if err != nil {
733+
t.Fatal(err)
734+
}
735+
if !assert.ElementsMatch(t, tc.want, got, "MGMTConfigMap keys do not match") {
736+
t.Error(cmp.Diff(tc.want, got))
737+
}
738+
})
739+
}
740+
}
741+
536742
// newTestCollectorForClusterWithNodes returns a telemetry collector configured
537743
// to simulate collecting data on a cluser with provided nodes.
538744
func newTestCollectorForClusterWithNodes(t *testing.T, nodes ...runtime.Object) *telemetry.Collector {

0 commit comments

Comments
 (0)