mirror of
				https://github.com/NVIDIA/nvidia-container-toolkit
				synced 2025-06-26 18:18:24 +00:00 
			
		
		
		
	Merge branch 'CNT-1380/add-crio-config' into 'main'
Add support for updating crio config See merge request nvidia/container-toolkit/container-toolkit!176
This commit is contained in:
		
						commit
						0bc09665a8
					
				@ -22,7 +22,9 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/runtime/nvidia"
 | 
						"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/runtime/nvidia"
 | 
				
			||||||
 | 
						"github.com/NVIDIA/nvidia-container-toolkit/internal/config/crio"
 | 
				
			||||||
	"github.com/NVIDIA/nvidia-container-toolkit/internal/config/docker"
 | 
						"github.com/NVIDIA/nvidia-container-toolkit/internal/config/docker"
 | 
				
			||||||
 | 
						"github.com/pelletier/go-toml"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -31,6 +33,7 @@ const (
 | 
				
			|||||||
	defaultRuntime = "docker"
 | 
						defaultRuntime = "docker"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultDockerConfigFilePath = "/etc/docker/daemon.json"
 | 
						defaultDockerConfigFilePath = "/etc/docker/daemon.json"
 | 
				
			||||||
 | 
						defaultCrioConfigFilePath   = "/etc/crio/crio.conf"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type command struct {
 | 
					type command struct {
 | 
				
			||||||
@ -75,7 +78,7 @@ func (m command) build() *cli.Command {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		&cli.StringFlag{
 | 
							&cli.StringFlag{
 | 
				
			||||||
			Name:        "runtime",
 | 
								Name:        "runtime",
 | 
				
			||||||
			Usage:       "the target runtime engine. One of [docker]",
 | 
								Usage:       "the target runtime engine. One of [crio, docker]",
 | 
				
			||||||
			Value:       defaultRuntime,
 | 
								Value:       defaultRuntime,
 | 
				
			||||||
			Destination: &config.runtime,
 | 
								Destination: &config.runtime,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@ -108,6 +111,8 @@ func (m command) build() *cli.Command {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (m command) configureWrapper(c *cli.Context, config *config) error {
 | 
					func (m command) configureWrapper(c *cli.Context, config *config) error {
 | 
				
			||||||
	switch config.runtime {
 | 
						switch config.runtime {
 | 
				
			||||||
 | 
						case "crio":
 | 
				
			||||||
 | 
							return m.configureCrio(c, config)
 | 
				
			||||||
	case "docker":
 | 
						case "docker":
 | 
				
			||||||
		return m.configureDocker(c, config)
 | 
							return m.configureDocker(c, config)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -127,9 +132,12 @@ func (m command) configureDocker(c *cli.Context, config *config) error {
 | 
				
			|||||||
		return fmt.Errorf("unable to load config: %v", err)
 | 
							return fmt.Errorf("unable to load config: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultRuntime := config.nvidiaOptions.DefaultRuntime()
 | 
						err = docker.UpdateConfig(
 | 
				
			||||||
	runtimeConfig := config.nvidiaOptions.Runtime().DockerRuntimesConfig()
 | 
							cfg,
 | 
				
			||||||
	err = docker.UpdateConfig(cfg, defaultRuntime, runtimeConfig)
 | 
							config.nvidiaOptions.RuntimeName,
 | 
				
			||||||
 | 
							config.nvidiaOptions.RuntimePath,
 | 
				
			||||||
 | 
							config.nvidiaOptions.SetAsDefault,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("unable to update config: %v", err)
 | 
							return fmt.Errorf("unable to update config: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -152,3 +160,44 @@ func (m command) configureDocker(c *cli.Context, config *config) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// configureCrio updates the crio config to enable the NVIDIA Container Runtime
 | 
				
			||||||
 | 
					func (m command) configureCrio(c *cli.Context, config *config) error {
 | 
				
			||||||
 | 
						configFilePath := config.configFilePath
 | 
				
			||||||
 | 
						if configFilePath == "" {
 | 
				
			||||||
 | 
							configFilePath = defaultCrioConfigFilePath
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg, err := crio.LoadConfig(configFilePath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to load config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = crio.UpdateConfig(
 | 
				
			||||||
 | 
							cfg,
 | 
				
			||||||
 | 
							config.nvidiaOptions.RuntimeName,
 | 
				
			||||||
 | 
							config.nvidiaOptions.RuntimePath,
 | 
				
			||||||
 | 
							config.nvidiaOptions.SetAsDefault,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to update config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config.dryRun {
 | 
				
			||||||
 | 
							output, err := toml.Marshal(cfg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("unable to convert to TOML: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							os.Stdout.WriteString(fmt.Sprintf("%s\n", output))
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = crio.FlushConfig(configFilePath, cfg)
 | 
				
			||||||
 | 
						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 cri-o daemon be restarted.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										125
									
								
								internal/config/crio/crio.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								internal/config/crio/crio.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,125 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					# 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 crio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/pelletier/go-toml"
 | 
				
			||||||
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadConfig loads the cri-o config from disk
 | 
				
			||||||
 | 
					func LoadConfig(config string) (*toml.Tree, 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")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configFile := config
 | 
				
			||||||
 | 
						if os.IsNotExist(err) {
 | 
				
			||||||
 | 
							configFile = "/dev/null"
 | 
				
			||||||
 | 
							log.Infof("Config file does not exist, creating new one")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg, err := toml.LoadFile(configFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Infof("Successfully loaded config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cfg, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateConfig updates the cri-o config to include the NVIDIA Container Runtime
 | 
				
			||||||
 | 
					func UpdateConfig(config *toml.Tree, runtimeClass string, runtimePath string, setAsDefault bool) error {
 | 
				
			||||||
 | 
						switch runc := config.Get("crio.runtime.runtimes.runc").(type) {
 | 
				
			||||||
 | 
						case *toml.Tree:
 | 
				
			||||||
 | 
							runc, _ = toml.Load(runc.String())
 | 
				
			||||||
 | 
							config.SetPath([]string{"crio", "runtime", "runtimes", runtimeClass}, runc)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config.SetPath([]string{"crio", "runtime", "runtimes", runtimeClass, "runtime_path"}, runtimePath)
 | 
				
			||||||
 | 
						config.SetPath([]string{"crio", "runtime", "runtimes", runtimeClass, "runtime_type"}, "oci")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if setAsDefault {
 | 
				
			||||||
 | 
							config.SetPath([]string{"crio", "runtime", "default_runtime"}, runtimeClass)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RevertConfig reverts the cri-o config to remove the NVIDIA Container Runtime
 | 
				
			||||||
 | 
					func RevertConfig(config *toml.Tree, runtimeClass string) error {
 | 
				
			||||||
 | 
						if runtime, ok := config.GetPath([]string{"crio", "runtime", "default_runtime"}).(string); ok {
 | 
				
			||||||
 | 
							if runtimeClass == runtime {
 | 
				
			||||||
 | 
								config.DeletePath([]string{"crio", "runtime", "default_runtime"})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runtimeClassPath := []string{"crio", "runtime", "runtimes", runtimeClass}
 | 
				
			||||||
 | 
						config.DeletePath(runtimeClassPath)
 | 
				
			||||||
 | 
						for i := 0; i < len(runtimeClassPath); i++ {
 | 
				
			||||||
 | 
							remainingPath := runtimeClassPath[:len(runtimeClassPath)-i]
 | 
				
			||||||
 | 
							if entry, ok := config.GetPath(remainingPath).(*toml.Tree); ok {
 | 
				
			||||||
 | 
								if len(entry.Keys()) != 0 {
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								config.DeletePath(remainingPath)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FlushConfig flushes the updated/reverted config out to disk
 | 
				
			||||||
 | 
					func FlushConfig(config string, cfg *toml.Tree) error {
 | 
				
			||||||
 | 
						log.Infof("Flushing config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output, err := cfg.ToTomlString()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to convert to TOML: %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(output)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("unable to write output: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Infof("Successfully flushed config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -57,11 +57,7 @@ func LoadConfig(configFilePath string) (map[string]interface{}, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateConfig updates the docker config to include the nvidia runtimes
 | 
					// UpdateConfig updates the docker config to include the nvidia runtimes
 | 
				
			||||||
func UpdateConfig(config map[string]interface{}, defaultRuntime string, newRuntimes map[string]interface{}) error {
 | 
					func UpdateConfig(config map[string]interface{}, runtimeName string, runtimePath string, setAsDefault bool) error {
 | 
				
			||||||
	if defaultRuntime != "" {
 | 
					 | 
				
			||||||
		config["default-runtime"] = defaultRuntime
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Read the existing runtimes
 | 
						// Read the existing runtimes
 | 
				
			||||||
	runtimes := make(map[string]interface{})
 | 
						runtimes := make(map[string]interface{})
 | 
				
			||||||
	if _, exists := config["runtimes"]; exists {
 | 
						if _, exists := config["runtimes"]; exists {
 | 
				
			||||||
@ -69,14 +65,20 @@ func UpdateConfig(config map[string]interface{}, defaultRuntime string, newRunti
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Add / update the runtime definitions
 | 
						// Add / update the runtime definitions
 | 
				
			||||||
	for name, rt := range newRuntimes {
 | 
						runtimes[runtimeName] = map[string]interface{}{
 | 
				
			||||||
		runtimes[name] = rt
 | 
							"path": runtimePath,
 | 
				
			||||||
 | 
							"args": []string{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update the runtimes definition
 | 
						// Update the runtimes definition
 | 
				
			||||||
	if len(runtimes) > 0 {
 | 
						if len(runtimes) > 0 {
 | 
				
			||||||
		config["runtimes"] = runtimes
 | 
							config["runtimes"] = runtimes
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if setAsDefault {
 | 
				
			||||||
 | 
							config["default-runtime"] = runtimeName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -27,30 +27,33 @@ import (
 | 
				
			|||||||
func TestUpdateConfigDefaultRuntime(t *testing.T) {
 | 
					func TestUpdateConfigDefaultRuntime(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		config                     map[string]interface{}
 | 
							config                     map[string]interface{}
 | 
				
			||||||
		defaultRuntime             string
 | 
					 | 
				
			||||||
		runtimeName                string
 | 
							runtimeName                string
 | 
				
			||||||
 | 
							setAsDefault               bool
 | 
				
			||||||
		expectedDefaultRuntimeName interface{}
 | 
							expectedDefaultRuntimeName interface{}
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			defaultRuntime:             "",
 | 
								setAsDefault:               false,
 | 
				
			||||||
			expectedDefaultRuntimeName: nil,
 | 
								expectedDefaultRuntimeName: nil,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			defaultRuntime:             "NAME",
 | 
								runtimeName:                "NAME",
 | 
				
			||||||
 | 
								setAsDefault:               true,
 | 
				
			||||||
			expectedDefaultRuntimeName: "NAME",
 | 
								expectedDefaultRuntimeName: "NAME",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			config: map[string]interface{}{
 | 
								config: map[string]interface{}{
 | 
				
			||||||
				"default-runtime": "ALREADY_SET",
 | 
									"default-runtime": "ALREADY_SET",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			defaultRuntime:             "",
 | 
								runtimeName:                "NAME",
 | 
				
			||||||
 | 
								setAsDefault:               false,
 | 
				
			||||||
			expectedDefaultRuntimeName: "ALREADY_SET",
 | 
								expectedDefaultRuntimeName: "ALREADY_SET",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			config: map[string]interface{}{
 | 
								config: map[string]interface{}{
 | 
				
			||||||
				"default-runtime": "ALREADY_SET",
 | 
									"default-runtime": "ALREADY_SET",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			defaultRuntime:             "NAME",
 | 
								runtimeName:                "NAME",
 | 
				
			||||||
 | 
								setAsDefault:               true,
 | 
				
			||||||
			expectedDefaultRuntimeName: "NAME",
 | 
								expectedDefaultRuntimeName: "NAME",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -60,7 +63,7 @@ func TestUpdateConfigDefaultRuntime(t *testing.T) {
 | 
				
			|||||||
			if tc.config == nil {
 | 
								if tc.config == nil {
 | 
				
			||||||
				tc.config = make(map[string]interface{})
 | 
									tc.config = make(map[string]interface{})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			err := UpdateConfig(tc.config, tc.defaultRuntime, nil)
 | 
								err := UpdateConfig(tc.config, tc.runtimeName, "", tc.setAsDefault)
 | 
				
			||||||
			require.NoError(t, err)
 | 
								require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			defaultRuntimeName := tc.config["default-runtime"]
 | 
								defaultRuntimeName := tc.config["default-runtime"]
 | 
				
			||||||
@ -72,20 +75,14 @@ func TestUpdateConfigDefaultRuntime(t *testing.T) {
 | 
				
			|||||||
func TestUpdateConfigRuntimes(t *testing.T) {
 | 
					func TestUpdateConfigRuntimes(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		config         map[string]interface{}
 | 
							config         map[string]interface{}
 | 
				
			||||||
		runtimes       map[string]interface{}
 | 
							runtimes       map[string]string
 | 
				
			||||||
		expectedConfig map[string]interface{}
 | 
							expectedConfig map[string]interface{}
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			config: map[string]interface{}{},
 | 
								config: map[string]interface{}{},
 | 
				
			||||||
			runtimes: map[string]interface{}{
 | 
								runtimes: map[string]string{
 | 
				
			||||||
				"runtime1": map[string]interface{}{
 | 
									"runtime1": "/test/runtime/dir/runtime1",
 | 
				
			||||||
					"path": "/test/runtime/dir/runtime1",
 | 
									"runtime2": "/test/runtime/dir/runtime2",
 | 
				
			||||||
					"args": []string{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				"runtime2": map[string]interface{}{
 | 
					 | 
				
			||||||
					"path": "/test/runtime/dir/runtime2",
 | 
					 | 
				
			||||||
					"args": []string{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectedConfig: map[string]interface{}{
 | 
								expectedConfig: map[string]interface{}{
 | 
				
			||||||
				"runtimes": map[string]interface{}{
 | 
									"runtimes": map[string]interface{}{
 | 
				
			||||||
@ -109,15 +106,9 @@ func TestUpdateConfigRuntimes(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			runtimes: map[string]interface{}{
 | 
								runtimes: map[string]string{
 | 
				
			||||||
				"runtime1": map[string]interface{}{
 | 
									"runtime1": "/test/runtime/dir/runtime1",
 | 
				
			||||||
					"path": "/test/runtime/dir/runtime1",
 | 
									"runtime2": "/test/runtime/dir/runtime2",
 | 
				
			||||||
					"args": []string{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				"runtime2": map[string]interface{}{
 | 
					 | 
				
			||||||
					"path": "/test/runtime/dir/runtime2",
 | 
					 | 
				
			||||||
					"args": []string{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectedConfig: map[string]interface{}{
 | 
								expectedConfig: map[string]interface{}{
 | 
				
			||||||
				"runtimes": map[string]interface{}{
 | 
									"runtimes": map[string]interface{}{
 | 
				
			||||||
@ -141,11 +132,8 @@ func TestUpdateConfigRuntimes(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			runtimes: map[string]interface{}{
 | 
								runtimes: map[string]string{
 | 
				
			||||||
				"runtime1": map[string]interface{}{
 | 
									"runtime1": "/test/runtime/dir/runtime1",
 | 
				
			||||||
					"path": "/test/runtime/dir/runtime1",
 | 
					 | 
				
			||||||
					"args": []string{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectedConfig: map[string]interface{}{
 | 
								expectedConfig: map[string]interface{}{
 | 
				
			||||||
				"runtimes": map[string]interface{}{
 | 
									"runtimes": map[string]interface{}{
 | 
				
			||||||
@ -169,11 +157,8 @@ func TestUpdateConfigRuntimes(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
				"storage-driver": "overlay2",
 | 
									"storage-driver": "overlay2",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			runtimes: map[string]interface{}{
 | 
								runtimes: map[string]string{
 | 
				
			||||||
				"runtime1": map[string]interface{}{
 | 
									"runtime1": "/test/runtime/dir/runtime1",
 | 
				
			||||||
					"path": "/test/runtime/dir/runtime1",
 | 
					 | 
				
			||||||
					"args": []string{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expectedConfig: map[string]interface{}{
 | 
								expectedConfig: map[string]interface{}{
 | 
				
			||||||
				"exec-opts":  []string{"native.cgroupdriver=systemd"},
 | 
									"exec-opts":  []string{"native.cgroupdriver=systemd"},
 | 
				
			||||||
@ -212,8 +197,10 @@ func TestUpdateConfigRuntimes(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for i, tc := range testCases {
 | 
						for i, tc := range testCases {
 | 
				
			||||||
		t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) {
 | 
							t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) {
 | 
				
			||||||
			err := UpdateConfig(tc.config, "", tc.runtimes)
 | 
								for runtimeName, runtimePath := range tc.runtimes {
 | 
				
			||||||
			require.NoError(t, err)
 | 
									err := UpdateConfig(tc.config, runtimeName, runtimePath, false)
 | 
				
			||||||
 | 
									require.NoError(t, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			configContent, err := json.MarshalIndent(tc.config, "", "    ")
 | 
								configContent, err := json.MarshalIndent(tc.config, "", "    ")
 | 
				
			||||||
			require.NoError(t, err)
 | 
								require.NoError(t, err)
 | 
				
			||||||
 | 
				
			|||||||
@ -20,23 +20,52 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
 | 
						"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
 | 
				
			||||||
 | 
						"github.com/NVIDIA/nvidia-container-toolkit/internal/config/crio"
 | 
				
			||||||
 | 
						"github.com/pelletier/go-toml"
 | 
				
			||||||
	log "github.com/sirupsen/logrus"
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
	cli "github.com/urfave/cli/v2"
 | 
						cli "github.com/urfave/cli/v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 | 
						restartModeSystemd = "systemd"
 | 
				
			||||||
 | 
						restartModeNone    = "none"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defaultConfigMode = "hook"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Hook-based settings
 | 
				
			||||||
	defaultHooksDir     = "/usr/share/containers/oci/hooks.d"
 | 
						defaultHooksDir     = "/usr/share/containers/oci/hooks.d"
 | 
				
			||||||
	defaultHookFilename = "oci-nvidia-hook.json"
 | 
						defaultHookFilename = "oci-nvidia-hook.json"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Config-based settings
 | 
				
			||||||
 | 
						defaultConfig        = "/etc/crio/crio.conf"
 | 
				
			||||||
 | 
						defaultRuntimeClass  = "nvidia"
 | 
				
			||||||
 | 
						defaultSetAsDefault  = true
 | 
				
			||||||
 | 
						defaultRestartMode   = restartModeSystemd
 | 
				
			||||||
 | 
						defaultHostRootMount = "/host"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var hooksDirFlag string
 | 
					// options stores the configuration from the command linek or environment variables
 | 
				
			||||||
var hookFilenameFlag string
 | 
					type options struct {
 | 
				
			||||||
var tooklitDirArg string
 | 
						configMode string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hooksDir     string
 | 
				
			||||||
 | 
						hookFilename string
 | 
				
			||||||
 | 
						runtimeDir   string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config        string
 | 
				
			||||||
 | 
						runtimeClass  string
 | 
				
			||||||
 | 
						setAsDefault  bool
 | 
				
			||||||
 | 
						restartMode   string
 | 
				
			||||||
 | 
						hostRootMount string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 | 
						options := options{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create the top-level CLI
 | 
						// Create the top-level CLI
 | 
				
			||||||
	c := cli.NewApp()
 | 
						c := cli.NewApp()
 | 
				
			||||||
	c.Name = "crio"
 | 
						c.Name = "crio"
 | 
				
			||||||
@ -47,17 +76,22 @@ func main() {
 | 
				
			|||||||
	// Create the 'setup' subcommand
 | 
						// Create the 'setup' subcommand
 | 
				
			||||||
	setup := cli.Command{}
 | 
						setup := cli.Command{}
 | 
				
			||||||
	setup.Name = "setup"
 | 
						setup.Name = "setup"
 | 
				
			||||||
	setup.Usage = "Create the cri-o hook required to run NVIDIA GPU containers"
 | 
						setup.Usage = "Configure cri-o for NVIDIA GPU containers"
 | 
				
			||||||
	setup.ArgsUsage = "<toolkit_dirname>"
 | 
						setup.ArgsUsage = "<toolkit_dirname>"
 | 
				
			||||||
	setup.Action = Setup
 | 
						setup.Action = func(c *cli.Context) error {
 | 
				
			||||||
	setup.Before = ParseArgs
 | 
							return Setup(c, &options)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						setup.Before = func(c *cli.Context) error {
 | 
				
			||||||
 | 
							return ParseArgs(c, &options)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create the 'cleanup' subcommand
 | 
						// Create the 'cleanup' subcommand
 | 
				
			||||||
	cleanup := cli.Command{}
 | 
						cleanup := cli.Command{}
 | 
				
			||||||
	cleanup.Name = "cleanup"
 | 
						cleanup.Name = "cleanup"
 | 
				
			||||||
	cleanup.Usage = "Remove the NVIDIA cri-o hook"
 | 
						cleanup.Usage = "Remove the NVIDIA-specific cri-o configuration"
 | 
				
			||||||
	cleanup.Action = Cleanup
 | 
						cleanup.Action = func(c *cli.Context) error {
 | 
				
			||||||
 | 
							return Cleanup(c, &options)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// Register the subcommands with the top-level CLI
 | 
						// Register the subcommands with the top-level CLI
 | 
				
			||||||
	c.Commands = []*cli.Command{
 | 
						c.Commands = []*cli.Command{
 | 
				
			||||||
		&setup,
 | 
							&setup,
 | 
				
			||||||
@ -74,7 +108,7 @@ func main() {
 | 
				
			|||||||
			Aliases:     []string{"d"},
 | 
								Aliases:     []string{"d"},
 | 
				
			||||||
			Usage:       "path to the cri-o hooks directory",
 | 
								Usage:       "path to the cri-o hooks directory",
 | 
				
			||||||
			Value:       defaultHooksDir,
 | 
								Value:       defaultHooksDir,
 | 
				
			||||||
			Destination: &hooksDirFlag,
 | 
								Destination: &options.hooksDir,
 | 
				
			||||||
			EnvVars:     []string{"CRIO_HOOKS_DIR"},
 | 
								EnvVars:     []string{"CRIO_HOOKS_DIR"},
 | 
				
			||||||
			DefaultText: defaultHooksDir,
 | 
								DefaultText: defaultHooksDir,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@ -83,10 +117,54 @@ func main() {
 | 
				
			|||||||
			Aliases:     []string{"f"},
 | 
								Aliases:     []string{"f"},
 | 
				
			||||||
			Usage:       "filename of the cri-o hook that will be created / removed in the hooks directory",
 | 
								Usage:       "filename of the cri-o hook that will be created / removed in the hooks directory",
 | 
				
			||||||
			Value:       defaultHookFilename,
 | 
								Value:       defaultHookFilename,
 | 
				
			||||||
			Destination: &hookFilenameFlag,
 | 
								Destination: &options.hookFilename,
 | 
				
			||||||
			EnvVars:     []string{"CRIO_HOOK_FILENAME"},
 | 
								EnvVars:     []string{"CRIO_HOOK_FILENAME"},
 | 
				
			||||||
			DefaultText: defaultHookFilename,
 | 
								DefaultText: defaultHookFilename,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							&cli.StringFlag{
 | 
				
			||||||
 | 
								Name:        "config-mode",
 | 
				
			||||||
 | 
								Usage:       "the configuration mode to use. One of [hook | config]",
 | 
				
			||||||
 | 
								Value:       defaultConfigMode,
 | 
				
			||||||
 | 
								Destination: &options.configMode,
 | 
				
			||||||
 | 
								EnvVars:     []string{"CRIO_CONFIG_MODE"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							&cli.StringFlag{
 | 
				
			||||||
 | 
								Name:        "config",
 | 
				
			||||||
 | 
								Usage:       "Path to the cri-o config file",
 | 
				
			||||||
 | 
								Value:       defaultConfig,
 | 
				
			||||||
 | 
								Destination: &options.config,
 | 
				
			||||||
 | 
								EnvVars:     []string{"CRIO_CONFIG"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							&cli.StringFlag{
 | 
				
			||||||
 | 
								Name:        "runtime-class",
 | 
				
			||||||
 | 
								Usage:       "The name of the runtime class to set for the nvidia-container-runtime",
 | 
				
			||||||
 | 
								Value:       defaultRuntimeClass,
 | 
				
			||||||
 | 
								Destination: &options.runtimeClass,
 | 
				
			||||||
 | 
								EnvVars:     []string{"CRIO_RUNTIME_CLASS"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							// The flags below are only used by the 'setup' command.
 | 
				
			||||||
 | 
							&cli.BoolFlag{
 | 
				
			||||||
 | 
								Name:        "set-as-default",
 | 
				
			||||||
 | 
								Usage:       "Set nvidia-container-runtime as the default runtime",
 | 
				
			||||||
 | 
								Value:       defaultSetAsDefault,
 | 
				
			||||||
 | 
								Destination: &options.setAsDefault,
 | 
				
			||||||
 | 
								EnvVars:     []string{"CRIO_SET_AS_DEFAULT"},
 | 
				
			||||||
 | 
								Hidden:      true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							&cli.StringFlag{
 | 
				
			||||||
 | 
								Name:        "restart-mode",
 | 
				
			||||||
 | 
								Usage:       "Specify how cri-o should be restarted;  If 'none' is selected, it will not be restarted [systemd | none]",
 | 
				
			||||||
 | 
								Value:       defaultRestartMode,
 | 
				
			||||||
 | 
								Destination: &options.restartMode,
 | 
				
			||||||
 | 
								EnvVars:     []string{"CRIO_RESTART_MODE"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							&cli.StringFlag{
 | 
				
			||||||
 | 
								Name:        "host-root",
 | 
				
			||||||
 | 
								Usage:       "Specify the path to the host root to be used when restarting crio using systemd",
 | 
				
			||||||
 | 
								Value:       defaultHostRootMount,
 | 
				
			||||||
 | 
								Destination: &options.hostRootMount,
 | 
				
			||||||
 | 
								EnvVars:     []string{"HOST_ROOT_MOUNT"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update the subcommand flags with the common subcommand flags
 | 
						// Update the subcommand flags with the common subcommand flags
 | 
				
			||||||
@ -100,16 +178,30 @@ func main() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Setup installs the prestart hook required to launch GPU-enabled containers
 | 
					// Setup installs the prestart hook required to launch GPU-enabled containers
 | 
				
			||||||
func Setup(c *cli.Context) error {
 | 
					func Setup(c *cli.Context, o *options) error {
 | 
				
			||||||
	log.Infof("Starting 'setup' for %v", c.App.Name)
 | 
						log.Infof("Starting 'setup' for %v", c.App.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := os.MkdirAll(hooksDirFlag, 0755)
 | 
						switch o.configMode {
 | 
				
			||||||
 | 
						case "hook":
 | 
				
			||||||
 | 
							return setupHook(o)
 | 
				
			||||||
 | 
						case "config":
 | 
				
			||||||
 | 
							return setupConfig(o)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid config-mode '%v'", o.configMode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setupHook installs the prestart hook required to launch GPU-enabled containers
 | 
				
			||||||
 | 
					func setupHook(o *options) error {
 | 
				
			||||||
 | 
						log.Infof("Installing prestart hook")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := os.MkdirAll(o.hooksDir, 0755)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("error creating hooks directory %v: %v", hooksDirFlag, err)
 | 
							return fmt.Errorf("error creating hooks directory %v: %v", o.hooksDir, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hookPath := getHookPath(hooksDirFlag, hookFilenameFlag)
 | 
						hookPath := getHookPath(o.hooksDir, o.hookFilename)
 | 
				
			||||||
	err = createHook(tooklitDirArg, hookPath)
 | 
						err = createHook(o.runtimeDir, hookPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("error creating hook: %v", err)
 | 
							return fmt.Errorf("error creating hook: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -117,11 +209,52 @@ func Setup(c *cli.Context) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setupConfig updates the cri-o config for the NVIDIA container runtime
 | 
				
			||||||
 | 
					func setupConfig(o *options) error {
 | 
				
			||||||
 | 
						log.Infof("Updating config file")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg, err := crio.LoadConfig(o.config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to load config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = UpdateConfig(cfg, o)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to update config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = crio.FlushConfig(o.config, cfg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to flush config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = RestartCrio(o)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to restart crio: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Cleanup removes the specified prestart hook
 | 
					// Cleanup removes the specified prestart hook
 | 
				
			||||||
func Cleanup(c *cli.Context) error {
 | 
					func Cleanup(c *cli.Context, o *options) error {
 | 
				
			||||||
	log.Infof("Starting 'cleanup' for %v", c.App.Name)
 | 
						log.Infof("Starting 'cleanup' for %v", c.App.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hookPath := getHookPath(hooksDirFlag, hookFilenameFlag)
 | 
						switch o.configMode {
 | 
				
			||||||
 | 
						case "hook":
 | 
				
			||||||
 | 
							return cleanupHook(o)
 | 
				
			||||||
 | 
						case "config":
 | 
				
			||||||
 | 
							return cleanupConfig(o)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid config-mode '%v'", o.configMode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// cleanupHook removes the prestart hook
 | 
				
			||||||
 | 
					func cleanupHook(o *options) error {
 | 
				
			||||||
 | 
						log.Infof("Removing prestart hook")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hookPath := getHookPath(o.hooksDir, o.hookFilename)
 | 
				
			||||||
	err := os.Remove(hookPath)
 | 
						err := os.Remove(hookPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("error removing hook '%v': %v", hookPath, err)
 | 
							return fmt.Errorf("error removing hook '%v': %v", hookPath, err)
 | 
				
			||||||
@ -130,15 +263,42 @@ func Cleanup(c *cli.Context) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// cleanupConfig removes the NVIDIA container runtime from the cri-o config
 | 
				
			||||||
 | 
					func cleanupConfig(o *options) error {
 | 
				
			||||||
 | 
						log.Infof("Reverting config file modifications")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg, err := crio.LoadConfig(o.config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to load config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = RevertConfig(cfg, o)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to update config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = crio.FlushConfig(o.config, cfg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to flush config: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = RestartCrio(o)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("unable to restart crio: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseArgs parses the command line arguments to the CLI
 | 
					// ParseArgs parses the command line arguments to the CLI
 | 
				
			||||||
func ParseArgs(c *cli.Context) error {
 | 
					func ParseArgs(c *cli.Context, o *options) error {
 | 
				
			||||||
	args := c.Args()
 | 
						args := c.Args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Infof("Parsing arguments: %v", args.Slice())
 | 
						log.Infof("Parsing arguments: %v", args.Slice())
 | 
				
			||||||
	if c.NArg() != 1 {
 | 
						if c.NArg() != 1 {
 | 
				
			||||||
		return fmt.Errorf("incorrect number of arguments")
 | 
							return fmt.Errorf("incorrect number of arguments")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tooklitDirArg = args.Get(0)
 | 
						o.runtimeDir = args.Get(0)
 | 
				
			||||||
	log.Infof("Successfully parsed arguments")
 | 
						log.Infof("Successfully parsed arguments")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
@ -152,7 +312,7 @@ func createHook(toolkitDir string, hookPath string) error {
 | 
				
			|||||||
	defer hook.Close()
 | 
						defer hook.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	encoder := json.NewEncoder(hook)
 | 
						encoder := json.NewEncoder(hook)
 | 
				
			||||||
	err = encoder.Encode(generateOciHook(tooklitDirArg))
 | 
						err = encoder.Encode(generateOciHook(toolkitDir))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("error writing hook file '%v': %v", hookPath, err)
 | 
							return fmt.Errorf("error writing hook file '%v': %v", hookPath, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -183,3 +343,45 @@ func generateOciHook(toolkitDir string) podmanHook {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return hook
 | 
						return hook
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateConfig updates the cri-o config to include the NVIDIA Container Runtime
 | 
				
			||||||
 | 
					func UpdateConfig(config *toml.Tree, o *options) error {
 | 
				
			||||||
 | 
						runtimePath := filepath.Join(o.runtimeDir, "nvidia-container-runtime")
 | 
				
			||||||
 | 
						return crio.UpdateConfig(config, o.runtimeClass, runtimePath, o.setAsDefault)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RevertConfig reverts the cri-o config to remove the NVIDIA Container Runtime
 | 
				
			||||||
 | 
					func RevertConfig(config *toml.Tree, o *options) error {
 | 
				
			||||||
 | 
						return crio.RevertConfig(config, o.runtimeClass)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RestartCrio restarts crio depending on the value of restartModeFlag
 | 
				
			||||||
 | 
					func RestartCrio(o *options) error {
 | 
				
			||||||
 | 
						switch o.restartMode {
 | 
				
			||||||
 | 
						case restartModeNone:
 | 
				
			||||||
 | 
							log.Warnf("Skipping restart of crio due to --restart-mode=%v", o.restartMode)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						case restartModeSystemd:
 | 
				
			||||||
 | 
							return RestartCrioSystemd(o.hostRootMount)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid restart mode specified: %v", o.restartMode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RestartCrioSystemd restarts cri-o using systemctl
 | 
				
			||||||
 | 
					func RestartCrioSystemd(hostRootMount string) error {
 | 
				
			||||||
 | 
						log.Infof("Restarting cri-o using systemd and host root mounted at %v", hostRootMount)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						command := "chroot"
 | 
				
			||||||
 | 
						args := []string{hostRootMount, "systemctl", "restart", "crio"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := exec.Command(command, args...)
 | 
				
			||||||
 | 
						cmd.Stdout = os.Stdout
 | 
				
			||||||
 | 
						cmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
						err := cmd.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("error restarting crio using systemd: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -250,10 +250,15 @@ func LoadConfig(config string) (map[string]interface{}, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// UpdateConfig updates the docker config to include the nvidia runtimes
 | 
					// UpdateConfig updates the docker config to include the nvidia runtimes
 | 
				
			||||||
func UpdateConfig(config map[string]interface{}, o *options) error {
 | 
					func UpdateConfig(config map[string]interface{}, o *options) error {
 | 
				
			||||||
	defaultRuntime := o.getDefaultRuntime()
 | 
						for runtimeName, runtimePath := range o.getRuntimeBinaries() {
 | 
				
			||||||
	runtimes := o.runtimes()
 | 
							setAsDefault := runtimeName == o.getDefaultRuntime()
 | 
				
			||||||
 | 
							err := docker.UpdateConfig(config, runtimeName, runtimePath, setAsDefault)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to update runtime %q: %v", runtimeName, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return docker.UpdateConfig(config, defaultRuntime, runtimes)
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//RevertConfig reverts the docker config to remove the nvidia runtime
 | 
					//RevertConfig reverts the docker config to remove the nvidia runtime
 | 
				
			||||||
@ -392,19 +397,6 @@ func (o options) getDefaultRuntime() string {
 | 
				
			|||||||
	return o.runtimeName
 | 
						return o.runtimeName
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// runtimes returns the docker runtime definitions for the supported nvidia runtimes
 | 
					 | 
				
			||||||
// for the given options. This includes the path with the options runtimeDir applied
 | 
					 | 
				
			||||||
func (o options) runtimes() map[string]interface{} {
 | 
					 | 
				
			||||||
	runtimes := make(map[string]interface{})
 | 
					 | 
				
			||||||
	for r, bin := range o.getRuntimeBinaries() {
 | 
					 | 
				
			||||||
		runtimes[r] = map[string]interface{}{
 | 
					 | 
				
			||||||
			"path": bin,
 | 
					 | 
				
			||||||
			"args": []string{},
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return runtimes
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getRuntimeBinaries returns a map of runtime names to binary paths. This includes the
 | 
					// getRuntimeBinaries returns a map of runtime names to binary paths. This includes the
 | 
				
			||||||
// renaming of the `nvidia` runtime as per the --runtime-class command line flag.
 | 
					// renaming of the `nvidia` runtime as per the --runtime-class command line flag.
 | 
				
			||||||
func (o options) getRuntimeBinaries() map[string]string {
 | 
					func (o options) getRuntimeBinaries() map[string]string {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user