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:
Sebastiaan van Stijn committed 2026-08-28 15:27:36 +02:00
1 parent 1335870fbc
commit ea60594619
4 files changed
+109 -172

No files matched your search

+1 -1
View File
@@ -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 (
+2 -2
View File
@@ -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=
+2 -2
View File
@@ -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
View File
@@ -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
}