From 799312a402c3dbcb23ccc1e9d806fe63a4e38232 Mon Sep 17 00:00:00 2001 From: Ayato Tokubi Date: Mon, 7 Jul 2025 14:58:13 +0000 Subject: [PATCH] Update pod resize test to accept new cpu.weight conversion. Signed-off-by: Ayato Tokubi --- go.mod | 2 +- go.sum | 4 +- .../common/node/framework/cgroups/cgroups.go | 21 +------- .../node/framework/cgroups/cgroups_linux.go | 50 +++++++++++++++++++ .../framework/cgroups/cgroups_unsupported.go | 26 ++++++++++ .../opencontainers/cgroups/config_linux.go | 2 +- .../opencontainers/cgroups/utils.go | 27 +++++++--- vendor/modules.txt | 2 +- 8 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 test/e2e/common/node/framework/cgroups/cgroups_linux.go create mode 100644 test/e2e/common/node/framework/cgroups/cgroups_unsupported.go diff --git a/go.mod b/go.mod index d7dd05e19c3..3fe8daef40c 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 - github.com/opencontainers/cgroups v0.0.2 + github.com/opencontainers/cgroups v0.0.3 github.com/opencontainers/selinux v1.11.1 github.com/pmezard/go-difflib v1.0.0 github.com/prometheus/client_golang v1.22.0 diff --git a/go.sum b/go.sum index 61b06b11aaa..e6d5ad691b6 100644 --- a/go.sum +++ b/go.sum @@ -255,8 +255,8 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/opencontainers/cgroups v0.0.2 h1:A+mAPPMfgKNCEZUUtibESFx06uvhAmvo8sSz3Abwk7o= -github.com/opencontainers/cgroups v0.0.2/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs= +github.com/opencontainers/cgroups v0.0.3 h1:Jc9dWh/0YLGjdy6J/9Ln8NM5BfTA4W2BY0GMozy3aDU= +github.com/opencontainers/cgroups v0.0.3/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= diff --git a/test/e2e/common/node/framework/cgroups/cgroups.go b/test/e2e/common/node/framework/cgroups/cgroups.go index fc71e946c68..ce9f758341d 100644 --- a/test/e2e/common/node/framework/cgroups/cgroups.go +++ b/test/e2e/common/node/framework/cgroups/cgroups.go @@ -207,23 +207,6 @@ func getExpectedCPULimitFromCPUQuota(cpuQuota int64, podOnCgroupV2 bool) string return expectedCPULimitString } -func getExpectedCPUShares(rr *v1.ResourceRequirements, podOnCgroupv2 bool) int64 { - cpuRequest := rr.Requests.Cpu() - cpuLimit := rr.Limits.Cpu() - var shares int64 - if cpuRequest.IsZero() && !cpuLimit.IsZero() { - shares = int64(kubecm.MilliCPUToShares(cpuLimit.MilliValue())) - } else { - shares = int64(kubecm.MilliCPUToShares(cpuRequest.MilliValue())) - } - if podOnCgroupv2 { - // TODO: This fomula should be a shared function. - return 1 + ((shares-2)*9999)/262142 - } else { - return shares - } -} - func getExpectedMemLimitString(rr *v1.ResourceRequirements, podOnCgroupv2 bool) string { expectedMemLimitInBytes := rr.Limits.Memory().Value() expectedMemLimitString := strconv.FormatInt(expectedMemLimitInBytes, 10) @@ -236,7 +219,7 @@ func getExpectedMemLimitString(rr *v1.ResourceRequirements, podOnCgroupv2 bool) func verifyContainerCPUWeight(f *framework.Framework, pod *v1.Pod, containerName string, expectedResources *v1.ResourceRequirements, podOnCgroupv2 bool) error { cpuWeightCgPath := getCgroupCPURequestPath(cgroupFsPath, podOnCgroupv2) expectedCPUShares := getExpectedCPUShares(expectedResources, podOnCgroupv2) - if err := VerifyCgroupValue(f, pod, containerName, cpuWeightCgPath, strconv.FormatInt(expectedCPUShares, 10)); err != nil { + if err := VerifyCgroupValue(f, pod, containerName, cpuWeightCgPath, expectedCPUShares...); err != nil { return fmt.Errorf("failed to verify cpu request cgroup value: %w", err) } return nil @@ -286,7 +269,7 @@ func verifyPodCPUWeight(f *framework.Framework, pod *v1.Pod, expectedResources * cpuWeightCgPath = fmt.Sprintf("%s/%s", podCgPath, cgroupCPUSharesFile) } expectedCPUShares := getExpectedCPUShares(expectedResources, podOnCgroupv2) - if err := VerifyCgroupValue(f, pod, pod.Spec.Containers[0].Name, cpuWeightCgPath, strconv.FormatInt(expectedCPUShares, 10)); err != nil { + if err := VerifyCgroupValue(f, pod, pod.Spec.Containers[0].Name, cpuWeightCgPath, expectedCPUShares...); err != nil { return fmt.Errorf("pod cgroup cpu weight verification failed: %w", err) } return nil diff --git a/test/e2e/common/node/framework/cgroups/cgroups_linux.go b/test/e2e/common/node/framework/cgroups/cgroups_linux.go new file mode 100644 index 00000000000..c77c560d50f --- /dev/null +++ b/test/e2e/common/node/framework/cgroups/cgroups_linux.go @@ -0,0 +1,50 @@ +/* +Copyright 2025 The Kubernetes Authors. + +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 cgroups + +import ( + "strconv" + + libcontainercgroups "github.com/opencontainers/cgroups" + v1 "k8s.io/api/core/v1" + kubecm "k8s.io/kubernetes/pkg/kubelet/cm" +) + +func getExpectedCPUShares(rr *v1.ResourceRequirements, podOnCgroupv2 bool) []string { + // This function is moved out from cgroups.go because opencontainers/cgroups can only be compiled in linux platforms. + cpuRequest := rr.Requests.Cpu() + cpuLimit := rr.Limits.Cpu() + var shares int64 + if cpuRequest.IsZero() && !cpuLimit.IsZero() { + shares = int64(kubecm.MilliCPUToShares(cpuLimit.MilliValue())) + } else { + shares = int64(kubecm.MilliCPUToShares(cpuRequest.MilliValue())) + } + if podOnCgroupv2 { + // Because of https://github.com/kubernetes/kubernetes/issues/131216, the way of conversion has been changed. + // runc: https://github.com/opencontainers/runc/pull/4785 + // crun: https://github.com/containers/crun/issues/1721 + // This is dependent on the container runtime version. In order not to break the tests when we upgrade the + // container runtimes, we check if either the old or the new conversion matches the actual value for now. + // TODO: Remove the old conversion once container runtimes are updated. + oldConverted := 1 + ((shares-2)*9999)/262142 + converted := libcontainercgroups.ConvertCPUSharesToCgroupV2Value(uint64(shares)) + return []string{strconv.FormatInt(oldConverted, 10), strconv.FormatInt(int64(converted), 10)} + } else { + return []string{strconv.FormatInt(shares, 10)} + } +} diff --git a/test/e2e/common/node/framework/cgroups/cgroups_unsupported.go b/test/e2e/common/node/framework/cgroups/cgroups_unsupported.go new file mode 100644 index 00000000000..8f5c823c8f1 --- /dev/null +++ b/test/e2e/common/node/framework/cgroups/cgroups_unsupported.go @@ -0,0 +1,26 @@ +//go:build !linux + +/* +Copyright 2025 The Kubernetes Authors. + +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 cgroups + +import v1 "k8s.io/api/core/v1" + +func getExpectedCPUShares(rr *v1.ResourceRequirements, podOnCgroupv2 bool) []string { + // cgroup is only supported in linux. + return []string{} +} diff --git a/vendor/github.com/opencontainers/cgroups/config_linux.go b/vendor/github.com/opencontainers/cgroups/config_linux.go index 9f1d44537b0..9bc58a3789b 100644 --- a/vendor/github.com/opencontainers/cgroups/config_linux.go +++ b/vendor/github.com/opencontainers/cgroups/config_linux.go @@ -29,7 +29,7 @@ type Cgroup struct { ScopePrefix string `json:"scope_prefix,omitempty"` // Resources contains various cgroups settings to apply. - *Resources `json:"Resources,omitempty"` + *Resources // Systemd tells if systemd should be used to manage cgroups. Systemd bool `json:"Systemd,omitempty"` diff --git a/vendor/github.com/opencontainers/cgroups/utils.go b/vendor/github.com/opencontainers/cgroups/utils.go index 98b6a07fab8..95b3310ab6e 100644 --- a/vendor/github.com/opencontainers/cgroups/utils.go +++ b/vendor/github.com/opencontainers/cgroups/utils.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "math" "os" "path/filepath" "strconv" @@ -413,16 +414,30 @@ func WriteCgroupProc(dir string, pid int) error { return err } -// Since the OCI spec is designed for cgroup v1, in some cases -// there is need to convert from the cgroup v1 configuration to cgroup v2 -// the formula for cpuShares is y = (1 + ((x - 2) * 9999) / 262142) -// convert from [2-262144] to [1-10000] -// 262144 comes from Linux kernel definition "#define MAX_SHARES (1UL << 18)" +// ConvertCPUSharesToCgroupV2Value converts CPU shares, used by cgroup v1, +// to CPU weight, used by cgroup v2. +// +// Cgroup v1 CPU shares has a range of [2^1...2^18], i.e. [2...262144], +// and the default value is 1024. +// +// Cgroup v2 CPU weight has a range of [10^0...10^4], i.e. [1...10000], +// and the default value is 100. func ConvertCPUSharesToCgroupV2Value(cpuShares uint64) uint64 { + // The value of 0 means "unset". if cpuShares == 0 { return 0 } - return (1 + ((cpuShares-2)*9999)/262142) + if cpuShares <= 2 { + return 1 + } + if cpuShares >= 262144 { + return 10000 + } + l := math.Log2(float64(cpuShares)) + // Quadratic function which fits min, max, and default. + exponent := (l*l+125*l)/612.0 - 7.0/34.0 + + return uint64(math.Ceil(math.Pow(10, exponent))) } // ConvertMemorySwapToCgroupV2Value converts MemorySwap value from OCI spec diff --git a/vendor/modules.txt b/vendor/modules.txt index f99965de1e5..ed3abdc4576 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -456,7 +456,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/node github.com/onsi/gomega/matchers/support/goraph/util github.com/onsi/gomega/types -# github.com/opencontainers/cgroups v0.0.2 +# github.com/opencontainers/cgroups v0.0.3 ## explicit; go 1.23.0 github.com/opencontainers/cgroups github.com/opencontainers/cgroups/devices/config