diff --git a/cmd/nvidia-container-runtime/main.go b/cmd/nvidia-container-runtime/main.go index ac2f0f84..787d9361 100644 --- a/cmd/nvidia-container-runtime/main.go +++ b/cmd/nvidia-container-runtime/main.go @@ -20,12 +20,12 @@ func main() { // run is an entry point that allows for idiomatic handling of errors // when calling from the main function. func run(argv []string) (rerr error) { - cfg, err := config.GetRuntimeConfig() + cfg, err := config.GetConfig() if err != nil { return fmt.Errorf("error loading config: %v", err) } - err = logger.LogToFile(cfg.DebugFilePath) + err = logger.LogToFile(cfg.NVIDIAContainerRuntimeConfig.DebugFilePath) if err != nil { return fmt.Errorf("error opening debug log file: %v", err) } diff --git a/cmd/nvidia-container-runtime/modifier/experimental.go b/cmd/nvidia-container-runtime/modifier/experimental.go index bc39cd6a..9693d05b 100644 --- a/cmd/nvidia-container-runtime/modifier/experimental.go +++ b/cmd/nvidia-container-runtime/modifier/experimental.go @@ -36,14 +36,13 @@ type experimental struct { // NewExperimentalModifier creates a modifier that applied the experimental // modications to an OCI spec if required by the runtime wrapper. -func NewExperimentalModifier(logger *logrus.Logger, cfg *config.RuntimeConfig) (oci.SpecModifier, error) { +func NewExperimentalModifier(logger *logrus.Logger, cfg *config.Config) (oci.SpecModifier, error) { logger.Infof("Constructing modifier from config: %+v", cfg) - // TODO: We need to specify the root - root := "" + root := cfg.NVIDIAContainerCLIConfig.Root var d discover.Discover - switch cfg.DiscoverMode { + switch cfg.NVIDIAContainerRuntimeConfig.DiscoverMode { case "legacy": legacyDiscoverer, err := discover.NewLegacyDiscoverer(logger, root) if err != nil { @@ -51,7 +50,7 @@ func NewExperimentalModifier(logger *logrus.Logger, cfg *config.RuntimeConfig) ( } d = legacyDiscoverer default: - return nil, fmt.Errorf("invalid discover mode: %v", cfg.DiscoverMode) + return nil, fmt.Errorf("invalid discover mode: %v", cfg.NVIDIAContainerRuntimeConfig.DiscoverMode) } return newExperimentalModifierFromDiscoverer(logger, d) diff --git a/cmd/nvidia-container-runtime/modifier/experimental_test.go b/cmd/nvidia-container-runtime/modifier/experimental_test.go index 07b67e8a..aefcff17 100644 --- a/cmd/nvidia-container-runtime/modifier/experimental_test.go +++ b/cmd/nvidia-container-runtime/modifier/experimental_test.go @@ -32,25 +32,31 @@ func TestConstructor(t *testing.T) { testCases := []struct { description string - cfg *config.RuntimeConfig + cfg *config.Config expectedError error }{ { - description: "empty config raises error", - cfg: &config.RuntimeConfig{}, + description: "empty config raises error", + cfg: &config.Config{ + NVIDIAContainerRuntimeConfig: config.RuntimeConfig{}, + }, expectedError: fmt.Errorf("invalid discover mode"), }, { description: "non-legacy discover mode raises error", - cfg: &config.RuntimeConfig{ - DiscoverMode: "non-legacy", + cfg: &config.Config{ + NVIDIAContainerRuntimeConfig: config.RuntimeConfig{ + DiscoverMode: "non-legacy", + }, }, expectedError: fmt.Errorf("invalid discover mode"), }, { description: "legacy discover mode returns modifier", - cfg: &config.RuntimeConfig{ - DiscoverMode: "legacy", + cfg: &config.Config{ + NVIDIAContainerRuntimeConfig: config.RuntimeConfig{ + DiscoverMode: "legacy", + }, }, }, } diff --git a/cmd/nvidia-container-runtime/runtime_factory.go b/cmd/nvidia-container-runtime/runtime_factory.go index c40f873e..9ae9a572 100644 --- a/cmd/nvidia-container-runtime/runtime_factory.go +++ b/cmd/nvidia-container-runtime/runtime_factory.go @@ -32,7 +32,7 @@ const ( ) // newNVIDIAContainerRuntime is a factory method that constructs a runtime based on the selected configuration and specified logger -func newNVIDIAContainerRuntime(logger *logrus.Logger, cfg *config.RuntimeConfig, argv []string) (oci.Runtime, error) { +func newNVIDIAContainerRuntime(logger *logrus.Logger, cfg *config.Config, argv []string) (oci.Runtime, error) { ociSpec, err := oci.NewSpec(logger, argv) if err != nil { return nil, fmt.Errorf("error constructing OCI specification: %v", err) @@ -45,7 +45,7 @@ func newNVIDIAContainerRuntime(logger *logrus.Logger, cfg *config.RuntimeConfig, } var specModifier oci.SpecModifier - if cfg.Experimental { + if cfg.NVIDIAContainerRuntimeConfig.Experimental { specModifier, err = modifier.NewExperimentalModifier(logger, cfg) if err != nil { return nil, fmt.Errorf("failed to construct experimental modifier: %v", err) diff --git a/cmd/nvidia-container-runtime/runtime_factory_test.go b/cmd/nvidia-container-runtime/runtime_factory_test.go index 467e5efc..145970d7 100644 --- a/cmd/nvidia-container-runtime/runtime_factory_test.go +++ b/cmd/nvidia-container-runtime/runtime_factory_test.go @@ -29,19 +29,21 @@ func TestFactoryMethod(t *testing.T) { testCases := []struct { description string - config config.RuntimeConfig + cfg *config.Config argv []string expectedError bool }{ { description: "empty config no error", - config: config.RuntimeConfig{}, + cfg: &config.Config{ + NVIDIAContainerRuntimeConfig: config.RuntimeConfig{}, + }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { - _, err := newNVIDIAContainerRuntime(logger, &tc.config, tc.argv) + _, err := newNVIDIAContainerRuntime(logger, tc.cfg, tc.argv) if tc.expectedError { require.Error(t, err) } else { diff --git a/internal/config/cli.go b/internal/config/cli.go new file mode 100644 index 00000000..8eca29de --- /dev/null +++ b/internal/config/cli.go @@ -0,0 +1,48 @@ +/** +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +**/ + +package config + +import ( + "github.com/pelletier/go-toml" +) + +// CLIConfig stores the options for the nvidia-container-cli +type CLIConfig struct { + Root string +} + +// getCLIConfigFrom reads the nvidia container runtime config from the specified toml Tree. +func getCLIConfigFrom(toml *toml.Tree) *CLIConfig { + cfg := getDefaultCLIConfig() + + if toml == nil { + return cfg + } + + cfg.Root = toml.GetDefault("nvidia-container-cli.root", cfg.Root).(string) + + return cfg +} + +// getDefaultCLIConfig defines the default values for the config +func getDefaultCLIConfig() *CLIConfig { + c := CLIConfig{ + Root: "", + } + + return &c +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 00000000..df880954 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,99 @@ +/** +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +**/ + +package config + +import ( + "fmt" + "io" + "os" + "path" + + "github.com/pelletier/go-toml" +) + +const ( + configOverride = "XDG_CONFIG_HOME" + configFilePath = "nvidia-container-runtime/config.toml" +) + +var ( + configDir = "/etc/" +) + +// Config represents the contents of the config.toml file for the NVIDIA Container Toolkit +// Note: This is currently duplicated by the HookConfig in cmd/nvidia-container-toolkit/hook_config.go +type Config struct { + NVIDIAContainerCLIConfig CLIConfig `toml:"nvidia-container-cli"` + NVIDIAContainerRuntimeConfig RuntimeConfig `toml:"nvidia-container-runtime"` +} + +// GetConfig sets up the config struct. Values are read from a toml file +// or set via the environment. +func GetConfig() (*Config, error) { + if XDGConfigDir := os.Getenv(configOverride); len(XDGConfigDir) != 0 { + configDir = XDGConfigDir + } + + configFilePath := path.Join(configDir, configFilePath) + + tomlFile, err := os.Open(configFilePath) + if err != nil { + return nil, fmt.Errorf("failed to open config file %v: %v", configFilePath, err) + } + defer tomlFile.Close() + + cfg, err := loadConfigFrom(tomlFile) + if err != nil { + return nil, fmt.Errorf("failed to read config values: %v", err) + } + + return cfg, nil +} + +// loadRuntimeConfigFrom reads the config from the specified Reader +func loadConfigFrom(reader io.Reader) (*Config, error) { + toml, err := toml.LoadReader(reader) + if err != nil { + return nil, err + } + + return getConfigFrom(toml), nil +} + +// getConfigFrom reads the nvidia container runtime config from the specified toml Tree. +func getConfigFrom(toml *toml.Tree) *Config { + cfg := getDefaultConfig() + + if toml == nil { + return cfg + } + + cfg.NVIDIAContainerCLIConfig = *getCLIConfigFrom(toml) + cfg.NVIDIAContainerRuntimeConfig = *getRuntimeConfigFrom(toml) + + return cfg +} + +// getDefaultConfig defines the default values for the config +func getDefaultConfig() *Config { + c := Config{ + NVIDIAContainerCLIConfig: *getDefaultCLIConfig(), + NVIDIAContainerRuntimeConfig: *getDefaultRuntimeConfig(), + } + + return &c +} diff --git a/internal/config/runtime.go b/internal/config/runtime.go index 5132c6d3..c0c43011 100644 --- a/internal/config/runtime.go +++ b/internal/config/runtime.go @@ -17,23 +17,9 @@ package config import ( - "fmt" - "io" - "os" - "path" - "github.com/pelletier/go-toml" ) -const ( - configOverride = "XDG_CONFIG_HOME" - configFilePath = "nvidia-container-runtime/config.toml" -) - -var ( - configDir = "/etc/" -) - // RuntimeConfig stores the config options for the NVIDIA Container Runtime type RuntimeConfig struct { DebugFilePath string @@ -41,39 +27,6 @@ type RuntimeConfig struct { DiscoverMode string } -// GetRuntimeConfig sets up the config struct. Values are read from a toml file -// or set via the environment. -func GetRuntimeConfig() (*RuntimeConfig, error) { - if XDGConfigDir := os.Getenv(configOverride); len(XDGConfigDir) != 0 { - configDir = XDGConfigDir - } - - configFilePath := path.Join(configDir, configFilePath) - - tomlFile, err := os.Open(configFilePath) - if err != nil { - return nil, fmt.Errorf("failed to open config file %v: %v", configFilePath, err) - } - defer tomlFile.Close() - - cfg, err := loadRuntimeConfigFrom(tomlFile) - if err != nil { - return nil, fmt.Errorf("failed to read config values: %v", err) - } - - return cfg, nil -} - -// loadRuntimeConfigFrom reads the config from the specified Reader -func loadRuntimeConfigFrom(reader io.Reader) (*RuntimeConfig, error) { - toml, err := toml.LoadReader(reader) - if err != nil { - return nil, err - } - - return getRuntimeConfigFrom(toml), nil -} - // getRuntimeConfigFrom reads the nvidia container runtime config from the specified toml Tree. func getRuntimeConfigFrom(toml *toml.Tree) *RuntimeConfig { cfg := getDefaultRuntimeConfig() diff --git a/internal/config/runtime_test.go b/internal/config/runtime_test.go index 1601f392..82495719 100644 --- a/internal/config/runtime_test.go +++ b/internal/config/runtime_test.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestGerRuntimeConfigWithCustomConfig(t *testing.T) { +func TestGetConfigWithCustomConfig(t *testing.T) { wd, err := os.Getwd() require.NoError(t, err) @@ -42,24 +42,29 @@ func TestGerRuntimeConfigWithCustomConfig(t *testing.T) { defer func() { require.NoError(t, os.RemoveAll(testDir)) }() - cfg, err := GetRuntimeConfig() + cfg, err := GetConfig() require.NoError(t, err) - require.Equal(t, cfg.DebugFilePath, "/nvidia-container-toolkit.log") + require.Equal(t, cfg.NVIDIAContainerRuntimeConfig.DebugFilePath, "/nvidia-container-toolkit.log") } -func TestGerRuntimeConfig(t *testing.T) { +func TestGetConfig(t *testing.T) { testCases := []struct { description string contents []string expectedError error - expectedConfig *RuntimeConfig + expectedConfig *Config }{ { description: "empty config is default", - expectedConfig: &RuntimeConfig{ - DebugFilePath: "/dev/null", - Experimental: false, - DiscoverMode: "legacy", + expectedConfig: &Config{ + NVIDIAContainerCLIConfig: CLIConfig{ + Root: "", + }, + NVIDIAContainerRuntimeConfig: RuntimeConfig{ + DebugFilePath: "/dev/null", + Experimental: false, + DiscoverMode: "legacy", + }, }, }, { @@ -69,10 +74,15 @@ func TestGerRuntimeConfig(t *testing.T) { "nvidia-container-runtime.experimental = true", "nvidia-container-runtime.discover-mode = \"not-legacy\"", }, - expectedConfig: &RuntimeConfig{ - DebugFilePath: "/foo/bar", - Experimental: true, - DiscoverMode: "not-legacy", + expectedConfig: &Config{ + NVIDIAContainerCLIConfig: CLIConfig{ + Root: "", + }, + NVIDIAContainerRuntimeConfig: RuntimeConfig{ + DebugFilePath: "/foo/bar", + Experimental: true, + DiscoverMode: "not-legacy", + }, }, }, { @@ -83,10 +93,15 @@ func TestGerRuntimeConfig(t *testing.T) { "experimental = true", "discover-mode = \"not-legacy\"", }, - expectedConfig: &RuntimeConfig{ - DebugFilePath: "/foo/bar", - Experimental: true, - DiscoverMode: "not-legacy", + expectedConfig: &Config{ + NVIDIAContainerCLIConfig: CLIConfig{ + Root: "", + }, + NVIDIAContainerRuntimeConfig: RuntimeConfig{ + DebugFilePath: "/foo/bar", + Experimental: true, + DiscoverMode: "not-legacy", + }, }, }, } @@ -95,7 +110,7 @@ func TestGerRuntimeConfig(t *testing.T) { t.Run(tc.description, func(t *testing.T) { reader := strings.NewReader(strings.Join(tc.contents, "\n")) - cfg, err := loadRuntimeConfigFrom(reader) + cfg, err := loadConfigFrom(reader) if tc.expectedError != nil { require.Error(t, err) } else {