mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2024-11-24 21:14:00 +00:00
Merge pull request #755 from elezar/fix-libcuda-so
Some checks failed
CodeQL / Analyze Go code with CodeQL (push) Has been cancelled
Golang / check (push) Has been cancelled
Golang / Unit test (push) Has been cancelled
Golang / Build (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, centos7-aarch64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, centos7-x86_64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, centos8-ppc64le) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, ubuntu18.04-amd64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, ubuntu18.04-arm64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, ubuntu18.04-ppc64le) (push) Has been cancelled
image / image (packaging, ${{github.event_name == 'pull_request'}}) (push) Has been cancelled
image / image (ubi8, ${{github.event_name == 'pull_request'}}) (push) Has been cancelled
image / image (ubuntu20.04, ${{github.event_name == 'pull_request'}}) (push) Has been cancelled
Some checks failed
CodeQL / Analyze Go code with CodeQL (push) Has been cancelled
Golang / check (push) Has been cancelled
Golang / Unit test (push) Has been cancelled
Golang / Build (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, centos7-aarch64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, centos7-x86_64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, centos8-ppc64le) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, ubuntu18.04-amd64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, ubuntu18.04-arm64) (push) Has been cancelled
image / packages (${{github.event_name == 'pull_request'}}, ubuntu18.04-ppc64le) (push) Has been cancelled
image / image (packaging, ${{github.event_name == 'pull_request'}}) (push) Has been cancelled
image / image (ubi8, ${{github.event_name == 'pull_request'}}) (push) Has been cancelled
image / image (ubuntu20.04, ${{github.event_name == 'pull_request'}}) (push) Has been cancelled
Fix bug where libcuda.so is not found in ldcache
This commit is contained in:
commit
88608781b6
@ -1,102 +0,0 @@
|
||||
/**
|
||||
# Copyright (c) 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 createdevicenodes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
logger logger.Interface
|
||||
}
|
||||
|
||||
type options struct {
|
||||
driverRoot string
|
||||
}
|
||||
|
||||
// NewCommand constructs a command sub-command with the specified logger
|
||||
func NewCommand(logger logger.Interface) *cli.Command {
|
||||
c := command{
|
||||
logger: logger,
|
||||
}
|
||||
return c.build()
|
||||
}
|
||||
|
||||
// build
|
||||
func (m command) build() *cli.Command {
|
||||
opts := options{}
|
||||
|
||||
c := cli.Command{
|
||||
Name: "print-ldcache",
|
||||
Usage: "A utility to print the contents of the ldcache",
|
||||
Before: func(c *cli.Context) error {
|
||||
return m.validateFlags(c, &opts)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
return m.run(c, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "driver-root",
|
||||
Usage: "the path to the driver root. Device nodes will be created at `DRIVER_ROOT`/dev",
|
||||
Value: "/",
|
||||
Destination: &opts.driverRoot,
|
||||
EnvVars: []string{"NVIDIA_DRIVER_ROOT", "DRIVER_ROOT"},
|
||||
},
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (m command) validateFlags(r *cli.Context, opts *options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m command) run(c *cli.Context, opts *options) error {
|
||||
cache, err := ldcache.New(m.logger, opts.driverRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create ldcache: %v", err)
|
||||
}
|
||||
|
||||
lib32, lib64 := cache.List()
|
||||
|
||||
if len(lib32) == 0 {
|
||||
m.logger.Info("No 32-bit libraries found")
|
||||
} else {
|
||||
m.logger.Infof("%d 32-bit libraries found", len(lib32))
|
||||
for _, lib := range lib32 {
|
||||
m.logger.Infof("%v", lib)
|
||||
}
|
||||
}
|
||||
if len(lib64) == 0 {
|
||||
m.logger.Info("No 64-bit libraries found")
|
||||
} else {
|
||||
m.logger.Infof("%d 64-bit libraries found", len(lib64))
|
||||
for _, lib := range lib64 {
|
||||
m.logger.Infof("%v", lib)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -21,7 +21,6 @@ import (
|
||||
|
||||
devchar "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system/create-dev-char-symlinks"
|
||||
devicenodes "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system/create-device-nodes"
|
||||
ldcache "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system/print-ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
@ -47,7 +46,6 @@ func (m command) build() *cli.Command {
|
||||
system.Subcommands = []*cli.Command{
|
||||
devchar.NewCommand(m.logger),
|
||||
devicenodes.NewCommand(m.logger),
|
||||
ldcache.NewCommand(m.logger),
|
||||
}
|
||||
|
||||
return &system
|
||||
|
@ -22,15 +22,12 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
|
||||
)
|
||||
|
||||
const ldcachePath = "/etc/ld.so.cache"
|
||||
@ -82,10 +79,9 @@ type entry2 struct {
|
||||
|
||||
// LDCache represents the interface for performing lookups into the LDCache
|
||||
//
|
||||
//go:generate moq -out ldcache_mock.go . LDCache
|
||||
//go:generate moq -rm -out ldcache_mock.go . LDCache
|
||||
type LDCache interface {
|
||||
List() ([]string, []string)
|
||||
Lookup(...string) ([]string, []string)
|
||||
}
|
||||
|
||||
type ldcache struct {
|
||||
@ -105,14 +101,7 @@ func New(logger logger.Interface, root string) (LDCache, error) {
|
||||
|
||||
logger.Debugf("Opening ld.conf at %v", path)
|
||||
f, err := os.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
logger.Warningf("Could not find ld.so.cache at %v; creating empty cache", path)
|
||||
e := &empty{
|
||||
logger: logger,
|
||||
path: path,
|
||||
}
|
||||
return e, nil
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
@ -196,7 +185,7 @@ type entry struct {
|
||||
}
|
||||
|
||||
// getEntries returns the entires of the ldcache in a go-friendly struct.
|
||||
func (c *ldcache) getEntries(selected func(string) bool) []entry {
|
||||
func (c *ldcache) getEntries() []entry {
|
||||
var entries []entry
|
||||
for _, e := range c.entries {
|
||||
bits := 0
|
||||
@ -223,9 +212,6 @@ func (c *ldcache) getEntries(selected func(string) bool) []entry {
|
||||
c.logger.Debugf("Skipping invalid lib")
|
||||
continue
|
||||
}
|
||||
if !selected(lib) {
|
||||
continue
|
||||
}
|
||||
value := bytesToString(c.libs[e.Value:])
|
||||
if value == "" {
|
||||
c.logger.Debugf("Skipping invalid value for lib %v", lib)
|
||||
@ -236,51 +222,19 @@ func (c *ldcache) getEntries(selected func(string) bool) []entry {
|
||||
bits: bits,
|
||||
value: value,
|
||||
}
|
||||
|
||||
entries = append(entries, e)
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// List creates a list of libraries in the ldcache.
|
||||
// The 32-bit and 64-bit libraries are returned separately.
|
||||
func (c *ldcache) List() ([]string, []string) {
|
||||
all := func(s string) bool { return true }
|
||||
|
||||
return c.resolveSelected(all)
|
||||
}
|
||||
|
||||
// Lookup searches the ldcache for the specified prefixes.
|
||||
// The 32-bit and 64-bit libraries matching the prefixes are returned.
|
||||
func (c *ldcache) Lookup(libPrefixes ...string) ([]string, []string) {
|
||||
c.logger.Debugf("Looking up %v in cache", libPrefixes)
|
||||
|
||||
// We define a functor to check whether a given library name matches any of the prefixes
|
||||
matchesAnyPrefix := func(s string) bool {
|
||||
for _, p := range libPrefixes {
|
||||
if strings.HasPrefix(s, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return c.resolveSelected(matchesAnyPrefix)
|
||||
}
|
||||
|
||||
// resolveSelected process the entries in the LDCach based on the supplied filter and returns the resolved paths.
|
||||
// The paths are separated by bittage.
|
||||
func (c *ldcache) resolveSelected(selected func(string) bool) ([]string, []string) {
|
||||
paths := make(map[int][]string)
|
||||
processed := make(map[string]bool)
|
||||
|
||||
for _, e := range c.getEntries(selected) {
|
||||
path, err := c.resolve(e.value)
|
||||
if err != nil {
|
||||
c.logger.Debugf("Could not resolve entry: %v", err)
|
||||
continue
|
||||
}
|
||||
for _, e := range c.getEntries() {
|
||||
path := filepath.Join(c.root, e.value)
|
||||
if processed[path] {
|
||||
continue
|
||||
}
|
||||
@ -291,29 +245,6 @@ func (c *ldcache) resolveSelected(selected func(string) bool) ([]string, []strin
|
||||
return paths[32], paths[64]
|
||||
}
|
||||
|
||||
// resolve resolves the specified ldcache entry based on the value being processed.
|
||||
// The input is the name of the entry in the cache.
|
||||
func (c *ldcache) resolve(target string) (string, error) {
|
||||
name := filepath.Join(c.root, target)
|
||||
|
||||
c.logger.Debugf("checking %v", name)
|
||||
|
||||
link, err := symlinks.Resolve(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to resolve symlink: %v", err)
|
||||
}
|
||||
if link == name {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// We return absolute paths for all targets
|
||||
if !filepath.IsAbs(link) || strings.HasPrefix(link, ".") {
|
||||
link = filepath.Join(filepath.Dir(target), link)
|
||||
}
|
||||
|
||||
return c.resolve(link)
|
||||
}
|
||||
|
||||
// bytesToString converts a byte slice to a string.
|
||||
// This assumes that the byte slice is null-terminated
|
||||
func bytesToString(value []byte) string {
|
||||
|
@ -20,9 +20,6 @@ var _ LDCache = &LDCacheMock{}
|
||||
// ListFunc: func() ([]string, []string) {
|
||||
// panic("mock out the List method")
|
||||
// },
|
||||
// LookupFunc: func(strings ...string) ([]string, []string) {
|
||||
// panic("mock out the Lookup method")
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// // use mockedLDCache in code that requires LDCache
|
||||
@ -33,22 +30,13 @@ type LDCacheMock struct {
|
||||
// ListFunc mocks the List method.
|
||||
ListFunc func() ([]string, []string)
|
||||
|
||||
// LookupFunc mocks the Lookup method.
|
||||
LookupFunc func(strings ...string) ([]string, []string)
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
// List holds details about calls to the List method.
|
||||
List []struct {
|
||||
}
|
||||
// Lookup holds details about calls to the Lookup method.
|
||||
Lookup []struct {
|
||||
// Strings is the strings argument value.
|
||||
Strings []string
|
||||
}
|
||||
}
|
||||
lockList sync.RWMutex
|
||||
lockLookup sync.RWMutex
|
||||
}
|
||||
|
||||
// List calls ListFunc.
|
||||
@ -77,35 +65,3 @@ func (mock *LDCacheMock) ListCalls() []struct {
|
||||
mock.lockList.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Lookup calls LookupFunc.
|
||||
func (mock *LDCacheMock) Lookup(strings ...string) ([]string, []string) {
|
||||
if mock.LookupFunc == nil {
|
||||
panic("LDCacheMock.LookupFunc: method is nil but LDCache.Lookup was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Strings []string
|
||||
}{
|
||||
Strings: strings,
|
||||
}
|
||||
mock.lockLookup.Lock()
|
||||
mock.calls.Lookup = append(mock.calls.Lookup, callInfo)
|
||||
mock.lockLookup.Unlock()
|
||||
return mock.LookupFunc(strings...)
|
||||
}
|
||||
|
||||
// LookupCalls gets all the calls that were made to Lookup.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedLDCache.LookupCalls())
|
||||
func (mock *LDCacheMock) LookupCalls() []struct {
|
||||
Strings []string
|
||||
} {
|
||||
var calls []struct {
|
||||
Strings []string
|
||||
}
|
||||
mock.lockLookup.RLock()
|
||||
calls = mock.calls.Lookup
|
||||
mock.lockLookup.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
118
internal/lookup/ldcache.go
Normal file
118
internal/lookup/ldcache.go
Normal file
@ -0,0 +1,118 @@
|
||||
/**
|
||||
# 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 lookup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
)
|
||||
|
||||
type ldcacheLocator struct {
|
||||
*builder
|
||||
resolvesTo map[string]string
|
||||
}
|
||||
|
||||
var _ Locator = (*ldcacheLocator)(nil)
|
||||
|
||||
func NewLdcacheLocator(opts ...Option) Locator {
|
||||
b := newBuilder(opts...)
|
||||
|
||||
cache, err := ldcache.New(b.logger, b.root)
|
||||
if err != nil {
|
||||
b.logger.Warningf("Failed to load ldcache: %v", err)
|
||||
if b.isOptional {
|
||||
return &null{}
|
||||
}
|
||||
return ¬Found{}
|
||||
}
|
||||
|
||||
chain := NewSymlinkChainLocator(WithOptional(true))
|
||||
|
||||
resolvesTo := make(map[string]string)
|
||||
_, libs64 := cache.List()
|
||||
for _, library := range libs64 {
|
||||
if _, processed := resolvesTo[library]; processed {
|
||||
continue
|
||||
}
|
||||
candidates, err := chain.Locate(library)
|
||||
if err != nil {
|
||||
b.logger.Errorf("error processing library %s from ldcache: %v", library, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(candidates) == 0 {
|
||||
resolvesTo[library] = library
|
||||
continue
|
||||
}
|
||||
|
||||
// candidates represents a symlink chain.
|
||||
// The first element represents the start of the chain and the last
|
||||
// element the final target.
|
||||
target := candidates[len(candidates)-1]
|
||||
for _, candidate := range candidates {
|
||||
resolvesTo[candidate] = target
|
||||
}
|
||||
}
|
||||
|
||||
return &ldcacheLocator{
|
||||
builder: b,
|
||||
resolvesTo: resolvesTo,
|
||||
}
|
||||
}
|
||||
|
||||
// Locate finds the specified libraryname.
|
||||
// If the input is a library name, the ldcache is searched otherwise the
|
||||
// provided path is resolved as a symlink.
|
||||
func (l ldcacheLocator) Locate(libname string) ([]string, error) {
|
||||
var matcher func(string, string) bool
|
||||
|
||||
if filepath.IsAbs(libname) {
|
||||
matcher = func(p string, c string) bool {
|
||||
m, _ := filepath.Match(filepath.Join(l.root, p), c)
|
||||
return m
|
||||
}
|
||||
} else {
|
||||
matcher = func(p string, c string) bool {
|
||||
m, _ := filepath.Match(p, filepath.Base(c))
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
var matches []string
|
||||
seen := make(map[string]bool)
|
||||
for name, target := range l.resolvesTo {
|
||||
if !matcher(libname, name) {
|
||||
continue
|
||||
}
|
||||
if seen[target] {
|
||||
continue
|
||||
}
|
||||
seen[target] = true
|
||||
matches = append(matches, target)
|
||||
}
|
||||
|
||||
slices.Sort(matches)
|
||||
|
||||
if len(matches) == 0 && !l.isOptional {
|
||||
return nil, fmt.Errorf("%s: %w", libname, ErrNotFound)
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
77
internal/lookup/ldcache_test.go
Normal file
77
internal/lookup/ldcache_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
package lookup
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
func TestLDCacheLookup(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
moduleRoot, err := test.GetModuleRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
rootFs string
|
||||
inputs []string
|
||||
expected string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
rootFs: "rootfs-empty",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expectedError: ErrNotFound,
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-1",
|
||||
inputs: []string{
|
||||
"libcuda.so.1",
|
||||
"libcuda.so.*",
|
||||
"libcuda.so.*.*",
|
||||
"libcuda.so.999.88.77",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.1",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.*",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.*.*",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.999.88.77",
|
||||
},
|
||||
expected: "/lib/x86_64-linux-gnu/libcuda.so.999.88.77",
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-2",
|
||||
inputs: []string{
|
||||
"libcuda.so.1",
|
||||
"libcuda.so.*",
|
||||
"libcuda.so.*.*",
|
||||
"libcuda.so.999.88.77",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.1",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.*",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.*.*",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
expected: "/var/lib/nvidia/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for _, input := range tc.inputs {
|
||||
t.Run(tc.rootFs+" "+input, func(t *testing.T) {
|
||||
rootfs := filepath.Join(moduleRoot, "testdata", "lookup", tc.rootFs)
|
||||
l := NewLdcacheLocator(
|
||||
WithLogger(logger),
|
||||
WithRoot(rootfs),
|
||||
)
|
||||
|
||||
candidates, err := l.Locate(input)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
if tc.expectedError == nil {
|
||||
require.Equal(t, []string{filepath.Join(rootfs, tc.expected)}, candidates)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -16,20 +16,6 @@
|
||||
|
||||
package lookup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
type ldcacheLocator struct {
|
||||
logger logger.Interface
|
||||
cache ldcache.LDCache
|
||||
}
|
||||
|
||||
var _ Locator = (*ldcacheLocator)(nil)
|
||||
|
||||
// NewLibraryLocator creates a library locator using the specified options.
|
||||
func NewLibraryLocator(opts ...Option) Locator {
|
||||
b := newBuilder(opts...)
|
||||
@ -63,39 +49,7 @@ func NewLibraryLocator(opts ...Option) Locator {
|
||||
|
||||
l := First(
|
||||
symlinkLocator,
|
||||
newLdcacheLocator(opts...),
|
||||
NewLdcacheLocator(opts...),
|
||||
)
|
||||
return l
|
||||
}
|
||||
|
||||
func newLdcacheLocator(opts ...Option) Locator {
|
||||
b := newBuilder(opts...)
|
||||
|
||||
cache, err := ldcache.New(b.logger, b.root)
|
||||
if err != nil {
|
||||
// If we failed to open the LDCache, we default to a symlink locator.
|
||||
b.logger.Warningf("Failed to load ldcache: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ldcacheLocator{
|
||||
logger: b.logger,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// Locate finds the specified libraryname.
|
||||
// If the input is a library name, the ldcache is searched otherwise the
|
||||
// provided path is resolved as a symlink.
|
||||
func (l ldcacheLocator) Locate(libname string) ([]string, error) {
|
||||
paths32, paths64 := l.cache.Lookup(libname)
|
||||
if len(paths32) > 0 {
|
||||
l.logger.Warningf("Ignoring 32-bit libraries for %v: %v", libname, paths32)
|
||||
}
|
||||
|
||||
if len(paths64) == 0 {
|
||||
return nil, fmt.Errorf("64-bit library %v: %w", libname, ErrNotFound)
|
||||
}
|
||||
|
||||
return paths64, nil
|
||||
}
|
||||
|
@ -24,82 +24,8 @@ import (
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
)
|
||||
|
||||
func TestLDCacheLocator(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
testDir := t.TempDir()
|
||||
symlinkDir := filepath.Join(testDir, "/lib/symlink")
|
||||
require.NoError(t, os.MkdirAll(symlinkDir, 0755))
|
||||
|
||||
versionLib := filepath.Join(symlinkDir, "libcuda.so.1.2.3")
|
||||
soLink := filepath.Join(symlinkDir, "libcuda.so")
|
||||
sonameLink := filepath.Join(symlinkDir, "libcuda.so.1")
|
||||
|
||||
_, err := os.Create(versionLib)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.Symlink(versionLib, sonameLink))
|
||||
require.NoError(t, os.Symlink(sonameLink, soLink))
|
||||
|
||||
lut := newLdcacheLocator(
|
||||
WithLogger(logger),
|
||||
WithRoot(testDir),
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
libname string
|
||||
ldcacheMap map[string]string
|
||||
expected []string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "lib only resolves in LDCache",
|
||||
libname: "libcuda.so",
|
||||
ldcacheMap: map[string]string{
|
||||
"libcuda.so": "/lib/from/ldcache/libcuda.so.4.5.6",
|
||||
},
|
||||
expected: []string{"/lib/from/ldcache/libcuda.so.4.5.6"},
|
||||
},
|
||||
{
|
||||
description: "lib only not in LDCache returns error",
|
||||
libname: "libnotcuda.so",
|
||||
expectedError: ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
// We override the LDCache with a mock implementation
|
||||
l := lut.(*ldcacheLocator)
|
||||
l.cache = &ldcache.LDCacheMock{
|
||||
LookupFunc: func(strings ...string) ([]string, []string) {
|
||||
var result []string
|
||||
for _, s := range strings {
|
||||
if v, ok := tc.ldcacheMap[s]; ok {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return nil, result
|
||||
},
|
||||
}
|
||||
|
||||
candidates, err := lut.Locate(tc.libname)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
|
||||
var cleanedCandidates []string
|
||||
for _, c := range candidates {
|
||||
// On MacOS /var and /tmp symlink to /private/var and /private/tmp which is included in the resolved path.
|
||||
cleanedCandidates = append(cleanedCandidates, strings.TrimPrefix(c, "/private"))
|
||||
}
|
||||
require.EqualValues(t, tc.expected, cleanedCandidates)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLibraryLocator(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
# Copyright (c) NVIDIA CORPORATION. All rights reserved.
|
||||
# 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.
|
||||
@ -14,24 +14,23 @@
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package ldcache
|
||||
package lookup
|
||||
|
||||
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
import "fmt"
|
||||
|
||||
type empty struct {
|
||||
logger logger.Interface
|
||||
path string
|
||||
// A null locator always returns an empty response.
|
||||
type null struct {
|
||||
}
|
||||
|
||||
var _ LDCache = (*empty)(nil)
|
||||
|
||||
// List always returns nil for an empty ldcache
|
||||
func (e *empty) List() ([]string, []string) {
|
||||
// Locate always returns empty for a null locator.
|
||||
func (l *null) Locate(string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Lookup logs a debug message and returns nil for an empty ldcache
|
||||
func (e *empty) Lookup(prefixes ...string) ([]string, []string) {
|
||||
e.logger.Debugf("Calling Lookup(%v) on empty ldcache: %v", prefixes, e.path)
|
||||
return nil, nil
|
||||
// A notFound locator always returns an ErrNotFound error.
|
||||
type notFound struct {
|
||||
}
|
||||
|
||||
func (l *notFound) Locate(s string) ([]string, error) {
|
||||
return nil, fmt.Errorf("%s: %w", s, ErrNotFound)
|
||||
}
|
81
internal/lookup/root/root_test.go
Normal file
81
internal/lookup/root/root_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
# 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 root
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
func TestDriverLibrariesLocate(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
moduleRoot, err := test.GetModuleRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
rootFs string
|
||||
inputs []string
|
||||
expected string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
rootFs: "rootfs-empty",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expectedError: lookup.ErrNotFound,
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-no-cache-lib64",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expected: "/usr/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-1",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expected: "/lib/x86_64-linux-gnu/libcuda.so.999.88.77",
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-2",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expected: "/var/lib/nvidia/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for _, input := range tc.inputs {
|
||||
t.Run(tc.rootFs+input, func(t *testing.T) {
|
||||
rootfs := filepath.Join(moduleRoot, "testdata", "lookup", tc.rootFs)
|
||||
driver := New(
|
||||
WithLogger(logger),
|
||||
WithDriverRoot(rootfs),
|
||||
)
|
||||
|
||||
candidates, err := driver.Libraries().Locate(input)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
if tc.expectedError == nil {
|
||||
require.Equal(t, []string{filepath.Join(rootfs, tc.expected)}, candidates)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -62,6 +62,7 @@ func (p symlinkChain) Locate(pattern string) ([]string, error) {
|
||||
return candidates, nil
|
||||
}
|
||||
|
||||
var filenames []string
|
||||
found := make(map[string]bool)
|
||||
for len(candidates) > 0 {
|
||||
candidate := candidates[0]
|
||||
@ -70,6 +71,7 @@ func (p symlinkChain) Locate(pattern string) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
found[candidate] = true
|
||||
filenames = append(filenames, candidate)
|
||||
|
||||
target, err := symlinks.Resolve(candidate)
|
||||
if err != nil {
|
||||
@ -88,11 +90,6 @@ func (p symlinkChain) Locate(pattern string) ([]string, error) {
|
||||
candidates = append(candidates, target)
|
||||
}
|
||||
}
|
||||
|
||||
var filenames []string
|
||||
for f := range found {
|
||||
filenames = append(filenames, f)
|
||||
}
|
||||
return filenames, nil
|
||||
}
|
||||
|
||||
|
0
testdata/go.mod
vendored
Normal file
0
testdata/go.mod
vendored
Normal file
2
testdata/lookup/rootfs-1/README.md
vendored
Normal file
2
testdata/lookup/rootfs-1/README.md
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
This rootfs represents a host with the CUDA driver libraries installed in
|
||||
/lib/x86_64-linux-gnu. The included /etc/ld.so.cache was copied from such as system.
|
BIN
testdata/lookup/rootfs-1/etc/ld.so.cache
vendored
Normal file
BIN
testdata/lookup/rootfs-1/etc/ld.so.cache
vendored
Normal file
Binary file not shown.
1
testdata/lookup/rootfs-1/lib/x86_64-linux-gnu/libcuda.so.1
vendored
Symbolic link
1
testdata/lookup/rootfs-1/lib/x86_64-linux-gnu/libcuda.so.1
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
libcuda.so.999.88.77
|
0
testdata/lookup/rootfs-1/lib/x86_64-linux-gnu/libcuda.so.999.88.77
vendored
Normal file
0
testdata/lookup/rootfs-1/lib/x86_64-linux-gnu/libcuda.so.999.88.77
vendored
Normal file
3
testdata/lookup/rootfs-2/README.md
vendored
Normal file
3
testdata/lookup/rootfs-2/README.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
This rootfs represents a host with the CUDA driver libraries installed in
|
||||
/var/lib/nvidia/lib64. The included /etc/ld.so.cache was generated in a container
|
||||
simulating such as system.
|
BIN
testdata/lookup/rootfs-2/etc/ld.so.cache
vendored
Normal file
BIN
testdata/lookup/rootfs-2/etc/ld.so.cache
vendored
Normal file
Binary file not shown.
1
testdata/lookup/rootfs-2/var/lib/nvidia/lib64/libcuda.so.1
vendored
Symbolic link
1
testdata/lookup/rootfs-2/var/lib/nvidia/lib64/libcuda.so.1
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
libcuda.so.999.88.77
|
0
testdata/lookup/rootfs-2/var/lib/nvidia/lib64/libcuda.so.999.88.77
vendored
Normal file
0
testdata/lookup/rootfs-2/var/lib/nvidia/lib64/libcuda.so.999.88.77
vendored
Normal file
1
testdata/lookup/rootfs-empty/README.md
vendored
Normal file
1
testdata/lookup/rootfs-empty/README.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
The folders represents an empty rootfs.
|
1
testdata/lookup/rootfs-no-cache-lib64/usr/lib64/libcuda.so.1
vendored
Symbolic link
1
testdata/lookup/rootfs-no-cache-lib64/usr/lib64/libcuda.so.1
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
libcuda.so.999.88.77
|
0
testdata/lookup/rootfs-no-cache-lib64/usr/lib64/libcuda.so.999.88.77
vendored
Normal file
0
testdata/lookup/rootfs-no-cache-lib64/usr/lib64/libcuda.so.999.88.77
vendored
Normal file
Loading…
Reference in New Issue
Block a user