From 69729de4663b43a5d05fb2f6f219590ec27f419c Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Mon, 20 Jan 2025 13:47:37 +0100 Subject: [PATCH] Add logic to detect minimum spec version on save Signed-off-by: Evan Lezar --- api/producer/options.go | 15 +++++++++--- api/producer/set-minimum-version.go | 36 +++++++++++++++++++++++++++++ api/producer/spec-format.go | 13 +++++++++++ api/producer/writer.go | 5 ++-- api/producer/writer_test.go | 19 +++++++++++++++ 5 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 api/producer/set-minimum-version.go diff --git a/api/producer/options.go b/api/producer/options.go index 8c247866..c8f0b1ef 100644 --- a/api/producer/options.go +++ b/api/producer/options.go @@ -25,9 +25,18 @@ import ( type Option func(*options) error type options struct { - specFormat SpecFormat - overwrite bool - permissions fs.FileMode + 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. diff --git a/api/producer/set-minimum-version.go b/api/producer/set-minimum-version.go new file mode 100644 index 00000000..29474204 --- /dev/null +++ b/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/api/producer/spec-format.go b/api/producer/spec-format.go index 7e4eebbb..d2900f72 100644 --- a/api/producer/spec-format.go +++ b/api/producer/spec-format.go @@ -83,9 +83,22 @@ 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) } diff --git a/api/producer/writer.go b/api/producer/writer.go index dd9f1161..e25c6bc1 100644 --- a/api/producer/writer.go +++ b/api/producer/writer.go @@ -36,8 +36,9 @@ func NewSpecWriter(opts ...Option) (*SpecWriter, error) { options: options{ overwrite: true, // TODO: This could be updated to 0644 to be world-readable. - permissions: 0600, - specFormat: DefaultSpecFormat, + permissions: 0600, + specFormat: DefaultSpecFormat, + detectMinimumVersion: false, }, } for _, opt := range opts { diff --git a/api/producer/writer_test.go b/api/producer/writer_test.go index 778d6a5d..64bbf5a9 100644 --- a/api/producer/writer_test.go +++ b/api/producer/writer_test.go @@ -126,6 +126,25 @@ devices: null kind: example.com/class `, }, + { + description: "minimum version is detected", + spec: cdi.Spec{ + Version: cdi.CurrentVersion, + Kind: "example.com/class", + ContainerEdits: cdi.ContainerEdits{ + DeviceNodes: []*cdi.DeviceNode{ + { + Path: "/dev/foo", + }, + }, + }, + }, + options: []Option{WithDetectMinimumVersion(true)}, + filename: "foo", + expectedFilename: "foo.yaml", + expectedPermissions: 0600, + expectedOutput: `--- +cdiVersion: 0.3.0 containerEdits: deviceNodes: - path: /dev/foo