mirror of
				https://github.com/NVIDIA/nvidia-container-toolkit
				synced 2025-06-26 18:18:24 +00:00 
			
		
		
		
	Merge branch 'CNT-2349/configure-docker' into 'main'
Add nvidia-ctk runtime configure command to update docker config See merge request nvidia/container-toolkit/container-toolkit!166
This commit is contained in:
		
						commit
						37ee972f74
					
				| @ -1,3 +1,17 @@ | ||||
| # NVIDIA Container Toolkit CLI | ||||
| 
 | ||||
| The NVIDIA Container Toolkit CLI `nvidia-ctk` provides a number of utilities that are useful for working with the NVIDIA Container Toolkit. | ||||
| 
 | ||||
| ## Functionality | ||||
| 
 | ||||
| ### Configure runtimes | ||||
| 
 | ||||
| The `runtime` command of the `nvidia-ctk` CLI provides a set of utilities to related to the configuration | ||||
| and management of supported container engines. | ||||
| 
 | ||||
| For example, running the following command: | ||||
| ```bash | ||||
| nvidia-ctk runtime configure --set-as-default | ||||
| ``` | ||||
| will ensure that the NVIDIA Container Runtime is added as the default runtime to the default container | ||||
| engine. | ||||
|  | ||||
| @ -20,6 +20,7 @@ import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/hook" | ||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/runtime" | ||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/internal/info" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| 	cli "github.com/urfave/cli/v2" | ||||
| @ -70,6 +71,7 @@ func main() { | ||||
| 	// Define the subcommands
 | ||||
| 	c.Commands = []*cli.Command{ | ||||
| 		hook.NewCommand(logger), | ||||
| 		runtime.NewCommand(logger), | ||||
| 	} | ||||
| 
 | ||||
| 	// Run the CLI
 | ||||
|  | ||||
							
								
								
									
										154
									
								
								cmd/nvidia-ctk/runtime/configure/configure.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								cmd/nvidia-ctk/runtime/configure/configure.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | ||||
| /** | ||||
| # 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 configure | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/runtime/nvidia" | ||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/internal/config/docker" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/urfave/cli/v2" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	defaultRuntime = "docker" | ||||
| 
 | ||||
| 	defaultDockerConfigFilePath = "/etc/docker/daemon.json" | ||||
| ) | ||||
| 
 | ||||
| type command struct { | ||||
| 	logger *logrus.Logger | ||||
| } | ||||
| 
 | ||||
| // NewCommand constructs an configure command with the specified logger
 | ||||
| func NewCommand(logger *logrus.Logger) *cli.Command { | ||||
| 	c := command{ | ||||
| 		logger: logger, | ||||
| 	} | ||||
| 	return c.build() | ||||
| } | ||||
| 
 | ||||
| // config defines the options that can be set for the CLI through config files,
 | ||||
| // environment variables, or command line config
 | ||||
| type config struct { | ||||
| 	dryRun         bool | ||||
| 	runtime        string | ||||
| 	configFilePath string | ||||
| 	nvidiaOptions  nvidia.Options | ||||
| } | ||||
| 
 | ||||
| func (m command) build() *cli.Command { | ||||
| 	// Create a config struct to hold the parsed environment variables or command line flags
 | ||||
| 	config := config{} | ||||
| 
 | ||||
| 	// Create the 'configure' command
 | ||||
| 	configure := cli.Command{ | ||||
| 		Name:  "configure", | ||||
| 		Usage: "Add a runtime to the specified container engine", | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			return m.configureWrapper(c, &config) | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	configure.Flags = []cli.Flag{ | ||||
| 		&cli.BoolFlag{ | ||||
| 			Name:        "dry-run", | ||||
| 			Usage:       "update the runtime configuration as required but don't write changes to disk", | ||||
| 			Destination: &config.dryRun, | ||||
| 		}, | ||||
| 		&cli.StringFlag{ | ||||
| 			Name:        "runtime", | ||||
| 			Usage:       "the target runtime engine. One of [docker]", | ||||
| 			Value:       defaultRuntime, | ||||
| 			Destination: &config.runtime, | ||||
| 		}, | ||||
| 		&cli.StringFlag{ | ||||
| 			Name:        "config", | ||||
| 			Usage:       "path to the config file for the target runtime", | ||||
| 			Destination: &config.configFilePath, | ||||
| 		}, | ||||
| 		&cli.StringFlag{ | ||||
| 			Name:        "nvidia-runtime-name", | ||||
| 			Usage:       "specify the name of the NVIDIA runtime that will be added", | ||||
| 			Value:       nvidia.RuntimeName, | ||||
| 			Destination: &config.nvidiaOptions.RuntimeName, | ||||
| 		}, | ||||
| 		&cli.StringFlag{ | ||||
| 			Name:        "runtime-path", | ||||
| 			Usage:       "specify the path to the NVIDIA runtime executable", | ||||
| 			Value:       nvidia.RuntimeExecutable, | ||||
| 			Destination: &config.nvidiaOptions.RuntimePath, | ||||
| 		}, | ||||
| 		&cli.BoolFlag{ | ||||
| 			Name:        "set-as-default", | ||||
| 			Usage:       "set the specified runtime as the default runtime", | ||||
| 			Destination: &config.nvidiaOptions.SetAsDefault, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	return &configure | ||||
| } | ||||
| 
 | ||||
| func (m command) configureWrapper(c *cli.Context, config *config) error { | ||||
| 	switch config.runtime { | ||||
| 	case "docker": | ||||
| 		return m.configureDocker(c, config) | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Errorf("unrecognized runtime '%v'", config.runtime) | ||||
| } | ||||
| 
 | ||||
| // configureDocker updates the docker config to enable the NVIDIA Container Runtime
 | ||||
| func (m command) configureDocker(c *cli.Context, config *config) error { | ||||
| 	configFilePath := config.configFilePath | ||||
| 	if configFilePath == "" { | ||||
| 		configFilePath = defaultDockerConfigFilePath | ||||
| 	} | ||||
| 
 | ||||
| 	cfg, err := docker.LoadConfig(configFilePath) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to load config: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	defaultRuntime := config.nvidiaOptions.DefaultRuntime() | ||||
| 	runtimeConfig := config.nvidiaOptions.Runtime().DockerRuntimesConfig() | ||||
| 	err = docker.UpdateConfig(cfg, defaultRuntime, runtimeConfig) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to update config: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if config.dryRun { | ||||
| 		output, err := json.MarshalIndent(cfg, "", "    ") | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to convert to JSON: %v", err) | ||||
| 		} | ||||
| 		os.Stdout.WriteString(fmt.Sprintf("%s\n", output)) | ||||
| 		return nil | ||||
| 	} | ||||
| 	err = docker.FlushConfig(cfg, configFilePath) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to flush config: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	m.logger.Infof("Wrote updated config to %v", configFilePath) | ||||
| 	m.logger.Infof("It is recommended that the docker daemon be restarted.") | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										75
									
								
								cmd/nvidia-ctk/runtime/nvidia/nvidia.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								cmd/nvidia-ctk/runtime/nvidia/nvidia.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| /* | ||||
| # 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 nvidia | ||||
| 
 | ||||
| const ( | ||||
| 	// RuntimeName is the default name to use in configs for the NVIDIA Container Runtime
 | ||||
| 	RuntimeName = "nvidia" | ||||
| 	// RuntimeExecutable is the default NVIDIA Container Runtime executable file name
 | ||||
| 	RuntimeExecutable = "nvidia-container-runtime" | ||||
| ) | ||||
| 
 | ||||
| // Options specifies the options for the NVIDIA Container Runtime w.r.t a container engine such as docker.
 | ||||
| type Options struct { | ||||
| 	SetAsDefault bool | ||||
| 	RuntimeName  string | ||||
| 	RuntimePath  string | ||||
| } | ||||
| 
 | ||||
| // Runtime defines an NVIDIA runtime with a name and a executable
 | ||||
| type Runtime struct { | ||||
| 	Name string | ||||
| 	Path string | ||||
| } | ||||
| 
 | ||||
| // DefaultRuntime returns the default runtime for the configured options.
 | ||||
| // If the configuration is invalid or the default runtimes should not be set
 | ||||
| // the empty string is returned.
 | ||||
| func (o Options) DefaultRuntime() string { | ||||
| 	if !o.SetAsDefault { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	return o.RuntimeName | ||||
| } | ||||
| 
 | ||||
| // Runtime creates a runtime struct based on the options.
 | ||||
| func (o Options) Runtime() Runtime { | ||||
| 	path := o.RuntimePath | ||||
| 
 | ||||
| 	if o.RuntimePath == "" { | ||||
| 		path = RuntimeExecutable | ||||
| 	} | ||||
| 
 | ||||
| 	r := Runtime{ | ||||
| 		Name: o.RuntimeName, | ||||
| 		Path: path, | ||||
| 	} | ||||
| 
 | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| // DockerRuntimesConfig generatest the expected docker config for the specified runtime
 | ||||
| func (r Runtime) DockerRuntimesConfig() map[string]interface{} { | ||||
| 	runtimes := make(map[string]interface{}) | ||||
| 	runtimes[r.Name] = map[string]interface{}{ | ||||
| 		"path": r.Path, | ||||
| 		"args": []string{}, | ||||
| 	} | ||||
| 
 | ||||
| 	return runtimes | ||||
| } | ||||
							
								
								
									
										49
									
								
								cmd/nvidia-ctk/runtime/runtime.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								cmd/nvidia-ctk/runtime/runtime.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| /** | ||||
| # 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 runtime | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/runtime/configure" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/urfave/cli/v2" | ||||
| ) | ||||
| 
 | ||||
| type runtimeCommand struct { | ||||
| 	logger *logrus.Logger | ||||
| } | ||||
| 
 | ||||
| // NewCommand constructs a runtime command with the specified logger
 | ||||
| func NewCommand(logger *logrus.Logger) *cli.Command { | ||||
| 	c := runtimeCommand{ | ||||
| 		logger: logger, | ||||
| 	} | ||||
| 	return c.build() | ||||
| } | ||||
| 
 | ||||
| func (m runtimeCommand) build() *cli.Command { | ||||
| 	// Create the 'runtime' command
 | ||||
| 	runtime := cli.Command{ | ||||
| 		Name:  "runtime", | ||||
| 		Usage: "A collection of runtime-related utilities for the NVIDIA Container Toolkit", | ||||
| 	} | ||||
| 
 | ||||
| 	runtime.Subcommands = []*cli.Command{ | ||||
| 		configure.NewCommand(m.logger), | ||||
| 	} | ||||
| 
 | ||||
| 	return &runtime | ||||
| } | ||||
							
								
								
									
										115
									
								
								internal/config/docker/docker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								internal/config/docker/docker.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | ||||
| /** | ||||
| # Copyright (c) 2021-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 docker | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 
 | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| // LoadConfig loads the docker config from disk
 | ||||
| func LoadConfig(configFilePath string) (map[string]interface{}, error) { | ||||
| 	log.Infof("Loading docker config from %v", configFilePath) | ||||
| 
 | ||||
| 	info, err := os.Stat(configFilePath) | ||||
| 	if os.IsExist(err) && info.IsDir() { | ||||
| 		return nil, fmt.Errorf("config file is a directory") | ||||
| 	} | ||||
| 
 | ||||
| 	cfg := make(map[string]interface{}) | ||||
| 
 | ||||
| 	if os.IsNotExist(err) { | ||||
| 		log.Infof("Config file does not exist, creating new one") | ||||
| 		return cfg, nil | ||||
| 	} | ||||
| 
 | ||||
| 	readBytes, err := ioutil.ReadFile(configFilePath) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to read config: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	reader := bytes.NewReader(readBytes) | ||||
| 	if err := json.NewDecoder(reader).Decode(&cfg); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	log.Infof("Successfully loaded config") | ||||
| 	return cfg, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateConfig updates the docker config to include the nvidia runtimes
 | ||||
| func UpdateConfig(config map[string]interface{}, defaultRuntime string, newRuntimes map[string]interface{}) error { | ||||
| 	if defaultRuntime != "" { | ||||
| 		config["default-runtime"] = defaultRuntime | ||||
| 	} | ||||
| 
 | ||||
| 	// Read the existing runtimes
 | ||||
| 	runtimes := make(map[string]interface{}) | ||||
| 	if _, exists := config["runtimes"]; exists { | ||||
| 		runtimes = config["runtimes"].(map[string]interface{}) | ||||
| 	} | ||||
| 
 | ||||
| 	// Add / update the runtime definitions
 | ||||
| 	for name, rt := range newRuntimes { | ||||
| 		runtimes[name] = rt | ||||
| 	} | ||||
| 
 | ||||
| 	// Update the runtimes definition
 | ||||
| 	if len(runtimes) > 0 { | ||||
| 		config["runtimes"] = runtimes | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // FlushConfig flushes the updated/reverted config out to disk
 | ||||
| func FlushConfig(cfg map[string]interface{}, configFilePath string) error { | ||||
| 	log.Infof("Flushing docker config to %v", configFilePath) | ||||
| 
 | ||||
| 	output, err := json.MarshalIndent(cfg, "", "    ") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to convert to JSON: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	switch len(output) { | ||||
| 	case 0: | ||||
| 		err := os.Remove(configFilePath) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to remove empty file: %v", err) | ||||
| 		} | ||||
| 		log.Infof("Config empty, removing file") | ||||
| 	default: | ||||
| 		f, err := os.Create(configFilePath) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to open %v for writing: %v", configFilePath, err) | ||||
| 		} | ||||
| 		defer f.Close() | ||||
| 
 | ||||
| 		_, err = f.WriteString(string(output)) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to write output: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	log.Infof("Successfully flushed config") | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										228
									
								
								internal/config/docker/docker_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								internal/config/docker/docker_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,228 @@ | ||||
| /** | ||||
| # Copyright (c) 2021-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 docker | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
| 
 | ||||
| func TestUpdateConfigDefaultRuntime(t *testing.T) { | ||||
| 	testCases := []struct { | ||||
| 		config                     map[string]interface{} | ||||
| 		defaultRuntime             string | ||||
| 		runtimeName                string | ||||
| 		expectedDefaultRuntimeName interface{} | ||||
| 	}{ | ||||
| 		{ | ||||
| 			defaultRuntime:             "", | ||||
| 			expectedDefaultRuntimeName: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			defaultRuntime:             "NAME", | ||||
| 			expectedDefaultRuntimeName: "NAME", | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: map[string]interface{}{ | ||||
| 				"default-runtime": "ALREADY_SET", | ||||
| 			}, | ||||
| 			defaultRuntime:             "", | ||||
| 			expectedDefaultRuntimeName: "ALREADY_SET", | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: map[string]interface{}{ | ||||
| 				"default-runtime": "ALREADY_SET", | ||||
| 			}, | ||||
| 			defaultRuntime:             "NAME", | ||||
| 			expectedDefaultRuntimeName: "NAME", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for i, tc := range testCases { | ||||
| 		t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) { | ||||
| 			if tc.config == nil { | ||||
| 				tc.config = make(map[string]interface{}) | ||||
| 			} | ||||
| 			err := UpdateConfig(tc.config, tc.defaultRuntime, nil) | ||||
| 			require.NoError(t, err) | ||||
| 
 | ||||
| 			defaultRuntimeName := tc.config["default-runtime"] | ||||
| 			require.EqualValues(t, tc.expectedDefaultRuntimeName, defaultRuntimeName) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestUpdateConfigRuntimes(t *testing.T) { | ||||
| 	testCases := []struct { | ||||
| 		config         map[string]interface{} | ||||
| 		runtimes       map[string]interface{} | ||||
| 		expectedConfig map[string]interface{} | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: map[string]interface{}{}, | ||||
| 			runtimes: map[string]interface{}{ | ||||
| 				"runtime1": map[string]interface{}{ | ||||
| 					"path": "/test/runtime/dir/runtime1", | ||||
| 					"args": []string{}, | ||||
| 				}, | ||||
| 				"runtime2": map[string]interface{}{ | ||||
| 					"path": "/test/runtime/dir/runtime2", | ||||
| 					"args": []string{}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedConfig: map[string]interface{}{ | ||||
| 				"runtimes": map[string]interface{}{ | ||||
| 					"runtime1": map[string]interface{}{ | ||||
| 						"path": "/test/runtime/dir/runtime1", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 					"runtime2": map[string]interface{}{ | ||||
| 						"path": "/test/runtime/dir/runtime2", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: map[string]interface{}{ | ||||
| 				"runtimes": map[string]interface{}{ | ||||
| 					"runtime1": map[string]interface{}{ | ||||
| 						"path": "runtime1", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			runtimes: map[string]interface{}{ | ||||
| 				"runtime1": map[string]interface{}{ | ||||
| 					"path": "/test/runtime/dir/runtime1", | ||||
| 					"args": []string{}, | ||||
| 				}, | ||||
| 				"runtime2": map[string]interface{}{ | ||||
| 					"path": "/test/runtime/dir/runtime2", | ||||
| 					"args": []string{}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedConfig: map[string]interface{}{ | ||||
| 				"runtimes": map[string]interface{}{ | ||||
| 					"runtime1": map[string]interface{}{ | ||||
| 						"path": "/test/runtime/dir/runtime1", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 					"runtime2": map[string]interface{}{ | ||||
| 						"path": "/test/runtime/dir/runtime2", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: map[string]interface{}{ | ||||
| 				"runtimes": map[string]interface{}{ | ||||
| 					"not-nvidia": map[string]interface{}{ | ||||
| 						"path": "some-other-path", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			runtimes: map[string]interface{}{ | ||||
| 				"runtime1": map[string]interface{}{ | ||||
| 					"path": "/test/runtime/dir/runtime1", | ||||
| 					"args": []string{}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedConfig: map[string]interface{}{ | ||||
| 				"runtimes": map[string]interface{}{ | ||||
| 					"not-nvidia": map[string]interface{}{ | ||||
| 						"path": "some-other-path", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 					"runtime1": map[string]interface{}{ | ||||
| 						"path": "/test/runtime/dir/runtime1", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: map[string]interface{}{ | ||||
| 				"exec-opts":  []string{"native.cgroupdriver=systemd"}, | ||||
| 				"log-driver": "json-file", | ||||
| 				"log-opts": map[string]string{ | ||||
| 					"max-size": "100m", | ||||
| 				}, | ||||
| 				"storage-driver": "overlay2", | ||||
| 			}, | ||||
| 			runtimes: map[string]interface{}{ | ||||
| 				"runtime1": map[string]interface{}{ | ||||
| 					"path": "/test/runtime/dir/runtime1", | ||||
| 					"args": []string{}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedConfig: map[string]interface{}{ | ||||
| 				"exec-opts":  []string{"native.cgroupdriver=systemd"}, | ||||
| 				"log-driver": "json-file", | ||||
| 				"log-opts": map[string]string{ | ||||
| 					"max-size": "100m", | ||||
| 				}, | ||||
| 				"storage-driver": "overlay2", | ||||
| 				"runtimes": map[string]interface{}{ | ||||
| 					"runtime1": map[string]interface{}{ | ||||
| 						"path": "/test/runtime/dir/runtime1", | ||||
| 						"args": []string{}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: map[string]interface{}{ | ||||
| 				"exec-opts":  []string{"native.cgroupdriver=systemd"}, | ||||
| 				"log-driver": "json-file", | ||||
| 				"log-opts": map[string]string{ | ||||
| 					"max-size": "100m", | ||||
| 				}, | ||||
| 				"storage-driver": "overlay2", | ||||
| 			}, | ||||
| 			expectedConfig: map[string]interface{}{ | ||||
| 				"exec-opts":  []string{"native.cgroupdriver=systemd"}, | ||||
| 				"log-driver": "json-file", | ||||
| 				"log-opts": map[string]string{ | ||||
| 					"max-size": "100m", | ||||
| 				}, | ||||
| 				"storage-driver": "overlay2", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for i, tc := range testCases { | ||||
| 		t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) { | ||||
| 			err := UpdateConfig(tc.config, "", tc.runtimes) | ||||
| 			require.NoError(t, err) | ||||
| 
 | ||||
| 			configContent, err := json.MarshalIndent(tc.config, "", "    ") | ||||
| 			require.NoError(t, err) | ||||
| 
 | ||||
| 			expectedContent, err := json.MarshalIndent(tc.expectedConfig, "", "    ") | ||||
| 			require.NoError(t, err) | ||||
| 
 | ||||
| 			require.EqualValues(t, string(expectedContent), string(configContent)) | ||||
| 		}) | ||||
| 
 | ||||
| 	} | ||||
| } | ||||
| @ -17,16 +17,14 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/internal/config/docker" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| 	cli "github.com/urfave/cli/v2" | ||||
| ) | ||||
| @ -247,52 +245,15 @@ func ParseArgs(c *cli.Context) (string, error) { | ||||
| 
 | ||||
| // LoadConfig loads the docker config from disk
 | ||||
| func LoadConfig(config string) (map[string]interface{}, error) { | ||||
| 	log.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") | ||||
| 	} | ||||
| 
 | ||||
| 	cfg := make(map[string]interface{}) | ||||
| 
 | ||||
| 	if os.IsNotExist(err) { | ||||
| 		log.Infof("Config file does not exist, creating new one") | ||||
| 		return cfg, nil | ||||
| 	} | ||||
| 
 | ||||
| 	readBytes, err := ioutil.ReadFile(config) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to read config: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	reader := bytes.NewReader(readBytes) | ||||
| 	if err := json.NewDecoder(reader).Decode(&cfg); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	log.Infof("Successfully loaded config") | ||||
| 	return cfg, nil | ||||
| 	return docker.LoadConfig(config) | ||||
| } | ||||
| 
 | ||||
| // UpdateConfig updates the docker config to include the nvidia runtimes
 | ||||
| func UpdateConfig(config map[string]interface{}, o *options) error { | ||||
| 	defaultRuntime := o.getDefaultRuntime() | ||||
| 	if defaultRuntime != "" { | ||||
| 		config["default-runtime"] = defaultRuntime | ||||
| 	} | ||||
| 	runtimes := o.runtimes() | ||||
| 
 | ||||
| 	runtimes := make(map[string]interface{}) | ||||
| 	if _, exists := config["runtimes"]; exists { | ||||
| 		runtimes = config["runtimes"].(map[string]interface{}) | ||||
| 	} | ||||
| 
 | ||||
| 	for name, rt := range o.runtimes() { | ||||
| 		runtimes[name] = rt | ||||
| 	} | ||||
| 
 | ||||
| 	config["runtimes"] = runtimes | ||||
| 	return nil | ||||
| 	return docker.UpdateConfig(config, defaultRuntime, runtimes) | ||||
| } | ||||
| 
 | ||||
| //RevertConfig reverts the docker config to remove the nvidia runtime
 | ||||
| @ -320,36 +281,7 @@ func RevertConfig(config map[string]interface{}) error { | ||||
| 
 | ||||
| // FlushConfig flushes the updated/reverted config out to disk
 | ||||
| func FlushConfig(cfg map[string]interface{}, config string) error { | ||||
| 	log.Infof("Flushing config") | ||||
| 
 | ||||
| 	output, err := json.MarshalIndent(cfg, "", "    ") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to convert to JSON: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	switch len(output) { | ||||
| 	case 0: | ||||
| 		err := os.Remove(config) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to remove empty file: %v", err) | ||||
| 		} | ||||
| 		log.Infof("Config empty, removing file") | ||||
| 	default: | ||||
| 		f, err := os.Create(config) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to open %v for writing: %v", config, err) | ||||
| 		} | ||||
| 		defer f.Close() | ||||
| 
 | ||||
| 		_, err = f.WriteString(string(output)) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to write output: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	log.Infof("Successfully flushed config") | ||||
| 
 | ||||
| 	return nil | ||||
| 	return docker.FlushConfig(cfg, config) | ||||
| } | ||||
| 
 | ||||
| // RestartDocker restarts docker depending on the value of restartModeFlag
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user