Skip to content

Commit

Permalink
pkg/cdi: sketch up an updated API proposal.
Browse files Browse the repository at this point in the history
Sketch up a proposal for updating the API. This should be
better suited for doing CDI device resolution/injection in
runtimes. The API now supports (almost, sans TODO's)

  - CDI Spec/device caching
  - manual cache refresh
  - automatic cache refresh (TODO)
  - device filtering (for vendor, class, and/or name)
  - device enumeration (filtered listing of devices)
  - device injection to OCI Spec
  - resolving devices in OCI Spec (CDI device in OCI device path)
  - injection of 'all' enumerated devices to OCI Spec (TODO)

Signed-off-by: Krisztian Litkey <krisztian.litkey@intel.com>
  • Loading branch information
klihub committed Nov 28, 2021
1 parent 7f4792f commit 28db497
Show file tree
Hide file tree
Showing 11 changed files with 3,147 additions and 0 deletions.
556 changes: 556 additions & 0 deletions pkg/cdi/cache.go

Large diffs are not rendered by default.

1,089 changes: 1,089 additions & 0 deletions pkg/cdi/cache_test.go

Large diffs are not rendered by default.

150 changes: 150 additions & 0 deletions pkg/cdi/container-edits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package cdi

import (
"strings"

specs "github.com/container-orchestrated-devices/container-device-interface/specs-go"
oci "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"

//"github.com/opencontainers/runtime-tools/generate"

"github.com/pkg/errors"
)

// ContainerEdits represent updates to be applied to an OCI Spec.
type ContainerEdits struct {
*specs.ContainerEdits
}

// Apply edits to the given OCI Spec.
func (e *ContainerEdits) Apply(spec *oci.Spec) error {
if spec == nil {
return errors.New("nil OCI Spec")
}
if e == nil || e.ContainerEdits == nil {
return nil
}

g := generate.NewFromSpec(spec)

if len(e.Env) > 0 {
g.AddMultipleProcessEnv(e.Env)
}
for _, d := range e.DeviceNodes {
g.AddDevice(d.ToOCI())
}
for _, m := range e.Mounts {
g.AddMount(m.ToOCI())
}
for _, h := range e.Hooks {
switch h.HookName {
case "prestart":
g.AddPreStartHook(h.ToOCI())
case "poststart":
g.AddPostStartHook(h.ToOCI())
case "poststop":
g.AddPostStopHook(h.ToOCI())
// TODO: Runtime-tools should be updated to support these, too.
case "createRuntime":
ensureSpecHooks(spec)
spec.Hooks.CreateRuntime = append(spec.Hooks.CreateRuntime, h.ToOCI())
case "createContainer":
ensureSpecHooks(spec)
spec.Hooks.CreateContainer = append(spec.Hooks.CreateContainer, h.ToOCI())
case "startContainer":
ensureSpecHooks(spec)
spec.Hooks.StartContainer = append(spec.Hooks.StartContainer, h.ToOCI())
}
}

return nil
}

// Validate basic sanity of the edits.
func (e *ContainerEdits) Validate() error {
if err := e.validateEnv(e.Env); err != nil {
return errors.Wrap(err, "invalid container edits")
}
if err := e.validateDevices(); err != nil {
return errors.Wrap(err, "invalid container edits")
}
if err := e.validateHooks(); err != nil {
return errors.Wrap(err, "invalid container edits")
}
if err := e.validateMounts(); err != nil {
return errors.Wrap(err, "invalid container edits")
}

return nil
}

func (e *ContainerEdits) validateEnv(env []string) error {
for _, v := range env {
if strings.IndexByte(v, byte('=')) <= 0 {
return errors.Errorf("invalid environment variable %q", v)
}
}

return nil
}

func (e *ContainerEdits) validateDevices() error {
for _, d := range e.DeviceNodes {
if d.Path == "" {
return errors.New("invalid (empty) device path")
}
if d.Type != "" && d.Type != "b" && d.Type != "c" {
return errors.Errorf("device %q: invalid type %q", d.Path, d.Type)
}
for _, c := range d.Permissions {
if c != 'r' && c != 'w' && c != 'm' {
return errors.Errorf("device %q: invalid permissions %q", d.Path, d.Permissions)
}
}
}

return nil
}

func (e *ContainerEdits) validateHooks() error {
for _, h := range e.Hooks {
switch h.HookName {
case "Prestart":
case "CreateRuntime":
case "CreateContainer":
case "StartContainer":
case "PostStart":
case "PostStop":
default:
return errors.Errorf("invalid hook name %q", h.HookName)
}
if h.Path == "" {
return errors.Errorf("invalid hook %q with empty path", h.HookName)
}
if err := e.validateEnv(h.Env); err != nil {
return errors.Wrapf(err, "invalid hook %q", h.HookName)
}
}

return nil
}

func (e *ContainerEdits) validateMounts() error {
for _, m := range e.Mounts {
if m.HostPath == "" {
return errors.New("invalid mount, empty host path")
}
if m.ContainerPath == "" {
return errors.New("invalid mount, empty container path")
}
}

return nil
}

func ensureSpecHooks(spec *oci.Spec) {
if spec.Hooks == nil {
spec.Hooks = &oci.Hooks{}
}
}
Loading

0 comments on commit 28db497

Please sign in to comment.