Skip to content

Commit 0e75444

Browse files
Load settings from config.toml file during CDI generation
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
1 parent f67c155 commit 0e75444

File tree

15 files changed

+603
-18
lines changed

15 files changed

+603
-18
lines changed

cmd/nvidia-ctk/cdi/cdi.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ import (
2626
)
2727

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

3233
// NewCommand constructs a cdi command with the specified logger
33-
func NewCommand(logger logger.Interface) *cli.Command {
34+
func NewCommand(logger logger.Interface, configFilePath *string) *cli.Command {
3435
c := command{
35-
logger: logger,
36+
logger: logger,
37+
configFilePath: configFilePath,
3638
}
3739
return c.build()
3840
}
@@ -44,9 +46,9 @@ func (m command) build() *cli.Command {
4446
Name: "cdi",
4547
Usage: "Provide tools for interacting with Container Device Interface specifications",
4648
Commands: []*cli.Command{
47-
generate.NewCommand(m.logger),
48-
transform.NewCommand(m.logger),
49+
generate.NewCommand(m.logger, m.configFilePath),
4950
list.NewCommand(m.logger),
51+
transform.NewCommand(m.logger),
5052
},
5153
}
5254

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

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"strings"
2525

2626
"github.com/urfave/cli/v3"
27+
2728
cdi "tags.cncf.io/container-device-interface/pkg/parser"
2829

2930
"github.com/NVIDIA/go-nvml/pkg/nvml"
@@ -42,6 +43,8 @@ const (
4243

4344
type command struct {
4445
logger logger.Interface
46+
47+
config *configAsValueSource
4548
}
4649

4750
type options struct {
@@ -70,9 +73,10 @@ type options struct {
7073
}
7174

7275
// NewCommand constructs a generate-cdi command with the specified logger
73-
func NewCommand(logger logger.Interface) *cli.Command {
76+
func NewCommand(logger logger.Interface, configFilePath *string) *cli.Command {
7477
c := command{
7578
logger: logger,
79+
config: New(configFilePath),
7680
}
7781
return c.build()
7882
}
@@ -121,7 +125,10 @@ func (m command) build() *cli.Command {
121125
"If mode is set to 'auto' the mode will be determined based on the system configuration.",
122126
Value: string(nvcdi.ModeAuto),
123127
Destination: &opts.mode,
124-
Sources: cli.EnvVars("NVIDIA_CTK_CDI_GENERATE_MODE"),
128+
Sources: cli.NewValueSourceChain(
129+
cli.EnvVar("NVIDIA_CTK_CDI_GENERATE_MODE"),
130+
m.config.ValueFrom("nvidia-container-runtime.mode"),
131+
),
125132
},
126133
&cli.StringFlag{
127134
Name: "dev-root",
@@ -140,7 +147,10 @@ func (m command) build() *cli.Command {
140147
Name: "driver-root",
141148
Usage: "Specify the NVIDIA GPU driver root to use when discovering the entities that should be included in the CDI specification.",
142149
Destination: &opts.driverRoot,
143-
Sources: cli.EnvVars("NVIDIA_CTK_DRIVER_ROOT"),
150+
Sources: cli.NewValueSourceChain(
151+
cli.EnvVar("NVIDIA_CTK_DRIVER_ROOT"),
152+
m.config.ValueFrom("nvidia-container-cli.root"),
153+
),
144154
},
145155
&cli.StringSliceFlag{
146156
Name: "library-search-path",
@@ -161,7 +171,10 @@ func (m command) build() *cli.Command {
161171
Name: "ldconfig-path",
162172
Usage: "Specify the path to use for ldconfig in the generated CDI specification",
163173
Destination: &opts.ldconfigPath,
164-
Sources: cli.EnvVars("NVIDIA_CTK_CDI_GENERATE_LDCONFIG_PATH"),
174+
Sources: cli.NewValueSourceChain(
175+
cli.EnvVar("NVIDIA_CTK_CDI_GENERATE_LDCONFIG_PATH"),
176+
m.config.ValueFrom("nvidia-container-cli.ldconfig"),
177+
),
165178
},
166179
&cli.StringFlag{
167180
Name: "vendor",

cmd/nvidia-ctk/main.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/runtime"
3030
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system"
3131
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
32+
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
3233

3334
cli "github.com/urfave/cli/v3"
3435
)
@@ -40,6 +41,8 @@ type options struct {
4041
Debug bool
4142
// Quiet indicates whether the CLI is started in "quiet" mode
4243
Quiet bool
44+
// Config specifies the path to the config file
45+
Config string
4346
}
4447

4548
func main() {
@@ -66,17 +69,11 @@ func main() {
6669
logLevel = logrus.ErrorLevel
6770
}
6871
logger.SetLevel(logLevel)
72+
6973
return ctx, nil
7074
},
7175
// Define the subcommands
72-
Commands: []*cli.Command{
73-
hook.NewCommand(logger),
74-
runtime.NewCommand(logger),
75-
infoCLI.NewCommand(logger),
76-
cdi.NewCommand(logger),
77-
system.NewCommand(logger),
78-
config.NewCommand(logger),
79-
},
76+
Commands: getCommands(logger, &opts.Config),
8077
Flags: []cli.Flag{
8178
&cli.BoolFlag{
8279
Name: "debug",
@@ -91,6 +88,12 @@ func main() {
9188
Destination: &opts.Quiet,
9289
Sources: cli.EnvVars("NVIDIA_CTK_QUIET"),
9390
},
91+
&cli.StringFlag{
92+
Name: "config",
93+
Usage: "Path to the config file",
94+
Destination: &opts.Config,
95+
Sources: cli.EnvVars("NVIDIA_CTK_CONFIG"),
96+
},
9497
},
9598
}
9699

@@ -101,3 +104,14 @@ func main() {
101104
os.Exit(1)
102105
}
103106
}
107+
108+
func getCommands(logger logger.Interface, configFilePath *string) []*cli.Command {
109+
return []*cli.Command{
110+
hook.NewCommand(logger),
111+
runtime.NewCommand(logger),
112+
infoCLI.NewCommand(logger),
113+
cdi.NewCommand(logger, configFilePath),
114+
system.NewCommand(logger),
115+
config.NewCommand(logger),
116+
}
117+
}

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module github.com/NVIDIA/nvidia-container-toolkit
22

3-
go 1.23.0
3+
go 1.23.2
4+
5+
toolchain go1.24.4
46

57
require (
68
github.com/NVIDIA/go-nvlib v0.7.3
@@ -13,6 +15,7 @@ require (
1315
github.com/pelletier/go-toml v1.9.5
1416
github.com/sirupsen/logrus v1.9.3
1517
github.com/stretchr/testify v1.10.0
18+
github.com/urfave/cli-altsrc/v3 v3.0.1
1619
github.com/urfave/cli/v3 v3.3.8
1720
golang.org/x/mod v0.25.0
1821
golang.org/x/sys v0.33.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf
6565
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
6666
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
6767
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
68+
github.com/urfave/cli-altsrc/v3 v3.0.1 h1:v+gHk59syLk8ao9rYybZs43+D5ut/gzj0omqQ1XYl8k=
69+
github.com/urfave/cli-altsrc/v3 v3.0.1/go.mod h1:8UtsKKcxFVzvaoySFPfvQOk413T+IXJhaCWyyoPW3yM=
6870
github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E=
6971
github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
7072
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=

vendor/github.com/urfave/cli-altsrc/v3/.gitignore

Lines changed: 10 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)