Skip to content

Commit f698396

Browse files
author
Evan Lezar
committed
Merge branch 'nvidia-ctk-cdi-transform' into 'main'
Add 'target-driver-root' option to 'nvidia-ctk cdi generate' to transform root... See merge request nvidia/container-toolkit/container-toolkit!363
2 parents 8eef7e5 + 7f7fc35 commit f698396

File tree

5 files changed

+250
-3
lines changed

5 files changed

+250
-3
lines changed

cmd/nvidia-ctk/cdi/cdi.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package cdi
1818

1919
import (
2020
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/generate"
21+
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/transform"
2122
"github.com/sirupsen/logrus"
2223
"github.com/urfave/cli/v2"
2324
)
@@ -44,6 +45,7 @@ func (m command) build() *cli.Command {
4445

4546
hook.Subcommands = []*cli.Command{
4647
generate.NewCommand(m.logger),
48+
transform.NewCommand(m.logger),
4749
}
4850

4951
return &hook

cmd/nvidia-ctk/cdi/generate/generate.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ type config struct {
4747
driverRoot string
4848
nvidiaCTKPath string
4949
mode string
50+
vendor string
51+
class string
5052
}
5153

5254
// NewCommand constructs a generate-cdi command with the specified logger
@@ -108,6 +110,20 @@ func (m command) build() *cli.Command {
108110
Usage: "Specify the path to use for the nvidia-ctk in the generated CDI specification. If this is left empty, the path will be searched.",
109111
Destination: &cfg.nvidiaCTKPath,
110112
},
113+
&cli.StringFlag{
114+
Name: "vendor",
115+
Aliases: []string{"cdi-vendor"},
116+
Usage: "the vendor string to use for the generated CDI specification.",
117+
Value: "nvidia.com",
118+
Destination: &cfg.vendor,
119+
},
120+
&cli.StringFlag{
121+
Name: "class",
122+
Aliases: []string{"cdi-class"},
123+
Usage: "the class string to use for the generated CDI specification.",
124+
Value: "gpu",
125+
Destination: &cfg.class,
126+
},
111127
}
112128

113129
return &c
@@ -149,6 +165,12 @@ func (m command) validateFlags(c *cli.Context, cfg *config) error {
149165
}
150166
}
151167

168+
if err := cdi.ValidateVendorName(cfg.vendor); err != nil {
169+
return fmt.Errorf("invalid CDI vendor name: %v", err)
170+
}
171+
if err := cdi.ValidateClassName(cfg.class); err != nil {
172+
return fmt.Errorf("invalid CDI class name: %v", err)
173+
}
152174
return nil
153175
}
154176

@@ -224,8 +246,8 @@ func (m command) generateSpec(cfg *config) (spec.Interface, error) {
224246
}
225247

226248
return spec.New(
227-
spec.WithVendor("nvidia.com"),
228-
spec.WithClass("gpu"),
249+
spec.WithVendor(cfg.vendor),
250+
spec.WithClass(cfg.class),
229251
spec.WithDeviceSpecs(deviceSpecs),
230252
spec.WithEdits(*commonEdits.ContainerEdits),
231253
spec.WithFormat(cfg.format),
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/**
2+
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
**/
16+
17+
package root
18+
19+
import (
20+
"fmt"
21+
"io"
22+
"os"
23+
24+
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
25+
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
26+
"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
27+
"github.com/sirupsen/logrus"
28+
"github.com/urfave/cli/v2"
29+
)
30+
31+
type loadSaver interface {
32+
Load() (spec.Interface, error)
33+
Save(spec.Interface) error
34+
}
35+
36+
type command struct {
37+
logger *logrus.Logger
38+
}
39+
40+
type transformOptions struct {
41+
input string
42+
output string
43+
}
44+
45+
type options struct {
46+
transformOptions
47+
from string
48+
to string
49+
}
50+
51+
// NewCommand constructs a generate-cdi command with the specified logger
52+
func NewCommand(logger *logrus.Logger) *cli.Command {
53+
c := command{
54+
logger: logger,
55+
}
56+
return c.build()
57+
}
58+
59+
// build creates the CLI command
60+
func (m command) build() *cli.Command {
61+
opts := options{}
62+
63+
c := cli.Command{
64+
Name: "root",
65+
Usage: "Apply a root transform to a CDI specification",
66+
Before: func(c *cli.Context) error {
67+
return m.validateFlags(c, &opts)
68+
},
69+
Action: func(c *cli.Context) error {
70+
return m.run(c, &opts)
71+
},
72+
}
73+
74+
c.Flags = []cli.Flag{
75+
&cli.StringFlag{
76+
Name: "input",
77+
Usage: "Specify the file to read the CDI specification from. If this is '-' the specification is read from STDIN",
78+
Value: "-",
79+
Destination: &opts.input,
80+
},
81+
&cli.StringFlag{
82+
Name: "output",
83+
Usage: "Specify the file to output the generated CDI specification to. If this is '' the specification is output to STDOUT",
84+
Destination: &opts.output,
85+
},
86+
&cli.StringFlag{
87+
Name: "from",
88+
Usage: "specify the root to be transformed",
89+
Destination: &opts.from,
90+
},
91+
&cli.StringFlag{
92+
Name: "to",
93+
Usage: "specify the replacement root. If this is the same as the from root, the transform is a no-op.",
94+
Value: "",
95+
Destination: &opts.to,
96+
},
97+
}
98+
99+
return &c
100+
}
101+
102+
func (m command) validateFlags(c *cli.Context, opts *options) error {
103+
return nil
104+
}
105+
106+
func (m command) run(c *cli.Context, opts *options) error {
107+
spec, err := opts.Load()
108+
if err != nil {
109+
return fmt.Errorf("failed to load CDI specification: %w", err)
110+
}
111+
112+
err = transform.NewRootTransformer(
113+
opts.from,
114+
opts.to,
115+
).Transform(spec.Raw())
116+
if err != nil {
117+
return fmt.Errorf("failed to transform CDI specification: %w", err)
118+
}
119+
120+
return opts.Save(spec)
121+
}
122+
123+
// Load lodas the input CDI specification
124+
func (o transformOptions) Load() (spec.Interface, error) {
125+
contents, err := o.getContents()
126+
if err != nil {
127+
return nil, fmt.Errorf("failed to read spec contents: %v", err)
128+
}
129+
130+
raw, err := cdi.ParseSpec(contents)
131+
if err != nil {
132+
return nil, fmt.Errorf("failed to parse CDI spec: %v", err)
133+
}
134+
135+
return spec.New(
136+
spec.WithRawSpec(raw),
137+
)
138+
}
139+
140+
func (o transformOptions) getContents() ([]byte, error) {
141+
if o.input == "-" {
142+
return io.ReadAll(os.Stdin)
143+
}
144+
145+
return os.ReadFile(o.input)
146+
}
147+
148+
// Save saves the CDI specification to the output file
149+
func (o transformOptions) Save(s spec.Interface) error {
150+
if o.output == "" {
151+
_, err := s.WriteTo(os.Stdout)
152+
if err != nil {
153+
return fmt.Errorf("failed to write CDI spec to STDOUT: %v", err)
154+
}
155+
return nil
156+
}
157+
158+
return s.Save(o.output)
159+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
**/
16+
17+
package transform
18+
19+
import (
20+
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/transform/root"
21+
"github.com/sirupsen/logrus"
22+
"github.com/urfave/cli/v2"
23+
)
24+
25+
type command struct {
26+
logger *logrus.Logger
27+
}
28+
29+
// NewCommand constructs a command with the specified logger
30+
func NewCommand(logger *logrus.Logger) *cli.Command {
31+
c := command{
32+
logger: logger,
33+
}
34+
return c.build()
35+
}
36+
37+
// build creates the CLI command
38+
func (m command) build() *cli.Command {
39+
c := cli.Command{
40+
Name: "transform",
41+
Usage: "Apply a transform to a CDI specification",
42+
}
43+
44+
c.Flags = []cli.Flag{}
45+
46+
c.Subcommands = []*cli.Command{
47+
root.NewCommand(m.logger),
48+
}
49+
50+
return &c
51+
}

pkg/nvcdi/spec/builder.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ func newBuilder(opts ...Option) *builder {
4141
for _, opt := range opts {
4242
opt(s)
4343
}
44+
if s.raw != nil {
45+
s.noSimplify = true
46+
vendor, class := cdi.ParseQualifier(s.raw.Kind)
47+
s.vendor = vendor
48+
s.class = class
49+
}
50+
4451
if s.version == "" {
4552
s.version = DetectMinimumVersion
4653
}
@@ -60,7 +67,6 @@ func newBuilder(opts ...Option) *builder {
6067
// Build builds a CDI spec form the spec builder.
6168
func (o *builder) Build() (*spec, error) {
6269
raw := o.raw
63-
6470
if raw == nil {
6571
raw = &specs.Spec{
6672
Version: o.version,
@@ -144,3 +150,10 @@ func WithNoSimplify(noSimplify bool) Option {
144150
o.noSimplify = noSimplify
145151
}
146152
}
153+
154+
// WithRawSpec sets the raw spec for the spec builder
155+
func WithRawSpec(raw *specs.Spec) Option {
156+
return func(o *builder) {
157+
o.raw = raw
158+
}
159+
}

0 commit comments

Comments
 (0)