k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/storage/drivers/in_tree.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 /* 18 * This file defines various in-tree volume test drivers for TestSuites. 19 * 20 * There are two ways, how to prepare test drivers: 21 * 1) With containerized server (NFS, Ceph, iSCSI, ...) 22 * It creates a server pod which defines one volume for the tests. 23 * These tests work only when privileged containers are allowed, exporting 24 * various filesystems (like NFS) usually needs some mounting or 25 * other privileged magic in the server pod. 26 * 27 * Note that the server containers are for testing purposes only and should not 28 * be used in production. 29 * 30 * 2) With server or cloud provider outside of Kubernetes (Cinder, GCE, AWS, Azure, ...) 31 * Appropriate server or cloud provider must exist somewhere outside 32 * the tested Kubernetes cluster. CreateVolume will create a new volume to be 33 * used in the TestSuites for inlineVolume or DynamicPV tests. 34 */ 35 36 package drivers 37 38 import ( 39 "context" 40 "fmt" 41 "strconv" 42 "strings" 43 "time" 44 45 "github.com/onsi/ginkgo/v2" 46 v1 "k8s.io/api/core/v1" 47 rbacv1 "k8s.io/api/rbac/v1" 48 storagev1 "k8s.io/api/storage/v1" 49 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 50 "k8s.io/apimachinery/pkg/runtime/schema" 51 "k8s.io/apimachinery/pkg/util/sets" 52 "k8s.io/apiserver/pkg/authentication/serviceaccount" 53 clientset "k8s.io/client-go/kubernetes" 54 "k8s.io/kubernetes/test/e2e/feature" 55 "k8s.io/kubernetes/test/e2e/framework" 56 e2eauth "k8s.io/kubernetes/test/e2e/framework/auth" 57 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 58 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 59 e2epv "k8s.io/kubernetes/test/e2e/framework/pv" 60 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 61 e2evolume "k8s.io/kubernetes/test/e2e/framework/volume" 62 storageframework "k8s.io/kubernetes/test/e2e/storage/framework" 63 "k8s.io/kubernetes/test/e2e/storage/utils" 64 imageutils "k8s.io/kubernetes/test/utils/image" 65 ) 66 67 const ( 68 // Template for iSCSI IQN. 69 iSCSIIQNTemplate = "iqn.2003-01.io.k8s:e2e.%s" 70 ) 71 72 // NFS 73 type nfsDriver struct { 74 externalProvisionerPod *v1.Pod 75 externalPluginName string 76 77 driverInfo storageframework.DriverInfo 78 } 79 80 type nfsVolume struct { 81 serverHost string 82 serverPod *v1.Pod 83 f *framework.Framework 84 } 85 86 var _ storageframework.TestDriver = &nfsDriver{} 87 var _ storageframework.PreprovisionedVolumeTestDriver = &nfsDriver{} 88 var _ storageframework.InlineVolumeTestDriver = &nfsDriver{} 89 var _ storageframework.PreprovisionedPVTestDriver = &nfsDriver{} 90 var _ storageframework.DynamicPVTestDriver = &nfsDriver{} 91 92 // InitNFSDriver returns nfsDriver that implements TestDriver interface 93 func InitNFSDriver() storageframework.TestDriver { 94 return &nfsDriver{ 95 driverInfo: storageframework.DriverInfo{ 96 Name: "nfs", 97 InTreePluginName: "kubernetes.io/nfs", 98 MaxFileSize: storageframework.FileSizeLarge, 99 SupportedSizeRange: e2evolume.SizeRange{ 100 Min: "1Gi", 101 }, 102 SupportedFsType: sets.NewString( 103 "", // Default fsType 104 ), 105 SupportedMountOption: sets.NewString("relatime"), 106 RequiredMountOption: sets.NewString("vers=4.0"), 107 Capabilities: map[storageframework.Capability]bool{ 108 storageframework.CapPersistence: true, 109 storageframework.CapExec: true, 110 storageframework.CapRWX: true, 111 storageframework.CapMultiPODs: true, 112 storageframework.CapMultiplePVsSameID: true, 113 }, 114 }, 115 } 116 } 117 118 func (n *nfsDriver) GetDriverInfo() *storageframework.DriverInfo { 119 return &n.driverInfo 120 } 121 122 func (n *nfsDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 123 } 124 125 func (n *nfsDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource { 126 nv, ok := e2evolume.(*nfsVolume) 127 if !ok { 128 framework.Failf("Failed to cast test volume of type %T to the NFS test volume", e2evolume) 129 } 130 return &v1.VolumeSource{ 131 NFS: &v1.NFSVolumeSource{ 132 Server: nv.serverHost, 133 Path: "/", 134 ReadOnly: readOnly, 135 }, 136 } 137 } 138 139 func (n *nfsDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) { 140 nv, ok := e2evolume.(*nfsVolume) 141 if !ok { 142 framework.Failf("Failed to cast test volume of type %T to the NFS test volume", e2evolume) 143 } 144 return &v1.PersistentVolumeSource{ 145 NFS: &v1.NFSVolumeSource{ 146 Server: nv.serverHost, 147 Path: "/", 148 ReadOnly: readOnly, 149 }, 150 }, nil 151 } 152 153 func (n *nfsDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { 154 provisioner := n.externalPluginName 155 parameters := map[string]string{"mountOptions": "vers=4.0"} 156 ns := config.Framework.Namespace.Name 157 158 return storageframework.GetStorageClass(provisioner, parameters, nil, ns) 159 } 160 161 func (n *nfsDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 162 cs := f.ClientSet 163 ns := f.Namespace 164 n.externalPluginName = fmt.Sprintf("example.com/nfs-%s", ns.Name) 165 166 // TODO(mkimuram): cluster-admin gives too much right but system:persistent-volume-provisioner 167 // is not enough. We should create new clusterrole for testing. 168 err := e2eauth.BindClusterRole(ctx, cs.RbacV1(), "cluster-admin", ns.Name, 169 rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Namespace: ns.Name, Name: "default"}) 170 framework.ExpectNoError(err) 171 ginkgo.DeferCleanup(cs.RbacV1().ClusterRoleBindings().Delete, ns.Name+"--"+"cluster-admin", *metav1.NewDeleteOptions(0)) 172 173 err = e2eauth.WaitForAuthorizationUpdate(ctx, cs.AuthorizationV1(), 174 serviceaccount.MakeUsername(ns.Name, "default"), 175 "", "get", schema.GroupResource{Group: "storage.k8s.io", Resource: "storageclasses"}, true) 176 framework.ExpectNoError(err, "Failed to update authorization: %v", err) 177 178 ginkgo.By("creating an external dynamic provisioner pod") 179 n.externalProvisionerPod = utils.StartExternalProvisioner(ctx, cs, ns.Name, n.externalPluginName) 180 ginkgo.DeferCleanup(e2epod.DeletePodWithWait, cs, n.externalProvisionerPod) 181 182 return &storageframework.PerTestConfig{ 183 Driver: n, 184 Prefix: "nfs", 185 Framework: f, 186 } 187 } 188 189 func (n *nfsDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { 190 f := config.Framework 191 cs := f.ClientSet 192 ns := f.Namespace 193 194 // NewNFSServer creates a pod for InlineVolume and PreprovisionedPV, 195 // and startExternalProvisioner creates a pod for DynamicPV. 196 // Therefore, we need a different PrepareTest logic for volType. 197 switch volType { 198 case storageframework.InlineVolume: 199 fallthrough 200 case storageframework.PreprovisionedPV: 201 c, serverPod, serverHost := e2evolume.NewNFSServer(ctx, cs, ns.Name, []string{}) 202 config.ServerConfig = &c 203 return &nfsVolume{ 204 serverHost: serverHost, 205 serverPod: serverPod, 206 f: f, 207 } 208 case storageframework.DynamicPV: 209 // Do nothing 210 default: 211 framework.Failf("Unsupported volType:%v is specified", volType) 212 } 213 return nil 214 } 215 216 func (v *nfsVolume) DeleteVolume(ctx context.Context) { 217 cleanUpVolumeServer(ctx, v.f, v.serverPod) 218 } 219 220 // iSCSI 221 // The iscsiadm utility and iscsi target kernel modules must be installed on all nodes. 222 type iSCSIDriver struct { 223 driverInfo storageframework.DriverInfo 224 } 225 type iSCSIVolume struct { 226 serverPod *v1.Pod 227 serverIP string 228 f *framework.Framework 229 iqn string 230 } 231 232 var _ storageframework.TestDriver = &iSCSIDriver{} 233 var _ storageframework.PreprovisionedVolumeTestDriver = &iSCSIDriver{} 234 var _ storageframework.InlineVolumeTestDriver = &iSCSIDriver{} 235 var _ storageframework.PreprovisionedPVTestDriver = &iSCSIDriver{} 236 237 // InitISCSIDriver returns iSCSIDriver that implements TestDriver interface 238 func InitISCSIDriver() storageframework.TestDriver { 239 return &iSCSIDriver{ 240 driverInfo: storageframework.DriverInfo{ 241 Name: "iscsi", 242 InTreePluginName: "kubernetes.io/iscsi", 243 TestTags: []interface{}{feature.Volumes}, 244 MaxFileSize: storageframework.FileSizeMedium, 245 SupportedFsType: sets.NewString( 246 "", // Default fsType 247 "ext4", 248 ), 249 TopologyKeys: []string{v1.LabelHostname}, 250 Capabilities: map[storageframework.Capability]bool{ 251 storageframework.CapPersistence: true, 252 storageframework.CapFsGroup: true, 253 storageframework.CapBlock: true, 254 storageframework.CapExec: true, 255 storageframework.CapMultiPODs: true, 256 storageframework.CapTopology: true, 257 storageframework.CapMultiplePVsSameID: true, 258 }, 259 }, 260 } 261 } 262 263 func (i *iSCSIDriver) GetDriverInfo() *storageframework.DriverInfo { 264 return &i.driverInfo 265 } 266 267 func (i *iSCSIDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 268 } 269 270 func (i *iSCSIDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource { 271 iv, ok := e2evolume.(*iSCSIVolume) 272 if !ok { 273 framework.Failf("failed to cast test volume of type %T to the iSCSI test volume", e2evolume) 274 } 275 276 volSource := v1.VolumeSource{ 277 ISCSI: &v1.ISCSIVolumeSource{ 278 TargetPortal: "127.0.0.1:3260", 279 IQN: iv.iqn, 280 Lun: 0, 281 ReadOnly: readOnly, 282 }, 283 } 284 if fsType != "" { 285 volSource.ISCSI.FSType = fsType 286 } 287 return &volSource 288 } 289 290 func (i *iSCSIDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) { 291 iv, ok := e2evolume.(*iSCSIVolume) 292 if !ok { 293 framework.Failf("failed to cast test volume of type %T to the iSCSI test volume", e2evolume) 294 } 295 296 pvSource := v1.PersistentVolumeSource{ 297 ISCSI: &v1.ISCSIPersistentVolumeSource{ 298 TargetPortal: "127.0.0.1:3260", 299 IQN: iv.iqn, 300 Lun: 0, 301 ReadOnly: readOnly, 302 }, 303 } 304 if fsType != "" { 305 pvSource.ISCSI.FSType = fsType 306 } 307 return &pvSource, nil 308 } 309 310 func (i *iSCSIDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 311 return &storageframework.PerTestConfig{ 312 Driver: i, 313 Prefix: "iscsi", 314 Framework: f, 315 } 316 } 317 318 func (i *iSCSIDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { 319 f := config.Framework 320 cs := f.ClientSet 321 ns := f.Namespace 322 323 c, serverPod, serverIP, iqn := newISCSIServer(ctx, cs, ns.Name) 324 config.ServerConfig = &c 325 config.ClientNodeSelection = c.ClientNodeSelection 326 return &iSCSIVolume{ 327 serverPod: serverPod, 328 serverIP: serverIP, 329 iqn: iqn, 330 f: f, 331 } 332 } 333 334 // newISCSIServer is an iSCSI-specific wrapper for CreateStorageServer. 335 func newISCSIServer(ctx context.Context, cs clientset.Interface, namespace string) (config e2evolume.TestConfig, pod *v1.Pod, ip, iqn string) { 336 // Generate cluster-wide unique IQN 337 iqn = fmt.Sprintf(iSCSIIQNTemplate, namespace) 338 config = e2evolume.TestConfig{ 339 Namespace: namespace, 340 Prefix: "iscsi", 341 ServerImage: imageutils.GetE2EImage(imageutils.VolumeISCSIServer), 342 ServerArgs: []string{iqn}, 343 ServerVolumes: map[string]string{ 344 // iSCSI container needs to insert modules from the host 345 "/lib/modules": "/lib/modules", 346 // iSCSI container needs to configure kernel 347 "/sys/kernel": "/sys/kernel", 348 // iSCSI source "block devices" must be available on the host 349 "/srv/iscsi": "/srv/iscsi", 350 // targetcli uses dbus 351 "/run/dbus": "/run/dbus", 352 }, 353 ServerReadyMessage: "iscsi target started", 354 ServerHostNetwork: true, 355 } 356 pod, ip = e2evolume.CreateStorageServer(ctx, cs, config) 357 // Make sure the client runs on the same node as server so we don't need to open any firewalls. 358 config.ClientNodeSelection = e2epod.NodeSelection{Name: pod.Spec.NodeName} 359 return config, pod, ip, iqn 360 } 361 362 func (v *iSCSIVolume) DeleteVolume(ctx context.Context) { 363 cleanUpVolumeServer(ctx, v.f, v.serverPod) 364 } 365 366 // Hostpath 367 type hostPathDriver struct { 368 driverInfo storageframework.DriverInfo 369 } 370 371 var _ storageframework.TestDriver = &hostPathDriver{} 372 var _ storageframework.PreprovisionedVolumeTestDriver = &hostPathDriver{} 373 var _ storageframework.InlineVolumeTestDriver = &hostPathDriver{} 374 375 // InitHostPathDriver returns hostPathDriver that implements TestDriver interface 376 func InitHostPathDriver() storageframework.TestDriver { 377 return &hostPathDriver{ 378 driverInfo: storageframework.DriverInfo{ 379 Name: "hostPath", 380 InTreePluginName: "kubernetes.io/host-path", 381 MaxFileSize: storageframework.FileSizeMedium, 382 SupportedFsType: sets.NewString( 383 "", // Default fsType 384 ), 385 TopologyKeys: []string{v1.LabelHostname}, 386 Capabilities: map[storageframework.Capability]bool{ 387 storageframework.CapPersistence: true, 388 storageframework.CapMultiPODs: true, 389 storageframework.CapSingleNodeVolume: true, 390 storageframework.CapTopology: true, 391 storageframework.CapMultiplePVsSameID: true, 392 }, 393 }, 394 } 395 } 396 397 func (h *hostPathDriver) GetDriverInfo() *storageframework.DriverInfo { 398 return &h.driverInfo 399 } 400 401 func (h *hostPathDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 402 } 403 404 func (h *hostPathDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource { 405 // hostPath doesn't support readOnly volume 406 if readOnly { 407 return nil 408 } 409 return &v1.VolumeSource{ 410 HostPath: &v1.HostPathVolumeSource{ 411 Path: "/tmp", 412 }, 413 } 414 } 415 416 func (h *hostPathDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 417 return &storageframework.PerTestConfig{ 418 Driver: h, 419 Prefix: "hostpath", 420 Framework: f, 421 } 422 } 423 424 func (h *hostPathDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { 425 f := config.Framework 426 cs := f.ClientSet 427 428 // pods should be scheduled on the node 429 node, err := e2enode.GetRandomReadySchedulableNode(ctx, cs) 430 framework.ExpectNoError(err) 431 config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name} 432 return nil 433 } 434 435 // HostPathSymlink 436 type hostPathSymlinkDriver struct { 437 driverInfo storageframework.DriverInfo 438 } 439 440 type hostPathSymlinkVolume struct { 441 targetPath string 442 sourcePath string 443 prepPod *v1.Pod 444 f *framework.Framework 445 } 446 447 var _ storageframework.TestDriver = &hostPathSymlinkDriver{} 448 var _ storageframework.PreprovisionedVolumeTestDriver = &hostPathSymlinkDriver{} 449 var _ storageframework.InlineVolumeTestDriver = &hostPathSymlinkDriver{} 450 451 // InitHostPathSymlinkDriver returns hostPathSymlinkDriver that implements TestDriver interface 452 func InitHostPathSymlinkDriver() storageframework.TestDriver { 453 return &hostPathSymlinkDriver{ 454 driverInfo: storageframework.DriverInfo{ 455 Name: "hostPathSymlink", 456 InTreePluginName: "kubernetes.io/host-path", 457 MaxFileSize: storageframework.FileSizeMedium, 458 SupportedFsType: sets.NewString( 459 "", // Default fsType 460 ), 461 TopologyKeys: []string{v1.LabelHostname}, 462 Capabilities: map[storageframework.Capability]bool{ 463 storageframework.CapPersistence: true, 464 storageframework.CapMultiPODs: true, 465 storageframework.CapSingleNodeVolume: true, 466 storageframework.CapTopology: true, 467 storageframework.CapMultiplePVsSameID: true, 468 }, 469 }, 470 } 471 } 472 473 func (h *hostPathSymlinkDriver) GetDriverInfo() *storageframework.DriverInfo { 474 return &h.driverInfo 475 } 476 477 func (h *hostPathSymlinkDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 478 } 479 480 func (h *hostPathSymlinkDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource { 481 hv, ok := e2evolume.(*hostPathSymlinkVolume) 482 if !ok { 483 framework.Failf("Failed to cast test volume of type %T to the Hostpath Symlink test volume", e2evolume) 484 } 485 486 // hostPathSymlink doesn't support readOnly volume 487 if readOnly { 488 return nil 489 } 490 return &v1.VolumeSource{ 491 HostPath: &v1.HostPathVolumeSource{ 492 Path: hv.targetPath, 493 }, 494 } 495 } 496 497 func (h *hostPathSymlinkDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 498 return &storageframework.PerTestConfig{ 499 Driver: h, 500 Prefix: "hostpathsymlink", 501 Framework: f, 502 } 503 } 504 505 func (h *hostPathSymlinkDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { 506 f := config.Framework 507 cs := f.ClientSet 508 509 sourcePath := fmt.Sprintf("/tmp/%v", f.Namespace.Name) 510 targetPath := fmt.Sprintf("/tmp/%v-link", f.Namespace.Name) 511 volumeName := "test-volume" 512 513 // pods should be scheduled on the node 514 node, err := e2enode.GetRandomReadySchedulableNode(ctx, cs) 515 framework.ExpectNoError(err) 516 config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name} 517 518 cmd := fmt.Sprintf("mkdir %v -m 777 && ln -s %v %v", sourcePath, sourcePath, targetPath) 519 privileged := true 520 521 // Launch pod to initialize hostPath directory and symlink 522 prepPod := &v1.Pod{ 523 ObjectMeta: metav1.ObjectMeta{ 524 Name: fmt.Sprintf("hostpath-symlink-prep-%s", f.Namespace.Name), 525 }, 526 Spec: v1.PodSpec{ 527 Containers: []v1.Container{ 528 { 529 Name: fmt.Sprintf("init-volume-%s", f.Namespace.Name), 530 Image: imageutils.GetE2EImage(imageutils.BusyBox), 531 Command: []string{"/bin/sh", "-ec", cmd}, 532 VolumeMounts: []v1.VolumeMount{ 533 { 534 Name: volumeName, 535 MountPath: "/tmp", 536 }, 537 }, 538 SecurityContext: &v1.SecurityContext{ 539 Privileged: &privileged, 540 }, 541 }, 542 }, 543 RestartPolicy: v1.RestartPolicyNever, 544 Volumes: []v1.Volume{ 545 { 546 Name: volumeName, 547 VolumeSource: v1.VolumeSource{ 548 HostPath: &v1.HostPathVolumeSource{ 549 Path: "/tmp", 550 }, 551 }, 552 }, 553 }, 554 NodeName: node.Name, 555 }, 556 } 557 // h.prepPod will be reused in cleanupDriver. 558 pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, prepPod, metav1.CreateOptions{}) 559 framework.ExpectNoError(err, "while creating hostPath init pod") 560 561 err = e2epod.WaitForPodSuccessInNamespaceTimeout(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStart) 562 framework.ExpectNoError(err, "while waiting for hostPath init pod to succeed") 563 564 err = e2epod.DeletePodWithWait(ctx, f.ClientSet, pod) 565 framework.ExpectNoError(err, "while deleting hostPath init pod") 566 return &hostPathSymlinkVolume{ 567 sourcePath: sourcePath, 568 targetPath: targetPath, 569 prepPod: prepPod, 570 f: f, 571 } 572 } 573 574 func (v *hostPathSymlinkVolume) DeleteVolume(ctx context.Context) { 575 f := v.f 576 577 cmd := fmt.Sprintf("rm -rf %v&& rm -rf %v", v.targetPath, v.sourcePath) 578 v.prepPod.Spec.Containers[0].Command = []string{"/bin/sh", "-ec", cmd} 579 580 pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, v.prepPod, metav1.CreateOptions{}) 581 framework.ExpectNoError(err, "while creating hostPath teardown pod") 582 583 err = e2epod.WaitForPodSuccessInNamespaceTimeout(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStart) 584 framework.ExpectNoError(err, "while waiting for hostPath teardown pod to succeed") 585 586 err = e2epod.DeletePodWithWait(ctx, f.ClientSet, pod) 587 framework.ExpectNoError(err, "while deleting hostPath teardown pod") 588 } 589 590 // emptydir 591 type emptydirDriver struct { 592 driverInfo storageframework.DriverInfo 593 } 594 595 var _ storageframework.TestDriver = &emptydirDriver{} 596 var _ storageframework.PreprovisionedVolumeTestDriver = &emptydirDriver{} 597 var _ storageframework.InlineVolumeTestDriver = &emptydirDriver{} 598 599 // InitEmptydirDriver returns emptydirDriver that implements TestDriver interface 600 func InitEmptydirDriver() storageframework.TestDriver { 601 return &emptydirDriver{ 602 driverInfo: storageframework.DriverInfo{ 603 Name: "emptydir", 604 InTreePluginName: "kubernetes.io/empty-dir", 605 MaxFileSize: storageframework.FileSizeMedium, 606 SupportedFsType: sets.NewString( 607 "", // Default fsType 608 ), 609 Capabilities: map[storageframework.Capability]bool{ 610 storageframework.CapExec: true, 611 storageframework.CapSingleNodeVolume: true, 612 }, 613 }, 614 } 615 } 616 617 func (e *emptydirDriver) GetDriverInfo() *storageframework.DriverInfo { 618 return &e.driverInfo 619 } 620 621 func (e *emptydirDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 622 } 623 624 func (e *emptydirDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource { 625 // emptydir doesn't support readOnly volume 626 if readOnly { 627 return nil 628 } 629 return &v1.VolumeSource{ 630 EmptyDir: &v1.EmptyDirVolumeSource{}, 631 } 632 } 633 634 func (e *emptydirDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { 635 return nil 636 } 637 638 func (e *emptydirDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 639 return &storageframework.PerTestConfig{ 640 Driver: e, 641 Prefix: "emptydir", 642 Framework: f, 643 } 644 } 645 646 // Cinder 647 // This tests only CSI migration with dynamically provisioned volumes. 648 type cinderDriver struct { 649 driverInfo storageframework.DriverInfo 650 } 651 652 var _ storageframework.TestDriver = &cinderDriver{} 653 var _ storageframework.DynamicPVTestDriver = &cinderDriver{} 654 655 // InitCinderDriver returns cinderDriver that implements TestDriver interface 656 func InitCinderDriver() storageframework.TestDriver { 657 return &cinderDriver{ 658 driverInfo: storageframework.DriverInfo{ 659 Name: "cinder", 660 InTreePluginName: "kubernetes.io/cinder", 661 MaxFileSize: storageframework.FileSizeMedium, 662 SupportedSizeRange: e2evolume.SizeRange{ 663 Min: "1Gi", 664 }, 665 SupportedFsType: sets.NewString( 666 "", // Default fsType 667 ), 668 TopologyKeys: []string{v1.LabelFailureDomainBetaZone}, 669 Capabilities: map[storageframework.Capability]bool{ 670 storageframework.CapPersistence: true, 671 storageframework.CapFsGroup: true, 672 storageframework.CapExec: true, 673 storageframework.CapBlock: true, 674 // Cinder supports volume limits, but the test creates large 675 // number of volumes and times out test suites. 676 storageframework.CapVolumeLimits: false, 677 storageframework.CapTopology: true, 678 }, 679 }, 680 } 681 } 682 683 func (c *cinderDriver) GetDriverInfo() *storageframework.DriverInfo { 684 return &c.driverInfo 685 } 686 687 func (c *cinderDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 688 e2eskipper.SkipUnlessProviderIs("openstack") 689 } 690 691 func (c *cinderDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { 692 provisioner := "kubernetes.io/cinder" 693 parameters := map[string]string{} 694 if fsType != "" { 695 parameters["fsType"] = fsType 696 } 697 ns := config.Framework.Namespace.Name 698 699 return storageframework.GetStorageClass(provisioner, parameters, nil, ns) 700 } 701 702 func (c *cinderDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 703 return &storageframework.PerTestConfig{ 704 Driver: c, 705 Prefix: "cinder", 706 Framework: f, 707 } 708 } 709 710 // GCE 711 type gcePdDriver struct { 712 driverInfo storageframework.DriverInfo 713 } 714 715 type gcePdVolume struct { 716 volumeName string 717 } 718 719 var _ storageframework.TestDriver = &gcePdDriver{} 720 var _ storageframework.PreprovisionedVolumeTestDriver = &gcePdDriver{} 721 var _ storageframework.InlineVolumeTestDriver = &gcePdDriver{} 722 var _ storageframework.PreprovisionedPVTestDriver = &gcePdDriver{} 723 var _ storageframework.DynamicPVTestDriver = &gcePdDriver{} 724 725 // InitGcePdDriver returns gcePdDriver that implements TestDriver interface 726 func InitGcePdDriver() storageframework.TestDriver { 727 supportedTypes := sets.NewString( 728 "", // Default fsType 729 "ext2", 730 "ext3", 731 "ext4", 732 "xfs", 733 ) 734 return &gcePdDriver{ 735 driverInfo: storageframework.DriverInfo{ 736 Name: "gcepd", 737 InTreePluginName: "kubernetes.io/gce-pd", 738 MaxFileSize: storageframework.FileSizeMedium, 739 SupportedSizeRange: e2evolume.SizeRange{ 740 Min: "1Gi", 741 }, 742 SupportedFsType: supportedTypes, 743 SupportedMountOption: sets.NewString("debug", "nouid32"), 744 TopologyKeys: []string{v1.LabelTopologyZone}, 745 Capabilities: map[storageframework.Capability]bool{ 746 storageframework.CapPersistence: true, 747 storageframework.CapFsGroup: true, 748 storageframework.CapBlock: true, 749 storageframework.CapExec: true, 750 storageframework.CapMultiPODs: true, 751 storageframework.CapControllerExpansion: true, 752 storageframework.CapOfflineExpansion: true, 753 storageframework.CapOnlineExpansion: true, 754 storageframework.CapNodeExpansion: true, 755 // GCE supports volume limits, but the test creates large 756 // number of volumes and times out test suites. 757 storageframework.CapVolumeLimits: false, 758 storageframework.CapTopology: true, 759 storageframework.CapMultiplePVsSameID: true, 760 }, 761 }, 762 } 763 } 764 765 // InitWindowsGcePdDriver returns gcePdDriver running on Windows cluster that implements TestDriver interface 766 // In current test structure, it first initialize the driver and then set up 767 // the new framework, so we cannot get the correct OS here and select which file system is supported. 768 // So here uses a separate Windows in-tree gce pd driver 769 func InitWindowsGcePdDriver() storageframework.TestDriver { 770 supportedTypes := sets.NewString( 771 "ntfs", 772 ) 773 return &gcePdDriver{ 774 driverInfo: storageframework.DriverInfo{ 775 Name: "windows-gcepd", 776 InTreePluginName: "kubernetes.io/gce-pd", 777 MaxFileSize: storageframework.FileSizeMedium, 778 SupportedSizeRange: e2evolume.SizeRange{ 779 Min: "1Gi", 780 }, 781 SupportedFsType: supportedTypes, 782 TopologyKeys: []string{v1.LabelZoneFailureDomain}, 783 Capabilities: map[storageframework.Capability]bool{ 784 storageframework.CapControllerExpansion: false, 785 storageframework.CapPersistence: true, 786 storageframework.CapExec: true, 787 storageframework.CapMultiPODs: true, 788 // GCE supports volume limits, but the test creates large 789 // number of volumes and times out test suites. 790 storageframework.CapVolumeLimits: false, 791 storageframework.CapTopology: true, 792 storageframework.CapMultiplePVsSameID: true, 793 }, 794 }, 795 } 796 } 797 798 func (g *gcePdDriver) GetDriverInfo() *storageframework.DriverInfo { 799 return &g.driverInfo 800 } 801 802 func (g *gcePdDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 803 e2eskipper.SkipUnlessProviderIs("gce", "gke") 804 for _, tag := range pattern.TestTags { 805 if tag == feature.Windows { 806 e2eskipper.SkipUnlessNodeOSDistroIs("windows") 807 } 808 } 809 } 810 811 func (g *gcePdDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource { 812 gv, ok := e2evolume.(*gcePdVolume) 813 if !ok { 814 framework.Failf("Failed to cast test volume of type %T to the GCE PD test volume", e2evolume) 815 } 816 volSource := v1.VolumeSource{ 817 GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ 818 PDName: gv.volumeName, 819 ReadOnly: readOnly, 820 }, 821 } 822 if fsType != "" { 823 volSource.GCEPersistentDisk.FSType = fsType 824 } 825 return &volSource 826 } 827 828 func (g *gcePdDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) { 829 gv, ok := e2evolume.(*gcePdVolume) 830 if !ok { 831 framework.Failf("Failed to cast test volume of type %T to the GCE PD test volume", e2evolume) 832 } 833 pvSource := v1.PersistentVolumeSource{ 834 GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ 835 PDName: gv.volumeName, 836 ReadOnly: readOnly, 837 }, 838 } 839 if fsType != "" { 840 pvSource.GCEPersistentDisk.FSType = fsType 841 } 842 return &pvSource, nil 843 } 844 845 func (g *gcePdDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { 846 provisioner := "kubernetes.io/gce-pd" 847 parameters := map[string]string{} 848 if fsType != "" { 849 parameters["fsType"] = fsType 850 } 851 ns := config.Framework.Namespace.Name 852 delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer 853 854 return storageframework.GetStorageClass(provisioner, parameters, &delayedBinding, ns) 855 } 856 857 func (g *gcePdDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 858 config := &storageframework.PerTestConfig{ 859 Driver: g, 860 Prefix: "gcepd", 861 Framework: f, 862 } 863 864 if framework.NodeOSDistroIs("windows") { 865 config.ClientNodeSelection = e2epod.NodeSelection{ 866 Selector: map[string]string{ 867 "kubernetes.io/os": "windows", 868 }, 869 } 870 } 871 return config 872 873 } 874 875 func (g *gcePdDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { 876 zone := getInlineVolumeZone(ctx, config.Framework) 877 if volType == storageframework.InlineVolume { 878 // PD will be created in framework.TestContext.CloudConfig.Zone zone, 879 // so pods should be also scheduled there. 880 config.ClientNodeSelection = e2epod.NodeSelection{ 881 Selector: map[string]string{ 882 v1.LabelTopologyZone: zone, 883 }, 884 } 885 } 886 ginkgo.By("creating a test gce pd volume") 887 vname, err := e2epv.CreatePDWithRetryAndZone(ctx, zone) 888 framework.ExpectNoError(err) 889 return &gcePdVolume{ 890 volumeName: vname, 891 } 892 } 893 894 func (v *gcePdVolume) DeleteVolume(ctx context.Context) { 895 _ = e2epv.DeletePDWithRetry(ctx, v.volumeName) 896 } 897 898 // vSphere 899 type vSphereDriver struct { 900 driverInfo storageframework.DriverInfo 901 } 902 903 var _ storageframework.TestDriver = &vSphereDriver{} 904 var _ storageframework.DynamicPVTestDriver = &vSphereDriver{} 905 906 // InitVSphereDriver returns vSphereDriver that implements TestDriver interface 907 func InitVSphereDriver() storageframework.TestDriver { 908 return &vSphereDriver{ 909 driverInfo: storageframework.DriverInfo{ 910 Name: "vsphere", 911 InTreePluginName: "kubernetes.io/vsphere-volume", 912 MaxFileSize: storageframework.FileSizeMedium, 913 SupportedSizeRange: e2evolume.SizeRange{ 914 Min: "1Gi", 915 }, 916 SupportedFsType: sets.NewString( 917 "", // Default fsType 918 "ext4", 919 "ntfs", 920 ), 921 TopologyKeys: []string{v1.LabelFailureDomainBetaZone}, 922 Capabilities: map[storageframework.Capability]bool{ 923 storageframework.CapPersistence: true, 924 storageframework.CapFsGroup: true, 925 storageframework.CapExec: true, 926 storageframework.CapMultiPODs: true, 927 storageframework.CapTopology: true, 928 storageframework.CapBlock: true, 929 storageframework.CapMultiplePVsSameID: false, 930 }, 931 }, 932 } 933 } 934 935 func (v *vSphereDriver) GetDriverInfo() *storageframework.DriverInfo { 936 return &v.driverInfo 937 } 938 939 func (v *vSphereDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 940 e2eskipper.SkipUnlessProviderIs("vsphere") 941 } 942 943 func (v *vSphereDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { 944 provisioner := "kubernetes.io/vsphere-volume" 945 parameters := map[string]string{} 946 if fsType != "" { 947 parameters["fsType"] = fsType 948 } 949 ns := config.Framework.Namespace.Name 950 951 return storageframework.GetStorageClass(provisioner, parameters, nil, ns) 952 } 953 954 func (v *vSphereDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 955 return &storageframework.PerTestConfig{ 956 Driver: v, 957 Prefix: "vsphere", 958 Framework: f, 959 } 960 } 961 962 // Azure Disk 963 type azureDiskDriver struct { 964 driverInfo storageframework.DriverInfo 965 } 966 967 var _ storageframework.TestDriver = &azureDiskDriver{} 968 var _ storageframework.DynamicPVTestDriver = &azureDiskDriver{} 969 var _ storageframework.CustomTimeoutsTestDriver = &azureDiskDriver{} 970 971 // InitAzureDiskDriver returns azureDiskDriver that implements TestDriver interface 972 func InitAzureDiskDriver() storageframework.TestDriver { 973 return &azureDiskDriver{ 974 driverInfo: storageframework.DriverInfo{ 975 Name: "azure-disk", 976 InTreePluginName: "kubernetes.io/azure-disk", 977 MaxFileSize: storageframework.FileSizeMedium, 978 SupportedSizeRange: e2evolume.SizeRange{ 979 Min: "1Gi", 980 }, 981 SupportedFsType: sets.NewString( 982 "", // Default fsType 983 "ext4", 984 "xfs", 985 ), 986 TopologyKeys: []string{v1.LabelFailureDomainBetaZone}, 987 Capabilities: map[storageframework.Capability]bool{ 988 storageframework.CapPersistence: true, 989 storageframework.CapFsGroup: true, 990 storageframework.CapBlock: true, 991 storageframework.CapExec: true, 992 storageframework.CapMultiPODs: true, 993 // Azure supports volume limits, but the test creates large 994 // number of volumes and times out test suites. 995 storageframework.CapVolumeLimits: false, 996 storageframework.CapTopology: true, 997 storageframework.CapMultiplePVsSameID: true, 998 }, 999 }, 1000 } 1001 } 1002 1003 func (a *azureDiskDriver) GetDriverInfo() *storageframework.DriverInfo { 1004 return &a.driverInfo 1005 } 1006 1007 func (a *azureDiskDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 1008 e2eskipper.SkipUnlessProviderIs("azure") 1009 } 1010 1011 func (a *azureDiskDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { 1012 provisioner := "kubernetes.io/azure-disk" 1013 parameters := map[string]string{} 1014 if fsType != "" { 1015 parameters["fsType"] = fsType 1016 } 1017 ns := config.Framework.Namespace.Name 1018 delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer 1019 1020 return storageframework.GetStorageClass(provisioner, parameters, &delayedBinding, ns) 1021 } 1022 1023 func (a *azureDiskDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 1024 return &storageframework.PerTestConfig{ 1025 Driver: a, 1026 Prefix: "azure", 1027 Framework: f, 1028 } 1029 } 1030 1031 func (a *azureDiskDriver) GetTimeouts() *framework.TimeoutContext { 1032 timeouts := framework.NewTimeoutContext() 1033 timeouts.PodStart = time.Minute * 15 1034 timeouts.PodDelete = time.Minute * 15 1035 timeouts.PVDelete = time.Minute * 20 1036 return timeouts 1037 } 1038 1039 // AWS 1040 type awsDriver struct { 1041 driverInfo storageframework.DriverInfo 1042 } 1043 1044 var _ storageframework.TestDriver = &awsDriver{} 1045 var _ storageframework.DynamicPVTestDriver = &awsDriver{} 1046 1047 // InitAwsDriver returns awsDriver that implements TestDriver interface 1048 func InitAwsDriver() storageframework.TestDriver { 1049 return &awsDriver{ 1050 driverInfo: storageframework.DriverInfo{ 1051 Name: "aws", 1052 InTreePluginName: "kubernetes.io/aws-ebs", 1053 MaxFileSize: storageframework.FileSizeMedium, 1054 SupportedSizeRange: e2evolume.SizeRange{ 1055 Min: "1Gi", 1056 }, 1057 SupportedFsType: sets.NewString( 1058 "", // Default fsType 1059 "ext4", 1060 "xfs", 1061 "ntfs", 1062 ), 1063 SupportedMountOption: sets.NewString("debug", "nouid32"), 1064 TopologyKeys: []string{v1.LabelTopologyZone}, 1065 Capabilities: map[storageframework.Capability]bool{ 1066 storageframework.CapPersistence: true, 1067 storageframework.CapFsGroup: true, 1068 storageframework.CapBlock: true, 1069 storageframework.CapExec: true, 1070 storageframework.CapMultiPODs: true, 1071 storageframework.CapControllerExpansion: true, 1072 storageframework.CapNodeExpansion: true, 1073 storageframework.CapOfflineExpansion: true, 1074 storageframework.CapOnlineExpansion: true, 1075 // AWS supports volume limits, but the test creates large 1076 // number of volumes and times out test suites. 1077 storageframework.CapVolumeLimits: false, 1078 storageframework.CapTopology: true, 1079 storageframework.CapMultiplePVsSameID: true, 1080 }, 1081 }, 1082 } 1083 } 1084 1085 func (a *awsDriver) GetDriverInfo() *storageframework.DriverInfo { 1086 return &a.driverInfo 1087 } 1088 1089 func (a *awsDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 1090 e2eskipper.SkipUnlessProviderIs("aws") 1091 } 1092 1093 func (a *awsDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { 1094 provisioner := "kubernetes.io/aws-ebs" 1095 parameters := map[string]string{} 1096 if fsType != "" { 1097 parameters["fsType"] = fsType 1098 } 1099 ns := config.Framework.Namespace.Name 1100 delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer 1101 1102 return storageframework.GetStorageClass(provisioner, parameters, &delayedBinding, ns) 1103 } 1104 1105 func (a *awsDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 1106 config := &storageframework.PerTestConfig{ 1107 Driver: a, 1108 Prefix: "aws", 1109 Framework: f, 1110 } 1111 1112 if framework.NodeOSDistroIs("windows") { 1113 config.ClientNodeSelection = e2epod.NodeSelection{ 1114 Selector: map[string]string{ 1115 "kubernetes.io/os": "windows", 1116 }, 1117 } 1118 } 1119 return config 1120 } 1121 1122 // local 1123 type localDriver struct { 1124 driverInfo storageframework.DriverInfo 1125 node *v1.Node 1126 hostExec utils.HostExec 1127 // volumeType represents local volume type we are testing, e.g. tmpfs, 1128 // directory, block device. 1129 volumeType utils.LocalVolumeType 1130 ltrMgr utils.LocalTestResourceManager 1131 } 1132 1133 type localVolume struct { 1134 ltrMgr utils.LocalTestResourceManager 1135 ltr *utils.LocalTestResource 1136 } 1137 1138 var ( 1139 // capabilities 1140 defaultLocalVolumeCapabilities = map[storageframework.Capability]bool{ 1141 storageframework.CapPersistence: true, 1142 storageframework.CapFsGroup: true, 1143 storageframework.CapBlock: false, 1144 storageframework.CapExec: true, 1145 storageframework.CapMultiPODs: true, 1146 storageframework.CapSingleNodeVolume: true, 1147 storageframework.CapMultiplePVsSameID: true, 1148 } 1149 localVolumeCapabitilies = map[utils.LocalVolumeType]map[storageframework.Capability]bool{ 1150 utils.LocalVolumeBlock: { 1151 storageframework.CapPersistence: true, 1152 storageframework.CapFsGroup: true, 1153 storageframework.CapBlock: true, 1154 storageframework.CapExec: true, 1155 storageframework.CapMultiPODs: true, 1156 storageframework.CapSingleNodeVolume: true, 1157 storageframework.CapMultiplePVsSameID: true, 1158 }, 1159 } 1160 // fstype 1161 defaultLocalVolumeSupportedFsTypes = sets.NewString("") 1162 localVolumeSupportedFsTypes = map[utils.LocalVolumeType]sets.String{ 1163 utils.LocalVolumeBlock: sets.NewString( 1164 "", // Default fsType 1165 "ext4", 1166 //"xfs", disabled see issue https://github.com/kubernetes/kubernetes/issues/74095 1167 ), 1168 } 1169 // max file size 1170 defaultLocalVolumeMaxFileSize = storageframework.FileSizeSmall 1171 localVolumeMaxFileSizes = map[utils.LocalVolumeType]int64{} 1172 ) 1173 1174 var _ storageframework.TestDriver = &localDriver{} 1175 var _ storageframework.PreprovisionedVolumeTestDriver = &localDriver{} 1176 var _ storageframework.PreprovisionedPVTestDriver = &localDriver{} 1177 1178 // InitLocalDriverWithVolumeType initializes the local driver based on the volume type. 1179 func InitLocalDriverWithVolumeType(volumeType utils.LocalVolumeType) func() storageframework.TestDriver { 1180 maxFileSize := defaultLocalVolumeMaxFileSize 1181 if maxFileSizeByVolType, ok := localVolumeMaxFileSizes[volumeType]; ok { 1182 maxFileSize = maxFileSizeByVolType 1183 } 1184 supportedFsTypes := defaultLocalVolumeSupportedFsTypes 1185 if supportedFsTypesByType, ok := localVolumeSupportedFsTypes[volumeType]; ok { 1186 supportedFsTypes = supportedFsTypesByType 1187 } 1188 capabilities := defaultLocalVolumeCapabilities 1189 if capabilitiesByType, ok := localVolumeCapabitilies[volumeType]; ok { 1190 capabilities = capabilitiesByType 1191 } 1192 return func() storageframework.TestDriver { 1193 // custom tag to distinguish from tests of other volume types 1194 testTags := []interface{}{fmt.Sprintf("[LocalVolumeType: %s]", volumeType)} 1195 // For GCE Local SSD volumes, we must run serially 1196 if volumeType == utils.LocalVolumeGCELocalSSD { 1197 testTags = append(testTags, framework.WithSerial()) 1198 } 1199 return &localDriver{ 1200 driverInfo: storageframework.DriverInfo{ 1201 Name: "local", 1202 InTreePluginName: "kubernetes.io/local-volume", 1203 TestTags: testTags, 1204 MaxFileSize: maxFileSize, 1205 SupportedFsType: supportedFsTypes, 1206 Capabilities: capabilities, 1207 }, 1208 volumeType: volumeType, 1209 } 1210 } 1211 } 1212 1213 func (l *localDriver) GetDriverInfo() *storageframework.DriverInfo { 1214 return &l.driverInfo 1215 } 1216 1217 func (l *localDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 1218 } 1219 1220 func (l *localDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 1221 var err error 1222 l.node, err = e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet) 1223 framework.ExpectNoError(err) 1224 1225 l.hostExec = utils.NewHostExec(f) 1226 l.ltrMgr = utils.NewLocalResourceManager("local-driver", l.hostExec, "/tmp") 1227 1228 // This can't be done in SkipUnsupportedTest because the test framework is not initialized yet 1229 if l.volumeType == utils.LocalVolumeGCELocalSSD { 1230 ssdInterface := "scsi" 1231 filesystemType := "fs" 1232 ssdCmd := fmt.Sprintf("ls -1 /mnt/disks/by-uuid/google-local-ssds-%s-%s/ | wc -l", ssdInterface, filesystemType) 1233 res, err := l.hostExec.IssueCommandWithResult(ctx, ssdCmd, l.node) 1234 framework.ExpectNoError(err) 1235 num, err := strconv.Atoi(strings.TrimSpace(res)) 1236 framework.ExpectNoError(err) 1237 if num < 1 { 1238 e2eskipper.Skipf("Requires at least 1 %s %s localSSD ", ssdInterface, filesystemType) 1239 } 1240 } 1241 1242 ginkgo.DeferCleanup(l.hostExec.Cleanup) 1243 return &storageframework.PerTestConfig{ 1244 Driver: l, 1245 Prefix: "local", 1246 Framework: f, 1247 ClientNodeSelection: e2epod.NodeSelection{Name: l.node.Name}, 1248 } 1249 } 1250 1251 func (l *localDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { 1252 switch volType { 1253 case storageframework.PreprovisionedPV: 1254 node := l.node 1255 // assign this to schedule pod on this node 1256 config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name} 1257 return &localVolume{ 1258 ltrMgr: l.ltrMgr, 1259 ltr: l.ltrMgr.Create(ctx, node, l.volumeType, nil), 1260 } 1261 default: 1262 framework.Failf("Unsupported volType: %v is specified", volType) 1263 } 1264 return nil 1265 } 1266 1267 func (v *localVolume) DeleteVolume(ctx context.Context) { 1268 v.ltrMgr.Remove(ctx, v.ltr) 1269 } 1270 1271 func (l *localDriver) nodeAffinityForNode(node *v1.Node) *v1.VolumeNodeAffinity { 1272 nodeKey := "kubernetes.io/hostname" 1273 if node.Labels == nil { 1274 framework.Failf("Node does not have labels") 1275 } 1276 nodeValue, found := node.Labels[nodeKey] 1277 if !found { 1278 framework.Failf("Node does not have required label %q", nodeKey) 1279 } 1280 return &v1.VolumeNodeAffinity{ 1281 Required: &v1.NodeSelector{ 1282 NodeSelectorTerms: []v1.NodeSelectorTerm{ 1283 { 1284 MatchExpressions: []v1.NodeSelectorRequirement{ 1285 { 1286 Key: nodeKey, 1287 Operator: v1.NodeSelectorOpIn, 1288 Values: []string{nodeValue}, 1289 }, 1290 }, 1291 }, 1292 }, 1293 }, 1294 } 1295 } 1296 1297 func (l *localDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) { 1298 lv, ok := e2evolume.(*localVolume) 1299 if !ok { 1300 framework.Failf("Failed to cast test volume of type %T to the local test volume", e2evolume) 1301 } 1302 return &v1.PersistentVolumeSource{ 1303 Local: &v1.LocalVolumeSource{ 1304 Path: lv.ltr.Path, 1305 FSType: &fsType, 1306 }, 1307 }, l.nodeAffinityForNode(lv.ltr.Node) 1308 } 1309 1310 // cleanUpVolumeServer is a wrapper of cleanup function for volume server without secret created by specific CreateStorageServer function. 1311 func cleanUpVolumeServer(ctx context.Context, f *framework.Framework, serverPod *v1.Pod) { 1312 cleanUpVolumeServerWithSecret(ctx, f, serverPod, nil) 1313 } 1314 1315 func getInlineVolumeZone(ctx context.Context, f *framework.Framework) string { 1316 if framework.TestContext.CloudConfig.Zone != "" { 1317 return framework.TestContext.CloudConfig.Zone 1318 } 1319 // if zone is not specified we will randomly pick a zone from schedulable nodes for inline tests 1320 node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet) 1321 framework.ExpectNoError(err) 1322 zone, ok := node.Labels[v1.LabelFailureDomainBetaZone] 1323 if ok { 1324 return zone 1325 } 1326 topologyZone, ok := node.Labels[v1.LabelTopologyZone] 1327 if ok { 1328 return topologyZone 1329 } 1330 return "" 1331 } 1332 1333 // cleanUpVolumeServerWithSecret is a wrapper of cleanup function for volume server with secret created by specific CreateStorageServer function. 1334 func cleanUpVolumeServerWithSecret(ctx context.Context, f *framework.Framework, serverPod *v1.Pod, secret *v1.Secret) { 1335 cs := f.ClientSet 1336 ns := f.Namespace 1337 1338 if secret != nil { 1339 framework.Logf("Deleting server secret %q...", secret.Name) 1340 err := cs.CoreV1().Secrets(ns.Name).Delete(ctx, secret.Name, metav1.DeleteOptions{}) 1341 if err != nil { 1342 framework.Logf("Delete secret failed: %v", err) 1343 } 1344 } 1345 1346 framework.Logf("Deleting server pod %q...", serverPod.Name) 1347 err := e2epod.DeletePodWithWait(ctx, cs, serverPod) 1348 if err != nil { 1349 framework.Logf("Server pod delete failed: %v", err) 1350 } 1351 } 1352 1353 // Azure File 1354 type azureFileDriver struct { 1355 driverInfo storageframework.DriverInfo 1356 } 1357 1358 var _ storageframework.TestDriver = &azureFileDriver{} 1359 var _ storageframework.DynamicPVTestDriver = &azureFileDriver{} 1360 1361 // InitAzureFileDriver returns azureFileDriver that implements TestDriver interface 1362 func InitAzureFileDriver() storageframework.TestDriver { 1363 return &azureFileDriver{ 1364 driverInfo: storageframework.DriverInfo{ 1365 Name: "azure-file", 1366 InTreePluginName: "kubernetes.io/azure-file", 1367 MaxFileSize: storageframework.FileSizeMedium, 1368 SupportedSizeRange: e2evolume.SizeRange{ 1369 Min: "1Gi", 1370 }, 1371 SupportedFsType: sets.NewString( 1372 "", // Default fsType 1373 ), 1374 Capabilities: map[storageframework.Capability]bool{ 1375 storageframework.CapPersistence: true, 1376 storageframework.CapExec: true, 1377 storageframework.CapRWX: true, 1378 storageframework.CapMultiPODs: true, 1379 storageframework.CapControllerExpansion: true, 1380 storageframework.CapNodeExpansion: true, 1381 storageframework.CapMultiplePVsSameID: true, 1382 }, 1383 }, 1384 } 1385 } 1386 1387 func (a *azureFileDriver) GetDriverInfo() *storageframework.DriverInfo { 1388 return &a.driverInfo 1389 } 1390 1391 func (a *azureFileDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { 1392 e2eskipper.SkipUnlessProviderIs("azure") 1393 } 1394 1395 func (a *azureFileDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { 1396 provisioner := "kubernetes.io/azure-file" 1397 parameters := map[string]string{} 1398 ns := config.Framework.Namespace.Name 1399 immediateBinding := storagev1.VolumeBindingImmediate 1400 return storageframework.GetStorageClass(provisioner, parameters, &immediateBinding, ns) 1401 } 1402 1403 func (a *azureFileDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig { 1404 return &storageframework.PerTestConfig{ 1405 Driver: a, 1406 Prefix: "azure-file", 1407 Framework: f, 1408 } 1409 }