diff --git a/charts/nginx-ingress/templates/clusterrole.yaml b/charts/nginx-ingress/templates/clusterrole.yaml index a231ca820e..c188603921 100644 --- a/charts/nginx-ingress/templates/clusterrole.yaml +++ b/charts/nginx-ingress/templates/clusterrole.yaml @@ -68,6 +68,7 @@ rules: - ingressclasses verbs: - get + - list {{- if .Values.controller.reportIngressStatus.enable }} - apiGroups: - networking.k8s.io diff --git a/deployments/rbac/rbac.yaml b/deployments/rbac/rbac.yaml index 6427b4e8e7..2c1da125f2 100644 --- a/deployments/rbac/rbac.yaml +++ b/deployments/rbac/rbac.yaml @@ -127,6 +127,7 @@ rules: - ingressclasses verbs: - get + - list - apiGroups: - cis.f5.com resources: diff --git a/docs/content/overview/product-telemetry.md b/docs/content/overview/product-telemetry.md index edf98540c8..e393f93f0f 100644 --- a/docs/content/overview/product-telemetry.md +++ b/docs/content/overview/product-telemetry.md @@ -34,8 +34,9 @@ These are the data points collected and reported by NGINX Ingress Controller: - **TransportServers** The number of TransportServer resources managed by NGINX Ingress Controller. - **Replicas** Number of Deployment replicas, or Daemonset instances. - **Secrets** Number of Secret resources managed by NGINX Ingress Controller. +- **Ingress Count** Number of Ingresses. +- **Ingress Classes** Number of Ingress Classes. - **Services** Number of Services referenced by VirtualServers, VirtualServerRoutes, TransportServers and Ingresses. -- **IngressCount** Number of Ingresses. ## Opt out diff --git a/internal/telemetry/cluster.go b/internal/telemetry/cluster.go index 82ccd030dd..dc30f2f091 100644 --- a/internal/telemetry/cluster.go +++ b/internal/telemetry/cluster.go @@ -136,6 +136,15 @@ func (c *Collector) IngressCount() int { return total } +// IngressClassCount returns number of Ingress Classes. +func (c *Collector) IngressClassCount(ctx context.Context) (int, error) { + ic, err := c.Config.K8sClientReader.NetworkingV1().IngressClasses().List(ctx, metaV1.ListOptions{}) + if err != nil { + return 0, err + } + return len(ic.Items), nil +} + // lookupPlatform takes a string representing a K8s PlatformID // retrieved from a cluster node and returns a string // representing the platform name. diff --git a/internal/telemetry/cluster_test.go b/internal/telemetry/cluster_test.go index bf78a010a4..ab25365a09 100644 --- a/internal/telemetry/cluster_test.go +++ b/internal/telemetry/cluster_test.go @@ -7,7 +7,9 @@ import ( "github.com/nginxinc/kubernetes-ingress/internal/telemetry" appsV1 "k8s.io/api/apps/v1" apiCoreV1 "k8s.io/api/core/v1" + networkingV1 "k8s.io/api/networking/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ) @@ -913,3 +915,26 @@ var ( Data: map[string][]byte{}, } ) + +// IngressClass for testing. +var ( + ingressClassList = &networkingV1.IngressClassList{ + TypeMeta: metaV1.TypeMeta{ + Kind: "List", + APIVersion: "v1", + }, + ListMeta: metaV1.ListMeta{}, + Items: []networkingV1.IngressClass{ + { + TypeMeta: metaV1.TypeMeta{ + Kind: "IngressClass", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metaV1.ObjectMeta{}, + Spec: networkingV1.IngressClassSpec{ + Controller: "nginx.org/ingress-controller", + }, + }, + }, + } +) diff --git a/internal/telemetry/collector.go b/internal/telemetry/collector.go index 32d30d6c0c..3ff2e25da7 100644 --- a/internal/telemetry/collector.go +++ b/internal/telemetry/collector.go @@ -118,6 +118,7 @@ func (c *Collector) Collect(ctx context.Context) { Secrets: int64(report.Secrets), Services: int64(report.ServiceCount), Ingresses: int64(report.IngressCount), + IngressClasses: int64(report.IngressClassCount), }, } @@ -147,6 +148,7 @@ type Report struct { TransportServers int Secrets int IngressCount int + IngressClassCount int } // BuildReport takes context, collects telemetry data and builds the report. @@ -197,6 +199,10 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) { glog.Errorf("Error collecting telemetry data: Secrets: %v", err) } ingressCount := c.IngressCount() + ingressClassCount, err := c.IngressClassCount(ctx) + if err != nil { + glog.Errorf("Error collecting telemetry data: Ingress Classes: %v", err) + } return Report{ Name: "NIC", @@ -214,5 +220,6 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) { TransportServers: tsCount, Secrets: secrets, IngressCount: ingressCount, + IngressClassCount: ingressClassCount, }, err } diff --git a/internal/telemetry/collector_test.go b/internal/telemetry/collector_test.go index c6cd6bcae2..e474048a16 100644 --- a/internal/telemetry/collector_test.go +++ b/internal/telemetry/collector_test.go @@ -764,6 +764,101 @@ func TestCountSecretsAddTwoSecretsAndDeleteOne(t *testing.T) { } } +func TestIngressClassCountReportsNoIngressClasses(t *testing.T) { + t.Parallel() + + buf := &bytes.Buffer{} + exp := &telemetry.StdoutExporter{Endpoint: buf} + + cfg := telemetry.CollectorConfig{ + Configurator: newConfigurator(t), + K8sClientReader: newTestClientset(node1, kubeNS), + Version: telemetryNICData.ProjectVersion, + } + + c, err := telemetry.NewCollector(cfg, telemetry.WithExporter(exp)) + if err != nil { + t.Fatal(err) + } + c.Collect(context.Background()) + + telData := tel.Data{ + ProjectName: telemetryNICData.ProjectName, + ProjectVersion: telemetryNICData.ProjectVersion, + ProjectArchitecture: telemetryNICData.ProjectArchitecture, + ClusterNodeCount: 1, + ClusterID: telemetryNICData.ClusterID, + ClusterVersion: telemetryNICData.ClusterVersion, + ClusterPlatform: "other", + } + + nicResourceCounts := telemetry.NICResourceCounts{ + VirtualServers: 0, + VirtualServerRoutes: 0, + TransportServers: 0, + Ingresses: 0, + IngressClasses: 0, + } + + td := telemetry.Data{ + telData, + nicResourceCounts, + } + + want := fmt.Sprintf("%+v", &td) + got := buf.String() + if !cmp.Equal(want, got) { + t.Error(cmp.Diff(want, got)) + } +} + +func TestIngressClassCountReportsIngressClasses(t *testing.T) { + t.Parallel() + buf := &bytes.Buffer{} + exp := &telemetry.StdoutExporter{Endpoint: buf} + + cfg := telemetry.CollectorConfig{ + Configurator: newConfigurator(t), + K8sClientReader: newTestClientset(node1, kubeNS, ingressClassList), + Version: telemetryNICData.ProjectVersion, + } + + c, err := telemetry.NewCollector(cfg, telemetry.WithExporter(exp)) + if err != nil { + t.Fatal(err) + } + c.Collect(context.Background()) + + telData := tel.Data{ + ProjectName: telemetryNICData.ProjectName, + ProjectVersion: telemetryNICData.ProjectVersion, + ProjectArchitecture: telemetryNICData.ProjectArchitecture, + ClusterNodeCount: 1, + ClusterID: telemetryNICData.ClusterID, + ClusterVersion: telemetryNICData.ClusterVersion, + ClusterPlatform: "other", + } + + nicResourceCounts := telemetry.NICResourceCounts{ + VirtualServers: 0, + VirtualServerRoutes: 0, + TransportServers: 0, + Ingresses: 0, + IngressClasses: 1, + } + + td := telemetry.Data{ + telData, + nicResourceCounts, + } + + want := fmt.Sprintf("%+v", &td) + got := buf.String() + if !cmp.Equal(want, got) { + t.Error(cmp.Diff(want, got)) + } +} + func TestCountVirtualServersServices(t *testing.T) { t.Parallel() diff --git a/internal/telemetry/data.avdl b/internal/telemetry/data.avdl index e8ae13b0cb..b0c214e6bf 100644 --- a/internal/telemetry/data.avdl +++ b/internal/telemetry/data.avdl @@ -54,5 +54,8 @@ It is the UID of the `kube-system` Namespace. */ /** Ingresses is the number of Ingresses. */ long? Ingresses = null; + /** IngressClasses is the number of Ingress Classes. */ + long? IngressClasses = null; + } } diff --git a/internal/telemetry/exporter.go b/internal/telemetry/exporter.go index dba9a08233..97e44d440c 100644 --- a/internal/telemetry/exporter.go +++ b/internal/telemetry/exporter.go @@ -22,7 +22,10 @@ type StdoutExporter struct { // Export takes context and trace data and writes to the endpoint. func (e *StdoutExporter) Export(_ context.Context, data tel.Exportable) error { - fmt.Fprintf(e.Endpoint, "%+v", data) + _, err := fmt.Fprintf(e.Endpoint, "%+v", data) + if err != nil { + return err + } return nil } @@ -76,4 +79,6 @@ type NICResourceCounts struct { Services int64 // Ingresses is the number of Ingresses. Ingresses int64 + // IngressClasses is the number of Ingress Classes. + IngressClasses int64 } diff --git a/internal/telemetry/nicresourcecounts_attributes_generated.go b/internal/telemetry/nicresourcecounts_attributes_generated.go index bee3e82a0a..7961cc61f4 100644 --- a/internal/telemetry/nicresourcecounts_attributes_generated.go +++ b/internal/telemetry/nicresourcecounts_attributes_generated.go @@ -20,6 +20,7 @@ func (d *NICResourceCounts) Attributes() []attribute.KeyValue { attrs = append(attrs, attribute.Int64("Secrets", d.Secrets)) attrs = append(attrs, attribute.Int64("Services", d.Services)) attrs = append(attrs, attribute.Int64("Ingresses", d.Ingresses)) + attrs = append(attrs, attribute.Int64("IngressClasses", d.IngressClasses)) return attrs }