k8s.io/kubernetes@v1.29.3/pkg/volume/fc/fc_test.go (about) 1 /* 2 Copyright 2015 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 fc 18 19 import ( 20 "os" 21 "path/filepath" 22 "runtime" 23 "strconv" 24 "strings" 25 "testing" 26 27 "k8s.io/mount-utils" 28 testingexec "k8s.io/utils/exec/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 "k8s.io/client-go/kubernetes/fake" 34 utiltesting "k8s.io/client-go/util/testing" 35 "k8s.io/kubernetes/pkg/volume" 36 volumetest "k8s.io/kubernetes/pkg/volume/testing" 37 ) 38 39 func TestCanSupport(t *testing.T) { 40 tmpDir, err := utiltesting.MkTmpdir("fc_test") 41 if err != nil { 42 t.Fatalf("error creating temp dir: %v", err) 43 } 44 defer os.RemoveAll(tmpDir) 45 46 plugMgr := volume.VolumePluginMgr{} 47 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil)) 48 49 plug, err := plugMgr.FindPluginByName("kubernetes.io/fc") 50 if err != nil { 51 t.Fatal("Can't find the plugin by name") 52 } 53 if plug.GetPluginName() != "kubernetes.io/fc" { 54 t.Errorf("Wrong name: %s", plug.GetPluginName()) 55 } 56 if plug.CanSupport(&volume.Spec{}) { 57 t.Errorf("Expected false") 58 } 59 if plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{}}}) { 60 t.Errorf("Expected false") 61 } 62 if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{FC: &v1.FCVolumeSource{}}}}) { 63 t.Errorf("Expected true") 64 } 65 if plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{}}}) { 66 t.Errorf("Expected false") 67 } 68 if plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{}}}}) { 69 t.Errorf("Expected false") 70 } 71 if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{FC: &v1.FCVolumeSource{}}}}}) { 72 t.Errorf("Expected true") 73 } 74 } 75 76 func TestGetAccessModes(t *testing.T) { 77 tmpDir, err := utiltesting.MkTmpdir("fc_test") 78 if err != nil { 79 t.Fatalf("error creating temp dir: %v", err) 80 } 81 defer os.RemoveAll(tmpDir) 82 83 plugMgr := volume.VolumePluginMgr{} 84 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil)) 85 86 plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/fc") 87 if err != nil { 88 t.Errorf("Can't find the plugin by name") 89 } 90 if !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteOnce) || !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadOnlyMany) { 91 t.Errorf("Expected two AccessModeTypes: %s and %s", v1.ReadWriteOnce, v1.ReadOnlyMany) 92 } 93 } 94 95 type fakeDiskManager struct { 96 tmpDir string 97 attachCalled bool 98 detachCalled bool 99 } 100 101 func newFakeDiskManager() *fakeDiskManager { 102 return &fakeDiskManager{ 103 tmpDir: utiltesting.MkTmpdirOrDie("fc_test"), 104 } 105 } 106 107 func (fake *fakeDiskManager) Cleanup() { 108 os.RemoveAll(fake.tmpDir) 109 } 110 111 func (fake *fakeDiskManager) MakeGlobalPDName(disk fcDisk) string { 112 return fake.tmpDir 113 } 114 115 func (fake *fakeDiskManager) MakeGlobalVDPDName(disk fcDisk) string { 116 return fake.tmpDir 117 } 118 119 func (fake *fakeDiskManager) AttachDisk(b fcDiskMounter) (string, error) { 120 globalPath := b.manager.MakeGlobalPDName(*b.fcDisk) 121 err := os.MkdirAll(globalPath, 0750) 122 if err != nil { 123 return "", err 124 } 125 fake.attachCalled = true 126 return "", nil 127 } 128 129 func (fake *fakeDiskManager) DetachDisk(c fcDiskUnmounter, mntPath string) error { 130 globalPath := c.manager.MakeGlobalPDName(*c.fcDisk) 131 err := os.RemoveAll(globalPath) 132 if err != nil { 133 return err 134 } 135 fake.detachCalled = true 136 return nil 137 } 138 139 func (fake *fakeDiskManager) DetachBlockFCDisk(c fcDiskUnmapper, mapPath, devicePath string) error { 140 err := os.RemoveAll(mapPath) 141 if err != nil { 142 return err 143 } 144 fake.detachCalled = true 145 return nil 146 } 147 148 func doTestPlugin(t *testing.T, spec *volume.Spec) { 149 tmpDir, err := utiltesting.MkTmpdir("fc_test") 150 if err != nil { 151 t.Fatalf("error creating temp dir: %v", err) 152 } 153 defer os.RemoveAll(tmpDir) 154 155 plugMgr := volume.VolumePluginMgr{} 156 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil)) 157 158 plug, err := plugMgr.FindPluginByName("kubernetes.io/fc") 159 if err != nil { 160 t.Errorf("Can't find the plugin by name") 161 } 162 fakeManager := newFakeDiskManager() 163 defer fakeManager.Cleanup() 164 fakeMounter := mount.NewFakeMounter(nil) 165 fakeExec := &testingexec.FakeExec{} 166 mounter, err := plug.(*fcPlugin).newMounterInternal(spec, types.UID("poduid"), fakeManager, fakeMounter, fakeExec) 167 if err != nil { 168 t.Errorf("Failed to make a new Mounter: %v", err) 169 } 170 if mounter == nil { 171 t.Errorf("Got a nil Mounter: %v", err) 172 } 173 174 path := mounter.GetPath() 175 expectedPath := filepath.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~fc/vol1") 176 if path != expectedPath { 177 t.Errorf("Unexpected path, expected %q, got: %q", expectedPath, path) 178 } 179 180 if err := mounter.SetUp(volume.MounterArgs{}); err != nil { 181 t.Errorf("Expected success, got: %v", err) 182 } 183 if _, err := os.Stat(path); err != nil { 184 if os.IsNotExist(err) { 185 t.Errorf("SetUp() failed, volume path not created: %s", path) 186 } else { 187 t.Errorf("SetUp() failed: %v", err) 188 } 189 } 190 191 fakeManager2 := newFakeDiskManager() 192 defer fakeManager2.Cleanup() 193 unmounter, err := plug.(*fcPlugin).newUnmounterInternal("vol1", types.UID("poduid"), fakeManager2, fakeMounter, fakeExec) 194 if err != nil { 195 t.Errorf("Failed to make a new Unmounter: %v", err) 196 } 197 if unmounter == nil { 198 t.Errorf("Got a nil Unmounter: %v", err) 199 } 200 201 if err := unmounter.TearDown(); err != nil { 202 t.Errorf("Expected success, got: %v", err) 203 } 204 if _, err := os.Stat(path); err == nil { 205 t.Errorf("TearDown() failed, volume path still exists: %s", path) 206 } else if !os.IsNotExist(err) { 207 t.Errorf("TearDown() failed: %v", err) 208 } 209 } 210 211 func doTestPluginNilMounter(t *testing.T, spec *volume.Spec) { 212 tmpDir, err := utiltesting.MkTmpdir("fc_test") 213 if err != nil { 214 t.Fatalf("error creating temp dir: %v", err) 215 } 216 defer os.RemoveAll(tmpDir) 217 218 plugMgr := volume.VolumePluginMgr{} 219 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil)) 220 221 plug, err := plugMgr.FindPluginByName("kubernetes.io/fc") 222 if err != nil { 223 t.Errorf("Can't find the plugin by name") 224 } 225 fakeManager := newFakeDiskManager() 226 defer fakeManager.Cleanup() 227 fakeMounter := mount.NewFakeMounter(nil) 228 fakeExec := &testingexec.FakeExec{} 229 mounter, err := plug.(*fcPlugin).newMounterInternal(spec, types.UID("poduid"), fakeManager, fakeMounter, fakeExec) 230 if err == nil { 231 t.Errorf("Error failed to make a new Mounter is expected: %v", err) 232 } 233 if mounter != nil { 234 t.Errorf("A nil Mounter is expected: %v", err) 235 } 236 } 237 238 func TestPluginVolume(t *testing.T) { 239 lun := int32(0) 240 vol := &v1.Volume{ 241 Name: "vol1", 242 VolumeSource: v1.VolumeSource{ 243 FC: &v1.FCVolumeSource{ 244 TargetWWNs: []string{"500a0981891b8dc5"}, 245 FSType: "ext4", 246 Lun: &lun, 247 }, 248 }, 249 } 250 doTestPlugin(t, volume.NewSpecFromVolume(vol)) 251 } 252 253 func TestPluginPersistentVolume(t *testing.T) { 254 lun := int32(0) 255 fs := v1.PersistentVolumeFilesystem 256 vol := &v1.PersistentVolume{ 257 ObjectMeta: metav1.ObjectMeta{ 258 Name: "vol1", 259 }, 260 Spec: v1.PersistentVolumeSpec{ 261 PersistentVolumeSource: v1.PersistentVolumeSource{ 262 FC: &v1.FCVolumeSource{ 263 TargetWWNs: []string{"500a0981891b8dc5"}, 264 FSType: "ext4", 265 Lun: &lun, 266 }, 267 }, 268 VolumeMode: &fs, 269 }, 270 } 271 doTestPlugin(t, volume.NewSpecFromPersistentVolume(vol, false)) 272 } 273 274 func TestPluginVolumeWWIDs(t *testing.T) { 275 vol := &v1.Volume{ 276 Name: "vol1", 277 VolumeSource: v1.VolumeSource{ 278 FC: &v1.FCVolumeSource{ 279 WWIDs: []string{"3600508b400105e210000900000490000"}, 280 FSType: "ext4", 281 }, 282 }, 283 } 284 doTestPlugin(t, volume.NewSpecFromVolume(vol)) 285 } 286 287 func TestPluginPersistentVolumeWWIDs(t *testing.T) { 288 fs := v1.PersistentVolumeFilesystem 289 vol := &v1.PersistentVolume{ 290 ObjectMeta: metav1.ObjectMeta{ 291 Name: "vol1", 292 }, 293 Spec: v1.PersistentVolumeSpec{ 294 PersistentVolumeSource: v1.PersistentVolumeSource{ 295 FC: &v1.FCVolumeSource{ 296 WWIDs: []string{"3600508b400105e21 000900000490000"}, 297 FSType: "ext4", 298 }, 299 }, 300 VolumeMode: &fs, 301 }, 302 } 303 doTestPlugin(t, volume.NewSpecFromPersistentVolume(vol, false)) 304 } 305 306 func TestPluginVolumeNoDiskInfo(t *testing.T) { 307 vol := &v1.Volume{ 308 Name: "vol1", 309 VolumeSource: v1.VolumeSource{ 310 FC: &v1.FCVolumeSource{ 311 FSType: "ext4", 312 }, 313 }, 314 } 315 doTestPluginNilMounter(t, volume.NewSpecFromVolume(vol)) 316 } 317 318 func TestPluginPersistentVolumeNoDiskInfo(t *testing.T) { 319 fs := v1.PersistentVolumeFilesystem 320 vol := &v1.PersistentVolume{ 321 ObjectMeta: metav1.ObjectMeta{ 322 Name: "vol1", 323 }, 324 Spec: v1.PersistentVolumeSpec{ 325 PersistentVolumeSource: v1.PersistentVolumeSource{ 326 FC: &v1.FCVolumeSource{ 327 FSType: "ext4", 328 }, 329 }, 330 VolumeMode: &fs, 331 }, 332 } 333 doTestPluginNilMounter(t, volume.NewSpecFromPersistentVolume(vol, false)) 334 } 335 336 func TestPersistentClaimReadOnlyFlag(t *testing.T) { 337 tmpDir, err := utiltesting.MkTmpdir("fc_test") 338 if err != nil { 339 t.Fatalf("error creating temp dir: %v", err) 340 } 341 defer os.RemoveAll(tmpDir) 342 343 lun := int32(0) 344 fs := v1.PersistentVolumeFilesystem 345 pv := &v1.PersistentVolume{ 346 ObjectMeta: metav1.ObjectMeta{ 347 Name: "pvA", 348 }, 349 Spec: v1.PersistentVolumeSpec{ 350 PersistentVolumeSource: v1.PersistentVolumeSource{ 351 FC: &v1.FCVolumeSource{ 352 TargetWWNs: []string{"some_wwn"}, 353 FSType: "ext4", 354 Lun: &lun, 355 }, 356 }, 357 ClaimRef: &v1.ObjectReference{ 358 Name: "claimA", 359 }, 360 VolumeMode: &fs, 361 }, 362 } 363 364 claim := &v1.PersistentVolumeClaim{ 365 ObjectMeta: metav1.ObjectMeta{ 366 Name: "claimA", 367 Namespace: "nsA", 368 }, 369 Spec: v1.PersistentVolumeClaimSpec{ 370 VolumeName: "pvA", 371 VolumeMode: &fs, 372 }, 373 Status: v1.PersistentVolumeClaimStatus{ 374 Phase: v1.ClaimBound, 375 }, 376 } 377 378 client := fake.NewSimpleClientset(pv, claim) 379 380 plugMgr := volume.VolumePluginMgr{} 381 plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, client, nil)) 382 plug, _ := plugMgr.FindPluginByName(fcPluginName) 383 384 // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes 385 spec := volume.NewSpecFromPersistentVolume(pv, true) 386 pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} 387 mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) 388 if mounter == nil { 389 t.Fatalf("Got a nil Mounter") 390 } 391 392 if !mounter.GetAttributes().ReadOnly { 393 t.Errorf("Expected true for mounter.IsReadOnly") 394 } 395 } 396 397 func Test_getWwnsLun(t *testing.T) { 398 num := int32(0) 399 fc := &v1.FCVolumeSource{ 400 TargetWWNs: []string{"500a0981891b8dc5"}, 401 FSType: "ext4", 402 Lun: &num, 403 } 404 wwn, lun, _, err := getWwnsLunWwids(fc) 405 // if no wwn and lun, exit 406 if (len(wwn) == 0 && lun != "0") || err != nil { 407 t.Errorf("no fc disk found") 408 } 409 } 410 411 func Test_getWwids(t *testing.T) { 412 fc := &v1.FCVolumeSource{ 413 FSType: "ext4", 414 WWIDs: []string{"3600508b400105e210000900000490000"}, 415 } 416 _, _, wwid, err := getWwnsLunWwids(fc) 417 // if no wwn and lun, exit 418 if len(wwid) == 0 || err != nil { 419 t.Errorf("no fc disk found") 420 } 421 } 422 423 func Test_getWwnsLunWwidsError(t *testing.T) { 424 fc := &v1.FCVolumeSource{ 425 FSType: "ext4", 426 } 427 wwn, lun, wwid, err := getWwnsLunWwids(fc) 428 // expected no wwn and lun and wwid 429 if (len(wwn) != 0 && lun != "" && len(wwid) != 0) || err == nil { 430 t.Errorf("unexpected fc disk found") 431 } 432 } 433 434 func Test_ConstructVolumeSpec(t *testing.T) { 435 if runtime.GOOS == "darwin" { 436 t.Skipf("Test_ConstructVolumeSpec is not supported on GOOS=%s", runtime.GOOS) 437 } 438 fm := mount.NewFakeMounter( 439 []mount.MountPoint{ 440 {Device: "/dev/sdb", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~fc/fc-in-pod1"}, 441 {Device: "/dev/sdb", Path: "/var/lib/kubelet/plugins/kubernetes.io/fc/50060e801049cfd1-lun-0"}, 442 {Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~fc/fc-in-pod2"}, 443 {Device: "/dev/sdc", Path: "/var/lib/kubelet/plugins/kubernetes.io/fc/volumeDevices/3600508b400105e210000900000490000"}, 444 }) 445 mountPaths := []string{ 446 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~fc/fc-in-pod1", 447 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~fc/fc-in-pod2", 448 } 449 for _, path := range mountPaths { 450 refs, err := fm.GetMountRefs(path) 451 if err != nil { 452 t.Errorf("couldn't get mountrefs. err: %v", err) 453 } 454 var globalPDPath string 455 for _, ref := range refs { 456 if strings.Contains(ref, "kubernetes.io/fc") { 457 globalPDPath = ref 458 break 459 } 460 } 461 if len(globalPDPath) == 0 { 462 t.Errorf("couldn't fetch mountrefs") 463 } 464 arr := strings.Split(globalPDPath, "/") 465 if len(arr) < 1 { 466 t.Errorf("failed to retrieve volume plugin information from globalPDPath: %v", globalPDPath) 467 } 468 volumeInfo := arr[len(arr)-1] 469 if strings.Contains(volumeInfo, "-lun-") { 470 wwnLun := strings.Split(volumeInfo, "-lun-") 471 if len(wwnLun) < 2 { 472 t.Errorf("failed to retrieve TargetWWN and Lun. volumeInfo is invalid: %v", volumeInfo) 473 } 474 lun, _ := strconv.Atoi(wwnLun[1]) 475 lun32 := int32(lun) 476 if wwnLun[0] != "50060e801049cfd1" || lun32 != 0 { 477 t.Errorf("failed to retrieve TargetWWN and Lun") 478 } 479 } else { 480 if volumeInfo != "3600508b400105e210000900000490000" { 481 t.Errorf("failed to retrieve WWIDs") 482 } 483 } 484 } 485 } 486 487 func Test_ConstructVolumeSpecNoRefs(t *testing.T) { 488 fm := mount.NewFakeMounter( 489 []mount.MountPoint{ 490 {Device: "/dev/sdd", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~fc/fc-in-pod1"}, 491 }) 492 mountPaths := []string{ 493 "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~fc/fc-in-pod1", 494 } 495 for _, path := range mountPaths { 496 refs, _ := fm.GetMountRefs(path) 497 var globalPDPath string 498 for _, ref := range refs { 499 if strings.Contains(ref, "kubernetes.io/fc") { 500 globalPDPath = ref 501 break 502 } 503 } 504 if len(globalPDPath) != 0 { 505 t.Errorf("invalid globalPDPath") 506 } 507 } 508 }