Skip to content

Commit 0f4bd44

Browse files
committed
[no-relnote] Refactor creation of discoverer for tegra systems
This change updates the way we construct a discoverer for tegra systems to be more flexible in terms of how the SOURCES of the mount specs can be specified. This allows for subsequent changes like adding (or removing) mount specs at the point of construction. Signed-off-by: Evan Lezar <[email protected]>
1 parent 58e2344 commit 0f4bd44

File tree

5 files changed

+162
-5
lines changed

5 files changed

+162
-5
lines changed

internal/platform-support/tegra/csv_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,17 @@ func TestDiscovererFromCSVFiles(t *testing.T) {
186186
o := options{
187187
logger: logger,
188188
hookCreator: hookCreator,
189-
ignorePatterns: tc.ignorePatterns,
190189
symlinkLocator: tc.symlinkLocator,
191190
symlinkChainLocator: tc.symlinkChainLocator,
192191
resolveSymlink: tc.symlinkResolver,
192+
193+
mountSpecs: Transform(
194+
tc.moutSpecs,
195+
IgnoreSymlinkMountSpecsByPattern(tc.ignorePatterns...),
196+
),
193197
}
194198

195-
d, err := o.newDiscovererFromMountSpecs(tc.moutSpecs)
199+
d, err := o.newDiscovererFromMountSpecs(o.mountSpecs.MountSpecPathsByType())
196200
require.ErrorIs(t, err, tc.expectedError)
197201

198202
hooks, err := d.Hooks()

internal/platform-support/tegra/filter.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,89 @@ func (d ignoreSymlinkMountSpecPatterns) Apply(input MountSpecPathsByTyper) Mount
5959

6060
return ms
6161
}
62+
63+
// A filter removes elements from an input list and returns the remaining
64+
// elements.
65+
type filter interface {
66+
apply(...string) []string
67+
}
68+
69+
// A stringMatcher implements the MatchString function.
70+
type stringMatcher interface {
71+
MatchString(string) bool
72+
}
73+
74+
// A matcherAsFilter is used to ensure that a string matcher can be used as a filter.
75+
type matcherAsFilter struct {
76+
stringMatcher
77+
}
78+
79+
type filterByMountSpecType map[csv.MountSpecType]filter
80+
81+
type pathPatterns []string
82+
type pathPattern string
83+
type basenamePattern string
84+
85+
// MatchString for a set of path patterns returns true if any of the patterns
86+
// matches against the input string.
87+
func (d pathPatterns) MatchString(input string) bool {
88+
for _, pattern := range d {
89+
if match := pathPattern(pattern).MatchString(input); match {
90+
return true
91+
}
92+
}
93+
return false
94+
}
95+
96+
// MatchString attempts to match a path pattern to the specified input string.
97+
// If the pattern starts with `**/` the input is treated as a path and only
98+
// the basenames are matched using regular glob rules.
99+
func (d pathPattern) MatchString(input string) bool {
100+
if strings.HasPrefix(string(d), "**/") {
101+
return basenamePattern(d).MatchString(input)
102+
}
103+
match, _ := filepath.Match(string(d), input)
104+
return match
105+
}
106+
107+
// MatchString for a basename pattern applies the specified pattern against the
108+
// basename of the input.
109+
// If the pattern starts with **/, this is stripped before attempting to match.
110+
func (d basenamePattern) MatchString(input string) bool {
111+
pattern := strings.TrimPrefix(string(d), "**/")
112+
match, _ := filepath.Match(pattern, filepath.Base(input))
113+
return match
114+
}
115+
116+
// Apply the specified per-type filters to the input mount specs.
117+
func (p filterByMountSpecType) Apply(input MountSpecPathsByTyper) MountSpecPathsByTyper {
118+
ms := input.MountSpecPathsByType()
119+
for t, filter := range p {
120+
if len(ms[t]) == 0 {
121+
continue
122+
}
123+
ms[t] = filter.apply(ms[t]...)
124+
}
125+
return ms
126+
}
127+
128+
// apply uses a matcher to filter an input string.
129+
// Each element in the input that matches is skipped and the remaining elements
130+
// are returned.
131+
func (f *matcherAsFilter) apply(input ...string) []string {
132+
var filtered []string
133+
for _, path := range input {
134+
if f.MatchString(path) {
135+
continue
136+
}
137+
filtered = append(filtered, path)
138+
}
139+
return filtered
140+
}
141+
142+
// removeAll is a filter that will not return any inputs.
143+
type removeAll struct{}
144+
145+
func (a removeAll) apply(...string) []string {
146+
return nil
147+
}

internal/platform-support/tegra/filter_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
func TestIgnorePatterns(t *testing.T) {
2626
testCases := []struct {
2727
description string
28-
blockedFilter []string
28+
blockedFilter pathPatterns
2929
input []string
3030
expected []string
3131
}{

internal/platform-support/tegra/mount_specs.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package tegra
1919

2020
import (
21+
"regexp"
22+
2123
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
2224
)
2325

@@ -80,10 +82,74 @@ type transformMountSpecByPathsByType struct {
8082
input MountSpecPathsByTyper
8183
}
8284

85+
type merge []MountSpecPathsByTyper
86+
87+
// Merge combines the MountSpecPathsByType for the specified sources.
88+
func Merge(sources ...MountSpecPathsByTyper) MountSpecPathsByTyper {
89+
return merge(sources)
90+
}
91+
92+
// MountSpecPathsByType for a set of merged mount specs combines the list of
93+
// paths per type.
94+
func (ts merge) MountSpecPathsByType() MountSpecPathsByType {
95+
targetsByType := make(MountSpecPathsByType)
96+
for _, t := range ts {
97+
if t == nil {
98+
continue
99+
}
100+
for tType, targets := range t.MountSpecPathsByType() {
101+
targetsByType[tType] = append(targetsByType[tType], targets...)
102+
}
103+
}
104+
return targetsByType
105+
}
106+
83107
func (m transformMountSpecByPathsByType) MountSpecPathsByType() MountSpecPathsByType {
84108
return m.Apply(m.input).MountSpecPathsByType()
85109
}
86110

87111
func IgnoreSymlinkMountSpecsByPattern(ignorePatterns ...string) Transformer {
88112
return ignoreSymlinkMountSpecPatterns(ignorePatterns)
89113
}
114+
115+
// OnlyDeviceNodes creates a transformer that will remove any input mounts specs
116+
// that are not of the `MountSpecDev` type.
117+
func OnlyDeviceNodes() Transformer {
118+
return filterByMountSpecType{
119+
csv.MountSpecDir: removeAll{},
120+
csv.MountSpecLib: removeAll{},
121+
csv.MountSpecSym: removeAll{},
122+
}
123+
}
124+
125+
// WithoutDeviceNodes creates a transformer that will remove entries with type
126+
// MountSpecDevice from the input.
127+
func WithoutDeviceNodes() Transformer {
128+
return filterByMountSpecType{
129+
csv.MountSpecDev: removeAll{},
130+
}
131+
}
132+
133+
// WithoutRegularDeviceNodes creates a transfomer which removes
134+
// regular `/dev/nvidia[0-9]+` device nodes from the source.
135+
func WithoutRegularDeviceNodes() Transformer {
136+
return filterByMountSpecType{
137+
csv.MountSpecDev: &matcherAsFilter{regexp.MustCompile("^/dev/nvidia[0-9]+$")},
138+
}
139+
}
140+
141+
// DeviceNodes creates a set of MountSpecPaths for the specified device nodes.
142+
// These have the MoutSpecDev type.
143+
func DeviceNodes(dn ...string) MountSpecPathsByTyper {
144+
return MountSpecPathsByType{
145+
csv.MountSpecDev: dn,
146+
}
147+
}
148+
149+
// DeviceNodes creates a set of MountSpecPaths for the specified symlinks.
150+
// These have the MountSpecSym type.
151+
func Symlinks(s ...string) MountSpecPathsByTyper {
152+
return MountSpecPathsByType{
153+
csv.MountSpecSym: s,
154+
}
155+
}

internal/platform-support/tegra/tegra.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func New(opts ...Option) (discover.Discover, error) {
5656

5757
mountSpecDiscoverer, err := o.newDiscovererFromMountSpecs(o.mountSpecs.MountSpecPathsByType())
5858
if err != nil {
59-
return nil, fmt.Errorf("failed to create discoverer for mount specs: %w", err)
59+
return nil, fmt.Errorf("failed to create discoverer for mount specs: %v", err)
6060
}
6161

6262
ldcacheUpdateHook, err := discover.NewLDCacheUpdateHook(o.logger, mountSpecDiscoverer, o.hookCreator, o.ldconfigPath)
@@ -75,7 +75,8 @@ func New(opts ...Option) (discover.Discover, error) {
7575

7676
d := discover.Merge(
7777
mountSpecDiscoverer,
78-
// The ldcacheUpdateHook is added last to ensure that the created symlinks are included
78+
// The ldcacheUpdateHook is added after the mount spec discoverer to
79+
// ensure that the symlinks are included.
7980
ldcacheUpdateHook,
8081
tegraSystemMounts,
8182
)

0 commit comments

Comments
 (0)