diff --git a/go.mod b/go.mod
index 654a25c42..318043c34 100644
--- a/go.mod
+++ b/go.mod
@@ -14,6 +14,7 @@ require (
 	golang.org/x/mod v0.22.0
 	golang.org/x/sys v0.29.0
 	tags.cncf.io/container-device-interface v0.8.0
+	tags.cncf.io/container-device-interface/api/producer v0.0.0
 	tags.cncf.io/container-device-interface/specs-go v0.8.0
 )
 
@@ -39,6 +40,6 @@ require (
 
 replace (
 	tags.cncf.io/container-device-interface => ../container-device-interface
-	tags.cncf.io/container-device-interface/specs-go => ../container-device-interface/specs-go
 	tags.cncf.io/container-device-interface/api/producer => ../container-device-interface/api/producer
+	tags.cncf.io/container-device-interface/specs-go => ../container-device-interface/specs-go
 )
diff --git a/go.sum b/go.sum
index 53824f861..ee71b1c82 100644
--- a/go.sum
+++ b/go.sum
@@ -88,7 +88,3 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
-tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
-tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
-tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
-tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
diff --git a/pkg/nvcdi/spec/builder.go b/pkg/nvcdi/spec/builder.go
index b7dffd98f..6b034a9c8 100644
--- a/pkg/nvcdi/spec/builder.go
+++ b/pkg/nvcdi/spec/builder.go
@@ -20,27 +20,28 @@ import (
 	"fmt"
 	"os"
 
-	"tags.cncf.io/container-device-interface/pkg/cdi"
+	"tags.cncf.io/container-device-interface/api/producer"
 	"tags.cncf.io/container-device-interface/pkg/parser"
-	"tags.cncf.io/container-device-interface/specs-go"
+	cdi "tags.cncf.io/container-device-interface/specs-go"
 
 	"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
 )
 
 type builder struct {
-	raw         *specs.Spec
+	raw         *cdi.Spec
 	version     string
 	vendor      string
 	class       string
-	deviceSpecs []specs.Device
-	edits       specs.ContainerEdits
+	deviceSpecs []cdi.Device
+	edits       cdi.ContainerEdits
 	format      string
 
 	mergedDeviceOptions []transform.MergedDeviceOption
 	noSimplify          bool
 	permissions         os.FileMode
 
-	transformOnSave transform.Transformer
+	detectMinimumVersion bool
+	transformOnSave      transform.Transformer
 }
 
 // newBuilder creates a new spec builder with the supplied options
@@ -64,7 +65,7 @@ func newBuilder(opts ...Option) *builder {
 		}
 	}
 	if s.version == "" || s.version == DetectMinimumVersion {
-		s.transformOnSave = &setMinimumRequiredVersion{}
+		s.detectMinimumVersion = true
 		s.version = cdi.CurrentVersion
 	}
 	if s.vendor == "" {
@@ -86,7 +87,7 @@ func newBuilder(opts ...Option) *builder {
 func (o *builder) Build() (*spec, error) {
 	raw := o.raw
 	if raw == nil {
-		raw = &specs.Spec{
+		raw = &cdi.Spec{
 			Version:        o.version,
 			Kind:           fmt.Sprintf("%s/%s", o.vendor, o.class),
 			Devices:        o.deviceSpecs,
@@ -114,11 +115,26 @@ func (o *builder) Build() (*spec, error) {
 		}
 	}
 
+	options := []producer.Option{
+		producer.WithDetectMinimumVersion(o.detectMinimumVersion),
+		producer.WithPermissions(o.permissions),
+	}
+	switch o.format {
+	case FormatJSON:
+		options = append(options, producer.WithSpecFormat(producer.SpecFormatJSON))
+	case FormatYAML:
+		options = append(options, producer.WithSpecFormat(producer.SpecFormatYAML))
+	}
+
+	producer, err := producer.NewSpecWriter(options...)
+	if err != nil {
+		return nil, err
+	}
+
 	s := spec{
-		Spec:            raw,
-		format:          o.format,
-		permissions:     o.permissions,
+		raw:             raw,
 		transformOnSave: o.transformOnSave,
+		SpecWriter:      producer,
 	}
 	return &s, nil
 }
@@ -127,14 +143,14 @@ func (o *builder) Build() (*spec, error) {
 type Option func(*builder)
 
 // WithDeviceSpecs sets the device specs for the spec builder
-func WithDeviceSpecs(deviceSpecs []specs.Device) Option {
+func WithDeviceSpecs(deviceSpecs []cdi.Device) Option {
 	return func(o *builder) {
 		o.deviceSpecs = deviceSpecs
 	}
 }
 
 // WithEdits sets the container edits for the spec builder
-func WithEdits(edits specs.ContainerEdits) Option {
+func WithEdits(edits cdi.ContainerEdits) Option {
 	return func(o *builder) {
 		o.edits = edits
 	}
@@ -176,7 +192,7 @@ func WithNoSimplify(noSimplify bool) Option {
 }
 
 // WithRawSpec sets the raw spec for the spec builder
-func WithRawSpec(raw *specs.Spec) Option {
+func WithRawSpec(raw *cdi.Spec) Option {
 	return func(o *builder) {
 		o.raw = raw
 	}
diff --git a/pkg/nvcdi/spec/set-minimum-version.go b/pkg/nvcdi/spec/set-minimum-version.go
deleted file mode 100644
index 69969c0b5..000000000
--- a/pkg/nvcdi/spec/set-minimum-version.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
-# Copyright 2024 NVIDIA CORPORATION
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-**/
-
-package spec
-
-import (
-	"fmt"
-
-	"tags.cncf.io/container-device-interface/pkg/cdi"
-	"tags.cncf.io/container-device-interface/specs-go"
-)
-
-type setMinimumRequiredVersion struct{}
-
-func (d setMinimumRequiredVersion) Transform(spec *specs.Spec) error {
-	minVersion, err := cdi.MinimumRequiredVersion(spec)
-	if err != nil {
-		return fmt.Errorf("failed to get minimum required CDI spec version: %v", err)
-	}
-	spec.Version = minVersion
-	return nil
-}
diff --git a/pkg/nvcdi/spec/spec.go b/pkg/nvcdi/spec/spec.go
index 28cccc518..75f3d1dbc 100644
--- a/pkg/nvcdi/spec/spec.go
+++ b/pkg/nvcdi/spec/spec.go
@@ -17,22 +17,18 @@
 package spec
 
 import (
-	"fmt"
 	"io"
-	"os"
-	"path/filepath"
 
-	"tags.cncf.io/container-device-interface/pkg/cdi"
-	"tags.cncf.io/container-device-interface/specs-go"
+	"tags.cncf.io/container-device-interface/api/producer"
+	cdi "tags.cncf.io/container-device-interface/specs-go"
 
 	"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
 )
 
 type spec struct {
-	*specs.Spec
-	format          string
-	permissions     os.FileMode
+	raw             *cdi.Spec
 	transformOnSave transform.Transformer
+	*producer.SpecWriter
 }
 
 var _ Interface = (*spec)(nil)
@@ -42,96 +38,32 @@ func New(opts ...Option) (Interface, error) {
 	return newBuilder(opts...).Build()
 }
 
+// Raw returns a pointer to the raw spec.
+func (s *spec) Raw() *cdi.Spec {
+	return s.raw
+}
+
 // Save writes the spec to the specified path and overwrites the file if it exists.
 func (s *spec) Save(path string) error {
 	if s.transformOnSave != nil {
-		err := s.transformOnSave.Transform(s.Raw())
+		err := s.transformOnSave.Transform(s.raw)
 		if err != nil {
-			return fmt.Errorf("error applying transform: %w", err)
+			return err
 		}
 	}
-	path, err := s.normalizePath(path)
-	if err != nil {
-		return fmt.Errorf("failed to normalize path: %w", err)
-	}
-
-	specDir := filepath.Dir(path)
-	cache, _ := cdi.NewCache(
-		cdi.WithAutoRefresh(false),
-		cdi.WithSpecDirs(specDir),
-	)
-	if err := cache.WriteSpec(s.Raw(), filepath.Base(path)); err != nil {
-		return fmt.Errorf("failed to write spec: %w", err)
-	}
-
-	if err := os.Chmod(path, s.permissions); err != nil {
-		return fmt.Errorf("failed to set permissions on spec file: %w", err)
-	}
-
-	return nil
+	// TODO: We should add validation here.
+	_, err := s.SpecWriter.Save(s.raw, path)
+	return err
 }
 
-// WriteTo writes the spec to the specified writer.
+// WriteTo writes the configured spec to the specified writer.
 func (s *spec) WriteTo(w io.Writer) (int64, error) {
-	name, err := cdi.GenerateNameForSpec(s.Raw())
-	if err != nil {
-		return 0, err
-	}
-
-	path, _ := s.normalizePath(name)
-	tmpFile, err := os.CreateTemp("", "*"+filepath.Base(path))
-	if err != nil {
-		return 0, err
-	}
-	defer os.Remove(tmpFile.Name())
-
-	if err := s.Save(tmpFile.Name()); err != nil {
-		return 0, err
-	}
-
-	err = tmpFile.Close()
-	if err != nil {
-		return 0, fmt.Errorf("failed to close temporary file: %w", err)
-	}
-
-	r, err := os.Open(tmpFile.Name())
-	if err != nil {
-		return 0, fmt.Errorf("failed to open temporary file: %w", err)
-	}
-	defer r.Close()
-
-	return io.Copy(w, r)
-}
-
-// Raw returns a pointer to the raw spec.
-func (s *spec) Raw() *specs.Spec {
-	return s.Spec
-}
-
-// normalizePath ensures that the specified path has a supported extension
-func (s *spec) normalizePath(path string) (string, error) {
-	if ext := filepath.Ext(path); ext != ".yaml" && ext != ".json" {
-		path += s.extension()
-	}
-
-	if filepath.Clean(filepath.Dir(path)) == "." {
-		pwd, err := os.Getwd()
+	if s.transformOnSave != nil {
+		err := s.transformOnSave.Transform(s.raw)
 		if err != nil {
-			return path, fmt.Errorf("failed to get current working directory: %v", err)
+			return 0, err
 		}
-		path = filepath.Join(pwd, path)
-	}
-
-	return path, nil
-}
-
-func (s *spec) extension() string {
-	switch s.format {
-	case FormatJSON:
-		return ".json"
-	case FormatYAML:
-		return ".yaml"
 	}
-
-	return ".yaml"
+	// TODO: We should add validation here.
+	return s.SpecWriter.WriteSpecTo(s.raw, w)
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 75dfeeaf5..065b1d4c7 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -87,12 +87,18 @@ gopkg.in/yaml.v3
 # sigs.k8s.io/yaml v1.3.0
 ## explicit; go 1.12
 sigs.k8s.io/yaml
-# tags.cncf.io/container-device-interface v0.8.0
+# tags.cncf.io/container-device-interface v0.8.0 => ../container-device-interface
 ## explicit; go 1.20
 tags.cncf.io/container-device-interface/internal/validation
 tags.cncf.io/container-device-interface/internal/validation/k8s
 tags.cncf.io/container-device-interface/pkg/cdi
 tags.cncf.io/container-device-interface/pkg/parser
-# tags.cncf.io/container-device-interface/specs-go v0.8.0
+# tags.cncf.io/container-device-interface/api/producer v0.0.0 => ../container-device-interface/api/producer
+## explicit; go 1.20
+tags.cncf.io/container-device-interface/api/producer
+# tags.cncf.io/container-device-interface/specs-go v0.8.0 => ../container-device-interface/specs-go
 ## explicit; go 1.19
 tags.cncf.io/container-device-interface/specs-go
+# tags.cncf.io/container-device-interface => ../container-device-interface
+# tags.cncf.io/container-device-interface/api/producer => ../container-device-interface/api/producer
+# tags.cncf.io/container-device-interface/specs-go => ../container-device-interface/specs-go
diff --git a/vendor/tags.cncf.io/container-device-interface/api/producer/api.go b/vendor/tags.cncf.io/container-device-interface/api/producer/api.go
new file mode 100644
index 000000000..7845b2e98
--- /dev/null
+++ b/vendor/tags.cncf.io/container-device-interface/api/producer/api.go
@@ -0,0 +1,34 @@
+/*
+   Copyright © 2024 The CDI Authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package producer
+
+import cdi "tags.cncf.io/container-device-interface/specs-go"
+
+const (
+	// DefaultSpecFormat defines the default encoding used to write CDI specs.
+	DefaultSpecFormat = SpecFormatYAML
+
+	// SpecFormatJSON defines a CDI spec formatted as JSON.
+	SpecFormatJSON = SpecFormat(".json")
+	// SpecFormatYAML defines a CDI spec formatted as YAML.
+	SpecFormatYAML = SpecFormat(".yaml")
+)
+
+// A SpecValidator is used to validate a CDI spec.
+type SpecValidator interface {
+	Validate(*cdi.Spec) error
+}
diff --git a/vendor/tags.cncf.io/container-device-interface/api/producer/options.go b/vendor/tags.cncf.io/container-device-interface/api/producer/options.go
new file mode 100644
index 000000000..c8f0b1efa
--- /dev/null
+++ b/vendor/tags.cncf.io/container-device-interface/api/producer/options.go
@@ -0,0 +1,70 @@
+/*
+   Copyright © 2024 The CDI Authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package producer
+
+import (
+	"fmt"
+	"io/fs"
+)
+
+// An Option defines a functional option for constructing a producer.
+type Option func(*options) error
+
+type options struct {
+	specFormat           SpecFormat
+	overwrite            bool
+	permissions          fs.FileMode
+	detectMinimumVersion bool
+}
+
+// WithDetectMinimumVersion toggles whether a minimum version should be detected for a CDI specification.
+func WithDetectMinimumVersion(detectMinimumVersion bool) Option {
+	return func(o *options) error {
+		o.detectMinimumVersion = detectMinimumVersion
+		return nil
+	}
+}
+
+// WithSpecFormat sets the output format of a CDI specification.
+func WithSpecFormat(format SpecFormat) Option {
+	return func(o *options) error {
+		switch format {
+		case SpecFormatJSON, SpecFormatYAML:
+			o.specFormat = format
+		default:
+			return fmt.Errorf("invalid CDI spec format %v", format)
+		}
+		return nil
+	}
+}
+
+// WithOverwrite specifies whether a producer should overwrite a CDI spec when
+// saving to file.
+func WithOverwrite(overwrite bool) Option {
+	return func(o *options) error {
+		o.overwrite = overwrite
+		return nil
+	}
+}
+
+// WithPermissions sets the file mode to be used for a saved CDI spec.
+func WithPermissions(permissions fs.FileMode) Option {
+	return func(o *options) error {
+		o.permissions = permissions
+		return nil
+	}
+}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec_linux.go b/vendor/tags.cncf.io/container-device-interface/api/producer/renamein_linux.go
similarity index 98%
rename from vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec_linux.go
rename to vendor/tags.cncf.io/container-device-interface/api/producer/renamein_linux.go
index 9ad273925..7d17b2f38 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec_linux.go
+++ b/vendor/tags.cncf.io/container-device-interface/api/producer/renamein_linux.go
@@ -14,7 +14,7 @@
    limitations under the License.
 */
 
-package cdi
+package producer
 
 import (
 	"fmt"
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec_other.go b/vendor/tags.cncf.io/container-device-interface/api/producer/renamein_other.go
similarity index 98%
rename from vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec_other.go
rename to vendor/tags.cncf.io/container-device-interface/api/producer/renamein_other.go
index 285e04e27..96ba268a0 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec_other.go
+++ b/vendor/tags.cncf.io/container-device-interface/api/producer/renamein_other.go
@@ -17,7 +17,7 @@
    limitations under the License.
 */
 
-package cdi
+package producer
 
 import (
 	"os"
diff --git a/vendor/tags.cncf.io/container-device-interface/api/producer/set-minimum-version.go b/vendor/tags.cncf.io/container-device-interface/api/producer/set-minimum-version.go
new file mode 100644
index 000000000..294742042
--- /dev/null
+++ b/vendor/tags.cncf.io/container-device-interface/api/producer/set-minimum-version.go
@@ -0,0 +1,36 @@
+/*
+   Copyright © 2025 The CDI Authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package producer
+
+import (
+	"fmt"
+
+	cdi "tags.cncf.io/container-device-interface/specs-go"
+)
+
+type setMinimumRequiredVersion struct{}
+
+// Transform detects the minimum required version required for the specified
+// spec and sets the version field accordingly.
+func (d setMinimumRequiredVersion) Transform(spec *cdi.Spec) error {
+	minVersion, err := cdi.MinimumRequiredVersion(spec)
+	if err != nil {
+		return fmt.Errorf("failed to get minimum required CDI spec version: %w", err)
+	}
+	spec.Version = minVersion
+	return nil
+}
diff --git a/vendor/tags.cncf.io/container-device-interface/api/producer/spec-format.go b/vendor/tags.cncf.io/container-device-interface/api/producer/spec-format.go
new file mode 100644
index 000000000..d2900f727
--- /dev/null
+++ b/vendor/tags.cncf.io/container-device-interface/api/producer/spec-format.go
@@ -0,0 +1,106 @@
+/*
+   Copyright © 2024 The CDI Authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package producer
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"path/filepath"
+
+	"sigs.k8s.io/yaml"
+
+	cdi "tags.cncf.io/container-device-interface/specs-go"
+)
+
+// A SpecFormat defines the encoding to use when reading or writing a CDI specification.
+type SpecFormat string
+
+type specFormatter struct {
+	*cdi.Spec
+	options
+}
+
+// WriteTo writes the spec to the specified writer.
+func (p *specFormatter) WriteTo(w io.Writer) (int64, error) {
+	data, err := p.contents()
+	if err != nil {
+		return 0, fmt.Errorf("failed to marshal Spec file: %w", err)
+	}
+
+	n, err := w.Write(data)
+	return int64(n), err
+}
+
+// marshal returns the raw contents of a CDI specification.
+// No validation is performed.
+func (p SpecFormat) marshal(spec *cdi.Spec) ([]byte, error) {
+	switch p {
+	case SpecFormatYAML:
+		data, err := yaml.Marshal(spec)
+		if err != nil {
+			return nil, err
+		}
+		data = append([]byte("---\n"), data...)
+		return data, nil
+	case SpecFormatJSON:
+		return json.Marshal(spec)
+	default:
+		return nil, fmt.Errorf("undefined CDI spec format %v", p)
+	}
+}
+
+// normalizeFilename ensures that the specified filename ends in a supported extension.
+func (p SpecFormat) normalizeFilename(filename string) (string, SpecFormat) {
+	switch filepath.Ext(filename) {
+	case ".json":
+		return filename, SpecFormatJSON
+	case ".yaml":
+		return filename, SpecFormatYAML
+	default:
+		return filename + string(p), p
+	}
+}
+
+// validate performs an explicit validation of the spec.
+// This is currently a placeholder for validation that should be performed when
+// saving a spec.
+func (p *specFormatter) validate() error {
+	return nil
+}
+
+// transform applies a transform to the spec associated with the CDI spec formatter.
+// This is currently limited to detecting (and updating) the spec so that the minimum
+// CDI spec version is used, but could be extended to apply other transformations.
+func (p *specFormatter) transform() error {
+	if !p.detectMinimumVersion {
+		return nil
+	}
+	return (&setMinimumRequiredVersion{}).Transform(p.Spec)
+}
+
+// contents returns the raw contents of a CDI specification.
+// Validation is performed before marshalling the contentent based on the spec format.
+func (p *specFormatter) contents() ([]byte, error) {
+	if err := p.transform(); err != nil {
+		return nil, fmt.Errorf("failed to transform spec: %w", err)
+	}
+	if err := p.validate(); err != nil {
+		return nil, fmt.Errorf("spec validation failed: %w", err)
+	}
+	return p.specFormat.marshal(p.Spec)
+}
diff --git a/vendor/tags.cncf.io/container-device-interface/api/producer/writer.go b/vendor/tags.cncf.io/container-device-interface/api/producer/writer.go
new file mode 100644
index 000000000..e25c6bc12
--- /dev/null
+++ b/vendor/tags.cncf.io/container-device-interface/api/producer/writer.go
@@ -0,0 +1,105 @@
+/*
+   Copyright © 2024 The CDI Authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package producer
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+
+	cdi "tags.cncf.io/container-device-interface/specs-go"
+)
+
+// A SpecWriter defines a structure for outputting CDI specifications.
+type SpecWriter struct {
+	options
+}
+
+// NewSpecWriter creates a spec writer with the supplied options.
+func NewSpecWriter(opts ...Option) (*SpecWriter, error) {
+	sw := &SpecWriter{
+		options: options{
+			overwrite: true,
+			// TODO: This could be updated to 0644 to be world-readable.
+			permissions:          0600,
+			specFormat:           DefaultSpecFormat,
+			detectMinimumVersion: false,
+		},
+	}
+	for _, opt := range opts {
+		err := opt(&sw.options)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return sw, nil
+}
+
+// Save writes a CDI spec to a file with the specified name.
+// If the filename ends in a supported extension, the format implied by the
+// extension takes precedence over the format with which the SpecWriter was
+// configured.
+func (p *SpecWriter) Save(spec *cdi.Spec, filename string) (string, error) {
+	filename, outputFormat := p.specFormat.normalizeFilename(filename)
+
+	options := p.options
+	options.specFormat = outputFormat
+	specFormatter := specFormatter{
+		Spec:    spec,
+		options: options,
+	}
+
+	dir := filepath.Dir(filename)
+	if dir != "" {
+		if err := os.MkdirAll(dir, 0o755); err != nil {
+			return "", fmt.Errorf("failed to create Spec dir: %w", err)
+		}
+	}
+
+	tmp, err := os.CreateTemp(dir, "spec.*.tmp")
+	if err != nil {
+		return "", fmt.Errorf("failed to create Spec file: %w", err)
+	}
+
+	_, err = specFormatter.WriteTo(tmp)
+	tmp.Close()
+	if err != nil {
+		return "", fmt.Errorf("failed to write Spec file: %w", err)
+	}
+
+	if err := os.Chmod(tmp.Name(), p.permissions); err != nil {
+		return "", fmt.Errorf("failed to set permissions on spec file: %w", err)
+	}
+
+	err = renameIn(dir, filepath.Base(tmp.Name()), filepath.Base(filename), p.overwrite)
+	if err != nil {
+		_ = os.Remove(tmp.Name())
+		return "", fmt.Errorf("failed to write Spec file: %w", err)
+	}
+	return filename, nil
+}
+
+// WriteSpecTo writes the specified spec to the specified writer.
+func (p *SpecWriter) WriteSpecTo(spec *cdi.Spec, w io.Writer) (int64, error) {
+	specFormatter := specFormatter{
+		Spec:    spec,
+		options: p.options,
+	}
+
+	return specFormatter.WriteTo(w)
+}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
index a38b0f1bc..a596c610b 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
@@ -71,7 +71,7 @@ func ParseAnnotations(annotations map[string]string) ([]string, []string, error)
 			continue
 		}
 		for _, d := range strings.Split(value, ",") {
-			if !IsQualifiedName(d) {
+			if !parser.IsQualifiedName(d) {
 				return nil, nil, fmt.Errorf("invalid CDI device name %q", d)
 			}
 			devices = append(devices, d)
@@ -130,7 +130,7 @@ func AnnotationKey(pluginName, deviceID string) (string, error) {
 func AnnotationValue(devices []string) (string, error) {
 	value, sep := "", ""
 	for _, d := range devices {
-		if _, _, _, err := ParseQualifiedName(d); err != nil {
+		if _, _, _, err := parser.ParseQualifiedName(d); err != nil {
 			return "", err
 		}
 		value += sep + d
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
index c2f7fe346..d6455d0d7 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
@@ -28,6 +28,7 @@ import (
 
 	"github.com/fsnotify/fsnotify"
 	oci "github.com/opencontainers/runtime-spec/specs-go"
+	"tags.cncf.io/container-device-interface/api/producer"
 	cdi "tags.cncf.io/container-device-interface/specs-go"
 )
 
@@ -116,7 +117,7 @@ func (c *Cache) configure(options ...Option) {
 		c.watch.setup(c.specDirs, c.dirErrors)
 		c.watch.start(&c.Mutex, c.refresh, c.dirErrors)
 	}
-	c.refresh()
+	_ = c.refresh() // we record but ignore errors
 }
 
 // Refresh rescans the CDI Spec directories and refreshes the Cache.
@@ -222,7 +223,8 @@ func (c *Cache) refreshIfRequired(force bool) (bool, error) {
 
 // InjectDevices injects the given qualified devices to an OCI Spec. It
 // returns any unresolvable devices and an error if injection fails for
-// any of the devices.
+// any of the devices. Might trigger a cache refresh, in which case any
+// errors encountered can be obtained using GetErrors().
 func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, error) {
 	var unresolved []string
 
@@ -233,7 +235,7 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	edits := &ContainerEdits{}
 	specs := map[*Spec]struct{}{}
@@ -282,28 +284,35 @@ func (c *Cache) highestPrioritySpecDir() (string, int) {
 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()
 	if specDir == "" {
 		return errors.New("no Spec directories to write to")
 	}
 
-	path = filepath.Join(specDir, name)
-	if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
-		path += defaultSpecExt
-	}
+	path := filepath.Join(specDir, name)
 
+	// We call newSpec to perform the required validation.
+	// This also sets the extension of the path.
 	spec, err = newSpec(raw, path, prio)
 	if err != nil {
 		return err
 	}
 
-	return spec.write(true)
+	p, err := producer.NewSpecWriter(
+		producer.WithSpecFormat(producer.SpecFormatYAML),
+		producer.WithOverwrite(true),
+	)
+	if err != nil {
+		return err
+	}
+	if _, err := p.Save(spec.Spec, spec.path); err != nil {
+		return err
+	}
+	return nil
 }
 
 // RemoveSpec removes a Spec with the given name from the highest
@@ -335,24 +344,27 @@ func (c *Cache) RemoveSpec(name string) error {
 	return err
 }
 
-// GetDevice returns the cached device for the given qualified name.
+// GetDevice returns the cached device for the given qualified name. Might trigger
+// a cache refresh, in which case any errors encountered can be obtained using
+// GetErrors().
 func (c *Cache) GetDevice(device string) *Device {
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	return c.devices[device]
 }
 
-// ListDevices lists all cached devices by qualified name.
+// 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.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	for name := range c.devices {
 		devices = append(devices, name)
@@ -362,14 +374,15 @@ func (c *Cache) ListDevices() []string {
 	return devices
 }
 
-// ListVendors lists all vendors known to the cache.
+// 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.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	for vendor := range c.specs {
 		vendors = append(vendors, vendor)
@@ -379,7 +392,8 @@ func (c *Cache) ListVendors() []string {
 	return vendors
 }
 
-// ListClasses lists all device classes known to the cache.
+// 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{}{}
@@ -389,7 +403,7 @@ func (c *Cache) ListClasses() []string {
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	for _, specs := range c.specs {
 		for _, spec := range specs {
@@ -404,12 +418,13 @@ func (c *Cache) ListClasses() []string {
 	return classes
 }
 
-// GetVendorSpecs returns all specs for the given vendor.
+// GetVendorSpecs returns all specs for the given vendor. Might trigger a cache
+// refresh, in which case any errors encountered can be obtained using GetErrors().
 func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	return c.specs[vendor]
 }
@@ -544,7 +559,7 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error
 			} else {
 				w.update(dirErrors)
 			}
-			refresh()
+			_ = refresh()
 			m.Unlock()
 
 		case _, ok := <-watch.Errors:
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
index 70791666c..4744eff8f 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
@@ -26,7 +26,7 @@ import (
 
 	oci "github.com/opencontainers/runtime-spec/specs-go"
 	ocigen "github.com/opencontainers/runtime-tools/generate"
-	"tags.cncf.io/container-device-interface/specs-go"
+	cdi "tags.cncf.io/container-device-interface/specs-go"
 )
 
 const (
@@ -64,7 +64,7 @@ var (
 // to all OCI Specs where at least one devices from the CDI Spec
 // is injected.
 type ContainerEdits struct {
-	*specs.ContainerEdits
+	*cdi.ContainerEdits
 }
 
 // Apply edits to the given OCI Spec. Updates the OCI Spec in place.
@@ -205,7 +205,7 @@ func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits {
 		e = &ContainerEdits{}
 	}
 	if e.ContainerEdits == nil {
-		e.ContainerEdits = &specs.ContainerEdits{}
+		e.ContainerEdits = &cdi.ContainerEdits{}
 	}
 
 	e.Env = append(e.Env, o.Env...)
@@ -259,7 +259,7 @@ func ValidateEnv(env []string) error {
 
 // DeviceNode is a CDI Spec DeviceNode wrapper, used for validating DeviceNodes.
 type DeviceNode struct {
-	*specs.DeviceNode
+	*cdi.DeviceNode
 }
 
 // Validate a CDI Spec DeviceNode.
@@ -289,7 +289,7 @@ func (d *DeviceNode) Validate() error {
 
 // Hook is a CDI Spec Hook wrapper, used for validating hooks.
 type Hook struct {
-	*specs.Hook
+	*cdi.Hook
 }
 
 // Validate a hook.
@@ -308,7 +308,7 @@ func (h *Hook) Validate() error {
 
 // Mount is a CDI Mount wrapper, used for validating mounts.
 type Mount struct {
-	*specs.Mount
+	*cdi.Mount
 }
 
 // Validate a mount.
@@ -325,13 +325,13 @@ func (m *Mount) Validate() error {
 // IntelRdt is a CDI IntelRdt wrapper.
 // This is used for validation and conversion to OCI specifications.
 type IntelRdt struct {
-	*specs.IntelRdt
+	*cdi.IntelRdt
 }
 
 // ValidateIntelRdt validates the IntelRdt configuration.
 //
 // Deprecated: ValidateIntelRdt is deprecated use IntelRdt.Validate() instead.
-func ValidateIntelRdt(i *specs.IntelRdt) error {
+func ValidateIntelRdt(i *cdi.IntelRdt) error {
 	return (&IntelRdt{i}).Validate()
 }
 
@@ -355,7 +355,7 @@ func ensureOCIHooks(spec *oci.Spec) {
 func sortMounts(specgen *ocigen.Generator) {
 	mounts := specgen.Mounts()
 	specgen.ClearMounts()
-	sort.Sort(orderedMounts(mounts))
+	sort.Stable(orderedMounts(mounts))
 	specgen.Config.Mounts = mounts
 }
 
@@ -375,14 +375,7 @@ func (m orderedMounts) Len() int {
 // 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
+	return m.parts(i) < m.parts(j)
 }
 
 // Swap swaps two items in an array of mounts. Used in sorting
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
index 00be48dd5..80629eff7 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
@@ -52,7 +52,7 @@ func (d *Device) GetSpec() *Spec {
 
 // GetQualifiedName returns the qualified name for this device.
 func (d *Device) GetQualifiedName() string {
-	return parser.QualifiedName(d.spec.GetVendor(), d.spec.GetClass(), d.Name)
+	return d.spec.Kind + "=" + d.Name
 }
 
 // ApplyEdits applies the device-speific container edits to an OCI Spec.
@@ -67,7 +67,7 @@ func (d *Device) edits() *ContainerEdits {
 
 // Validate the device.
 func (d *Device) validate() error {
-	if err := ValidateDeviceName(d.Name); err != nil {
+	if err := parser.ValidateDeviceName(d.Name); err != nil {
 		return err
 	}
 	name := d.Name
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
index 0ea071455..d00e0d339 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
@@ -35,25 +35,11 @@
 // available and instantiated the first time it is referenced directly
 // or indirectly. The most frequently used cache functions are available
 // as identically named package level functions which operate on the
-// default cache instance. Moreover, the registry also operates on the
-// same default cache. We plan to deprecate the registry and eventually
-// remove it in a future release.
-//
-// # CDI Registry
-//
-// Note: the Registry and its related interfaces are deprecated and will
-// be removed in a future version. Please use the default cache and its
-// related package-level function instead.
-//
-// The primary interface to interact with CDI devices is the Registry. It
-// is essentially a cache of all Specs and devices discovered in standard
-// CDI directories on the host. The registry has two main functionality,
-// injecting devices into an OCI Spec and refreshing the cache of CDI
-// Specs and devices.
+// default cache instance.
 //
 // # Device Injection
 //
-// Using the Registry one can inject CDI devices into a container with code
+// Using the Cache one can inject CDI devices into a container with code
 // similar to the following snippet:
 //
 //	import (
@@ -63,13 +49,14 @@
 //	    log "github.com/sirupsen/logrus"
 //
 //	    "tags.cncf.io/container-device-interface/pkg/cdi"
-//	    oci "github.com/opencontainers/runtime-spec/specs-go"
+//	    "github.com/opencontainers/runtime-spec/specs-go"
 //	)
 //
-//	func injectCDIDevices(spec *oci.Spec, devices []string) error {
+//	func injectCDIDevices(spec *specs.Spec, devices []string) error {
 //	    log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
 //
-//	    unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices)
+//	    cache := cdi.GetDefaultCache()
+//	    unresolved, err := cache.InjectDevices(spec, devices)
 //	    if err != nil {
 //	        return fmt.Errorf("CDI device injection failed: %w", err)
 //	    }
@@ -106,17 +93,17 @@
 //	    log "github.com/sirupsen/logrus"
 //
 //	    "tags.cncf.io/container-device-interface/pkg/cdi"
-//	    oci "github.com/opencontainers/runtime-spec/specs-go"
+//	    "github.com/opencontainers/runtime-spec/specs-go"
 //	)
 //
-//	func injectCDIDevices(spec *oci.Spec, devices []string) error {
-//	    registry := cdi.GetRegistry()
+//	func injectCDIDevices(spec *specs.Spec, devices []string) error {
+//	    cache := cdi.GetDefaultCache()
 //
-//	    if err := registry.Refresh(); err != nil {
+//	    if err := cache.Refresh(); err != nil {
 //	        // Note:
 //	        //   It is up to the implementation to decide whether
 //	        //   to abort injection on errors. A failed Refresh()
-//	        //   does not necessarily render the registry unusable.
+//	        //   does not necessarily render the cache unusable.
 //	        //   For instance, a parse error in a Spec file for
 //	        //   vendor A does not have any effect on devices of
 //	        //   vendor B...
@@ -125,7 +112,7 @@
 //
 //	    log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
 //
-//	    unresolved, err := registry.InjectDevices(spec, devices)
+//	    unresolved, err := cache.InjectDevices(spec, devices)
 //	    if err != nil {
 //	        return fmt.Errorf("CDI device injection failed: %w", err)
 //	    }
@@ -192,7 +179,7 @@
 // )
 //
 //	func generateDeviceSpecs() error {
-//	    registry := cdi.GetRegistry()
+//	    cache := specs.GetDefaultCache()
 //	    spec := &specs.Spec{
 //	        Version: specs.CurrentVersion,
 //	        Kind:    vendor+"/"+class,
@@ -210,7 +197,7 @@
 //	        return fmt.Errorf("failed to generate Spec name: %w", err)
 //	    }
 //
-//	    return registry.SpecDB().WriteSpec(spec, specName)
+//	    return cache.WriteSpec(spec, specName)
 //	}
 //
 // Similarly, generating and later cleaning up transient Spec files can be
@@ -229,7 +216,7 @@
 // )
 //
 //	func generateTransientSpec(ctr Container) error {
-//	    registry := cdi.GetRegistry()
+//	    cache := specs.GetDefaultCache()
 //	    devices := getContainerDevs(ctr, vendor, class)
 //	    spec := &specs.Spec{
 //	        Version: specs.CurrentVersion,
@@ -257,21 +244,21 @@
 //	        return fmt.Errorf("failed to generate Spec name: %w", err)
 //	    }
 //
-//	    return registry.SpecDB().WriteSpec(spec, specName)
+//	    return cache.WriteSpec(spec, specName)
 //	}
 //
 //	func removeTransientSpec(ctr Container) error {
-//	    registry := cdi.GetRegistry()
+//	    cache := specs.GetDefaultCache()
 //	    transientID := getSomeSufficientlyUniqueIDForContainer(ctr)
 //	    specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID)
 //
-//	    return registry.SpecDB().RemoveSpec(specName)
+//	    return cache.RemoveSpec(specName)
 //	}
 //
 // # CDI Spec Validation
 //
 // This package performs both syntactic and semantic validation of CDI
-// Spec file data when a Spec file is loaded via the registry or using
+// Spec file data when a Spec file is loaded via the cache or using
 // the ReadSpec API function. As part of the semantic verification, the
 // Spec file is verified against the CDI Spec JSON validation schema.
 //
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go
deleted file mode 100644
index 0bdfdc166..000000000
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-   Copyright © 2021 The CDI Authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package cdi
-
-import (
-	"tags.cncf.io/container-device-interface/pkg/parser"
-)
-
-// QualifiedName returns the qualified name for a device.
-// The syntax for a qualified device names is
-//
-//	"<vendor>/<class>=<name>".
-//
-// A valid vendor and class name may contain the following runes:
-//
-//	'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'.
-//
-// A valid device name may contain the following runes:
-//
-//	'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':'
-//
-// Deprecated: use parser.QualifiedName instead
-func QualifiedName(vendor, class, name string) string {
-	return parser.QualifiedName(vendor, class, name)
-}
-
-// IsQualifiedName tests if a device name is qualified.
-//
-// Deprecated: use parser.IsQualifiedName instead
-func IsQualifiedName(device string) bool {
-	return parser.IsQualifiedName(device)
-}
-
-// ParseQualifiedName splits a qualified name into device vendor, class,
-// and name. If the device fails to parse as a qualified name, or if any
-// of the split components fail to pass syntax validation, vendor and
-// class are returned as empty, together with the verbatim input as the
-// name and an error describing the reason for failure.
-//
-// Deprecated: use parser.ParseQualifiedName instead
-func ParseQualifiedName(device string) (string, string, string, error) {
-	return parser.ParseQualifiedName(device)
-}
-
-// ParseDevice tries to split a device name into vendor, class, and name.
-// If this fails, for instance in the case of unqualified device names,
-// ParseDevice returns an empty vendor and class together with name set
-// to the verbatim input.
-//
-// Deprecated: use parser.ParseDevice instead
-func ParseDevice(device string) (string, string, string) {
-	return parser.ParseDevice(device)
-}
-
-// ParseQualifier splits a device qualifier into vendor and class.
-// The syntax for a device qualifier is
-//
-//	"<vendor>/<class>"
-//
-// If parsing fails, an empty vendor and the class set to the
-// verbatim input is returned.
-//
-// Deprecated: use parser.ParseQualifier instead
-func ParseQualifier(kind string) (string, string) {
-	return parser.ParseQualifier(kind)
-}
-
-// ValidateVendorName checks the validity of a vendor name.
-// A vendor name may contain the following ASCII characters:
-//   - upper- and lowercase letters ('A'-'Z', 'a'-'z')
-//   - digits ('0'-'9')
-//   - underscore, dash, and dot ('_', '-', and '.')
-//
-// Deprecated: use parser.ValidateVendorName instead
-func ValidateVendorName(vendor string) error {
-	return parser.ValidateVendorName(vendor)
-}
-
-// ValidateClassName checks the validity of class name.
-// A class name may contain the following ASCII characters:
-//   - upper- and lowercase letters ('A'-'Z', 'a'-'z')
-//   - digits ('0'-'9')
-//   - underscore, dash, and dot ('_', '-', and '.')
-//
-// Deprecated: use parser.ValidateClassName instead
-func ValidateClassName(class string) error {
-	return parser.ValidateClassName(class)
-}
-
-// ValidateDeviceName checks the validity of a device name.
-// A device name may contain the following ASCII characters:
-//   - upper- and lowercase letters ('A'-'Z', 'a'-'z')
-//   - digits ('0'-'9')
-//   - underscore, dash, dot, colon ('_', '-', '.', ':')
-//
-// Deprecated: use parser.ValidateDeviceName instead
-func ValidateDeviceName(name string) error {
-	return parser.ValidateDeviceName(name)
-}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go
deleted file mode 100644
index 3113a05a2..000000000
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-   Copyright © 2021 The CDI Authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package cdi
-
-import (
-	"sync"
-
-	oci "github.com/opencontainers/runtime-spec/specs-go"
-	cdi "tags.cncf.io/container-device-interface/specs-go"
-)
-
-// Registry keeps a cache of all CDI Specs installed or generated on
-// the host. Registry is the primary interface clients should use to
-// interact with CDI.
-//
-// The most commonly used Registry functions are for refreshing the
-// registry and injecting CDI devices into an OCI Spec.
-//
-// Deprecated: Registry is deprecated and will be removed in a future
-// version. Please update your code to use the corresponding package-
-// level functions Configure(), Refresh(), InjectDevices(), GetErrors(),
-// and GetDefaultCache().
-type Registry interface {
-	RegistryResolver
-	RegistryRefresher
-	DeviceDB() RegistryDeviceDB
-	SpecDB() RegistrySpecDB
-}
-
-// RegistryRefresher is the registry interface for refreshing the
-// cache of CDI Specs and devices.
-//
-// Configure reconfigures the registry with the given options.
-//
-// Refresh rescans all CDI Spec directories and updates the
-// state of the cache to reflect any changes. It returns any
-// errors encountered during the refresh.
-//
-// GetErrors returns all errors encountered for any of the scanned
-// Spec files during the last cache refresh.
-//
-// GetSpecDirectories returns the set up CDI Spec directories
-// currently in use. The directories are returned in the scan
-// order of Refresh().
-//
-// GetSpecDirErrors returns any errors related to the configured
-// Spec directories.
-//
-// Deprecated: RegistryRefresher is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-type RegistryRefresher interface {
-	Configure(...Option) error
-	Refresh() error
-	GetErrors() map[string][]error
-	GetSpecDirectories() []string
-	GetSpecDirErrors() map[string]error
-}
-
-// RegistryResolver is the registry interface for injecting CDI
-// devices into an OCI Spec.
-//
-// InjectDevices takes an OCI Spec and injects into it a set of
-// CDI devices given by qualified name. It returns the names of
-// any unresolved devices and an error if injection fails.
-//
-// Deprecated: RegistryRefresher is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-type RegistryResolver interface {
-	InjectDevices(spec *oci.Spec, device ...string) (unresolved []string, err error)
-}
-
-// RegistryDeviceDB is the registry interface for querying devices.
-//
-// GetDevice returns the CDI device for the given qualified name. If
-// the device is not GetDevice returns nil.
-//
-// ListDevices returns a slice with the names of qualified device
-// known. The returned slice is sorted.
-//
-// Deprecated: RegistryDeviceDB is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-// and will be removed in a future version. Please use the default
-// cache and its related package-level functions instead.
-type RegistryDeviceDB interface {
-	GetDevice(device string) *Device
-	ListDevices() []string
-}
-
-// RegistrySpecDB is the registry interface for querying CDI Specs.
-//
-// ListVendors returns a slice with all vendors known. The returned
-// slice is sorted.
-//
-// ListClasses returns a slice with all classes known. The returned
-// slice is sorted.
-//
-// GetVendorSpecs returns a slice of all Specs for the vendor.
-//
-// GetSpecErrors returns any errors for the Spec encountered during
-// the last cache refresh.
-//
-// WriteSpec writes the Spec with the given content and name to the
-// last Spec directory.
-//
-// Deprecated: RegistrySpecDB is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-type RegistrySpecDB interface {
-	ListVendors() []string
-	ListClasses() []string
-	GetVendorSpecs(vendor string) []*Spec
-	GetSpecErrors(*Spec) []error
-	WriteSpec(raw *cdi.Spec, name string) error
-	RemoveSpec(name string) error
-}
-
-type registry struct {
-	*Cache
-}
-
-var _ Registry = &registry{}
-
-var (
-	reg      *registry
-	initOnce sync.Once
-)
-
-// GetRegistry returns the CDI registry. If any options are given, those
-// are applied to the registry.
-//
-// Deprecated: GetRegistry is deprecated and will be removed in a future
-// version. Please use the default cache and its related package-level
-// functions instead.
-func GetRegistry(options ...Option) Registry {
-	initOnce.Do(func() {
-		reg = &registry{GetDefaultCache()}
-	})
-	if len(options) > 0 {
-		// We don't care about errors here
-		_ = reg.Configure(options...)
-	}
-	return reg
-}
-
-// DeviceDB returns the registry interface for querying devices.
-//
-// Deprecated: DeviceDB is deprecated and will be removed in a future
-// version. Please use the default cache and its related package-level
-// functions instead.
-func (r *registry) DeviceDB() RegistryDeviceDB {
-	return r
-}
-
-// SpecDB returns the registry interface for querying Specs.
-//
-// Deprecated: SpecDB is deprecated and will be removed in a future
-// version. Please use the default cache and its related package-level
-// functions instead.
-func (r *registry) SpecDB() RegistrySpecDB {
-	return r
-}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
index 09005d690..b192f962c 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
@@ -35,8 +35,7 @@ var (
 	// While altering this variable changes the package defaults,
 	// the preferred way of overriding the default directories is
 	// to use a WithSpecDirs options. Otherwise the change is only
-	// effective if it takes place before creating the Registry or
-	// other Cache instances.
+	// effective if it takes place before creating the cache instance.
 	DefaultSpecDirs = []string{DefaultStaticDir, DefaultDynamicDir}
 	// ErrStopScan can be returned from a ScanSpecFunc to stop the scan.
 	ErrStopScan = errors.New("stop Spec scan")
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
index 1a0a662b8..06678b3c2 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
@@ -17,7 +17,6 @@
 package cdi
 
 import (
-	"encoding/json"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -28,6 +27,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	"tags.cncf.io/container-device-interface/internal/validation"
+	"tags.cncf.io/container-device-interface/pkg/parser"
 	cdi "tags.cncf.io/container-device-interface/specs-go"
 )
 
@@ -105,7 +105,7 @@ func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
 		spec.path += defaultSpecExt
 	}
 
-	spec.vendor, spec.class = ParseQualifier(spec.Kind)
+	spec.vendor, spec.class = parser.ParseQualifier(spec.Kind)
 
 	if spec.devices, err = spec.validate(); err != nil {
 		return nil, fmt.Errorf("invalid CDI Spec: %w", err)
@@ -114,57 +114,6 @@ func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
 	return spec, nil
 }
 
-// Write the CDI Spec to the file associated with it during instantiation
-// by newSpec() or ReadSpec().
-func (s *Spec) write(overwrite bool) error {
-	var (
-		data []byte
-		dir  string
-		tmp  *os.File
-		err  error
-	)
-
-	err = validateSpec(s.Spec)
-	if err != nil {
-		return err
-	}
-
-	if filepath.Ext(s.path) == ".yaml" {
-		data, err = yaml.Marshal(s.Spec)
-		data = append([]byte("---\n"), data...)
-	} else {
-		data, err = json.Marshal(s.Spec)
-	}
-	if err != nil {
-		return fmt.Errorf("failed to marshal Spec file: %w", err)
-	}
-
-	dir = filepath.Dir(s.path)
-	err = os.MkdirAll(dir, 0o755)
-	if err != nil {
-		return fmt.Errorf("failed to create Spec dir: %w", err)
-	}
-
-	tmp, err = os.CreateTemp(dir, "spec.*.tmp")
-	if err != nil {
-		return fmt.Errorf("failed to create Spec file: %w", err)
-	}
-	_, err = tmp.Write(data)
-	tmp.Close()
-	if err != nil {
-		return fmt.Errorf("failed to write Spec file: %w", err)
-	}
-
-	err = renameIn(dir, filepath.Base(tmp.Name()), filepath.Base(s.path), overwrite)
-
-	if err != nil {
-		os.Remove(tmp.Name())
-		err = fmt.Errorf("failed to write Spec file: %w", err)
-	}
-
-	return err
-}
-
 // GetVendor returns the vendor of this Spec.
 func (s *Spec) GetVendor() string {
 	return s.vendor
@@ -200,24 +149,21 @@ func (s *Spec) edits() *ContainerEdits {
 	return &ContainerEdits{&s.ContainerEdits}
 }
 
+// MinimumRequiredVersion determines the minimum spec version for the input spec.
+// Deprecated: use cdi.MinimumRequiredVersion instead
+func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
+	return cdi.MinimumRequiredVersion(spec)
+}
+
 // Validate the Spec.
 func (s *Spec) validate() (map[string]*Device, error) {
-	if err := validateVersion(s.Version); err != nil {
+	if err := cdi.ValidateVersion(s.Spec); err != nil {
 		return nil, err
 	}
-
-	minVersion, err := MinimumRequiredVersion(s.Spec)
-	if err != nil {
-		return nil, fmt.Errorf("could not determine minimum required version: %v", err)
-	}
-	if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) {
-		return nil, fmt.Errorf("the spec version must be at least v%v", minVersion)
-	}
-
-	if err := ValidateVendorName(s.vendor); err != nil {
+	if err := parser.ValidateVendorName(s.vendor); err != nil {
 		return nil, err
 	}
-	if err := ValidateClassName(s.class); err != nil {
+	if err := parser.ValidateClassName(s.class); err != nil {
 		return nil, err
 	}
 	if err := validation.ValidateSpecAnnotations(s.Kind, s.Annotations); err != nil {
@@ -242,15 +188,6 @@ func (s *Spec) validate() (map[string]*Device, error) {
 	return devices, nil
 }
 
-// validateVersion checks whether the specified spec version is supported.
-func validateVersion(version string) error {
-	if !validSpecVersions.isValidVersion(version) {
-		return fmt.Errorf("invalid version %q", version)
-	}
-
-	return nil
-}
-
 // ParseSpec parses CDI Spec data into a raw CDI Spec.
 func ParseSpec(data []byte) (*cdi.Spec, error) {
 	var raw *cdi.Spec
@@ -270,7 +207,7 @@ func SetSpecValidator(fn func(*cdi.Spec) error) {
 	specValidator = fn
 }
 
-// validateSpec validates the Spec using the extneral validator.
+// validateSpec validates the Spec using the extneral validation.
 func validateSpec(raw *cdi.Spec) error {
 	validatorLock.RLock()
 	defer validatorLock.RUnlock()
@@ -328,7 +265,7 @@ func GenerateTransientSpecName(vendor, class, transientID string) string {
 // the Spec does not contain a valid vendor or class, it returns
 // an empty name and a non-nil error.
 func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
-	vendor, class := ParseQualifier(raw.Kind)
+	vendor, class := parser.ParseQualifier(raw.Kind)
 	if vendor == "" {
 		return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
 	}
@@ -342,7 +279,7 @@ func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
 // If the Spec does not contain a valid vendor or class, it returns an
 // an empty name and a non-nil error.
 func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) {
-	vendor, class := ParseQualifier(raw.Kind)
+	vendor, class := parser.ParseQualifier(raw.Kind)
 	if vendor == "" {
 		return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
 	}
diff --git a/vendor/tags.cncf.io/container-device-interface/specs-go/LICENSE b/vendor/tags.cncf.io/container-device-interface/specs-go/LICENSE
deleted file mode 100644
index 261eeb9e9..000000000
--- a/vendor/tags.cncf.io/container-device-interface/specs-go/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/vendor/tags.cncf.io/container-device-interface/specs-go/config.go b/vendor/tags.cncf.io/container-device-interface/specs-go/config.go
index d6d6302ff..77f095c05 100644
--- a/vendor/tags.cncf.io/container-device-interface/specs-go/config.go
+++ b/vendor/tags.cncf.io/container-device-interface/specs-go/config.go
@@ -2,14 +2,12 @@ package specs
 
 import "os"
 
-// CurrentVersion is the current version of the Spec.
-const CurrentVersion = "0.8.0"
-
 // Spec is the base configuration for CDI
 type Spec struct {
 	Version string `json:"cdiVersion"`
 	Kind    string `json:"kind"`
 	// Annotations add meta information per CDI spec. Note these are CDI-specific and do not affect container metadata.
+	// Added in v0.6.0.
 	Annotations    map[string]string `json:"annotations,omitempty"`
 	Devices        []Device          `json:"devices"`
 	ContainerEdits ContainerEdits    `json:"containerEdits,omitempty"`
@@ -19,6 +17,7 @@ type Spec struct {
 type Device struct {
 	Name string `json:"name"`
 	// Annotations add meta information per device. Note these are CDI-specific and do not affect container metadata.
+	// Added in v0.6.0.
 	Annotations    map[string]string `json:"annotations,omitempty"`
 	ContainerEdits ContainerEdits    `json:"containerEdits"`
 }
@@ -29,14 +28,14 @@ type ContainerEdits struct {
 	DeviceNodes    []*DeviceNode `json:"deviceNodes,omitempty"`
 	Hooks          []*Hook       `json:"hooks,omitempty"`
 	Mounts         []*Mount      `json:"mounts,omitempty"`
-	IntelRdt       *IntelRdt     `json:"intelRdt,omitempty"`
-	AdditionalGIDs []uint32      `json:"additionalGids,omitempty"`
+	IntelRdt       *IntelRdt     `json:"intelRdt,omitempty"`       // Added in v0.7.0
+	AdditionalGIDs []uint32      `json:"additionalGids,omitempty"` // Added in v0.7.0
 }
 
 // DeviceNode represents a device node that needs to be added to the OCI spec.
 type DeviceNode struct {
 	Path        string       `json:"path"`
-	HostPath    string       `json:"hostPath,omitempty"`
+	HostPath    string       `json:"hostPath,omitempty"` // Added in v0.5.0
 	Type        string       `json:"type,omitempty"`
 	Major       int64        `json:"major,omitempty"`
 	Minor       int64        `json:"minor,omitempty"`
@@ -51,7 +50,7 @@ type Mount struct {
 	HostPath      string   `json:"hostPath"`
 	ContainerPath string   `json:"containerPath"`
 	Options       []string `json:"options,omitempty"`
-	Type          string   `json:"type,omitempty"`
+	Type          string   `json:"type,omitempty"` // Added in v0.4.0
 }
 
 // Hook represents a hook that needs to be added to the OCI spec.
diff --git a/vendor/tags.cncf.io/container-device-interface/specs-go/oci.go b/vendor/tags.cncf.io/container-device-interface/specs-go/oci.go
deleted file mode 100644
index ce485cb23..000000000
--- a/vendor/tags.cncf.io/container-device-interface/specs-go/oci.go
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-Copyright © 2021 The CDI Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package specs
-
-import "errors"
-
-// errDeprecated is returned for the ToOCI functions below.
-// This should provide better guidance for user when migrating from the API
-// below to the APIs provided in the cdi package.
-var errDeprecated = errors.New("deprecated; Use cdi package functions instead")
-
-// ToOCI returns the opencontainers runtime Spec Hook for this Hook.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.Hook.toOCI
-// and made private.
-func (h *Hook) ToOCI() error {
-	return errDeprecated
-}
-
-// ToOCI returns the opencontainers runtime Spec Mount for this Mount.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.Mount.toOCI
-// and made private.
-func (m *Mount) ToOCI() error {
-	return errDeprecated
-}
-
-// ToOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.DeviceNode.toOCI
-// and made private.
-func (d *DeviceNode) ToOCI() error {
-	return errDeprecated
-}
-
-// ToOCI returns the opencontainers runtime Spec LinuxIntelRdt for this IntelRdt config.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.IntelRdt.toOCI
-// and made private.
-func (i *IntelRdt) ToOCI() error {
-	return errDeprecated
-}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/version.go b/vendor/tags.cncf.io/container-device-interface/specs-go/version.go
similarity index 72%
rename from vendor/tags.cncf.io/container-device-interface/pkg/cdi/version.go
rename to vendor/tags.cncf.io/container-device-interface/specs-go/version.go
index 9ca912671..7f0bbdd8d 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/version.go
+++ b/vendor/tags.cncf.io/container-device-interface/specs-go/version.go
@@ -14,20 +14,18 @@
    limitations under the License.
 */
 
-package cdi
+package specs
 
 import (
+	"fmt"
 	"strings"
 
 	"golang.org/x/mod/semver"
-
-	"tags.cncf.io/container-device-interface/pkg/parser"
-	cdi "tags.cncf.io/container-device-interface/specs-go"
 )
 
 const (
-	// CurrentVersion is the current version of the CDI Spec.
-	CurrentVersion = cdi.CurrentVersion
+	// CurrentVersion is the current version of the Spec.
+	CurrentVersion = "0.8.0"
 
 	// vCurrent is the current version as a semver-comparable type
 	vCurrent version = "v" + CurrentVersion
@@ -60,8 +58,26 @@ var validSpecVersions = requiredVersionMap{
 	v080: requiresV080,
 }
 
+// ValidateVersion checks whether the specified spec version is valid.
+// In addition to checking whether the spec version is in the set of known versions,
+// the spec is inspected to determine whether the features used are available in specified
+// version.
+func ValidateVersion(spec *Spec) error {
+	if !validSpecVersions.isValidVersion(spec.Version) {
+		return fmt.Errorf("invalid version %q", spec.Version)
+	}
+	minVersion, err := MinimumRequiredVersion(spec)
+	if err != nil {
+		return fmt.Errorf("could not determine minimum required version: %w", err)
+	}
+	if newVersion(minVersion).isGreaterThan(newVersion(spec.Version)) {
+		return fmt.Errorf("the spec version must be at least v%v", minVersion)
+	}
+	return nil
+}
+
 // MinimumRequiredVersion determines the minimum spec version for the input spec.
-func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
+func MinimumRequiredVersion(spec *Spec) (string, error) {
 	minVersion := validSpecVersions.requiredVersion(spec)
 	return minVersion.String(), nil
 }
@@ -80,17 +96,17 @@ func (v version) String() string {
 	return strings.TrimPrefix(string(v), "v")
 }
 
-// IsGreaterThan checks with a version is greater than the specified version.
-func (v version) IsGreaterThan(o version) bool {
+// isGreaterThan checks with a version is greater than the specified version.
+func (v version) isGreaterThan(o version) bool {
 	return semver.Compare(string(v), string(o)) > 0
 }
 
-// IsLatest checks whether the version is the latest supported version
-func (v version) IsLatest() bool {
+// isLatest checks whether the version is the latest supported version
+func (v version) isLatest() bool {
 	return v == vCurrent
 }
 
-type requiredFunc func(*cdi.Spec) bool
+type requiredFunc func(*Spec) bool
 
 type requiredVersionMap map[version]requiredFunc
 
@@ -103,18 +119,18 @@ func (r requiredVersionMap) isValidVersion(specVersion string) bool {
 }
 
 // requiredVersion returns the minimum version required for the given spec
-func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
+func (r requiredVersionMap) requiredVersion(spec *Spec) version {
 	minVersion := vEarliest
 
 	for v, isRequired := range validSpecVersions {
 		if isRequired == nil {
 			continue
 		}
-		if isRequired(spec) && v.IsGreaterThan(minVersion) {
+		if isRequired(spec) && v.isGreaterThan(minVersion) {
 			minVersion = v
 		}
 		// If we have already detected the latest version then no later version could be detected
-		if minVersion.IsLatest() {
+		if minVersion.isLatest() {
 			break
 		}
 	}
@@ -125,12 +141,12 @@ func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
 // requiresV080 returns true if the spec uses v0.8.0 features.
 // Since the v0.8.0 spec bump was due to the removed .ToOCI functions on the
 // spec types, there are explicit spec changes.
-func requiresV080(_ *cdi.Spec) bool {
+func requiresV080(_ *Spec) bool {
 	return false
 }
 
 // requiresV070 returns true if the spec uses v0.7.0 features
-func requiresV070(spec *cdi.Spec) bool {
+func requiresV070(spec *Spec) bool {
 	if spec.ContainerEdits.IntelRdt != nil {
 		return true
 	}
@@ -153,7 +169,7 @@ func requiresV070(spec *cdi.Spec) bool {
 }
 
 // requiresV060 returns true if the spec uses v0.6.0 features
-func requiresV060(spec *cdi.Spec) bool {
+func requiresV060(spec *Spec) bool {
 	// The v0.6.0 spec allows annotations to be specified at a spec level
 	for range spec.Annotations {
 		return true
@@ -167,23 +183,20 @@ func requiresV060(spec *cdi.Spec) bool {
 	}
 
 	// The v0.6.0 spec allows dots "." in Kind name label (class)
-	vendor, class := parser.ParseQualifier(spec.Kind)
-	if vendor != "" {
-		if strings.ContainsRune(class, '.') {
-			return true
-		}
+	if !strings.Contains(spec.Kind, "/") {
+		return false
 	}
-
-	return false
+	class := strings.SplitN(spec.Kind, "/", 2)[1]
+	return strings.Contains(class, ".")
 }
 
 // requiresV050 returns true if the spec uses v0.5.0 features
-func requiresV050(spec *cdi.Spec) bool {
-	var edits []*cdi.ContainerEdits
+func requiresV050(spec *Spec) bool {
+	var edits []*ContainerEdits
 
 	for _, d := range spec.Devices {
-		// The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter
-		if len(d.Name) > 0 && !parser.IsLetter(rune(d.Name[0])) {
+		// The v0.5.0 spec allowed device name to start with a digit
+		if len(d.Name) > 0 && '0' <= d.Name[0] && d.Name[0] <= '9' {
 			return true
 		}
 		edits = append(edits, &d.ContainerEdits)
@@ -202,8 +215,8 @@ func requiresV050(spec *cdi.Spec) bool {
 }
 
 // requiresV040 returns true if the spec uses v0.4.0 features
-func requiresV040(spec *cdi.Spec) bool {
-	var edits []*cdi.ContainerEdits
+func requiresV040(spec *Spec) bool {
+	var edits []*ContainerEdits
 
 	for _, d := range spec.Devices {
 		edits = append(edits, &d.ContainerEdits)