mounts_test.go 7.59 KB
Newer Older
1
package runtime
2
3
4
5
6
7
8
9

import (
	"os"
	"path/filepath"
	"testing"

	specs "github.com/opencontainers/runtime-spec/specs-go"

10
	"github.com/ai-dynamo/dynamo/deploy/snapshot/internal/types"
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
)

func TestClassifyMounts(t *testing.T) {
	tests := []struct {
		name    string
		mounts  []types.MountInfo
		ociSpec *specs.Spec
		rootFS  string
		want    map[string]bool // mountpoint → expected IsOCIManaged
	}{
		{
			name: "mount matching OCI destination",
			mounts: []types.MountInfo{
				{MountPoint: "/etc/hostname"},
			},
			ociSpec: &specs.Spec{
				Mounts: []specs.Mount{{Destination: "/etc/hostname"}},
			},
			want: map[string]bool{"/etc/hostname": true},
		},
		{
			name: "mount with no OCI match",
			mounts: []types.MountInfo{
				{MountPoint: "/some/random/path"},
			},
			ociSpec: &specs.Spec{
				Mounts: []specs.Mount{{Destination: "/etc/hostname"}},
			},
			want: map[string]bool{"/some/random/path": false},
		},
		{
			name: "/run/ mount aliased to /var/run/ in OCI spec",
			mounts: []types.MountInfo{
				{MountPoint: "/run/secrets"},
			},
			ociSpec: &specs.Spec{
				Mounts: []specs.Mount{{Destination: "/var/run/secrets"}},
			},
			want: map[string]bool{"/run/secrets": true},
		},
		{
			name: "/var/run/ mount aliased to /run/ in OCI spec",
			mounts: []types.MountInfo{
				{MountPoint: "/var/run/secrets"},
			},
			ociSpec: &specs.Spec{
				Mounts: []specs.Mount{{Destination: "/run/secrets"}},
			},
			want: map[string]bool{"/var/run/secrets": true},
		},
		{
			name: "/run/ prefix without alias match stays unmanaged",
			mounts: []types.MountInfo{
				{MountPoint: "/run/other"},
			},
			ociSpec: &specs.Spec{
				Mounts: []specs.Mount{{Destination: "/var/run/different"}},
			},
			want: map[string]bool{"/run/other": false},
		},
		{
			name:   "nil OCI spec classifies nothing",
			mounts: []types.MountInfo{{MountPoint: "/etc/hostname"}},
			want:   map[string]bool{"/etc/hostname": false},
		},
		{
			name: "masked and readonly paths are OCI-managed",
			mounts: []types.MountInfo{
				{MountPoint: "/proc/acpi"},
				{MountPoint: "/proc/sys"},
			},
			ociSpec: &specs.Spec{
				Linux: &specs.Linux{
					MaskedPaths:   []string{"/proc/acpi"},
					ReadonlyPaths: []string{"/proc/sys"},
				},
			},
			want: map[string]bool{
				"/proc/acpi": true,
				"/proc/sys":  true,
			},
		},
		{
			name:   "empty mounts slice",
			mounts: []types.MountInfo{},
			ociSpec: &specs.Spec{
				Mounts: []specs.Mount{{Destination: "/etc/hostname"}},
			},
			want: map[string]bool{},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := ClassifyMounts(tc.mounts, tc.ociSpec, tc.rootFS)
			for _, m := range result {
				expected, ok := tc.want[m.MountPoint]
				if !ok {
					continue
				}
				if m.IsOCIManaged != expected {
					t.Errorf("mount %q: IsOCIManaged = %v, want %v", m.MountPoint, m.IsOCIManaged, expected)
				}
			}
		})
	}
}

func TestBuildMountPolicy(t *testing.T) {
	tests := []struct {
		name         string
		mounts       []types.MountInfo
		rootFS       string
		maskedPaths  []string
		wantExt      map[string]string // expected entries in extMap
		wantSkipped  []string          // expected entries in skipped
		wantNotInExt []string          // keys that must NOT be in extMap
	}{
		{
			name: "non-OCI /proc submount is skipped",
			mounts: []types.MountInfo{
				{MountPoint: "/proc/kcore", IsOCIManaged: false},
			},
			wantSkipped:  []string{"/proc/kcore"},
			wantNotInExt: []string{"/proc/kcore"},
		},
		{
			name: "non-OCI /sys submount is skipped",
			mounts: []types.MountInfo{
				{MountPoint: "/sys/firmware", IsOCIManaged: false},
			},
			wantSkipped:  []string{"/sys/firmware"},
			wantNotInExt: []string{"/sys/firmware"},
		},
		{
			name: "non-OCI /run submount is skipped",
			mounts: []types.MountInfo{
				{MountPoint: "/run/some-daemon", IsOCIManaged: false},
			},
			wantSkipped:  []string{"/run/some-daemon"},
			wantNotInExt: []string{"/run/some-daemon"},
		},
		{
			name: "OCI-managed /proc submount is externalized, not skipped",
			mounts: []types.MountInfo{
				{MountPoint: "/proc/acpi", IsOCIManaged: true},
			},
			wantExt: map[string]string{"/proc/acpi": "/proc/acpi"},
		},
		{
			name: "/dev/shm tmpfs is not externalized",
			mounts: []types.MountInfo{
				{MountPoint: "/dev/shm", FSType: "tmpfs"},
			},
			wantNotInExt: []string{"/dev/shm"},
		},
		{
			name: "/dev/shm non-tmpfs is externalized",
			mounts: []types.MountInfo{
				{MountPoint: "/dev/shm", FSType: "bind"},
			},
			wantExt: map[string]string{"/dev/shm": "/dev/shm"},
		},
		{
			name: "normal mount is externalized",
			mounts: []types.MountInfo{
				{MountPoint: "/etc/hostname", IsOCIManaged: true},
			},
			wantExt: map[string]string{"/etc/hostname": "/etc/hostname"},
		},
		{
			name: "empty mount point is ignored",
			mounts: []types.MountInfo{
				{MountPoint: ""},
			},
			wantExt: map[string]string{},
		},
		{
189
			name:   "masked path non-dir file maps to /dev/null",
190
191
192
			mounts: []types.MountInfo{},
			rootFS: func() string {
				dir := t.TempDir()
193
194
195
				if err := os.WriteFile(filepath.Join(dir, "proc"), []byte("x"), 0644); err != nil {
					t.Fatalf("write masked file: %v", err)
				}
196
197
198
199
200
201
				return dir
			}(),
			maskedPaths: []string{"/proc"},
			wantExt:     map[string]string{"/proc": "/dev/null"},
		},
		{
202
			name:   "masked path directory is ignored",
203
204
205
			mounts: []types.MountInfo{},
			rootFS: func() string {
				dir := t.TempDir()
206
207
208
				if err := os.MkdirAll(filepath.Join(dir, "proc"), 0755); err != nil {
					t.Fatalf("mkdir masked dir: %v", err)
				}
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
				return dir
			}(),
			maskedPaths:  []string{"/proc"},
			wantNotInExt: []string{"/proc"},
		},
		{
			name:         "masked path that doesn't exist is ignored",
			mounts:       []types.MountInfo{},
			rootFS:       t.TempDir(),
			maskedPaths:  []string{"/nonexistent"},
			wantNotInExt: []string{"/nonexistent"},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			extMap, skipped := BuildMountPolicy(tc.mounts, tc.rootFS, tc.maskedPaths)

			for k, v := range tc.wantExt {
				got, ok := extMap[k]
				if !ok {
					t.Errorf("expected extMap[%q] to exist", k)
					continue
				}
				if got != v {
					t.Errorf("extMap[%q] = %q, want %q", k, got, v)
				}
			}
			for _, k := range tc.wantNotInExt {
				if _, ok := extMap[k]; ok {
					t.Errorf("extMap should not contain %q", k)
				}
			}
			if tc.wantSkipped != nil {
				skippedSet := make(map[string]struct{}, len(skipped))
				for _, s := range skipped {
					skippedSet[s] = struct{}{}
				}
				for _, want := range tc.wantSkipped {
					if _, ok := skippedSet[want]; !ok {
						t.Errorf("expected %q in skipped list, got %v", want, skipped)
					}
				}
			}
		})
	}
}

func TestNormalizeOCIPath(t *testing.T) {
	tests := []struct {
		name   string
		raw    string
		rootFS string
		want   string
	}{
		{name: "normal absolute path", raw: "/etc/hostname", want: "/etc/hostname"},
		{name: "empty string", raw: "", want: ""},
		{name: "whitespace only", raw: "   ", want: ""},
		{name: "dot path", raw: ".", want: ""},
		{name: "path with trailing slashes cleaned", raw: "/etc/hostname///", want: "/etc/hostname"},
		{
			name: "with rootFS strips prefix via securejoin",
			raw:  "/etc/hostname",
			// SecureJoin(rootFS, "/etc/hostname") → rootFS+"/etc/hostname", then strip rootFS prefix
			rootFS: "/tmp/fakefs",
			want:   "/etc/hostname",
		},
		{
			name:   "root path with rootFS returns /",
			raw:    "/",
			rootFS: "/tmp/fakefs",
			want:   "/",
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got := normalizeOCIPath(tc.raw, tc.rootFS)
			if got != tc.want {
				t.Errorf("normalizeOCIPath(%q, %q) = %q, want %q", tc.raw, tc.rootFS, got, tc.want)
			}
		})
	}
}