diff --git a/CHANGELOG.md b/CHANGELOG.md index 2425317f..bd998fa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,13 @@ * Fix bug in creation of `/dev/char` symlinks by failing operation if kernel modules are not loaded. * Add option to load kernel modules when creating device nodes * Add option to create device nodes when creating `/dev/char` symlinks -* Bump CUDA base image version to 12.1.1. +* Create ouput folders if required when running `nvidia-ctk runtime configure` + * [libnvidia-container] Support OpenSSL 3 with the Encrypt/Decrypt library +* [toolkit-container] Bump CUDA base image version to 12.1.1. + ## v1.13.1 * Update `update-ldcache` hook to only update ldcache if it exists. diff --git a/cmd/nvidia-ctk/runtime/configure/configure.go b/cmd/nvidia-ctk/runtime/configure/configure.go index cd5a245a..bb6129bc 100644 --- a/cmd/nvidia-ctk/runtime/configure/configure.go +++ b/cmd/nvidia-ctk/runtime/configure/configure.go @@ -190,12 +190,14 @@ func (m command) configureWrapper(c *cli.Context, config *config) error { return fmt.Errorf("unable to flush config: %v", err) } - if n == 0 { - m.logger.Infof("Removed empty config from %v", outputPath) - } else { - m.logger.Infof("Wrote updated config to %v", outputPath) + if outputPath != "" { + if n == 0 { + m.logger.Infof("Removed empty config from %v", outputPath) + } else { + m.logger.Infof("Wrote updated config to %v", outputPath) + } + m.logger.Infof("It is recommended that %v daemon be restarted.", config.runtime) } - m.logger.Infof("It is recommended that %v daemon be restarted.", config.runtime) return nil } diff --git a/pkg/config/engine/config.go b/pkg/config/engine/config.go new file mode 100644 index 00000000..f82a59f2 --- /dev/null +++ b/pkg/config/engine/config.go @@ -0,0 +1,61 @@ +/** +# Copyright (c) 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 engine + +import ( + "fmt" + "os" + "path/filepath" +) + +// Config represents a runtime config +type Config string + +// Write writes the specified contents to a config file. +func (c Config) Write(output []byte) (int, error) { + path := string(c) + if path == "" { + n, err := os.Stdout.Write(output) + if err == nil { + os.Stdout.WriteString("\n") + } + return n, err + } + + if len(output) == 0 { + err := os.Remove(path) + if err != nil { + return 0, fmt.Errorf("unable to remove empty file: %v", err) + } + return 0, nil + } + + if dir := filepath.Dir(path); dir != "" { + err := os.MkdirAll(dir, 0755) + if err != nil { + return 0, fmt.Errorf("unable to create directory %v: %v", dir, err) + } + } + + f, err := os.Create(path) + if err != nil { + return 0, fmt.Errorf("unable to open %v for writing: %v", path, err) + } + defer f.Close() + + return f.Write(output) +} diff --git a/pkg/config/engine/containerd/config_v2.go b/pkg/config/engine/containerd/config_v2.go index bd9abada..3fcdb9c5 100644 --- a/pkg/config/engine/containerd/config_v2.go +++ b/pkg/config/engine/containerd/config_v2.go @@ -18,8 +18,8 @@ package containerd import ( "fmt" - "os" + "github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine" "github.com/pelletier/go-toml" ) @@ -133,34 +133,11 @@ func (c *Config) RemoveRuntime(name string) error { // Save writes the config to the specified path func (c Config) Save(path string) (int64, error) { config := c.Tree - output, err := config.ToTomlString() + output, err := config.Marshal() if err != nil { return 0, fmt.Errorf("unable to convert to TOML: %v", err) } - if path == "" { - os.Stdout.WriteString(fmt.Sprintf("%s\n", output)) - return int64(len(output)), nil - } - - if len(output) == 0 { - err := os.Remove(path) - if err != nil { - return 0, fmt.Errorf("unable to remove empty file: %v", err) - } - return 0, nil - } - - f, err := os.Create(path) - if err != nil { - return 0, fmt.Errorf("unable to open '%v' for writing: %v", path, err) - } - defer f.Close() - - n, err := f.WriteString(output) - if err != nil { - return 0, fmt.Errorf("unable to write output: %v", err) - } - + n, err := engine.Config(path).Write(output) return int64(n), err } diff --git a/pkg/config/engine/containerd/option.go b/pkg/config/engine/containerd/option.go index bb0bda6f..23eeb484 100644 --- a/pkg/config/engine/containerd/option.go +++ b/pkg/config/engine/containerd/option.go @@ -108,26 +108,23 @@ func (b *builder) build() (engine.Interface, error) { // loadConfig loads the containerd config from disk func (b *builder) loadConfig(config string) (*Config, error) { - b.logger.Infof("Loading config: %v", config) - info, err := os.Stat(config) if os.IsExist(err) && info.IsDir() { return nil, fmt.Errorf("config file is a directory") } - configFile := config if os.IsNotExist(err) { - configFile = "/dev/null" - b.logger.Infof("Config file does not exist, creating new one") + b.logger.Infof("Config file does not exist; using empty config") + config = "/dev/null" + } else { + b.logger.Infof("Loading config from %v", config) } - tomlConfig, err := toml.LoadFile(configFile) + tomlConfig, err := toml.LoadFile(config) if err != nil { return nil, err } - b.logger.Infof("Successfully loaded config") - cfg := Config{ Tree: tomlConfig, } diff --git a/pkg/config/engine/crio/crio.go b/pkg/config/engine/crio/crio.go index dda5c451..03db8bff 100644 --- a/pkg/config/engine/crio/crio.go +++ b/pkg/config/engine/crio/crio.go @@ -18,7 +18,6 @@ package crio import ( "fmt" - "os" "github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine" "github.com/pelletier/go-toml" @@ -103,34 +102,11 @@ func (c *Config) RemoveRuntime(name string) error { // Save writes the config to the specified path func (c Config) Save(path string) (int64, error) { config := (toml.Tree)(c) - output, err := config.ToTomlString() + output, err := config.Marshal() if err != nil { return 0, fmt.Errorf("unable to convert to TOML: %v", err) } - if path == "" { - os.Stdout.WriteString(fmt.Sprintf("%s\n", output)) - return int64(len(output)), nil - } - - if len(output) == 0 { - err := os.Remove(path) - if err != nil { - return 0, fmt.Errorf("unable to remove empty file: %v", err) - } - return 0, nil - } - - f, err := os.Create(path) - if err != nil { - return 0, fmt.Errorf("unable to open '%v' for writing: %v", path, err) - } - defer f.Close() - - n, err := f.WriteString(output) - if err != nil { - return 0, fmt.Errorf("unable to write output: %v", err) - } - + n, err := engine.Config(path).Write(output) return int64(n), err } diff --git a/pkg/config/engine/crio/option.go b/pkg/config/engine/crio/option.go index f6660f68..7ad90e4f 100644 --- a/pkg/config/engine/crio/option.go +++ b/pkg/config/engine/crio/option.go @@ -67,13 +67,14 @@ func (b *builder) loadConfig(config string) (*Config, error) { return nil, fmt.Errorf("config file is a directory") } - configFile := config if os.IsNotExist(err) { - configFile = "/dev/null" - b.logger.Infof("Config file does not exist, creating new one") + b.logger.Infof("Config file does not exist; using empty config") + config = "/dev/null" + } else { + b.logger.Infof("Loading config from %v", config) } - cfg, err := toml.LoadFile(configFile) + cfg, err := toml.LoadFile(config) if err != nil { return nil, err } diff --git a/pkg/config/engine/docker/docker.go b/pkg/config/engine/docker/docker.go index dc64a3c0..92594cc5 100644 --- a/pkg/config/engine/docker/docker.go +++ b/pkg/config/engine/docker/docker.go @@ -19,7 +19,6 @@ package docker import ( "encoding/json" "fmt" - "os" "github.com/NVIDIA/nvidia-container-toolkit/internal/logger" "github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine" @@ -122,29 +121,6 @@ func (c Config) Save(path string) (int64, error) { return 0, fmt.Errorf("unable to convert to JSON: %v", err) } - if path == "" { - os.Stdout.WriteString(fmt.Sprintf("%s\n", output)) - return int64(len(output)), nil - } - - if len(output) == 0 { - err := os.Remove(path) - if err != nil { - return 0, fmt.Errorf("unable to remove empty file: %v", err) - } - return 0, nil - } - - f, err := os.Create(path) - if err != nil { - return 0, fmt.Errorf("unable to open %v for writing: %v", path, err) - } - defer f.Close() - - n, err := f.WriteString(string(output)) - if err != nil { - return 0, fmt.Errorf("unable to write output: %v", err) - } - - return int64(n), nil + n, err := engine.Config(path).Write(output) + return int64(n), err } diff --git a/pkg/config/engine/docker/option.go b/pkg/config/engine/docker/option.go index 893ed0d1..4f64382f 100644 --- a/pkg/config/engine/docker/option.go +++ b/pkg/config/engine/docker/option.go @@ -58,10 +58,8 @@ func (b *builder) build() (*Config, error) { } // loadConfig loads the docker config from disk -func (b *builder) loadConfig(configFilePath string) (*Config, error) { - b.logger.Infof("Loading docker config from %v", configFilePath) - - info, err := os.Stat(configFilePath) +func (b *builder) loadConfig(config string) (*Config, error) { + info, err := os.Stat(config) if os.IsExist(err) && info.IsDir() { return nil, fmt.Errorf("config file is a directory") } @@ -69,11 +67,12 @@ func (b *builder) loadConfig(configFilePath string) (*Config, error) { cfg := make(Config) if os.IsNotExist(err) { - b.logger.Infof("Config file does not exist, creating new one") + b.logger.Infof("Config file does not exist; using empty config") return &cfg, nil } - readBytes, err := ioutil.ReadFile(configFilePath) + b.logger.Infof("Loading config from %v", config) + readBytes, err := ioutil.ReadFile(config) if err != nil { return nil, fmt.Errorf("unable to read config: %v", err) } @@ -82,7 +81,5 @@ func (b *builder) loadConfig(configFilePath string) (*Config, error) { if err := json.NewDecoder(reader).Decode(&cfg); err != nil { return nil, err } - - b.logger.Infof("Successfully loaded config") return &cfg, nil }