From b9c5b543237716f29d00bb65a70823989ae4ddcb Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 13 Aug 2025 22:13:55 -0700 Subject: [PATCH 1/5] Bump go to 1.24 Bump go to 1.24 in go.mod. Remove go 1.23 and add go 1.25 to CI. Signed-off-by: Kir Kolyshkin --- .github/workflows/test.yml | 2 +- .github/workflows/validate.yml | 2 +- go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4faed60..62fc030 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.23.x, 1.24.x] + go-version: [1.24.x, 1.25.x] race: ["-race", ""] runs-on: ubuntu-24.04 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index e5ed8b7..1cf7c01 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -11,7 +11,7 @@ on: # Runs at 00:00 UTC every Monday - cron: '0 0 * * 1' env: - GO_VERSION: 1.24 + GO_VERSION: 1.25 permissions: contents: read diff --git a/go.mod b/go.mod index 946822e..6a4e266 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/opencontainers/cgroups -go 1.23.0 +go 1.24.0 require ( github.com/cilium/ebpf v0.17.3 From 9c9b70065319bcff73bea5bde94eccc3c50f8858 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 13 Aug 2025 22:37:11 -0700 Subject: [PATCH 2/5] stats.go: replace omitempty with omitzero For some fields (like nested struct fields), omitempty has no effect but omitzero does. Let's use it. NOTE it might result in some compatibility issues since now zero-only fields are not serialized. Since linter-extra thinks it's a new code, add some naming exceptions. Signed-off-by: Kir Kolyshkin --- .golangci-extra.yml | 4 ++ stats.go | 138 ++++++++++++++++++++++---------------------- 2 files changed, 73 insertions(+), 69 deletions(-) diff --git a/.golangci-extra.yml b/.golangci-extra.yml index b98dba1..42a0bc9 100644 --- a/.golangci-extra.yml +++ b/.golangci-extra.yml @@ -19,3 +19,7 @@ linters: - -QF1008 # https://staticcheck.dev/docs/checks/#QF1008 Omit embedded fields from selector expression. exclusions: generated: strict + rules: + # Legacy names we can't change without breaking compatibility. + - path: stats.go + text: "(type|struct field) (CpuUsage|CpuStats) should be " diff --git a/stats.go b/stats.go index 0170133..debc2df 100644 --- a/stats.go +++ b/stats.go @@ -2,19 +2,19 @@ package cgroups type ThrottlingData struct { // Number of periods with throttling active - Periods uint64 `json:"periods,omitempty"` + Periods uint64 `json:"periods,omitzero"` // Number of periods when the container hit its throttling limit. - ThrottledPeriods uint64 `json:"throttled_periods,omitempty"` + ThrottledPeriods uint64 `json:"throttled_periods,omitzero"` // Aggregate time the container was throttled for in nanoseconds. - ThrottledTime uint64 `json:"throttled_time,omitempty"` + ThrottledTime uint64 `json:"throttled_time,omitzero"` } type BurstData struct { // Number of periods bandwidth burst occurs - BurstsPeriods uint64 `json:"bursts_periods,omitempty"` + BurstsPeriods uint64 `json:"bursts_periods,omitzero"` // Cumulative wall-time that any cpus has used above quota in respective periods // Units: nanoseconds. - BurstTime uint64 `json:"burst_time,omitempty"` + BurstTime uint64 `json:"burst_time,omitzero"` } // CpuUsage denotes the usage of a CPU. @@ -22,10 +22,10 @@ type BurstData struct { type CpuUsage struct { // Total CPU time consumed. // Units: nanoseconds. - TotalUsage uint64 `json:"total_usage,omitempty"` + TotalUsage uint64 `json:"total_usage,omitzero"` // Total CPU time consumed per core. // Units: nanoseconds. - PercpuUsage []uint64 `json:"percpu_usage,omitempty"` + PercpuUsage []uint64 `json:"percpu_usage,omitzero"` // CPU time consumed per core in kernel mode // Units: nanoseconds. PercpuUsageInKernelmode []uint64 `json:"percpu_usage_in_kernelmode"` @@ -48,26 +48,26 @@ type PSIData struct { } type PSIStats struct { - Some PSIData `json:"some,omitempty"` - Full PSIData `json:"full,omitempty"` + Some PSIData `json:"some,omitzero"` + Full PSIData `json:"full,omitzero"` } type CpuStats struct { - CpuUsage CpuUsage `json:"cpu_usage,omitempty"` - ThrottlingData ThrottlingData `json:"throttling_data,omitempty"` - PSI *PSIStats `json:"psi,omitempty"` - BurstData BurstData `json:"burst_data,omitempty"` + CpuUsage CpuUsage `json:"cpu_usage,omitzero"` + ThrottlingData ThrottlingData `json:"throttling_data,omitzero"` + PSI *PSIStats `json:"psi,omitzero"` + BurstData BurstData `json:"burst_data,omitzero"` } type CPUSetStats struct { // List of the physical numbers of the CPUs on which processes // in that cpuset are allowed to execute - CPUs []uint16 `json:"cpus,omitempty"` + CPUs []uint16 `json:"cpus,omitzero"` // cpu_exclusive flag CPUExclusive uint64 `json:"cpu_exclusive"` // List of memory nodes on which processes in that cpuset // are allowed to allocate memory - Mems []uint16 `json:"mems,omitempty"` + Mems []uint16 `json:"mems,omitzero"` // mem_hardwall flag MemHardwall uint64 `json:"mem_hardwall"` // mem_exclusive flag @@ -87,122 +87,122 @@ type CPUSetStats struct { } type MemoryData struct { - Usage uint64 `json:"usage,omitempty"` - MaxUsage uint64 `json:"max_usage,omitempty"` + Usage uint64 `json:"usage,omitzero"` + MaxUsage uint64 `json:"max_usage,omitzero"` Failcnt uint64 `json:"failcnt"` Limit uint64 `json:"limit"` } type MemoryStats struct { // memory used for cache - Cache uint64 `json:"cache,omitempty"` + Cache uint64 `json:"cache,omitzero"` // usage of memory - Usage MemoryData `json:"usage,omitempty"` + Usage MemoryData `json:"usage,omitzero"` // usage of memory + swap - SwapUsage MemoryData `json:"swap_usage,omitempty"` + SwapUsage MemoryData `json:"swap_usage,omitzero"` // usage of swap only - SwapOnlyUsage MemoryData `json:"swap_only_usage,omitempty"` + SwapOnlyUsage MemoryData `json:"swap_only_usage,omitzero"` // usage of kernel memory - KernelUsage MemoryData `json:"kernel_usage,omitempty"` + KernelUsage MemoryData `json:"kernel_usage,omitzero"` // usage of kernel TCP memory - KernelTCPUsage MemoryData `json:"kernel_tcp_usage,omitempty"` + KernelTCPUsage MemoryData `json:"kernel_tcp_usage,omitzero"` // usage of memory pages by NUMA node // see chapter 5.6 of memory controller documentation - PageUsageByNUMA PageUsageByNUMA `json:"page_usage_by_numa,omitempty"` + PageUsageByNUMA PageUsageByNUMA `json:"page_usage_by_numa,omitzero"` // if true, memory usage is accounted for throughout a hierarchy of cgroups. UseHierarchy bool `json:"use_hierarchy"` - Stats map[string]uint64 `json:"stats,omitempty"` - PSI *PSIStats `json:"psi,omitempty"` + Stats map[string]uint64 `json:"stats,omitzero"` + PSI *PSIStats `json:"psi,omitzero"` } type PageUsageByNUMA struct { // Embedding is used as types can't be recursive. PageUsageByNUMAInner - Hierarchical PageUsageByNUMAInner `json:"hierarchical,omitempty"` + Hierarchical PageUsageByNUMAInner `json:"hierarchical,omitzero"` } type PageUsageByNUMAInner struct { - Total PageStats `json:"total,omitempty"` - File PageStats `json:"file,omitempty"` - Anon PageStats `json:"anon,omitempty"` - Unevictable PageStats `json:"unevictable,omitempty"` + Total PageStats `json:"total,omitzero"` + File PageStats `json:"file,omitzero"` + Anon PageStats `json:"anon,omitzero"` + Unevictable PageStats `json:"unevictable,omitzero"` } type PageStats struct { - Total uint64 `json:"total,omitempty"` - Nodes map[uint8]uint64 `json:"nodes,omitempty"` + Total uint64 `json:"total,omitzero"` + Nodes map[uint8]uint64 `json:"nodes,omitzero"` } type PidsStats struct { // number of pids in the cgroup - Current uint64 `json:"current,omitempty"` + Current uint64 `json:"current,omitzero"` // active pids hard limit - Limit uint64 `json:"limit,omitempty"` + Limit uint64 `json:"limit,omitzero"` } type BlkioStatEntry struct { - Major uint64 `json:"major,omitempty"` - Minor uint64 `json:"minor,omitempty"` - Op string `json:"op,omitempty"` - Value uint64 `json:"value,omitempty"` + Major uint64 `json:"major,omitzero"` + Minor uint64 `json:"minor,omitzero"` + Op string `json:"op,omitzero"` + Value uint64 `json:"value,omitzero"` } type BlkioStats struct { // number of bytes transferred to and from the block device - IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"` - IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"` - IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"` - IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive,omitempty"` - IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive,omitempty"` - IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive,omitempty"` - IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive,omitempty"` - SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"` - PSI *PSIStats `json:"psi,omitempty"` - IoCostUsage []BlkioStatEntry `json:"io_cost_usage,omitempty"` - IoCostWait []BlkioStatEntry `json:"io_cost_wait,omitempty"` - IoCostIndebt []BlkioStatEntry `json:"io_cost_indebt,omitempty"` - IoCostIndelay []BlkioStatEntry `json:"io_cost_indelay,omitempty"` + IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitzero"` + IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitzero"` + IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitzero"` + IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive,omitzero"` + IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive,omitzero"` + IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive,omitzero"` + IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive,omitzero"` + SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitzero"` + PSI *PSIStats `json:"psi,omitzero"` + IoCostUsage []BlkioStatEntry `json:"io_cost_usage,omitzero"` + IoCostWait []BlkioStatEntry `json:"io_cost_wait,omitzero"` + IoCostIndebt []BlkioStatEntry `json:"io_cost_indebt,omitzero"` + IoCostIndelay []BlkioStatEntry `json:"io_cost_indelay,omitzero"` } type HugetlbStats struct { // current res_counter usage for hugetlb - Usage uint64 `json:"usage,omitempty"` + Usage uint64 `json:"usage,omitzero"` // maximum usage ever recorded. - MaxUsage uint64 `json:"max_usage,omitempty"` + MaxUsage uint64 `json:"max_usage,omitzero"` // number of times hugetlb usage allocation failure. Failcnt uint64 `json:"failcnt"` } type RdmaEntry struct { - Device string `json:"device,omitempty"` - HcaHandles uint32 `json:"hca_handles,omitempty"` - HcaObjects uint32 `json:"hca_objects,omitempty"` + Device string `json:"device,omitzero"` + HcaHandles uint32 `json:"hca_handles,omitzero"` + HcaObjects uint32 `json:"hca_objects,omitzero"` } type RdmaStats struct { - RdmaLimit []RdmaEntry `json:"rdma_limit,omitempty"` - RdmaCurrent []RdmaEntry `json:"rdma_current,omitempty"` + RdmaLimit []RdmaEntry `json:"rdma_limit,omitzero"` + RdmaCurrent []RdmaEntry `json:"rdma_current,omitzero"` } type MiscStats struct { // current resource usage for a key in misc - Usage uint64 `json:"usage,omitempty"` + Usage uint64 `json:"usage,omitzero"` // number of times the resource usage was about to go over the max boundary - Events uint64 `json:"events,omitempty"` + Events uint64 `json:"events,omitzero"` } type Stats struct { - CpuStats CpuStats `json:"cpu_stats,omitempty"` - CPUSetStats CPUSetStats `json:"cpuset_stats,omitempty"` - MemoryStats MemoryStats `json:"memory_stats,omitempty"` - PidsStats PidsStats `json:"pids_stats,omitempty"` - BlkioStats BlkioStats `json:"blkio_stats,omitempty"` + CpuStats CpuStats `json:"cpu_stats,omitzero"` + CPUSetStats CPUSetStats `json:"cpuset_stats,omitzero"` + MemoryStats MemoryStats `json:"memory_stats,omitzero"` + PidsStats PidsStats `json:"pids_stats,omitzero"` + BlkioStats BlkioStats `json:"blkio_stats,omitzero"` // the map is in the format "size of hugepage: stats of the hugepage" - HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"` - RdmaStats RdmaStats `json:"rdma_stats,omitempty"` + HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitzero"` + RdmaStats RdmaStats `json:"rdma_stats,omitzero"` // the map is in the format "misc resource name: stats of the key" - MiscStats map[string]MiscStats `json:"misc_stats,omitempty"` + MiscStats map[string]MiscStats `json:"misc_stats,omitzero"` } func NewStats() *Stats { From 68ec63d5eb41b7309e82be02a454a7f1c39337e1 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 30 Oct 2025 17:39:10 -0700 Subject: [PATCH 3/5] config_linux.go: replace omitempty with omitzero For some fields (like nested struct fields), omitempty has no effect but omitzero does. Let's use it (everywhere -- for consistency). NOTE it might result in some compatibility issues since now zero-only fields are not serialized. Since linter-extra thinks it's a new code, add some naming exceptions. Signed-off-by: Kir Kolyshkin --- .golangci-extra.yml | 2 ++ config_linux.go | 76 ++++++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/.golangci-extra.yml b/.golangci-extra.yml index 42a0bc9..5287bba 100644 --- a/.golangci-extra.yml +++ b/.golangci-extra.yml @@ -23,3 +23,5 @@ linters: # Legacy names we can't change without breaking compatibility. - path: stats.go text: "(type|struct field) (CpuUsage|CpuStats) should be " + - path: config_linux.go + text: "struct field (CpuShares|CpuQuota|CpuBurst|CpuPeriod|CpuRtRuntime|CpuRtPeriod|CpuWeight) should be " diff --git a/config_linux.go b/config_linux.go index 3d29d93..8b5e505 100644 --- a/config_linux.go +++ b/config_linux.go @@ -16,23 +16,23 @@ const ( // Cgroup holds properties of a cgroup on Linux. type Cgroup struct { // Name specifies the name of the cgroup - Name string `json:"name,omitempty"` + Name string `json:"name,omitzero"` // Parent specifies the name of parent of cgroup or slice - Parent string `json:"parent,omitempty"` + Parent string `json:"parent,omitzero"` // Path specifies the path to cgroups that are created and/or joined by the container. // The path is assumed to be relative to the host system cgroup mountpoint. - Path string `json:"path,omitempty"` + Path string `json:"path,omitzero"` // ScopePrefix describes prefix for the scope name. - ScopePrefix string `json:"scope_prefix,omitempty"` + ScopePrefix string `json:"scope_prefix,omitzero"` // Resources contains various cgroups settings to apply. *Resources // Systemd tells if systemd should be used to manage cgroups. - Systemd bool `json:"Systemd,omitempty"` + Systemd bool `json:"Systemd,omitzero"` // SystemdProps are any additional properties for systemd, // derived from org.systemd.property.xxx annotations. @@ -40,108 +40,108 @@ type Cgroup struct { SystemdProps []systemdDbus.Property `json:"-"` // Rootless tells if rootless cgroups should be used. - Rootless bool `json:"Rootless,omitempty"` + Rootless bool `json:"Rootless,omitzero"` // The host UID that should own the cgroup, or nil to accept // the default ownership. This should only be set when the // cgroupfs is to be mounted read/write. // Not all cgroup manager implementations support changing // the ownership. - OwnerUID *int `json:"owner_uid,omitempty"` + OwnerUID *int `json:"owner_uid,omitzero"` } type Resources struct { // Devices is the set of access rules for devices in the container. - Devices []*devices.Rule `json:"devices,omitempty"` + Devices []*devices.Rule `json:"devices,omitzero"` // Memory limit (in bytes). - Memory int64 `json:"memory,omitempty"` + Memory int64 `json:"memory,omitzero"` // Memory reservation or soft_limit (in bytes). - MemoryReservation int64 `json:"memory_reservation,omitempty"` + MemoryReservation int64 `json:"memory_reservation,omitzero"` // Total memory usage (memory+swap); use -1 for unlimited swap. - MemorySwap int64 `json:"memory_swap,omitempty"` + MemorySwap int64 `json:"memory_swap,omitzero"` // CPU shares (relative weight vs. other containers). - CpuShares uint64 `json:"cpu_shares,omitempty"` //nolint:revive // Suppress "var-naming: struct field CpuShares should be CPUShares". + CpuShares uint64 `json:"cpu_shares,omitzero"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. - CpuQuota int64 `json:"cpu_quota,omitempty"` //nolint:revive // Suppress "var-naming: struct field CpuQuota should be CPUQuota". + CpuQuota int64 `json:"cpu_quota,omitzero"` // CPU hardcap burst limit (in usecs). Allowed accumulated cpu time additionally for burst in a given period. - CpuBurst *uint64 `json:"cpu_burst,omitempty"` //nolint:revive // Suppress "var-naming: struct field CpuBurst should be CPUBurst". + CpuBurst *uint64 `json:"cpu_burst,omitzero"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. - CpuPeriod uint64 `json:"cpu_period,omitempty"` //nolint:revive // Suppress "var-naming: struct field CpuPeriod should be CPUPeriod". + CpuPeriod uint64 `json:"cpu_period,omitzero"` // How many time CPU will use in realtime scheduling (in usecs). - CpuRtRuntime int64 `json:"cpu_rt_quota,omitempty"` //nolint:revive // Suppress "var-naming: struct field CpuRtRuntime should be CPURtRuntime". + CpuRtRuntime int64 `json:"cpu_rt_quota,omitzero"` // CPU period to be used for realtime scheduling (in usecs). - CpuRtPeriod uint64 `json:"cpu_rt_period,omitempty"` //nolint:revive // Suppress "var-naming: struct field CpuQuota should be CPUQuota". + CpuRtPeriod uint64 `json:"cpu_rt_period,omitzero"` // Cpuset CPUs to use. - CpusetCpus string `json:"cpuset_cpus,omitempty"` + CpusetCpus string `json:"cpuset_cpus,omitzero"` // Cpuset memory nodes to use. - CpusetMems string `json:"cpuset_mems,omitempty"` + CpusetMems string `json:"cpuset_mems,omitzero"` // Cgroup's SCHED_IDLE value. - CPUIdle *int64 `json:"cpu_idle,omitempty"` + CPUIdle *int64 `json:"cpu_idle,omitzero"` // Process limit; set < `0' to disable limit. `nil` means "keep current limit". - PidsLimit *int64 `json:"pids_limit,omitempty"` + PidsLimit *int64 `json:"pids_limit,omitzero"` // Specifies per cgroup weight, range is from 10 to 1000. - BlkioWeight uint16 `json:"blkio_weight,omitempty"` + BlkioWeight uint16 `json:"blkio_weight,omitzero"` // Tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only. - BlkioLeafWeight uint16 `json:"blkio_leaf_weight,omitempty"` + BlkioLeafWeight uint16 `json:"blkio_leaf_weight,omitzero"` // Weight per cgroup per device, can override BlkioWeight. - BlkioWeightDevice []*WeightDevice `json:"blkio_weight_device,omitempty"` + BlkioWeightDevice []*WeightDevice `json:"blkio_weight_device,omitzero"` // IO read rate limit per cgroup per device, bytes per second. - BlkioThrottleReadBpsDevice []*ThrottleDevice `json:"blkio_throttle_read_bps_device,omitempty"` + BlkioThrottleReadBpsDevice []*ThrottleDevice `json:"blkio_throttle_read_bps_device,omitzero"` // IO write rate limit per cgroup per device, bytes per second. - BlkioThrottleWriteBpsDevice []*ThrottleDevice `json:"blkio_throttle_write_bps_device,omitempty"` + BlkioThrottleWriteBpsDevice []*ThrottleDevice `json:"blkio_throttle_write_bps_device,omitzero"` // IO read rate limit per cgroup per device, IO per second. - BlkioThrottleReadIOPSDevice []*ThrottleDevice `json:"blkio_throttle_read_iops_device,omitempty"` + BlkioThrottleReadIOPSDevice []*ThrottleDevice `json:"blkio_throttle_read_iops_device,omitzero"` // IO write rate limit per cgroup per device, IO per second. - BlkioThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkio_throttle_write_iops_device,omitempty"` + BlkioThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkio_throttle_write_iops_device,omitzero"` // Freeze value for the process. - Freezer FreezerState `json:"freezer,omitempty"` + Freezer FreezerState `json:"freezer,omitzero"` // Hugetlb limit (in bytes). - HugetlbLimit []*HugepageLimit `json:"hugetlb_limit,omitempty"` + HugetlbLimit []*HugepageLimit `json:"hugetlb_limit,omitzero"` // Whether to disable OOM killer. - OomKillDisable bool `json:"oom_kill_disable,omitempty"` + OomKillDisable bool `json:"oom_kill_disable,omitzero"` // Tuning swappiness behaviour per cgroup. - MemorySwappiness *uint64 `json:"memory_swappiness,omitempty"` + MemorySwappiness *uint64 `json:"memory_swappiness,omitzero"` // Set priority of network traffic for container. - NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap,omitempty"` + NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap,omitzero"` // Set class identifier for container's network packets. - NetClsClassid uint32 `json:"net_cls_classid_u,omitempty"` + NetClsClassid uint32 `json:"net_cls_classid_u,omitzero"` // Rdma resource restriction configuration. - Rdma map[string]LinuxRdma `json:"rdma,omitempty"` + Rdma map[string]LinuxRdma `json:"rdma,omitzero"` // Used on cgroups v2: // CpuWeight sets a proportional bandwidth limit. - CpuWeight uint64 `json:"cpu_weight,omitempty"` //nolint:revive // Suppress "var-naming: struct field CpuWeight should be CPUWeight". + CpuWeight uint64 `json:"cpu_weight,omitzero"` // Unified is cgroupv2-only key-value map. - Unified map[string]string `json:"unified,omitempty"` + Unified map[string]string `json:"unified,omitzero"` // SkipDevices allows to skip configuring device permissions. // Used by e.g. kubelet while creating a parent cgroup (kubepods) @@ -165,5 +165,5 @@ type Resources struct { // MemoryCheckBeforeUpdate is a flag for cgroup v2 managers to check // if the new memory limits (Memory and MemorySwap) being set are lower // than the current memory usage, and reject if so. - MemoryCheckBeforeUpdate bool `json:"memory_check_before_update,omitempty"` + MemoryCheckBeforeUpdate bool `json:"memory_check_before_update,omitzero"` } From 5ae4a33e01506b9d83e4370afc68f8cdb05a1f18 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 30 Oct 2025 17:42:06 -0700 Subject: [PATCH 4/5] devices/config: replace omitempty with omitzero This is technically a no-op, but let's change it for consistency. While at it, move linter-extra exceptions to golangci-extra.yml. Signed-off-by: Kir Kolyshkin --- .golangci-extra.yml | 2 ++ devices/config/device.go | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.golangci-extra.yml b/.golangci-extra.yml index 5287bba..ce687ce 100644 --- a/.golangci-extra.yml +++ b/.golangci-extra.yml @@ -25,3 +25,5 @@ linters: text: "(type|struct field) (CpuUsage|CpuStats) should be " - path: config_linux.go text: "struct field (CpuShares|CpuQuota|CpuBurst|CpuPeriod|CpuRtRuntime|CpuRtPeriod|CpuWeight) should be " + - path: devices/config/device.go + text: "struct field (Uid|Gid) should be " diff --git a/devices/config/device.go b/devices/config/device.go index 295575c..b36a3e8 100644 --- a/devices/config/device.go +++ b/devices/config/device.go @@ -20,10 +20,10 @@ type Device struct { FileMode os.FileMode `json:"file_mode"` // Uid of the device. - Uid uint32 `json:"uid,omitempty"` //nolint:revive // Suppress "var-naming: struct field Uid should be UID". + Uid uint32 `json:"uid,omitzero"` // Gid of the device. - Gid uint32 `json:"gid,omitempty"` //nolint:revive // Suppress "var-naming: struct field Gid should be GID". + Gid uint32 `json:"gid,omitzero"` } // Permissions is a cgroupv1-style string to represent device access. It From c3dd4629cb6d0fbe13496963998a52ab4b1eec16 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 13 Aug 2025 22:32:15 -0700 Subject: [PATCH 5/5] Modernize the code for Go 1.24 Mostly done by modernize -fix -test ./... with some minor manual edits on top. Signed-off-by: Kir Kolyshkin --- devices/devicefilter_test.go | 2 +- devices/devices_emulator_test.go | 5 +---- devices/systemd_test.go | 2 +- file_test.go | 3 +-- fs/cpuacct.go | 2 +- fs/cpuacct_test.go | 3 +-- fs/cpuset.go | 2 +- fs/fs_test.go | 3 +-- fs2/create.go | 5 ++--- fscommon/utils.go | 3 +-- getallpids_test.go | 2 +- systemd/common.go | 2 +- systemd/cpuset.go | 2 +- systemd/freeze_test.go | 1 - systemd/systemd_test.go | 1 - utils.go | 2 +- utils_test.go | 8 +++----- v1_utils.go | 2 +- 18 files changed, 19 insertions(+), 31 deletions(-) diff --git a/devices/devicefilter_test.go b/devices/devicefilter_test.go index 4010deb..6df6af2 100644 --- a/devices/devicefilter_test.go +++ b/devices/devicefilter_test.go @@ -9,7 +9,7 @@ import ( func hash(s, comm string) string { var res []string - for _, l := range strings.Split(s, "\n") { + for l := range strings.SplitSeq(s, "\n") { trimmed := strings.TrimSpace(l) if trimmed == "" || strings.HasPrefix(trimmed, comm) { continue diff --git a/devices/devices_emulator_test.go b/devices/devices_emulator_test.go index 24c1d1e..57c245a 100644 --- a/devices/devices_emulator_test.go +++ b/devices/devices_emulator_test.go @@ -202,7 +202,6 @@ c 10:200 rwm`, } for _, test := range tests { - test := test // capture range variable t.Run(test.name, func(t *testing.T) { list := bytes.NewBufferString(test.list) emu, err := emulatorFromList(list) @@ -741,7 +740,6 @@ func testDeviceEmulatorApply(t *testing.T, baseDefaultAllow bool) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { err := test.base.Apply(test.rule) if err != nil && test.expected != nil { @@ -1058,7 +1056,6 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { // If we are in black-list mode, we need to prepend the relevant // clear-all rule (the expected rule lists are written with @@ -1130,7 +1127,7 @@ c 10:200 rwm` var r *deviceRule var err error - for i := 0; i < b.N; i++ { + for b.Loop() { s := bufio.NewScanner(strings.NewReader(list)) for s.Scan() { line := s.Text() diff --git a/devices/systemd_test.go b/devices/systemd_test.go index 2cbb5ca..3c9a8e6 100644 --- a/devices/systemd_test.go +++ b/devices/systemd_test.go @@ -256,7 +256,7 @@ func TestFindDeviceGroup(t *testing.T) { } func BenchmarkFindDeviceGroup(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { if err := testFindDeviceGroup(); err != nil { b.Fatal(err) } diff --git a/file_test.go b/file_test.go index 9c3bf18..2183f33 100644 --- a/file_test.go +++ b/file_test.go @@ -82,8 +82,7 @@ func BenchmarkWriteFile(b *testing.B) { "\n\n\n\n\n\n\n\n", } - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { for _, val := range tc { if err := WriteFileByLine(dir, "file", val); err != nil { b.Fatal(err) diff --git a/fs/cpuacct.go b/fs/cpuacct.go index 391a023..48a016d 100644 --- a/fs/cpuacct.go +++ b/fs/cpuacct.go @@ -105,7 +105,7 @@ func getPercpuUsage(path string) ([]uint64, error) { if err != nil { return percpuUsage, err } - for _, value := range strings.Fields(data) { + for value := range strings.FieldsSeq(data) { value, err := strconv.ParseUint(value, 10, 64) if err != nil { return percpuUsage, &parseError{Path: path, File: file, Err: err} diff --git a/fs/cpuacct_test.go b/fs/cpuacct_test.go index c0c9543..73c6152 100644 --- a/fs/cpuacct_test.go +++ b/fs/cpuacct_test.go @@ -102,8 +102,7 @@ func BenchmarkGetCpuUsageBreakdown(b *testing.B) { "cpuacct.stat": cpuAcctStatContents, }) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { _, _, err := getCpuUsageBreakdown(path) if err != nil { b.Fatal(err) diff --git a/fs/cpuset.go b/fs/cpuset.go index ef6ff7d..f3f96df 100644 --- a/fs/cpuset.go +++ b/fs/cpuset.go @@ -82,7 +82,7 @@ func getCpusetStat(path string, file string) ([]uint16, error) { return extracted, &parseError{Path: path, File: file, Err: errors.New("empty file")} } - for _, s := range strings.Split(fileContent, ",") { + for s := range strings.SplitSeq(fileContent, ",") { fromStr, toStr, ok := strings.Cut(s, "-") if ok { from, err := strconv.ParseUint(fromStr, 10, 16) diff --git a/fs/fs_test.go b/fs/fs_test.go index f9a0935..331e9f1 100644 --- a/fs/fs_test.go +++ b/fs/fs_test.go @@ -36,8 +36,7 @@ func BenchmarkGetStats(b *testing.B) { var st *cgroups.Stats - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { st, err = m.GetStats() if err != nil { b.Fatal(err) diff --git a/fs2/create.go b/fs2/create.go index 565ca88..6be11c2 100644 --- a/fs2/create.go +++ b/fs2/create.go @@ -28,7 +28,7 @@ func needAnyControllers(r *cgroups.Resources) (bool, error) { return false, err } avail := make(map[string]struct{}) - for _, ctr := range strings.Fields(content) { + for ctr := range strings.FieldsSeq(content) { avail[ctr] = struct{}{} } @@ -137,8 +137,7 @@ func CreateCgroupPath(path string, c *cgroups.Cgroup) (Err error) { if i < len(elements)-1 { if err := cgroups.WriteFile(current, cgStCtlFile, res); err != nil { // try write one by one - allCtrs := strings.Split(res, " ") - for _, ctr := range allCtrs { + for ctr := range strings.SplitSeq(res, " ") { _ = cgroups.WriteFile(current, cgStCtlFile, ctr) } } diff --git a/fscommon/utils.go b/fscommon/utils.go index d8f8dfc..a8b32aa 100644 --- a/fscommon/utils.go +++ b/fscommon/utils.go @@ -82,8 +82,7 @@ func GetValueByKey(path, file, key string) (uint64, error) { } key += " " - lines := strings.Split(content, "\n") - for _, line := range lines { + for line := range strings.SplitSeq(content, "\n") { v, ok := strings.CutPrefix(line, key) if ok { val, err := ParseUint(v, 10, 64) diff --git a/getallpids_test.go b/getallpids_test.go index e6b0632..125fa0a 100644 --- a/getallpids_test.go +++ b/getallpids_test.go @@ -6,7 +6,7 @@ import ( func BenchmarkGetAllPids(b *testing.B) { total := 0 - for i := 0; i < b.N; i++ { + for b.Loop() { i, err := GetAllPids("/sys/fs/cgroup") if err != nil { b.Fatal(err) diff --git a/systemd/common.go b/systemd/common.go index 537defb..42083d0 100644 --- a/systemd/common.go +++ b/systemd/common.go @@ -77,7 +77,7 @@ func ExpandSlice(slice string) (string, error) { if sliceName == "-" { return "/", nil } - for _, component := range strings.Split(sliceName, "-") { + for component := range strings.SplitSeq(sliceName, "-") { // test--a.slice isn't permitted, nor is -test.slice. if component == "" { return "", fmt.Errorf("invalid slice name: %s", slice) diff --git a/systemd/cpuset.go b/systemd/cpuset.go index c6f5642..f260362 100644 --- a/systemd/cpuset.go +++ b/systemd/cpuset.go @@ -14,7 +14,7 @@ import ( func RangeToBits(str string) ([]byte, error) { bits := new(big.Int) - for _, r := range strings.Split(str, ",") { + for r := range strings.SplitSeq(str, ",") { // allow extra spaces around r = strings.TrimSpace(r) // allow empty elements (extra commas) diff --git a/systemd/freeze_test.go b/systemd/freeze_test.go index 35558a8..0f05137 100644 --- a/systemd/freeze_test.go +++ b/systemd/freeze_test.go @@ -131,7 +131,6 @@ func TestFreezeBeforeSet(t *testing.T) { } for _, tc := range testCases { - tc := tc t.Run(tc.desc, func(t *testing.T) { m, err := NewLegacyManager(tc.cg, nil) if err != nil { diff --git a/systemd/systemd_test.go b/systemd/systemd_test.go index 60a6c1f..156e55e 100644 --- a/systemd/systemd_test.go +++ b/systemd/systemd_test.go @@ -160,7 +160,6 @@ func TestUnifiedResToSystemdProps(t *testing.T) { } for _, tc := range testCases { - tc := tc t.Run(tc.name, func(t *testing.T) { if tc.minVer != 0 && systemdVersion(cm) < tc.minVer { t.Skipf("requires systemd >= %d", tc.minVer) diff --git a/utils.go b/utils.go index 95b3310..469475c 100644 --- a/utils.go +++ b/utils.go @@ -207,7 +207,7 @@ func parseCgroupFromReader(r io.Reader) (map[string]string, error) { return nil, fmt.Errorf("invalid cgroup entry: must contain at least two colons: %v", text) } - for _, subs := range strings.Split(parts[1], ",") { + for subs := range strings.SplitSeq(parts[1], ",") { cgroups[subs] = parts[2] } } diff --git a/utils_test.go b/utils_test.go index 79023b7..6de34d3 100644 --- a/utils_test.go +++ b/utils_test.go @@ -323,8 +323,8 @@ func BenchmarkGetCgroupMounts(b *testing.B) { if err != nil { b.Fatal(err) } - b.ResetTimer() - for i := 0; i < b.N; i++ { + + for b.Loop() { if _, err := getCgroupMountsHelper(subsystems, mi, false); err != nil { b.Fatal(err) } @@ -449,7 +449,7 @@ func BenchmarkGetHugePageSizeImpl(b *testing.B) { output []string err error ) - for i := 0; i < b.N; i++ { + for b.Loop() { output, err = getHugePageSizeFromFilenames(input) } if err != nil || len(output) != len(input) { @@ -512,7 +512,6 @@ func TestGetHugePageSizeImpl(t *testing.T) { } for _, c := range testCases { - c := c t.Run(c.doc, func(t *testing.T) { output, err := getHugePageSizeFromFilenames(c.input) t.Log("input:", c.input, "; output:", output, "; err:", err) @@ -639,7 +638,6 @@ func TestConvertMemorySwapToCgroupV2Value(t *testing.T) { } for _, c := range cases { - c := c t.Run(c.descr, func(t *testing.T) { swap, err := ConvertMemorySwapToCgroupV2Value(c.memswap, c.memory) if c.expErr { diff --git a/v1_utils.go b/v1_utils.go index 19b8af1..11025b2 100644 --- a/v1_utils.go +++ b/v1_utils.go @@ -170,7 +170,7 @@ func getCgroupMountsHelper(ss map[string]bool, mounts []*mountinfo.Info, all boo Mountpoint: mi.Mountpoint, Root: mi.Root, } - for _, opt := range strings.Split(mi.VFSOptions, ",") { + for opt := range strings.SplitSeq(mi.VFSOptions, ",") { seen, known := ss[opt] if !known || (!all && seen) { continue