diff --git a/go.mod b/go.mod index d5bb5411e..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 ) @@ -36,3 +37,9 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) + +replace ( + 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/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 -// -// "/=". -// -// 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 -// -// "/" -// -// 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 = ®istry{} - -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 = ®istry{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)