Skip to content

Commit 1dbcdd2

Browse files
committed
Switch to CDI producer API
This change switces to the CDI producer API to write CDI specifications. Signed-off-by: Evan Lezar <[email protected]>
1 parent 02b1e25 commit 1dbcdd2

File tree

23 files changed

+510
-808
lines changed

23 files changed

+510
-808
lines changed

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
golang.org/x/mod v0.22.0
1515
golang.org/x/sys v0.29.0
1616
tags.cncf.io/container-device-interface v0.8.0
17+
tags.cncf.io/container-device-interface/api/producer v0.0.0
1718
tags.cncf.io/container-device-interface/specs-go v0.8.0
1819
)
1920

@@ -39,4 +40,5 @@ require (
3940

4041
replace (
4142
tags.cncf.io/container-device-interface => ../container-device-interface
42-
)
43+
tags.cncf.io/container-device-interface/api/producer => ../container-device-interface/api/producer
44+
)

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
8888
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
8989
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
9090
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
91-
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
92-
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
9391
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
9492
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=

pkg/nvcdi/spec/builder.go

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,28 @@ import (
2020
"fmt"
2121
"os"
2222

23-
"tags.cncf.io/container-device-interface/pkg/cdi"
23+
"tags.cncf.io/container-device-interface/api/producer"
2424
"tags.cncf.io/container-device-interface/pkg/parser"
25-
"tags.cncf.io/container-device-interface/specs-go"
25+
cdi "tags.cncf.io/container-device-interface/specs-go"
2626

2727
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
2828
)
2929

3030
type builder struct {
31-
raw *specs.Spec
31+
raw *cdi.Spec
3232
version string
3333
vendor string
3434
class string
35-
deviceSpecs []specs.Device
36-
edits specs.ContainerEdits
35+
deviceSpecs []cdi.Device
36+
edits cdi.ContainerEdits
3737
format string
3838

3939
mergedDeviceOptions []transform.MergedDeviceOption
4040
noSimplify bool
4141
permissions os.FileMode
4242

43-
transformOnSave transform.Transformer
43+
detectMinimumVersion bool
44+
transformOnSave transform.Transformer
4445
}
4546

4647
// newBuilder creates a new spec builder with the supplied options
@@ -64,7 +65,7 @@ func newBuilder(opts ...Option) *builder {
6465
}
6566
}
6667
if s.version == "" || s.version == DetectMinimumVersion {
67-
s.transformOnSave = &setMinimumRequiredVersion{}
68+
s.detectMinimumVersion = true
6869
s.version = cdi.CurrentVersion
6970
}
7071
if s.vendor == "" {
@@ -86,7 +87,7 @@ func newBuilder(opts ...Option) *builder {
8687
func (o *builder) Build() (*spec, error) {
8788
raw := o.raw
8889
if raw == nil {
89-
raw = &specs.Spec{
90+
raw = &cdi.Spec{
9091
Version: o.version,
9192
Kind: fmt.Sprintf("%s/%s", o.vendor, o.class),
9293
Devices: o.deviceSpecs,
@@ -114,11 +115,26 @@ func (o *builder) Build() (*spec, error) {
114115
}
115116
}
116117

118+
options := []producer.Option{
119+
producer.WithDetectMinimumVersion(o.detectMinimumVersion),
120+
producer.WithPermissions(o.permissions),
121+
}
122+
switch o.format {
123+
case FormatJSON:
124+
options = append(options, producer.WithSpecFormat(producer.SpecFormatJSON))
125+
case FormatYAML:
126+
options = append(options, producer.WithSpecFormat(producer.SpecFormatYAML))
127+
}
128+
129+
producer, err := producer.NewSpecWriter(options...)
130+
if err != nil {
131+
return nil, err
132+
}
133+
117134
s := spec{
118-
Spec: raw,
119-
format: o.format,
120-
permissions: o.permissions,
135+
raw: raw,
121136
transformOnSave: o.transformOnSave,
137+
SpecWriter: producer,
122138
}
123139
return &s, nil
124140
}
@@ -127,14 +143,14 @@ func (o *builder) Build() (*spec, error) {
127143
type Option func(*builder)
128144

129145
// WithDeviceSpecs sets the device specs for the spec builder
130-
func WithDeviceSpecs(deviceSpecs []specs.Device) Option {
146+
func WithDeviceSpecs(deviceSpecs []cdi.Device) Option {
131147
return func(o *builder) {
132148
o.deviceSpecs = deviceSpecs
133149
}
134150
}
135151

136152
// WithEdits sets the container edits for the spec builder
137-
func WithEdits(edits specs.ContainerEdits) Option {
153+
func WithEdits(edits cdi.ContainerEdits) Option {
138154
return func(o *builder) {
139155
o.edits = edits
140156
}
@@ -176,7 +192,7 @@ func WithNoSimplify(noSimplify bool) Option {
176192
}
177193

178194
// WithRawSpec sets the raw spec for the spec builder
179-
func WithRawSpec(raw *specs.Spec) Option {
195+
func WithRawSpec(raw *cdi.Spec) Option {
180196
return func(o *builder) {
181197
o.raw = raw
182198
}
@@ -195,3 +211,17 @@ func WithMergedDeviceOptions(opts ...transform.MergedDeviceOption) Option {
195211
o.mergedDeviceOptions = opts
196212
}
197213
}
214+
215+
type transformAsValidator struct {
216+
transform.Transformer
217+
}
218+
219+
func (t *transformAsValidator) Validate(spec *cdi.Spec) error {
220+
if t != nil && t.Transformer != nil {
221+
if err := t.Transformer.Transform(spec); err != nil {
222+
return fmt.Errorf("failed to apply transform to spec")
223+
}
224+
}
225+
// TODO: We should perform a spec validation here.
226+
return nil
227+
}

pkg/nvcdi/spec/set-minimum-version.go

Lines changed: 0 additions & 35 deletions
This file was deleted.

pkg/nvcdi/spec/spec.go

Lines changed: 20 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,19 @@
1717
package spec
1818

1919
import (
20-
"fmt"
2120
"io"
22-
"os"
23-
"path/filepath"
2421

25-
"tags.cncf.io/container-device-interface/pkg/cdi"
22+
"tags.cncf.io/container-device-interface/api/producer"
2623
"tags.cncf.io/container-device-interface/specs-go"
24+
cdi "tags.cncf.io/container-device-interface/specs-go"
2725

2826
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
2927
)
3028

3129
type spec struct {
32-
*specs.Spec
33-
format string
34-
permissions os.FileMode
30+
raw *cdi.Spec
3531
transformOnSave transform.Transformer
32+
*producer.SpecWriter
3633
}
3734

3835
var _ Interface = (*spec)(nil)
@@ -42,96 +39,32 @@ func New(opts ...Option) (Interface, error) {
4239
return newBuilder(opts...).Build()
4340
}
4441

42+
// Raw returns a pointer to the raw spec.
43+
func (s *spec) Raw() *specs.Spec {
44+
return s.raw
45+
}
46+
4547
// Save writes the spec to the specified path and overwrites the file if it exists.
4648
func (s *spec) Save(path string) error {
4749
if s.transformOnSave != nil {
48-
err := s.transformOnSave.Transform(s.Raw())
50+
err := s.transformOnSave.Transform(s.raw)
4951
if err != nil {
50-
return fmt.Errorf("error applying transform: %w", err)
52+
return err
5153
}
5254
}
53-
path, err := s.normalizePath(path)
54-
if err != nil {
55-
return fmt.Errorf("failed to normalize path: %w", err)
56-
}
57-
58-
specDir := filepath.Dir(path)
59-
cache, _ := cdi.NewCache(
60-
cdi.WithAutoRefresh(false),
61-
cdi.WithSpecDirs(specDir),
62-
)
63-
if err := cache.WriteSpec(s.Raw(), filepath.Base(path)); err != nil {
64-
return fmt.Errorf("failed to write spec: %w", err)
65-
}
66-
67-
if err := os.Chmod(path, s.permissions); err != nil {
68-
return fmt.Errorf("failed to set permissions on spec file: %w", err)
69-
}
70-
71-
return nil
55+
// TODO: We should add validation here.
56+
_, err := s.SpecWriter.Save(s.raw, path)
57+
return err
7258
}
7359

74-
// WriteTo writes the spec to the specified writer.
60+
// WriteTo writes the configured spec to the specified writer.
7561
func (s *spec) WriteTo(w io.Writer) (int64, error) {
76-
name, err := cdi.GenerateNameForSpec(s.Raw())
77-
if err != nil {
78-
return 0, err
79-
}
80-
81-
path, _ := s.normalizePath(name)
82-
tmpFile, err := os.CreateTemp("", "*"+filepath.Base(path))
83-
if err != nil {
84-
return 0, err
85-
}
86-
defer os.Remove(tmpFile.Name())
87-
88-
if err := s.Save(tmpFile.Name()); err != nil {
89-
return 0, err
90-
}
91-
92-
err = tmpFile.Close()
93-
if err != nil {
94-
return 0, fmt.Errorf("failed to close temporary file: %w", err)
95-
}
96-
97-
r, err := os.Open(tmpFile.Name())
98-
if err != nil {
99-
return 0, fmt.Errorf("failed to open temporary file: %w", err)
100-
}
101-
defer r.Close()
102-
103-
return io.Copy(w, r)
104-
}
105-
106-
// Raw returns a pointer to the raw spec.
107-
func (s *spec) Raw() *specs.Spec {
108-
return s.Spec
109-
}
110-
111-
// normalizePath ensures that the specified path has a supported extension
112-
func (s *spec) normalizePath(path string) (string, error) {
113-
if ext := filepath.Ext(path); ext != ".yaml" && ext != ".json" {
114-
path += s.extension()
115-
}
116-
117-
if filepath.Clean(filepath.Dir(path)) == "." {
118-
pwd, err := os.Getwd()
62+
if s.transformOnSave != nil {
63+
err := s.transformOnSave.Transform(s.raw)
11964
if err != nil {
120-
return path, fmt.Errorf("failed to get current working directory: %v", err)
65+
return 0, err
12166
}
122-
path = filepath.Join(pwd, path)
123-
}
124-
125-
return path, nil
126-
}
127-
128-
func (s *spec) extension() string {
129-
switch s.format {
130-
case FormatJSON:
131-
return ".json"
132-
case FormatYAML:
133-
return ".yaml"
13467
}
135-
136-
return ".yaml"
68+
// TODO: We should add validation here.
69+
return s.SpecWriter.WriteSpecTo(s.raw, w)
13770
}

vendor/modules.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,17 @@ gopkg.in/yaml.v3
8787
# sigs.k8s.io/yaml v1.3.0
8888
## explicit; go 1.12
8989
sigs.k8s.io/yaml
90-
# tags.cncf.io/container-device-interface v0.8.0
90+
# tags.cncf.io/container-device-interface v0.8.0 => ../container-device-interface
9191
## explicit; go 1.20
9292
tags.cncf.io/container-device-interface/internal/validation
9393
tags.cncf.io/container-device-interface/internal/validation/k8s
9494
tags.cncf.io/container-device-interface/pkg/cdi
9595
tags.cncf.io/container-device-interface/pkg/parser
96+
# tags.cncf.io/container-device-interface/api/producer v0.0.0 => ../container-device-interface/api/producer
97+
## explicit; go 1.20
98+
tags.cncf.io/container-device-interface/api/producer
9699
# tags.cncf.io/container-device-interface/specs-go v0.8.0
97100
## explicit; go 1.19
98101
tags.cncf.io/container-device-interface/specs-go
102+
# tags.cncf.io/container-device-interface => ../container-device-interface
103+
# tags.cncf.io/container-device-interface/api/producer => ../container-device-interface/api/producer

vendor/tags.cncf.io/container-device-interface/api/producer/api.go

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)