mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-03-09 13:50:22 +00:00
Add create-soname-symlinks hook
This change adds a create-soname-symlinks hook that can be used to ensure that the soname symlinks for injected libraries exist in a container. This is done by calling ldconfig -n -N for the folders containing the injected libraries. This also ensures that libcuda.so is present in the ldcache when the update-ldcache hook is run. Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
parent
c92f6c0ea4
commit
9daa179f25
@ -20,6 +20,7 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/chmod"
|
||||
soname "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-soname-symlinks"
|
||||
symlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-symlinks"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/cudacompat"
|
||||
ldcache "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/update-ldcache"
|
||||
@ -34,5 +35,6 @@ func New(logger logger.Interface) []*cli.Command {
|
||||
symlinks.NewCommand(logger),
|
||||
chmod.NewCommand(logger),
|
||||
cudacompat.NewCommand(logger),
|
||||
soname.NewCommand(logger),
|
||||
}
|
||||
}
|
||||
|
131
cmd/nvidia-cdi-hook/create-soname-symlinks/soname-symlinks.go
Normal file
131
cmd/nvidia-cdi-hook/create-soname-symlinks/soname-symlinks.go
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
# Copyright (c) 2025, 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 soname
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/utils"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
logger logger.Interface
|
||||
utils.SafeExecer
|
||||
}
|
||||
|
||||
type options struct {
|
||||
folders cli.StringSlice
|
||||
ldconfigPath string
|
||||
containerSpec string
|
||||
}
|
||||
|
||||
// NewCommand constructs an create-soname-symlinks command with the specified logger
|
||||
func NewCommand(logger logger.Interface) *cli.Command {
|
||||
c := command{
|
||||
logger: logger,
|
||||
SafeExecer: utils.NewSafeExecer(logger),
|
||||
}
|
||||
return c.build()
|
||||
}
|
||||
|
||||
// build the create-soname-symlinks command
|
||||
func (m command) build() *cli.Command {
|
||||
cfg := options{}
|
||||
|
||||
// Create the 'create-soname-symlinks' command
|
||||
c := cli.Command{
|
||||
Name: "create-soname-symlinks",
|
||||
Usage: "Create soname symlinks for the specified folders using ldconfig -n -N",
|
||||
Before: func(c *cli.Context) error {
|
||||
return m.validateFlags(c, &cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
return m.run(c, &cfg)
|
||||
},
|
||||
}
|
||||
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "folder",
|
||||
Usage: "Specify a folder to search for shared libraries for which soname symlinks need to be created",
|
||||
Destination: &cfg.folders,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ldconfig-path",
|
||||
Usage: "Specify the path to the ldconfig program",
|
||||
Destination: &cfg.ldconfigPath,
|
||||
Value: "/sbin/ldconfig",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "container-spec",
|
||||
Usage: "Specify the path to the OCI container spec. If empty or '-' the spec will be read from STDIN",
|
||||
Destination: &cfg.containerSpec,
|
||||
},
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (m command) validateFlags(c *cli.Context, cfg *options) error {
|
||||
if cfg.ldconfigPath == "" {
|
||||
return errors.New("ldconfig-path must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m command) run(c *cli.Context, cfg *options) error {
|
||||
s, err := oci.LoadContainerState(cfg.containerSpec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load container state: %v", err)
|
||||
}
|
||||
|
||||
containerRoot, err := s.GetContainerRoot()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determined container root: %v", err)
|
||||
}
|
||||
if containerRoot == "" {
|
||||
m.logger.Warningf("No container root detected")
|
||||
return nil
|
||||
}
|
||||
|
||||
dirs := cfg.folders.Value()
|
||||
if len(dirs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ldconfigPath := utils.ResolveHostLDConfigPath(cfg.ldconfigPath)
|
||||
args := []string{filepath.Base(ldconfigPath)}
|
||||
|
||||
args = append(args,
|
||||
// Specify the containerRoot to use.
|
||||
"-r", containerRoot,
|
||||
// Specify -n to only process the specified folders.
|
||||
"-n",
|
||||
// Explicitly disable updating the LDCache.
|
||||
"-N",
|
||||
)
|
||||
// Explicitly specific the directories to add.
|
||||
args = append(args, dirs...)
|
||||
|
||||
return m.Exec(ldconfigPath, args, nil)
|
||||
}
|
@ -86,6 +86,13 @@ containerEdits:
|
||||
- --host-driver-version=999.88.77
|
||||
hookName: createContainer
|
||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- create-soname-symlinks
|
||||
- --folder
|
||||
- /lib/x86_64-linux-gnu
|
||||
hookName: createContainer
|
||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- update-ldcache
|
||||
|
@ -85,6 +85,13 @@ containerEdits:
|
||||
- --host-driver-version=999.88.77
|
||||
hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- create-soname-symlinks
|
||||
- --folder
|
||||
- /lib/x86_64-linux-gnu
|
||||
hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- update-ldcache
|
||||
|
@ -50,16 +50,16 @@ func (d ldconfig) Hooks() ([]Hook, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to discover mounts for ldcache update: %v", err)
|
||||
}
|
||||
h := CreateLDCacheUpdateHook(
|
||||
hooks := CreateLDCacheUpdateHooks(
|
||||
d.nvidiaCDIHookPath,
|
||||
d.ldconfigPath,
|
||||
getLibraryPaths(mounts),
|
||||
)
|
||||
return []Hook{h}, nil
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
// CreateLDCacheUpdateHook locates the NVIDIA Container Toolkit CLI and creates a hook for updating the LD Cache
|
||||
func CreateLDCacheUpdateHook(executable string, ldconfig string, libraries []string) Hook {
|
||||
// CreateLDCacheUpdateHooks locates the NVIDIA Container Toolkit CLI and creates a hook for updating the LD Cache
|
||||
func CreateLDCacheUpdateHooks(executable string, ldconfig string, libraries []string) []Hook {
|
||||
var args []string
|
||||
|
||||
if ldconfig != "" {
|
||||
@ -70,13 +70,20 @@ func CreateLDCacheUpdateHook(executable string, ldconfig string, libraries []str
|
||||
args = append(args, "--folder", f)
|
||||
}
|
||||
|
||||
hook := CreateNvidiaCDIHook(
|
||||
executable,
|
||||
"update-ldcache",
|
||||
args...,
|
||||
)
|
||||
hooks := []Hook{
|
||||
CreateNvidiaCDIHook(
|
||||
executable,
|
||||
"create-soname-symlinks",
|
||||
args...,
|
||||
),
|
||||
CreateNvidiaCDIHook(
|
||||
executable,
|
||||
"update-ldcache",
|
||||
args...,
|
||||
),
|
||||
}
|
||||
|
||||
return hook
|
||||
return hooks
|
||||
}
|
||||
|
||||
// getLibraryPaths extracts the library dirs from the specified mounts
|
||||
|
@ -38,11 +38,22 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
mounts []Mount
|
||||
mountError error
|
||||
expectedError error
|
||||
expectedArgs []string
|
||||
expectedHooks []Hook
|
||||
}{
|
||||
{
|
||||
description: "empty mounts",
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache"},
|
||||
description: "empty mounts",
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks"},
|
||||
},
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "update-ldcache"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "mount error",
|
||||
@ -65,7 +76,18 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
Path: "/usr/local/lib/libbar.so",
|
||||
},
|
||||
},
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
||||
},
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "host paths are ignored",
|
||||
@ -75,12 +97,34 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
Path: "/usr/local/lib/libfoo.so",
|
||||
},
|
||||
},
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib"},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks", "--folder", "/usr/local/lib"},
|
||||
},
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "explicit ldconfig path is passed",
|
||||
ldconfigPath: testLdconfigPath,
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--ldconfig-path", testLdconfigPath},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks", "--ldconfig-path", testLdconfigPath},
|
||||
},
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: []string{"nvidia-cdi-hook", "update-ldcache", "--ldconfig-path", testLdconfigPath},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -91,12 +135,6 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
return tc.mounts, tc.mountError
|
||||
},
|
||||
}
|
||||
expectedHook := Hook{
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: tc.expectedArgs,
|
||||
Lifecycle: "createContainer",
|
||||
}
|
||||
|
||||
d, err := NewLDCacheUpdateHook(logger, mountMock, testNvidiaCDIHookPath, tc.ldconfigPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -110,9 +148,7 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Len(t, hooks, 1)
|
||||
|
||||
require.EqualValues(t, hooks[0], expectedHook)
|
||||
require.EqualValues(t, tc.expectedHooks, hooks)
|
||||
|
||||
devices, err := d.Devices()
|
||||
require.NoError(t, err)
|
||||
|
@ -215,4 +215,26 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
|
||||
Expect(ldconfigOut).To(ContainSubstring("/usr/lib64"))
|
||||
})
|
||||
})
|
||||
|
||||
When("A container is run using CDI", Ordered, func() {
|
||||
BeforeAll(func(ctx context.Context) {
|
||||
_, _, err := r.Run("docker pull ubuntu")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should include libcuda.so in the ldcache", func(ctx context.Context) {
|
||||
ldcacheOutput, _, err := r.Run("docker run --rm -i --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=runtime.nvidia.com/gpu=all ubuntu bash -c \"ldconfig -p | grep 'libcuda.so'\"")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ldcacheOutput).ToNot(BeEmpty())
|
||||
|
||||
ldcacheLines := strings.Split(ldcacheOutput, "\n")
|
||||
var libs []string
|
||||
for _, line := range ldcacheLines {
|
||||
parts := strings.SplitN(line, " (", 2)
|
||||
libs = append(libs, strings.TrimSpace(parts[0]))
|
||||
}
|
||||
|
||||
Expect(libs).To(ContainElements([]string{"libcuda.so", "libcuda.so.1"}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user