Skip to content

Commit cd52be8

Browse files
authored
Merge pull request #529 from elezar/vulkan-location
Support vulkan ICD files in a driver root
2 parents b5743da + 5f2be72 commit cd52be8

File tree

4 files changed

+258
-3
lines changed

4 files changed

+258
-3
lines changed

internal/discover/graphics.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@ func NewGraphicsMountsDiscoverer(logger logger.Interface, driver *root.Driver, n
6565
driver.Root,
6666
[]string{
6767
"glvnd/egl_vendor.d/10_nvidia.json",
68-
"vulkan/icd.d/nvidia_icd.json",
69-
"vulkan/icd.d/nvidia_layers.json",
70-
"vulkan/implicit_layer.d/nvidia_layers.json",
7168
"egl/egl_external_platform.d/15_nvidia_gbm.json",
7269
"egl/egl_external_platform.d/10_nvidia_wayland.json",
7370
"nvidia/nvoptix.bin",
@@ -79,12 +76,31 @@ func NewGraphicsMountsDiscoverer(logger logger.Interface, driver *root.Driver, n
7976
discover := Merge(
8077
libraries,
8178
jsonMounts,
79+
newVulkanMountsDiscoverer(logger, driver),
8280
xorg,
8381
)
8482

8583
return discover, nil
8684
}
8785

86+
// newVulkanMountsDiscoverer creates a discoverer for vulkan ICD files.
87+
// For these files we search the standard driver config paths as well as the
88+
// driver root itself. This allows us to support GKE installations where the
89+
// vulkan ICD files are at {{ .driverRoot }}/vulkan instead of in /etc/vulkan.
90+
func newVulkanMountsDiscoverer(logger logger.Interface, driver *root.Driver) Discover {
91+
locator := lookup.First(driver.Configs(), driver.Files())
92+
return &mountsToContainerPath{
93+
logger: logger,
94+
locator: locator,
95+
required: []string{
96+
"vulkan/icd.d/nvidia_icd.json",
97+
"vulkan/icd.d/nvidia_layers.json",
98+
"vulkan/implicit_layer.d/nvidia_layers.json",
99+
},
100+
containerRoot: "/etc",
101+
}
102+
}
103+
88104
type drmDevicesByPath struct {
89105
None
90106
logger logger.Interface
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
# Copyright 2024 NVIDIA CORPORATION
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
**/
16+
17+
package discover
18+
19+
import (
20+
"fmt"
21+
"path/filepath"
22+
"strings"
23+
24+
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
25+
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
26+
)
27+
28+
// mountsToContainerPath defines a Discoverer for a required set of mounts.
29+
// When these are discovered by a locator the specified container root is used
30+
// to construct the container path for the mount returned.
31+
type mountsToContainerPath struct {
32+
None
33+
logger logger.Interface
34+
locator lookup.Locator
35+
required []string
36+
containerRoot string
37+
}
38+
39+
func (d *mountsToContainerPath) Mounts() ([]Mount, error) {
40+
seen := make(map[string]bool)
41+
var mounts []Mount
42+
for _, target := range d.required {
43+
if strings.Contains(target, "*") {
44+
// TODO: We could relax this condition.
45+
return nil, fmt.Errorf("wildcard patterns are not supported: %s", target)
46+
}
47+
candidates, err := d.locator.Locate(target)
48+
if err != nil {
49+
d.logger.Warningf("Could not locate %v: %v", target, err)
50+
continue
51+
}
52+
if len(candidates) == 0 {
53+
d.logger.Warningf("Missing %v", target)
54+
continue
55+
}
56+
hostPath := candidates[0]
57+
if seen[hostPath] {
58+
d.logger.Debugf("Skipping duplicate mount %v", hostPath)
59+
continue
60+
}
61+
seen[hostPath] = true
62+
d.logger.Debugf("Located %v as %v", target, hostPath)
63+
64+
containerPath := filepath.Join(d.containerRoot, target)
65+
d.logger.Infof("Selecting %v as %v", hostPath, containerPath)
66+
67+
mount := Mount{
68+
HostPath: hostPath,
69+
Path: containerPath,
70+
Options: []string{
71+
"ro",
72+
"nosuid",
73+
"nodev",
74+
"bind",
75+
},
76+
}
77+
mounts = append(mounts, mount)
78+
}
79+
80+
return mounts, nil
81+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**
2+
# Copyright 2024 NVIDIA CORPORATION
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
**/
16+
17+
package discover
18+
19+
import (
20+
"errors"
21+
"testing"
22+
23+
testlog "github.com/sirupsen/logrus/hooks/test"
24+
"github.com/stretchr/testify/require"
25+
26+
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
27+
)
28+
29+
func TestMountsToContainerPath(t *testing.T) {
30+
logger, _ := testlog.NewNullLogger()
31+
mountOptions := []string{
32+
"ro",
33+
"nosuid",
34+
"nodev",
35+
"bind",
36+
}
37+
38+
testCases := []struct {
39+
description string
40+
required []string
41+
locator lookup.Locator
42+
containerRoot string
43+
expectedMounts []Mount
44+
expectedError error
45+
}{
46+
{
47+
description: "containerRoot is prepended",
48+
required: []string{"a/path/exists.txt", "another/path/exists.txt"},
49+
locator: &lookup.LocatorMock{
50+
LocateFunc: func(s string) ([]string, error) {
51+
return []string{"/located/root/" + s}, nil
52+
},
53+
},
54+
containerRoot: "/container",
55+
expectedMounts: []Mount{
56+
{
57+
HostPath: "/located/root/a/path/exists.txt",
58+
Path: "/container/a/path/exists.txt",
59+
Options: mountOptions,
60+
},
61+
{
62+
HostPath: "/located/root/another/path/exists.txt",
63+
Path: "/container/another/path/exists.txt",
64+
Options: mountOptions,
65+
},
66+
},
67+
},
68+
{
69+
description: "duplicate mounts are skipped",
70+
required: []string{"a/path/exists.txt", "another/path/exists.txt"},
71+
locator: &lookup.LocatorMock{
72+
LocateFunc: func(s string) ([]string, error) {
73+
return []string{"/located/root/single.txt"}, nil
74+
},
75+
},
76+
containerRoot: "/container",
77+
expectedMounts: []Mount{
78+
{
79+
HostPath: "/located/root/single.txt",
80+
Path: "/container/a/path/exists.txt",
81+
Options: mountOptions,
82+
},
83+
},
84+
},
85+
{
86+
description: "locator errors are ignored",
87+
required: []string{"a/path/exists.txt"},
88+
locator: &lookup.LocatorMock{
89+
LocateFunc: func(s string) ([]string, error) {
90+
return nil, errors.New("not found")
91+
},
92+
},
93+
containerRoot: "/container",
94+
expectedMounts: []Mount{},
95+
},
96+
{
97+
description: "not located are ignored",
98+
required: []string{"a/path/exists.txt"},
99+
locator: &lookup.LocatorMock{
100+
LocateFunc: func(s string) ([]string, error) {
101+
return nil, nil
102+
},
103+
},
104+
containerRoot: "/container",
105+
expectedMounts: []Mount{},
106+
},
107+
{
108+
description: "second candidate is ignored",
109+
required: []string{"a/path/exists.txt"},
110+
locator: &lookup.LocatorMock{
111+
LocateFunc: func(s string) ([]string, error) {
112+
return []string{"/located/root/" + s, "/located2/root/" + s}, nil
113+
},
114+
},
115+
containerRoot: "/container",
116+
expectedMounts: []Mount{
117+
{
118+
HostPath: "/located/root/a/path/exists.txt",
119+
Path: "/container/a/path/exists.txt",
120+
Options: mountOptions,
121+
},
122+
},
123+
},
124+
}
125+
126+
for _, tc := range testCases {
127+
t.Run(tc.description, func(t *testing.T) {
128+
d := mountsToContainerPath{
129+
logger: logger,
130+
locator: tc.locator,
131+
required: tc.required,
132+
containerRoot: tc.containerRoot,
133+
}
134+
135+
devices, err := d.Devices()
136+
require.NoError(t, err)
137+
require.Empty(t, devices)
138+
139+
hooks, err := d.Hooks()
140+
require.NoError(t, err)
141+
require.Empty(t, hooks)
142+
143+
mounts, err := d.Mounts()
144+
require.ErrorIs(t, err, tc.expectedError)
145+
require.ElementsMatch(t, tc.expectedMounts, mounts)
146+
})
147+
}
148+
}

internal/lookup/root/root.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ func New(opts ...Option) *Driver {
4747
return d
4848
}
4949

50+
// Files returns a Locator for arbitrary driver files.
51+
func (r *Driver) Files(opts ...lookup.Option) lookup.Locator {
52+
return lookup.NewFileLocator(
53+
append(opts,
54+
lookup.WithLogger(r.logger),
55+
lookup.WithRoot(r.Root),
56+
)...,
57+
)
58+
}
59+
5060
// Libraries returns a Locator for driver libraries.
5161
func (r *Driver) Libraries() lookup.Locator {
5262
return lookup.NewLibraryLocator(

0 commit comments

Comments
 (0)