k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/csi/csi_plugin_test.go (about) 1 /* 2 Copyright 2017 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 "fmt" 21 "math/rand" 22 "os" 23 "path/filepath" 24 "testing" 25 "time" 26 27 api "k8s.io/api/core/v1" 28 storage "k8s.io/api/storage/v1" 29 meta "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/types" 31 utilversion "k8s.io/apimachinery/pkg/util/version" 32 "k8s.io/apimachinery/pkg/util/wait" 33 utilfeature "k8s.io/apiserver/pkg/util/feature" 34 "k8s.io/client-go/informers" 35 fakeclient "k8s.io/client-go/kubernetes/fake" 36 utiltesting "k8s.io/client-go/util/testing" 37 featuregatetesting "k8s.io/component-base/featuregate/testing" 38 "k8s.io/kubernetes/pkg/features" 39 "k8s.io/kubernetes/pkg/volume" 40 volumetest "k8s.io/kubernetes/pkg/volume/testing" 41 ) 42 43 const ( 44 volumeHostType int = iota 45 kubeletVolumeHostType 46 attachDetachVolumeHostType 47 ) 48 49 func newTestPlugin(t *testing.T, client *fakeclient.Clientset) (*csiPlugin, string) { 50 return newTestPluginWithVolumeHost(t, client, kubeletVolumeHostType) 51 } 52 53 func newTestPluginWithAttachDetachVolumeHost(t *testing.T, client *fakeclient.Clientset) (*csiPlugin, string) { 54 return newTestPluginWithVolumeHost(t, client, attachDetachVolumeHostType) 55 } 56 57 // create a plugin mgr to load plugins and setup a fake client 58 func newTestPluginWithVolumeHost(t *testing.T, client *fakeclient.Clientset, hostType int) (*csiPlugin, string) { 59 tmpDir, err := utiltesting.MkTmpdir("csi-test") 60 if err != nil { 61 t.Fatalf("can't create temp dir: %v", err) 62 } 63 64 if client == nil { 65 client = fakeclient.NewSimpleClientset() 66 } 67 68 client.Tracker().Add(&api.Node{ 69 ObjectMeta: meta.ObjectMeta{ 70 Name: "fakeNode", 71 }, 72 Spec: api.NodeSpec{}, 73 }) 74 75 // Start informer for CSIDrivers. 76 factory := informers.NewSharedInformerFactory(client, CsiResyncPeriod) 77 csiDriverInformer := factory.Storage().V1().CSIDrivers() 78 csiDriverLister := csiDriverInformer.Lister() 79 volumeAttachmentInformer := factory.Storage().V1().VolumeAttachments() 80 volumeAttachmentLister := volumeAttachmentInformer.Lister() 81 82 factory.Start(wait.NeverStop) 83 syncedTypes := factory.WaitForCacheSync(wait.NeverStop) 84 if len(syncedTypes) != 2 { 85 t.Fatalf("informers are not synced") 86 } 87 for ty, ok := range syncedTypes { 88 if !ok { 89 t.Fatalf("failed to sync: %#v", ty) 90 } 91 } 92 93 var host volume.VolumeHost 94 switch hostType { 95 case volumeHostType: 96 host = volumetest.NewFakeVolumeHostWithCSINodeName(t, 97 tmpDir, 98 client, 99 ProbeVolumePlugins(), 100 "fakeNode", 101 csiDriverLister, 102 nil, 103 ) 104 case kubeletVolumeHostType: 105 host = volumetest.NewFakeKubeletVolumeHostWithCSINodeName(t, 106 tmpDir, 107 client, 108 ProbeVolumePlugins(), 109 "fakeNode", 110 csiDriverLister, 111 volumeAttachmentLister, 112 ) 113 case attachDetachVolumeHostType: 114 host = volumetest.NewFakeAttachDetachVolumeHostWithCSINodeName(t, 115 tmpDir, 116 client, 117 ProbeVolumePlugins(), 118 "fakeNode", 119 csiDriverLister, 120 volumeAttachmentLister, 121 ) 122 default: 123 t.Fatalf("Unsupported volume host type") 124 } 125 126 fakeHost, ok := host.(volumetest.FakeVolumeHost) 127 if !ok { 128 t.Fatalf("Unsupported volume host type") 129 } 130 131 pluginMgr := fakeHost.GetPluginMgr() 132 plug, err := pluginMgr.FindPluginByName(CSIPluginName) 133 if err != nil { 134 t.Fatalf("can't find plugin %v", CSIPluginName) 135 } 136 137 csiPlug, ok := plug.(*csiPlugin) 138 if !ok { 139 t.Fatalf("cannot assert plugin to be type csiPlugin") 140 } 141 142 return csiPlug, tmpDir 143 } 144 145 func registerFakePlugin(pluginName, endpoint string, versions []string, t *testing.T) { 146 highestSupportedVersions, err := utilversion.HighestSupportedVersion(versions) 147 if err != nil { 148 t.Fatalf("unexpected error parsing versions (%v) for pluginName %q endpoint %q: %#v", versions, pluginName, endpoint, err) 149 } 150 151 csiDrivers.Clear() 152 csiDrivers.Set(pluginName, Driver{ 153 endpoint: endpoint, 154 highestSupportedVersion: highestSupportedVersions, 155 }) 156 } 157 158 func TestPluginGetPluginName(t *testing.T) { 159 plug, tmpDir := newTestPlugin(t, nil) 160 defer os.RemoveAll(tmpDir) 161 if plug.GetPluginName() != "kubernetes.io/csi" { 162 t.Errorf("unexpected plugin name %v", plug.GetPluginName()) 163 } 164 } 165 166 func TestPluginGetVolumeName(t *testing.T) { 167 plug, tmpDir := newTestPlugin(t, nil) 168 defer os.RemoveAll(tmpDir) 169 testCases := []struct { 170 name string 171 driverName string 172 volName string 173 spec *volume.Spec 174 shouldFail bool 175 }{ 176 { 177 name: "alphanum names", 178 driverName: "testdr", 179 volName: "testvol", 180 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "testdr", "testvol"), false), 181 }, 182 { 183 name: "mixchar driver", 184 driverName: "test.dr.cc", 185 volName: "testvol", 186 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "test.dr.cc", "testvol"), false), 187 }, 188 { 189 name: "mixchar volume", 190 driverName: "testdr", 191 volName: "test-vol-name", 192 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "testdr", "test-vol-name"), false), 193 }, 194 { 195 name: "mixchars all", 196 driverName: "test-driver", 197 volName: "test.vol.name", 198 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "test-driver", "test.vol.name"), false), 199 }, 200 { 201 name: "volume source with mixchars all", 202 driverName: "test-driver", 203 volName: "test.vol.name", 204 spec: volume.NewSpecFromVolume(makeTestVol("test-pv", "test-driver")), 205 shouldFail: true, // csi inline feature off 206 }, 207 { 208 name: "missing spec", 209 shouldFail: true, 210 }, 211 } 212 213 for _, tc := range testCases { 214 t.Logf("testing: %s", tc.name) 215 registerFakePlugin(tc.driverName, "endpoint", []string{"1.3.0"}, t) 216 name, err := plug.GetVolumeName(tc.spec) 217 if tc.shouldFail != (err != nil) { 218 t.Fatal("shouldFail does match expected error") 219 } 220 if tc.shouldFail && err != nil { 221 t.Log(err) 222 continue 223 } 224 if name != fmt.Sprintf("%s%s%s", tc.driverName, volNameSep, tc.volName) { 225 t.Errorf("unexpected volume name %s", name) 226 } 227 } 228 } 229 230 func TestPluginGetVolumeNameWithInline(t *testing.T) { 231 modes := []storage.VolumeLifecycleMode{ 232 storage.VolumeLifecyclePersistent, 233 } 234 driver := getTestCSIDriver(testDriver, nil, nil, modes) 235 client := fakeclient.NewSimpleClientset(driver) 236 plug, tmpDir := newTestPlugin(t, client) 237 defer os.RemoveAll(tmpDir) 238 testCases := []struct { 239 name string 240 driverName string 241 volName string 242 shouldFail bool 243 spec *volume.Spec 244 }{ 245 { 246 name: "missing spec", 247 shouldFail: true, 248 }, 249 { 250 name: "alphanum names for pv", 251 driverName: "testdr", 252 volName: "testvol", 253 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "testdr", "testvol"), false), 254 }, 255 { 256 name: "alphanum names for vol source", 257 driverName: "testdr", 258 volName: "testvol", 259 spec: volume.NewSpecFromVolume(makeTestVol("test-pv", "testdr")), 260 shouldFail: true, 261 }, 262 } 263 264 for _, tc := range testCases { 265 t.Logf("testing: %s", tc.name) 266 registerFakePlugin(tc.driverName, "endpoint", []string{"1.3.0"}, t) 267 name, err := plug.GetVolumeName(tc.spec) 268 if tc.shouldFail != (err != nil) { 269 t.Fatal("shouldFail does match expected error") 270 } 271 if tc.shouldFail && err != nil { 272 t.Log(err) 273 continue 274 } 275 if name != fmt.Sprintf("%s%s%s", tc.driverName, volNameSep, tc.volName) { 276 t.Errorf("unexpected volume name %s", name) 277 } 278 } 279 } 280 281 func TestPluginCanSupport(t *testing.T) { 282 tests := []struct { 283 name string 284 spec *volume.Spec 285 canSupport bool 286 }{ 287 { 288 name: "no spec provided", 289 canSupport: false, 290 }, 291 { 292 name: "can support volume source", 293 spec: volume.NewSpecFromVolume(makeTestVol("test-vol", testDriver)), 294 canSupport: true, 295 }, 296 { 297 name: "can support persistent volume source", 298 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 20, testDriver, testVol), true), 299 canSupport: true, 300 }, 301 } 302 303 plug, tmpDir := newTestPlugin(t, nil) 304 defer os.RemoveAll(tmpDir) 305 registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t) 306 307 for _, tc := range tests { 308 t.Run(tc.name, func(t *testing.T) { 309 310 actual := plug.CanSupport(tc.spec) 311 if tc.canSupport != actual { 312 t.Errorf("expecting canSupport %t, got %t", tc.canSupport, actual) 313 } 314 }) 315 } 316 } 317 318 func TestPluginConstructVolumeSpec(t *testing.T) { 319 plug, tmpDir := newTestPlugin(t, nil) 320 defer os.RemoveAll(tmpDir) 321 322 testCases := []struct { 323 name string 324 seLinuxMountEnabled bool 325 originSpec *volume.Spec 326 originSELinuxMountContext string 327 specVolID string 328 volHandle string 329 expectedSELinuxContext string 330 podUID types.UID 331 }{ 332 { 333 name: "construct spec1 from original persistent spec", 334 specVolID: "test.vol.id", 335 volHandle: "testvol-handle1", 336 337 originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("test.vol.id", 20, testDriver, "testvol-handle1"), true), 338 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 339 }, 340 { 341 name: "construct spec2 from original persistent spec", 342 specVolID: "spec2", 343 volHandle: "handle2", 344 originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true), 345 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 346 }, 347 { 348 name: "construct SELinux context from original persistent spec when the feature is enabled", 349 seLinuxMountEnabled: true, 350 specVolID: "spec3", 351 volHandle: "handle3", 352 originSELinuxMountContext: "system_u:object_r:container_file_t:s0:c314,c894", 353 originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec3", 20, testDriver, "handle3"), true), 354 expectedSELinuxContext: "system_u:object_r:container_file_t:s0:c314,c894", 355 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 356 }, 357 { 358 name: "construct empty SELinux from original persistent spec when the feature is disabled", 359 seLinuxMountEnabled: false, 360 specVolID: "spec4", 361 volHandle: "handle4", 362 originSELinuxMountContext: "system_u:object_r:container_file_t:s0:c314,c894", 363 originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec4", 20, testDriver, "handle4"), true), 364 expectedSELinuxContext: "", // The context is cleared when the feature gate is off 365 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 366 }, 367 } 368 369 registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t) 370 371 for _, tc := range testCases { 372 t.Run(tc.name, func(t *testing.T) { 373 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, tc.seLinuxMountEnabled) 374 375 mounter, err := plug.NewMounter( 376 tc.originSpec, 377 &api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}}, 378 volume.VolumeOptions{}, 379 ) 380 if err != nil { 381 t.Fatal(err) 382 } 383 if mounter == nil { 384 t.Fatal("failed to create CSI mounter") 385 } 386 csiMounter := mounter.(*csiMountMgr) 387 388 mountPath := filepath.Dir(csiMounter.GetPath()) 389 err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode), tc.originSELinuxMountContext) 390 if err != nil { 391 t.Fatalf("failed to save fake volume info file: %s", err) 392 } 393 394 // rebuild spec 395 rec, err := plug.ConstructVolumeSpec("test-pv", filepath.Dir(csiMounter.GetPath())) 396 if err != nil { 397 t.Fatal(err) 398 } 399 if rec.Spec == nil { 400 t.Fatal("nil volume.Spec constructed") 401 } 402 403 // inspect spec 404 if rec.Spec.PersistentVolume == nil || rec.Spec.PersistentVolume.Spec.CSI == nil { 405 t.Fatal("CSIPersistentVolume not found in constructed spec ") 406 } 407 408 volHandle := rec.Spec.PersistentVolume.Spec.CSI.VolumeHandle 409 if volHandle != tc.originSpec.PersistentVolume.Spec.CSI.VolumeHandle { 410 t.Error("unexpected volumeHandle constructed:", volHandle) 411 } 412 driverName := rec.Spec.PersistentVolume.Spec.CSI.Driver 413 if driverName != tc.originSpec.PersistentVolume.Spec.CSI.Driver { 414 t.Error("unexpected driverName constructed:", driverName) 415 } 416 417 if rec.Spec.PersistentVolume.Spec.VolumeMode == nil { 418 t.Fatalf("Volume mode has not been set.") 419 } 420 421 if *rec.Spec.PersistentVolume.Spec.VolumeMode != api.PersistentVolumeFilesystem { 422 t.Errorf("Unexpected volume mode %q", *rec.Spec.PersistentVolume.Spec.VolumeMode) 423 } 424 425 if rec.Spec.Name() != tc.specVolID { 426 t.Errorf("Unexpected spec name constructed %s", rec.Spec.Name()) 427 } 428 429 if rec.SELinuxMountContext != tc.expectedSELinuxContext { 430 t.Errorf("Expected SELinux context %q, got %q", tc.expectedSELinuxContext, rec.SELinuxMountContext) 431 } 432 }) 433 } 434 } 435 436 func TestPluginConstructVolumeSpecWithInline(t *testing.T) { 437 testCases := []struct { 438 name string 439 originSpec *volume.Spec 440 specVolID string 441 volHandle string 442 podUID types.UID 443 shouldFail bool 444 modes []storage.VolumeLifecycleMode 445 }{ 446 { 447 name: "construct spec1 from persistent spec", 448 specVolID: "test.vol.id", 449 volHandle: "testvol-handle1", 450 originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("test.vol.id", 20, testDriver, "testvol-handle1"), true), 451 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 452 modes: []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent}, 453 }, 454 { 455 name: "construct spec2 from persistent spec", 456 specVolID: "spec2", 457 volHandle: "handle2", 458 originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true), 459 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 460 modes: []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent}, 461 }, 462 { 463 name: "construct spec2 from persistent spec, missing mode", 464 specVolID: "spec2", 465 volHandle: "handle2", 466 originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true), 467 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 468 modes: []storage.VolumeLifecycleMode{}, 469 shouldFail: true, 470 }, 471 { 472 name: "construct spec from volume spec", 473 specVolID: "volspec", 474 originSpec: volume.NewSpecFromVolume(makeTestVol("volspec", testDriver)), 475 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 476 modes: []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral}, 477 }, 478 { 479 name: "construct spec from volume spec2", 480 specVolID: "volspec2", 481 originSpec: volume.NewSpecFromVolume(makeTestVol("volspec2", testDriver)), 482 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 483 modes: []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral}, 484 }, 485 { 486 name: "construct spec from volume spec2, missing mode", 487 specVolID: "volspec2", 488 originSpec: volume.NewSpecFromVolume(makeTestVol("volspec2", testDriver)), 489 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 490 modes: []storage.VolumeLifecycleMode{}, 491 shouldFail: true, 492 }, 493 { 494 name: "missing spec", 495 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 496 shouldFail: true, 497 }, 498 } 499 500 registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t) 501 502 for _, tc := range testCases { 503 t.Run(tc.name, func(t *testing.T) { 504 driver := getTestCSIDriver(testDriver, nil, nil, tc.modes) 505 client := fakeclient.NewSimpleClientset(driver) 506 plug, tmpDir := newTestPlugin(t, client) 507 defer os.RemoveAll(tmpDir) 508 509 mounter, err := plug.NewMounter( 510 tc.originSpec, 511 &api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}}, 512 volume.VolumeOptions{}, 513 ) 514 if tc.shouldFail && err != nil { 515 t.Log(err) 516 return 517 } 518 if !tc.shouldFail && err != nil { 519 t.Fatal(err) 520 } 521 if mounter == nil { 522 t.Fatal("failed to create CSI mounter") 523 } 524 csiMounter := mounter.(*csiMountMgr) 525 526 mountPath := filepath.Dir(csiMounter.GetPath()) 527 err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode), "") 528 if err != nil { 529 t.Fatalf("failed to save fake volume info file: %s", err) 530 } 531 532 // rebuild spec 533 rec, err := plug.ConstructVolumeSpec("test-pv", filepath.Dir(csiMounter.GetPath())) 534 if err != nil { 535 t.Fatal(err) 536 } 537 if rec.Spec == nil { 538 t.Fatal("nil volume.Spec constructed") 539 } 540 541 if rec.Spec.Name() != tc.specVolID { 542 t.Errorf("unexpected spec name constructed volume.Spec: %s", rec.Spec.Name()) 543 } 544 545 switch { 546 case rec.Spec.Volume != nil: 547 if rec.Spec.Volume.CSI == nil { 548 t.Error("missing CSIVolumeSource in constructed volume.Spec") 549 } 550 if rec.Spec.Volume.CSI.Driver != tc.originSpec.Volume.CSI.Driver { 551 t.Error("unexpected driver in constructed volume source:", rec.Spec.Volume.CSI.Driver) 552 } 553 554 case rec.Spec.PersistentVolume != nil: 555 if rec.Spec.PersistentVolume.Spec.CSI == nil { 556 t.Fatal("missing CSIPersistentVolumeSource in constructed volume.spec") 557 } 558 volHandle := rec.Spec.PersistentVolume.Spec.CSI.VolumeHandle 559 if volHandle != tc.originSpec.PersistentVolume.Spec.CSI.VolumeHandle { 560 t.Error("unexpected volumeHandle constructed in persistent volume source:", volHandle) 561 } 562 driverName := rec.Spec.PersistentVolume.Spec.CSI.Driver 563 if driverName != tc.originSpec.PersistentVolume.Spec.CSI.Driver { 564 t.Error("unexpected driverName constructed in persistent volume source:", driverName) 565 } 566 if rec.Spec.PersistentVolume.Spec.VolumeMode == nil { 567 t.Fatalf("Volume mode has not been set.") 568 } 569 if *rec.Spec.PersistentVolume.Spec.VolumeMode != api.PersistentVolumeFilesystem { 570 t.Errorf("Unexpected volume mode %q", *rec.Spec.PersistentVolume.Spec.VolumeMode) 571 } 572 default: 573 t.Fatal("invalid volume.Spec constructed") 574 } 575 576 }) 577 } 578 } 579 580 func TestPluginNewMounter(t *testing.T) { 581 tests := []struct { 582 name string 583 spec *volume.Spec 584 podUID types.UID 585 namespace string 586 volumeLifecycleMode storage.VolumeLifecycleMode 587 shouldFail bool 588 }{ 589 { 590 name: "mounter from persistent volume source", 591 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv1", 20, testDriver, testVol), true), 592 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 593 namespace: "test-ns1", 594 volumeLifecycleMode: storage.VolumeLifecyclePersistent, 595 }, 596 { 597 name: "mounter from volume source", 598 spec: volume.NewSpecFromVolume(makeTestVol("test-vol1", testDriver)), 599 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 600 namespace: "test-ns2", 601 volumeLifecycleMode: storage.VolumeLifecycleEphemeral, 602 shouldFail: false, // NewMounter works with disabled inline volumes 603 }, 604 { 605 name: "mounter from no spec provided", 606 shouldFail: true, 607 }, 608 } 609 610 for _, test := range tests { 611 t.Run(test.name, func(t *testing.T) { 612 plug, tmpDir := newTestPlugin(t, nil) 613 defer os.RemoveAll(tmpDir) 614 615 registerFakePlugin(testDriver, "endpoint", []string{"1.2.0"}, t) 616 mounter, err := plug.NewMounter( 617 test.spec, 618 &api.Pod{ObjectMeta: meta.ObjectMeta{UID: test.podUID, Namespace: test.namespace}}, 619 volume.VolumeOptions{}, 620 ) 621 if test.shouldFail != (err != nil) { 622 t.Fatal("Unexpected error:", err) 623 } 624 if test.shouldFail && err != nil { 625 t.Log(err) 626 return 627 } 628 629 if mounter == nil { 630 t.Fatal("failed to create CSI mounter") 631 } 632 csiMounter := mounter.(*csiMountMgr) 633 634 // validate mounter fields 635 if string(csiMounter.driverName) != testDriver { 636 t.Error("mounter driver name not set") 637 } 638 if csiMounter.volumeID == "" { 639 t.Error("mounter volume id not set") 640 } 641 if csiMounter.pod == nil { 642 t.Error("mounter pod not set") 643 } 644 if string(csiMounter.podUID) != string(test.podUID) { 645 t.Error("mounter podUID not set") 646 } 647 csiClient, err := csiMounter.csiClientGetter.Get() 648 if csiClient == nil { 649 t.Errorf("mounter csiClient is nil: %v", err) 650 } 651 if err != nil { 652 t.Fatal(err) 653 } 654 if csiMounter.volumeLifecycleMode != test.volumeLifecycleMode { 655 t.Error("unexpected driver mode:", csiMounter.volumeLifecycleMode) 656 } 657 }) 658 } 659 } 660 661 func TestPluginNewMounterWithInline(t *testing.T) { 662 bothModes := []storage.VolumeLifecycleMode{ 663 storage.VolumeLifecycleEphemeral, 664 storage.VolumeLifecyclePersistent, 665 } 666 persistentMode := []storage.VolumeLifecycleMode{ 667 storage.VolumeLifecyclePersistent, 668 } 669 ephemeralMode := []storage.VolumeLifecycleMode{ 670 storage.VolumeLifecycleEphemeral, 671 } 672 tests := []struct { 673 name string 674 spec *volume.Spec 675 podUID types.UID 676 namespace string 677 volumeLifecycleMode storage.VolumeLifecycleMode 678 shouldFail bool 679 }{ 680 { 681 name: "mounter with missing spec", 682 shouldFail: true, 683 }, 684 { 685 name: "mounter with spec with both volSrc and pvSrc", 686 spec: &volume.Spec{ 687 Volume: makeTestVol("test-vol1", testDriver), 688 PersistentVolume: makeTestPV("test-pv1", 20, testDriver, testVol), 689 ReadOnly: true, 690 }, 691 shouldFail: true, 692 }, 693 { 694 name: "mounter with persistent volume source", 695 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv1", 20, testDriver, testVol), true), 696 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 697 namespace: "test-ns1", 698 volumeLifecycleMode: storage.VolumeLifecyclePersistent, 699 }, 700 { 701 name: "mounter with volume source", 702 spec: volume.NewSpecFromVolume(makeTestVol("test-vol1", testDriver)), 703 podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())), 704 namespace: "test-ns2", 705 volumeLifecycleMode: storage.VolumeLifecycleEphemeral, 706 }, 707 } 708 709 runAll := func(t *testing.T, supported []storage.VolumeLifecycleMode) { 710 for _, test := range tests { 711 t.Run(test.name, func(t *testing.T) { 712 driver := getTestCSIDriver(testDriver, nil, nil, supported) 713 fakeClient := fakeclient.NewSimpleClientset(driver) 714 plug, tmpDir := newTestPlugin(t, fakeClient) 715 defer os.RemoveAll(tmpDir) 716 717 registerFakePlugin(testDriver, "endpoint", []string{"1.2.0"}, t) 718 719 mounter, err := plug.NewMounter( 720 test.spec, 721 &api.Pod{ObjectMeta: meta.ObjectMeta{UID: test.podUID, Namespace: test.namespace}}, 722 volume.VolumeOptions{}, 723 ) 724 725 // Some test cases are meant to fail because their input data is broken. 726 shouldFail := test.shouldFail 727 if shouldFail != (err != nil) { 728 t.Fatal("Unexpected error:", err) 729 } 730 if shouldFail && err != nil { 731 t.Log(err) 732 return 733 } 734 735 if mounter == nil { 736 t.Fatal("failed to create CSI mounter") 737 } 738 csiMounter := mounter.(*csiMountMgr) 739 740 // validate mounter fields 741 if string(csiMounter.driverName) != testDriver { 742 t.Error("mounter driver name not set") 743 } 744 if csiMounter.volumeID == "" { 745 t.Error("mounter volume id not set") 746 } 747 if csiMounter.pod == nil { 748 t.Error("mounter pod not set") 749 } 750 if string(csiMounter.podUID) != string(test.podUID) { 751 t.Error("mounter podUID not set") 752 } 753 csiClient, err := csiMounter.csiClientGetter.Get() 754 if csiClient == nil { 755 t.Errorf("mounter csiClient is nil: %v", err) 756 } 757 if csiMounter.volumeLifecycleMode != test.volumeLifecycleMode { 758 t.Error("unexpected driver mode:", csiMounter.volumeLifecycleMode) 759 } 760 }) 761 } 762 } 763 764 t.Run("both supported", func(t *testing.T) { 765 runAll(t, bothModes) 766 }) 767 t.Run("persistent supported", func(t *testing.T) { 768 runAll(t, persistentMode) 769 }) 770 t.Run("ephemeral supported", func(t *testing.T) { 771 runAll(t, ephemeralMode) 772 }) 773 } 774 775 func TestPluginNewUnmounter(t *testing.T) { 776 plug, tmpDir := newTestPlugin(t, nil) 777 defer os.RemoveAll(tmpDir) 778 779 registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t) 780 pv := makeTestPV("test-pv", 10, testDriver, testVol) 781 782 // save the data file to re-create client 783 dir := filepath.Join(getTargetPath(testPodUID, pv.ObjectMeta.Name, plug.host), "/mount") 784 if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) { 785 t.Errorf("failed to create dir [%s]: %v", dir, err) 786 } 787 788 if err := saveVolumeData( 789 filepath.Dir(dir), 790 volDataFileName, 791 map[string]string{ 792 volDataKey.specVolID: pv.ObjectMeta.Name, 793 volDataKey.driverName: testDriver, 794 volDataKey.volHandle: testVol, 795 }, 796 ); err != nil { 797 t.Fatalf("failed to save volume data: %v", err) 798 } 799 800 // test unmounter 801 unmounter, err := plug.NewUnmounter(pv.ObjectMeta.Name, testPodUID) 802 csiUnmounter := unmounter.(*csiMountMgr) 803 804 if err != nil { 805 t.Fatalf("Failed to make a new Unmounter: %v", err) 806 } 807 808 if csiUnmounter == nil { 809 t.Fatal("failed to create CSI Unmounter") 810 } 811 812 if csiUnmounter.podUID != testPodUID { 813 t.Error("podUID not set") 814 } 815 816 csiClient, err := csiUnmounter.csiClientGetter.Get() 817 if csiClient == nil { 818 t.Errorf("mounter csiClient is nil: %v", err) 819 } 820 } 821 822 func TestPluginNewAttacher(t *testing.T) { 823 plug, tmpDir := newTestPlugin(t, nil) 824 defer os.RemoveAll(tmpDir) 825 826 attacher, err := plug.NewAttacher() 827 if err != nil { 828 t.Fatalf("failed to create new attacher: %v", err) 829 } 830 831 csiAttacher := getCsiAttacherFromVolumeAttacher(attacher, testWatchTimeout) 832 if csiAttacher.plugin == nil { 833 t.Error("plugin not set for attacher") 834 } 835 if csiAttacher.k8s == nil { 836 t.Error("Kubernetes client not set for attacher") 837 } 838 if csiAttacher.watchTimeout == time.Duration(0) { 839 t.Error("watch timeout not set for attacher") 840 } 841 } 842 843 func TestPluginNewDetacher(t *testing.T) { 844 plug, tmpDir := newTestPlugin(t, nil) 845 defer os.RemoveAll(tmpDir) 846 847 detacher, err := plug.NewDetacher() 848 if err != nil { 849 t.Fatalf("failed to create new detacher: %v", err) 850 } 851 852 csiDetacher := getCsiAttacherFromVolumeDetacher(detacher, testWatchTimeout) 853 if csiDetacher.plugin == nil { 854 t.Error("plugin not set for detacher") 855 } 856 if csiDetacher.k8s == nil { 857 t.Error("Kubernetes client not set for detacher") 858 } 859 if csiDetacher.watchTimeout == time.Duration(0) { 860 t.Error("watch timeout not set for detacher") 861 } 862 } 863 864 func TestPluginCanAttach(t *testing.T) { 865 tests := []struct { 866 name string 867 driverName string 868 spec *volume.Spec 869 canAttach bool 870 shouldFail bool 871 }{ 872 { 873 name: "non-attachable inline", 874 driverName: "attachable-inline", 875 spec: volume.NewSpecFromVolume(makeTestVol("test-vol", "attachable-inline")), 876 canAttach: false, 877 }, 878 { 879 name: "attachable PV", 880 driverName: "attachable-pv", 881 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "attachable-pv", testVol), true), 882 canAttach: true, 883 }, 884 { 885 name: "incomplete spec", 886 driverName: "attachable-pv", 887 spec: &volume.Spec{ReadOnly: true}, 888 canAttach: false, 889 shouldFail: true, 890 }, 891 { 892 name: "nil spec", 893 driverName: "attachable-pv", 894 canAttach: false, 895 shouldFail: true, 896 }, 897 } 898 899 for _, test := range tests { 900 t.Run(test.name, func(t *testing.T) { 901 csiDriver := getTestCSIDriver(test.driverName, nil, &test.canAttach, nil) 902 fakeCSIClient := fakeclient.NewSimpleClientset(csiDriver) 903 plug, tmpDir := newTestPlugin(t, fakeCSIClient) 904 defer os.RemoveAll(tmpDir) 905 906 pluginCanAttach, err := plug.CanAttach(test.spec) 907 if err != nil && !test.shouldFail { 908 t.Fatalf("unexpected plugin.CanAttach error: %s", err) 909 } 910 if pluginCanAttach != test.canAttach { 911 t.Fatalf("expecting plugin.CanAttach %t got %t", test.canAttach, pluginCanAttach) 912 } 913 }) 914 } 915 } 916 917 func TestPluginFindAttachablePlugin(t *testing.T) { 918 tests := []struct { 919 name string 920 driverName string 921 spec *volume.Spec 922 canAttach bool 923 shouldFail bool 924 }{ 925 { 926 name: "non-attachable inline", 927 driverName: "attachable-inline", 928 spec: volume.NewSpecFromVolume(makeTestVol("test-vol", "attachable-inline")), 929 canAttach: false, 930 }, 931 { 932 name: "attachable PV", 933 driverName: "attachable-pv", 934 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "attachable-pv", testVol), true), 935 canAttach: true, 936 }, 937 { 938 name: "incomplete spec", 939 driverName: "attachable-pv", 940 spec: &volume.Spec{ReadOnly: true}, 941 canAttach: false, 942 shouldFail: true, 943 }, 944 { 945 name: "nil spec", 946 driverName: "attachable-pv", 947 canAttach: false, 948 shouldFail: true, 949 }, 950 } 951 952 for _, test := range tests { 953 t.Run(test.name, func(t *testing.T) { 954 tmpDir, err := utiltesting.MkTmpdir("csi-test") 955 if err != nil { 956 t.Fatalf("can't create temp dir: %v", err) 957 } 958 defer os.RemoveAll(tmpDir) 959 960 client := fakeclient.NewSimpleClientset( 961 getTestCSIDriver(test.driverName, nil, &test.canAttach, nil), 962 &api.Node{ 963 ObjectMeta: meta.ObjectMeta{ 964 Name: "fakeNode", 965 }, 966 Spec: api.NodeSpec{}, 967 }, 968 ) 969 factory := informers.NewSharedInformerFactory(client, CsiResyncPeriod) 970 host := volumetest.NewFakeKubeletVolumeHostWithCSINodeName(t, 971 tmpDir, 972 client, 973 ProbeVolumePlugins(), 974 "fakeNode", 975 factory.Storage().V1().CSIDrivers().Lister(), 976 factory.Storage().V1().VolumeAttachments().Lister(), 977 ) 978 979 plugMgr := host.GetPluginMgr() 980 981 plugin, err := plugMgr.FindAttachablePluginBySpec(test.spec) 982 if err != nil && !test.shouldFail { 983 t.Fatalf("unexpected error calling pluginMgr.FindAttachablePluginBySpec: %s", err) 984 } 985 if (plugin != nil) != test.canAttach { 986 t.Fatal("expecting attachable plugin, but got nil") 987 } 988 }) 989 } 990 } 991 992 func TestPluginCanDeviceMount(t *testing.T) { 993 tests := []struct { 994 name string 995 driverName string 996 spec *volume.Spec 997 canDeviceMount bool 998 shouldFail bool 999 }{ 1000 { 1001 name: "non device mountable inline", 1002 driverName: "inline-driver", 1003 spec: volume.NewSpecFromVolume(makeTestVol("test-vol", "inline-driver")), 1004 canDeviceMount: false, 1005 }, 1006 { 1007 name: "device mountable PV", 1008 driverName: "device-mountable-pv", 1009 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "device-mountable-pv", testVol), true), 1010 canDeviceMount: true, 1011 }, 1012 { 1013 name: "incomplete spec", 1014 driverName: "device-unmountable", 1015 spec: &volume.Spec{ReadOnly: true}, 1016 canDeviceMount: false, 1017 shouldFail: true, 1018 }, 1019 { 1020 name: "missing spec", 1021 driverName: "device-unmountable", 1022 canDeviceMount: false, 1023 shouldFail: true, 1024 }, 1025 } 1026 1027 for _, test := range tests { 1028 t.Run(test.name, func(t *testing.T) { 1029 plug, tmpDir := newTestPlugin(t, nil) 1030 defer os.RemoveAll(tmpDir) 1031 1032 pluginCanDeviceMount, err := plug.CanDeviceMount(test.spec) 1033 if err != nil && !test.shouldFail { 1034 t.Fatalf("unexpected error in plug.CanDeviceMount: %s", err) 1035 } 1036 if pluginCanDeviceMount != test.canDeviceMount { 1037 t.Fatalf("expecting plugin.CanAttach %t got %t", test.canDeviceMount, pluginCanDeviceMount) 1038 } 1039 }) 1040 } 1041 } 1042 1043 func TestPluginFindDeviceMountablePluginBySpec(t *testing.T) { 1044 tests := []struct { 1045 name string 1046 driverName string 1047 spec *volume.Spec 1048 canDeviceMount bool 1049 shouldFail bool 1050 }{ 1051 { 1052 name: "non device mountable inline", 1053 driverName: "inline-driver", 1054 spec: volume.NewSpecFromVolume(makeTestVol("test-vol", "inline-driver")), 1055 canDeviceMount: false, 1056 }, 1057 { 1058 name: "device mountable PV", 1059 driverName: "device-mountable-pv", 1060 spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "device-mountable-pv", testVol), true), 1061 canDeviceMount: true, 1062 }, 1063 { 1064 name: "incomplete spec", 1065 driverName: "device-unmountable", 1066 spec: &volume.Spec{ReadOnly: true}, 1067 canDeviceMount: false, 1068 shouldFail: true, 1069 }, 1070 { 1071 name: "missing spec", 1072 driverName: "device-unmountable", 1073 canDeviceMount: false, 1074 shouldFail: true, 1075 }, 1076 } 1077 1078 for _, test := range tests { 1079 t.Run(test.name, func(t *testing.T) { 1080 tmpDir, err := utiltesting.MkTmpdir("csi-test") 1081 if err != nil { 1082 t.Fatalf("can't create temp dir: %v", err) 1083 } 1084 defer os.RemoveAll(tmpDir) 1085 1086 client := fakeclient.NewSimpleClientset( 1087 &api.Node{ 1088 ObjectMeta: meta.ObjectMeta{ 1089 Name: "fakeNode", 1090 }, 1091 Spec: api.NodeSpec{}, 1092 }, 1093 ) 1094 host := volumetest.NewFakeVolumeHostWithCSINodeName(t, tmpDir, client, ProbeVolumePlugins(), "fakeNode", nil, nil) 1095 plugMgr := host.GetPluginMgr() 1096 plug, err := plugMgr.FindDeviceMountablePluginBySpec(test.spec) 1097 if err != nil && !test.shouldFail { 1098 t.Fatalf("unexpected error in plugMgr.FindDeviceMountablePluginBySpec: %s", err) 1099 } 1100 if (plug != nil) != test.canDeviceMount { 1101 t.Fatalf("expecting deviceMountablePlugin, but got nil") 1102 } 1103 }) 1104 } 1105 } 1106 1107 func TestPluginNewBlockMapper(t *testing.T) { 1108 plug, tmpDir := newTestPlugin(t, nil) 1109 defer os.RemoveAll(tmpDir) 1110 1111 registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t) 1112 pv := makeTestPV("test-block-pv", 10, testDriver, testVol) 1113 mounter, err := plug.NewBlockVolumeMapper( 1114 volume.NewSpecFromPersistentVolume(pv, pv.Spec.PersistentVolumeSource.CSI.ReadOnly), 1115 &api.Pod{ObjectMeta: meta.ObjectMeta{UID: testPodUID, Namespace: testns}}, 1116 volume.VolumeOptions{}, 1117 ) 1118 if err != nil { 1119 t.Fatalf("Failed to make a new BlockMapper: %v", err) 1120 } 1121 1122 if mounter == nil { 1123 t.Fatal("failed to create CSI BlockMapper, mapper is nill") 1124 } 1125 csiMapper := mounter.(*csiBlockMapper) 1126 1127 // validate mounter fields 1128 if string(csiMapper.driverName) != testDriver { 1129 t.Error("CSI block mapper missing driver name") 1130 } 1131 if csiMapper.volumeID != testVol { 1132 t.Error("CSI block mapper missing volumeID") 1133 } 1134 1135 if csiMapper.podUID == types.UID("") { 1136 t.Error("CSI block mapper missing pod.UID") 1137 } 1138 csiClient, err := csiMapper.csiClientGetter.Get() 1139 if csiClient == nil { 1140 t.Errorf("mapper csiClient is nil: %v", err) 1141 } 1142 1143 // ensure data file is created 1144 dataFile := getVolumeDeviceDataDir(csiMapper.spec.Name(), plug.host) 1145 if _, err := os.Stat(dataFile); err != nil { 1146 if os.IsNotExist(err) { 1147 t.Errorf("data file not created %s", dataFile) 1148 } else { 1149 t.Fatal(err) 1150 } 1151 } 1152 } 1153 1154 func TestPluginNewUnmapper(t *testing.T) { 1155 plug, tmpDir := newTestPlugin(t, nil) 1156 defer os.RemoveAll(tmpDir) 1157 1158 registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t) 1159 pv := makeTestPV("test-pv", 10, testDriver, testVol) 1160 1161 // save the data file to re-create client 1162 dir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host) 1163 if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) { 1164 t.Errorf("failed to create dir [%s]: %v", dir, err) 1165 } 1166 1167 if err := saveVolumeData( 1168 dir, 1169 volDataFileName, 1170 map[string]string{ 1171 volDataKey.specVolID: pv.ObjectMeta.Name, 1172 volDataKey.driverName: testDriver, 1173 volDataKey.volHandle: testVol, 1174 }, 1175 ); err != nil { 1176 t.Fatalf("failed to save volume data: %v", err) 1177 } 1178 1179 // test unmounter 1180 unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID) 1181 csiUnmapper := unmapper.(*csiBlockMapper) 1182 1183 if err != nil { 1184 t.Fatalf("Failed to make a new Unmounter: %v", err) 1185 } 1186 1187 if csiUnmapper == nil { 1188 t.Fatal("failed to create CSI Unmounter") 1189 } 1190 1191 if csiUnmapper.podUID != testPodUID { 1192 t.Error("podUID not set") 1193 } 1194 1195 if csiUnmapper.specName != pv.ObjectMeta.Name { 1196 t.Error("specName not set") 1197 } 1198 1199 csiClient, err := csiUnmapper.csiClientGetter.Get() 1200 if csiClient == nil { 1201 t.Errorf("unmapper csiClient is nil: %v", err) 1202 } 1203 1204 // test loaded vol data 1205 if string(csiUnmapper.driverName) != testDriver { 1206 t.Error("unmapper driverName not set") 1207 } 1208 if csiUnmapper.volumeID != testVol { 1209 t.Error("unmapper volumeHandle not set") 1210 } 1211 } 1212 1213 func TestPluginConstructBlockVolumeSpec(t *testing.T) { 1214 plug, tmpDir := newTestPlugin(t, nil) 1215 defer os.RemoveAll(tmpDir) 1216 1217 testCases := []struct { 1218 name string 1219 specVolID string 1220 data map[string]string 1221 shouldFail bool 1222 }{ 1223 { 1224 name: "valid spec name", 1225 specVolID: "test.vol.id", 1226 data: map[string]string{volDataKey.specVolID: "test.vol.id", volDataKey.volHandle: "test-vol0", volDataKey.driverName: "test-driver0"}, 1227 }, 1228 } 1229 1230 for _, tc := range testCases { 1231 t.Logf("test case: %s", tc.name) 1232 deviceDataDir := getVolumeDeviceDataDir(tc.specVolID, plug.host) 1233 1234 // create data file in csi plugin dir 1235 if tc.data != nil { 1236 if err := os.MkdirAll(deviceDataDir, 0755); err != nil && !os.IsNotExist(err) { 1237 t.Errorf("failed to create dir [%s]: %v", deviceDataDir, err) 1238 } 1239 if err := saveVolumeData(deviceDataDir, volDataFileName, tc.data); err != nil { 1240 t.Fatal(err) 1241 } 1242 } 1243 1244 // rebuild spec 1245 spec, err := plug.ConstructBlockVolumeSpec("test-podUID", tc.specVolID, getVolumeDevicePluginDir(tc.specVolID, plug.host)) 1246 if tc.shouldFail { 1247 if err == nil { 1248 t.Fatal("expecting ConstructVolumeSpec to fail, but got nil error") 1249 } 1250 continue 1251 } 1252 1253 if spec.PersistentVolume.Spec.VolumeMode == nil { 1254 t.Fatalf("Volume mode has not been set.") 1255 } 1256 1257 if *spec.PersistentVolume.Spec.VolumeMode != api.PersistentVolumeBlock { 1258 t.Errorf("Unexpected volume mode %q", *spec.PersistentVolume.Spec.VolumeMode) 1259 } 1260 1261 volHandle := spec.PersistentVolume.Spec.CSI.VolumeHandle 1262 if volHandle != tc.data[volDataKey.volHandle] { 1263 t.Errorf("expected volID %s, got volID %s", tc.data[volDataKey.volHandle], volHandle) 1264 } 1265 1266 if spec.Name() != tc.specVolID { 1267 t.Errorf("Unexpected spec name %s", spec.Name()) 1268 } 1269 } 1270 } 1271 1272 func TestValidatePlugin(t *testing.T) { 1273 testCases := []struct { 1274 pluginName string 1275 endpoint string 1276 versions []string 1277 shouldFail bool 1278 }{ 1279 { 1280 pluginName: "test.plugin", 1281 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1282 versions: []string{"v1.0.0"}, 1283 shouldFail: false, 1284 }, 1285 { 1286 pluginName: "test.plugin", 1287 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1288 versions: []string{"0.3.0"}, 1289 shouldFail: true, 1290 }, 1291 { 1292 pluginName: "test.plugin", 1293 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1294 versions: []string{"0.2.0"}, 1295 shouldFail: true, 1296 }, 1297 { 1298 pluginName: "test.plugin", 1299 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1300 versions: []string{"0.2.0", "v0.3.0"}, 1301 shouldFail: true, 1302 }, 1303 { 1304 pluginName: "test.plugin", 1305 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1306 versions: []string{"0.2.0", "v1.0.0"}, 1307 shouldFail: false, 1308 }, 1309 { 1310 pluginName: "test.plugin", 1311 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1312 versions: []string{"0.2.0", "v1.2.3"}, 1313 shouldFail: false, 1314 }, 1315 { 1316 pluginName: "test.plugin", 1317 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1318 versions: []string{"v1.2.3", "v0.3.0"}, 1319 shouldFail: false, 1320 }, 1321 { 1322 pluginName: "test.plugin", 1323 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1324 versions: []string{"v1.2.3", "v0.3.0", "2.0.1"}, 1325 shouldFail: false, 1326 }, 1327 { 1328 pluginName: "test.plugin", 1329 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1330 versions: []string{"v1.2.3", "4.9.12", "v0.3.0", "2.0.1"}, 1331 shouldFail: false, 1332 }, 1333 { 1334 pluginName: "test.plugin", 1335 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1336 versions: []string{"v1.2.3", "boo", "v0.3.0", "2.0.1"}, 1337 shouldFail: false, 1338 }, 1339 { 1340 pluginName: "test.plugin", 1341 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1342 versions: []string{"4.9.12", "2.0.1"}, 1343 shouldFail: true, 1344 }, 1345 { 1346 pluginName: "test.plugin", 1347 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1348 versions: []string{}, 1349 shouldFail: true, 1350 }, 1351 { 1352 pluginName: "test.plugin", 1353 endpoint: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1354 versions: []string{"var", "boo", "foo"}, 1355 shouldFail: true, 1356 }, 1357 } 1358 1359 for _, tc := range testCases { 1360 // Arrange & Act 1361 err := PluginHandler.ValidatePlugin(tc.pluginName, tc.endpoint, tc.versions) 1362 1363 // Assert 1364 if tc.shouldFail && err == nil { 1365 t.Fatalf("expecting ValidatePlugin to fail, but got nil error for testcase: %#v", tc) 1366 } 1367 if !tc.shouldFail && err != nil { 1368 t.Fatalf("unexpected error during ValidatePlugin for testcase: %#v\r\n err:%v", tc, err) 1369 } 1370 } 1371 } 1372 1373 func TestValidatePluginExistingDriver(t *testing.T) { 1374 testCases := []struct { 1375 pluginName1 string 1376 endpoint1 string 1377 versions1 []string 1378 pluginName2 string 1379 endpoint2 string 1380 versions2 []string 1381 shouldFail bool 1382 }{ 1383 { 1384 pluginName1: "test.plugin", 1385 endpoint1: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1386 versions1: []string{"v1.0.0"}, 1387 pluginName2: "test.plugin2", 1388 endpoint2: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1389 versions2: []string{"v1.0.0"}, 1390 shouldFail: false, 1391 }, 1392 { 1393 pluginName1: "test.plugin", 1394 endpoint1: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1395 versions1: []string{"v1.0.0"}, 1396 pluginName2: "test.plugin", 1397 endpoint2: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1398 versions2: []string{"v1.0.0"}, 1399 shouldFail: true, 1400 }, 1401 { 1402 pluginName1: "test.plugin", 1403 endpoint1: "/var/log/kubelet/plugins/myplugin/csi.sock", 1404 versions1: []string{"v0.3.0", "v0.2.0", "v1.0.0"}, 1405 pluginName2: "test.plugin", 1406 endpoint2: "/var/log/kubelet/plugins_registry/myplugin/csi.sock", 1407 versions2: []string{"v1.0.1"}, 1408 shouldFail: false, 1409 }, 1410 } 1411 1412 for _, tc := range testCases { 1413 // Arrange & Act 1414 highestSupportedVersions1, err := utilversion.HighestSupportedVersion(tc.versions1) 1415 if err != nil { 1416 t.Fatalf("unexpected error parsing version for testcase: %#v: %v", tc, err) 1417 } 1418 1419 csiDrivers.Clear() 1420 csiDrivers.Set(tc.pluginName1, Driver{ 1421 endpoint: tc.endpoint1, 1422 highestSupportedVersion: highestSupportedVersions1, 1423 }) 1424 1425 // Arrange & Act 1426 err = PluginHandler.ValidatePlugin(tc.pluginName2, tc.endpoint2, tc.versions2) 1427 1428 // Assert 1429 if tc.shouldFail && err == nil { 1430 t.Fatalf("expecting ValidatePlugin to fail, but got nil error for testcase: %#v", tc) 1431 } 1432 if !tc.shouldFail && err != nil { 1433 t.Fatalf("unexpected error during ValidatePlugin for testcase: %#v\r\n err:%v", tc, err) 1434 } 1435 } 1436 }