1// +build linux 2 3/* 4Copyright 2014 The Kubernetes Authors. 5 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17*/ 18 19package mount 20 21import ( 22 "io/ioutil" 23 "os" 24 "reflect" 25 "strings" 26 "testing" 27) 28 29func TestReadProcMountsFrom(t *testing.T) { 30 successCase := 31 `/dev/0 /path/to/0 type0 flags 0 0 32/dev/1 /path/to/1 type1 flags 1 1 33/dev/2 /path/to/2 type2 flags,1,2=3 2 2 34` 35 // NOTE: readProcMountsFrom has been updated to using fnv.New32a() 36 mounts, err := parseProcMounts([]byte(successCase)) 37 if err != nil { 38 t.Errorf("expected success, got %v", err) 39 } 40 if len(mounts) != 3 { 41 t.Fatalf("expected 3 mounts, got %d", len(mounts)) 42 } 43 mp := MountPoint{"/dev/0", "/path/to/0", "type0", []string{"flags"}, 0, 0} 44 if !mountPointsEqual(&mounts[0], &mp) { 45 t.Errorf("got unexpected MountPoint[0]: %#v", mounts[0]) 46 } 47 mp = MountPoint{"/dev/1", "/path/to/1", "type1", []string{"flags"}, 1, 1} 48 if !mountPointsEqual(&mounts[1], &mp) { 49 t.Errorf("got unexpected MountPoint[1]: %#v", mounts[1]) 50 } 51 mp = MountPoint{"/dev/2", "/path/to/2", "type2", []string{"flags", "1", "2=3"}, 2, 2} 52 if !mountPointsEqual(&mounts[2], &mp) { 53 t.Errorf("got unexpected MountPoint[2]: %#v", mounts[2]) 54 } 55 56 errorCases := []string{ 57 "/dev/0 /path/to/mount\n", 58 "/dev/1 /path/to/mount type flags a 0\n", 59 "/dev/2 /path/to/mount type flags 0 b\n", 60 } 61 for _, ec := range errorCases { 62 _, err := parseProcMounts([]byte(ec)) 63 if err == nil { 64 t.Errorf("expected error") 65 } 66 } 67} 68 69func mountPointsEqual(a, b *MountPoint) bool { 70 if a.Device != b.Device || a.Path != b.Path || a.Type != b.Type || !reflect.DeepEqual(a.Opts, b.Opts) || a.Pass != b.Pass || a.Freq != b.Freq { 71 return false 72 } 73 return true 74} 75 76func TestGetMountRefs(t *testing.T) { 77 fm := NewFakeMounter( 78 []MountPoint{ 79 {Device: "/dev/sdb", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd"}, 80 {Device: "/dev/sdb", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod"}, 81 {Device: "/dev/sdc", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2"}, 82 {Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1"}, 83 {Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2"}, 84 }) 85 86 tests := []struct { 87 mountPath string 88 expectedRefs []string 89 }{ 90 { 91 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod", 92 []string{ 93 "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd", 94 }, 95 }, 96 { 97 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1", 98 []string{ 99 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2", 100 "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2", 101 }, 102 }, 103 { 104 "/var/fake/directory/that/doesnt/exist", 105 []string{}, 106 }, 107 } 108 109 for i, test := range tests { 110 if refs, err := fm.GetMountRefs(test.mountPath); err != nil || !setEquivalent(test.expectedRefs, refs) { 111 t.Errorf("%d. getMountRefs(%q) = %v, %v; expected %v, nil", i, test.mountPath, refs, err, test.expectedRefs) 112 } 113 } 114} 115 116func setEquivalent(set1, set2 []string) bool { 117 map1 := make(map[string]bool) 118 map2 := make(map[string]bool) 119 for _, s := range set1 { 120 map1[s] = true 121 } 122 for _, s := range set2 { 123 map2[s] = true 124 } 125 126 for s := range map1 { 127 if !map2[s] { 128 return false 129 } 130 } 131 for s := range map2 { 132 if !map1[s] { 133 return false 134 } 135 } 136 return true 137} 138 139func TestGetDeviceNameFromMount(t *testing.T) { 140 fm := NewFakeMounter( 141 []MountPoint{ 142 {Device: "/dev/disk/by-path/prefix-lun-1", 143 Path: "/mnt/111"}, 144 {Device: "/dev/disk/by-path/prefix-lun-1", 145 Path: "/mnt/222"}, 146 }) 147 148 tests := []struct { 149 mountPath string 150 expectedDevice string 151 expectedRefs int 152 }{ 153 { 154 "/mnt/222", 155 "/dev/disk/by-path/prefix-lun-1", 156 2, 157 }, 158 } 159 160 for i, test := range tests { 161 if device, refs, err := GetDeviceNameFromMount(fm, test.mountPath); err != nil || test.expectedRefs != refs || test.expectedDevice != device { 162 t.Errorf("%d. GetDeviceNameFromMount(%s) = (%s, %d), %v; expected (%s,%d), nil", i, test.mountPath, device, refs, err, test.expectedDevice, test.expectedRefs) 163 } 164 } 165} 166 167func TestGetMountRefsByDev(t *testing.T) { 168 fm := NewFakeMounter( 169 []MountPoint{ 170 {Device: "/dev/sdb", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd"}, 171 {Device: "/dev/sdb", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod"}, 172 {Device: "/dev/sdc", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2"}, 173 {Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1"}, 174 {Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2"}, 175 }) 176 177 tests := []struct { 178 mountPath string 179 expectedRefs []string 180 }{ 181 { 182 "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd", 183 []string{ 184 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod", 185 }, 186 }, 187 { 188 "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2", 189 []string{ 190 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1", 191 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2", 192 }, 193 }, 194 } 195 196 for i, test := range tests { 197 198 if refs, err := getMountRefsByDev(fm, test.mountPath); err != nil || !setEquivalent(test.expectedRefs, refs) { 199 t.Errorf("%d. getMountRefsByDev(%q) = %v, %v; expected %v, nil", i, test.mountPath, refs, err, test.expectedRefs) 200 } 201 } 202} 203 204func TestPathWithinBase(t *testing.T) { 205 tests := []struct { 206 name string 207 fullPath string 208 basePath string 209 expected bool 210 }{ 211 { 212 name: "good subpath", 213 fullPath: "/a/b/c", 214 basePath: "/a", 215 expected: true, 216 }, 217 { 218 name: "good subpath 2", 219 fullPath: "/a/b/c", 220 basePath: "/a/b", 221 expected: true, 222 }, 223 { 224 name: "good subpath end slash", 225 fullPath: "/a/b/c/", 226 basePath: "/a/b", 227 expected: true, 228 }, 229 { 230 name: "good subpath backticks", 231 fullPath: "/a/b/../c", 232 basePath: "/a", 233 expected: true, 234 }, 235 { 236 name: "good subpath equal", 237 fullPath: "/a/b/c", 238 basePath: "/a/b/c", 239 expected: true, 240 }, 241 { 242 name: "good subpath equal 2", 243 fullPath: "/a/b/c/", 244 basePath: "/a/b/c", 245 expected: true, 246 }, 247 { 248 name: "good subpath root", 249 fullPath: "/a", 250 basePath: "/", 251 expected: true, 252 }, 253 { 254 name: "bad subpath parent", 255 fullPath: "/a/b/c", 256 basePath: "/a/b/c/d", 257 expected: false, 258 }, 259 { 260 name: "bad subpath outside", 261 fullPath: "/b/c", 262 basePath: "/a/b/c", 263 expected: false, 264 }, 265 { 266 name: "bad subpath prefix", 267 fullPath: "/a/b/cd", 268 basePath: "/a/b/c", 269 expected: false, 270 }, 271 { 272 name: "bad subpath backticks", 273 fullPath: "/a/../b", 274 basePath: "/a", 275 expected: false, 276 }, 277 { 278 name: "configmap subpath", 279 fullPath: "/var/lib/kubelet/pods/uuid/volumes/kubernetes.io~configmap/config/..timestamp/file.txt", 280 basePath: "/var/lib/kubelet/pods/uuid/volumes/kubernetes.io~configmap/config", 281 expected: true, 282 }, 283 } 284 for _, test := range tests { 285 if PathWithinBase(test.fullPath, test.basePath) != test.expected { 286 t.Errorf("test %q failed: expected %v", test.name, test.expected) 287 } 288 289 } 290} 291 292func TestSearchMountPoints(t *testing.T) { 293 base := ` 29419 25 0:18 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw 29520 25 0:4 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw 29621 25 0:6 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=4058156k,nr_inodes=1014539,mode=755 29722 21 0:14 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000 29823 25 0:19 / /run rw,nosuid,noexec,relatime shared:5 - tmpfs tmpfs rw,size=815692k,mode=755 29925 0 252:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered 30026 19 0:12 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:8 - securityfs securityfs rw 30127 21 0:21 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw 30228 23 0:22 / /run/lock rw,nosuid,nodev,noexec,relatime shared:6 - tmpfs tmpfs rw,size=5120k 30329 19 0:23 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755 30430 29 0:24 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd 30531 19 0:25 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:11 - pstore pstore rw 30632 29 0:26 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,devices 30733 29 0:27 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,freezer 30834 29 0:28 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,pids 30935 29 0:29 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,blkio 31036 29 0:30 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,memory 31137 29 0:31 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,perf_event 31238 29 0:32 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb 31339 29 0:33 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,cpu,cpuacct 31440 29 0:34 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,cpuset 31541 29 0:35 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,net_cls,net_prio 31658 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordere 317` 318 319 testcases := []struct { 320 name string 321 source string 322 mountInfos string 323 expectedRefs []string 324 expectedErr error 325 }{ 326 { 327 "dir", 328 "/mnt/disks/vol1", 329 base, 330 nil, 331 nil, 332 }, 333 { 334 "dir-used", 335 "/mnt/disks/vol1", 336 base + ` 33756 25 252:0 /mnt/disks/vol1 /var/lib/kubelet/pods/1890aef5-5a60-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered 33857 25 0:45 / /mnt/disks/vol rw,relatime shared:36 - tmpfs tmpfs rw 339`, 340 []string{"/var/lib/kubelet/pods/1890aef5-5a60-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"}, 341 nil, 342 }, 343 { 344 "tmpfs-vol", 345 "/mnt/disks/vol1", 346 base + `120 25 0:76 / /mnt/disks/vol1 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k 347`, 348 nil, 349 nil, 350 }, 351 { 352 "tmpfs-vol-used-by-two-pods", 353 "/mnt/disks/vol1", 354 base + `120 25 0:76 / /mnt/disks/vol1 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k 355196 25 0:76 / /var/lib/kubelet/pods/ade3ac21-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k 356228 25 0:76 / /var/lib/kubelet/pods/ac60532d-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k 357`, 358 []string{ 359 "/var/lib/kubelet/pods/ade3ac21-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585", 360 "/var/lib/kubelet/pods/ac60532d-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585", 361 }, 362 nil, 363 }, 364 { 365 "tmpfs-subdir-used-indirectly-via-bindmount-dir-by-one-pod", 366 "/mnt/vol1/foo", 367 base + `177 25 0:46 / /mnt/data rw,relatime shared:37 - tmpfs data rw 368190 25 0:46 /vol1 /mnt/vol1 rw,relatime shared:37 - tmpfs data rw 369191 25 0:46 /vol2 /mnt/vol2 rw,relatime shared:37 - tmpfs data rw 37062 25 0:46 /vol1/foo /var/lib/kubelet/pods/e25f2f01-5b06-11e8-8694-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:37 - tmpfs data rw 371`, 372 []string{"/var/lib/kubelet/pods/e25f2f01-5b06-11e8-8694-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"}, 373 nil, 374 }, 375 { 376 "dir-bindmounted", 377 "/mnt/disks/vol2", 378 base + `342 25 252:0 /mnt/disks/vol2 /mnt/disks/vol2 rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered 379`, 380 nil, 381 nil, 382 }, 383 { 384 "dir-bindmounted-used-by-one-pod", 385 "/mnt/disks/vol2", 386 base + `342 25 252:0 /mnt/disks/vol2 /mnt/disks/vol2 rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered 38777 25 252:0 /mnt/disks/vol2 /var/lib/kubelet/pods/f30dc360-5a5d-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-1fb30a1c rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered 388`, 389 []string{"/var/lib/kubelet/pods/f30dc360-5a5d-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-1fb30a1c"}, 390 nil, 391 }, 392 { 393 "blockfs", 394 "/mnt/disks/blkvol1", 395 base + `58 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered 396`, 397 nil, 398 nil, 399 }, 400 { 401 "blockfs-used-by-one-pod", 402 "/mnt/disks/blkvol1", 403 base + `58 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered 40462 25 7:1 / /var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered 405`, 406 []string{"/var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"}, 407 nil, 408 }, 409 { 410 "blockfs-used-by-two-pods", 411 "/mnt/disks/blkvol1", 412 base + `58 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered 41362 25 7:1 / /var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered 41495 25 7:1 / /var/lib/kubelet/pods/4854a48b-5a64-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered 415`, 416 []string{"/var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test", 417 "/var/lib/kubelet/pods/4854a48b-5a64-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"}, 418 nil, 419 }, 420 } 421 tmpFile, err := ioutil.TempFile("", "test-get-filetype") 422 if err != nil { 423 t.Fatal(err) 424 } 425 defer os.Remove(tmpFile.Name()) 426 defer tmpFile.Close() 427 for _, v := range testcases { 428 tmpFile.Truncate(0) 429 tmpFile.Seek(0, 0) 430 tmpFile.WriteString(v.mountInfos) 431 tmpFile.Sync() 432 refs, err := SearchMountPoints(v.source, tmpFile.Name()) 433 if !reflect.DeepEqual(refs, v.expectedRefs) { 434 t.Errorf("test %q: expected Refs: %#v, got %#v", v.name, v.expectedRefs, refs) 435 } 436 if !reflect.DeepEqual(err, v.expectedErr) { 437 t.Errorf("test %q: expected err: %v, got %v", v.name, v.expectedErr, err) 438 } 439 } 440} 441 442func TestSensitiveMountOptions(t *testing.T) { 443 // Arrange 444 testcases := []struct { 445 source string 446 target string 447 fstype string 448 options []string 449 sensitiveOptions []string 450 mountFlags []string 451 }{ 452 { 453 454 source: "mySrc", 455 target: "myTarget", 456 fstype: "myFS", 457 options: []string{"o1", "o2"}, 458 sensitiveOptions: []string{"s1", "s2"}, 459 mountFlags: []string{}, 460 }, 461 { 462 463 source: "mySrc", 464 target: "myTarget", 465 fstype: "myFS", 466 options: []string{}, 467 sensitiveOptions: []string{"s1", "s2"}, 468 mountFlags: []string{}, 469 }, 470 { 471 472 source: "mySrc", 473 target: "myTarget", 474 fstype: "myFS", 475 options: []string{"o1", "o2"}, 476 sensitiveOptions: []string{}, 477 mountFlags: []string{}, 478 }, 479 { 480 481 source: "mySrc", 482 target: "myTarget", 483 fstype: "myFS", 484 options: []string{"o1", "o2"}, 485 sensitiveOptions: []string{"s1", "s2"}, 486 mountFlags: []string{"--no-canonicalize"}, 487 }, 488 } 489 490 for _, v := range testcases { 491 // Act 492 mountArgs, mountArgsLogStr := MakeMountArgsSensitiveWithMountFlags(v.source, v.target, v.fstype, v.options, v.sensitiveOptions, v.mountFlags) 493 494 // Assert 495 t.Logf("\r\nmountArgs =%q\r\nmountArgsLogStr=%q", mountArgs, mountArgsLogStr) 496 for _, mountFlag := range v.mountFlags { 497 if found := mountArgsContainString(t, mountArgs, mountFlag); !found { 498 t.Errorf("Expected mountFlag (%q) to exist in returned mountArgs (%q), but it does not", mountFlag, mountArgs) 499 } 500 if !strings.Contains(mountArgsLogStr, mountFlag) { 501 t.Errorf("Expected mountFlag (%q) to exist in returned mountArgsLogStr (%q), but it does", mountFlag, mountArgsLogStr) 502 } 503 } 504 for _, option := range v.options { 505 if found := mountArgsContainOption(t, mountArgs, option); !found { 506 t.Errorf("Expected option (%q) to exist in returned mountArgs (%q), but it does not", option, mountArgs) 507 } 508 if !strings.Contains(mountArgsLogStr, option) { 509 t.Errorf("Expected option (%q) to exist in returned mountArgsLogStr (%q), but it does", option, mountArgsLogStr) 510 } 511 } 512 for _, sensitiveOption := range v.sensitiveOptions { 513 if found := mountArgsContainOption(t, mountArgs, sensitiveOption); !found { 514 t.Errorf("Expected sensitiveOption (%q) to exist in returned mountArgs (%q), but it does not", sensitiveOption, mountArgs) 515 } 516 if strings.Contains(mountArgsLogStr, sensitiveOption) { 517 t.Errorf("Expected sensitiveOption (%q) to not exist in returned mountArgsLogStr (%q), but it does", sensitiveOption, mountArgsLogStr) 518 } 519 } 520 } 521} 522 523func mountArgsContainString(t *testing.T, mountArgs []string, wanted string) bool { 524 for _, mountArg := range mountArgs { 525 if mountArg == wanted { 526 return true 527 } 528 } 529 return false 530} 531 532func mountArgsContainOption(t *testing.T, mountArgs []string, option string) bool { 533 optionsIndex := -1 534 for i, s := range mountArgs { 535 if s == "-o" { 536 optionsIndex = i + 1 537 break 538 } 539 } 540 541 if optionsIndex < 0 || optionsIndex >= len(mountArgs) { 542 return false 543 } 544 545 return strings.Contains(mountArgs[optionsIndex], option) 546} 547