github.com/google/cadvisor@v0.49.1/fs/fs_test.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fs 16 17 import ( 18 "errors" 19 "os" 20 "reflect" 21 "testing" 22 23 mount "github.com/moby/sys/mountinfo" 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 ) 27 28 func TestMountInfoFromDir(t *testing.T) { 29 as := assert.New(t) 30 fsInfo := &RealFsInfo{ 31 mounts: map[string]mount.Info{ 32 "/": {}, 33 }, 34 } 35 testDirs := []string{"/var/lib/kubelet", "/var/lib/rancher"} 36 for _, testDir := range testDirs { 37 _, found := fsInfo.mountInfoFromDir(testDir) 38 as.True(found, "failed to find MountInfo %s from FsInfo %s", testDir, fsInfo) 39 } 40 } 41 42 func TestGetDiskStatsMap(t *testing.T) { 43 diskStatsMap, err := getDiskStatsMap("test_resources/diskstats") 44 if err != nil { 45 t.Errorf("Error calling getDiskStatsMap %s", err) 46 } 47 if len(diskStatsMap) != 30 { 48 t.Errorf("diskStatsMap %+v not valid", diskStatsMap) 49 } 50 keySet := map[string]string{ 51 "/dev/sda": "/dev/sda", 52 "/dev/sdb": "/dev/sdb", 53 "/dev/sdc": "/dev/sdc", 54 "/dev/sdd": "/dev/sdd", 55 "/dev/sde": "/dev/sde", 56 "/dev/sdf": "/dev/sdf", 57 "/dev/sdg": "/dev/sdg", 58 "/dev/sdh": "/dev/sdh", 59 "/dev/sdb1": "/dev/sdb1", 60 "/dev/sdb2": "/dev/sdb2", 61 "/dev/sda1": "/dev/sda1", 62 "/dev/sda2": "/dev/sda2", 63 "/dev/sdc1": "/dev/sdc1", 64 "/dev/sdc2": "/dev/sdc2", 65 "/dev/sdc3": "/dev/sdc3", 66 "/dev/sdc4": "/dev/sdc4", 67 "/dev/sdd1": "/dev/sdd1", 68 "/dev/sdd2": "/dev/sdd2", 69 "/dev/sdd3": "/dev/sdd3", 70 "/dev/sdd4": "/dev/sdd4", 71 "/dev/sde1": "/dev/sde1", 72 "/dev/sde2": "/dev/sde2", 73 "/dev/sdf1": "/dev/sdf1", 74 "/dev/sdf2": "/dev/sdf2", 75 "/dev/sdg1": "/dev/sdg1", 76 "/dev/sdg2": "/dev/sdg2", 77 "/dev/sdh1": "/dev/sdh1", 78 "/dev/sdh2": "/dev/sdh2", 79 "/dev/dm-0": "/dev/dm-0", 80 "/dev/dm-1": "/dev/dm-1", 81 } 82 83 for device := range diskStatsMap { 84 if _, ok := keySet[device]; !ok { 85 t.Errorf("Cannot find device %s", device) 86 } 87 delete(keySet, device) 88 } 89 if len(keySet) != 0 { 90 t.Errorf("diskStatsMap %+v contains illegal keys %+v", diskStatsMap, keySet) 91 } 92 } 93 94 func TestGetDiskStatsMapMajorMinorNum(t *testing.T) { 95 diskStatsMap, err := getDiskStatsMap("test_resources/diskstats") 96 if err != nil { 97 t.Errorf("Error calling getDiskStatsMap %s", err) 98 } 99 if len(diskStatsMap) != 30 { 100 t.Errorf("diskStatsMap %+v not valid", diskStatsMap) 101 } 102 103 if stat, ok := diskStatsMap["/dev/dm-0"]; ok { 104 if stat.MajorNum != 252 && stat.MinorNum != 1 { 105 t.Fatalf("getDiskStatsMap did not return correct major (%d) and minor (%d) numbers", stat.MajorNum, stat.MinorNum) 106 } 107 } 108 } 109 110 func TestFileNotExist(t *testing.T) { 111 _, err := getDiskStatsMap("/file_does_not_exist") 112 if err != nil { 113 t.Fatalf("getDiskStatsMap must not error for absent file: %s", err) 114 } 115 } 116 117 func TestDirDiskUsage(t *testing.T) { 118 as := assert.New(t) 119 fsInfo, err := NewFsInfo(Context{}) 120 as.NoError(err) 121 dir := t.TempDir() 122 as.NoError(err) 123 defer os.RemoveAll(dir) 124 dataSize := 1024 * 100 //100 KB 125 b := make([]byte, dataSize) 126 f, err := os.CreateTemp(dir, "") 127 as.NoError(err) 128 as.NoError(os.WriteFile(f.Name(), b, 0700)) 129 fi, err := f.Stat() 130 as.NoError(err) 131 expectedSize := uint64(fi.Size()) 132 usage, err := fsInfo.GetDirUsage(dir) 133 as.NoError(err) 134 as.True(expectedSize <= usage.Bytes, "expected dir size to be at-least %d; got size: %d", expectedSize, usage.Bytes) 135 } 136 137 func TestDirInodeUsage(t *testing.T) { 138 as := assert.New(t) 139 fsInfo, err := NewFsInfo(Context{}) 140 as.NoError(err) 141 dir := t.TempDir() 142 defer os.RemoveAll(dir) 143 numFiles := 1000 144 for i := 0; i < numFiles; i++ { 145 _, err := os.MkdirTemp(dir, "") 146 require.NoError(t, err) 147 } 148 usage, err := fsInfo.GetDirUsage(dir) 149 as.NoError(err) 150 // We sould get numFiles+1 inodes, since we get 1 inode for each file, plus 1 for the directory 151 as.True(uint64(numFiles+1) == usage.Inodes, "expected inodes in dir to be %d; got inodes: %d", numFiles+1, usage.Inodes) 152 } 153 154 var dmStatusTests = []struct { 155 dmStatus string 156 used uint64 157 total uint64 158 errExpected bool 159 }{ 160 {`0 409534464 thin-pool 64085 3705/4161600 88106/3199488 - rw no_discard_passdown queue_if_no_space -`, 88106, 3199488, false}, 161 {`0 209715200 thin-pool 707 1215/524288 30282/1638400 - rw discard_passdown`, 30282, 1638400, false}, 162 {`Invalid status line`, 0, 0, false}, 163 } 164 165 func TestParseDMStatus(t *testing.T) { 166 for _, tt := range dmStatusTests { 167 used, total, err := parseDMStatus(tt.dmStatus) 168 if tt.errExpected && err != nil { 169 t.Errorf("parseDMStatus(%q) expected error", tt.dmStatus) 170 } 171 if used != tt.used { 172 t.Errorf("parseDMStatus(%q) wrong used value => %q, want %q", tt.dmStatus, used, tt.used) 173 } 174 if total != tt.total { 175 t.Errorf("parseDMStatus(%q) wrong total value => %q, want %q", tt.dmStatus, total, tt.total) 176 } 177 } 178 } 179 180 var dmTableTests = []struct { 181 dmTable string 182 major uint 183 minor uint 184 dataBlkSize uint 185 errExpected bool 186 }{ 187 {`0 409534464 thin-pool 253:6 253:7 128 32768 1 skip_block_zeroing`, 253, 7, 128, false}, 188 {`0 409534464 thin-pool 253:6 258:9 512 32768 1 skip_block_zeroing otherstuff`, 258, 9, 512, false}, 189 {`Invalid status line`, 0, 0, 0, false}, 190 } 191 192 func TestParseDMTable(t *testing.T) { 193 for _, tt := range dmTableTests { 194 major, minor, dataBlkSize, err := parseDMTable(tt.dmTable) 195 if tt.errExpected && err != nil { 196 t.Errorf("parseDMTable(%q) expected error", tt.dmTable) 197 } 198 if major != tt.major { 199 t.Errorf("parseDMTable(%q) wrong major value => %q, want %q", tt.dmTable, major, tt.major) 200 } 201 if minor != tt.minor { 202 t.Errorf("parseDMTable(%q) wrong minor value => %q, want %q", tt.dmTable, minor, tt.minor) 203 } 204 if dataBlkSize != tt.dataBlkSize { 205 t.Errorf("parseDMTable(%q) wrong dataBlkSize value => %q, want %q", tt.dmTable, dataBlkSize, tt.dataBlkSize) 206 } 207 } 208 } 209 210 func TestAddSystemRootLabel(t *testing.T) { 211 tests := []struct { 212 mounts []*mount.Info 213 expected string 214 }{ 215 { 216 mounts: []*mount.Info{ 217 {Source: "/dev/sda1", Mountpoint: "/foo"}, 218 {Source: "/dev/sdb1", Mountpoint: "/"}, 219 }, 220 expected: "/dev/sdb1", 221 }, 222 } 223 224 for i, tt := range tests { 225 fsInfo := &RealFsInfo{ 226 labels: map[string]string{}, 227 partitions: map[string]partition{}, 228 } 229 fsInfo.addSystemRootLabel(tt.mounts) 230 231 if source, ok := fsInfo.labels[LabelSystemRoot]; !ok || source != tt.expected { 232 t.Errorf("case %d: expected mount source '%s', got '%s'", i, tt.expected, source) 233 } 234 } 235 } 236 237 type testDmsetup struct { 238 data []byte 239 err error 240 } 241 242 func (*testDmsetup) Message(deviceName string, sector int, message string) ([]byte, error) { 243 return nil, nil 244 } 245 246 func (*testDmsetup) Status(deviceName string) ([]byte, error) { 247 return nil, nil 248 } 249 250 func (t *testDmsetup) Table(poolName string) ([]byte, error) { 251 return t.data, t.err 252 } 253 254 func TestGetDockerDeviceMapperInfo(t *testing.T) { 255 tests := []struct { 256 name string 257 driver string 258 driverStatus map[string]string 259 dmsetupTable string 260 dmsetupTableError error 261 expectedDevice string 262 expectedPartition *partition 263 expectedError bool 264 }{ 265 { 266 name: "not devicemapper", 267 driver: "btrfs", 268 expectedDevice: "", 269 expectedPartition: nil, 270 expectedError: false, 271 }, 272 { 273 name: "nil driver status", 274 driver: "devicemapper", 275 driverStatus: nil, 276 expectedDevice: "", 277 expectedPartition: nil, 278 expectedError: true, 279 }, 280 { 281 name: "loopback", 282 driver: "devicemapper", 283 driverStatus: map[string]string{"Data loop file": "/var/lib/docker/devicemapper/devicemapper/data"}, 284 expectedDevice: "", 285 expectedPartition: nil, 286 expectedError: false, 287 }, 288 { 289 name: "missing pool name", 290 driver: "devicemapper", 291 driverStatus: map[string]string{}, 292 expectedDevice: "", 293 expectedPartition: nil, 294 expectedError: true, 295 }, 296 { 297 name: "error invoking dmsetup", 298 driver: "devicemapper", 299 driverStatus: map[string]string{"Pool Name": "vg_vagrant-docker--pool"}, 300 dmsetupTableError: errors.New("foo"), 301 expectedDevice: "", 302 expectedPartition: nil, 303 expectedError: true, 304 }, 305 { 306 name: "unable to parse dmsetup table", 307 driver: "devicemapper", 308 driverStatus: map[string]string{"Pool Name": "vg_vagrant-docker--pool"}, 309 dmsetupTable: "no data here!", 310 expectedDevice: "", 311 expectedPartition: nil, 312 expectedError: true, 313 }, 314 { 315 name: "happy path", 316 driver: "devicemapper", 317 driverStatus: map[string]string{"Pool Name": "vg_vagrant-docker--pool"}, 318 dmsetupTable: "0 53870592 thin-pool 253:2 253:3 1024 0 1 skip_block_zeroing", 319 expectedDevice: "vg_vagrant-docker--pool", 320 expectedPartition: &partition{ 321 fsType: "devicemapper", 322 major: 253, 323 minor: 3, 324 blockSize: 1024, 325 }, 326 expectedError: false, 327 }, 328 } 329 330 for _, tt := range tests { 331 fsInfo := &RealFsInfo{ 332 dmsetup: &testDmsetup{ 333 data: []byte(tt.dmsetupTable), 334 }, 335 } 336 337 dockerCtx := DockerContext{ 338 Driver: tt.driver, 339 DriverStatus: tt.driverStatus, 340 } 341 342 device, partition, err := fsInfo.getDockerDeviceMapperInfo(dockerCtx) 343 344 if tt.expectedError && err == nil { 345 t.Errorf("%s: expected error but got nil", tt.name) 346 continue 347 } 348 if !tt.expectedError && err != nil { 349 t.Errorf("%s: unexpected error: %v", tt.name, err) 350 continue 351 } 352 353 if e, a := tt.expectedDevice, device; e != a { 354 t.Errorf("%s: device: expected %q, got %q", tt.name, e, a) 355 } 356 357 if e, a := tt.expectedPartition, partition; !reflect.DeepEqual(e, a) { 358 t.Errorf("%s: partition: expected %#v, got %#v", tt.name, e, a) 359 } 360 } 361 } 362 363 func TestAddDockerImagesLabel(t *testing.T) { 364 tests := []struct { 365 name string 366 driver string 367 driverStatus map[string]string 368 dmsetupTable string 369 getDockerDeviceMapperInfoError error 370 mounts []*mount.Info 371 expectedDockerDevice string 372 expectedPartition *partition 373 }{ 374 { 375 name: "single partition, no dedicated image fs", 376 driver: "overlay2", 377 mounts: []*mount.Info{ 378 { 379 Source: "/dev/root", 380 Mountpoint: "/", 381 FSType: "ext4", 382 }, 383 { 384 Source: "/sys/fs/cgroup", 385 Mountpoint: "/sys/fs/cgroup", 386 FSType: "tmpfs", 387 }, 388 }, 389 expectedDockerDevice: "/dev/root", 390 }, 391 { 392 name: "devicemapper, not loopback", 393 driver: "devicemapper", 394 driverStatus: map[string]string{"Pool Name": "vg_vagrant-docker--pool"}, 395 dmsetupTable: "0 53870592 thin-pool 253:2 253:3 1024 0 1 skip_block_zeroing", 396 mounts: []*mount.Info{ 397 { 398 Source: "/dev/mapper/vg_vagrant-lv_root", 399 Mountpoint: "/", 400 FSType: "devicemapper", 401 }, 402 }, 403 expectedDockerDevice: "vg_vagrant-docker--pool", 404 expectedPartition: &partition{ 405 fsType: "devicemapper", 406 major: 253, 407 minor: 3, 408 blockSize: 1024, 409 }, 410 }, 411 { 412 name: "devicemapper, loopback on non-root partition", 413 driver: "devicemapper", 414 driverStatus: map[string]string{"Data loop file": "/var/lib/docker/devicemapper/devicemapper/data"}, 415 mounts: []*mount.Info{ 416 { 417 Source: "/dev/mapper/vg_vagrant-lv_root", 418 Mountpoint: "/", 419 FSType: "devicemapper", 420 }, 421 { 422 Source: "/dev/sdb1", 423 Mountpoint: "/var/lib/docker/devicemapper", 424 }, 425 }, 426 expectedDockerDevice: "/dev/sdb1", 427 }, 428 { 429 name: "multiple mounts - innermost check", 430 driver: "overlay2", 431 mounts: []*mount.Info{ 432 { 433 Source: "/dev/sda1", 434 Mountpoint: "/", 435 FSType: "ext4", 436 }, 437 { 438 Source: "/dev/sdb1", 439 Mountpoint: "/var/lib/docker", 440 FSType: "ext4", 441 }, 442 { 443 Source: "/dev/sdb2", 444 Mountpoint: "/var/lib/docker/btrfs", 445 FSType: "btrfs", 446 }, 447 }, 448 expectedDockerDevice: "/dev/sdb2", 449 }, 450 { 451 name: "root fs inside container, docker-images bindmount", 452 driver: "overlay2", 453 mounts: []*mount.Info{ 454 { 455 Source: "overlay", 456 Mountpoint: "/", 457 FSType: "overlay", 458 }, 459 { 460 Source: "/dev/sda1", 461 Mountpoint: "/var/lib/docker", 462 FSType: "ext4", 463 }, 464 }, 465 expectedDockerDevice: "/dev/sda1", 466 }, 467 { 468 name: "[overlay2] root fs inside container - /var/lib/docker bindmount", 469 driver: "overlay2", 470 mounts: []*mount.Info{ 471 { 472 Source: "overlay", 473 Mountpoint: "/", 474 FSType: "overlay", 475 }, 476 { 477 Source: "/dev/sdb1", 478 Mountpoint: "/var/lib/docker", 479 FSType: "ext4", 480 }, 481 { 482 Source: "/dev/sdb2", 483 Mountpoint: "/var/lib/docker/overlay2", 484 FSType: "ext4", 485 }, 486 }, 487 expectedDockerDevice: "/dev/sdb2", 488 }, 489 } 490 491 for _, tt := range tests { 492 fsInfo := &RealFsInfo{ 493 labels: map[string]string{}, 494 partitions: map[string]partition{}, 495 dmsetup: &testDmsetup{ 496 data: []byte(tt.dmsetupTable), 497 }, 498 } 499 500 context := Context{ 501 Docker: DockerContext{ 502 Root: "/var/lib/docker", 503 Driver: tt.driver, 504 DriverStatus: tt.driverStatus, 505 }, 506 } 507 508 fsInfo.addDockerImagesLabel(context, tt.mounts) 509 510 if e, a := tt.expectedDockerDevice, fsInfo.labels[LabelDockerImages]; e != a { 511 t.Errorf("%s: docker device: expected %q, got %q", tt.name, e, a) 512 } 513 514 if tt.expectedPartition == nil { 515 continue 516 } 517 if e, a := *tt.expectedPartition, fsInfo.partitions[tt.expectedDockerDevice]; !reflect.DeepEqual(e, a) { 518 t.Errorf("%s: docker partition: expected %#v, got %#v", tt.name, e, a) 519 } 520 } 521 } 522 523 func TestAddCrioImagesLabel(t *testing.T) { 524 tests := []struct { 525 name string 526 driver string 527 driverStatus map[string]string 528 dmsetupTable string 529 mounts []*mount.Info 530 imageStore string 531 expectedCrioImages string 532 expectedCrioContainers string 533 expectedPartition *partition 534 }{ 535 { 536 name: "single partition, no dedicated image fs", 537 driver: "overlay2", 538 mounts: []*mount.Info{ 539 { 540 Source: "/dev/root", 541 Mountpoint: "/", 542 FSType: "ext4", 543 }, 544 { 545 Source: "/sys/fs/cgroup", 546 Mountpoint: "/sys/fs/cgroup", 547 FSType: "tmpfs", 548 }, 549 }, 550 expectedCrioImages: "/dev/root", 551 expectedCrioContainers: "", 552 }, 553 { 554 name: "root fs inside container, docker-images bindmount", 555 driver: "overlay2", 556 mounts: []*mount.Info{ 557 { 558 Source: "overlay", 559 Mountpoint: "/", 560 FSType: "overlay", 561 }, 562 { 563 Source: "/dev/sda1", 564 Mountpoint: "/var/lib/container", 565 FSType: "ext4", 566 }, 567 }, 568 expectedCrioImages: "/dev/sda1", 569 }, 570 { 571 name: "[overlay2] image and container separate", 572 driver: "overlay2", 573 mounts: []*mount.Info{ 574 { 575 Source: "/dev/sdb1", 576 Mountpoint: "/imagestore", 577 FSType: "ext4", 578 }, 579 { 580 Source: "/dev/sdb2", 581 Mountpoint: "/var/lib/container", 582 FSType: "ext4", 583 }, 584 }, 585 expectedCrioImages: "/dev/sdb1", 586 expectedCrioContainers: "/dev/sdb2", 587 imageStore: "/imagestore", 588 }, 589 } 590 591 for _, tt := range tests { 592 fsInfo := &RealFsInfo{ 593 labels: map[string]string{}, 594 partitions: map[string]partition{}, 595 dmsetup: &testDmsetup{ 596 data: []byte(tt.dmsetupTable), 597 }, 598 } 599 600 context := Context{ 601 Crio: CrioContext{ 602 Root: "/var/lib/container", 603 ImageStore: tt.imageStore, 604 Driver: "overlay", 605 }, 606 } 607 608 fsInfo.addCrioImagesLabel(context, tt.mounts) 609 610 if tt.imageStore != "" { 611 if e, a := tt.expectedCrioImages, fsInfo.labels[LabelCrioImages]; e != a { 612 t.Errorf("%s: docker device: expected %q, got %q", tt.name, e, a) 613 } 614 615 if e, a := tt.expectedCrioContainers, fsInfo.labels[LabelCrioContainers]; e != a { 616 t.Errorf("%s: docker device: expected %q, got %q", tt.name, e, a) 617 } 618 619 } 620 if tt.imageStore == "" { 621 if e, a := tt.expectedCrioImages, fsInfo.labels[LabelCrioImages]; e != a { 622 t.Errorf("%s: docker device: expected %q, got %q", tt.name, e, a) 623 } 624 625 } 626 627 if tt.expectedPartition == nil { 628 continue 629 } 630 } 631 } 632 633 func TestProcessMounts(t *testing.T) { 634 tests := []struct { 635 name string 636 mounts []*mount.Info 637 excludedPrefixes []string 638 expected map[string]partition 639 }{ 640 { 641 name: "unsupported fs types", 642 mounts: []*mount.Info{ 643 {FSType: "somethingelse"}, 644 }, 645 expected: map[string]partition{}, 646 }, 647 { 648 name: "avoid bind mounts", 649 mounts: []*mount.Info{ 650 {Root: "/", Mountpoint: "/", Source: "/dev/sda1", FSType: "xfs", Major: 253, Minor: 0}, 651 {Root: "/foo", Mountpoint: "/bar", Source: "/dev/sda1", FSType: "xfs", Major: 253, Minor: 0}, 652 }, 653 expected: map[string]partition{ 654 "/dev/sda1": {fsType: "xfs", mountpoint: "/", major: 253, minor: 0}, 655 }, 656 }, 657 { 658 name: "exclude prefixes", 659 mounts: []*mount.Info{ 660 {Root: "/", Mountpoint: "/someother", Source: "/dev/sda1", FSType: "xfs", Major: 253, Minor: 2}, 661 {Root: "/", Mountpoint: "/", Source: "/dev/sda2", FSType: "xfs", Major: 253, Minor: 0}, 662 {Root: "/", Mountpoint: "/excludeme", Source: "/dev/sda3", FSType: "xfs", Major: 253, Minor: 1}, 663 }, 664 excludedPrefixes: []string{"/exclude", "/some"}, 665 expected: map[string]partition{ 666 "/dev/sda2": {fsType: "xfs", mountpoint: "/", major: 253, minor: 0}, 667 }, 668 }, 669 { 670 name: "supported fs types", 671 mounts: []*mount.Info{ 672 {Root: "/", Mountpoint: "/a", Source: "/dev/sda", FSType: "ext3", Major: 253, Minor: 0}, 673 {Root: "/", Mountpoint: "/b", Source: "/dev/sdb", FSType: "ext4", Major: 253, Minor: 1}, 674 {Root: "/", Mountpoint: "/c", Source: "/dev/sdc", FSType: "btrfs", Major: 253, Minor: 2}, 675 {Root: "/", Mountpoint: "/d", Source: "/dev/sdd", FSType: "xfs", Major: 253, Minor: 3}, 676 {Root: "/", Mountpoint: "/e", Source: "/dev/sde", FSType: "zfs", Major: 253, Minor: 4}, 677 {Root: "/", Mountpoint: "/f", Source: "overlay", FSType: "overlay", Major: 253, Minor: 5}, 678 {Root: "/", Mountpoint: "/g", Source: "127.0.0.1:/nfs", FSType: "nfs4", Major: 253, Minor: 6}, 679 {Root: "/", Mountpoint: "/test1", Source: "tmpfs", FSType: "tmpfs", Major: 253, Minor: 4}, 680 {Root: "/", Mountpoint: "/test2", Source: "tmpfs", FSType: "tmpfs", Major: 253, Minor: 4}, 681 }, 682 expected: map[string]partition{ 683 "/dev/sda": {fsType: "ext3", mountpoint: "/a", major: 253, minor: 0}, 684 "/dev/sdb": {fsType: "ext4", mountpoint: "/b", major: 253, minor: 1}, 685 "/dev/sdc": {fsType: "btrfs", mountpoint: "/c", major: 253, minor: 2}, 686 "/dev/sdd": {fsType: "xfs", mountpoint: "/d", major: 253, minor: 3}, 687 "/dev/sde": {fsType: "zfs", mountpoint: "/e", major: 253, minor: 4}, 688 "overlay_253-5": {fsType: "overlay", mountpoint: "/f", major: 253, minor: 5}, 689 "127.0.0.1:/nfs": {fsType: "nfs4", mountpoint: "/g", major: 253, minor: 6}, 690 "/test1": {fsType: "tmpfs", mountpoint: "/test1", major: 253, minor: 4}, 691 "/test2": {fsType: "tmpfs", mountpoint: "/test2", major: 253, minor: 4}, 692 }, 693 }, 694 } 695 696 for _, test := range tests { 697 actual := processMounts(test.mounts, test.excludedPrefixes) 698 if !reflect.DeepEqual(test.expected, actual) { 699 t.Errorf("%s: expected %#v, got %#v", test.name, test.expected, actual) 700 } 701 } 702 }