From 6d480e3f69a9bf27aff3e7434f9e31f6610378ec Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Mon, 20 Jul 2026 23:35:03 +0500 Subject: [PATCH 1/2] storage/stdout: nil-check Cpu, Network, and Memory stats ContainerStats fields for Cpu, Network, and Memory are pointers and are often unset for some containers. The stdout driver always dereferenced them, which panics as soon as Network is missing (reproduced with -storage_driver=stdout). Skip optional sections when the corresponding pointer is nil. Fixes #3911 Signed-off-by: Dean Chen <862469039@qq.com> --- cmd/internal/storage/stdout/stdout.go | 46 +++++++++------- cmd/internal/storage/stdout/stdout_test.go | 63 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 cmd/internal/storage/stdout/stdout_test.go 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) + } +} From c8376b58678c3da124805a5cfd9d114d06635d2c Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Tue, 21 Jul 2026 00:59:48 +0500 Subject: [PATCH 2/2] chore: empty commit to re-trigger Google CLA check Signed-off-by: Dean Chen <862469039@qq.com>