mirror of
				https://github.com/NVIDIA/nvidia-container-toolkit
				synced 2025-06-26 18:18:24 +00:00 
			
		
		
		
	Add support for adding additional containerd configs
This allow for options such as SystemdCgroup to be optionally set. Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
		
							parent
							
								
									c86c3aeeaf
								
							
						
					
					
						commit
						b435b797af
					
				| @ -17,6 +17,7 @@ | |||||||
| package configure | package configure | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 
 | 
 | ||||||
| @ -66,6 +67,8 @@ type config struct { | |||||||
| 	mode           string | 	mode           string | ||||||
| 	hookFilePath   string | 	hookFilePath   string | ||||||
| 
 | 
 | ||||||
|  | 	runtimeConfigOverrideJSON string | ||||||
|  | 
 | ||||||
| 	nvidiaRuntime struct { | 	nvidiaRuntime struct { | ||||||
| 		name         string | 		name         string | ||||||
| 		path         string | 		path         string | ||||||
| @ -153,6 +156,13 @@ func (m command) build() *cli.Command { | |||||||
| 			Usage:       "Enable CDI in the configured runtime", | 			Usage:       "Enable CDI in the configured runtime", | ||||||
| 			Destination: &config.cdi.enabled, | 			Destination: &config.cdi.enabled, | ||||||
| 		}, | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:        "runtime-config-override", | ||||||
|  | 			Destination: &config.runtimeConfigOverrideJSON, | ||||||
|  | 			Usage:       "specify additional runtime options as a JSON string. The paths are relative to the runtime config.", | ||||||
|  | 			Value:       "{}", | ||||||
|  | 			EnvVars:     []string{"RUNTIME_CONFIG_OVERRIDE"}, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &configure | 	return &configure | ||||||
| @ -194,6 +204,11 @@ func (m command) validateFlags(c *cli.Context, config *config) error { | |||||||
| 		config.cdi.enabled = false | 		config.cdi.enabled = false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if config.runtimeConfigOverrideJSON != "" && config.runtime != "containerd" { | ||||||
|  | 		m.logger.Warningf("Ignoring runtime-config-override flag for %v", config.runtime) | ||||||
|  | 		config.runtimeConfigOverrideJSON = "" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -237,10 +252,16 @@ func (m command) configureConfigFile(c *cli.Context, config *config) error { | |||||||
| 		return fmt.Errorf("unable to load config for runtime %v: %v", config.runtime, err) | 		return fmt.Errorf("unable to load config for runtime %v: %v", config.runtime, err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	runtimeConfigOverride, err := config.runtimeConfigOverride() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("unable to parse config overrides: %w", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	err = cfg.AddRuntime( | 	err = cfg.AddRuntime( | ||||||
| 		config.nvidiaRuntime.name, | 		config.nvidiaRuntime.name, | ||||||
| 		config.nvidiaRuntime.path, | 		config.nvidiaRuntime.path, | ||||||
| 		config.nvidiaRuntime.setAsDefault, | 		config.nvidiaRuntime.setAsDefault, | ||||||
|  | 		runtimeConfigOverride, | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("unable to update config: %v", err) | 		return fmt.Errorf("unable to update config: %v", err) | ||||||
| @ -293,6 +314,20 @@ func (c *config) getOuputConfigPath() string { | |||||||
| 	return c.resolveConfigFilePath() | 	return c.resolveConfigFilePath() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // runtimeConfigOverride converts the specified runtimeConfigOverride JSON string to a map.
 | ||||||
|  | func (o *config) runtimeConfigOverride() (map[string]interface{}, error) { | ||||||
|  | 	if o.runtimeConfigOverrideJSON == "" { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	runtimeOptions := make(map[string]interface{}) | ||||||
|  | 	if err := json.Unmarshal([]byte(o.runtimeConfigOverrideJSON), &runtimeOptions); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to read %v as JSON: %w", o.runtimeConfigOverrideJSON, err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return runtimeOptions, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // configureOCIHook creates and configures the OCI hook for the NVIDIA runtime
 | // configureOCIHook creates and configures the OCI hook for the NVIDIA runtime
 | ||||||
| func (m *command) configureOCIHook(c *cli.Context, config *config) error { | func (m *command) configureOCIHook(c *cli.Context, config *config) error { | ||||||
| 	err := ocihook.CreateHook(config.hookFilePath, config.nvidiaRuntime.hookPath) | 	err := ocihook.CreateHook(config.hookFilePath, config.nvidiaRuntime.hookPath) | ||||||
|  | |||||||
| @ -67,8 +67,8 @@ func ParseArgs(c *cli.Context, o *Options) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Configure applies the options to the specified config
 | // Configure applies the options to the specified config
 | ||||||
| func (o Options) Configure(cfg engine.Interface) error { | func (o Options) Configure(cfg engine.Interface, configOverrides ...map[string]interface{}) error { | ||||||
| 	err := o.UpdateConfig(cfg) | 	err := o.UpdateConfig(cfg, configOverrides...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("unable to update config: %v", err) | 		return fmt.Errorf("unable to update config: %v", err) | ||||||
| 	} | 	} | ||||||
| @ -98,14 +98,14 @@ func (o Options) flush(cfg engine.Interface) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UpdateConfig updates the specified config to include the nvidia runtimes
 | // UpdateConfig updates the specified config to include the nvidia runtimes
 | ||||||
| func (o Options) UpdateConfig(cfg engine.Interface) error { | func (o Options) UpdateConfig(cfg engine.Interface, configOverrides ...map[string]interface{}) error { | ||||||
| 	runtimes := operator.GetRuntimes( | 	runtimes := operator.GetRuntimes( | ||||||
| 		operator.WithNvidiaRuntimeName(o.RuntimeName), | 		operator.WithNvidiaRuntimeName(o.RuntimeName), | ||||||
| 		operator.WithSetAsDefault(o.SetAsDefault), | 		operator.WithSetAsDefault(o.SetAsDefault), | ||||||
| 		operator.WithRoot(o.RuntimeDir), | 		operator.WithRoot(o.RuntimeDir), | ||||||
| 	) | 	) | ||||||
| 	for name, runtime := range runtimes { | 	for name, runtime := range runtimes { | ||||||
| 		err := cfg.AddRuntime(name, runtime.Path, runtime.SetAsDefault) | 		err := cfg.AddRuntime(name, runtime.Path, runtime.SetAsDefault, configOverrides...) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("failed to update runtime %q: %v", name, err) | 			return fmt.Errorf("failed to update runtime %q: %v", name, err) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ | |||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
| @ -47,6 +48,8 @@ type options struct { | |||||||
| 	runtimeType     string | 	runtimeType     string | ||||||
| 
 | 
 | ||||||
| 	ContainerRuntimeModesCDIAnnotationPrefixes cli.StringSlice | 	ContainerRuntimeModesCDIAnnotationPrefixes cli.StringSlice | ||||||
|  | 
 | ||||||
|  | 	runtimeConfigOverrideJSON string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func main() { | func main() { | ||||||
| @ -162,6 +165,13 @@ func main() { | |||||||
| 			Destination: &options.ContainerRuntimeModesCDIAnnotationPrefixes, | 			Destination: &options.ContainerRuntimeModesCDIAnnotationPrefixes, | ||||||
| 			EnvVars:     []string{"NVIDIA_CONTAINER_RUNTIME_MODES_CDI_ANNOTATION_PREFIXES"}, | 			EnvVars:     []string{"NVIDIA_CONTAINER_RUNTIME_MODES_CDI_ANNOTATION_PREFIXES"}, | ||||||
| 		}, | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:        "runtime-config-override", | ||||||
|  | 			Destination: &options.runtimeConfigOverrideJSON, | ||||||
|  | 			Usage:       "specify additional runtime options as a JSON string. The paths are relative to the runtime config.", | ||||||
|  | 			Value:       "{}", | ||||||
|  | 			EnvVars:     []string{"RUNTIME_CONFIG_OVERRIDE", "CONTAINERD_RUNTIME_CONFIG_OVERRIDE"}, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update the subcommand flags with the common subcommand flags
 | 	// Update the subcommand flags with the common subcommand flags
 | ||||||
| @ -170,7 +180,7 @@ func main() { | |||||||
| 
 | 
 | ||||||
| 	// Run the top-level CLI
 | 	// Run the top-level CLI
 | ||||||
| 	if err := c.Run(os.Args); err != nil { | 	if err := c.Run(os.Args); err != nil { | ||||||
| 		log.Fatal(fmt.Errorf("Error: %v", err)) | 		log.Fatal(fmt.Errorf("error: %v", err)) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -188,7 +198,11 @@ func Setup(c *cli.Context, o *options) error { | |||||||
| 		return fmt.Errorf("unable to load config: %v", err) | 		return fmt.Errorf("unable to load config: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = o.Configure(cfg) | 	runtimeConfigOverride, err := o.runtimeConfigOverride() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("unable to parse config overrides: %w", err) | ||||||
|  | 	} | ||||||
|  | 	err = o.Configure(cfg, runtimeConfigOverride) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("unable to configure containerd: %v", err) | 		return fmt.Errorf("unable to configure containerd: %v", err) | ||||||
| 	} | 	} | ||||||
| @ -246,3 +260,16 @@ func (o *options) containerAnnotationsFromCDIPrefixes() []string { | |||||||
| 
 | 
 | ||||||
| 	return annotations | 	return annotations | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (o *options) runtimeConfigOverride() (map[string]interface{}, error) { | ||||||
|  | 	if o.runtimeConfigOverrideJSON == "" { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	runtimeOptions := make(map[string]interface{}) | ||||||
|  | 	if err := json.Unmarshal([]byte(o.runtimeConfigOverrideJSON), &runtimeOptions); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to read %v as JSON: %w", o.runtimeConfigOverrideJSON, err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return runtimeOptions, nil | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										72
									
								
								tools/container/containerd/containerd_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								tools/container/containerd/containerd_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | /** | ||||||
|  | # Copyright 2024 NVIDIA CORPORATION | ||||||
|  | # | ||||||
|  | # 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 main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/stretchr/testify/require" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestRuntimeOptions(t *testing.T) { | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		description   string | ||||||
|  | 		options       options | ||||||
|  | 		expected      map[string]interface{} | ||||||
|  | 		expectedError error | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			description: "empty is nil", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			description: "empty json", | ||||||
|  | 			options: options{ | ||||||
|  | 				runtimeConfigOverrideJSON: "{}", | ||||||
|  | 			}, | ||||||
|  | 			expected:      map[string]interface{}{}, | ||||||
|  | 			expectedError: nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			description: "SystemdCgroup is true", | ||||||
|  | 			options: options{ | ||||||
|  | 				runtimeConfigOverrideJSON: "{\"SystemdCgroup\": true}", | ||||||
|  | 			}, | ||||||
|  | 			expected: map[string]interface{}{ | ||||||
|  | 				"SystemdCgroup": true, | ||||||
|  | 			}, | ||||||
|  | 			expectedError: nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			description: "SystemdCgroup is false", | ||||||
|  | 			options: options{ | ||||||
|  | 				runtimeConfigOverrideJSON: "{\"SystemdCgroup\": false}", | ||||||
|  | 			}, | ||||||
|  | 			expected: map[string]interface{}{ | ||||||
|  | 				"SystemdCgroup": false, | ||||||
|  | 			}, | ||||||
|  | 			expectedError: nil, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, tc := range testCases { | ||||||
|  | 		t.Run(tc.description, func(t *testing.T) { | ||||||
|  | 			runtimeOptions, err := tc.options.runtimeConfigOverride() | ||||||
|  | 			require.ErrorIs(t, tc.expectedError, err) | ||||||
|  | 			require.EqualValues(t, tc.expected, runtimeOptions) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user