Skip to content

Commit

Permalink
Merge branch 'main' into chore/telem-endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
shaun-nx authored Apr 17, 2024
2 parents b65e973 + 4ff496a commit 3a9d5c3
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/content/configuration/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The Ingress Controller requires a service account which is configured using RBAC
We strongly recommend using the [RBAC configuration](https://github.com/nginxinc/kubernetes-ingress/blob/v3.5.0/deployments/rbac/rbac.yaml) provided in our standard deployment configuration. It is configured with the least amount of privilege required for the Ingress Controller to work.

We strongly recommend inspecting the RBAC configuration for [Manifests](https://github.com/nginxinc/kubernetes-ingress/blob/v3.5.0/deployments/rbac/rbac.yaml)
or for [Helm](https://github.com/nginxinc/kubernetes-ingress/blob/v3.5.0/charts/nginx-ingress/templates/rbac.yaml) to understand what access the Ingress Controller service account has and to which resources. For example, by default the service account has access to all Secret resources in the cluster.
or for [Helm](https://github.com/nginxinc/kubernetes-ingress/blob/v3.5.0/charts/nginx-ingress/templates/clusterrole.yaml) to understand what access the Ingress Controller service account has and to which resources. For example, by default the service account has access to all Secret resources in the cluster.

### Certificates and Privacy Keys

Expand Down
3 changes: 3 additions & 0 deletions docs/content/overview/product-telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ If you would prefer to avoid sending any telemetry data, you can [opt-out](#opt-
## Data Collected

These are the data points collected and reported by NGINX Ingress Controller:

- **Project Name** The name of the software, which will be labelled `NIC`.
- **Project Version** NGINX Ingress Controller version.
- **Project Architecture** The architecture of the kubernetes environment. (e.g. amd64, arm64, etc...)
Expand All @@ -33,6 +34,8 @@ 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.


## Opt out

Expand Down
13 changes: 13 additions & 0 deletions internal/telemetry/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ func (c *Collector) Secrets() (int, error) {
return len(c.Config.SecretStore.GetSecretReferenceMap()), nil
}

// IngressCount returns number of Ingresses in the namespaces watched by NIC.
func (c *Collector) IngressCount() int {
if c.Config.Configurator == nil {
return 0
}
ic := c.Config.Configurator.GetIngressCounts()
total := 0
for _, v := range ic {
total += v
}
return total
}

// lookupPlatform takes a string representing a K8s PlatformID
// retrieved from a cluster node and returns a string
// representing the platform name.
Expand Down
4 changes: 4 additions & 0 deletions internal/telemetry/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func (c *Collector) Collect(ctx context.Context) {
TransportServers: int64(report.TransportServers),
Replicas: int64(report.NICReplicaCount),
Secrets: int64(report.Secrets),
Ingresses: int64(report.IngressCount),
},
}

Expand Down Expand Up @@ -143,6 +144,7 @@ type Report struct {
VirtualServerRoutes int
TransportServers int
Secrets int
IngressCount int
}

// BuildReport takes context, collects telemetry data and builds the report.
Expand Down Expand Up @@ -190,6 +192,7 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) {
if err != nil {
glog.Errorf("Error collecting telemetry data: Secrets: %v", err)
}
ingressCount := c.IngressCount()

return Report{
Name: "NIC",
Expand All @@ -205,5 +208,6 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) {
VirtualServerRoutes: vsrCount,
TransportServers: tsCount,
Secrets: secrets,
IngressCount: ingressCount,
}, err
}
187 changes: 185 additions & 2 deletions internal/telemetry/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import (
"testing"
"time"

"github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets"

"github.com/nginxinc/kubernetes-ingress/internal/configs"
"github.com/nginxinc/kubernetes-ingress/internal/configs/version1"
"github.com/nginxinc/kubernetes-ingress/internal/configs/version2"
"github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets"
"github.com/nginxinc/kubernetes-ingress/internal/nginx"

"github.com/google/go-cmp/cmp"
"github.com/nginxinc/kubernetes-ingress/internal/telemetry"
conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1"
tel "github.com/nginxinc/telemetry-exporter/pkg/telemetry"
coreV1 "k8s.io/api/core/v1"
networkingV1 "k8s.io/api/networking/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -249,6 +251,101 @@ func TestCollectClusterVersion(t *testing.T) {
}
}

func TestIngressCountReportsNoDeployedIngresses(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,
}

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 TestIngressCountReportsNumberOfDeployedIngresses(t *testing.T) {
t.Parallel()

buf := &bytes.Buffer{}
exp := &telemetry.StdoutExporter{Endpoint: buf}

configurator := newConfiguratorWithIngress(t)

cfg := telemetry.CollectorConfig{
Configurator: configurator,
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: 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 TestCountVirtualServers(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -666,10 +763,95 @@ func TestCountSecretsAddTwoSecretsAndDeleteOne(t *testing.T) {
}
}

func createCafeIngressEx() configs.IngressEx {
cafeIngress := networkingV1.Ingress{
ObjectMeta: metaV1.ObjectMeta{
Name: "cafe-ingress",
Namespace: "default",
Annotations: map[string]string{
"kubernetes.io/ingress.class": "nginx",
},
},
Spec: networkingV1.IngressSpec{
TLS: []networkingV1.IngressTLS{
{
Hosts: []string{"cafe.example.com"},
SecretName: "cafe-secret",
},
},
Rules: []networkingV1.IngressRule{
{
Host: "cafe.example.com",
IngressRuleValue: networkingV1.IngressRuleValue{
HTTP: &networkingV1.HTTPIngressRuleValue{
Paths: []networkingV1.HTTPIngressPath{
{
Path: "/coffee",
Backend: networkingV1.IngressBackend{
Service: &networkingV1.IngressServiceBackend{
Name: "coffee-svc",
Port: networkingV1.ServiceBackendPort{
Number: 80,
},
},
},
},
{
Path: "/tea",
Backend: networkingV1.IngressBackend{
Service: &networkingV1.IngressServiceBackend{
Name: "tea-svc",
Port: networkingV1.ServiceBackendPort{
Number: 80,
},
},
},
},
},
},
},
},
},
},
}
cafeIngressEx := configs.IngressEx{
Ingress: &cafeIngress,
Endpoints: map[string][]string{
"coffee-svc80": {"10.0.0.1:80"},
"tea-svc80": {"10.0.0.2:80"},
},
ExternalNameSvcs: map[string]bool{},
ValidHosts: map[string]bool{
"cafe.example.com": true,
},
SecretRefs: map[string]*secrets.SecretReference{
"cafe-secret": {
Secret: &coreV1.Secret{
Type: coreV1.SecretTypeTLS,
},
Path: "/etc/nginx/secrets/default-cafe-secret",
},
},
}
return cafeIngressEx
}

func getResourceKey(namespace, name string) string {
return fmt.Sprintf("%s_%s", namespace, name)
}

func newConfiguratorWithIngress(t *testing.T) *configs.Configurator {
t.Helper()

ingressEx := createCafeIngressEx()
c := newConfigurator(t)
_, err := c.AddOrUpdateIngress(&ingressEx)
if err != nil {
t.Fatal(err)
}
return c
}

func newConfigurator(t *testing.T) *configs.Configurator {
t.Helper()

Expand Down Expand Up @@ -705,6 +887,7 @@ func newConfigurator(t *testing.T) *configs.Configurator {
IsPrometheusEnabled: false,
IsLatencyMetricsEnabled: false,
})

return cnf
}

Expand Down
3 changes: 3 additions & 0 deletions internal/telemetry/data.avdl
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,8 @@ It is the UID of the `kube-system` Namespace. */
/** Secrets is the number of Secret resources managed by the Ingress Controller. */
long? Secrets = null;

/** Ingresses is the number of Ingresses. */
long? Ingresses = null;

}
}
2 changes: 2 additions & 0 deletions internal/telemetry/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,6 @@ type NICResourceCounts struct {
Replicas int64
// Secrets is the number of Secret resources managed by the Ingress Controller.
Secrets int64
// Ingresses is the number of Ingresses.
Ingresses int64
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func (d *NICResourceCounts) Attributes() []attribute.KeyValue {
attrs = append(attrs, attribute.Int64("TransportServers", d.TransportServers))
attrs = append(attrs, attribute.Int64("Replicas", d.Replicas))
attrs = append(attrs, attribute.Int64("Secrets", d.Secrets))
attrs = append(attrs, attribute.Int64("Ingresses", d.Ingresses))

return attrs
}
Expand Down

0 comments on commit 3a9d5c3

Please sign in to comment.