diff --git a/cmd/gateway/commands.go b/cmd/gateway/commands.go index 4cb67bf3ec..b8c23b1ec8 100644 --- a/cmd/gateway/commands.go +++ b/cmd/gateway/commands.go @@ -209,6 +209,11 @@ func createControllerCommand() *cobra.Command { imageSource = "unknown" } + buildOs := os.Getenv("BUILD_OS") + if buildOs == "" { + buildOs = "alpine" + } + period, err := time.ParseDuration(telemetryReportPeriod) if err != nil { return fmt.Errorf("error parsing telemetry report period: %w", err) @@ -271,6 +276,7 @@ func createControllerCommand() *cobra.Command { Plus: plus, ExperimentalFeatures: gwExperimentalFeatures, ImageSource: imageSource, + BuildOS: buildOs, Flags: config.Flags{ Names: flagKeys, Values: flagValues, diff --git a/internal/controller/config/config.go b/internal/controller/config/config.go index e23f73ca59..5105acac18 100644 --- a/internal/controller/config/config.go +++ b/internal/controller/config/config.go @@ -26,6 +26,8 @@ type Config struct { GatewayClassName string // ImageSource is the source of the NGINX Gateway image. ImageSource string + // BuildOS is the OS the NGF and NGINX binary was built on. + BuildOS string // GatewayCtlrName is the name of this controller. GatewayCtlrName string // UsageReportConfig specifies the NGINX Plus usage reporting configuration. diff --git a/internal/controller/manager.go b/internal/controller/manager.go index a4e9fd9cf0..a4b1bc1d64 100644 --- a/internal/controller/manager.go +++ b/internal/controller/manager.go @@ -290,6 +290,7 @@ func StartManager(cfg config.Config) error { Name: cfg.GatewayPodConfig.Name, }, ImageSource: cfg.ImageSource, + BuildOS: cfg.BuildOS, Flags: cfg.Flags, NginxOneConsoleConnection: cfg.NginxOneConsoleTelemetryConfig.DataplaneKeySecretName != "", }) diff --git a/internal/controller/telemetry/collector.go b/internal/controller/telemetry/collector.go index e06da3f0b8..4fd4ca45e2 100644 --- a/internal/controller/telemetry/collector.go +++ b/internal/controller/telemetry/collector.go @@ -66,6 +66,8 @@ type Data struct { ControlPlanePodCount int64 // NginxOneConnectionEnabled is a boolean that indicates whether the connection to the Nginx One Console is enabled. NginxOneConnectionEnabled bool + // BuildOS is the OS the NGF and NGINX binary was built on. + BuildOS string } // NGFResourceCounts stores the counts of all relevant resources that NGF processes and generates configuration from. @@ -121,6 +123,8 @@ type DataCollectorConfig struct { Version string // ImageSource is the source of the NGF image. ImageSource string + // BuildOS is the OS the NGF and NGINX binary was built on. + BuildOS string // Flags contains the command-line NGF flag keys and values. Flags config.Flags // NginxOneConsoleConnection is a boolean that indicates whether the connection to the Nginx One Console is enabled. @@ -174,6 +178,11 @@ func (c DataCollectorImpl) Collect(ctx context.Context) (Data, error) { nginxPodCount := getNginxPodCount(g, clusterInfo.NodeCount) + buildOS := c.cfg.BuildOS + if buildOS == "" { + buildOS = "alpine" + } + data := Data{ Data: tel.Data{ ProjectName: "NGF", @@ -187,6 +196,7 @@ func (c DataCollectorImpl) Collect(ctx context.Context) (Data, error) { }, NGFResourceCounts: graphResourceCount, ImageSource: c.cfg.ImageSource, + BuildOS: buildOS, FlagNames: c.cfg.Flags.Names, FlagValues: c.cfg.Flags.Values, SnippetsFiltersDirectives: snippetsFiltersDirectives, diff --git a/internal/controller/telemetry/collector_test.go b/internal/controller/telemetry/collector_test.go index 8c749fdfbe..eef847bef0 100644 --- a/internal/controller/telemetry/collector_test.go +++ b/internal/controller/telemetry/collector_test.go @@ -72,6 +72,64 @@ func createGetCallsFunc(objects ...client.Object) getCallsFunc { } var _ = Describe("Collector", Ordered, func() { + + Describe("BuildOS field", func() { + var ( + k8sClientReader *kubernetesfakes.FakeReader + fakeGraphGetter *telemetryfakes.FakeGraphGetter + fakeConfigurationGetter *telemetryfakes.FakeConfigurationGetter + version string + podNSName types.NamespacedName + flags config.Flags + ) + + BeforeEach(func() { + version = "1.1" + k8sClientReader = &kubernetesfakes.FakeReader{} + fakeGraphGetter = &telemetryfakes.FakeGraphGetter{} + fakeConfigurationGetter = &telemetryfakes.FakeConfigurationGetter{} + podNSName = types.NamespacedName{Namespace: "nginx-gateway", Name: "ngf-pod"} + flags = config.Flags{} + fakeGraphGetter.GetLatestGraphReturns(&graph.Graph{}) + fakeConfigurationGetter.GetLatestConfigurationReturns(nil) + }) + + It("sets BuildOS to 'alpine' when config.BuildOS is empty", func(ctx SpecContext) { + dataCollector := telemetry.NewDataCollectorImpl(telemetry.DataCollectorConfig{ + K8sClientReader: k8sClientReader, + GraphGetter: fakeGraphGetter, + ConfigurationGetter: fakeConfigurationGetter, + Version: version, + PodNSName: podNSName, + ImageSource: "local", + Flags: flags, + NginxOneConsoleConnection: true, + BuildOS: "", + }) + + data, err := dataCollector.Collect(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(data.BuildOS).To(Equal("alpine")) + }) + + It("sets BuildOS to 'ubi' when config.BuildOS is 'ubi'", func(ctx SpecContext) { + dataCollector := telemetry.NewDataCollectorImpl(telemetry.DataCollectorConfig{ + K8sClientReader: k8sClientReader, + GraphGetter: fakeGraphGetter, + ConfigurationGetter: fakeConfigurationGetter, + Version: version, + PodNSName: podNSName, + ImageSource: "local", + Flags: flags, + NginxOneConsoleConnection: true, + BuildOS: "ubi", + }) + + data, err := dataCollector.Collect(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(data.BuildOS).To(Equal("ubi")) + }) + }) var ( k8sClientReader *kubernetesfakes.FakeReader fakeGraphGetter *telemetryfakes.FakeGraphGetter @@ -195,6 +253,7 @@ var _ = Describe("Collector", Ordered, func() { ImageSource: "local", Flags: flags, NginxOneConsoleConnection: true, + BuildOS: "", }) baseGetCalls = createGetCallsFunc(ngfPod, ngfReplicaSet, kubeNamespace) diff --git a/internal/controller/telemetry/data.avdl b/internal/controller/telemetry/data.avdl index c19881315a..50a3b35cb8 100644 --- a/internal/controller/telemetry/data.avdl +++ b/internal/controller/telemetry/data.avdl @@ -114,5 +114,8 @@ attached at the Gateway level. */ /** NginxOneConnectionEnabled is a boolean that indicates whether the connection to the Nginx One Console is enabled. */ boolean? NginxOneConnectionEnabled = null; + /** BuildOS is the OS the NGF and NGINX binary was built on. */ + string? BuildOS = null; + } } diff --git a/internal/controller/telemetry/data_attributes_generated.go b/internal/controller/telemetry/data_attributes_generated.go index 3b8b3dcf3f..3423c902c9 100644 --- a/internal/controller/telemetry/data_attributes_generated.go +++ b/internal/controller/telemetry/data_attributes_generated.go @@ -23,6 +23,7 @@ func (d *Data) Attributes() []attribute.KeyValue { attrs = append(attrs, attribute.Int64("NginxPodCount", d.NginxPodCount)) attrs = append(attrs, attribute.Int64("ControlPlanePodCount", d.ControlPlanePodCount)) attrs = append(attrs, attribute.Bool("NginxOneConnectionEnabled", d.NginxOneConnectionEnabled)) + attrs = append(attrs, attribute.String("BuildOS", d.BuildOS)) return attrs } diff --git a/tests/suite/telemetry_test.go b/tests/suite/telemetry_test.go index 2ad0c0b3a0..bc57c1ca1e 100644 --- a/tests/suite/telemetry_test.go +++ b/tests/suite/telemetry_test.go @@ -96,6 +96,7 @@ var _ = Describe("Telemetry test with OTel collector", Label("telemetry"), func( "NginxPodCount: Int(0)", "ControlPlanePodCount: Int(1)", "NginxOneConnectionEnabled: Bool(false)", + "BuildOS:", }, ) })