From 762800c9eaf00e100ef34d467235cb0d2a3dbe13 Mon Sep 17 00:00:00 2001 From: Zhou Hao Date: Thu, 3 May 2018 16:36:35 +0800 Subject: [PATCH] validation: add cgroup devices validation Signed-off-by: Zhou Hao --- cgroups/cgroups_v1.go | 46 +++++++++++++++++++ validation/linux_cgroups_devices.go | 22 +++++++++ validation/linux_cgroups_relative_devices.go | 22 +++++++++ validation/util/linux_resources_devices.go | 47 ++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 validation/linux_cgroups_devices.go create mode 100644 validation/linux_cgroups_relative_devices.go create mode 100644 validation/util/linux_resources_devices.go diff --git a/cgroups/cgroups_v1.go b/cgroups/cgroups_v1.go index 3f056821c..0ac070a3b 100644 --- a/cgroups/cgroups_v1.go +++ b/cgroups/cgroups_v1.go @@ -289,6 +289,52 @@ func (cg *CgroupV1) GetCPUData(pid int, cgPath string) (*rspec.LinuxCPU, error) // GetDevicesData gets cgroup devices data func (cg *CgroupV1) GetDevicesData(pid int, cgPath string) ([]rspec.LinuxDeviceCgroup, error) { ld := []rspec.LinuxDeviceCgroup{} + fileName := strings.Join([]string{"devices", "list"}, ".") + filePath := filepath.Join(cg.MountPath, "devices", cgPath, fileName) + if !filepath.IsAbs(cgPath) { + subPath, err := GetSubsystemPath(pid, "devices") + if err != nil { + return nil, err + } + if !strings.Contains(subPath, cgPath) { + return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "devices") + } + filePath = filepath.Join(cg.MountPath, "devices", subPath, fileName) + } + contents, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, err + } + parts := strings.Split(strings.TrimSpace(string(contents)), "\n") + for _, part := range parts { + elem := strings.Split(part, " ") + ele := strings.Split(elem[1], ":") + var major, minor int64 + if ele[0] == "*" { + major = 0 + } else { + major, err = strconv.ParseInt(ele[0], 10, 64) + if err != nil { + return nil, err + } + } + if ele[1] == "*" { + minor = 0 + } else { + minor, err = strconv.ParseInt(ele[1], 10, 64) + if err != nil { + return nil, err + } + } + + device := rspec.LinuxDeviceCgroup{} + device.Allow = true + device.Type = elem[0] + device.Major = &major + device.Minor = &minor + device.Access = elem[2] + ld = append(ld, device) + } return ld, nil } diff --git a/validation/linux_cgroups_devices.go b/validation/linux_cgroups_devices.go new file mode 100644 index 000000000..825efadcb --- /dev/null +++ b/validation/linux_cgroups_devices.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/cgroups" + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + var major1, minor1, major2, minor2, major3, minor3 int64 = 10, 229, 8, 0, 10, 200 + g, err := util.GetDefaultGenerator() + if err != nil { + util.Fatal(err) + } + g.SetLinuxCgroupsPath(cgroups.AbsCgroupPath) + g.AddLinuxResourcesDevice(true, "c", &major1, &minor1, "rwm") + g.AddLinuxResourcesDevice(false, "b", &major2, &minor2, "rw") + g.AddLinuxResourcesDevice(true, "a", &major3, &minor3, "r") + err = util.RuntimeOutsideValidate(g, util.ValidateLinuxResourcesDevices) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_cgroups_relative_devices.go b/validation/linux_cgroups_relative_devices.go new file mode 100644 index 000000000..b411be323 --- /dev/null +++ b/validation/linux_cgroups_relative_devices.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/cgroups" + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + var major1, minor1, major2, minor2, major3, minor3 int64 = 10, 229, 8, 0, 10, 200 + g, err := util.GetDefaultGenerator() + if err != nil { + util.Fatal(err) + } + g.SetLinuxCgroupsPath(cgroups.RelCgroupPath) + g.AddLinuxResourcesDevice(true, "c", &major1, &minor1, "rwm") + g.AddLinuxResourcesDevice(false, "b", &major2, &minor2, "rw") + g.AddLinuxResourcesDevice(true, "a", &major3, &minor3, "r") + err = util.RuntimeOutsideValidate(g, util.ValidateLinuxResourcesDevices) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/util/linux_resources_devices.go b/validation/util/linux_resources_devices.go new file mode 100644 index 000000000..08e4b2738 --- /dev/null +++ b/validation/util/linux_resources_devices.go @@ -0,0 +1,47 @@ +package util + +import ( + "fmt" + + "github.com/mndrix/tap-go" + rspec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/cgroups" +) + +// ValidateLinuxResourcesDevices validates linux.resources.devices. +func ValidateLinuxResourcesDevices(config *rspec.Spec, state *rspec.State) error { + t := tap.New() + t.Header(0) + + cg, err := cgroups.FindCgroup() + t.Ok((err == nil), "find devices") + if err != nil { + t.Diagnostic(err.Error()) + t.AutoPlan() + return nil + } + + lnd, err := cg.GetDevicesData(state.Pid, config.Linux.CgroupsPath) + t.Ok((err == nil), "get devices data") + if err != nil { + t.Diagnostic(err.Error()) + t.AutoPlan() + return nil + } + + for _, device := range config.Linux.Resources.Devices { + for _, lid := range lnd { + if lid.Allow == device.Allow { + t.Ok(lid.Allow == device.Allow && lid.Type == device.Type && *lid.Major == *device.Major && + *lid.Minor == *device.Minor && lid.Access == device.Access, + fmt.Sprintf("devices %s %d:%d %s is set correctly", + device.Type, *device.Major, *device.Minor, device.Access)) + t.Diagnosticf("expect: %s %d:%d %s, actual: %s %d:%d %s", + device.Type, *device.Major, *device.Minor, device.Access, lid.Type, *lid.Major, *lid.Minor, lid.Access) + } + } + } + + t.AutoPlan() + return nil +}