diff --git a/cmd/internal/storage/stdout/stdout.go b/cmd/internal/storage/stdout/stdout.go index c185f21b55..95d2b723a0 100644 --- a/cmd/internal/storage/stdout/stdout.go +++ b/cmd/internal/storage/stdout/stdout.go @@ -109,31 +109,36 @@ func (driver *stdoutStorage) containerStatsToValues(stats *info.ContainerStats) // Unix Timestamp series[serTimestamp] = uint64(time.Now().UnixNano()) - // Total usage in nanoseconds - series[serCPUUsageTotal] = stats.Cpu.Usage.Total + // Cpu/Network became pointers; some containers omit one or both. + if stats.Cpu != nil { + // Total usage in nanoseconds + series[serCPUUsageTotal] = stats.Cpu.Usage.Total - // To be deprecated in 0.39 - series[colCPUCumulativeUsage] = series[serCPUUsageTotal] + // To be deprecated in 0.39 + series[colCPUCumulativeUsage] = series[serCPUUsageTotal] - // CPU usage: Time spend in system space (in nanoseconds) - series[serCPUUsageSystem] = stats.Cpu.Usage.System + // CPU usage: Time spend in system space (in nanoseconds) + series[serCPUUsageSystem] = stats.Cpu.Usage.System - // CPU usage: Time spent in user space (in nanoseconds) - series[serCPUUsageUser] = stats.Cpu.Usage.User + // CPU usage: Time spent in user space (in nanoseconds) + series[serCPUUsageUser] = stats.Cpu.Usage.User - // CPU usage per CPU - for i := 0; i < len(stats.Cpu.Usage.PerCpu); i++ { - series[serCPUUsagePerCPU+"."+strconv.Itoa(i)] = stats.Cpu.Usage.PerCpu[i] - } + // CPU usage per CPU + for i := 0; i < len(stats.Cpu.Usage.PerCpu); i++ { + series[serCPUUsagePerCPU+"."+strconv.Itoa(i)] = stats.Cpu.Usage.PerCpu[i] + } - // Load Average - series[serLoadAverage] = uint64(stats.Cpu.LoadAverage) + // Load Average + series[serLoadAverage] = uint64(stats.Cpu.LoadAverage) + } - // Network stats. - series[serRxBytes] = stats.Network.RxBytes - series[serRxErrors] = stats.Network.RxErrors - series[serTxBytes] = stats.Network.TxBytes - series[serTxErrors] = stats.Network.TxErrors + if stats.Network != nil { + // Network stats. + series[serRxBytes] = stats.Network.RxBytes + series[serRxErrors] = stats.Network.RxErrors + series[serTxBytes] = stats.Network.TxBytes + series[serTxErrors] = stats.Network.TxErrors + } // Referenced Memory series[serReferencedMemory] = stats.ReferencedMemory @@ -154,6 +159,9 @@ func (driver *stdoutStorage) containerFsStatsToValues(series *map[string]uint64, } func (driver *stdoutStorage) memoryStatsToValues(series *map[string]uint64, stats *info.ContainerStats) { + if stats.Memory == nil { + return + } // Memory Usage (*series)[serMemoryUsage] = stats.Memory.Usage // Maximum memory usage recorded diff --git a/cmd/internal/storage/stdout/stdout_test.go b/cmd/internal/storage/stdout/stdout_test.go new file mode 100644 index 0000000000..d276d14a81 --- /dev/null +++ b/cmd/internal/storage/stdout/stdout_test.go @@ -0,0 +1,63 @@ +// Copyright 2026 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stdout + +import ( + "testing" + "time" + + info "github.com/google/cadvisor/info/v1" +) + +func TestAddStatsNilOptionalFields(t *testing.T) { + driver, err := newStorage("testhost") + if err != nil { + t.Fatal(err) + } + + cInfo := &info.ContainerInfo{ + ContainerReference: info.ContainerReference{ + Name: "/test", + }, + } + // Cpu present, Network/Memory omitted — used to panic at stats.Network.RxBytes. + stats := &info.ContainerStats{ + Timestamp: time.Now(), + Cpu: &info.CpuStats{ + Usage: info.CpuUsage{ + Total: 1000, + User: 600, + System: 400, + }, + }, + } + + if err := driver.AddStats(cInfo, stats); err != nil { + t.Fatalf("AddStats: %v", err) + } +} + +func TestAddStatsNilStats(t *testing.T) { + driver, err := newStorage("testhost") + if err != nil { + t.Fatal(err) + } + cInfo := &info.ContainerInfo{ + ContainerReference: info.ContainerReference{Name: "/test"}, + } + if err := driver.AddStats(cInfo, nil); err != nil { + t.Fatalf("AddStats(nil): %v", err) + } +}