csi.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. /*
  14. * This file defines various csi volume test drivers for TestSuites.
  15. *
  16. * There are two ways, how to prepare test drivers:
  17. * 1) With containerized server (NFS, Ceph, Gluster, iSCSI, ...)
  18. * It creates a server pod which defines one volume for the tests.
  19. * These tests work only when privileged containers are allowed, exporting
  20. * various filesystems (NFS, GlusterFS, ...) usually needs some mounting or
  21. * other privileged magic in the server pod.
  22. *
  23. * Note that the server containers are for testing purposes only and should not
  24. * be used in production.
  25. *
  26. * 2) With server or cloud provider outside of Kubernetes (Cinder, GCE, AWS, Azure, ...)
  27. * Appropriate server or cloud provider must exist somewhere outside
  28. * the tested Kubernetes cluster. CreateVolume will create a new volume to be
  29. * used in the TestSuites for inlineVolume or DynamicPV tests.
  30. */
  31. package drivers
  32. import (
  33. "context"
  34. "fmt"
  35. "strconv"
  36. "time"
  37. "github.com/onsi/ginkgo"
  38. v1 "k8s.io/api/core/v1"
  39. storagev1 "k8s.io/api/storage/v1"
  40. storagev1beta1 "k8s.io/api/storage/v1beta1"
  41. apierrors "k8s.io/apimachinery/pkg/api/errors"
  42. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  43. "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  44. "k8s.io/apimachinery/pkg/util/sets"
  45. "k8s.io/apimachinery/pkg/util/wait"
  46. clientset "k8s.io/client-go/kubernetes"
  47. "k8s.io/kubernetes/test/e2e/framework"
  48. e2enode "k8s.io/kubernetes/test/e2e/framework/node"
  49. e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
  50. e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
  51. "k8s.io/kubernetes/test/e2e/framework/volume"
  52. "k8s.io/kubernetes/test/e2e/storage/testpatterns"
  53. "k8s.io/kubernetes/test/e2e/storage/testsuites"
  54. "k8s.io/kubernetes/test/e2e/storage/utils"
  55. )
  56. const (
  57. // GCEPDCSIDriverName is the name of GCE Persistent Disk CSI driver
  58. GCEPDCSIDriverName = "pd.csi.storage.gke.io"
  59. // GCEPDCSIZoneTopologyKey is the key of GCE Persistent Disk CSI zone topology
  60. GCEPDCSIZoneTopologyKey = "topology.gke.io/zone"
  61. )
  62. // hostpathCSI
  63. type hostpathCSIDriver struct {
  64. driverInfo testsuites.DriverInfo
  65. manifests []string
  66. volumeAttributes []map[string]string
  67. }
  68. func initHostPathCSIDriver(name string, capabilities map[testsuites.Capability]bool, volumeAttributes []map[string]string, manifests ...string) testsuites.TestDriver {
  69. return &hostpathCSIDriver{
  70. driverInfo: testsuites.DriverInfo{
  71. Name: name,
  72. FeatureTag: "",
  73. MaxFileSize: testpatterns.FileSizeMedium,
  74. SupportedFsType: sets.NewString(
  75. "", // Default fsType
  76. ),
  77. SupportedSizeRange: volume.SizeRange{
  78. Min: "1Mi",
  79. },
  80. Capabilities: capabilities,
  81. },
  82. manifests: manifests,
  83. volumeAttributes: volumeAttributes,
  84. }
  85. }
  86. var _ testsuites.TestDriver = &hostpathCSIDriver{}
  87. var _ testsuites.DynamicPVTestDriver = &hostpathCSIDriver{}
  88. var _ testsuites.SnapshottableTestDriver = &hostpathCSIDriver{}
  89. var _ testsuites.EphemeralTestDriver = &hostpathCSIDriver{}
  90. // InitHostPathCSIDriver returns hostpathCSIDriver that implements TestDriver interface
  91. func InitHostPathCSIDriver() testsuites.TestDriver {
  92. capabilities := map[testsuites.Capability]bool{
  93. testsuites.CapPersistence: true,
  94. testsuites.CapSnapshotDataSource: true,
  95. testsuites.CapMultiPODs: true,
  96. testsuites.CapBlock: true,
  97. testsuites.CapPVCDataSource: true,
  98. testsuites.CapControllerExpansion: true,
  99. testsuites.CapSingleNodeVolume: true,
  100. testsuites.CapVolumeLimits: true,
  101. }
  102. return initHostPathCSIDriver("csi-hostpath",
  103. capabilities,
  104. // Volume attributes don't matter, but we have to provide at least one map.
  105. []map[string]string{
  106. {"foo": "bar"},
  107. },
  108. "test/e2e/testing-manifests/storage-csi/external-attacher/rbac.yaml",
  109. "test/e2e/testing-manifests/storage-csi/external-provisioner/rbac.yaml",
  110. "test/e2e/testing-manifests/storage-csi/external-snapshotter/rbac.yaml",
  111. "test/e2e/testing-manifests/storage-csi/external-resizer/rbac.yaml",
  112. "test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-attacher.yaml",
  113. "test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-driverinfo.yaml",
  114. "test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-plugin.yaml",
  115. "test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-provisioner.yaml",
  116. "test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-resizer.yaml",
  117. "test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-snapshotter.yaml",
  118. "test/e2e/testing-manifests/storage-csi/hostpath/hostpath/e2e-test-rbac.yaml",
  119. )
  120. }
  121. func (h *hostpathCSIDriver) GetDriverInfo() *testsuites.DriverInfo {
  122. return &h.driverInfo
  123. }
  124. func (h *hostpathCSIDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  125. if pattern.VolType == testpatterns.CSIInlineVolume && len(h.volumeAttributes) == 0 {
  126. e2eskipper.Skipf("%s has no volume attributes defined, doesn't support ephemeral inline volumes", h.driverInfo.Name)
  127. }
  128. }
  129. func (h *hostpathCSIDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  130. provisioner := config.GetUniqueDriverName()
  131. parameters := map[string]string{}
  132. ns := config.Framework.Namespace.Name
  133. suffix := fmt.Sprintf("%s-sc", provisioner)
  134. return testsuites.GetStorageClass(provisioner, parameters, nil, ns, suffix)
  135. }
  136. func (h *hostpathCSIDriver) GetVolume(config *testsuites.PerTestConfig, volumeNumber int) (map[string]string, bool, bool) {
  137. return h.volumeAttributes[volumeNumber%len(h.volumeAttributes)], false /* not shared */, false /* read-write */
  138. }
  139. func (h *hostpathCSIDriver) GetCSIDriverName(config *testsuites.PerTestConfig) string {
  140. return config.GetUniqueDriverName()
  141. }
  142. func (h *hostpathCSIDriver) GetSnapshotClass(config *testsuites.PerTestConfig) *unstructured.Unstructured {
  143. snapshotter := config.GetUniqueDriverName()
  144. parameters := map[string]string{}
  145. ns := config.Framework.Namespace.Name
  146. suffix := fmt.Sprintf("%s-vsc", snapshotter)
  147. return testsuites.GetSnapshotClass(snapshotter, parameters, ns, suffix)
  148. }
  149. func (h *hostpathCSIDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  150. ginkgo.By(fmt.Sprintf("deploying %s driver", h.driverInfo.Name))
  151. cancelLogging := testsuites.StartPodLogs(f)
  152. cs := f.ClientSet
  153. // The hostpath CSI driver only works when everything runs on the same node.
  154. node, err := e2enode.GetRandomReadySchedulableNode(cs)
  155. framework.ExpectNoError(err)
  156. config := &testsuites.PerTestConfig{
  157. Driver: h,
  158. Prefix: "hostpath",
  159. Framework: f,
  160. ClientNodeSelection: e2epod.NodeSelection{Name: node.Name},
  161. }
  162. o := utils.PatchCSIOptions{
  163. OldDriverName: h.driverInfo.Name,
  164. NewDriverName: config.GetUniqueDriverName(),
  165. DriverContainerName: "hostpath",
  166. DriverContainerArguments: []string{"--drivername=" + config.GetUniqueDriverName()},
  167. ProvisionerContainerName: "csi-provisioner",
  168. SnapshotterContainerName: "csi-snapshotter",
  169. NodeName: node.Name,
  170. }
  171. cleanup, err := utils.CreateFromManifests(config.Framework, func(item interface{}) error {
  172. return utils.PatchCSIDeployment(config.Framework, o, item)
  173. },
  174. h.manifests...)
  175. if err != nil {
  176. framework.Failf("deploying %s driver: %v", h.driverInfo.Name, err)
  177. }
  178. return config, func() {
  179. ginkgo.By(fmt.Sprintf("uninstalling %s driver", h.driverInfo.Name))
  180. cleanup()
  181. cancelLogging()
  182. }
  183. }
  184. // mockCSI
  185. type mockCSIDriver struct {
  186. driverInfo testsuites.DriverInfo
  187. manifests []string
  188. podInfo *bool
  189. attachable bool
  190. attachLimit int
  191. enableNodeExpansion bool
  192. }
  193. // CSIMockDriverOpts defines options used for csi driver
  194. type CSIMockDriverOpts struct {
  195. RegisterDriver bool
  196. DisableAttach bool
  197. PodInfo *bool
  198. AttachLimit int
  199. EnableResizing bool
  200. EnableNodeExpansion bool
  201. }
  202. var _ testsuites.TestDriver = &mockCSIDriver{}
  203. var _ testsuites.DynamicPVTestDriver = &mockCSIDriver{}
  204. // InitMockCSIDriver returns a mockCSIDriver that implements TestDriver interface
  205. func InitMockCSIDriver(driverOpts CSIMockDriverOpts) testsuites.TestDriver {
  206. driverManifests := []string{
  207. "test/e2e/testing-manifests/storage-csi/external-attacher/rbac.yaml",
  208. "test/e2e/testing-manifests/storage-csi/external-provisioner/rbac.yaml",
  209. "test/e2e/testing-manifests/storage-csi/external-resizer/rbac.yaml",
  210. "test/e2e/testing-manifests/storage-csi/mock/csi-mock-rbac.yaml",
  211. "test/e2e/testing-manifests/storage-csi/mock/csi-storageclass.yaml",
  212. "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driver.yaml",
  213. }
  214. if driverOpts.RegisterDriver {
  215. driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driverinfo.yaml")
  216. }
  217. if !driverOpts.DisableAttach {
  218. driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driver-attacher.yaml")
  219. }
  220. if driverOpts.EnableResizing {
  221. driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driver-resizer.yaml")
  222. }
  223. return &mockCSIDriver{
  224. driverInfo: testsuites.DriverInfo{
  225. Name: "csi-mock",
  226. FeatureTag: "",
  227. MaxFileSize: testpatterns.FileSizeMedium,
  228. SupportedFsType: sets.NewString(
  229. "", // Default fsType
  230. ),
  231. Capabilities: map[testsuites.Capability]bool{
  232. testsuites.CapPersistence: false,
  233. testsuites.CapFsGroup: false,
  234. testsuites.CapExec: false,
  235. testsuites.CapVolumeLimits: true,
  236. },
  237. },
  238. manifests: driverManifests,
  239. podInfo: driverOpts.PodInfo,
  240. attachable: !driverOpts.DisableAttach,
  241. attachLimit: driverOpts.AttachLimit,
  242. enableNodeExpansion: driverOpts.EnableNodeExpansion,
  243. }
  244. }
  245. func (m *mockCSIDriver) GetDriverInfo() *testsuites.DriverInfo {
  246. return &m.driverInfo
  247. }
  248. func (m *mockCSIDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  249. }
  250. func (m *mockCSIDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  251. provisioner := config.GetUniqueDriverName()
  252. parameters := map[string]string{}
  253. ns := config.Framework.Namespace.Name
  254. suffix := fmt.Sprintf("%s-sc", provisioner)
  255. return testsuites.GetStorageClass(provisioner, parameters, nil, ns, suffix)
  256. }
  257. func (m *mockCSIDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  258. ginkgo.By("deploying csi mock driver")
  259. cancelLogging := testsuites.StartPodLogs(f)
  260. cs := f.ClientSet
  261. // pods should be scheduled on the node
  262. node, err := e2enode.GetRandomReadySchedulableNode(cs)
  263. framework.ExpectNoError(err)
  264. config := &testsuites.PerTestConfig{
  265. Driver: m,
  266. Prefix: "mock",
  267. Framework: f,
  268. ClientNodeSelection: e2epod.NodeSelection{Name: node.Name},
  269. }
  270. containerArgs := []string{"--name=csi-mock-" + f.UniqueName}
  271. if !m.attachable {
  272. containerArgs = append(containerArgs, "--disable-attach")
  273. }
  274. if m.attachLimit > 0 {
  275. containerArgs = append(containerArgs, "--attach-limit", strconv.Itoa(m.attachLimit))
  276. }
  277. if m.enableNodeExpansion {
  278. containerArgs = append(containerArgs, "--node-expand-required=true")
  279. }
  280. o := utils.PatchCSIOptions{
  281. OldDriverName: "csi-mock",
  282. NewDriverName: "csi-mock-" + f.UniqueName,
  283. DriverContainerName: "mock",
  284. DriverContainerArguments: containerArgs,
  285. ProvisionerContainerName: "csi-provisioner",
  286. NodeName: node.Name,
  287. PodInfo: m.podInfo,
  288. CanAttach: &m.attachable,
  289. VolumeLifecycleModes: &[]storagev1beta1.VolumeLifecycleMode{
  290. storagev1beta1.VolumeLifecyclePersistent,
  291. storagev1beta1.VolumeLifecycleEphemeral,
  292. },
  293. }
  294. cleanup, err := utils.CreateFromManifests(f, func(item interface{}) error {
  295. return utils.PatchCSIDeployment(f, o, item)
  296. },
  297. m.manifests...)
  298. if err != nil {
  299. framework.Failf("deploying csi mock driver: %v", err)
  300. }
  301. return config, func() {
  302. ginkgo.By("uninstalling csi mock driver")
  303. cleanup()
  304. cancelLogging()
  305. }
  306. }
  307. // gce-pd
  308. type gcePDCSIDriver struct {
  309. driverInfo testsuites.DriverInfo
  310. }
  311. var _ testsuites.TestDriver = &gcePDCSIDriver{}
  312. var _ testsuites.DynamicPVTestDriver = &gcePDCSIDriver{}
  313. var _ testsuites.SnapshottableTestDriver = &gcePDCSIDriver{}
  314. // InitGcePDCSIDriver returns gcePDCSIDriver that implements TestDriver interface
  315. func InitGcePDCSIDriver() testsuites.TestDriver {
  316. return &gcePDCSIDriver{
  317. driverInfo: testsuites.DriverInfo{
  318. Name: GCEPDCSIDriverName,
  319. FeatureTag: "[Serial]",
  320. MaxFileSize: testpatterns.FileSizeMedium,
  321. SupportedSizeRange: volume.SizeRange{
  322. Min: "5Gi",
  323. },
  324. SupportedFsType: sets.NewString(
  325. "", // Default fsType
  326. "ext2",
  327. "ext3",
  328. "ext4",
  329. "xfs",
  330. ),
  331. SupportedMountOption: sets.NewString("debug", "nouid32"),
  332. Capabilities: map[testsuites.Capability]bool{
  333. testsuites.CapPersistence: true,
  334. testsuites.CapBlock: true,
  335. testsuites.CapFsGroup: true,
  336. testsuites.CapExec: true,
  337. testsuites.CapMultiPODs: true,
  338. // GCE supports volume limits, but the test creates large
  339. // number of volumes and times out test suites.
  340. testsuites.CapVolumeLimits: false,
  341. testsuites.CapTopology: true,
  342. testsuites.CapControllerExpansion: true,
  343. testsuites.CapNodeExpansion: true,
  344. testsuites.CapSnapshotDataSource: true,
  345. },
  346. RequiredAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
  347. TopologyKeys: []string{GCEPDCSIZoneTopologyKey},
  348. },
  349. }
  350. }
  351. func (g *gcePDCSIDriver) GetDriverInfo() *testsuites.DriverInfo {
  352. return &g.driverInfo
  353. }
  354. func (g *gcePDCSIDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  355. e2eskipper.SkipUnlessProviderIs("gce", "gke")
  356. if pattern.FsType == "xfs" {
  357. e2eskipper.SkipUnlessNodeOSDistroIs("ubuntu", "custom")
  358. }
  359. if pattern.FeatureTag == "[sig-windows]" {
  360. e2eskipper.Skipf("Skipping tests for windows since CSI does not support it yet")
  361. }
  362. }
  363. func (g *gcePDCSIDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  364. ns := config.Framework.Namespace.Name
  365. provisioner := g.driverInfo.Name
  366. suffix := fmt.Sprintf("%s-sc", g.driverInfo.Name)
  367. parameters := map[string]string{"type": "pd-standard"}
  368. if fsType != "" {
  369. parameters["csi.storage.k8s.io/fstype"] = fsType
  370. }
  371. delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer
  372. return testsuites.GetStorageClass(provisioner, parameters, &delayedBinding, ns, suffix)
  373. }
  374. func (g *gcePDCSIDriver) GetSnapshotClass(config *testsuites.PerTestConfig) *unstructured.Unstructured {
  375. parameters := map[string]string{}
  376. snapshotter := g.driverInfo.Name
  377. ns := config.Framework.Namespace.Name
  378. suffix := fmt.Sprintf("%s-vsc", snapshotter)
  379. return testsuites.GetSnapshotClass(snapshotter, parameters, ns, suffix)
  380. }
  381. func (g *gcePDCSIDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  382. ginkgo.By("deploying csi gce-pd driver")
  383. cancelLogging := testsuites.StartPodLogs(f)
  384. // It would be safer to rename the gcePD driver, but that
  385. // hasn't been done before either and attempts to do so now led to
  386. // errors during driver registration, therefore it is disabled
  387. // by passing a nil function below.
  388. //
  389. // These are the options which would have to be used:
  390. // o := utils.PatchCSIOptions{
  391. // OldDriverName: g.driverInfo.Name,
  392. // NewDriverName: testsuites.GetUniqueDriverName(g),
  393. // DriverContainerName: "gce-driver",
  394. // ProvisionerContainerName: "csi-external-provisioner",
  395. // }
  396. createGCESecrets(f.ClientSet, f.Namespace.Name)
  397. manifests := []string{
  398. "test/e2e/testing-manifests/storage-csi/external-attacher/rbac.yaml",
  399. "test/e2e/testing-manifests/storage-csi/external-provisioner/rbac.yaml",
  400. "test/e2e/testing-manifests/storage-csi/gce-pd/csi-controller-rbac.yaml",
  401. "test/e2e/testing-manifests/storage-csi/gce-pd/node_ds.yaml",
  402. "test/e2e/testing-manifests/storage-csi/gce-pd/controller_ss.yaml",
  403. }
  404. cleanup, err := utils.CreateFromManifests(f, nil, manifests...)
  405. if err != nil {
  406. framework.Failf("deploying csi gce-pd driver: %v", err)
  407. }
  408. if err = waitForCSIDriverRegistrationOnAllNodes(GCEPDCSIDriverName, f.ClientSet); err != nil {
  409. framework.Failf("waiting for csi driver node registration on: %v", err)
  410. }
  411. return &testsuites.PerTestConfig{
  412. Driver: g,
  413. Prefix: "gcepd",
  414. Framework: f,
  415. }, func() {
  416. ginkgo.By("uninstalling gce-pd driver")
  417. cleanup()
  418. cancelLogging()
  419. }
  420. }
  421. func waitForCSIDriverRegistrationOnAllNodes(driverName string, cs clientset.Interface) error {
  422. nodes, err := e2enode.GetReadySchedulableNodes(cs)
  423. if err != nil {
  424. return err
  425. }
  426. for _, node := range nodes.Items {
  427. if err := waitForCSIDriverRegistrationOnNode(node.Name, driverName, cs); err != nil {
  428. return err
  429. }
  430. }
  431. return nil
  432. }
  433. func waitForCSIDriverRegistrationOnNode(nodeName string, driverName string, cs clientset.Interface) error {
  434. const csiNodeRegisterTimeout = 1 * time.Minute
  435. waitErr := wait.PollImmediate(10*time.Second, csiNodeRegisterTimeout, func() (bool, error) {
  436. csiNode, err := cs.StorageV1().CSINodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
  437. if err != nil && !apierrors.IsNotFound(err) {
  438. return false, err
  439. }
  440. for _, driver := range csiNode.Spec.Drivers {
  441. if driver.Name == driverName {
  442. return true, nil
  443. }
  444. }
  445. return false, nil
  446. })
  447. if waitErr != nil {
  448. return fmt.Errorf("error waiting for CSI driver %s registration on node %s: %v", driverName, nodeName, waitErr)
  449. }
  450. return nil
  451. }