k8s.io/kubernetes@v1.29.3/pkg/volume/local/local_test.go (about) 1 //go:build linux || darwin || windows 2 // +build linux darwin windows 3 4 /* 5 Copyright 2017 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package local 21 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 "reflect" 27 "runtime" 28 "testing" 29 30 v1 "k8s.io/api/core/v1" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/types" 33 utiltesting "k8s.io/client-go/util/testing" 34 "k8s.io/kubernetes/pkg/volume" 35 volumetest "k8s.io/kubernetes/pkg/volume/testing" 36 "k8s.io/kubernetes/pkg/volume/util/hostutil" 37 "k8s.io/mount-utils" 38 ) 39 40 const ( 41 testPVName = "pvA" 42 testMountPath = "pods/poduid/volumes/kubernetes.io~local-volume/pvA" 43 testGlobalPath = "plugins/kubernetes.io~local-volume/volumeDevices/pvA" 44 testPodPath = "pods/poduid/volumeDevices/kubernetes.io~local-volume" 45 testBlockFormattingToFSGlobalPath = "plugins/kubernetes.io/local-volume/mounts/pvA" 46 ) 47 48 func getPlugin(t *testing.T) (string, volume.VolumePlugin) { 49 tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") 50 if err != nil { 51 t.Fatalf("can't make a temp dir: %v", err) 52 } 53 54 plugMgr := volume.VolumePluginMgr{} 55 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)) 56 57 plug, err := plugMgr.FindPluginByName(localVolumePluginName) 58 if err != nil { 59 os.RemoveAll(tmpDir) 60 t.Fatalf("Can't find the plugin by name") 61 } 62 if plug.GetPluginName() != localVolumePluginName { 63 t.Errorf("Wrong name: %s", plug.GetPluginName()) 64 } 65 return tmpDir, plug 66 } 67 68 func getBlockPlugin(t *testing.T) (string, volume.BlockVolumePlugin) { 69 tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") 70 if err != nil { 71 t.Fatalf("can't make a temp dir: %v", err) 72 } 73 74 plugMgr := volume.VolumePluginMgr{} 75 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)) 76 plug, err := plugMgr.FindMapperPluginByName(localVolumePluginName) 77 if err != nil { 78 os.RemoveAll(tmpDir) 79 t.Fatalf("Can't find the plugin by name: %q", localVolumePluginName) 80 } 81 if plug.GetPluginName() != localVolumePluginName { 82 t.Errorf("Wrong name: %s", plug.GetPluginName()) 83 } 84 return tmpDir, plug 85 } 86 87 func getNodeExpandablePlugin(t *testing.T, isBlockDevice bool) (string, volume.NodeExpandableVolumePlugin) { 88 tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") 89 if err != nil { 90 t.Fatalf("can't make a temp dir: %v", err) 91 } 92 93 plugMgr := volume.VolumePluginMgr{} 94 var pathToFSType map[string]hostutil.FileType 95 if isBlockDevice { 96 pathToFSType = map[string]hostutil.FileType{ 97 tmpDir: hostutil.FileTypeBlockDev, 98 } 99 } else { 100 pathToFSType = map[string]hostutil.FileType{ 101 tmpDir: hostutil.FileTypeDirectory, 102 } 103 } 104 105 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHostWithMounterFSType(t, tmpDir, nil, nil, pathToFSType)) 106 107 plug, err := plugMgr.FindNodeExpandablePluginByName(localVolumePluginName) 108 if err != nil { 109 os.RemoveAll(tmpDir) 110 t.Fatalf("Can't find the plugin by name") 111 } 112 if plug.GetPluginName() != localVolumePluginName { 113 t.Errorf("Wrong name: %s", plug.GetPluginName()) 114 } 115 return tmpDir, plug 116 } 117 118 func getPersistentPlugin(t *testing.T) (string, volume.PersistentVolumePlugin) { 119 tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") 120 if err != nil { 121 t.Fatalf("can't make a temp dir: %v", err) 122 } 123 124 plugMgr := volume.VolumePluginMgr{} 125 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)) 126 127 plug, err := plugMgr.FindPersistentPluginByName(localVolumePluginName) 128 if err != nil { 129 os.RemoveAll(tmpDir) 130 t.Fatalf("Can't find the plugin by name") 131 } 132 if plug.GetPluginName() != localVolumePluginName { 133 t.Errorf("Wrong name: %s", plug.GetPluginName()) 134 } 135 return tmpDir, plug 136 } 137 138 func getDeviceMountablePluginWithBlockPath(t *testing.T, isBlockDevice bool) (string, volume.DeviceMountableVolumePlugin) { 139 var ( 140 source string 141 err error 142 ) 143 144 if isBlockDevice && runtime.GOOS == "windows" { 145 // On Windows, block devices are referenced by the disk number, which is validated by the mounter, 146 source = "0" 147 } else { 148 source, err = utiltesting.MkTmpdir("localVolumeTest") 149 if err != nil { 150 t.Fatalf("can't make a temp dir: %v", err) 151 } 152 } 153 154 plugMgr := volume.VolumePluginMgr{} 155 var pathToFSType map[string]hostutil.FileType 156 if isBlockDevice { 157 pathToFSType = map[string]hostutil.FileType{ 158 source: hostutil.FileTypeBlockDev, 159 } 160 } 161 162 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHostWithMounterFSType(t, source, nil, nil, pathToFSType)) 163 164 plug, err := plugMgr.FindDeviceMountablePluginByName(localVolumePluginName) 165 if err != nil { 166 os.RemoveAll(source) 167 t.Fatalf("Can't find the plugin by name") 168 } 169 if plug.GetPluginName() != localVolumePluginName { 170 t.Errorf("Wrong name: %s", plug.GetPluginName()) 171 } 172 return source, plug 173 } 174 175 func getTestVolume(readOnly bool, path string, isBlock bool, mountOptions []string) *volume.Spec { 176 pv := &v1.PersistentVolume{ 177 ObjectMeta: metav1.ObjectMeta{ 178 Name: testPVName, 179 }, 180 Spec: v1.PersistentVolumeSpec{ 181 PersistentVolumeSource: v1.PersistentVolumeSource{ 182 Local: &v1.LocalVolumeSource{ 183 Path: path, 184 }, 185 }, 186 MountOptions: mountOptions, 187 }, 188 } 189 190 if isBlock { 191 blockMode := v1.PersistentVolumeBlock 192 pv.Spec.VolumeMode = &blockMode 193 } else { 194 fsMode := v1.PersistentVolumeFilesystem 195 pv.Spec.VolumeMode = &fsMode 196 } 197 return volume.NewSpecFromPersistentVolume(pv, readOnly) 198 } 199 200 func TestCanSupport(t *testing.T) { 201 tmpDir, plug := getPlugin(t) 202 defer os.RemoveAll(tmpDir) 203 204 if !plug.CanSupport(getTestVolume(false, tmpDir, false, nil)) { 205 t.Errorf("Expected true") 206 } 207 } 208 209 func TestGetAccessModes(t *testing.T) { 210 tmpDir, plug := getPersistentPlugin(t) 211 defer os.RemoveAll(tmpDir) 212 213 modes := plug.GetAccessModes() 214 if !volumetest.ContainsAccessMode(modes, v1.ReadWriteOnce) { 215 t.Errorf("Expected AccessModeType %q", v1.ReadWriteOnce) 216 } 217 218 if volumetest.ContainsAccessMode(modes, v1.ReadWriteMany) { 219 t.Errorf("Found AccessModeType %q, expected not", v1.ReadWriteMany) 220 } 221 if volumetest.ContainsAccessMode(modes, v1.ReadOnlyMany) { 222 t.Errorf("Found AccessModeType %q, expected not", v1.ReadOnlyMany) 223 } 224 } 225 226 func TestGetVolumeName(t *testing.T) { 227 tmpDir, plug := getPersistentPlugin(t) 228 defer os.RemoveAll(tmpDir) 229 230 volName, err := plug.GetVolumeName(getTestVolume(false, tmpDir, false, nil)) 231 if err != nil { 232 t.Errorf("Failed to get volume name: %v", err) 233 } 234 if volName != testPVName { 235 t.Errorf("Expected volume name %q, got %q", testPVName, volName) 236 } 237 } 238 239 func TestInvalidLocalPath(t *testing.T) { 240 tmpDir, plug := getPlugin(t) 241 defer os.RemoveAll(tmpDir) 242 243 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 244 mounter, err := plug.NewMounter(getTestVolume(false, "/no/backsteps/allowed/..", false, nil), pod, volume.VolumeOptions{}) 245 if err != nil { 246 t.Fatal(err) 247 } 248 249 err = mounter.SetUp(volume.MounterArgs{}) 250 expectedMsg := "invalid path: /no/backsteps/allowed/.. must not contain '..'" 251 if err.Error() != expectedMsg { 252 t.Fatalf("expected error `%s` but got `%s`", expectedMsg, err) 253 } 254 } 255 256 func TestBlockDeviceGlobalPathAndMountDevice(t *testing.T) { 257 // Block device global mount path testing 258 tmpBlockDir, plug := getDeviceMountablePluginWithBlockPath(t, true) 259 defer os.RemoveAll(tmpBlockDir) 260 261 dm, err := plug.NewDeviceMounter() 262 if err != nil { 263 t.Errorf("Failed to make a new device mounter: %v", err) 264 } 265 266 pvSpec := getTestVolume(false, tmpBlockDir, false, nil) 267 268 expectedGlobalPath := filepath.Join(tmpBlockDir, testBlockFormattingToFSGlobalPath) 269 actualPath, err := dm.GetDeviceMountPath(pvSpec) 270 if err != nil { 271 t.Errorf("Failed to get device mount path: %v", err) 272 } 273 if expectedGlobalPath != actualPath { 274 t.Fatalf("Expected device mount global path:%s, got: %s", expectedGlobalPath, actualPath) 275 } 276 277 fmt.Println("expected global path is:", expectedGlobalPath) 278 279 err = dm.MountDevice(pvSpec, tmpBlockDir, expectedGlobalPath, volume.DeviceMounterArgs{}) 280 if err != nil { 281 t.Fatal(err) 282 } 283 if _, err := os.Stat(actualPath); err != nil { 284 if os.IsNotExist(err) { 285 t.Errorf("DeviceMounter.MountDevice() failed, device mount path not created: %s", actualPath) 286 } else { 287 t.Errorf("DeviceMounter.MountDevice() failed: %v", err) 288 } 289 } 290 291 du, err := plug.NewDeviceUnmounter() 292 if err != nil { 293 t.Fatalf("Create device unmounter error: %v", err) 294 } 295 296 err = du.UnmountDevice(actualPath) 297 if err != nil { 298 t.Fatalf("Unmount device error: %v", err) 299 } 300 } 301 302 func TestFSGlobalPathAndMountDevice(t *testing.T) { 303 // FS global path testing 304 tmpFSDir, plug := getDeviceMountablePluginWithBlockPath(t, false) 305 defer os.RemoveAll(tmpFSDir) 306 307 dm, err := plug.NewDeviceMounter() 308 if err != nil { 309 t.Errorf("Failed to make a new device mounter: %v", err) 310 } 311 312 pvSpec := getTestVolume(false, tmpFSDir, false, nil) 313 314 expectedGlobalPath := tmpFSDir 315 actualPath, err := dm.GetDeviceMountPath(pvSpec) 316 if err != nil { 317 t.Errorf("Failed to get device mount path: %v", err) 318 } 319 if expectedGlobalPath != actualPath { 320 t.Fatalf("Expected device mount global path:%s, got: %s", expectedGlobalPath, actualPath) 321 } 322 323 // Actually, we will do nothing if the local path is FS type 324 err = dm.MountDevice(pvSpec, tmpFSDir, expectedGlobalPath, volume.DeviceMounterArgs{}) 325 if err != nil { 326 t.Fatal(err) 327 } 328 if _, err := os.Stat(expectedGlobalPath); err != nil { 329 if os.IsNotExist(err) { 330 t.Errorf("DeviceMounter.MountDevice() failed, device mount path not created: %s", expectedGlobalPath) 331 } else { 332 t.Errorf("DeviceMounter.MountDevice() failed: %v", err) 333 } 334 } 335 } 336 337 func TestNodeExpand(t *testing.T) { 338 // FS global path testing 339 tmpFSDir, plug := getNodeExpandablePlugin(t, false) 340 defer os.RemoveAll(tmpFSDir) 341 342 pvSpec := getTestVolume(false, tmpFSDir, false, nil) 343 344 resizeOptions := volume.NodeResizeOptions{ 345 VolumeSpec: pvSpec, 346 DevicePath: tmpFSDir, 347 } 348 349 // Actually, we will do no volume expansion if volume is of type dir 350 resizeDone, err := plug.NodeExpand(resizeOptions) 351 if err != nil { 352 t.Fatal(err) 353 } 354 if !resizeDone { 355 t.Errorf("expected resize to be done") 356 } 357 } 358 359 func TestMountUnmount(t *testing.T) { 360 tmpDir, plug := getPlugin(t) 361 defer os.RemoveAll(tmpDir) 362 363 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 364 mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{}) 365 if err != nil { 366 t.Errorf("Failed to make a new Mounter: %v", err) 367 } 368 if mounter == nil { 369 t.Fatalf("Got a nil Mounter") 370 } 371 372 volPath := filepath.Join(tmpDir, testMountPath) 373 path := mounter.GetPath() 374 if path != volPath { 375 t.Errorf("Got unexpected path: %s", path) 376 } 377 378 if err := mounter.SetUp(volume.MounterArgs{}); err != nil { 379 t.Errorf("Expected success, got: %v", err) 380 } 381 382 if runtime.GOOS != "windows" { 383 // skip this check in windows since the "bind mount" logic is implemented differently in mount_windows.go 384 if _, err := os.Stat(path); err != nil { 385 if os.IsNotExist(err) { 386 t.Errorf("SetUp() failed, volume path not created: %s", path) 387 } else { 388 t.Errorf("SetUp() failed: %v", err) 389 } 390 } 391 } 392 393 unmounter, err := plug.NewUnmounter(testPVName, pod.UID) 394 if err != nil { 395 t.Errorf("Failed to make a new Unmounter: %v", err) 396 } 397 if unmounter == nil { 398 t.Fatalf("Got a nil Unmounter") 399 } 400 401 if err := unmounter.TearDown(); err != nil { 402 t.Errorf("Expected success, got: %v", err) 403 } 404 if _, err := os.Stat(path); err == nil { 405 t.Errorf("TearDown() failed, volume path still exists: %s", path) 406 } else if !os.IsNotExist(err) { 407 t.Errorf("TearDown() failed: %v", err) 408 } 409 } 410 411 // TestMapUnmap tests block map and unmap interfaces. 412 func TestMapUnmap(t *testing.T) { 413 tmpDir, plug := getBlockPlugin(t) 414 defer os.RemoveAll(tmpDir) 415 416 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 417 volSpec := getTestVolume(false, tmpDir, true /*isBlock*/, nil) 418 mapper, err := plug.NewBlockVolumeMapper(volSpec, pod, volume.VolumeOptions{}) 419 if err != nil { 420 t.Errorf("Failed to make a new Mounter: %v", err) 421 } 422 if mapper == nil { 423 t.Fatalf("Got a nil Mounter") 424 } 425 426 expectedGlobalPath := filepath.Join(tmpDir, testGlobalPath) 427 globalPath, err := mapper.GetGlobalMapPath(volSpec) 428 if err != nil { 429 t.Errorf("Failed to get global path: %v", err) 430 } 431 if globalPath != expectedGlobalPath { 432 t.Errorf("Got unexpected path: %s, expected %s", globalPath, expectedGlobalPath) 433 } 434 expectedPodPath := filepath.Join(tmpDir, testPodPath) 435 podPath, volName := mapper.GetPodDeviceMapPath() 436 if podPath != expectedPodPath { 437 t.Errorf("Got unexpected pod path: %s, expected %s", podPath, expectedPodPath) 438 } 439 if volName != testPVName { 440 t.Errorf("Got unexpected volNamne: %s, expected %s", volName, testPVName) 441 } 442 var devPath string 443 444 if customMapper, ok := mapper.(volume.CustomBlockVolumeMapper); ok { 445 _, err = customMapper.SetUpDevice() 446 if err != nil { 447 t.Errorf("Failed to SetUpDevice, err: %v", err) 448 } 449 devPath, err = customMapper.MapPodDevice() 450 if err != nil { 451 t.Errorf("Failed to MapPodDevice, err: %v", err) 452 } 453 } 454 455 if _, err := os.Stat(devPath); err != nil { 456 if os.IsNotExist(err) { 457 t.Errorf("SetUpDevice() failed, volume path not created: %s", devPath) 458 } else { 459 t.Errorf("SetUpDevice() failed: %v", err) 460 } 461 } 462 463 unmapper, err := plug.NewBlockVolumeUnmapper(testPVName, pod.UID) 464 if err != nil { 465 t.Fatalf("Failed to make a new Unmapper: %v", err) 466 } 467 if unmapper == nil { 468 t.Fatalf("Got a nil Unmapper") 469 } 470 471 if customUnmapper, ok := unmapper.(volume.CustomBlockVolumeUnmapper); ok { 472 if err := customUnmapper.UnmapPodDevice(); err != nil { 473 t.Errorf("UnmapPodDevice failed, err: %v", err) 474 } 475 476 if err := customUnmapper.TearDownDevice(globalPath, devPath); err != nil { 477 t.Errorf("TearDownDevice failed, err: %v", err) 478 } 479 } 480 } 481 482 func testFSGroupMount(plug volume.VolumePlugin, pod *v1.Pod, tmpDir string, fsGroup int64) error { 483 mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{}) 484 if err != nil { 485 return err 486 } 487 if mounter == nil { 488 return fmt.Errorf("got a nil Mounter") 489 } 490 491 volPath := filepath.Join(tmpDir, testMountPath) 492 path := mounter.GetPath() 493 if path != volPath { 494 return fmt.Errorf("got unexpected path: %s", path) 495 } 496 497 var mounterArgs volume.MounterArgs 498 mounterArgs.FsGroup = &fsGroup 499 if err := mounter.SetUp(mounterArgs); err != nil { 500 return err 501 } 502 return nil 503 } 504 505 func TestConstructVolumeSpec(t *testing.T) { 506 tests := []struct { 507 name string 508 mountPoints []mount.MountPoint 509 expectedPath string 510 }{ 511 { 512 name: "filesystem volume with directory source", 513 mountPoints: []mount.MountPoint{ 514 { 515 Device: "/mnt/disk/ssd0", 516 Path: "pods/poduid/volumes/kubernetes.io~local-volume/pvA", 517 }, 518 }, 519 expectedPath: "", 520 }, 521 { 522 name: "filesystem volume with block source", 523 mountPoints: []mount.MountPoint{ 524 { 525 Device: "/dev/loop0", 526 Path: testMountPath, 527 }, 528 { 529 Device: "/dev/loop0", 530 Path: testBlockFormattingToFSGlobalPath, 531 }, 532 }, 533 expectedPath: "/dev/loop0", 534 }, 535 } 536 537 for _, tt := range tests { 538 t.Run(tt.name, func(t *testing.T) { 539 tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") 540 if err != nil { 541 t.Fatalf("can't make a temp dir: %v", err) 542 } 543 defer os.RemoveAll(tmpDir) 544 plug := &localVolumePlugin{ 545 host: volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil), 546 } 547 mounter := plug.host.GetMounter(plug.GetPluginName()) 548 fakeMountPoints := []mount.MountPoint{} 549 for _, mp := range tt.mountPoints { 550 fakeMountPoint := mp 551 fakeMountPoint.Path = filepath.Join(tmpDir, mp.Path) 552 fakeMountPoints = append(fakeMountPoints, fakeMountPoint) 553 } 554 mounter.(*mount.FakeMounter).MountPoints = fakeMountPoints 555 volPath := filepath.Join(tmpDir, testMountPath) 556 rec, err := plug.ConstructVolumeSpec(testPVName, volPath) 557 if err != nil { 558 t.Errorf("ConstructVolumeSpec() failed: %v", err) 559 } 560 if rec.Spec == nil { 561 t.Fatalf("ConstructVolumeSpec() returned nil") 562 } 563 564 volName := rec.Spec.Name() 565 if volName != testPVName { 566 t.Errorf("Expected volume name %q, got %q", testPVName, volName) 567 } 568 569 if rec.Spec.Volume != nil { 570 t.Errorf("Volume object returned, expected nil") 571 } 572 573 pv := rec.Spec.PersistentVolume 574 if pv == nil { 575 t.Fatalf("PersistentVolume object nil") 576 } 577 578 if rec.Spec.PersistentVolume.Spec.VolumeMode == nil { 579 t.Fatalf("Volume mode has not been set.") 580 } 581 582 if *rec.Spec.PersistentVolume.Spec.VolumeMode != v1.PersistentVolumeFilesystem { 583 t.Errorf("Unexpected volume mode %q", *rec.Spec.PersistentVolume.Spec.VolumeMode) 584 } 585 586 ls := pv.Spec.PersistentVolumeSource.Local 587 if ls == nil { 588 t.Fatalf("LocalVolumeSource object nil") 589 } 590 591 if pv.Spec.PersistentVolumeSource.Local.Path != tt.expectedPath { 592 t.Fatalf("Unexpected path got %q, expected %q", pv.Spec.PersistentVolumeSource.Local.Path, tt.expectedPath) 593 } 594 }) 595 } 596 597 } 598 599 func TestConstructBlockVolumeSpec(t *testing.T) { 600 tmpDir, plug := getBlockPlugin(t) 601 defer os.RemoveAll(tmpDir) 602 603 podPath := filepath.Join(tmpDir, testPodPath) 604 spec, err := plug.ConstructBlockVolumeSpec(types.UID("poduid"), testPVName, podPath) 605 if err != nil { 606 t.Errorf("ConstructBlockVolumeSpec() failed: %v", err) 607 } 608 if spec == nil { 609 t.Fatalf("ConstructBlockVolumeSpec() returned nil") 610 } 611 612 volName := spec.Name() 613 if volName != testPVName { 614 t.Errorf("Expected volume name %q, got %q", testPVName, volName) 615 } 616 617 if spec.Volume != nil { 618 t.Errorf("Volume object returned, expected nil") 619 } 620 621 pv := spec.PersistentVolume 622 if pv == nil { 623 t.Fatalf("PersistentVolume object nil") 624 } 625 626 if spec.PersistentVolume.Spec.VolumeMode == nil { 627 t.Fatalf("Volume mode has not been set.") 628 } 629 630 if *spec.PersistentVolume.Spec.VolumeMode != v1.PersistentVolumeBlock { 631 t.Errorf("Unexpected volume mode %q", *spec.PersistentVolume.Spec.VolumeMode) 632 } 633 634 ls := pv.Spec.PersistentVolumeSource.Local 635 if ls == nil { 636 t.Fatalf("LocalVolumeSource object nil") 637 } 638 } 639 640 func TestMountOptions(t *testing.T) { 641 tmpDir, plug := getPlugin(t) 642 defer os.RemoveAll(tmpDir) 643 644 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 645 mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, []string{"test-option"}), pod, volume.VolumeOptions{}) 646 if err != nil { 647 t.Errorf("Failed to make a new Mounter: %v", err) 648 } 649 if mounter == nil { 650 t.Fatalf("Got a nil Mounter") 651 } 652 653 // Wrap with FakeMounter. 654 fakeMounter := mount.NewFakeMounter(nil) 655 mounter.(*localVolumeMounter).mounter = fakeMounter 656 657 if err := mounter.SetUp(volume.MounterArgs{}); err != nil { 658 t.Errorf("Expected success, got: %v", err) 659 } 660 mountOptions := fakeMounter.MountPoints[0].Opts 661 expectedMountOptions := []string{"bind", "test-option"} 662 if !reflect.DeepEqual(mountOptions, expectedMountOptions) { 663 t.Errorf("Expected mount options to be %v got %v", expectedMountOptions, mountOptions) 664 } 665 } 666 667 func TestPersistentClaimReadOnlyFlag(t *testing.T) { 668 tmpDir, plug := getPlugin(t) 669 defer os.RemoveAll(tmpDir) 670 671 // Read only == true 672 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 673 mounter, err := plug.NewMounter(getTestVolume(true, tmpDir, false, nil), pod, volume.VolumeOptions{}) 674 if err != nil { 675 t.Errorf("Failed to make a new Mounter: %v", err) 676 } 677 if mounter == nil { 678 t.Fatalf("Got a nil Mounter") 679 } 680 if !mounter.GetAttributes().ReadOnly { 681 t.Errorf("Expected true for mounter.IsReadOnly") 682 } 683 684 // Read only == false 685 mounter, err = plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{}) 686 if err != nil { 687 t.Errorf("Failed to make a new Mounter: %v", err) 688 } 689 if mounter == nil { 690 t.Fatalf("Got a nil Mounter") 691 } 692 if mounter.GetAttributes().ReadOnly { 693 t.Errorf("Expected false for mounter.IsReadOnly") 694 } 695 } 696 697 func TestUnsupportedPlugins(t *testing.T) { 698 tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") 699 if err != nil { 700 t.Fatalf("can't make a temp dir: %v", err) 701 } 702 defer os.RemoveAll(tmpDir) 703 704 plugMgr := volume.VolumePluginMgr{} 705 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)) 706 spec := getTestVolume(false, tmpDir, false, nil) 707 708 recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec) 709 if err == nil && recyclePlug != nil { 710 t.Errorf("Recyclable plugin found, expected none") 711 } 712 713 deletePlug, err := plugMgr.FindDeletablePluginByName(localVolumePluginName) 714 if err == nil && deletePlug != nil { 715 t.Errorf("Deletable plugin found, expected none") 716 } 717 718 attachPlug, err := plugMgr.FindAttachablePluginByName(localVolumePluginName) 719 if err == nil && attachPlug != nil { 720 t.Errorf("Attachable plugin found, expected none") 721 } 722 723 createPlug, err := plugMgr.FindCreatablePluginBySpec(spec) 724 if err == nil && createPlug != nil { 725 t.Errorf("Creatable plugin found, expected none") 726 } 727 728 provisionPlug, err := plugMgr.FindProvisionablePluginByName(localVolumePluginName) 729 if err == nil && provisionPlug != nil { 730 t.Errorf("Provisionable plugin found, expected none") 731 } 732 } 733 734 func TestFilterPodMounts(t *testing.T) { 735 tmpDir, plug := getPlugin(t) 736 defer os.RemoveAll(tmpDir) 737 738 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 739 mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{}) 740 if err != nil { 741 t.Fatal(err) 742 } 743 lvMounter, ok := mounter.(*localVolumeMounter) 744 if !ok { 745 t.Fatal("mounter is not localVolumeMounter") 746 } 747 748 host := volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil) 749 podsDir := host.GetPodsDir() 750 751 cases := map[string]struct { 752 input []string 753 expected []string 754 }{ 755 "empty": { 756 []string{}, 757 []string{}, 758 }, 759 "not-pod-mount": { 760 []string{"/mnt/outside"}, 761 []string{}, 762 }, 763 "pod-mount": { 764 []string{filepath.Join(podsDir, "pod-mount")}, 765 []string{filepath.Join(podsDir, "pod-mount")}, 766 }, 767 "not-directory-prefix": { 768 []string{podsDir + "pod-mount"}, 769 []string{}, 770 }, 771 "mix": { 772 []string{"/mnt/outside", 773 filepath.Join(podsDir, "pod-mount"), 774 "/another/outside", 775 filepath.Join(podsDir, "pod-mount2")}, 776 []string{filepath.Join(podsDir, "pod-mount"), 777 filepath.Join(podsDir, "pod-mount2")}, 778 }, 779 } 780 for name, test := range cases { 781 output := lvMounter.filterPodMounts(test.input) 782 if !reflect.DeepEqual(output, test.expected) { 783 t.Errorf("%v failed: output %+v doesn't equal expected %+v", name, output, test.expected) 784 } 785 } 786 }