Skip to content

Commit 465460d

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

File tree

22 files changed

+682
-75
lines changed

22 files changed

+682
-75
lines changed

cmd/nvidia-ctk/cdi/cdi.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,24 @@
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

3233
// NewCommand constructs a cdi command with the specified logger
33-
func NewCommand(logger logger.Interface) *cli.Command {
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
}
@@ -46,9 +48,9 @@ func (m command) build() *cli.Command {
4648
}
4749

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

5456
return &cdi
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: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ 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"
@@ -41,7 +42,8 @@ const (
4142
)
4243

4344
type command struct {
44-
logger logger.Interface
45+
logger *logrus.Logger
46+
configFile *string
4547
}
4648

4749
type options struct {
@@ -60,19 +62,24 @@ type options struct {
6062
librarySearchPaths []string
6163
disabledHooks []string
6264

65+
config string
66+
6367
csv struct {
6468
files []string
6569
ignorePatterns []string
6670
}
6771

72+
appliedConfig *configAsValueSource
73+
6874
// the following are used for dependency injection during spec generation.
6975
nvmllib nvml.Interface
7076
}
7177

7278
// NewCommand constructs a generate-cdi command with the specified logger
73-
func NewCommand(logger logger.Interface) *cli.Command {
79+
func NewCommand(logger *logrus.Logger, configFile *string) *cli.Command {
7480
c := command{
75-
logger: logger,
81+
logger: logger,
82+
configFile: configFile,
7683
}
7784
return c.build()
7885
}
@@ -81,21 +88,24 @@ func NewCommand(logger logger.Interface) *cli.Command {
8188
func (m command) build() *cli.Command {
8289
opts := options{}
8390

91+
var flags []cli.Flag
92+
8493
// Create the 'generate-cdi' command
8594
c := cli.Command{
8695
Name: "generate",
8796
Usage: "Generate CDI specifications for use with CDI-enabled runtimes",
8897
UseShortOptionHandling: true,
8998
EnableShellCompletion: true,
9099
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
100+
opts.config = *m.configFile
91101
return ctx, m.validateFlags(cmd, &opts)
92102
},
93103
Action: func(ctx context.Context, cmd *cli.Command) error {
94104
return m.run(&opts)
95105
},
96106
}
97107

98-
c.Flags = []cli.Flag{
108+
flags = []cli.Flag{
99109
&cli.StringSliceFlag{
100110
Name: "config-search-path",
101111
Usage: "Specify the path to search for config files when discovering the entities that should be included in the CDI specification.",
@@ -123,7 +133,10 @@ func (m command) build() *cli.Command {
123133
"If mode is set to 'auto' the mode will be determined based on the system configuration.",
124134
Value: string(nvcdi.ModeAuto),
125135
Destination: &opts.mode,
126-
Sources: cli.EnvVars("NVIDIA_CTK_CDI_GENERATE_MODE"),
136+
Sources: cli.NewValueSourceChain(
137+
cli.EnvVar("NVIDIA_CTK_CDI_GENERATE_MODE"),
138+
opts.Config().ValueSource("nvidia-container-runtime.mode"),
139+
),
127140
},
128141
&cli.StringFlag{
129142
Name: "dev-root",
@@ -142,7 +155,10 @@ func (m command) build() *cli.Command {
142155
Name: "driver-root",
143156
Usage: "Specify the NVIDIA GPU driver root to use when discovering the entities that should be included in the CDI specification.",
144157
Destination: &opts.driverRoot,
145-
Sources: cli.EnvVars("NVIDIA_CTK_DRIVER_ROOT"),
158+
Sources: cli.NewValueSourceChain(
159+
cli.EnvVar("NVIDIA_CTK_DRIVER_ROOT"),
160+
opts.Config().ValueSource("nvidia-container-runtime.root"),
161+
),
146162
},
147163
&cli.StringSliceFlag{
148164
Name: "library-search-path",
@@ -163,7 +179,10 @@ func (m command) build() *cli.Command {
163179
Name: "ldconfig-path",
164180
Usage: "Specify the path to use for ldconfig in the generated CDI specification",
165181
Destination: &opts.ldconfigPath,
166-
Sources: cli.EnvVars("NVIDIA_CTK_CDI_GENERATE_LDCONFIG_PATH"),
182+
Sources: cli.NewValueSourceChain(
183+
cli.EnvVar("NVIDIA_CTK_CDI_GENERATE_LDCONFIG_PATH"),
184+
opts.Config().ValueSource("nvidia-container-runtime.ldconfig"),
185+
),
167186
},
168187
&cli.StringFlag{
169188
Name: "vendor",
@@ -206,6 +225,8 @@ func (m command) build() *cli.Command {
206225
},
207226
}
208227

228+
c.Flags = flags
229+
209230
return &c
210231
}
211232

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
}

cmd/nvidia-ctk/config/config.go

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

27+
"github.com/sirupsen/logrus"
2728
"github.com/urfave/cli/v3"
2829

2930
createdefault "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/config/create-default"
3031
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/config/flags"
3132
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
32-
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
3333
)
3434

3535
type command struct {
36-
logger logger.Interface
36+
logger *logrus.Logger
37+
configFile *string
3738
}
3839

3940
// options stores the subcommand options
@@ -43,10 +44,11 @@ type options struct {
4344
sets []string
4445
}
4546

46-
// NewCommand constructs an config command with the specified logger
47-
func NewCommand(logger logger.Interface) *cli.Command {
47+
// NewCommand constructs a config command with the specified logger
48+
func NewCommand(logger *logrus.Logger, configFile *string) *cli.Command {
4849
c := command{
49-
logger: logger,
50+
logger: logger,
51+
configFile: configFile,
5052
}
5153
return c.build()
5254
}

0 commit comments

Comments
 (0)