diff --git a/internal/ldconfig/ldconfig.go b/internal/ldconfig/ldconfig.go index 4c9939f1e..49a21c860 100644 --- a/internal/ldconfig/ldconfig.go +++ b/internal/ldconfig/ldconfig.go @@ -67,7 +67,8 @@ func NewRunner(id string, ldconfigPath string, containerRoot string, additionala // 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 +// args[0] is the reexec initializer function name and is required. +// // The following flags are required: // // --ldconfig-path=LDCONFIG_PATH the path to ldconfig on the host @@ -76,16 +77,20 @@ func NewRunner(id string, ldconfigPath string, containerRoot string, additionala // The following flags are optional: // // --is-debian-like-host Indicates that the host system is debian-based. +// See https://github.com/NVIDIA/nvidia-container-toolkit/pull/1444 // // 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) + fs := flag.NewFlagSet("ldconfig-options", 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") + isDebianLikeHost := fs.Bool("is-debian-like-host", false, `indicates that the host system is debian-based. +This allows us to handle the case where there are differences in behavior +between the ldconfig from the host (as executed from an update-ldcache hook) and +ldconfig in the container. Such differences include system search paths.`) if err := fs.Parse(args[1:]); err != nil { return nil, err } @@ -98,11 +103,10 @@ func NewFromArgs(args ...string) (*Ldconfig, error) { } l := &Ldconfig{ - ldconfigPath: *ldconfigPath, - inRoot: *containerRoot, - isDebianLikeHost: *isDebianLikeHost, - isDebianLikeContainer: isDebian(), - directories: fs.Args(), + ldconfigPath: *ldconfigPath, + inRoot: *containerRoot, + isDebianLikeHost: *isDebianLikeHost, + directories: fs.Args(), } return l, nil } @@ -113,10 +117,13 @@ func (l *Ldconfig) UpdateLDCache() error { return err } + // `prepareRoot` pivots to the container root, so can now set the container "debian-ness". + l.isDebianLikeContainer = isDebian() + // Explicitly specify using /etc/ld.so.conf since the host's ldconfig may // be configured to use a different config file by default. const topLevelLdsoconfFilePath = "/etc/ld.so.conf" - filteredDirectories, err := l.filterDirectories(topLevelLdsoconfFilePath, l.directories...) + filteredDirectories, ldconfigDirs, err := l.filterDirectories(topLevelLdsoconfFilePath, l.directories...) if err != nil { return err } @@ -126,6 +133,19 @@ func (l *Ldconfig) UpdateLDCache() error { "-f", topLevelLdsoconfFilePath, "-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. + // We only do this if they are not already tracked, since the folders on + // on the command line have a higher priority than folders in ld.so.conf. + if l.isDebianLikeHost && !l.isDebianLikeContainer { + for _, systemSearchPath := range l.getSystemSearchPaths() { + if _, ok := ldconfigDirs[systemSearchPath]; ok { + continue + } + args = append(args, systemSearchPath) + } + } if err := createLdsoconfdFile(ldsoconfdFilenamePattern, filteredDirectories...); err != nil { return fmt.Errorf("failed to update ld.so.conf.d: %w", err) @@ -163,10 +183,10 @@ func (l *Ldconfig) prepareRoot() (string, error) { return ldconfigPath, nil } -func (l *Ldconfig) filterDirectories(configFilePath string, directories ...string) ([]string, error) { +func (l *Ldconfig) filterDirectories(configFilePath string, directories ...string) ([]string, map[string]struct{}, error) { ldconfigDirs, err := l.getLdsoconfDirectories(configFilePath) if err != nil { - return nil, err + return nil, nil, err } var filtered []string @@ -175,8 +195,9 @@ func (l *Ldconfig) filterDirectories(configFilePath string, directories ...strin continue } filtered = append(filtered, d) + ldconfigDirs[d] = struct{}{} } - return filtered, nil + return filtered, ldconfigDirs, nil } // createLdsoconfdFile creates a file at /etc/ld.so.conf.d/. @@ -224,7 +245,7 @@ func createLdsoconfdFile(pattern string, dirs ...string) error { // files that refer to the directory. func (l *Ldconfig) getLdsoconfDirectories(configFilePath string) (map[string]struct{}, error) { ldconfigDirs := make(map[string]struct{}) - for _, d := range l.getSystemSerachPaths() { + for _, d := range l.getSystemSearchPaths() { ldconfigDirs[d] = struct{}{} } @@ -253,9 +274,9 @@ func (l *Ldconfig) getLdsoconfDirectories(configFilePath string) (map[string]str return ldconfigDirs, nil } -func (l *Ldconfig) getSystemSerachPaths() []string { +func (l *Ldconfig) getSystemSearchPaths() []string { if l.isDebianLikeContainer { - debianSystemSearchPaths() + return debianSystemSearchPaths() } return nonDebianSystemSearchPaths() } diff --git a/internal/ldconfig/ldconfig_test.go b/internal/ldconfig/ldconfig_test.go index a22876719..308faa0ec 100644 --- a/internal/ldconfig/ldconfig_test.go +++ b/internal/ldconfig/ldconfig_test.go @@ -117,7 +117,7 @@ include INCLUDED_PATTERN* l := &Ldconfig{ isDebianLikeContainer: true, } - filtered, err := l.filterDirectories(topLevelConfPath, tc.input...) + filtered, _, err := l.filterDirectories(topLevelConfPath, tc.input...) require.NoError(t, err) require.Equal(t, tc.expected, filtered) diff --git a/tests/e2e/nvidia-container-toolkit_test.go b/tests/e2e/nvidia-container-toolkit_test.go index 69086301b..33181345e 100644 --- a/tests/e2e/nvidia-container-toolkit_test.go +++ b/tests/e2e/nvidia-container-toolkit_test.go @@ -570,4 +570,23 @@ EOF`) Expect(output).To(Equal(expectedOutput)) }) }) + + When("running 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)) + }) + }) })