Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package cadvisor for openSUSE:Factory checked in at 2026-10-02 23:09:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/cadvisor (Old) and /work/SRC/openSUSE:Factory/.cadvisor.new.1631729 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "cadvisor" Fri Oct 2 23:09:09 2026 rev:9 rq:1382134 version:0.60.6 Changes: -------- --- /work/SRC/openSUSE:Factory/cadvisor/cadvisor.changes 2026-09-07 15:49:56.968346629 +0200 +++ /work/SRC/openSUSE:Factory/.cadvisor.new.1631729/cadvisor.changes 2026-10-02 23:09:11.549999343 +0200 @@ -1,0 +2,13 @@ +Fri Oct 2 14:23:56 UTC 2026 - Dirk Müller <[email protected]> + +- update to 0.60.6: + * workflows: mirror release tags to lib/ module tags + * fs/zfs: fall back to VFS when zfs stats are unavailable + * storage/stdout: nil-check Cpu, Network, and Memory stats + * Treat memory-only NUMA nodes as valid topology +- update vendor.tar to fix + * bsc#1283657, CVE-2026-63209: cadvisor: + github.com/klauspost/compress: signed integer overflow in + s2.NewDict() + +------------------------------------------------------------------- Old: ---- cadvisor-0.60.5.tar.gz New: ---- cadvisor-0.60.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ cadvisor.spec ++++++ --- /var/tmp/diff_new_pack.S58pzl/_old 2026-10-02 23:09:12.599043197 +0200 +++ /var/tmp/diff_new_pack.S58pzl/_new 2026-10-02 23:09:12.601043281 +0200 @@ -19,7 +19,7 @@ %global goipath github.com/google/cadvisor Name: cadvisor -Version: 0.60.5 +Version: 0.60.6 Release: 0 Summary: A Simple and Comprehensive Vulnerability Scanner for Containers License: Apache-2.0 @@ -32,7 +32,7 @@ BuildRequires: golang-packaging BuildRequires: systemd-rpm-macros BuildRequires: zstd -BuildRequires: golang(API) = 1.26 +BuildRequires: golang(API) = 1.27 Requires: ca-certificates Requires: git-core Requires: rpm ++++++ _service ++++++ --- /var/tmp/diff_new_pack.S58pzl/_old 2026-10-02 23:09:12.643045037 +0200 +++ /var/tmp/diff_new_pack.S58pzl/_new 2026-10-02 23:09:12.646045162 +0200 @@ -5,6 +5,7 @@ <param name="compression">zst</param> <param name="replace">go.opentelemetry.io/otel=go.opentelemetry.io/[email protected]</param> <param name="replace">google.golang.org/grpc=google.golang.org/[email protected]</param> + <param name="replace">github.com/klauspost/compress=github.com/klauspost/[email protected]</param> </service> <service name="set_version" mode="manual"> <param name="basename">cadvisor</param> ++++++ cadvisor-0.60.5.tar.gz -> cadvisor-0.60.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cadvisor-0.60.5/.github/workflows/tag-lib-module.yml new/cadvisor-0.60.6/.github/workflows/tag-lib-module.yml --- old/cadvisor-0.60.5/.github/workflows/tag-lib-module.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/cadvisor-0.60.6/.github/workflows/tag-lib-module.yml 2026-09-09 02:35:58.000000000 +0200 @@ -0,0 +1,44 @@ +name: Tag lib module + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + tag: + description: 'Existing v* tag to mirror as lib/<tag>' + required: true + type: string + +jobs: + tag-lib: + runs-on: ubuntu-latest + permissions: + contents: write # Needed for pushing the lib/<tag> tag + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} + + - name: Set TAG environment variable + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "TAG=${INPUTS_TAG}" >> "$GITHUB_ENV" + else + echo "TAG=${GITHUB_REF#refs/tags/}" >> "$GITHUB_ENV" + fi + env: + INPUTS_TAG: ${{ inputs.tag }} + + - name: Create and push lib/${{ env.TAG }} tag + run: | + # Release tags cut from branches that predate the lib module have nothing to mirror + if [ ! -f lib/go.mod ]; then + echo "No lib/go.mod at ${TAG}; skipping" + exit 0 + fi + git tag "lib/${TAG}" + # Pushing an identical existing tag is a no-op; a conflicting one fails the job + git push origin "lib/${TAG}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cadvisor-0.60.5/cmd/internal/storage/stdout/stdout.go new/cadvisor-0.60.6/cmd/internal/storage/stdout/stdout.go --- old/cadvisor-0.60.5/cmd/internal/storage/stdout/stdout.go 2026-07-11 07:07:30.000000000 +0200 +++ new/cadvisor-0.60.6/cmd/internal/storage/stdout/stdout.go 2026-09-09 02:35:58.000000000 +0200 @@ -109,31 +109,36 @@ // 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] + + // 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 per CPU + for i := 0; i < len(stats.Cpu.Usage.PerCpu); i++ { + series[serCPUUsagePerCPU+"."+strconv.Itoa(i)] = stats.Cpu.Usage.PerCpu[i] + } - // 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 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] + // 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) 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cadvisor-0.60.5/cmd/internal/storage/stdout/stdout_test.go new/cadvisor-0.60.6/cmd/internal/storage/stdout/stdout_test.go --- old/cadvisor-0.60.5/cmd/internal/storage/stdout/stdout_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/cadvisor-0.60.6/cmd/internal/storage/stdout/stdout_test.go 2026-09-09 02:35:58.000000000 +0200 @@ -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) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cadvisor-0.60.5/lib/fs/zfs/plugin.go new/cadvisor-0.60.6/lib/fs/zfs/plugin.go --- old/cadvisor-0.60.5/lib/fs/zfs/plugin.go 2026-07-11 07:07:30.000000000 +0200 +++ new/cadvisor-0.60.6/lib/fs/zfs/plugin.go 2026-09-09 02:35:58.000000000 +0200 @@ -24,6 +24,12 @@ type zfsPlugin struct{} +// Overridable for tests. +var ( + zfsDevicePath = "/dev/zfs" + getZfsStats = GetZfsStats +) + // NewPlugin creates a new ZFS filesystem plugin. func NewPlugin() fs.FsPlugin { return &zfsPlugin{} @@ -44,15 +50,19 @@ } // GetStats returns filesystem statistics for ZFS. +// +// When /dev/zfs is missing, or when zfs tooling cannot collect stats (common +// in unprivileged containers that still see the device node but lack +// permission to use it), fall back to VFS so mountpoint capacity remains +// available via statfs. func (p *zfsPlugin) GetStats(device string, partition fs.PartitionInfo) (*fs.FsStats, error) { - // Check if ZFS is available - if /dev/zfs doesn't exist, fall back to VFS - if _, err := os.Stat("/dev/zfs"); os.IsNotExist(err) { + if _, err := os.Stat(zfsDevicePath); err != nil { return nil, fs.ErrFallbackToVFS } - capacity, free, avail, err := GetZfsStats(device) + capacity, free, avail, err := getZfsStats(device) if err != nil { - return nil, err + return nil, fs.ErrFallbackToVFS } return &fs.FsStats{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cadvisor-0.60.5/lib/fs/zfs/plugin_test.go new/cadvisor-0.60.6/lib/fs/zfs/plugin_test.go --- old/cadvisor-0.60.5/lib/fs/zfs/plugin_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/cadvisor-0.60.6/lib/fs/zfs/plugin_test.go 2026-09-09 02:35:58.000000000 +0200 @@ -0,0 +1,90 @@ +// Copyright 2024 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 zfs + +import ( + "errors" + "os" + "path/filepath" + "testing" + + "github.com/google/cadvisor/lib/fs" +) + +func TestGetStatsFallsBackWhenDevMissing(t *testing.T) { + oldPath := zfsDevicePath + zfsDevicePath = filepath.Join(t.TempDir(), "missing-zfs") + defer func() { zfsDevicePath = oldPath }() + + _, err := NewPlugin().GetStats("tank/root", fs.PartitionInfo{Mountpoint: "/"}) + if !errors.Is(err, fs.ErrFallbackToVFS) { + t.Fatalf("GetStats() error = %v, want ErrFallbackToVFS", err) + } +} + +func TestGetStatsFallsBackOnZfsError(t *testing.T) { + // Simulate /dev/zfs present but unusable (e.g. EPERM in an unprivileged + // container): device node exists, yet zfs list fails. + dev := filepath.Join(t.TempDir(), "zfs") + if err := os.WriteFile(dev, nil, 0o644); err != nil { + t.Fatal(err) + } + + oldPath, oldFn := zfsDevicePath, getZfsStats + zfsDevicePath = dev + getZfsStats = func(string) (uint64, uint64, uint64, error) { + return 0, 0, 0, os.ErrPermission + } + defer func() { + zfsDevicePath = oldPath + getZfsStats = oldFn + }() + + _, err := NewPlugin().GetStats("tank/root", fs.PartitionInfo{Mountpoint: "/"}) + if !errors.Is(err, fs.ErrFallbackToVFS) { + t.Fatalf("GetStats() error = %v, want ErrFallbackToVFS", err) + } +} + +func TestGetStatsSuccess(t *testing.T) { + dev := filepath.Join(t.TempDir(), "zfs") + if err := os.WriteFile(dev, nil, 0o644); err != nil { + t.Fatal(err) + } + + oldPath, oldFn := zfsDevicePath, getZfsStats + zfsDevicePath = dev + getZfsStats = func(pool string) (uint64, uint64, uint64, error) { + if pool != "tank/root" { + t.Errorf("pool = %q, want tank/root", pool) + } + return 300, 100, 100, nil + } + defer func() { + zfsDevicePath = oldPath + getZfsStats = oldFn + }() + + stats, err := NewPlugin().GetStats("tank/root", fs.PartitionInfo{Mountpoint: "/"}) + if err != nil { + t.Fatalf("GetStats() unexpected error: %v", err) + } + if stats.Capacity != 300 || stats.Free != 100 || stats.Available != 100 { + t.Errorf("stats = %+v, want capacity=300 free=100 available=100", stats) + } + if stats.Type != fs.ZFS { + t.Errorf("Type = %q, want %q", stats.Type, fs.ZFS) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cadvisor-0.60.5/lib/utils/sysinfo/sysinfo.go new/cadvisor-0.60.6/lib/utils/sysinfo/sysinfo.go --- old/cadvisor-0.60.5/lib/utils/sysinfo/sysinfo.go 2026-07-11 07:07:30.000000000 +0200 +++ new/cadvisor-0.60.6/lib/utils/sysinfo/sysinfo.go 2026-09-09 02:35:58.000000000 +0200 @@ -223,8 +223,11 @@ node := info.Node{Id: id} cpuDirs, err := sysFs.GetCPUsPaths(nodeDir) + if err != nil { + return nil, 0, fmt.Errorf("failed to get CPUs for NUMA node %q: %w", nodeDir, err) + } if len(cpuDirs) == 0 { - klog.Warningf("Found node without any CPU, nodeDir: %s, number of cpuDirs %d, err: %v", nodeDir, len(cpuDirs), err) + klog.V(4).Infof("Found memory-only NUMA node, nodeDir: %s", nodeDir) } else { cores, err := getCoresInfo(sysFs, cpuDirs) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cadvisor-0.60.5/lib/utils/sysinfo/sysinfo_test.go new/cadvisor-0.60.6/lib/utils/sysinfo/sysinfo_test.go --- old/cadvisor-0.60.5/lib/utils/sysinfo/sysinfo_test.go 2026-07-11 07:07:30.000000000 +0200 +++ new/cadvisor-0.60.6/lib/utils/sysinfo/sysinfo_test.go 2026-09-09 02:35:58.000000000 +0200 @@ -16,6 +16,7 @@ import ( "encoding/json" + "errors" "fmt" "os" "sort" @@ -372,6 +373,58 @@ } } +func TestGetNodesInfoWithMemoryOnlyNode(t *testing.T) { + fakeSys := &fakesysfs.FakeSysFs{} + nodePath := "/fakeSysfs/devices/system/node/node1" + fakeSys.SetNodesPaths([]string{nodePath}, nil) + fakeSys.SetCPUsPaths(map[string][]string{nodePath: {}}, nil) + fakeSys.SetMemory("MemTotal: 1048576 kB", nil) + fakeSys.SetHugePages(nil, nil) + fakeSys.SetDistances(nodePath, "10", nil) + wantMemory := uint64(1024 * 1024 * 1024) + + nodes, cores, err := GetNodesInfo(fakeSys) + + if err != nil { + t.Fatalf("GetNodesInfo() returned unexpected error: %v", err) + } + if cores != 0 { + t.Errorf("GetNodesInfo() cores = %d, want 0", cores) + } + if len(nodes) != 1 { + t.Fatalf("GetNodesInfo() returned %d nodes, want 1", len(nodes)) + } + if nodes[0].Id != 1 { + t.Errorf("GetNodesInfo() node ID = %d, want 1", nodes[0].Id) + } + if len(nodes[0].Cores) != 0 { + t.Errorf("GetNodesInfo() memory-only node cores = %v, want none", nodes[0].Cores) + } + if nodes[0].Memory != wantMemory { + t.Errorf("GetNodesInfo() memory = %d, want %d", nodes[0].Memory, wantMemory) + } +} + +func TestGetNodesInfoReturnsCPUPathError(t *testing.T) { + fakeSys := &fakesysfs.FakeSysFs{} + nodePath := "/fakeSysfs/devices/system/node/node0" + wantErr := errors.New("cannot read CPU paths") + fakeSys.SetNodesPaths([]string{nodePath}, nil) + fakeSys.SetCPUsPaths(nil, wantErr) + + nodes, cores, err := GetNodesInfo(fakeSys) + + if !errors.Is(err, wantErr) { + t.Errorf("GetNodesInfo() error = %v, want wrapped %v", err, wantErr) + } + if nodes != nil { + t.Errorf("GetNodesInfo() nodes on error = %v, want nil", nodes) + } + if cores != 0 { + t.Errorf("GetNodesInfo() cores on error = %d, want 0", cores) + } +} + func TestGetNodesInfoWithOfflineCPUs(t *testing.T) { fakeSys := &fakesysfs.FakeSysFs{} c := sysfs.CacheInfo{ ++++++ vendor.tar.zst ++++++ ++++ 3366 lines of diff (skipped)
