From caf6f8d1527007eea582c54e5a52a7a8445790a5 Mon Sep 17 00:00:00 2001 From: Daniel Dao Date: Tue, 9 Nov 2021 11:51:44 +0000 Subject: [PATCH] cgroupv2: fix CPUQuota parsing CPUQuota can return "max PERIOD", in this case, we detect "max" and return `-1, nil`, which for the current usecase of detecting cpu-num from quota should be sufficient. Signed-off-by: Daniel Dao --- runsc/cgroup/cgroup_v2.go | 13 +++++++++- runsc/cgroup/cgroup_v2_test.go | 43 ++++++++++++++++++++++++++++++++++ runsc/sandbox/sandbox.go | 2 +- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/runsc/cgroup/cgroup_v2.go b/runsc/cgroup/cgroup_v2.go index 36ea4360f..5e6158ea7 100644 --- a/runsc/cgroup/cgroup_v2.go +++ b/runsc/cgroup/cgroup_v2.go @@ -234,11 +234,21 @@ func (c *cgroupV2) CPUQuota() (float64, error) { if err != nil { return -1, err } - data := strings.SplitN(cpuMax, " ", 2) + + return parseCPUQuota(cpuMax) +} + +func parseCPUQuota(cpuMax string) (float64, error) { + data := strings.SplitN(strings.TrimSpace(cpuMax), " ", 2) if len(data) != 2 { return -1, fmt.Errorf("invalid cpu.max data %q", cpuMax) } + // no cpu limit if quota is max + if data[0] == "max" { + return -1, nil + } + quota, err := strconv.ParseInt(data[0], 10, 64) if err != nil { return -1, err @@ -253,6 +263,7 @@ func (c *cgroupV2) CPUQuota() (float64, error) { return -1, err } return float64(quota) / float64(period), nil + } // CPUUsage returns the total CPU usage of the cgroup. diff --git a/runsc/cgroup/cgroup_v2_test.go b/runsc/cgroup/cgroup_v2_test.go index 20d32609a..62ed8f2be 100644 --- a/runsc/cgroup/cgroup_v2_test.go +++ b/runsc/cgroup/cgroup_v2_test.go @@ -197,3 +197,46 @@ func TestConvertMemorySwapToCgroupV2Value(t *testing.T) { } } } + +func TestParseCPUQuota(t *testing.T) { + cases := []struct { + quota string + expected float64 + expErr bool + }{ + { + quota: "max 100000\n", + expected: -1, + }, + { + quota: "10000 100000", + expected: 0.1, + }, + { + quota: "20000 100000\n", + expected: 0.2, + }, + + { + quota: "-1", + expected: -1, + expErr: true, + }, + } + + for _, c := range cases { + res, err := parseCPUQuota(c.quota) + if c.expErr { + if err == nil { + t.Errorf("quota: %q, expected error, got %.2f, nil", c.quota, res) + } + continue + } + if err != nil { + t.Errorf("quota: %q, expected success, got error %s", c.quota, err) + } + if res != c.expected { + t.Errorf("quota: %q, expected %.2f, got error %.2f", c.quota, c.expected, res) + } + } +} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 8f7022fa1..2605b7cd3 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -777,7 +777,7 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn quota, err := s.CgroupJSON.Cgroup.CPUQuota() if err != nil { - return fmt.Errorf("getting cpu qouta from cgroups: %v", err) + return fmt.Errorf("getting cpu quota from cgroups: %v", err) } if n := int(math.Ceil(quota)); n > 0 { if n < minCPUs {