From 46b7c5bc63dec21d1b2a68a36ad84298f9f176f2 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Wed, 23 Feb 2022 08:02:44 +0200 Subject: Bump CDI go dependency to v0.3.0 This updates the CDI dependency to the v0.3.0 tagged version instead of relying on a pseudo version. This also addresses the fact that cgroups are not set correctly for devices using the previous dependency. Signed-off-by: Evan Lezar --- .../pkg/cdi/container-edits.go | 143 ++++++++++++++++++++- 1 file changed, 139 insertions(+), 4 deletions(-) (limited to 'vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go') diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go index 767cba57b..80d88b118 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go @@ -17,6 +17,9 @@ package cdi import ( + "os" + "path/filepath" + "sort" "strings" "github.com/pkg/errors" @@ -24,6 +27,8 @@ import ( "github.com/container-orchestrated-devices/container-device-interface/specs-go" oci "github.com/opencontainers/runtime-spec/specs-go" ocigen "github.com/opencontainers/runtime-tools/generate" + + runc "github.com/opencontainers/runc/libcontainer/devices" ) const ( @@ -78,12 +83,44 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error { if len(e.Env) > 0 { specgen.AddMultipleProcessEnv(e.Env) } + for _, d := range e.DeviceNodes { - specgen.AddDevice(d.ToOCI()) + dev := d.ToOCI() + if err := fillMissingInfo(&dev); err != nil { + return err + } + + if dev.UID == nil && spec.Process != nil { + if uid := spec.Process.User.UID; uid > 0 { + dev.UID = &uid + } + } + if dev.GID == nil && spec.Process != nil { + if gid := spec.Process.User.GID; gid > 0 { + dev.GID = &gid + } + } + + specgen.RemoveDevice(dev.Path) + specgen.AddDevice(dev) + + if dev.Type == "b" || dev.Type == "c" { + access := d.Permissions + if access == "" { + access = "rwm" + } + specgen.AddLinuxResourcesDevice(true, dev.Type, &dev.Major, &dev.Minor, access) + } } - for _, m := range e.Mounts { - specgen.AddMount(m.ToOCI()) + + if len(e.Mounts) > 0 { + for _, m := range e.Mounts { + specgen.RemoveMount(m.ContainerPath) + specgen.AddMount(m.ToOCI()) + } + sortMounts(&specgen) } + for _, h := range e.Hooks { switch h.HookName { case PrestartHook: @@ -138,6 +175,27 @@ func (e *ContainerEdits) Validate() error { return nil } +// Append other edits into this one. If called with a nil receiver, +// allocates and returns newly allocated edits. +func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits { + if o == nil || o.ContainerEdits == nil { + return e + } + if e == nil { + e = &ContainerEdits{} + } + if e.ContainerEdits == nil { + e.ContainerEdits = &specs.ContainerEdits{} + } + + e.Env = append(e.Env, o.Env...) + e.DeviceNodes = append(e.DeviceNodes, o.DeviceNodes...) + e.Hooks = append(e.Hooks, o.Hooks...) + e.Mounts = append(e.Mounts, o.Mounts...) + + return e +} + // isEmpty returns true if these edits are empty. This is valid in a // global Spec context but invalid in a Device context. func (e *ContainerEdits) isEmpty() bool { @@ -164,10 +222,18 @@ type DeviceNode struct { // Validate a CDI Spec DeviceNode. func (d *DeviceNode) Validate() error { + validTypes := map[string]struct{}{ + "": {}, + "b": {}, + "c": {}, + "u": {}, + "p": {}, + } + if d.Path == "" { return errors.New("invalid (empty) device path") } - if d.Type != "" && d.Type != "b" && d.Type != "c" { + if _, ok := validTypes[d.Type]; !ok { return errors.Errorf("device %q: invalid type %q", d.Path, d.Type) } for _, bit := range d.Permissions { @@ -220,3 +286,72 @@ func ensureOCIHooks(spec *oci.Spec) { spec.Hooks = &oci.Hooks{} } } + +// fillMissingInfo fills in missing mandatory attributes from the host device. +func fillMissingInfo(dev *oci.LinuxDevice) error { + if dev.Type != "" && (dev.Major != 0 || dev.Type == "p") { + return nil + } + hostDev, err := runc.DeviceFromPath(dev.Path, "rwm") + if err != nil { + return errors.Wrapf(err, "failed to stat CDI host device %q", dev.Path) + } + + if dev.Type == "" { + dev.Type = string(hostDev.Type) + } else { + if dev.Type != string(hostDev.Type) { + return errors.Errorf("CDI device %q, host type mismatch (%s, %s)", + dev.Path, dev.Type, string(hostDev.Type)) + } + } + if dev.Major == 0 && dev.Type != "p" { + dev.Major = hostDev.Major + dev.Minor = hostDev.Minor + } + + return nil +} + +// sortMounts sorts the mounts in the given OCI Spec. +func sortMounts(specgen *ocigen.Generator) { + mounts := specgen.Mounts() + specgen.ClearMounts() + sort.Sort(orderedMounts(mounts)) + specgen.Config.Mounts = mounts +} + +// orderedMounts defines how to sort an OCI Spec Mount slice. +// This is the almost the same implementation sa used by CRI-O and Docker, +// with a minor tweak for stable sorting order (easier to test): +// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26 +type orderedMounts []oci.Mount + +// Len returns the number of mounts. Used in sorting. +func (m orderedMounts) Len() int { + return len(m) +} + +// Less returns true if the number of parts (a/b/c would be 3 parts) in the +// mount indexed by parameter 1 is less than that of the mount indexed by +// parameter 2. Used in sorting. +func (m orderedMounts) Less(i, j int) bool { + ip, jp := m.parts(i), m.parts(j) + if ip < jp { + return true + } + if jp < ip { + return false + } + return m[i].Destination < m[j].Destination +} + +// Swap swaps two items in an array of mounts. Used in sorting +func (m orderedMounts) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} + +// parts returns the number of parts in the destination of a mount. Used in sorting. +func (m orderedMounts) parts(i int) int { + return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) +} -- cgit v1.2.3-54-g00ecf