diff --git a/build/Dockerfile b/build/Dockerfile index 0473c43d3d..8ce48e499c 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -546,6 +546,8 @@ ARG IC_VERSION ARG TARGETPLATFORM ARG NAP_MODULES=none +ENV BUILD_OS=${BUILD_OS} + RUN --mount=type=bind,target=/tmp \ --mount=type=bind,from=nginx-files,src=common.sh,target=/usr/local/bin/common.sh \ --mount=type=bind,from=nginx-files,src=patch-os.sh,target=/usr/local/bin/patch-os.sh \ diff --git a/cmd/nginx-ingress/main.go b/cmd/nginx-ingress/main.go index f646199045..a9b7036000 100644 --- a/cmd/nginx-ingress/main.go +++ b/cmd/nginx-ingress/main.go @@ -64,6 +64,8 @@ func main() { parseFlags() parsedFlags := os.Args[1:] + buildOS := os.Getenv("BUILD_OS") + config, kubeClient := createConfigAndKubeClient() kubernetesVersionInfo(kubeClient) @@ -230,6 +232,7 @@ func main() { WatchNamespaceLabel: *watchNamespaceLabel, EnableTelemetryReporting: *enableTelemetryReporting, TelemetryReportingEndpoint: telemetryEndpoint, + BuildOS: buildOS, NICVersion: version, DynamicWeightChangesReload: *enableDynamicWeightChangesReload, InstallationFlags: parsedFlags, diff --git a/docs/content/overview/product-telemetry.md b/docs/content/overview/product-telemetry.md index 39c92802c2..bd4cc57b77 100644 --- a/docs/content/overview/product-telemetry.md +++ b/docs/content/overview/product-telemetry.md @@ -57,6 +57,7 @@ These are the data points collected and reported by NGINX Ingress Controller: - **AppProtectVersion** The AppProtect version - **IsPlus** Represents whether NGINX is Plus or OSS - **InstallationFlags** List of command line arguments configured for NGINX Ingress Controller +- **BuildOS** The base operating system image in which NGINX Ingress Controller is running on. --- diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 6968dfb9e5..fa20ad1979 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -216,6 +216,7 @@ type NewLoadBalancerControllerInput struct { WatchNamespaceLabel string EnableTelemetryReporting bool TelemetryReportingEndpoint string + BuildOS string NICVersion string DynamicWeightChangesReload bool InstallationFlags []string @@ -371,6 +372,7 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc K8sClientReader: input.KubeClient, Version: input.NICVersion, AppProtectVersion: input.AppProtectVersion, + BuildOS: input.BuildOS, InstallationFlags: input.InstallationFlags, GlobalConfiguration: lbc.watchGlobalConfiguration, Configurator: lbc.configurator, diff --git a/internal/telemetry/cluster.go b/internal/telemetry/cluster.go index f44bb45375..0afbd9be10 100644 --- a/internal/telemetry/cluster.go +++ b/internal/telemetry/cluster.go @@ -231,6 +231,11 @@ func (c *Collector) ServiceCounts() (map[string]int, error) { return serviceCounts, nil } +// BuildOS returns a string which is the base operating system image tha NIC is running in. +func (c *Collector) BuildOS() string { + return c.Config.BuildOS +} + // 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/collector.go b/internal/telemetry/collector.go index 4c480670bf..1a86983429 100644 --- a/internal/telemetry/collector.go +++ b/internal/telemetry/collector.go @@ -75,6 +75,9 @@ type CollectorConfig struct { // AppProtectVersion represents the version of App Protect. AppProtectVersion string + // BuildOS represents the base operating system image + BuildOS string + // IsPlus represents whether NGINX is Plus or OSS IsPlus bool @@ -154,6 +157,7 @@ func (c *Collector) Collect(ctx context.Context) { AppProtectVersion: report.AppProtectVersion, IsPlus: report.IsPlus, InstallationFlags: report.InstallationFlags, + BuildOS: report.BuildOS, }, } @@ -203,6 +207,7 @@ type Report struct { AppProtectVersion string IsPlus bool InstallationFlags []string + BuildOS string } // BuildReport takes context, collects telemetry data and builds the report. @@ -334,5 +339,6 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) { AppProtectVersion: appProtectVersion, IsPlus: isPlus, InstallationFlags: installationFlags, + BuildOS: c.BuildOS(), }, err } diff --git a/internal/telemetry/collector_test.go b/internal/telemetry/collector_test.go index 992265a72e..7a29d87b83 100644 --- a/internal/telemetry/collector_test.go +++ b/internal/telemetry/collector_test.go @@ -1129,6 +1129,65 @@ func TestCollectInstallationFlags(t *testing.T) { } } +func TestCollectBuildOS(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + buildOS string + wantOS string + }{ + { + name: "debian plus image", + buildOS: "debian-plus", + wantOS: "debian-plus", + }, + { + name: "ubi-9 plus app protect image", + buildOS: "ubi-9-plus-nap", + wantOS: "ubi-9-plus-nap", + }, + { + name: "alpine oss image", + buildOS: "alpine", + wantOS: "alpine", + }, + { + name: "self built image", + buildOS: "", + wantOS: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + buf := &bytes.Buffer{} + exp := &telemetry.StdoutExporter{Endpoint: buf} + + configurator := newConfiguratorWithIngress(t) + + cfg := telemetry.CollectorConfig{ + Configurator: configurator, + K8sClientReader: newTestClientset(node1, kubeNS), + Version: telemetryNICData.ProjectVersion, + BuildOS: tc.buildOS, + } + + c, err := telemetry.NewCollector(cfg, telemetry.WithExporter(exp)) + if err != nil { + t.Fatal(err) + } + c.Collect(context.Background()) + + buildOS := c.BuildOS() + + if tc.wantOS != buildOS { + t.Errorf("want: %s, got: %s", tc.wantOS, buildOS) + } + }) + } +} + func TestCountVirtualServersOnCustomResourceEnabled(t *testing.T) { t.Parallel() diff --git a/internal/telemetry/data.avdl b/internal/telemetry/data.avdl index f37eb01948..bf244800f5 100644 --- a/internal/telemetry/data.avdl +++ b/internal/telemetry/data.avdl @@ -1,5 +1,4 @@ @namespace("ingress.nginx.com") protocol NICProductTelemetry { - /** Data is the product telemetry data of NGINX Ingress Controller. */ @df_datatype("nic-product-telemetry") record Data { /** The field that identifies what type of data this is. */ string dataType; @@ -115,5 +114,8 @@ It is the UID of the `kube-system` Namespace. */ /** InstallationFlags is the list of command line arguments configured for NGINX Ingress Controller */ union {null, array} InstallationFlags = null; + /** BuildOS returns the base buildOS image */ + string? BuildOS = null; + } } diff --git a/internal/telemetry/exporter.go b/internal/telemetry/exporter.go index aac38756c5..109e453ffe 100644 --- a/internal/telemetry/exporter.go +++ b/internal/telemetry/exporter.go @@ -119,4 +119,6 @@ type NICResourceCounts struct { IsPlus bool // InstallationFlags is the list of command line arguments configured for NGINX Ingress Controller InstallationFlags []string + // BuildOS represents the base operating system image + BuildOS string } diff --git a/internal/telemetry/nicresourcecounts_attributes_generated.go b/internal/telemetry/nicresourcecounts_attributes_generated.go index f68e171f7c..ca8939a20d 100644 --- a/internal/telemetry/nicresourcecounts_attributes_generated.go +++ b/internal/telemetry/nicresourcecounts_attributes_generated.go @@ -40,6 +40,7 @@ func (d *NICResourceCounts) Attributes() []attribute.KeyValue { attrs = append(attrs, attribute.String("AppProtectVersion", d.AppProtectVersion)) attrs = append(attrs, attribute.Bool("IsPlus", d.IsPlus)) attrs = append(attrs, attribute.StringSlice("InstallationFlags", d.InstallationFlags)) + attrs = append(attrs, attribute.String("BuildOS", d.BuildOS)) return attrs }