Resolve LDConfig path passed to nvidia-container-cli

Instead of relying solely on a static config, we resolve the path
to ldconfig. The path is checked for existence and a .real suffix is preferred.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
Evan Lezar 2023-11-14 16:56:50 +01:00
parent 039d7fd324
commit 232df647c1
6 changed files with 125 additions and 13 deletions

View File

@ -1,6 +1,8 @@
# NVIDIA Container Toolkit Changelog # NVIDIA Container Toolkit Changelog
* Skip update of ldcache in containers without ldconfig. The .so.SONAME symlinks are still created. * Skip update of ldcache in containers without ldconfig. The .so.SONAME symlinks are still created.
* Normalize ldconfig path on use. This automatically adjust the ldconfig setting applied to ldconfig.real on systems where this exists.
* [libnvidia-container] Fix device permission check when using cgroupv2 (fixes #227) * [libnvidia-container] Fix device permission check when using cgroupv2 (fixes #227)
## v1.14.3 ## v1.14.3

View File

@ -111,8 +111,8 @@ func doPrestart() {
} }
args = append(args, "configure") args = append(args, "configure")
if cli.Ldconfig != "" { if ldconfigPath := cli.NormalizeLDConfigPath(); ldconfigPath != "" {
args = append(args, fmt.Sprintf("--ldconfig=%s", cli.Ldconfig)) args = append(args, fmt.Sprintf("--ldconfig=%s", ldconfigPath))
} }
if cli.NoCgroups { if cli.NoCgroups {
args = append(args, "--no-cgroups") args = append(args, "--no-cgroups")

View File

@ -16,6 +16,11 @@
package config package config
import (
"os"
"strings"
)
// ContainerCLIConfig stores the options for the nvidia-container-cli // ContainerCLIConfig stores the options for the nvidia-container-cli
type ContainerCLIConfig struct { type ContainerCLIConfig struct {
Root string `toml:"root"` Root string `toml:"root"`
@ -31,3 +36,27 @@ type ContainerCLIConfig struct {
User string `toml:"user"` User string `toml:"user"`
Ldconfig string `toml:"ldconfig"` Ldconfig string `toml:"ldconfig"`
} }
// NormalizeLDConfigPath returns the resolved path of the configured LDConfig binary.
// This is only done for host LDConfigs and is required to handle systems where
// /sbin/ldconfig is a wrapper around /sbin/ldconfig.real.
func (c *ContainerCLIConfig) NormalizeLDConfigPath() string {
return NormalizeLDConfigPath(c.Ldconfig)
}
// NormalizeLDConfigPath returns the resolved path of the configured LDConfig binary.
// This is only done for host LDConfigs and is required to handle systems where
// /sbin/ldconfig is a wrapper around /sbin/ldconfig.real.
func NormalizeLDConfigPath(path string) string {
if !strings.HasPrefix(path, "@") {
return path
}
trimmedPath := strings.TrimSuffix(strings.TrimPrefix(path, "@"), ".real")
// If the .real path exists, we return that.
if _, err := os.Stat(trimmedPath + ".real"); err == nil {
return "@" + trimmedPath + ".real"
}
// If the .real path does not exists (or cannot be read) we return the non-.real path.
return "@" + trimmedPath
}

View File

@ -0,0 +1,83 @@
/**
# Copyright 2023 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 config
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestNormalizeLDConfigPath(t *testing.T) {
testDir := t.TempDir()
f, err := os.Create(filepath.Join(testDir, "exists.real"))
require.NoError(t, err)
_ = f.Close()
testCases := []struct {
description string
ldconfig string
expected string
}{
{
description: "empty input",
},
{
description: "non-host with .real suffix returns as is",
ldconfig: "/some/path/ldconfig.real",
expected: "/some/path/ldconfig.real",
},
{
description: "non-host without .real suffix returns as is",
ldconfig: "/some/path/ldconfig",
expected: "/some/path/ldconfig",
},
{
description: "host .real file exists is returned",
ldconfig: "@" + filepath.Join(testDir, "exists.real"),
expected: "@" + filepath.Join(testDir, "exists.real"),
},
{
description: "host resolves .real file",
ldconfig: "@" + filepath.Join(testDir, "exists"),
expected: "@" + filepath.Join(testDir, "exists.real"),
},
{
description: "host .real file not exists strips suffix",
ldconfig: "@/does/not/exist.real",
expected: "@/does/not/exist",
},
{
description: "host file returned as is if no .real file exsits",
ldconfig: "@/does/not/exist",
expected: "@/does/not/exist",
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
c := ContainerCLIConfig{
Ldconfig: tc.ldconfig,
}
require.Equal(t, tc.expected, c.NormalizeLDConfigPath())
})
}
}

View File

@ -122,10 +122,7 @@ func GetDefault() (*Config, error) {
} }
func getLdConfigPath() string { func getLdConfigPath() string {
if _, err := os.Stat("/sbin/ldconfig.real"); err == nil { return NormalizeLDConfigPath("@/sbin/ldconfig")
return "@/sbin/ldconfig.real"
}
return "@/sbin/ldconfig"
} }
// getCommentedUserGroup returns whether the nvidia-container-cli user and group config option should be commented. // getCommentedUserGroup returns whether the nvidia-container-cli user and group config option should be commented.

View File

@ -23,6 +23,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
"github.com/NVIDIA/nvidia-container-toolkit/internal/system/nvdevices" "github.com/NVIDIA/nvidia-container-toolkit/internal/system/nvdevices"
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
@ -383,7 +384,7 @@ func installLibrary(libName string, toolkitRoot string) error {
func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContainerCliExecutablePath string, nvidiaCTKPath string, nvidaContainerRuntimeHookPath string, opts *options) error { func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContainerCliExecutablePath string, nvidiaCTKPath string, nvidaContainerRuntimeHookPath string, opts *options) error {
log.Infof("Installing NVIDIA container toolkit config '%v'", toolkitConfigPath) log.Infof("Installing NVIDIA container toolkit config '%v'", toolkitConfigPath)
config, err := loadConfig(nvidiaContainerToolkitConfigSource) cfg, err := loadConfig(nvidiaContainerToolkitConfigSource)
if err != nil { if err != nil {
return fmt.Errorf("could not open source config file: %v", err) return fmt.Errorf("could not open source config file: %v", err)
} }
@ -396,9 +397,9 @@ func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContai
// Read the ldconfig path from the config as this may differ per platform // Read the ldconfig path from the config as this may differ per platform
// On ubuntu-based systems this ends in `.real` // On ubuntu-based systems this ends in `.real`
ldconfigPath := fmt.Sprintf("%s", config.GetDefault("nvidia-container-cli.ldconfig", "/sbin/ldconfig")) ldconfigPath := fmt.Sprintf("%s", cfg.GetDefault("nvidia-container-cli.ldconfig", "/sbin/ldconfig"))
// Use the driver run root as the root: // Use the driver run root as the root:
driverLdconfigPath := "@" + filepath.Join(opts.DriverRoot, strings.TrimPrefix(ldconfigPath, "@/")) driverLdconfigPath := config.NormalizeLDConfigPath("@" + filepath.Join(opts.DriverRoot, strings.TrimPrefix(ldconfigPath, "@/")))
configValues := map[string]interface{}{ configValues := map[string]interface{}{
// Set the options in the root toml table // Set the options in the root toml table
@ -415,7 +416,7 @@ func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContai
"nvidia-container-runtime-hook.skip-mode-detection": opts.ContainerRuntimeHookSkipModeDetection, "nvidia-container-runtime-hook.skip-mode-detection": opts.ContainerRuntimeHookSkipModeDetection,
} }
for key, value := range configValues { for key, value := range configValues {
config.Set(key, value) cfg.Set(key, value)
} }
// Set the optional config options // Set the optional config options
@ -452,15 +453,15 @@ func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContai
log.Warningf("Unexpected type for option %v=%v: %T", key, value, v) log.Warningf("Unexpected type for option %v=%v: %T", key, value, v)
} }
config.Set(key, value) cfg.Set(key, value)
} }
if _, err := config.WriteTo(targetConfig); err != nil { if _, err := cfg.WriteTo(targetConfig); err != nil {
return fmt.Errorf("error writing config: %v", err) return fmt.Errorf("error writing config: %v", err)
} }
os.Stdout.WriteString("Using config:\n") os.Stdout.WriteString("Using config:\n")
if _, err = config.WriteTo(os.Stdout); err != nil { if _, err = cfg.WriteTo(os.Stdout); err != nil {
log.Warningf("Failed to output config to STDOUT: %v", err) log.Warningf("Failed to output config to STDOUT: %v", err)
} }