Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions cmd/nvidia-cdi-hook/update-ldcache/update-ldcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,25 +140,11 @@ func updateLdCacheHandler() {
// It is invoked from a reexec'd handler and provides namespace isolation for
// the operations performed by this hook. At the point where this is invoked,
// we are in a new mount namespace that is cloned from the parent.
//
// args[0] is the reexec initializer function name
// args[1] is the path of the ldconfig binary on the host
// args[2] is the container root directory
// The remaining args are folders where soname symlinks need to be created.
func updateLdCache(args []string) error {
if len(args) < 3 {
return fmt.Errorf("incorrect arguments: %v", args)
}
hostLdconfigPath := args[1]
containerRootDirPath := args[2]

ldconfig, err := ldconfig.New(
hostLdconfigPath,
containerRootDirPath,
)
ldconfig, err := ldconfig.NewFromArgs(args...)
if err != nil {
return fmt.Errorf("failed to construct ldconfig runner: %w", err)
}

return ldconfig.UpdateLDCache(args[3:]...)
return ldconfig.UpdateLDCache()
}
76 changes: 62 additions & 14 deletions internal/ldconfig/ldconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package ldconfig

import (
"flag"
"fmt"
"os"
"os/exec"
Expand All @@ -37,39 +38,72 @@ const (
)

type Ldconfig struct {
ldconfigPath string
inRoot string
ldconfigPath string
inRoot string
isDebianLikeHost bool
directories []string
}

// NewRunner creates an exec.Cmd that can be used to run ldconfig.
func NewRunner(id string, ldconfigPath string, containerRoot string, additionalargs ...string) (*exec.Cmd, error) {
args := []string{
id,
strings.TrimPrefix(config.NormalizeLDConfigPath("@"+ldconfigPath), "@"),
containerRoot,
"--ldconfig-path", strings.TrimPrefix(config.NormalizeLDConfigPath("@"+ldconfigPath), "@"),
"--container-root", containerRoot,
}
if isDebian() {
args = append(args, "--is-debian-like-host")
}
args = append(args, additionalargs...)

return createReexecCommand(args)
}

// New creates an Ldconfig struct that is used to perform operations on the
// ldcache and libraries in a particular root (e.g. a container).
func New(ldconfigPath string, inRoot string) (*Ldconfig, error) {
l := &Ldconfig{
ldconfigPath: ldconfigPath,
inRoot: inRoot,
// NewFromArgs creates an Ldconfig struct from the args passed to the Cmd
// above.
// This struct is used to perform operations on the ldcache and libraries in a
// particular root (e.g. a container).
//
// args[0] is the reexec initializer function name
// The following flags are required:
//
// --ldconfig-path=LDCONFIG_PATH the path to ldconfig on the host
// --container-root=CONTAINER_ROOT the path in which ldconfig must be run
//
// The following flags are optional:
//
// --is-debian-like-host Indicates that the host system is debian-based.
//
// The remaining args are folders where soname symlinks need to be created.
func NewFromArgs(args ...string) (*Ldconfig, error) {
if len(args) < 1 {
return nil, fmt.Errorf("incorrect arguments: %v", args)
}
fs := flag.NewFlagSet(args[1], flag.ExitOnError)
ldconfigPath := fs.String("ldconfig-path", "", "the path to ldconfig on the host")
containerRoot := fs.String("container-root", "", "the path in which ldconfig must be run")
isDebianLikeHost := fs.Bool("is-debian-like-host", false, "the hook is running from a Debian-like host")
if err := fs.Parse(args[1:]); err != nil {
return nil, err
}
if ldconfigPath == "" {

if *ldconfigPath == "" {
return nil, fmt.Errorf("an ldconfig path must be specified")
}
if inRoot == "" || inRoot == "/" {
if *containerRoot == "" || *containerRoot == "/" {
return nil, fmt.Errorf("ldconfig must be run in the non-system root")
}

l := &Ldconfig{
ldconfigPath: *ldconfigPath,
inRoot: *containerRoot,
isDebianLikeHost: *isDebianLikeHost,
directories: fs.Args(),
}
return l, nil
}

func (l *Ldconfig) UpdateLDCache(directories ...string) error {
func (l *Ldconfig) UpdateLDCache() error {
ldconfigPath, err := l.prepareRoot()
if err != nil {
return err
Expand All @@ -82,14 +116,28 @@ func (l *Ldconfig) UpdateLDCache(directories ...string) error {
"-f", "/etc/ld.so.conf",
"-C", "/etc/ld.so.cache",
}
// If we are running in a non-debian container on a debian host we also
// need to add the system directories for non-debian hosts to the list of
// folders processed by ldconfig.
if l.isDebianLikeHost && !isDebian() {
args = append(args, "/lib64", "/usr/lib64")
}

if err := createLdsoconfdFile(ldsoconfdFilenamePattern, directories...); err != nil {
if err := createLdsoconfdFile(ldsoconfdFilenamePattern, l.directories...); err != nil {
return fmt.Errorf("failed to update ld.so.conf.d: %w", err)
}

return SafeExec(ldconfigPath, args, nil)
}

func isDebian() bool {
info, err := os.Stat("/etc/debian_version")
if err != nil {
return false
}
return !info.IsDir()
}

func (l *Ldconfig) prepareRoot() (string, error) {
// To prevent leaking the parent proc filesystem, we create a new proc mount
// in the specified root.
Expand Down
19 changes: 19 additions & 0 deletions tests/e2e/nvidia-container-toolkit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,4 +533,23 @@ EOF`)
Expect(err).ToNot(HaveOccurred())
})
})

When("running a container a ubi9 container", Ordered, func() {
var (
expectedOutput string
)
BeforeAll(func(ctx context.Context) {
_, _, err := runner.Run(`docker pull redhat/ubi9`)
Expect(err).ToNot(HaveOccurred())

expectedOutput, _, err = runner.Run(`docker run --rm --runtime=runc redhat/ubi9 bash -c "ldconfig -p | grep libc.so."`)
Expect(err).ToNot(HaveOccurred())
})

It("should include the system libraries when using the nvidia-container-runtime", func(ctx context.Context) {
output, _, err := runner.Run(`docker run --rm --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all redhat/ubi9 bash -c "ldconfig -p | grep libc.so."`)
Expect(err).ToNot(HaveOccurred())
Expect(output).To(Equal(expectedOutput))
})
})
})