Skip to content

Commit d0cf0d6

Browse files
Load settings from config.toml file during CDI generation
Signed-off-by: Carlos Eduardo Arango Gutierrez <[email protected]>
1 parent c53fe93 commit d0cf0d6

File tree

23 files changed

+692
-79
lines changed

23 files changed

+692
-79
lines changed

cmd/nvidia-cdi-hook/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func main() {
8080
Usage: "Suppress all output except for errors; overrides --debug",
8181
Destination: &opts.Quiet,
8282
// TODO: Support for NVIDIA_CDI_QUIET is deprecated and NVIDIA_CTK_QUIET should be used instead.
83-
Sources: cli.EnvVars("NVDIA_CTK_QUIET", "NVIDIA_CDI_QUIET"),
83+
Sources: cli.EnvVars("NVIDIA_CTK_QUIET", "NVIDIA_CDI_QUIET"),
8484
},
8585
}
8686

cmd/nvidia-ctk/cdi/cdi.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,41 @@
1717
package cdi
1818

1919
import (
20+
"github.com/sirupsen/logrus"
2021
"github.com/urfave/cli/v3"
2122

2223
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/generate"
2324
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/list"
2425
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/transform"
25-
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
2626
)
2727

2828
type command struct {
29-
logger logger.Interface
29+
logger *logrus.Logger
30+
configFile *string
3031
}
3132

32-
// NewCommand constructs an info command with the specified logger
33-
func NewCommand(logger logger.Interface) *cli.Command {
33+
// NewCommand constructs a cdi command with the specified logger
34+
func NewCommand(logger *logrus.Logger, configFile *string) *cli.Command {
3435
c := command{
35-
logger: logger,
36+
logger: logger,
37+
configFile: configFile,
3638
}
3739
return c.build()
3840
}
3941

4042
// build
4143
func (m command) build() *cli.Command {
42-
// Create the 'hook' command
43-
hook := cli.Command{
44+
// Create the 'cdi' command
45+
cdi := cli.Command{
4446
Name: "cdi",
4547
Usage: "Provide tools for interacting with Container Device Interface specifications",
4648
}
4749

48-
hook.Commands = []*cli.Command{
49-
generate.NewCommand(m.logger),
50-
transform.NewCommand(m.logger),
51-
list.NewCommand(m.logger),
50+
cdi.Commands = []*cli.Command{
51+
generate.NewCommand(m.logger, m.configFile),
52+
list.NewCommand(m.logger, m.configFile),
53+
transform.NewCommand(m.logger, m.configFile),
5254
}
5355

54-
return &hook
56+
return &cdi
5557
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
# Copyright (c) 2025, 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 generate
18+
19+
import (
20+
"fmt"
21+
"sync"
22+
23+
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
24+
25+
altsrc "github.com/urfave/cli-altsrc/v3"
26+
"github.com/urfave/cli/v3"
27+
)
28+
29+
// Config returns a configAsValueSource for the generate options.
30+
// This allows for flags to be specified in a config file.
31+
func (o *options) Config() *configAsValueSource {
32+
if o.appliedConfig == nil {
33+
o.appliedConfig = &configAsValueSource{}
34+
}
35+
return o.appliedConfig.From(&o.config)
36+
}
37+
38+
// configAsValueSource provides flag values from a TOML configuration file, acting as a factory for specific cli.ValueSource instances.
39+
// It uses an altsrc.Sourcer to find the configuration file path and loads it on demand.
40+
type configAsValueSource struct {
41+
sync.Mutex
42+
source altsrc.Sourcer
43+
*config.Toml
44+
}
45+
46+
// configValueLookup is a cli.ValueSource that looks up values from a configAsValueSource.
47+
type configValueLookup struct {
48+
key string
49+
from *configAsValueSource
50+
}
51+
52+
// From sets the source for the config file path.
53+
// It uses a string pointer to the config file path option.
54+
func (c *configAsValueSource) From(config *string) *configAsValueSource {
55+
c.Lock()
56+
defer c.Unlock()
57+
if c.source == nil {
58+
c.source = altsrc.NewStringPtrSourcer(config)
59+
}
60+
return c
61+
}
62+
63+
// LoadIfRequired loads the config file if it has not already been loaded.
64+
// If the config file path is not specified, it uses the default config file path.
65+
func (c *configAsValueSource) LoadIfRequired() error {
66+
c.Lock()
67+
defer c.Unlock()
68+
69+
if c.Toml != nil {
70+
return nil
71+
}
72+
73+
configFilePath := c.source.SourceURI()
74+
75+
if configFilePath == "" {
76+
configFilePath = config.GetConfigFilePath()
77+
}
78+
configToml, err := config.New(
79+
config.WithConfigFile(configFilePath),
80+
)
81+
if err != nil {
82+
return err
83+
}
84+
c.Toml = configToml
85+
86+
return nil
87+
}
88+
89+
// GetAsString gets the specified key from the config file as a string.
90+
// It returns the value and a boolean indicating if the value was found.
91+
func (c *configAsValueSource) GetAsString(key string) (string, bool) {
92+
if c == nil {
93+
return "", false
94+
}
95+
if err := c.LoadIfRequired(); err != nil {
96+
return "", false
97+
}
98+
99+
value := c.Get(key)
100+
if key == value {
101+
return "", false
102+
}
103+
104+
return fmt.Sprintf("%v", value), true
105+
}
106+
107+
// ValueSource returns a cli.ValueSource for the given key.
108+
// This can be used in the Sources chain for a cli.Flag.
109+
func (c *configAsValueSource) ValueSource(key string) cli.ValueSource {
110+
return &configValueLookup{
111+
key: key,
112+
from: c,
113+
}
114+
}
115+
116+
// Lookup returns the value for the key from the config file.
117+
func (c *configValueLookup) Lookup() (string, bool) {
118+
return c.from.GetAsString(c.key)
119+
}
120+
121+
// GoString returns a Go-syntax representation of the configuration source.
122+
func (c *configValueLookup) GoString() string {
123+
return c.String()
124+
}
125+
126+
// String returns a string representation of the configuration source.
127+
func (c *configValueLookup) String() string {
128+
if c.from == nil || c.from.source == nil {
129+
return "config file"
130+
}
131+
configFile := c.from.source.SourceURI()
132+
if configFile == "" {
133+
configFile = config.GetConfigFilePath()
134+
}
135+
if configFile == "" {
136+
return "config file"
137+
}
138+
return fmt.Sprintf("config file %q", configFile)
139+
}

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

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,28 @@ import (
2323
"path/filepath"
2424
"strings"
2525

26+
"github.com/sirupsen/logrus"
2627
"github.com/urfave/cli/v3"
28+
2729
cdi "tags.cncf.io/container-device-interface/pkg/parser"
2830

2931
"github.com/NVIDIA/go-nvml/pkg/nvml"
3032

3133
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
32-
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
3334
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
3435
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
3536
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
3637
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
3738
)
3839

3940
const (
40-
allDeviceName = "all"
41+
allDeviceName = "all"
42+
defaultNvidiaCTKPath = "/usr/bin/nvidia-ctk"
4143
)
4244

4345
type command struct {
44-
logger logger.Interface
46+
logger *logrus.Logger
47+
configFile *string
4548
}
4649

4750
type options struct {
@@ -60,19 +63,24 @@ type options struct {
6063
librarySearchPaths []string
6164
disabledHooks []string
6265

66+
config string
67+
6368
csv struct {
6469
files []string
6570
ignorePatterns []string
6671
}
6772

73+
appliedConfig *configAsValueSource
74+
6875
// the following are used for dependency injection during spec generation.
6976
nvmllib nvml.Interface
7077
}
7178

7279
// NewCommand constructs a generate-cdi command with the specified logger
73-
func NewCommand(logger logger.Interface) *cli.Command {
80+
func NewCommand(logger *logrus.Logger, configFile *string) *cli.Command {
7481
c := command{
75-
logger: logger,
82+
logger: logger,
83+
configFile: configFile,
7684
}
7785
return c.build()
7886
}
@@ -81,21 +89,24 @@ func NewCommand(logger logger.Interface) *cli.Command {
8189
func (m command) build() *cli.Command {
8290
opts := options{}
8391

92+
var flags []cli.Flag
93+
8494
// Create the 'generate-cdi' command
8595
c := cli.Command{
8696
Name: "generate",
8797
Usage: "Generate CDI specifications for use with CDI-enabled runtimes",
8898
UseShortOptionHandling: true,
8999
EnableShellCompletion: true,
90100
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
101+
opts.config = *m.configFile
91102
return ctx, m.validateFlags(cmd, &opts)
92103
},
93104
Action: func(ctx context.Context, cmd *cli.Command) error {
94105
return m.run(&opts)
95106
},
96107
}
97108

98-
c.Flags = []cli.Flag{
109+
flags = []cli.Flag{
99110
&cli.StringSliceFlag{
100111
Name: "config-search-path",
101112
Usage: "Specify the path to search for config files when discovering the entities that should be included in the CDI specification.",
@@ -123,7 +134,10 @@ func (m command) build() *cli.Command {
123134
"If mode is set to 'auto' the mode will be determined based on the system configuration.",
124135
Value: string(nvcdi.ModeAuto),
125136
Destination: &opts.mode,
126-
Sources: cli.EnvVars("NVIDIA_CTK_CDI_GENERATE_MODE"),
137+
Sources: cli.NewValueSourceChain(
138+
cli.EnvVar("NVIDIA_CTK_CDI_GENERATE_MODE"),
139+
opts.Config().ValueSource("nvidia-container-runtime.mode"),
140+
),
127141
},
128142
&cli.StringFlag{
129143
Name: "dev-root",
@@ -142,7 +156,10 @@ func (m command) build() *cli.Command {
142156
Name: "driver-root",
143157
Usage: "Specify the NVIDIA GPU driver root to use when discovering the entities that should be included in the CDI specification.",
144158
Destination: &opts.driverRoot,
145-
Sources: cli.EnvVars("NVIDIA_CTK_DRIVER_ROOT"),
159+
Sources: cli.NewValueSourceChain(
160+
cli.EnvVar("NVIDIA_CTK_DRIVER_ROOT"),
161+
opts.Config().ValueSource("nvidia-container-runtime.root"),
162+
),
146163
},
147164
&cli.StringSliceFlag{
148165
Name: "library-search-path",
@@ -163,7 +180,10 @@ func (m command) build() *cli.Command {
163180
Name: "ldconfig-path",
164181
Usage: "Specify the path to use for ldconfig in the generated CDI specification",
165182
Destination: &opts.ldconfigPath,
166-
Sources: cli.EnvVars("NVIDIA_CTK_CDI_GENERATE_LDCONFIG_PATH"),
183+
Sources: cli.NewValueSourceChain(
184+
cli.EnvVar("NVIDIA_CTK_CDI_GENERATE_LDCONFIG_PATH"),
185+
opts.Config().ValueSource("nvidia-container-runtime.ldconfig"),
186+
),
167187
},
168188
&cli.StringFlag{
169189
Name: "vendor",
@@ -206,6 +226,8 @@ func (m command) build() *cli.Command {
206226
},
207227
}
208228

229+
c.Flags = flags
230+
209231
return &c
210232
}
211233

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,25 @@ import (
2121
"errors"
2222
"fmt"
2323

24+
"github.com/sirupsen/logrus"
2425
"github.com/urfave/cli/v3"
2526
"tags.cncf.io/container-device-interface/pkg/cdi"
26-
27-
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
2827
)
2928

3029
type command struct {
31-
logger logger.Interface
30+
logger *logrus.Logger
31+
configFile *string
3232
}
3333

3434
type config struct {
3535
cdiSpecDirs []string
3636
}
3737

3838
// NewCommand constructs a cdi list command with the specified logger
39-
func NewCommand(logger logger.Interface) *cli.Command {
39+
func NewCommand(logger *logrus.Logger, configFile *string) *cli.Command {
4040
c := command{
41-
logger: logger,
41+
logger: logger,
42+
configFile: configFile,
4243
}
4344
return c.build()
4445
}

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,22 @@
1717
package transform
1818

1919
import (
20+
"github.com/sirupsen/logrus"
2021
"github.com/urfave/cli/v3"
2122

2223
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/transform/root"
23-
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
2424
)
2525

2626
type command struct {
27-
logger logger.Interface
27+
logger *logrus.Logger
28+
configFile *string
2829
}
2930

30-
// NewCommand constructs a command with the specified logger
31-
func NewCommand(logger logger.Interface) *cli.Command {
31+
// NewCommand constructs a transform command with the specified logger.
32+
func NewCommand(logger *logrus.Logger, configFile *string) *cli.Command {
3233
c := command{
33-
logger: logger,
34+
logger: logger,
35+
configFile: configFile,
3436
}
3537
return c.build()
3638
}

0 commit comments

Comments
 (0)