mirror of
https://github.com/containerd/containerd.git
synced 2026-10-28 09:35:10 +08:00
vendor: tags.cncf.io/container-device-interface 73444d1f71f2
full diff https://github.com/cncf-tags/container-device-interface/compare/04278701a635...73444d1f71f20b3e6ff785585ec7321c79c94912 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
4 files changed
+109
-172
No files matched your search
@@ -89,7 +89,7 @@ require (
|
||||
k8s.io/cri-streaming v0.37.0
|
||||
k8s.io/klog/v2 v2.140.0
|
||||
k8s.io/streaming v0.37.0
|
||||
tags.cncf.io/container-device-interface v1.1.1-0.20260826075544-04278701a635
|
||||
tags.cncf.io/container-device-interface v1.1.1-0.20260828125525-73444d1f71f2
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -452,7 +452,7 @@ sigs.k8s.io/structured-merge-diff/v6 v6.4.2 h1:qdOxHwrl2Kaag1aQEarlYcOA9vSyGCp3C
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.4.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
tags.cncf.io/container-device-interface v1.1.1-0.20260826075544-04278701a635 h1:BSdEG36pYJ3iFfMvua9IqGV/3XgK9p32+OKH+bS9Ubw=
|
||||
tags.cncf.io/container-device-interface v1.1.1-0.20260826075544-04278701a635/go.mod h1:HA0pPeZss+nHZlCo/jQcm8aWXfHLZfIH9U7pGokV/V4=
|
||||
tags.cncf.io/container-device-interface v1.1.1-0.20260828125525-73444d1f71f2 h1:E9YJ+rESW1Lg8DfvSVXm1oekEvUiYnRkvQ7TyRiVwno=
|
||||
tags.cncf.io/container-device-interface v1.1.1-0.20260828125525-73444d1f71f2/go.mod h1:JoJO4/GFKHbYPani2CvVN6GqySLEwmKIPay9IgfhwZE=
|
||||
tags.cncf.io/container-device-interface/specs-go v1.1.0 h1:QRZVeAceQM+zTZe12eyfuJuuzp524EKYwhmvLd+h+yQ=
|
||||
tags.cncf.io/container-device-interface/specs-go v1.1.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ=
|
||||
Vendored
+2
-2
@@ -1018,8 +1018,8 @@ sigs.k8s.io/structured-merge-diff/v6/value
|
||||
# sigs.k8s.io/yaml v1.6.0
|
||||
## explicit; go 1.22
|
||||
sigs.k8s.io/yaml
|
||||
# tags.cncf.io/container-device-interface v1.1.1-0.20260826075544-04278701a635
|
||||
## explicit; go 1.21
|
||||
# tags.cncf.io/container-device-interface v1.1.1-0.20260828125525-73444d1f71f2
|
||||
## explicit; go 1.23
|
||||
tags.cncf.io/container-device-interface/internal/ociedit
|
||||
tags.cncf.io/container-device-interface/internal/validation
|
||||
tags.cncf.io/container-device-interface/internal/validation/k8s
|
||||
|
||||
+104
-167
@@ -20,11 +20,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -121,18 +120,17 @@ func (c *Cache) Configure(options ...Option) error {
|
||||
// Configure the Cache. Start/stop CDI Spec directory watch, refresh
|
||||
// the Cache if necessary.
|
||||
func (c *Cache) configure(options ...Option) {
|
||||
c.watch.stop()
|
||||
c.dirErrors = nil
|
||||
|
||||
for _, o := range options {
|
||||
o(c)
|
||||
}
|
||||
|
||||
c.dirErrors = make(map[string]error)
|
||||
|
||||
c.watch.stop()
|
||||
if c.autoRefresh {
|
||||
c.watch.setup(c.specDirs, c.dirErrors)
|
||||
c.watch.start(&c.mu, c.refresh, c.dirErrors)
|
||||
c.watch.start(c)
|
||||
}
|
||||
_ = c.refresh() // we record but ignore errors
|
||||
c.refresh()
|
||||
}
|
||||
|
||||
// Refresh rescans the CDI Spec directories and refreshes the Cache.
|
||||
@@ -143,12 +141,14 @@ func (c *Cache) Refresh() error {
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// force a refresh in manual mode
|
||||
if refreshed, err := c.refreshIfRequired(!c.autoRefresh); refreshed {
|
||||
return err
|
||||
if !c.autoRefresh {
|
||||
c.refresh()
|
||||
} else {
|
||||
c.refreshIfRequired()
|
||||
}
|
||||
|
||||
// collect and return cached errors, much like refresh() does it
|
||||
errs := []error{}
|
||||
// collect and return cached errors.
|
||||
var errs []error
|
||||
for _, specErrs := range c.errors {
|
||||
errs = append(errs, errors.Join(specErrs...))
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (c *Cache) Refresh() error {
|
||||
}
|
||||
|
||||
// Refresh the Cache by rescanning CDI Spec directories and files.
|
||||
func (c *Cache) refresh() error {
|
||||
func (c *Cache) refresh() {
|
||||
var (
|
||||
specs = map[string][]*Spec{}
|
||||
devices = map[string]*Device{}
|
||||
@@ -216,23 +216,14 @@ func (c *Cache) refresh() error {
|
||||
c.specs = specs
|
||||
c.devices = devices
|
||||
c.errors = specErrors
|
||||
|
||||
errs := []error{}
|
||||
for _, specErrs := range specErrors {
|
||||
errs = append(errs, errors.Join(specErrs...))
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// RefreshIfRequired triggers a refresh if necessary.
|
||||
func (c *Cache) refreshIfRequired(force bool) (bool, error) {
|
||||
// We need to refresh if
|
||||
// - it's forced by an explicit call to Refresh() in manual mode
|
||||
// - a missing Spec dir appears (added to watch) in auto-refresh mode
|
||||
if force || (c.autoRefresh && c.watch.update(c.dirErrors)) {
|
||||
return true, c.refresh()
|
||||
// refreshIfRequired triggers a refresh if necessary.
|
||||
func (c *Cache) refreshIfRequired() {
|
||||
// We need to refresh if a missing Spec dir appears (added to watch) in auto-refresh mode.
|
||||
if c.autoRefresh && c.watch.update(c.dirErrors) {
|
||||
c.refresh()
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// InjectDevices injects the given qualified devices to an OCI Spec. It
|
||||
@@ -245,7 +236,7 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
c.refreshIfRequired()
|
||||
cachedDevices := c.devices
|
||||
c.mu.Unlock()
|
||||
|
||||
@@ -297,25 +288,17 @@ func (c *Cache) highestPrioritySpecDir() (string, int) {
|
||||
// priority Spec directory. If name has a "json" or "yaml" extension it
|
||||
// choses the encoding. Otherwise the default YAML encoding is used.
|
||||
func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error {
|
||||
var (
|
||||
specDir string
|
||||
path string
|
||||
prio int
|
||||
spec *Spec
|
||||
err error
|
||||
)
|
||||
|
||||
specDir, prio = c.highestPrioritySpecDir()
|
||||
specDir, prio := c.highestPrioritySpecDir()
|
||||
if specDir == "" {
|
||||
return errors.New("no Spec directories to write to")
|
||||
}
|
||||
|
||||
path = filepath.Join(specDir, name)
|
||||
path := filepath.Join(specDir, name)
|
||||
if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
|
||||
path += defaultSpecExt
|
||||
}
|
||||
|
||||
spec, err = newSpec(raw, path, prio)
|
||||
spec, err := newSpec(raw, path, prio)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -328,25 +311,19 @@ func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error {
|
||||
// Spec previously written by WriteSpec(). If the file exists and
|
||||
// its removal fails RemoveSpec returns an error.
|
||||
func (c *Cache) RemoveSpec(name string) error {
|
||||
var (
|
||||
specDir string
|
||||
path string
|
||||
err error
|
||||
)
|
||||
|
||||
specDir, _ = c.highestPrioritySpecDir()
|
||||
specDir, _ := c.highestPrioritySpecDir()
|
||||
if specDir == "" {
|
||||
return errors.New("no Spec directories to remove from")
|
||||
}
|
||||
|
||||
path = filepath.Join(specDir, name)
|
||||
path := filepath.Join(specDir, name)
|
||||
if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
|
||||
path += defaultSpecExt
|
||||
}
|
||||
|
||||
err = os.Remove(path)
|
||||
if err != nil && errors.Is(err, fs.ErrNotExist) {
|
||||
err = nil
|
||||
err := os.Remove(path)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
@@ -359,7 +336,7 @@ func (c *Cache) GetDevice(device string) *Device {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
c.refreshIfRequired()
|
||||
|
||||
return c.devices[device]
|
||||
}
|
||||
@@ -367,61 +344,46 @@ func (c *Cache) GetDevice(device string) *Device {
|
||||
// ListDevices lists all cached devices by qualified name. Might trigger a cache
|
||||
// refresh, in which case any errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) ListDevices() []string {
|
||||
var devices []string
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
c.refreshIfRequired()
|
||||
|
||||
for name := range c.devices {
|
||||
devices = append(devices, name)
|
||||
}
|
||||
sort.Strings(devices)
|
||||
|
||||
return devices
|
||||
return slices.Sorted(maps.Keys(c.devices))
|
||||
}
|
||||
|
||||
// ListVendors lists all vendors known to the cache. Might trigger a cache refresh,
|
||||
// in which case any errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) ListVendors() []string {
|
||||
var vendors []string
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
c.refreshIfRequired()
|
||||
|
||||
for vendor := range c.specs {
|
||||
vendors = append(vendors, vendor)
|
||||
}
|
||||
sort.Strings(vendors)
|
||||
|
||||
return vendors
|
||||
return slices.Sorted(maps.Keys(c.specs))
|
||||
}
|
||||
|
||||
// ListClasses lists all device classes known to the cache. Might trigger a cache
|
||||
// refresh, in which case any errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) ListClasses() []string {
|
||||
var (
|
||||
cmap = map[string]struct{}{}
|
||||
classes []string
|
||||
)
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
c.refreshIfRequired()
|
||||
|
||||
var classes []string
|
||||
seen := map[string]struct{}{}
|
||||
for _, specs := range c.specs {
|
||||
for _, spec := range specs {
|
||||
cmap[spec.GetClass()] = struct{}{}
|
||||
class := spec.GetClass()
|
||||
if _, ok := seen[class]; ok {
|
||||
continue
|
||||
}
|
||||
seen[class] = struct{}{}
|
||||
classes = append(classes, class)
|
||||
}
|
||||
}
|
||||
for class := range cmap {
|
||||
classes = append(classes, class)
|
||||
}
|
||||
sort.Strings(classes)
|
||||
slices.Sort(classes)
|
||||
|
||||
return classes
|
||||
}
|
||||
@@ -432,7 +394,7 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
c.refreshIfRequired()
|
||||
|
||||
return c.specs[vendor]
|
||||
}
|
||||
@@ -442,12 +404,7 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
|
||||
func (c *Cache) GetSpecErrors(spec *Spec) []error {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
var errs []error
|
||||
if e, ok := c.errors[spec.GetPath()]; ok {
|
||||
errs = make([]error, len(e))
|
||||
copy(errs, e)
|
||||
}
|
||||
return errs
|
||||
return slices.Clone(c.errors[spec.GetPath()])
|
||||
}
|
||||
|
||||
// GetErrors returns all errors encountered during the last
|
||||
@@ -471,25 +428,14 @@ func (c *Cache) GetErrors() map[string][]error {
|
||||
func (c *Cache) GetSpecDirectories() []string {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
dirs := make([]string, len(c.specDirs))
|
||||
copy(dirs, c.specDirs)
|
||||
return dirs
|
||||
return slices.Clone(c.specDirs)
|
||||
}
|
||||
|
||||
// GetSpecDirErrors returns any errors related to configured Spec directories.
|
||||
func (c *Cache) GetSpecDirErrors() map[string]error {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
if c.dirErrors == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs := make(map[string]error)
|
||||
for dir, err := range c.dirErrors {
|
||||
errs[dir] = err
|
||||
}
|
||||
return errs
|
||||
return maps.Clone(c.dirErrors)
|
||||
}
|
||||
|
||||
// Our fsnotify helper wrapper.
|
||||
@@ -498,31 +444,30 @@ type watch struct {
|
||||
tracked map[string]bool
|
||||
}
|
||||
|
||||
// Setup monitoring for the given Spec directories.
|
||||
func (w *watch) setup(dirs []string, dirErrors map[string]error) {
|
||||
var (
|
||||
dir string
|
||||
err error
|
||||
)
|
||||
w.tracked = make(map[string]bool)
|
||||
for _, dir = range dirs {
|
||||
w.tracked[dir] = false
|
||||
}
|
||||
|
||||
w.watcher, err = fsnotify.NewWatcher()
|
||||
// Start watching the Spec directories configured in the cache, recording
|
||||
// watch errors and triggering cache refreshes as directories change.
|
||||
func (w *watch) start(c *Cache) {
|
||||
c.dirErrors = make(map[string]error)
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
for _, dir := range dirs {
|
||||
dirErrors[dir] = fmt.Errorf("failed to create watcher: %w", err)
|
||||
for _, dir := range c.specDirs {
|
||||
c.dirErrors[dir] = fmt.Errorf("failed to create watcher: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.update(dirErrors)
|
||||
}
|
||||
w.watcher = watcher
|
||||
w.tracked = make(map[string]bool, len(c.specDirs))
|
||||
for _, dir := range c.specDirs {
|
||||
if err := watcher.Add(dir); err != nil {
|
||||
c.dirErrors[dir] = fmt.Errorf("failed to monitor for changes: %w", err)
|
||||
w.tracked[dir] = false
|
||||
continue
|
||||
}
|
||||
w.tracked[dir] = true
|
||||
}
|
||||
|
||||
// Start watching Spec directories for relevant changes.
|
||||
func (w *watch) start(m sync.Locker, refresh func() error, dirErrors map[string]error) {
|
||||
go w.watch(w.watcher, m, refresh, dirErrors)
|
||||
go w.watch(watcher, c)
|
||||
}
|
||||
|
||||
// Stop watching directories.
|
||||
@@ -532,48 +477,45 @@ func (w *watch) stop() {
|
||||
}
|
||||
|
||||
_ = w.watcher.Close()
|
||||
w.watcher = nil
|
||||
w.tracked = nil
|
||||
}
|
||||
|
||||
// Watch Spec directory changes, triggering a refresh if necessary.
|
||||
func (w *watch) watch(fsw *fsnotify.Watcher, m sync.Locker, refresh func() error, dirErrors map[string]error) {
|
||||
watch := fsw
|
||||
if watch == nil {
|
||||
return
|
||||
}
|
||||
|
||||
eventMask := fsnotify.Rename | fsnotify.Remove | fsnotify.Write
|
||||
// On macOS, we also need to watch for Create events.
|
||||
if runtime.GOOS == "darwin" {
|
||||
eventMask |= fsnotify.Create
|
||||
}
|
||||
func (w *watch) watch(fsw *fsnotify.Watcher, c *Cache) {
|
||||
// Watch for Spec file changes. Atomic writes may create a temporary file and
|
||||
// rename it into place. On Linux, fsnotify reports the destination of such a
|
||||
// rename as a Create event, so Create must be watched on all platforms.
|
||||
eventMask := fsnotify.Create | fsnotify.Rename | fsnotify.Remove | fsnotify.Write
|
||||
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watch.Events:
|
||||
case event, ok := <-fsw.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.Op & eventMask) == 0 {
|
||||
fsOp := event.Op & eventMask
|
||||
if fsOp == 0 {
|
||||
continue
|
||||
}
|
||||
if event.Op == fsnotify.Write || event.Op == fsnotify.Create {
|
||||
if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
if event.Op == fsnotify.Remove && w.tracked[event.Name] {
|
||||
w.update(dirErrors, event.Name)
|
||||
} else {
|
||||
w.update(dirErrors)
|
||||
}
|
||||
_ = refresh()
|
||||
m.Unlock()
|
||||
c.mu.Lock()
|
||||
_, isTracked := w.tracked[event.Name]
|
||||
|
||||
case _, ok := <-watch.Errors:
|
||||
// Ignore changes unrelated to Spec files or configured Spec directories.
|
||||
if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" && !isTracked {
|
||||
c.mu.Unlock()
|
||||
continue
|
||||
}
|
||||
if fsOp&fsnotify.Remove != 0 && isTracked {
|
||||
w.markRemoved(c.dirErrors, event.Name)
|
||||
}
|
||||
w.update(c.dirErrors)
|
||||
c.refresh()
|
||||
c.mu.Unlock()
|
||||
|
||||
case _, ok := <-fsw.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -581,15 +523,16 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m sync.Locker, refresh func() error
|
||||
}
|
||||
}
|
||||
|
||||
// Update watch with pending/missing or removed directories.
|
||||
func (w *watch) update(dirErrors map[string]error, removed ...string) bool {
|
||||
var (
|
||||
dir string
|
||||
ok bool
|
||||
err error
|
||||
update bool
|
||||
)
|
||||
// markRemoved marks a configured Spec directory as not currently watched so
|
||||
// its watch can be restored if the directory is recreated.
|
||||
func (w *watch) markRemoved(dirErrors map[string]error, dir string) {
|
||||
w.tracked[dir] = false
|
||||
dirErrors[dir] = errors.New("directory removed")
|
||||
}
|
||||
|
||||
// update restores watches for configured directories that are not currently
|
||||
// being watched. It reports whether any watches were restored.
|
||||
func (w *watch) update(dirErrors map[string]error) bool {
|
||||
// If we failed to create an fsnotify.Watcher we have a nil watcher here
|
||||
// (but with autoRefresh left on). One known case when this can happen is
|
||||
// if we have too many open files. In that case we always return true and
|
||||
@@ -598,25 +541,19 @@ func (w *watch) update(dirErrors map[string]error, removed ...string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
for dir, ok = range w.tracked {
|
||||
if ok {
|
||||
var update bool
|
||||
for dir, watched := range w.tracked {
|
||||
if watched {
|
||||
continue
|
||||
}
|
||||
|
||||
err = w.watcher.Add(dir)
|
||||
if err == nil {
|
||||
w.tracked[dir] = true
|
||||
delete(dirErrors, dir)
|
||||
update = true
|
||||
} else {
|
||||
w.tracked[dir] = false
|
||||
if err := w.watcher.Add(dir); err != nil {
|
||||
dirErrors[dir] = fmt.Errorf("failed to monitor for changes: %w", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir = range removed {
|
||||
w.tracked[dir] = false
|
||||
dirErrors[dir] = errors.New("directory removed")
|
||||
w.tracked[dir] = true
|
||||
delete(dirErrors, dir)
|
||||
update = true
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user