k8s.io/kubernetes@v1.29.3/pkg/volume/csi/csi_block_test.go (about) 1 /* 2 Copyright 2018 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 csi 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "path/filepath" 24 "reflect" 25 "testing" 26 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/status" 29 30 api "k8s.io/api/core/v1" 31 storagev1 "k8s.io/api/storage/v1" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 fakeclient "k8s.io/client-go/kubernetes/fake" 34 "k8s.io/kubernetes/pkg/volume" 35 volumetypes "k8s.io/kubernetes/pkg/volume/util/types" 36 ) 37 38 func prepareBlockMapperTest(plug *csiPlugin, specVolumeName string, t *testing.T) (*csiBlockMapper, *volume.Spec, *api.PersistentVolume, error) { 39 registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t) 40 pv := makeTestPV(specVolumeName, 10, testDriver, testVol) 41 spec := volume.NewSpecFromPersistentVolume(pv, pv.Spec.PersistentVolumeSource.CSI.ReadOnly) 42 mapper, err := plug.NewBlockVolumeMapper( 43 spec, 44 &api.Pod{ObjectMeta: metav1.ObjectMeta{UID: testPodUID, Namespace: testns, Name: testPod}}, 45 volume.VolumeOptions{}, 46 ) 47 if err != nil { 48 return nil, nil, nil, fmt.Errorf("failed to make a new Mapper: %w", err) 49 } 50 csiMapper := mapper.(*csiBlockMapper) 51 return csiMapper, spec, pv, nil 52 } 53 54 func TestBlockMapperGetGlobalMapPath(t *testing.T) { 55 plug, tmpDir := newTestPlugin(t, nil) 56 defer os.RemoveAll(tmpDir) 57 58 // TODO (vladimirvivien) specName with slashes will not work 59 testCases := []struct { 60 name string 61 specVolumeName string 62 path string 63 }{ 64 { 65 name: "simple specName", 66 specVolumeName: "spec-0", 67 path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/%s/%s", "spec-0", "dev")), 68 }, 69 { 70 name: "specName with dots", 71 specVolumeName: "test.spec.1", 72 path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/%s/%s", "test.spec.1", "dev")), 73 }, 74 } 75 for _, tc := range testCases { 76 t.Logf("test case: %s", tc.name) 77 csiMapper, spec, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t) 78 if err != nil { 79 t.Fatalf("Failed to make a new Mapper: %v", err) 80 } 81 82 path, err := csiMapper.GetGlobalMapPath(spec) 83 if err != nil { 84 t.Errorf("mapper GetGlobalMapPath failed: %v", err) 85 } 86 87 if tc.path != path { 88 t.Errorf("expecting path %s, got %s", tc.path, path) 89 } 90 } 91 } 92 93 func TestBlockMapperGetStagingPath(t *testing.T) { 94 plug, tmpDir := newTestPlugin(t, nil) 95 defer os.RemoveAll(tmpDir) 96 97 testCases := []struct { 98 name string 99 specVolumeName string 100 path string 101 }{ 102 { 103 name: "simple specName", 104 specVolumeName: "spec-0", 105 path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/staging/%s", "spec-0")), 106 }, 107 { 108 name: "specName with dots", 109 specVolumeName: "test.spec.1", 110 path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/staging/%s", "test.spec.1")), 111 }, 112 } 113 for _, tc := range testCases { 114 t.Logf("test case: %s", tc.name) 115 csiMapper, _, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t) 116 if err != nil { 117 t.Fatalf("Failed to make a new Mapper: %v", err) 118 } 119 120 path := csiMapper.GetStagingPath() 121 122 if tc.path != path { 123 t.Errorf("expecting path %s, got %s", tc.path, path) 124 } 125 } 126 } 127 128 func TestBlockMapperGetPublishPath(t *testing.T) { 129 plug, tmpDir := newTestPlugin(t, nil) 130 defer os.RemoveAll(tmpDir) 131 132 testCases := []struct { 133 name string 134 specVolumeName string 135 path string 136 }{ 137 { 138 name: "simple specName", 139 specVolumeName: "spec-0", 140 path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/publish/%s/%s", "spec-0", testPodUID)), 141 }, 142 { 143 name: "specName with dots", 144 specVolumeName: "test.spec.1", 145 path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/publish/%s/%s", "test.spec.1", testPodUID)), 146 }, 147 } 148 for _, tc := range testCases { 149 t.Logf("test case: %s", tc.name) 150 csiMapper, _, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t) 151 if err != nil { 152 t.Fatalf("Failed to make a new Mapper: %v", err) 153 } 154 155 path := csiMapper.getPublishPath() 156 157 if tc.path != path { 158 t.Errorf("expecting path %s, got %s", tc.path, path) 159 } 160 } 161 } 162 163 func TestBlockMapperGetDeviceMapPath(t *testing.T) { 164 plug, tmpDir := newTestPlugin(t, nil) 165 defer os.RemoveAll(tmpDir) 166 167 testCases := []struct { 168 name string 169 specVolumeName string 170 path string 171 }{ 172 { 173 name: "simple specName", 174 specVolumeName: "spec-0", 175 path: filepath.Join(tmpDir, fmt.Sprintf("pods/%s/volumeDevices/kubernetes.io~csi", testPodUID)), 176 }, 177 { 178 name: "specName with dots", 179 specVolumeName: "test.spec.1", 180 path: filepath.Join(tmpDir, fmt.Sprintf("pods/%s/volumeDevices/kubernetes.io~csi", testPodUID)), 181 }, 182 } 183 for _, tc := range testCases { 184 t.Logf("test case: %s", tc.name) 185 csiMapper, _, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t) 186 if err != nil { 187 t.Fatalf("Failed to make a new Mapper: %v", err) 188 } 189 190 path, volName := csiMapper.GetPodDeviceMapPath() 191 192 if tc.path != path { 193 t.Errorf("expecting path %s, got %s", tc.path, path) 194 } 195 196 if tc.specVolumeName != volName { 197 t.Errorf("expecting volName %s, got %s", tc.specVolumeName, volName) 198 } 199 } 200 } 201 202 func TestBlockMapperSetupDevice(t *testing.T) { 203 plug, tmpDir := newTestPlugin(t, nil) 204 defer os.RemoveAll(tmpDir) 205 206 csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 207 if err != nil { 208 t.Fatalf("Failed to make a new Mapper: %v", err) 209 } 210 211 pvName := pv.GetName() 212 nodeName := string(plug.host.GetNodeName()) 213 214 csiMapper.csiClient = setupClient(t, true) 215 216 attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), string(nodeName)) 217 attachment := makeTestAttachment(attachID, nodeName, pvName) 218 attachment.Status.Attached = true 219 _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.TODO(), attachment, metav1.CreateOptions{}) 220 if err != nil { 221 t.Fatalf("failed to setup VolumeAttachment: %v", err) 222 } 223 t.Log("created attachment ", attachID) 224 225 stagingPath, err := csiMapper.SetUpDevice() 226 if err != nil { 227 t.Fatalf("mapper failed to SetupDevice: %v", err) 228 } 229 230 // Check if NodeStageVolume staged to the right path 231 svols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodeStagedVolumes() 232 svol, ok := svols[csiMapper.volumeID] 233 if !ok { 234 t.Error("csi server may not have received NodeStageVolume call") 235 } 236 if svol.Path != stagingPath { 237 t.Errorf("csi server expected device path %s, got %s", stagingPath, svol.Path) 238 } 239 } 240 241 func TestBlockMapperSetupDeviceError(t *testing.T) { 242 plug, tmpDir := newTestPlugin(t, nil) 243 defer os.RemoveAll(tmpDir) 244 245 csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 246 if err != nil { 247 t.Fatalf("Failed to make a new Mapper: %v", err) 248 } 249 250 pvName := pv.GetName() 251 nodeName := string(plug.host.GetNodeName()) 252 253 csiMapper.csiClient = setupClient(t, true) 254 fClient := csiMapper.csiClient.(*fakeCsiDriverClient) 255 fClient.nodeClient.SetNextError(status.Error(codes.InvalidArgument, "mock final error")) 256 257 attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), string(nodeName)) 258 attachment := makeTestAttachment(attachID, nodeName, pvName) 259 attachment.Status.Attached = true 260 _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.Background(), attachment, metav1.CreateOptions{}) 261 if err != nil { 262 t.Fatalf("failed to setup VolumeAttachment: %v", err) 263 } 264 t.Log("created attachment ", attachID) 265 266 stagingPath, err := csiMapper.SetUpDevice() 267 if err == nil { 268 t.Fatal("mapper unexpectedly succeeded") 269 } 270 271 // Check that all directories have been cleaned 272 // Check that all metadata / staging / publish directories were deleted 273 dataDir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host) 274 if _, err := os.Stat(dataDir); err == nil { 275 t.Errorf("volume publish data directory %s was not deleted", dataDir) 276 } 277 devDir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host) 278 if _, err := os.Stat(devDir); err == nil { 279 t.Errorf("volume publish device directory %s was not deleted", devDir) 280 } 281 282 if _, err := os.Stat(stagingPath); err == nil { 283 t.Errorf("volume staging path %s was not deleted", stagingPath) 284 } 285 } 286 287 func TestBlockMapperSetupDeviceNoClientError(t *testing.T) { 288 transientError := volumetypes.NewTransientOperationFailure("") 289 plug, tmpDir := newTestPlugin(t, nil) 290 defer os.RemoveAll(tmpDir) 291 292 csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 293 if err != nil { 294 t.Fatalf("Failed to make a new Mapper: %v", err) 295 } 296 297 pvName := pv.GetName() 298 nodeName := string(plug.host.GetNodeName()) 299 300 csiMapper.csiClient = setupClient(t, true) 301 302 attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), string(nodeName)) 303 attachment := makeTestAttachment(attachID, nodeName, pvName) 304 attachment.Status.Attached = true 305 _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.TODO(), attachment, metav1.CreateOptions{}) 306 if err != nil { 307 t.Fatalf("failed to setup VolumeAttachment: %v", err) 308 } 309 t.Log("created attachment ", attachID) 310 311 // Clear out the clients 312 // The lookup to generate a new client will fail when it tries to query a driver with an unknown name 313 csiMapper.csiClient = nil 314 csiMapper.csiClientGetter.csiClient = nil 315 // Note that prepareBlockMapperTest above will create a driver with a name of "test-driver" 316 csiMapper.csiClientGetter.driverName = "unknown-driver" 317 318 _, err = csiMapper.SetUpDevice() 319 if err == nil { 320 t.Errorf("test should fail, but no error occurred") 321 } else if reflect.TypeOf(transientError) != reflect.TypeOf(err) { 322 t.Fatalf("expected exitError type: %v got: %v (%v)", reflect.TypeOf(transientError), reflect.TypeOf(err), err) 323 } 324 } 325 326 func TestBlockMapperMapPodDevice(t *testing.T) { 327 plug, tmpDir := newTestPlugin(t, nil) 328 defer os.RemoveAll(tmpDir) 329 330 csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 331 if err != nil { 332 t.Fatalf("Failed to make a new Mapper: %v", err) 333 } 334 335 pvName := pv.GetName() 336 nodeName := string(plug.host.GetNodeName()) 337 338 csiMapper.csiClient = setupClient(t, true) 339 340 attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), nodeName) 341 attachment := makeTestAttachment(attachID, nodeName, pvName) 342 attachment.Status.Attached = true 343 _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.Background(), attachment, metav1.CreateOptions{}) 344 if err != nil { 345 t.Fatalf("failed to setup VolumeAttachment: %v", err) 346 } 347 t.Log("created attachment ", attachID) 348 349 // Map device to global and pod device map path 350 path, err := csiMapper.MapPodDevice() 351 if err != nil { 352 t.Fatalf("mapper failed to GetGlobalMapPath: %v", err) 353 } 354 355 // Check if NodePublishVolume published to the right path 356 pvols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes() 357 pvol, ok := pvols[csiMapper.volumeID] 358 if !ok { 359 t.Error("csi server may not have received NodePublishVolume call") 360 } 361 362 publishPath := csiMapper.getPublishPath() 363 if pvol.Path != publishPath { 364 t.Errorf("csi server expected path %s, got %s", publishPath, pvol.Path) 365 } 366 if path != publishPath { 367 t.Errorf("csi server expected path %s, but MapPodDevice returned %s", publishPath, path) 368 } 369 } 370 371 func TestBlockMapperMapPodDeviceNotSupportAttach(t *testing.T) { 372 fakeClient := fakeclient.NewSimpleClientset() 373 attachRequired := false 374 fakeDriver := &storagev1.CSIDriver{ 375 ObjectMeta: metav1.ObjectMeta{ 376 Name: testDriver, 377 }, 378 Spec: storagev1.CSIDriverSpec{ 379 AttachRequired: &attachRequired, 380 }, 381 } 382 _, err := fakeClient.StorageV1().CSIDrivers().Create(context.TODO(), fakeDriver, metav1.CreateOptions{}) 383 if err != nil { 384 t.Fatalf("Failed to create a fakeDriver: %v", err) 385 } 386 387 // after the driver is created, create the plugin. newTestPlugin waits for the informer to sync, 388 // such that csiMapper.SetUpDevice below sees the VolumeAttachment object in the lister. 389 390 plug, tmpDir := newTestPlugin(t, fakeClient) 391 defer os.RemoveAll(tmpDir) 392 393 csiMapper, _, _, err := prepareBlockMapperTest(plug, "test-pv", t) 394 if err != nil { 395 t.Fatalf("Failed to make a new Mapper: %v", err) 396 } 397 csiMapper.csiClient = setupClient(t, true) 398 399 // Map device to global and pod device map path 400 path, err := csiMapper.MapPodDevice() 401 if err != nil { 402 t.Fatalf("mapper failed to GetGlobalMapPath: %v", err) 403 } 404 publishPath := csiMapper.getPublishPath() 405 if path != publishPath { 406 t.Errorf("path %s and %s doesn't match", path, publishPath) 407 } 408 } 409 410 func TestBlockMapperMapPodDeviceWithPodInfo(t *testing.T) { 411 fakeClient := fakeclient.NewSimpleClientset() 412 attachRequired := false 413 podInfo := true 414 fakeDriver := &storagev1.CSIDriver{ 415 ObjectMeta: metav1.ObjectMeta{ 416 Name: testDriver, 417 }, 418 Spec: storagev1.CSIDriverSpec{ 419 PodInfoOnMount: &podInfo, 420 AttachRequired: &attachRequired, 421 }, 422 } 423 _, err := fakeClient.StorageV1().CSIDrivers().Create(context.TODO(), fakeDriver, metav1.CreateOptions{}) 424 if err != nil { 425 t.Fatalf("Failed to create a fakeDriver: %v", err) 426 } 427 428 // after the driver is created, create the plugin. newTestPlugin waits for the informer to sync, 429 // such that csiMapper.SetUpDevice below sees the VolumeAttachment object in the lister. 430 431 plug, tmpDir := newTestPlugin(t, fakeClient) 432 defer os.RemoveAll(tmpDir) 433 434 csiMapper, _, _, err := prepareBlockMapperTest(plug, "test-pv", t) 435 if err != nil { 436 t.Fatalf("Failed to make a new Mapper: %v", err) 437 } 438 csiMapper.csiClient = setupClient(t, true) 439 440 // Map device to global and pod device map path 441 _, err = csiMapper.MapPodDevice() 442 if err != nil { 443 t.Fatalf("mapper failed to GetGlobalMapPath: %v", err) 444 } 445 pvols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes() 446 pvol, ok := pvols[csiMapper.volumeID] 447 if !ok { 448 t.Error("csi server may not have received NodePublishVolume call") 449 } 450 451 if !reflect.DeepEqual(pvol.VolumeContext, map[string]string{"csi.storage.k8s.io/pod.uid": "test-pod", "csi.storage.k8s.io/serviceAccount.name": "", "csi.storage.k8s.io/pod.name": "test-pod", "csi.storage.k8s.io/pod.namespace": "test-ns", "csi.storage.k8s.io/ephemeral": "false"}) { 452 t.Error("csi mapper check pod info failed") 453 } 454 } 455 456 func TestBlockMapperMapPodDeviceNoClientError(t *testing.T) { 457 transientError := volumetypes.NewTransientOperationFailure("") 458 plug, tmpDir := newTestPlugin(t, nil) 459 defer os.RemoveAll(tmpDir) 460 461 csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 462 if err != nil { 463 t.Fatalf("Failed to make a new Mapper: %v", err) 464 } 465 466 pvName := pv.GetName() 467 nodeName := string(plug.host.GetNodeName()) 468 469 csiMapper.csiClient = setupClient(t, true) 470 471 attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), nodeName) 472 attachment := makeTestAttachment(attachID, nodeName, pvName) 473 attachment.Status.Attached = true 474 _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.Background(), attachment, metav1.CreateOptions{}) 475 if err != nil { 476 t.Fatalf("failed to setup VolumeAttachment: %v", err) 477 } 478 t.Log("created attachment ", attachID) 479 480 // Clear out the clients 481 // The lookup to generate a new client will fail when it tries to query a driver with an unknown name 482 csiMapper.csiClient = nil 483 csiMapper.csiClientGetter.csiClient = nil 484 // Note that prepareBlockMapperTest above will create a driver with a name of "test-driver" 485 csiMapper.csiClientGetter.driverName = "unknown-driver" 486 487 _, err = csiMapper.MapPodDevice() 488 if err == nil { 489 t.Errorf("test should fail, but no error occurred") 490 } else if reflect.TypeOf(transientError) != reflect.TypeOf(err) { 491 t.Fatalf("expected exitError type: %v got: %v (%v)", reflect.TypeOf(transientError), reflect.TypeOf(err), err) 492 } 493 } 494 495 func TestBlockMapperTearDownDevice(t *testing.T) { 496 plug, tmpDir := newTestPlugin(t, nil) 497 defer os.RemoveAll(tmpDir) 498 499 _, spec, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 500 if err != nil { 501 t.Fatalf("Failed to make a new Mapper: %v", err) 502 } 503 504 // save volume data 505 dir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host) 506 if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) { 507 t.Errorf("failed to create dir [%s]: %v", dir, err) 508 } 509 510 if err := saveVolumeData( 511 dir, 512 volDataFileName, 513 map[string]string{ 514 volDataKey.specVolID: pv.ObjectMeta.Name, 515 volDataKey.driverName: testDriver, 516 volDataKey.volHandle: testVol, 517 }, 518 ); err != nil { 519 t.Fatalf("failed to save volume data: %v", err) 520 } 521 522 unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID) 523 if err != nil { 524 t.Fatalf("failed to make a new Unmapper: %v", err) 525 } 526 527 csiUnmapper := unmapper.(*csiBlockMapper) 528 csiUnmapper.csiClient = setupClient(t, true) 529 530 globalMapPath, err := csiUnmapper.GetGlobalMapPath(spec) 531 if err != nil { 532 t.Fatalf("unmapper failed to GetGlobalMapPath: %v", err) 533 } 534 535 err = csiUnmapper.TearDownDevice(globalMapPath, "/dev/test") 536 if err != nil { 537 t.Fatal(err) 538 } 539 540 // ensure csi client call and node unpblished 541 pubs := csiUnmapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes() 542 if _, ok := pubs[csiUnmapper.volumeID]; ok { 543 t.Error("csi server may not have received NodeUnpublishVolume call") 544 } 545 546 // ensure csi client call and node unstaged 547 vols := csiUnmapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodeStagedVolumes() 548 if _, ok := vols[csiUnmapper.volumeID]; ok { 549 t.Error("csi server may not have received NodeUnstageVolume call") 550 } 551 } 552 553 func TestBlockMapperTearDownDeviceNoClientError(t *testing.T) { 554 transientError := volumetypes.NewTransientOperationFailure("") 555 plug, tmpDir := newTestPlugin(t, nil) 556 defer os.RemoveAll(tmpDir) 557 558 _, spec, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 559 if err != nil { 560 t.Fatalf("Failed to make a new Mapper: %v", err) 561 } 562 563 // save volume data 564 dir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host) 565 if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) { 566 t.Errorf("failed to create dir [%s]: %v", dir, err) 567 } 568 569 if err := saveVolumeData( 570 dir, 571 volDataFileName, 572 map[string]string{ 573 volDataKey.specVolID: pv.ObjectMeta.Name, 574 volDataKey.driverName: testDriver, 575 volDataKey.volHandle: testVol, 576 }, 577 ); err != nil { 578 t.Fatalf("failed to save volume data: %v", err) 579 } 580 581 unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID) 582 if err != nil { 583 t.Fatalf("failed to make a new Unmapper: %v", err) 584 } 585 586 csiUnmapper := unmapper.(*csiBlockMapper) 587 csiUnmapper.csiClient = setupClient(t, true) 588 589 globalMapPath, err := csiUnmapper.GetGlobalMapPath(spec) 590 if err != nil { 591 t.Fatalf("unmapper failed to GetGlobalMapPath: %v", err) 592 } 593 594 // Clear out the clients 595 // The lookup to generate a new client will fail when it tries to query a driver with an unknown name 596 csiUnmapper.csiClient = nil 597 csiUnmapper.csiClientGetter.csiClient = nil 598 // Note that prepareBlockMapperTest above will create a driver with a name of "test-driver" 599 csiUnmapper.csiClientGetter.driverName = "unknown-driver" 600 601 err = csiUnmapper.TearDownDevice(globalMapPath, "/dev/test") 602 if err == nil { 603 t.Errorf("test should fail, but no error occurred") 604 } else if reflect.TypeOf(transientError) != reflect.TypeOf(err) { 605 t.Fatalf("expected exitError type: %v got: %v (%v)", reflect.TypeOf(transientError), reflect.TypeOf(err), err) 606 } 607 } 608 609 func TestVolumeSetupTeardown(t *testing.T) { 610 // Follow volume setup + teardown sequences at top of cs_block.go and set up / clean up one CSI block device. 611 // Focus on testing that there were no leftover files present after the cleanup. 612 613 plug, tmpDir := newTestPlugin(t, nil) 614 defer os.RemoveAll(tmpDir) 615 616 csiMapper, spec, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 617 if err != nil { 618 t.Fatalf("Failed to make a new Mapper: %v", err) 619 } 620 621 pvName := pv.GetName() 622 nodeName := string(plug.host.GetNodeName()) 623 624 csiMapper.csiClient = setupClient(t, true) 625 626 attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), string(nodeName)) 627 attachment := makeTestAttachment(attachID, nodeName, pvName) 628 attachment.Status.Attached = true 629 _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.TODO(), attachment, metav1.CreateOptions{}) 630 if err != nil { 631 t.Fatalf("failed to setup VolumeAttachment: %v", err) 632 } 633 t.Log("created attachment ", attachID) 634 635 stagingPath, err := csiMapper.SetUpDevice() 636 if err != nil { 637 t.Fatalf("mapper failed to SetupDevice: %v", err) 638 } 639 // Check if NodeStageVolume staged to the right path 640 svols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodeStagedVolumes() 641 svol, ok := svols[csiMapper.volumeID] 642 if !ok { 643 t.Error("csi server may not have received NodeStageVolume call") 644 } 645 if svol.Path != stagingPath { 646 t.Errorf("csi server expected device path %s, got %s", stagingPath, svol.Path) 647 } 648 649 path, err := csiMapper.MapPodDevice() 650 if err != nil { 651 t.Fatalf("mapper failed to GetGlobalMapPath: %v", err) 652 } 653 pvols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes() 654 pvol, ok := pvols[csiMapper.volumeID] 655 if !ok { 656 t.Error("csi server may not have received NodePublishVolume call") 657 } 658 publishPath := csiMapper.getPublishPath() 659 if pvol.Path != publishPath { 660 t.Errorf("csi server expected path %s, got %s", publishPath, pvol.Path) 661 } 662 if path != publishPath { 663 t.Errorf("csi server expected path %s, but MapPodDevice returned %s", publishPath, path) 664 } 665 666 unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID) 667 if err != nil { 668 t.Fatalf("failed to make a new Unmapper: %v", err) 669 } 670 671 csiUnmapper := unmapper.(*csiBlockMapper) 672 csiUnmapper.csiClient = csiMapper.csiClient 673 674 globalMapPath, err := csiUnmapper.GetGlobalMapPath(spec) 675 if err != nil { 676 t.Fatalf("unmapper failed to GetGlobalMapPath: %v", err) 677 } 678 679 err = csiUnmapper.UnmapPodDevice() 680 if err != nil { 681 t.Errorf("unmapper failed to call UnmapPodDevice: %v", err) 682 } 683 684 // GenerateUnmapDeviceFunc uses "" as pod UUID, it is global operation over all pods that used the volume 685 unmapper, err = plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, "") 686 if err != nil { 687 t.Fatalf("failed to make a new Unmapper: %v", err) 688 } 689 csiUnmapper = unmapper.(*csiBlockMapper) 690 csiUnmapper.csiClient = csiMapper.csiClient 691 692 err = csiUnmapper.TearDownDevice(globalMapPath, "/dev/test") 693 if err != nil { 694 t.Fatal(err) 695 } 696 pubs := csiUnmapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes() 697 if _, ok := pubs[csiUnmapper.volumeID]; ok { 698 t.Error("csi server may not have received NodeUnpublishVolume call") 699 } 700 vols := csiUnmapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodeStagedVolumes() 701 if _, ok := vols[csiUnmapper.volumeID]; ok { 702 t.Error("csi server may not have received NodeUnstageVolume call") 703 } 704 705 // Check that all metadata / staging / publish directories were deleted 706 dataDir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host) 707 if _, err := os.Stat(dataDir); err == nil { 708 t.Errorf("volume publish data directory %s was not deleted", dataDir) 709 } 710 devDir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host) 711 if _, err := os.Stat(devDir); err == nil { 712 t.Errorf("volume publish device directory %s was not deleted", devDir) 713 } 714 if _, err := os.Stat(publishPath); err == nil { 715 t.Errorf("volume publish path %s was not deleted", publishPath) 716 } 717 publishDir := filepath.Dir(publishPath) 718 if _, err := os.Stat(publishDir); err == nil { 719 t.Errorf("volume publish parent directory %s was not deleted", publishDir) 720 } 721 if _, err := os.Stat(stagingPath); err == nil { 722 t.Errorf("volume staging path %s was not deleted", stagingPath) 723 } 724 } 725 726 func TestUnmapPodDeviceNoClientError(t *testing.T) { 727 transientError := volumetypes.NewTransientOperationFailure("") 728 plug, tmpDir := newTestPlugin(t, nil) 729 defer os.RemoveAll(tmpDir) 730 731 csiMapper, spec, pv, err := prepareBlockMapperTest(plug, "test-pv", t) 732 if err != nil { 733 t.Fatalf("Failed to make a new Mapper: %v", err) 734 } 735 736 pvName := pv.GetName() 737 nodeName := string(plug.host.GetNodeName()) 738 739 csiMapper.csiClient = setupClient(t, true) 740 741 attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), string(nodeName)) 742 attachment := makeTestAttachment(attachID, nodeName, pvName) 743 attachment.Status.Attached = true 744 _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.TODO(), attachment, metav1.CreateOptions{}) 745 if err != nil { 746 t.Fatalf("failed to setup VolumeAttachment: %v", err) 747 } 748 t.Log("created attachment ", attachID) 749 750 stagingPath, err := csiMapper.SetUpDevice() 751 if err != nil { 752 t.Fatalf("mapper failed to SetupDevice: %v", err) 753 } 754 // Check if NodeStageVolume staged to the right path 755 svols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodeStagedVolumes() 756 svol, ok := svols[csiMapper.volumeID] 757 if !ok { 758 t.Error("csi server may not have received NodeStageVolume call") 759 } 760 if svol.Path != stagingPath { 761 t.Errorf("csi server expected device path %s, got %s", stagingPath, svol.Path) 762 } 763 764 path, err := csiMapper.MapPodDevice() 765 if err != nil { 766 t.Fatalf("mapper failed to GetGlobalMapPath: %v", err) 767 } 768 pvols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes() 769 pvol, ok := pvols[csiMapper.volumeID] 770 if !ok { 771 t.Error("csi server may not have received NodePublishVolume call") 772 } 773 publishPath := csiMapper.getPublishPath() 774 if pvol.Path != publishPath { 775 t.Errorf("csi server expected path %s, got %s", publishPath, pvol.Path) 776 } 777 if path != publishPath { 778 t.Errorf("csi server expected path %s, but MapPodDevice returned %s", publishPath, path) 779 } 780 781 unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID) 782 if err != nil { 783 t.Fatalf("failed to make a new Unmapper: %v", err) 784 } 785 786 csiUnmapper := unmapper.(*csiBlockMapper) 787 csiUnmapper.csiClient = csiMapper.csiClient 788 789 _, err = csiUnmapper.GetGlobalMapPath(spec) 790 if err != nil { 791 t.Fatalf("unmapper failed to GetGlobalMapPath: %v", err) 792 } 793 794 // Clear out the clients 795 // The lookup to generate a new client will fail when it tries to query a driver with an unknown name 796 csiUnmapper.csiClient = nil 797 csiUnmapper.csiClientGetter.csiClient = nil 798 // Note that prepareBlockMapperTest above will create a driver with a name of "test-driver" 799 csiUnmapper.csiClientGetter.driverName = "unknown-driver" 800 801 err = csiUnmapper.UnmapPodDevice() 802 if err == nil { 803 t.Errorf("test should fail, but no error occurred") 804 } else if reflect.TypeOf(transientError) != reflect.TypeOf(err) { 805 t.Fatalf("expected exitError type: %v got: %v (%v)", reflect.TypeOf(transientError), reflect.TypeOf(err), err) 806 } 807 }