github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/mount/mountutil_test.go (about) 1 /* 2 Copyright 2019 OpenEBS Authors. 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 mount 18 19 import ( 20 "errors" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "strings" 25 "testing" 26 27 "github.com/stretchr/testify/assert" 28 ) 29 30 const ( 31 getMountName = iota 32 getPartitionName 33 ) 34 35 func TestNewMountUtil(t *testing.T) { 36 filePath := "/host/proc/1/mounts" 37 devPath := "/dev/sda" 38 mountPoint := "/home" 39 // TODO 40 expectedMountUtil1 := DiskMountUtil{ 41 filePath: filePath, 42 devPath: devPath, 43 } 44 expectedMountUtil2 := DiskMountUtil{ 45 filePath: filePath, 46 mountPoint: mountPoint, 47 } 48 49 tests := map[string]struct { 50 actualMU DiskMountUtil 51 expectedMU DiskMountUtil 52 }{ 53 "test for generated mount util with devPath": {NewMountUtil(filePath, devPath, ""), expectedMountUtil1}, 54 "test for generated mount util with mountpoint": {NewMountUtil(filePath, "", mountPoint), expectedMountUtil2}, 55 } 56 57 for name, test := range tests { 58 t.Run(name, func(t *testing.T) { 59 assert.Equal(t, test.expectedMU, test.actualMU) 60 }) 61 } 62 } 63 64 func TestGetMountAttr(t *testing.T) { 65 filePath := "/tmp/data" 66 fileContent1 := []byte("/dev/sda4 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0") 67 fileContent2 := []byte("/dev/sda3 /home ext4 rw,relatime,errors=remount-ro,data=ordered 0 0") 68 fileContent3 := []byte("sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0") 69 fileContent4 := []byte(`/dev/sda3 /home ext4 rw,relatime,errors=remount-ro,data=ordered 0 0 70 /dev/sda3 /usr ext4 rw,relatime,errors=remount-ro,data=ordered 0 0`) 71 72 mountAttrTests := map[string]struct { 73 devPath string 74 mountPoint string 75 attrFunc int 76 expectedMountAttr DeviceMountAttr 77 expectedError error 78 fileContent []byte 79 }{ 80 "sda4 mounted at /": { 81 "/dev/sda4", 82 "", 83 getMountName, 84 DeviceMountAttr{MountPoint: []string{"/"}, FileSystem: "ext4"}, 85 nil, 86 fileContent1, 87 }, 88 "sda3 mounted at /home": { 89 "/dev/sda3", 90 "", 91 getMountName, 92 DeviceMountAttr{MountPoint: []string{"/home"}, FileSystem: "ext4"}, 93 nil, 94 fileContent2, 95 }, 96 "device is not mounted": { 97 "/dev/sda3", 98 "", 99 getMountName, 100 DeviceMountAttr{}, 101 errors.New("could not get device mount attributes, Path/MountPoint not present in mounts file"), 102 fileContent3, 103 }, 104 "sda3 mounted at /home and /usr": { 105 "/dev/sda3", 106 "", 107 getMountName, 108 DeviceMountAttr{MountPoint: []string{"/home", "/usr"}, FileSystem: "ext4"}, 109 nil, 110 fileContent4, 111 }, 112 "Mountpoint /": { 113 "", 114 "/", 115 getPartitionName, 116 DeviceMountAttr{DevPath: "sda4"}, 117 nil, 118 fileContent1, 119 }, 120 "Mountpoint /home": { 121 "", 122 "/home", 123 getPartitionName, 124 DeviceMountAttr{DevPath: "sda3"}, 125 nil, 126 fileContent2, 127 }, 128 "Mountpoint not found": { 129 "", 130 "/usr", 131 getPartitionName, 132 DeviceMountAttr{}, 133 errors.New("could not get device mount attributes, Path/MountPoint not present in mounts file"), 134 fileContent2, 135 }, 136 "Mountpoint found but device not /dev/*": { 137 "", 138 "/sys", 139 getPartitionName, 140 DeviceMountAttr{}, 141 errors.New("could not get device mount attributes, Path/MountPoint not present in mounts file"), 142 fileContent3, 143 }, 144 "Mountpoint /home, /dev/sda3 mounted twice": { 145 "", 146 "/home", 147 getPartitionName, 148 DeviceMountAttr{DevPath: "sda3"}, 149 nil, 150 fileContent4, 151 }, 152 "Mountpoint /usr, /dev/sda3 mounted twice": { 153 "", 154 "/usr", 155 getPartitionName, 156 DeviceMountAttr{DevPath: "sda3"}, 157 nil, 158 fileContent4, 159 }, 160 } 161 for name, test := range mountAttrTests { 162 t.Run(name, func(t *testing.T) { 163 var fn getMountData 164 mountUtil := NewMountUtil(filePath, test.devPath, test.mountPoint) 165 166 // create the temp file which will be read for getting attributes 167 err := ioutil.WriteFile(filePath, test.fileContent, 0644) 168 if err != nil { 169 t.Fatal(err) 170 } 171 172 switch test.attrFunc { 173 case getMountName: 174 fn = mountUtil.getMountName 175 case getPartitionName: 176 fn = mountUtil.getPartitionName 177 } 178 mountAttr, err := mountUtil.getDeviceMountAttr(fn) 179 180 assert.Equal(t, test.expectedMountAttr, mountAttr) 181 assert.Equal(t, test.expectedError, err) 182 183 // remove the temp file 184 os.Remove(filePath) 185 }) 186 } 187 188 // invalid path mountAttrTests 189 mountUtil := NewMountUtil(filePath, "/dev/sda3", "") 190 _, err := mountUtil.getDeviceMountAttr(mountUtil.getMountName) 191 assert.NotNil(t, err) 192 } 193 194 func TestGetPartitionName(t *testing.T) { 195 mountLine := "/dev/sda4 /home ext4 rw,relatime,errors=remount-ro,data=ordered 0 0" 196 mountPoint1 := "/home" 197 mountPoint2 := "/" 198 tests := map[string]struct { 199 expectedAttr DeviceMountAttr 200 expectedOk bool 201 mountPoint string 202 line string 203 }{ 204 "mount point is present in line": {DeviceMountAttr{DevPath: "sda4"}, true, mountPoint1, mountLine}, 205 "mount point is not present in line": {DeviceMountAttr{}, false, mountPoint2, mountLine}, 206 "mountline is empty": {DeviceMountAttr{}, false, mountPoint2, ""}, 207 } 208 209 for name, test := range tests { 210 t.Run(name, func(t *testing.T) { 211 mountPointUtil := NewMountUtil("", "", test.mountPoint) 212 mountAttr, ok := mountPointUtil.getPartitionName(test.line) 213 assert.Equal(t, test.expectedAttr, mountAttr) 214 assert.Equal(t, test.expectedOk, ok) 215 }) 216 } 217 } 218 219 func TestGetMountName(t *testing.T) { 220 mountLine := "/dev/sda4 /home ext4 rw,relatime,errors=remount-ro,data=ordered 0 0" 221 devPath1 := "/dev/sda4" 222 devPath2 := "/dev/sda3" 223 fsType := "ext4" 224 mountPoint := "/home" 225 tests := map[string]struct { 226 expectedMountAttr DeviceMountAttr 227 expectedOk bool 228 devPath string 229 line string 230 }{ 231 "device sda4 is mounted": {DeviceMountAttr{MountPoint: []string{mountPoint}, FileSystem: fsType}, true, devPath1, mountLine}, 232 "device sda3 is not mounted": {DeviceMountAttr{}, false, devPath2, mountLine}, 233 "mount line is empty": {DeviceMountAttr{}, false, devPath2, ""}, 234 } 235 236 for name, test := range tests { 237 t.Run(name, func(t *testing.T) { 238 mountPointUtil := NewMountUtil("", test.devPath, "") 239 attr, ok := mountPointUtil.getMountName(test.line) 240 assert.Equal(t, test.expectedMountAttr, attr) 241 assert.Equal(t, test.expectedOk, ok) 242 }) 243 } 244 } 245 246 func TestOsDiskPath(t *testing.T) { 247 filePath := "/proc/self/mounts" 248 mountPointUtil := NewMountUtil(filePath, "", "/") 249 path, err := mountPointUtil.GetDiskPath() 250 tests := map[string]struct { 251 actualPath string 252 actualError error 253 expectedError error 254 }{ 255 "test case for os disk path": {actualPath: path, actualError: err, expectedError: nil}, 256 } 257 for name, test := range tests { 258 t.Run(name, func(t *testing.T) { 259 _, err := filepath.EvalSymlinks(test.actualPath) 260 if err != nil { 261 t.Error(err) 262 } 263 assert.Equal(t, test.expectedError, test.actualError) 264 }) 265 } 266 } 267 268 func TestGetParentBlockDevice(t *testing.T) { 269 tests := map[string]struct { 270 syspath string 271 expectedParentBlockDevice string 272 expectedOk bool 273 }{ 274 "getting parent of a main blockdevice itself": { 275 syspath: "/sys/devices/pci0000:00/0000:00:0d.0/ata1/host0/target0:0:0/0:0:0:0/block/sda", 276 expectedParentBlockDevice: "sda", 277 expectedOk: true, 278 }, 279 "getting parent of a partition": { 280 syspath: "/sys/devices/pci0000:00/0000:00:0d.0/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1", 281 expectedParentBlockDevice: "sda", 282 expectedOk: true, 283 }, 284 "getting parent of main NVMe blockdevice": { 285 syspath: "/sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1", 286 expectedParentBlockDevice: "nvme0n1", 287 expectedOk: true, 288 }, 289 "getting parent of partitioned NVMe blockdevice": { 290 syspath: "/sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1/nvme0n1p1", 291 expectedParentBlockDevice: "nvme0n1", 292 expectedOk: true, 293 }, 294 "getting parent of main virtual NVMe blockdevice": { 295 syspath: "/sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1", 296 expectedParentBlockDevice: "nvme0n1", 297 expectedOk: true, 298 }, 299 "getting parent of partitioned virtual NVMe blockdevice": { 300 syspath: "/sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1/nvme0n1p1", 301 expectedParentBlockDevice: "nvme0n1", 302 expectedOk: true, 303 }, 304 "getting parent of wrong disk": { 305 syspath: "/sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0", 306 expectedParentBlockDevice: "", 307 expectedOk: false, 308 }, 309 "giving a wrong syspath": { 310 syspath: "/sys/devices/pci0000:00/0000:00:0e.0", 311 expectedParentBlockDevice: "", 312 expectedOk: false, 313 }, 314 } 315 316 for name, test := range tests { 317 t.Run(name, func(t *testing.T) { 318 parentBlockDevice, ok := getParentBlockDevice(test.syspath) 319 assert.Equal(t, test.expectedParentBlockDevice, parentBlockDevice) 320 assert.Equal(t, test.expectedOk, ok) 321 }) 322 } 323 } 324 325 func TestParseRootDeviceLink(t *testing.T) { 326 tests := map[string]struct { 327 content string 328 expectedPath string 329 expectedError error 330 }{ 331 "empty content": { 332 "", 333 "", 334 ErrCouldNotFindRootDevice, 335 }, 336 "single line with root only": { 337 "root=UUID=d41162ba-25e4-4c44-8793-2abef96d27e9", 338 "/dev/disk/by-uuid/d41162ba-25e4-4c44-8793-2abef96d27e9", 339 nil, 340 }, 341 "single line with multiple attributes": { 342 "BOOT_IMAGE=/boot/vmlinuz-5.4.0-48-generic root=UUID=d41162ba-25e4-4c44-8793-2abef96d27e9 ro intel_iommu=on quiet splash vt.handoff=7", 343 "/dev/disk/by-uuid/d41162ba-25e4-4c44-8793-2abef96d27e9", 344 nil, 345 }, 346 "single line without root attribute": { 347 "BOOT_IMAGE=/boot/vmlinuz-5.4.0-48-generic ro intel_iommu=on quiet splash vt.handoff=7", 348 "", 349 ErrCouldNotFindRootDevice, 350 }, 351 "multi line with multiple attributes": { 352 "\n\nBOOT_IMAGE=/boot/vmlinuz-5.4.0-48-generic root=PARTUUID=325c5bfa-08a8-433c-bc62-2dd5255213fd ro\n", 353 "/dev/disk/by-partuuid/325c5bfa-08a8-433c-bc62-2dd5255213fd", 354 nil, 355 }, 356 "single line with root on dm device, (simulates cmdline in GKE)": { 357 "BOOT_IMAGE=/syslinux/vmlinuz.A root=/dev/dm-0", 358 "/dev/dm-0", 359 nil, 360 }, 361 } 362 363 for name, test := range tests { 364 t.Run(name, func(t *testing.T) { 365 366 actualPath, actualError := parseRootDeviceLink(strings.NewReader(test.content)) 367 368 assert.Equal(t, test.expectedError, actualError) 369 370 if actualError == nil { 371 assert.Equal(t, test.expectedPath, actualPath) 372 } 373 }) 374 } 375 } 376 377 func TestGetSoftLinkForPartition(t *testing.T) { 378 tests := map[string]string{ 379 "sda1": "/sys/class/block/sda1", 380 "nvme0n1": "/sys/class/block/nvme0n1", 381 } 382 383 for partition, expectedSoftlink := range tests { 384 t.Run(partition, func(t *testing.T) { 385 actualSoftLink, actualError := getSoftLinkForPartition(partition) 386 assert.NoError(t, actualError) 387 assert.Equal(t, expectedSoftlink, actualSoftLink) 388 }) 389 } 390 391 t.Run("root", func(t *testing.T) { 392 actualSoftLink, actualError := getSoftLinkForPartition("root") 393 assert.NoError(t, actualError) 394 assert.NotEqual(t, "/sys/class/block/root", actualSoftLink) 395 assert.True(t, strings.HasPrefix(actualSoftLink, "/sys/class/block/")) 396 }) 397 } 398 399 func TestGetDiskDevPath_WithRoot(t *testing.T) { 400 path, err := getPartitionDevPath("root") 401 402 assert.NoError(t, err) 403 assert.True(t, strings.HasPrefix(path, "/dev/")) 404 } 405 406 func TestMergeDeviceMountAttrs(t *testing.T) { 407 tests := map[string]struct { 408 first DeviceMountAttr 409 second DeviceMountAttr 410 expected DeviceMountAttr 411 }{ 412 "First empty, second has DevPath only": { 413 first: DeviceMountAttr{}, 414 second: DeviceMountAttr{DevPath: "/dev/sda3"}, 415 expected: DeviceMountAttr{DevPath: "/dev/sda3"}, 416 }, 417 "First has DevPath, does not match with second's DevPath": { 418 first: DeviceMountAttr{DevPath: "/dev/sda3"}, 419 second: DeviceMountAttr{ 420 DevPath: "/dev/sda4", 421 MountPoint: []string{"/home"}, 422 FileSystem: "ext4", 423 }, 424 expected: DeviceMountAttr{DevPath: "/dev/sda3"}, 425 }, 426 "DevPaths match, FileSystem empty in first only": { 427 first: DeviceMountAttr{ 428 DevPath: "/dev/sda3", 429 }, 430 second: DeviceMountAttr{ 431 DevPath: "/dev/sda3", 432 MountPoint: []string{"/"}, 433 FileSystem: "ext4", 434 }, 435 expected: DeviceMountAttr{ 436 DevPath: "/dev/sda3", 437 MountPoint: []string{"/"}, 438 FileSystem: "ext4", 439 }, 440 }, 441 "DevPaths match, FileSystems don't match": { 442 first: DeviceMountAttr{ 443 DevPath: "/dev/sda3", 444 MountPoint: []string{"/"}, 445 FileSystem: "ext3", 446 }, 447 second: DeviceMountAttr{ 448 DevPath: "/dev/sda3", 449 MountPoint: []string{"/home"}, 450 FileSystem: "ext4", 451 }, 452 expected: DeviceMountAttr{ 453 DevPath: "/dev/sda3", 454 MountPoint: []string{"/"}, 455 FileSystem: "ext3", 456 }, 457 }, 458 "Both DevPath and FileSystem match, FileSystem empty": { 459 first: DeviceMountAttr{ 460 DevPath: "/dev/sda3", 461 MountPoint: []string{"/"}, 462 FileSystem: "", 463 }, 464 second: DeviceMountAttr{ 465 DevPath: "/dev/sda3", 466 MountPoint: []string{"/home"}, 467 FileSystem: "", 468 }, 469 expected: DeviceMountAttr{ 470 DevPath: "/dev/sda3", 471 MountPoint: []string{"/"}, 472 FileSystem: "", 473 }, 474 }, 475 "Both DevPath and FileSystem match, FileSystem non-empty": { 476 first: DeviceMountAttr{ 477 DevPath: "/dev/sda3", 478 MountPoint: []string{"/"}, 479 FileSystem: "ext4", 480 }, 481 second: DeviceMountAttr{ 482 DevPath: "/dev/sda3", 483 MountPoint: []string{"/home"}, 484 FileSystem: "ext4", 485 }, 486 expected: DeviceMountAttr{ 487 DevPath: "/dev/sda3", 488 MountPoint: []string{"/", "/home"}, 489 FileSystem: "ext4", 490 }, 491 }, 492 "Both DevPaths empty, FileSystems match and non-empty": { 493 first: DeviceMountAttr{ 494 DevPath: "", 495 MountPoint: []string{"/"}, 496 FileSystem: "ext4", 497 }, 498 second: DeviceMountAttr{ 499 DevPath: "", 500 MountPoint: []string{"/home"}, 501 FileSystem: "ext4", 502 }, 503 expected: DeviceMountAttr{ 504 DevPath: "", 505 MountPoint: []string{"/", "/home"}, 506 FileSystem: "ext4", 507 }, 508 }, 509 } 510 511 for name, test := range tests { 512 t.Run(name, func(t *testing.T) { 513 mergeDeviceMountAttrs(&test.first, &test.second) 514 assert.Equal(t, test.first, test.expected) 515 }) 516 } 517 }