diff --git a/cmd/nginx-ingress/main.go b/cmd/nginx-ingress/main.go index 1eb4b8c1bd..db38de9bd5 100644 --- a/cmd/nginx-ingress/main.go +++ b/cmd/nginx-ingress/main.go @@ -59,6 +59,7 @@ func main() { fmt.Printf("NGINX Ingress Controller Version=%v Commit=%v Date=%v DirtyState=%v Arch=%v/%v Go=%v\n", version, commitHash, commitTime, dirtyBuild, runtime.GOOS, runtime.GOARCH, runtime.Version()) parseFlags() + parsedFlags := os.Args[1:] config, kubeClient := createConfigAndKubeClient() @@ -217,6 +218,7 @@ func main() { TelemetryReportingEndpoint: telemetryEndpoint, NICVersion: version, DynamicWeightChangesReload: *enableDynamicWeightChangesReload, + InstallationFlags: parsedFlags, } lbc := k8s.NewLoadBalancerController(lbcInput) diff --git a/docs/content/overview/product-telemetry.md b/docs/content/overview/product-telemetry.md index c93e283a15..c461b127f9 100644 --- a/docs/content/overview/product-telemetry.md +++ b/docs/content/overview/product-telemetry.md @@ -49,6 +49,7 @@ These are the data points collected and reported by NGINX Ingress Controller: - **GlobalConfiguration** Represents the use of a GlobalConfiguration resource. - **AppProtectVersion** The AppProtect version - **IsPlus** Represents whether NGINX is Plus or OSS +- **InstallationFlags** List of command line arguments configured for NGINX Ingress Controller ## Opt out diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 43a0c4fb89..c6e2ab664a 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -218,6 +218,7 @@ type NewLoadBalancerControllerInput struct { TelemetryReportingEndpoint string NICVersion string DynamicWeightChangesReload bool + InstallationFlags []string } // NewLoadBalancerController creates a controller @@ -366,6 +367,7 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc K8sClientReader: input.KubeClient, Version: input.NICVersion, AppProtectVersion: input.AppProtectVersion, + InstallationFlags: input.InstallationFlags, GlobalConfiguration: lbc.watchGlobalConfiguration, Configurator: lbc.configurator, SecretStore: lbc.secretStore, diff --git a/internal/telemetry/cluster.go b/internal/telemetry/cluster.go index 4e3af5167f..191694a509 100644 --- a/internal/telemetry/cluster.go +++ b/internal/telemetry/cluster.go @@ -202,6 +202,11 @@ func (c *Collector) IsPlusEnabled() bool { return c.Config.IsPlus } +// InstallationFlags returns the list of all set flags +func (c *Collector) InstallationFlags() []string { + return c.Config.InstallationFlags +} + // 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 5ba851741a..fb88ba7004 100644 --- a/internal/telemetry/cluster_test.go +++ b/internal/telemetry/cluster_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/google/go-cmp/cmp" + "github.com/nginxinc/kubernetes-ingress/internal/telemetry" appsV1 "k8s.io/api/apps/v1" apiCoreV1 "k8s.io/api/core/v1" @@ -436,6 +438,27 @@ func TestInstallationIDFailsOnMissingDaemonSet(t *testing.T) { } } +func TestGetInstallationFlags(t *testing.T) { + t.Parallel() + + c, err := telemetry.NewCollector( + telemetry.CollectorConfig{ + InstallationFlags: []string{ + "-nginx-plus=true", + }, + }, + ) + if err != nil { + t.Fatal(err) + } + + got := c.InstallationFlags() + want := []string{"-nginx-plus=true"} + if !cmp.Equal(want, got) { + t.Error(cmp.Diff(want, got)) + } +} + // newTestCollectorForClusterWithNodes returns a telemetry collector configured // to simulate collecting data on a cluser with provided nodes. func newTestCollectorForClusterWithNodes(t *testing.T, nodes ...runtime.Object) *telemetry.Collector { diff --git a/internal/telemetry/collector.go b/internal/telemetry/collector.go index 7621d54bba..5fa5249e46 100644 --- a/internal/telemetry/collector.go +++ b/internal/telemetry/collector.go @@ -77,6 +77,9 @@ type CollectorConfig struct { // IsPlus represents whether NGINX is Plus or OSS IsPlus bool + + // InstallationFlags represents the list of set flags managed by NIC + InstallationFlags []string } // NewCollector takes 0 or more options and creates a new TraceReporter. @@ -141,6 +144,7 @@ func (c *Collector) Collect(ctx context.Context) { IngressAnnotations: report.IngressAnnotations, AppProtectVersion: report.AppProtectVersion, IsPlus: report.IsPlus, + InstallationFlags: report.InstallationFlags, }, } @@ -183,6 +187,7 @@ type Report struct { IngressAnnotations []string AppProtectVersion string IsPlus bool + InstallationFlags []string } // BuildReport takes context, collects telemetry data and builds the report. @@ -255,6 +260,8 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) { isPlus := c.IsPlusEnabled() + installationFlags := c.InstallationFlags() + return Report{ Name: "NIC", Version: c.Config.Version, @@ -284,5 +291,6 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) { IngressAnnotations: ingressAnnotations, AppProtectVersion: appProtectVersion, IsPlus: isPlus, + InstallationFlags: installationFlags, }, err } diff --git a/internal/telemetry/collector_test.go b/internal/telemetry/collector_test.go index 308fba89ab..69aea3c5ee 100644 --- a/internal/telemetry/collector_test.go +++ b/internal/telemetry/collector_test.go @@ -876,6 +876,103 @@ func TestCollectInvalidAppProtectVersion(t *testing.T) { } } +func TestCollectInstallationFlags(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + setFlags []string + wantFlags []string + }{ + { + name: "first flag", + setFlags: []string{ + "nginx-plus=true", + }, + wantFlags: []string{ + "nginx-plus=true", + }, + }, + { + name: "second flag", + setFlags: []string{ + "-v=3", + }, + wantFlags: []string{ + "-v=3", + }, + }, + { + name: "multiple flags", + setFlags: []string{ + "nginx-plus=true", + "-v=3", + }, + wantFlags: []string{ + "nginx-plus=true", + "-v=3", + }, + }, + { + name: "no flags", + setFlags: []string{}, + wantFlags: []string{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + buf := &bytes.Buffer{} + exp := &telemetry.StdoutExporter{Endpoint: buf} + + configurator := newConfigurator(t) + + cfg := telemetry.CollectorConfig{ + Configurator: configurator, + K8sClientReader: newTestClientset(node1, kubeNS), + Version: telemetryNICData.ProjectVersion, + InstallationFlags: tc.setFlags, + } + + 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, + InstallationFlags: tc.wantFlags, + } + + td := telemetry.Data{ + Data: telData, + NICResourceCounts: nicResourceCounts, + } + + want := fmt.Sprintf("%+v", &td) + + got := buf.String() + if !cmp.Equal(want, got) { + t.Error(cmp.Diff(got, want)) + } + }) + } +} + func TestCountVirtualServers(t *testing.T) { t.Parallel() diff --git a/internal/telemetry/data.avdl b/internal/telemetry/data.avdl index 23c17f4fa1..36c46b4484 100644 --- a/internal/telemetry/data.avdl +++ b/internal/telemetry/data.avdl @@ -93,5 +93,8 @@ It is the UID of the `kube-system` Namespace. */ /** IsPlus represents whether NGINX is Plus or OSS */ boolean? IsPlus = null; + /** InstallationFlags is the list of command line arguments configured for NGINX Ingress Controller */ + union {null, array} InstallationFlags = null; + } } diff --git a/internal/telemetry/exporter.go b/internal/telemetry/exporter.go index ce2dc11e3e..ccdf703664 100644 --- a/internal/telemetry/exporter.go +++ b/internal/telemetry/exporter.go @@ -105,4 +105,6 @@ type NICResourceCounts struct { AppProtectVersion string // IsPlus represents whether NGINX is Plus or OSS IsPlus bool + // InstallationFlags is the list of command line arguments configured for NGINX Ingress Controller + InstallationFlags []string } diff --git a/internal/telemetry/nicresourcecounts_attributes_generated.go b/internal/telemetry/nicresourcecounts_attributes_generated.go index f215145ad4..6d11eab24f 100644 --- a/internal/telemetry/nicresourcecounts_attributes_generated.go +++ b/internal/telemetry/nicresourcecounts_attributes_generated.go @@ -33,6 +33,7 @@ func (d *NICResourceCounts) Attributes() []attribute.KeyValue { attrs = append(attrs, attribute.StringSlice("IngressAnnotations", d.IngressAnnotations)) attrs = append(attrs, attribute.String("AppProtectVersion", d.AppProtectVersion)) attrs = append(attrs, attribute.Bool("IsPlus", d.IsPlus)) + attrs = append(attrs, attribute.StringSlice("InstallationFlags", d.InstallationFlags)) return attrs }