k8s.io/kubernetes@v1.29.3/pkg/volume/rbd/rbd_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes 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 rbd 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 "reflect" 24 "runtime" 25 "strings" 26 "sync" 27 "testing" 28 "time" 29 30 "k8s.io/mount-utils" 31 32 v1 "k8s.io/api/core/v1" 33 "k8s.io/apimachinery/pkg/api/resource" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/types" 36 "k8s.io/apimachinery/pkg/util/uuid" 37 "k8s.io/client-go/kubernetes/fake" 38 utiltesting "k8s.io/client-go/util/testing" 39 "k8s.io/kubernetes/pkg/volume" 40 volumetest "k8s.io/kubernetes/pkg/volume/testing" 41 ) 42 43 const ( 44 testVolName = "vol-1234" 45 testRBDImage = "volume-a4b47414-a675-47dc-a9cc-c223f13439b0" 46 testRBDPool = "volumes" 47 testGlobalPath = "plugins/kubernetes.io/rbd/volumeDevices/volumes-image-volume-a4b47414-a675-47dc-a9cc-c223f13439b0" 48 ) 49 50 func TestGetVolumeSpecFromGlobalMapPath(t *testing.T) { 51 // make our test path for fake GlobalMapPath 52 // /tmp symbolized our pluginDir 53 // /tmp/testGlobalPathXXXXX/plugins/kubernetes.io/rbd/volumeDevices/pdVol1 54 tmpVDir, err := utiltesting.MkTmpdir("rbdBlockTest") 55 if err != nil { 56 t.Fatalf("can't make a temp dir: %v", err) 57 } 58 //deferred clean up 59 defer os.RemoveAll(tmpVDir) 60 61 expectedGlobalPath := filepath.Join(tmpVDir, testGlobalPath) 62 63 //Bad Path 64 badspec, err := getVolumeSpecFromGlobalMapPath("", testVolName) 65 if badspec != nil || err == nil { 66 t.Fatalf("Expected not to get spec from GlobalMapPath but did") 67 } 68 69 // Good Path 70 spec, err := getVolumeSpecFromGlobalMapPath(expectedGlobalPath, testVolName) 71 if spec == nil || err != nil { 72 t.Fatalf("Failed to get spec from GlobalMapPath: %v", err) 73 } 74 75 if spec.PersistentVolume.Name != testVolName { 76 t.Errorf("Invalid spec name for GlobalMapPath spec: %s", spec.PersistentVolume.Name) 77 } 78 79 if spec.PersistentVolume.Spec.RBD.RBDPool != testRBDPool { 80 t.Errorf("Invalid RBDPool from GlobalMapPath spec: %s", spec.PersistentVolume.Spec.RBD.RBDPool) 81 } 82 83 if spec.PersistentVolume.Spec.RBD.RBDImage != testRBDImage { 84 t.Errorf("Invalid RBDImage from GlobalMapPath spec: %s", spec.PersistentVolume.Spec.RBD.RBDImage) 85 } 86 87 block := v1.PersistentVolumeBlock 88 specMode := spec.PersistentVolume.Spec.VolumeMode 89 if specMode == nil { 90 t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v - %v", specMode, block) 91 } 92 if *specMode != block { 93 t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v - %v", *specMode, block) 94 } 95 } 96 97 func TestCanSupport(t *testing.T) { 98 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 99 if err != nil { 100 t.Fatalf("error creating temp dir: %v", err) 101 } 102 defer os.RemoveAll(tmpDir) 103 104 plugMgr := volume.VolumePluginMgr{} 105 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)) 106 107 plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd") 108 if err != nil { 109 t.Fatal("Can't find the plugin by name") 110 } 111 if plug.GetPluginName() != "kubernetes.io/rbd" { 112 t.Errorf("Wrong name: %s", plug.GetPluginName()) 113 } 114 if plug.CanSupport(&volume.Spec{}) { 115 t.Errorf("Expected false") 116 } 117 if plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{}}}) { 118 t.Errorf("Expected false") 119 } 120 if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{RBD: &v1.RBDVolumeSource{}}}}) { 121 t.Errorf("Expected true") 122 } 123 if plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{}}}) { 124 t.Errorf("Expected false") 125 } 126 if plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{}}}}) { 127 t.Errorf("Expected false") 128 } 129 if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{RBD: &v1.RBDPersistentVolumeSource{}}}}}) { 130 t.Errorf("Expected true") 131 } 132 } 133 134 type fakeDiskManager struct { 135 // Make sure we can run tests in parallel. 136 mutex sync.RWMutex 137 // Key format: "<pool>/<image>" 138 rbdImageLocks map[string]bool 139 rbdMapIndex int 140 rbdDevices map[string]bool 141 } 142 143 func newFakeDiskManager() *fakeDiskManager { 144 return &fakeDiskManager{ 145 rbdImageLocks: make(map[string]bool), 146 rbdMapIndex: 0, 147 rbdDevices: make(map[string]bool), 148 } 149 } 150 151 func (fake *fakeDiskManager) MakeGlobalPDName(rbd rbd) string { 152 return makePDNameInternal(rbd.plugin.host, rbd.Pool, rbd.Image) 153 } 154 155 func (fake *fakeDiskManager) MakeGlobalVDPDName(rbd rbd) string { 156 return makePDNameInternal(rbd.plugin.host, rbd.Pool, rbd.Image) 157 } 158 159 func (fake *fakeDiskManager) DetachDisk(r *rbdPlugin, deviceMountPath string, device string) error { 160 fake.mutex.Lock() 161 defer fake.mutex.Unlock() 162 ok := fake.rbdDevices[device] 163 if !ok { 164 return fmt.Errorf("rbd: failed to detach device %s, it does not exist", device) 165 } 166 delete(fake.rbdDevices, device) 167 return nil 168 } 169 170 func (fake *fakeDiskManager) DetachBlockDisk(r rbdDiskUnmapper, device string) error { 171 fake.mutex.Lock() 172 defer fake.mutex.Unlock() 173 ok := fake.rbdDevices[device] 174 if !ok { 175 return fmt.Errorf("rbd: failed to detach device %s, it does not exist", device) 176 } 177 delete(fake.rbdDevices, device) 178 return nil 179 } 180 181 func (fake *fakeDiskManager) CreateImage(provisioner *rbdVolumeProvisioner) (r *v1.RBDPersistentVolumeSource, volumeSizeGB int, err error) { 182 return nil, 0, fmt.Errorf("not implemented") 183 } 184 185 func (fake *fakeDiskManager) DeleteImage(deleter *rbdVolumeDeleter) error { 186 return fmt.Errorf("not implemented") 187 } 188 189 func (fake *fakeDiskManager) Fencing(r rbdMounter, nodeName string) error { 190 fake.mutex.Lock() 191 defer fake.mutex.Unlock() 192 key := fmt.Sprintf("%s/%s", r.Pool, r.Image) 193 isLocked, ok := fake.rbdImageLocks[key] 194 if ok && isLocked { 195 // not expected in testing 196 return fmt.Errorf("%s is already locked", key) 197 } 198 fake.rbdImageLocks[key] = true 199 return nil 200 } 201 202 func (fake *fakeDiskManager) Defencing(r rbdMounter, nodeName string) error { 203 fake.mutex.Lock() 204 defer fake.mutex.Unlock() 205 key := fmt.Sprintf("%s/%s", r.Pool, r.Image) 206 isLocked, ok := fake.rbdImageLocks[key] 207 if !ok || !isLocked { 208 // not expected in testing 209 return fmt.Errorf("%s is not locked", key) 210 } 211 delete(fake.rbdImageLocks, key) 212 return nil 213 } 214 215 func (fake *fakeDiskManager) IsLocked(r rbdMounter, nodeName string) (bool, error) { 216 fake.mutex.RLock() 217 defer fake.mutex.RUnlock() 218 key := fmt.Sprintf("%s/%s", r.Pool, r.Image) 219 isLocked, ok := fake.rbdImageLocks[key] 220 return ok && isLocked, nil 221 } 222 223 func (fake *fakeDiskManager) ExpandImage(rbdExpander *rbdVolumeExpander, oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error) { 224 return resource.Quantity{}, fmt.Errorf("not implemented") 225 } 226 227 // checkMounterLog checks fakeMounter must have expected logs, and the last action msut equal to expectedAction. 228 func checkMounterLog(t *testing.T, fakeMounter *mount.FakeMounter, expected int, expectedAction mount.FakeAction) { 229 log := fakeMounter.GetLog() 230 if len(log) != expected { 231 t.Fatalf("fakeMounter should have %d logs, actual: %d", expected, len(log)) 232 } 233 lastIndex := len(log) - 1 234 lastAction := log[lastIndex] 235 if !reflect.DeepEqual(expectedAction, lastAction) { 236 t.Fatalf("fakeMounter.Log[%d] should be %#v, not: %#v", lastIndex, expectedAction, lastAction) 237 } 238 } 239 240 func doTestPlugin(t *testing.T, c *testcase) { 241 fakeVolumeHost := volumetest.NewFakeKubeletVolumeHost(t, c.root, nil, nil) 242 plugMgr := volume.VolumePluginMgr{} 243 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, fakeVolumeHost) 244 plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd") 245 if err != nil { 246 t.Fatal("Can't find the plugin by name") 247 } 248 fakeMounter := fakeVolumeHost.GetMounter(plug.GetPluginName()).(*mount.FakeMounter) 249 fakeNodeName := types.NodeName("localhost") 250 fdm := newFakeDiskManager() 251 252 // attacher 253 attacher, err := plug.(*rbdPlugin).newAttacherInternal(fdm) 254 if err != nil { 255 t.Errorf("Failed to make a new Attacher: %v", err) 256 } 257 deviceAttachPath, err := attacher.Attach(c.spec, fakeNodeName) 258 if err != nil { 259 t.Fatal(err) 260 } 261 devicePath, err := attacher.WaitForAttach(c.spec, deviceAttachPath, c.pod, time.Second*10) 262 if err != nil { 263 t.Fatal(err) 264 } 265 if devicePath != c.expectedDevicePath { 266 t.Errorf("Unexpected path, expected %q, not: %q", c.expectedDevicePath, devicePath) 267 } 268 deviceMountPath, err := attacher.GetDeviceMountPath(c.spec) 269 if err != nil { 270 t.Fatal(err) 271 } 272 if deviceMountPath != c.expectedDeviceMountPath { 273 t.Errorf("Unexpected mount path, expected %q, not: %q", c.expectedDeviceMountPath, deviceMountPath) 274 } 275 err = attacher.MountDevice(c.spec, devicePath, deviceMountPath, volume.DeviceMounterArgs{}) 276 if err != nil { 277 t.Fatal(err) 278 } 279 if _, err := os.Stat(deviceMountPath); err != nil { 280 if os.IsNotExist(err) { 281 t.Errorf("Attacher.MountDevice() failed, device mount path not created: %s", deviceMountPath) 282 } else { 283 t.Errorf("Attacher.MountDevice() failed: %v", err) 284 } 285 } 286 loggedSource, err := getLoggedSource(devicePath) 287 if err != nil { 288 t.Fatal(err) 289 } 290 checkMounterLog(t, fakeMounter, 1, mount.FakeAction{Action: "mount", Target: c.expectedDeviceMountPath, Source: loggedSource, FSType: "ext4"}) 291 292 // mounter 293 mounter, err := plug.(*rbdPlugin).newMounterInternal(c.spec, c.pod.UID, fdm, "secrets") 294 if err != nil { 295 t.Errorf("Failed to make a new Mounter: %v", err) 296 } 297 if mounter == nil { 298 t.Error("Got a nil Mounter") 299 } 300 path := mounter.GetPath() 301 if path != c.expectedPodMountPath { 302 t.Errorf("Unexpected path, expected %q, got: %q", c.expectedPodMountPath, path) 303 } 304 305 if err := mounter.SetUp(volume.MounterArgs{}); err != nil { 306 t.Errorf("Expected success, got: %v", err) 307 } 308 if _, err := os.Stat(path); err != nil { 309 if os.IsNotExist(err) { 310 t.Errorf("SetUp() failed, volume path not created: %s", path) 311 } else { 312 t.Errorf("SetUp() failed: %v", err) 313 } 314 } 315 checkMounterLog(t, fakeMounter, 2, mount.FakeAction{Action: "mount", Target: c.expectedPodMountPath, Source: loggedSource, FSType: ""}) 316 317 // unmounter 318 unmounter, err := plug.(*rbdPlugin).newUnmounterInternal(c.spec.Name(), c.pod.UID, fdm) 319 if err != nil { 320 t.Errorf("Failed to make a new Unmounter: %v", err) 321 } 322 if unmounter == nil { 323 t.Error("Got a nil Unmounter") 324 } 325 326 if err := unmounter.TearDown(); err != nil { 327 t.Errorf("Expected success, got: %v", err) 328 } 329 if _, err := os.Stat(path); err == nil { 330 t.Errorf("TearDown() failed, volume path still exists: %s", path) 331 } else if !os.IsNotExist(err) { 332 t.Errorf("TearDown() failed: %v", err) 333 } 334 checkMounterLog(t, fakeMounter, 3, mount.FakeAction{Action: "unmount", Target: c.expectedPodMountPath, Source: "", FSType: ""}) 335 336 // detacher 337 detacher, err := plug.(*rbdPlugin).newDetacherInternal(fdm) 338 if err != nil { 339 t.Errorf("Failed to make a new Attacher: %v", err) 340 } 341 err = detacher.UnmountDevice(deviceMountPath) 342 if err != nil { 343 t.Fatalf("Detacher.UnmountDevice failed to unmount %s", deviceMountPath) 344 } 345 checkMounterLog(t, fakeMounter, 4, mount.FakeAction{Action: "unmount", Target: c.expectedDeviceMountPath, Source: "", FSType: ""}) 346 err = detacher.Detach(deviceMountPath, fakeNodeName) 347 if err != nil { 348 t.Fatalf("Detacher.Detach failed to detach %s from %s", deviceMountPath, fakeNodeName) 349 } 350 } 351 352 type testcase struct { 353 spec *volume.Spec 354 root string 355 pod *v1.Pod 356 expectedDevicePath string 357 expectedDeviceMountPath string 358 expectedPodMountPath string 359 } 360 361 func TestPlugin(t *testing.T) { 362 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 363 if err != nil { 364 t.Fatalf("error creating temp dir: %v", err) 365 } 366 defer os.RemoveAll(tmpDir) 367 tmpDir, err = filepath.EvalSymlinks(tmpDir) 368 if err != nil { 369 t.Fatal(err) 370 } 371 372 expectedDevicePath := "/dev/rbd0" 373 if runtime.GOOS == "windows" { 374 // Windows expects Disk Numbers. 375 expectedDevicePath = "0" 376 } 377 378 podUID := uuid.NewUUID() 379 var cases []*testcase 380 cases = append(cases, &testcase{ 381 spec: volume.NewSpecFromVolume(&v1.Volume{ 382 Name: "vol1", 383 VolumeSource: v1.VolumeSource{ 384 RBD: &v1.RBDVolumeSource{ 385 CephMonitors: []string{"a", "b"}, 386 RBDPool: "pool1", 387 RBDImage: "image1", 388 FSType: "ext4", 389 }, 390 }, 391 }), 392 root: tmpDir, 393 pod: &v1.Pod{ 394 ObjectMeta: metav1.ObjectMeta{ 395 Name: "testpod", 396 Namespace: "testns", 397 UID: podUID, 398 }, 399 }, 400 expectedDevicePath: expectedDevicePath, 401 expectedDeviceMountPath: filepath.Join(tmpDir, "plugins/kubernetes.io/rbd/mounts/pool1-image-image1"), 402 expectedPodMountPath: filepath.Join(tmpDir, "pods", string(podUID), "volumes/kubernetes.io~rbd/vol1"), 403 }) 404 cases = append(cases, &testcase{ 405 spec: volume.NewSpecFromPersistentVolume(&v1.PersistentVolume{ 406 ObjectMeta: metav1.ObjectMeta{ 407 Name: "vol2", 408 }, 409 Spec: v1.PersistentVolumeSpec{ 410 PersistentVolumeSource: v1.PersistentVolumeSource{ 411 RBD: &v1.RBDPersistentVolumeSource{ 412 CephMonitors: []string{"a", "b"}, 413 RBDPool: "pool2", 414 RBDImage: "image2", 415 FSType: "ext4", 416 }, 417 }, 418 AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany}, 419 }, 420 }, false), 421 root: tmpDir, 422 pod: &v1.Pod{ 423 ObjectMeta: metav1.ObjectMeta{ 424 Name: "testpod", 425 Namespace: "testns", 426 UID: podUID, 427 }, 428 }, 429 expectedDevicePath: expectedDevicePath, 430 expectedDeviceMountPath: filepath.Join(tmpDir, "plugins/kubernetes.io/rbd/mounts/pool2-image-image2"), 431 expectedPodMountPath: filepath.Join(tmpDir, "pods", string(podUID), "volumes/kubernetes.io~rbd/vol2"), 432 }) 433 434 for i := 0; i < len(cases); i++ { 435 doTestPlugin(t, cases[i]) 436 } 437 } 438 439 func TestPersistentClaimReadOnlyFlag(t *testing.T) { 440 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 441 if err != nil { 442 t.Fatalf("error creating temp dir: %v", err) 443 } 444 defer os.RemoveAll(tmpDir) 445 446 pv := &v1.PersistentVolume{ 447 ObjectMeta: metav1.ObjectMeta{ 448 Name: "pvA", 449 }, 450 Spec: v1.PersistentVolumeSpec{ 451 PersistentVolumeSource: v1.PersistentVolumeSource{ 452 RBD: &v1.RBDPersistentVolumeSource{ 453 CephMonitors: []string{"a", "b"}, 454 RBDImage: "bar", 455 FSType: "ext4", 456 }, 457 }, 458 ClaimRef: &v1.ObjectReference{ 459 Name: "claimA", 460 }, 461 }, 462 } 463 464 claim := &v1.PersistentVolumeClaim{ 465 ObjectMeta: metav1.ObjectMeta{ 466 Name: "claimA", 467 Namespace: "nsA", 468 }, 469 Spec: v1.PersistentVolumeClaimSpec{ 470 VolumeName: "pvA", 471 }, 472 Status: v1.PersistentVolumeClaimStatus{ 473 Phase: v1.ClaimBound, 474 }, 475 } 476 477 client := fake.NewSimpleClientset(pv, claim) 478 479 plugMgr := volume.VolumePluginMgr{} 480 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, client, nil)) 481 plug, _ := plugMgr.FindPluginByName(rbdPluginName) 482 483 // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes 484 spec := volume.NewSpecFromPersistentVolume(pv, true) 485 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 486 mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) 487 if mounter == nil { 488 t.Fatalf("Got a nil Mounter") 489 } 490 491 if !mounter.GetAttributes().ReadOnly { 492 t.Errorf("Expected true for mounter.IsReadOnly") 493 } 494 } 495 496 func TestGetSecretNameAndNamespace(t *testing.T) { 497 secretName := "test-secret-name" 498 secretNamespace := "test-secret-namespace" 499 500 volSpec := &volume.Spec{ 501 PersistentVolume: &v1.PersistentVolume{ 502 Spec: v1.PersistentVolumeSpec{ 503 PersistentVolumeSource: v1.PersistentVolumeSource{ 504 RBD: &v1.RBDPersistentVolumeSource{ 505 CephMonitors: []string{"a", "b"}, 506 RBDImage: "bar", 507 FSType: "ext4", 508 }, 509 }, 510 }, 511 }, 512 } 513 514 secretRef := new(v1.SecretReference) 515 secretRef.Name = secretName 516 secretRef.Namespace = secretNamespace 517 volSpec.PersistentVolume.Spec.PersistentVolumeSource.RBD.SecretRef = secretRef 518 519 foundSecretName, foundSecretNamespace, err := getSecretNameAndNamespace(volSpec, "default") 520 if err != nil { 521 t.Errorf("getSecretNameAndNamespace failed to get Secret's name and namespace: %v", err) 522 } 523 if strings.Compare(secretName, foundSecretName) != 0 || strings.Compare(secretNamespace, foundSecretNamespace) != 0 { 524 t.Errorf("getSecretNameAndNamespace returned incorrect values, expected %s and %s but got %s and %s", secretName, secretNamespace, foundSecretName, foundSecretNamespace) 525 } 526 } 527 528 // https://github.com/kubernetes/kubernetes/issues/57744 529 func TestGetDeviceMountPath(t *testing.T) { 530 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 531 if err != nil { 532 t.Fatalf("error creating temp dir: %v", err) 533 } 534 defer os.RemoveAll(tmpDir) 535 536 fakeVolumeHost := volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil) 537 plugMgr := volume.VolumePluginMgr{} 538 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, fakeVolumeHost) 539 plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd") 540 if err != nil { 541 t.Errorf("Can't find the plugin by name") 542 } 543 fdm := newFakeDiskManager() 544 545 // attacher 546 attacher, err := plug.(*rbdPlugin).newAttacherInternal(fdm) 547 if err != nil { 548 t.Errorf("Failed to make a new Attacher: %v", err) 549 } 550 551 pool, image := "pool", "image" 552 spec := volume.NewSpecFromVolume(&v1.Volume{ 553 Name: "vol", 554 VolumeSource: v1.VolumeSource{ 555 RBD: &v1.RBDVolumeSource{ 556 CephMonitors: []string{"a", "b"}, 557 RBDPool: pool, 558 RBDImage: image, 559 FSType: "ext4", 560 }, 561 }, 562 }) 563 564 deprecatedDir := filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/rbd/rbd/%s-image-%s", pool, image)) 565 canonicalDir := filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/rbd/mounts/%s-image-%s", pool, image)) 566 567 type testCase struct { 568 deprecated bool 569 targetPath string 570 } 571 for _, c := range []testCase{ 572 {false, canonicalDir}, 573 {true, deprecatedDir}, 574 } { 575 if c.deprecated { 576 // This is a deprecated device mount path, we create it, 577 // and hope attacher.GetDeviceMountPath return c.targetPath. 578 if err := os.MkdirAll(c.targetPath, 0700); err != nil { 579 t.Fatalf("Create deprecated mount path failed: %v", err) 580 } 581 } 582 mountPath, err := attacher.GetDeviceMountPath(spec) 583 if err != nil { 584 t.Fatalf("GetDeviceMountPath failed: %v", err) 585 } 586 if mountPath != c.targetPath { 587 t.Errorf("Mismatch device mount path: wanted %s, got %s", c.targetPath, mountPath) 588 } 589 } 590 } 591 592 // https://github.com/kubernetes/kubernetes/issues/57744 593 func TestConstructVolumeSpec(t *testing.T) { 594 if runtime.GOOS == "darwin" { 595 t.Skipf("TestConstructVolumeSpec is not supported on GOOS=%s", runtime.GOOS) 596 } 597 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 598 if err != nil { 599 t.Fatalf("error creating temp dir: %v", err) 600 } 601 defer os.RemoveAll(tmpDir) 602 603 fakeVolumeHost := volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil) 604 plugMgr := volume.VolumePluginMgr{} 605 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, fakeVolumeHost) 606 plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd") 607 if err != nil { 608 t.Fatal("Can't find the plugin by name") 609 } 610 fakeMounter := fakeVolumeHost.GetMounter(plug.GetPluginName()).(*mount.FakeMounter) 611 612 pool, image, volumeName := "pool", "image", "vol" 613 podMountPath := filepath.Join(tmpDir, "pods/pod123/volumes/kubernetes.io~rbd", volumeName) 614 deprecatedDir := filepath.Join(tmpDir, "plugins/kubernetes.io/rbd/rbd", fmt.Sprintf("%s-image-%s", pool, image)) 615 canonicalDir := filepath.Join(tmpDir, "plugins/kubernetes.io/rbd/mounts", fmt.Sprintf("%s-image-%s", pool, image)) 616 617 type testCase struct { 618 volumeName string 619 targetPath string 620 } 621 622 for _, c := range []testCase{ 623 {"vol", canonicalDir}, 624 {"vol", deprecatedDir}, 625 } { 626 if err := os.MkdirAll(c.targetPath, 0700); err != nil { 627 t.Fatalf("Create mount path %s failed: %v", c.targetPath, err) 628 } 629 if err = fakeMounter.Mount("/dev/rbd0", c.targetPath, "fake", nil); err != nil { 630 t.Fatalf("Mount %s to %s failed: %v", c.targetPath, podMountPath, err) 631 } 632 if err = fakeMounter.Mount(c.targetPath, podMountPath, "fake", []string{"bind"}); err != nil { 633 t.Fatalf("Mount %s to %s failed: %v", c.targetPath, podMountPath, err) 634 } 635 rec, err := plug.ConstructVolumeSpec(c.volumeName, podMountPath) 636 if err != nil { 637 t.Errorf("ConstructVolumeSpec failed: %v", err) 638 } else { 639 if rec.Spec.Volume.RBD.RBDPool != pool { 640 t.Errorf("Mismatch rbd pool: wanted %s, got %s", pool, rec.Spec.Volume.RBD.RBDPool) 641 } 642 if rec.Spec.Volume.RBD.RBDImage != image { 643 t.Fatalf("Mismatch rbd image: wanted %s, got %s", image, rec.Spec.Volume.RBD.RBDImage) 644 } 645 } 646 if err = fakeMounter.Unmount(podMountPath); err != nil { 647 t.Fatalf("Unmount pod path %s failed: %v", podMountPath, err) 648 } 649 if err = fakeMounter.Unmount(c.targetPath); err != nil { 650 t.Fatalf("Unmount device path %s failed: %v", c.targetPath, err) 651 } 652 } 653 } 654 655 func TestGetAccessModes(t *testing.T) { 656 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 657 if err != nil { 658 t.Fatalf("error creating temp dir: %v", err) 659 } 660 defer os.RemoveAll(tmpDir) 661 662 plugMgr := volume.VolumePluginMgr{} 663 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)) 664 665 plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/rbd") 666 if err != nil { 667 t.Errorf("Can't find the plugin by name") 668 } 669 modes := plug.GetAccessModes() 670 for _, v := range modes { 671 if !volumetest.ContainsAccessMode(modes, v) { 672 t.Errorf("Expected AccessModeTypes: %s", v) 673 } 674 } 675 } 676 677 func TestRequiresRemount(t *testing.T) { 678 tmpDir, _ := utiltesting.MkTmpdir("rbd_test") 679 defer os.RemoveAll(tmpDir) 680 plugMgr := volume.VolumePluginMgr{} 681 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)) 682 plug, _ := plugMgr.FindPluginByName("kubernetes.io/rbd") 683 has := plug.RequiresRemount(nil) 684 if has { 685 t.Errorf("Expected RequiresRemount to be false, got %t", has) 686 } 687 } 688 689 func TestGetRbdImageSize(t *testing.T) { 690 for i, c := range []struct { 691 Output string 692 TargetSize int 693 }{ 694 { 695 Output: `{"name":"kubernetes-dynamic-pvc-18e7a4d9-050d-11e9-b905-548998f3478f","size":10737418240,"objects":2560,"order":22,"object_size":4194304,"block_name_prefix":"rbd_data.9f4ff7238e1f29","format":2}`, 696 TargetSize: 10240, 697 }, 698 { 699 Output: `{"name":"kubernetes-dynamic-pvc-070635bf-e33f-11e8-aab7-548998f3478f","size":1073741824,"objects":256,"order":22,"object_size":4194304,"block_name_prefix":"rbd_data.670ac4238e1f29","format":2}`, 700 TargetSize: 1024, 701 }, 702 } { 703 size, err := getRbdImageSize([]byte(c.Output)) 704 if err != nil { 705 t.Errorf("Case %d: getRbdImageSize failed: %v", i, err) 706 continue 707 } 708 if size != c.TargetSize { 709 t.Errorf("Case %d: unexpected size, wanted %d, got %d", i, c.TargetSize, size) 710 } 711 } 712 } 713 714 func TestGetRbdImageInfo(t *testing.T) { 715 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 716 if err != nil { 717 t.Fatalf("error creating temp dir: %v", err) 718 } 719 defer os.RemoveAll(tmpDir) 720 721 for i, c := range []struct { 722 DeviceMountPath string 723 TargetRbdImageInfo *rbdImageInfo 724 }{ 725 { 726 DeviceMountPath: fmt.Sprintf("%s/plugins/kubernetes.io/rbd/rbd/pool1-image-image1", tmpDir), 727 TargetRbdImageInfo: &rbdImageInfo{pool: "pool1", name: "image1"}, 728 }, 729 { 730 DeviceMountPath: fmt.Sprintf("%s/plugins/kubernetes.io/rbd/mounts/pool2-image-image2", tmpDir), 731 TargetRbdImageInfo: &rbdImageInfo{pool: "pool2", name: "image2"}, 732 }, 733 } { 734 rbdImageInfo, err := getRbdImageInfo(c.DeviceMountPath) 735 if err != nil { 736 t.Errorf("Case %d: getRbdImageInfo failed: %v", i, err) 737 continue 738 } 739 if !reflect.DeepEqual(rbdImageInfo, c.TargetRbdImageInfo) { 740 t.Errorf("Case %d: unexpected RbdImageInfo, wanted %v, got %v", i, c.TargetRbdImageInfo, rbdImageInfo) 741 } 742 } 743 } 744 745 func TestUnsupportedVolumeHost(t *testing.T) { 746 tmpDir, err := utiltesting.MkTmpdir("rbd_test") 747 if err != nil { 748 t.Fatalf("error creating temp dir: %v", err) 749 } 750 defer os.RemoveAll(tmpDir) 751 752 plugMgr := volume.VolumePluginMgr{} 753 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil)) 754 755 plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd") 756 if err != nil { 757 t.Fatal("Can't find the plugin by name") 758 } 759 760 _, err = plug.ConstructVolumeSpec("", "") 761 if err == nil { 762 t.Errorf("Expected failure constructing volume spec with unsupported VolumeHost") 763 } 764 }