mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-27 02:54:27 +01:00
Fixes #344 Add host cgroup support for kata. This commits only adds cpu.cfs_period and cpu.cfs_quota support. It will create 3-level hierarchy, take "cpu" cgroup as an example: ``` /sys/fs/cgroup |---cpu |---kata |---<sandbox-id> |--vcpu |---<sandbox-id> ``` * `vc` cgroup is common parent for all kata-container sandbox, it won't be removed after sandbox removed. This cgroup has no limitation. * `<sandbox-id>` cgroup is the layer for each sandbox, it contains all other qemu threads except for vcpu threads. In future, we can consider putting all shim processes and proxy process here. This cgroup has no limitation yet. * `vcpu` cgroup contains vcpu threads from qemu. Currently cpu quota and period constraint applies to this cgroup. Signed-off-by: Wei Zhang <zhangwei555@huawei.com> Signed-off-by: Jingxiao Lu <lujingxiao@huawei.com>
212 lines
5.4 KiB
Go
212 lines
5.4 KiB
Go
// Copyright (c) 2018 Huawei Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package virtcontainers
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
|
|
)
|
|
|
|
func getCgroupDestination(subsystem string) (string, error) {
|
|
f, err := os.Open("/proc/self/mountinfo")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer f.Close()
|
|
s := bufio.NewScanner(f)
|
|
for s.Scan() {
|
|
if err := s.Err(); err != nil {
|
|
return "", err
|
|
}
|
|
fields := strings.Fields(s.Text())
|
|
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
|
if opt == subsystem {
|
|
return fields[4], nil
|
|
}
|
|
}
|
|
}
|
|
return "", fmt.Errorf("failed to find cgroup mountpoint for %q", subsystem)
|
|
}
|
|
|
|
func TestMergeSpecResource(t *testing.T) {
|
|
s := &Sandbox{
|
|
config: &SandboxConfig{
|
|
Containers: []ContainerConfig{
|
|
{
|
|
ID: "containerA",
|
|
Annotations: make(map[string]string),
|
|
},
|
|
{
|
|
ID: "containerA",
|
|
Annotations: make(map[string]string),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
contA := s.config.Containers[0]
|
|
contB := s.config.Containers[1]
|
|
|
|
getIntP := func(x int64) *int64 { return &x }
|
|
getUintP := func(x uint64) *uint64 { return &x }
|
|
|
|
type testData struct {
|
|
first *specs.LinuxResources
|
|
second *specs.LinuxResources
|
|
expected *specs.LinuxResources
|
|
}
|
|
|
|
for _, testdata := range []testData{
|
|
{
|
|
nil,
|
|
nil,
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{}},
|
|
},
|
|
{
|
|
nil,
|
|
&specs.LinuxResources{},
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{}},
|
|
},
|
|
{
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(0), Period: getUintP(100000)}},
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(20000), Period: getUintP(100000)}},
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(20000), Period: getUintP(100000)}},
|
|
},
|
|
{
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(10000), Period: getUintP(0)}},
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(20000), Period: getUintP(100000)}},
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(20000), Period: getUintP(100000)}},
|
|
},
|
|
{
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(1000), Period: getUintP(2000)}},
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(20000), Period: getUintP(100000)}},
|
|
&specs.LinuxResources{CPU: &specs.LinuxCPU{Quota: getIntP(1400), Period: getUintP(2000)}},
|
|
},
|
|
} {
|
|
data, err := json.Marshal(&specs.Spec{
|
|
Linux: &specs.Linux{
|
|
Resources: testdata.first,
|
|
},
|
|
})
|
|
assert.Nil(t, err)
|
|
contA.Annotations[annotations.ConfigJSONKey] = string(data)
|
|
|
|
data, err = json.Marshal(&specs.Spec{
|
|
Linux: &specs.Linux{
|
|
Resources: testdata.second,
|
|
},
|
|
})
|
|
assert.Nil(t, err)
|
|
contB.Annotations[annotations.ConfigJSONKey] = string(data)
|
|
|
|
rc, err := s.mergeSpecResource()
|
|
assert.Nil(t, err)
|
|
assert.True(t, reflect.DeepEqual(rc, testdata.expected), "should be equal, got: %#v, expected: %#v", rc, testdata.expected)
|
|
}
|
|
}
|
|
|
|
func TestSetupCgroups(t *testing.T) {
|
|
if os.Geteuid() != 0 {
|
|
t.Skip("Test disabled as requires root privileges")
|
|
}
|
|
|
|
s := &Sandbox{
|
|
id: "test-sandbox",
|
|
hypervisor: &mockHypervisor{},
|
|
config: &SandboxConfig{
|
|
Containers: []ContainerConfig{
|
|
{
|
|
ID: "containerA",
|
|
Annotations: make(map[string]string),
|
|
},
|
|
{
|
|
ID: "containerA",
|
|
Annotations: make(map[string]string),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
contA := s.config.Containers[0]
|
|
contB := s.config.Containers[1]
|
|
|
|
getIntP := func(x int64) *int64 { return &x }
|
|
getUintP := func(x uint64) *uint64 { return &x }
|
|
|
|
data, err := json.Marshal(&specs.Spec{
|
|
Linux: &specs.Linux{
|
|
Resources: &specs.LinuxResources{
|
|
CPU: &specs.LinuxCPU{
|
|
Quota: getIntP(5000),
|
|
Period: getUintP(10000),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
assert.Nil(t, err)
|
|
contA.Annotations[annotations.ConfigJSONKey] = string(data)
|
|
|
|
data, err = json.Marshal(&specs.Spec{
|
|
Linux: &specs.Linux{
|
|
Resources: &specs.LinuxResources{
|
|
CPU: &specs.LinuxCPU{
|
|
Quota: getIntP(10000),
|
|
Period: getUintP(40000),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
assert.Nil(t, err)
|
|
contB.Annotations[annotations.ConfigJSONKey] = string(data)
|
|
|
|
err = s.newCgroups()
|
|
assert.Nil(t, err, "failed to create cgroups")
|
|
|
|
defer s.destroyCgroups()
|
|
|
|
// test if function works without error
|
|
err = s.setupCgroups()
|
|
assert.Nil(t, err, "setup host cgroup failed")
|
|
|
|
// test if the quota and period value are written into cgroup files
|
|
cpu, err := getCgroupDestination("cpu")
|
|
assert.Nil(t, err, "failed to get cpu cgroup path")
|
|
assert.NotEqual(t, "", cpu, "cpu cgroup value can't be empty")
|
|
|
|
parentDir := filepath.Join(cpu, defaultCgroupParent, "test-sandbox", "vcpu")
|
|
quotaFile := filepath.Join(parentDir, "cpu.cfs_quota_us")
|
|
periodFile := filepath.Join(parentDir, "cpu.cfs_period_us")
|
|
|
|
expectedQuota := "7500\n"
|
|
expectedPeriod := "10000\n"
|
|
|
|
fquota, err := os.Open(quotaFile)
|
|
assert.Nil(t, err, "open file %q failed", quotaFile)
|
|
defer fquota.Close()
|
|
data, err = ioutil.ReadAll(fquota)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, expectedQuota, string(data), "failed to get expected cfs_quota")
|
|
|
|
fperiod, err := os.Open(periodFile)
|
|
assert.Nil(t, err, "open file %q failed", periodFile)
|
|
defer fperiod.Close()
|
|
data, err = ioutil.ReadAll(fperiod)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, expectedPeriod, string(data), "failed to get expected cfs_period")
|
|
}
|