in_tree.go 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959
  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 in-tree 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. "os/exec"
  36. "strconv"
  37. "strings"
  38. "time"
  39. "github.com/onsi/ginkgo"
  40. v1 "k8s.io/api/core/v1"
  41. rbacv1 "k8s.io/api/rbac/v1"
  42. storagev1 "k8s.io/api/storage/v1"
  43. apierrors "k8s.io/apimachinery/pkg/api/errors"
  44. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  45. "k8s.io/apimachinery/pkg/runtime/schema"
  46. "k8s.io/apimachinery/pkg/util/sets"
  47. "k8s.io/apiserver/pkg/authentication/serviceaccount"
  48. clientset "k8s.io/client-go/kubernetes"
  49. "k8s.io/kubernetes/test/e2e/framework"
  50. "k8s.io/kubernetes/test/e2e/framework/auth"
  51. e2enode "k8s.io/kubernetes/test/e2e/framework/node"
  52. e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
  53. e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
  54. e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
  55. "k8s.io/kubernetes/test/e2e/framework/volume"
  56. "k8s.io/kubernetes/test/e2e/storage/testpatterns"
  57. "k8s.io/kubernetes/test/e2e/storage/testsuites"
  58. "k8s.io/kubernetes/test/e2e/storage/utils"
  59. vspheretest "k8s.io/kubernetes/test/e2e/storage/vsphere"
  60. imageutils "k8s.io/kubernetes/test/utils/image"
  61. )
  62. const (
  63. // Template for iSCSI IQN.
  64. iSCSIIQNTemplate = "iqn.2003-01.io.k8s:e2e.%s"
  65. )
  66. // NFS
  67. type nfsDriver struct {
  68. externalProvisionerPod *v1.Pod
  69. externalPluginName string
  70. driverInfo testsuites.DriverInfo
  71. }
  72. type nfsVolume struct {
  73. serverIP string
  74. serverPod *v1.Pod
  75. f *framework.Framework
  76. }
  77. var _ testsuites.TestDriver = &nfsDriver{}
  78. var _ testsuites.PreprovisionedVolumeTestDriver = &nfsDriver{}
  79. var _ testsuites.InlineVolumeTestDriver = &nfsDriver{}
  80. var _ testsuites.PreprovisionedPVTestDriver = &nfsDriver{}
  81. var _ testsuites.DynamicPVTestDriver = &nfsDriver{}
  82. // InitNFSDriver returns nfsDriver that implements TestDriver interface
  83. func InitNFSDriver() testsuites.TestDriver {
  84. return &nfsDriver{
  85. driverInfo: testsuites.DriverInfo{
  86. Name: "nfs",
  87. InTreePluginName: "kubernetes.io/nfs",
  88. MaxFileSize: testpatterns.FileSizeLarge,
  89. SupportedSizeRange: volume.SizeRange{
  90. Min: "5Gi",
  91. },
  92. SupportedFsType: sets.NewString(
  93. "", // Default fsType
  94. ),
  95. SupportedMountOption: sets.NewString("proto=tcp", "relatime"),
  96. RequiredMountOption: sets.NewString("vers=4.1"),
  97. Capabilities: map[testsuites.Capability]bool{
  98. testsuites.CapPersistence: true,
  99. testsuites.CapExec: true,
  100. testsuites.CapRWX: true,
  101. testsuites.CapMultiPODs: true,
  102. },
  103. },
  104. }
  105. }
  106. func (n *nfsDriver) GetDriverInfo() *testsuites.DriverInfo {
  107. return &n.driverInfo
  108. }
  109. func (n *nfsDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  110. }
  111. func (n *nfsDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  112. nv, ok := volume.(*nfsVolume)
  113. framework.ExpectEqual(ok, true, "Failed to cast test volume to NFS test volume")
  114. return &v1.VolumeSource{
  115. NFS: &v1.NFSVolumeSource{
  116. Server: nv.serverIP,
  117. Path: "/",
  118. ReadOnly: readOnly,
  119. },
  120. }
  121. }
  122. func (n *nfsDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  123. nv, ok := volume.(*nfsVolume)
  124. framework.ExpectEqual(ok, true, "Failed to cast test volume to NFS test volume")
  125. return &v1.PersistentVolumeSource{
  126. NFS: &v1.NFSVolumeSource{
  127. Server: nv.serverIP,
  128. Path: "/",
  129. ReadOnly: readOnly,
  130. },
  131. }, nil
  132. }
  133. func (n *nfsDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  134. provisioner := n.externalPluginName
  135. parameters := map[string]string{"mountOptions": "vers=4.1"}
  136. ns := config.Framework.Namespace.Name
  137. suffix := fmt.Sprintf("%s-sc", n.driverInfo.Name)
  138. return testsuites.GetStorageClass(provisioner, parameters, nil, ns, suffix)
  139. }
  140. func (n *nfsDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  141. cs := f.ClientSet
  142. ns := f.Namespace
  143. n.externalPluginName = fmt.Sprintf("example.com/nfs-%s", ns.Name)
  144. // TODO(mkimuram): cluster-admin gives too much right but system:persistent-volume-provisioner
  145. // is not enough. We should create new clusterrole for testing.
  146. err := auth.BindClusterRole(cs.RbacV1(), "cluster-admin", ns.Name,
  147. rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Namespace: ns.Name, Name: "default"})
  148. framework.ExpectNoError(err)
  149. err = auth.WaitForAuthorizationUpdate(cs.AuthorizationV1(),
  150. serviceaccount.MakeUsername(ns.Name, "default"),
  151. "", "get", schema.GroupResource{Group: "storage.k8s.io", Resource: "storageclasses"}, true)
  152. framework.ExpectNoError(err, "Failed to update authorization: %v", err)
  153. ginkgo.By("creating an external dynamic provisioner pod")
  154. n.externalProvisionerPod = utils.StartExternalProvisioner(cs, ns.Name, n.externalPluginName)
  155. return &testsuites.PerTestConfig{
  156. Driver: n,
  157. Prefix: "nfs",
  158. Framework: f,
  159. }, func() {
  160. framework.ExpectNoError(e2epod.DeletePodWithWait(cs, n.externalProvisionerPod))
  161. clusterRoleBindingName := ns.Name + "--" + "cluster-admin"
  162. cs.RbacV1().ClusterRoleBindings().Delete(context.TODO(), clusterRoleBindingName, metav1.NewDeleteOptions(0))
  163. }
  164. }
  165. func (n *nfsDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  166. f := config.Framework
  167. cs := f.ClientSet
  168. ns := f.Namespace
  169. // NewNFSServer creates a pod for InlineVolume and PreprovisionedPV,
  170. // and startExternalProvisioner creates a pod for DynamicPV.
  171. // Therefore, we need a different PrepareTest logic for volType.
  172. switch volType {
  173. case testpatterns.InlineVolume:
  174. fallthrough
  175. case testpatterns.PreprovisionedPV:
  176. c, serverPod, serverIP := volume.NewNFSServer(cs, ns.Name, []string{})
  177. config.ServerConfig = &c
  178. return &nfsVolume{
  179. serverIP: serverIP,
  180. serverPod: serverPod,
  181. f: f,
  182. }
  183. case testpatterns.DynamicPV:
  184. // Do nothing
  185. default:
  186. framework.Failf("Unsupported volType:%v is specified", volType)
  187. }
  188. return nil
  189. }
  190. func (v *nfsVolume) DeleteVolume() {
  191. cleanUpVolumeServer(v.f, v.serverPod)
  192. }
  193. // Gluster
  194. type glusterFSDriver struct {
  195. driverInfo testsuites.DriverInfo
  196. }
  197. type glusterVolume struct {
  198. prefix string
  199. serverPod *v1.Pod
  200. f *framework.Framework
  201. }
  202. var _ testsuites.TestDriver = &glusterFSDriver{}
  203. var _ testsuites.PreprovisionedVolumeTestDriver = &glusterFSDriver{}
  204. var _ testsuites.InlineVolumeTestDriver = &glusterFSDriver{}
  205. var _ testsuites.PreprovisionedPVTestDriver = &glusterFSDriver{}
  206. // InitGlusterFSDriver returns glusterFSDriver that implements TestDriver interface
  207. func InitGlusterFSDriver() testsuites.TestDriver {
  208. return &glusterFSDriver{
  209. driverInfo: testsuites.DriverInfo{
  210. Name: "gluster",
  211. InTreePluginName: "kubernetes.io/glusterfs",
  212. MaxFileSize: testpatterns.FileSizeMedium,
  213. SupportedSizeRange: volume.SizeRange{
  214. Min: "5Gi",
  215. },
  216. SupportedFsType: sets.NewString(
  217. "", // Default fsType
  218. ),
  219. Capabilities: map[testsuites.Capability]bool{
  220. testsuites.CapPersistence: true,
  221. testsuites.CapExec: true,
  222. testsuites.CapRWX: true,
  223. testsuites.CapMultiPODs: true,
  224. },
  225. },
  226. }
  227. }
  228. func (g *glusterFSDriver) GetDriverInfo() *testsuites.DriverInfo {
  229. return &g.driverInfo
  230. }
  231. func (g *glusterFSDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  232. e2eskipper.SkipUnlessNodeOSDistroIs("gci", "ubuntu", "custom")
  233. }
  234. func (g *glusterFSDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  235. gv, ok := volume.(*glusterVolume)
  236. framework.ExpectEqual(ok, true, "Failed to cast test volume to Gluster test volume")
  237. name := gv.prefix + "-server"
  238. return &v1.VolumeSource{
  239. Glusterfs: &v1.GlusterfsVolumeSource{
  240. EndpointsName: name,
  241. // 'test_vol' comes from test/images/volumes-tester/gluster/run_gluster.sh
  242. Path: "test_vol",
  243. ReadOnly: readOnly,
  244. },
  245. }
  246. }
  247. func (g *glusterFSDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  248. gv, ok := volume.(*glusterVolume)
  249. framework.ExpectEqual(ok, true, "Failed to cast test volume to Gluster test volume")
  250. name := gv.prefix + "-server"
  251. return &v1.PersistentVolumeSource{
  252. Glusterfs: &v1.GlusterfsPersistentVolumeSource{
  253. EndpointsName: name,
  254. // 'test_vol' comes from test/images/volumes-tester/gluster/run_gluster.sh
  255. Path: "test_vol",
  256. ReadOnly: readOnly,
  257. },
  258. }, nil
  259. }
  260. func (g *glusterFSDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  261. return &testsuites.PerTestConfig{
  262. Driver: g,
  263. Prefix: "gluster",
  264. Framework: f,
  265. }, func() {}
  266. }
  267. func (g *glusterFSDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  268. f := config.Framework
  269. cs := f.ClientSet
  270. ns := f.Namespace
  271. c, serverPod, _ := volume.NewGlusterfsServer(cs, ns.Name)
  272. config.ServerConfig = &c
  273. return &glusterVolume{
  274. prefix: config.Prefix,
  275. serverPod: serverPod,
  276. f: f,
  277. }
  278. }
  279. func (v *glusterVolume) DeleteVolume() {
  280. f := v.f
  281. cs := f.ClientSet
  282. ns := f.Namespace
  283. name := v.prefix + "-server"
  284. framework.Logf("Deleting Gluster endpoints %q...", name)
  285. err := cs.CoreV1().Endpoints(ns.Name).Delete(context.TODO(), name, nil)
  286. if err != nil {
  287. if !apierrors.IsNotFound(err) {
  288. framework.Failf("Gluster delete endpoints failed: %v", err)
  289. }
  290. framework.Logf("Gluster endpoints %q not found, assuming deleted", name)
  291. }
  292. framework.Logf("Deleting Gluster server pod %q...", v.serverPod.Name)
  293. err = e2epod.DeletePodWithWait(cs, v.serverPod)
  294. if err != nil {
  295. framework.Failf("Gluster server pod delete failed: %v", err)
  296. }
  297. }
  298. // iSCSI
  299. // The iscsiadm utility and iscsi target kernel modules must be installed on all nodes.
  300. type iSCSIDriver struct {
  301. driverInfo testsuites.DriverInfo
  302. }
  303. type iSCSIVolume struct {
  304. serverPod *v1.Pod
  305. serverIP string
  306. f *framework.Framework
  307. iqn string
  308. }
  309. var _ testsuites.TestDriver = &iSCSIDriver{}
  310. var _ testsuites.PreprovisionedVolumeTestDriver = &iSCSIDriver{}
  311. var _ testsuites.InlineVolumeTestDriver = &iSCSIDriver{}
  312. var _ testsuites.PreprovisionedPVTestDriver = &iSCSIDriver{}
  313. // InitISCSIDriver returns iSCSIDriver that implements TestDriver interface
  314. func InitISCSIDriver() testsuites.TestDriver {
  315. return &iSCSIDriver{
  316. driverInfo: testsuites.DriverInfo{
  317. Name: "iscsi",
  318. InTreePluginName: "kubernetes.io/iscsi",
  319. FeatureTag: "[Feature:Volumes]",
  320. MaxFileSize: testpatterns.FileSizeMedium,
  321. SupportedFsType: sets.NewString(
  322. "", // Default fsType
  323. "ext2",
  324. // TODO: fix iSCSI driver can work with ext3
  325. //"ext3",
  326. "ext4",
  327. ),
  328. TopologyKeys: []string{v1.LabelHostname},
  329. Capabilities: map[testsuites.Capability]bool{
  330. testsuites.CapPersistence: true,
  331. testsuites.CapFsGroup: true,
  332. testsuites.CapBlock: true,
  333. testsuites.CapExec: true,
  334. testsuites.CapMultiPODs: true,
  335. testsuites.CapTopology: true,
  336. },
  337. },
  338. }
  339. }
  340. func (i *iSCSIDriver) GetDriverInfo() *testsuites.DriverInfo {
  341. return &i.driverInfo
  342. }
  343. func (i *iSCSIDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  344. }
  345. func (i *iSCSIDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  346. iv, ok := volume.(*iSCSIVolume)
  347. framework.ExpectEqual(ok, true, "Failed to cast test volume to iSCSI test volume")
  348. volSource := v1.VolumeSource{
  349. ISCSI: &v1.ISCSIVolumeSource{
  350. TargetPortal: "127.0.0.1:3260",
  351. IQN: iv.iqn,
  352. Lun: 0,
  353. ReadOnly: readOnly,
  354. },
  355. }
  356. if fsType != "" {
  357. volSource.ISCSI.FSType = fsType
  358. }
  359. return &volSource
  360. }
  361. func (i *iSCSIDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  362. iv, ok := volume.(*iSCSIVolume)
  363. framework.ExpectEqual(ok, true, "Failed to cast test volume to iSCSI test volume")
  364. pvSource := v1.PersistentVolumeSource{
  365. ISCSI: &v1.ISCSIPersistentVolumeSource{
  366. TargetPortal: "127.0.0.1:3260",
  367. IQN: iv.iqn,
  368. Lun: 0,
  369. ReadOnly: readOnly,
  370. },
  371. }
  372. if fsType != "" {
  373. pvSource.ISCSI.FSType = fsType
  374. }
  375. return &pvSource, nil
  376. }
  377. func (i *iSCSIDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  378. return &testsuites.PerTestConfig{
  379. Driver: i,
  380. Prefix: "iscsi",
  381. Framework: f,
  382. }, func() {}
  383. }
  384. func (i *iSCSIDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  385. f := config.Framework
  386. cs := f.ClientSet
  387. ns := f.Namespace
  388. c, serverPod, serverIP, iqn := newISCSIServer(cs, ns.Name)
  389. config.ServerConfig = &c
  390. config.ClientNodeSelection = c.ClientNodeSelection
  391. return &iSCSIVolume{
  392. serverPod: serverPod,
  393. serverIP: serverIP,
  394. iqn: iqn,
  395. f: f,
  396. }
  397. }
  398. // newISCSIServer is an iSCSI-specific wrapper for CreateStorageServer.
  399. func newISCSIServer(cs clientset.Interface, namespace string) (config volume.TestConfig, pod *v1.Pod, ip, iqn string) {
  400. // Generate cluster-wide unique IQN
  401. iqn = fmt.Sprintf(iSCSIIQNTemplate, namespace)
  402. config = volume.TestConfig{
  403. Namespace: namespace,
  404. Prefix: "iscsi",
  405. ServerImage: imageutils.GetE2EImage(imageutils.VolumeISCSIServer),
  406. ServerArgs: []string{iqn},
  407. ServerVolumes: map[string]string{
  408. // iSCSI container needs to insert modules from the host
  409. "/lib/modules": "/lib/modules",
  410. // iSCSI container needs to configure kernel
  411. "/sys/kernel": "/sys/kernel",
  412. // iSCSI source "block devices" must be available on the host
  413. "/srv/iscsi": "/srv/iscsi",
  414. },
  415. ServerReadyMessage: "iscsi target started",
  416. ServerHostNetwork: true,
  417. }
  418. pod, ip = volume.CreateStorageServer(cs, config)
  419. // Make sure the client runs on the same node as server so we don't need to open any firewalls.
  420. config.ClientNodeSelection = e2epod.NodeSelection{Name: pod.Spec.NodeName}
  421. return config, pod, ip, iqn
  422. }
  423. // newRBDServer is a CephRBD-specific wrapper for CreateStorageServer.
  424. func newRBDServer(cs clientset.Interface, namespace string) (config volume.TestConfig, pod *v1.Pod, secret *v1.Secret, ip string) {
  425. config = volume.TestConfig{
  426. Namespace: namespace,
  427. Prefix: "rbd",
  428. ServerImage: imageutils.GetE2EImage(imageutils.VolumeRBDServer),
  429. ServerPorts: []int{6789},
  430. ServerVolumes: map[string]string{
  431. "/lib/modules": "/lib/modules",
  432. },
  433. ServerReadyMessage: "Ceph is ready",
  434. }
  435. pod, ip = volume.CreateStorageServer(cs, config)
  436. // create secrets for the server
  437. secret = &v1.Secret{
  438. TypeMeta: metav1.TypeMeta{
  439. Kind: "Secret",
  440. APIVersion: "v1",
  441. },
  442. ObjectMeta: metav1.ObjectMeta{
  443. Name: config.Prefix + "-secret",
  444. },
  445. Data: map[string][]byte{
  446. // from test/images/volumes-tester/rbd/keyring
  447. "key": []byte("AQDRrKNVbEevChAAEmRC+pW/KBVHxa0w/POILA=="),
  448. },
  449. Type: "kubernetes.io/rbd",
  450. }
  451. secret, err := cs.CoreV1().Secrets(config.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
  452. if err != nil {
  453. framework.Failf("Failed to create secrets for Ceph RBD: %v", err)
  454. }
  455. return config, pod, secret, ip
  456. }
  457. func (v *iSCSIVolume) DeleteVolume() {
  458. cleanUpVolumeServer(v.f, v.serverPod)
  459. }
  460. // Ceph RBD
  461. type rbdDriver struct {
  462. driverInfo testsuites.DriverInfo
  463. }
  464. type rbdVolume struct {
  465. serverPod *v1.Pod
  466. serverIP string
  467. secret *v1.Secret
  468. f *framework.Framework
  469. }
  470. var _ testsuites.TestDriver = &rbdDriver{}
  471. var _ testsuites.PreprovisionedVolumeTestDriver = &rbdDriver{}
  472. var _ testsuites.InlineVolumeTestDriver = &rbdDriver{}
  473. var _ testsuites.PreprovisionedPVTestDriver = &rbdDriver{}
  474. // InitRbdDriver returns rbdDriver that implements TestDriver interface
  475. func InitRbdDriver() testsuites.TestDriver {
  476. return &rbdDriver{
  477. driverInfo: testsuites.DriverInfo{
  478. Name: "rbd",
  479. InTreePluginName: "kubernetes.io/rbd",
  480. FeatureTag: "[Feature:Volumes][Serial]",
  481. MaxFileSize: testpatterns.FileSizeMedium,
  482. SupportedSizeRange: volume.SizeRange{
  483. Min: "5Gi",
  484. },
  485. SupportedFsType: sets.NewString(
  486. "", // Default fsType
  487. "ext2",
  488. // TODO: fix rbd driver can work with ext3
  489. //"ext3",
  490. "ext4",
  491. ),
  492. Capabilities: map[testsuites.Capability]bool{
  493. testsuites.CapPersistence: true,
  494. testsuites.CapFsGroup: true,
  495. testsuites.CapBlock: true,
  496. testsuites.CapExec: true,
  497. testsuites.CapMultiPODs: true,
  498. },
  499. },
  500. }
  501. }
  502. func (r *rbdDriver) GetDriverInfo() *testsuites.DriverInfo {
  503. return &r.driverInfo
  504. }
  505. func (r *rbdDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  506. }
  507. func (r *rbdDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  508. rv, ok := volume.(*rbdVolume)
  509. framework.ExpectEqual(ok, true, "Failed to cast test volume to RBD test volume")
  510. volSource := v1.VolumeSource{
  511. RBD: &v1.RBDVolumeSource{
  512. CephMonitors: []string{rv.serverIP},
  513. RBDPool: "rbd",
  514. RBDImage: "foo",
  515. RadosUser: "admin",
  516. SecretRef: &v1.LocalObjectReference{
  517. Name: rv.secret.Name,
  518. },
  519. ReadOnly: readOnly,
  520. },
  521. }
  522. if fsType != "" {
  523. volSource.RBD.FSType = fsType
  524. }
  525. return &volSource
  526. }
  527. func (r *rbdDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  528. rv, ok := volume.(*rbdVolume)
  529. framework.ExpectEqual(ok, true, "Failed to cast test volume to RBD test volume")
  530. f := rv.f
  531. ns := f.Namespace
  532. pvSource := v1.PersistentVolumeSource{
  533. RBD: &v1.RBDPersistentVolumeSource{
  534. CephMonitors: []string{rv.serverIP},
  535. RBDPool: "rbd",
  536. RBDImage: "foo",
  537. RadosUser: "admin",
  538. SecretRef: &v1.SecretReference{
  539. Name: rv.secret.Name,
  540. Namespace: ns.Name,
  541. },
  542. ReadOnly: readOnly,
  543. },
  544. }
  545. if fsType != "" {
  546. pvSource.RBD.FSType = fsType
  547. }
  548. return &pvSource, nil
  549. }
  550. func (r *rbdDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  551. return &testsuites.PerTestConfig{
  552. Driver: r,
  553. Prefix: "rbd",
  554. Framework: f,
  555. }, func() {}
  556. }
  557. func (r *rbdDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  558. f := config.Framework
  559. cs := f.ClientSet
  560. ns := f.Namespace
  561. c, serverPod, secret, serverIP := newRBDServer(cs, ns.Name)
  562. config.ServerConfig = &c
  563. return &rbdVolume{
  564. serverPod: serverPod,
  565. serverIP: serverIP,
  566. secret: secret,
  567. f: f,
  568. }
  569. }
  570. func (v *rbdVolume) DeleteVolume() {
  571. cleanUpVolumeServerWithSecret(v.f, v.serverPod, v.secret)
  572. }
  573. // Ceph
  574. type cephFSDriver struct {
  575. driverInfo testsuites.DriverInfo
  576. }
  577. type cephVolume struct {
  578. serverPod *v1.Pod
  579. serverIP string
  580. secret *v1.Secret
  581. f *framework.Framework
  582. }
  583. var _ testsuites.TestDriver = &cephFSDriver{}
  584. var _ testsuites.PreprovisionedVolumeTestDriver = &cephFSDriver{}
  585. var _ testsuites.InlineVolumeTestDriver = &cephFSDriver{}
  586. var _ testsuites.PreprovisionedPVTestDriver = &cephFSDriver{}
  587. // InitCephFSDriver returns cephFSDriver that implements TestDriver interface
  588. func InitCephFSDriver() testsuites.TestDriver {
  589. return &cephFSDriver{
  590. driverInfo: testsuites.DriverInfo{
  591. Name: "ceph",
  592. InTreePluginName: "kubernetes.io/cephfs",
  593. FeatureTag: "[Feature:Volumes][Serial]",
  594. MaxFileSize: testpatterns.FileSizeMedium,
  595. SupportedSizeRange: volume.SizeRange{
  596. Min: "5Gi",
  597. },
  598. SupportedFsType: sets.NewString(
  599. "", // Default fsType
  600. ),
  601. Capabilities: map[testsuites.Capability]bool{
  602. testsuites.CapPersistence: true,
  603. testsuites.CapExec: true,
  604. testsuites.CapRWX: true,
  605. testsuites.CapMultiPODs: true,
  606. },
  607. },
  608. }
  609. }
  610. func (c *cephFSDriver) GetDriverInfo() *testsuites.DriverInfo {
  611. return &c.driverInfo
  612. }
  613. func (c *cephFSDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  614. }
  615. func (c *cephFSDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  616. cv, ok := volume.(*cephVolume)
  617. framework.ExpectEqual(ok, true, "Failed to cast test volume to Ceph test volume")
  618. return &v1.VolumeSource{
  619. CephFS: &v1.CephFSVolumeSource{
  620. Monitors: []string{cv.serverIP + ":6789"},
  621. User: "kube",
  622. SecretRef: &v1.LocalObjectReference{
  623. Name: cv.secret.Name,
  624. },
  625. ReadOnly: readOnly,
  626. },
  627. }
  628. }
  629. func (c *cephFSDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  630. cv, ok := volume.(*cephVolume)
  631. framework.ExpectEqual(ok, true, "Failed to cast test volume to Ceph test volume")
  632. ns := cv.f.Namespace
  633. return &v1.PersistentVolumeSource{
  634. CephFS: &v1.CephFSPersistentVolumeSource{
  635. Monitors: []string{cv.serverIP + ":6789"},
  636. User: "kube",
  637. SecretRef: &v1.SecretReference{
  638. Name: cv.secret.Name,
  639. Namespace: ns.Name,
  640. },
  641. ReadOnly: readOnly,
  642. },
  643. }, nil
  644. }
  645. func (c *cephFSDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  646. return &testsuites.PerTestConfig{
  647. Driver: c,
  648. Prefix: "cephfs",
  649. Framework: f,
  650. }, func() {}
  651. }
  652. func (c *cephFSDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  653. f := config.Framework
  654. cs := f.ClientSet
  655. ns := f.Namespace
  656. cfg, serverPod, secret, serverIP := newRBDServer(cs, ns.Name)
  657. config.ServerConfig = &cfg
  658. return &cephVolume{
  659. serverPod: serverPod,
  660. serverIP: serverIP,
  661. secret: secret,
  662. f: f,
  663. }
  664. }
  665. func (v *cephVolume) DeleteVolume() {
  666. cleanUpVolumeServerWithSecret(v.f, v.serverPod, v.secret)
  667. }
  668. // Hostpath
  669. type hostPathDriver struct {
  670. driverInfo testsuites.DriverInfo
  671. }
  672. var _ testsuites.TestDriver = &hostPathDriver{}
  673. var _ testsuites.PreprovisionedVolumeTestDriver = &hostPathDriver{}
  674. var _ testsuites.InlineVolumeTestDriver = &hostPathDriver{}
  675. // InitHostPathDriver returns hostPathDriver that implements TestDriver interface
  676. func InitHostPathDriver() testsuites.TestDriver {
  677. return &hostPathDriver{
  678. driverInfo: testsuites.DriverInfo{
  679. Name: "hostPath",
  680. InTreePluginName: "kubernetes.io/host-path",
  681. MaxFileSize: testpatterns.FileSizeMedium,
  682. SupportedFsType: sets.NewString(
  683. "", // Default fsType
  684. ),
  685. TopologyKeys: []string{v1.LabelHostname},
  686. Capabilities: map[testsuites.Capability]bool{
  687. testsuites.CapPersistence: true,
  688. testsuites.CapMultiPODs: true,
  689. testsuites.CapSingleNodeVolume: true,
  690. testsuites.CapTopology: true,
  691. },
  692. },
  693. }
  694. }
  695. func (h *hostPathDriver) GetDriverInfo() *testsuites.DriverInfo {
  696. return &h.driverInfo
  697. }
  698. func (h *hostPathDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  699. }
  700. func (h *hostPathDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  701. // hostPath doesn't support readOnly volume
  702. if readOnly {
  703. return nil
  704. }
  705. return &v1.VolumeSource{
  706. HostPath: &v1.HostPathVolumeSource{
  707. Path: "/tmp",
  708. },
  709. }
  710. }
  711. func (h *hostPathDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  712. return &testsuites.PerTestConfig{
  713. Driver: h,
  714. Prefix: "hostpath",
  715. Framework: f,
  716. }, func() {}
  717. }
  718. func (h *hostPathDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  719. f := config.Framework
  720. cs := f.ClientSet
  721. // pods should be scheduled on the node
  722. node, err := e2enode.GetRandomReadySchedulableNode(cs)
  723. framework.ExpectNoError(err)
  724. config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name}
  725. return nil
  726. }
  727. // HostPathSymlink
  728. type hostPathSymlinkDriver struct {
  729. driverInfo testsuites.DriverInfo
  730. }
  731. type hostPathSymlinkVolume struct {
  732. targetPath string
  733. sourcePath string
  734. prepPod *v1.Pod
  735. f *framework.Framework
  736. }
  737. var _ testsuites.TestDriver = &hostPathSymlinkDriver{}
  738. var _ testsuites.PreprovisionedVolumeTestDriver = &hostPathSymlinkDriver{}
  739. var _ testsuites.InlineVolumeTestDriver = &hostPathSymlinkDriver{}
  740. // InitHostPathSymlinkDriver returns hostPathSymlinkDriver that implements TestDriver interface
  741. func InitHostPathSymlinkDriver() testsuites.TestDriver {
  742. return &hostPathSymlinkDriver{
  743. driverInfo: testsuites.DriverInfo{
  744. Name: "hostPathSymlink",
  745. InTreePluginName: "kubernetes.io/host-path",
  746. MaxFileSize: testpatterns.FileSizeMedium,
  747. SupportedFsType: sets.NewString(
  748. "", // Default fsType
  749. ),
  750. TopologyKeys: []string{v1.LabelHostname},
  751. Capabilities: map[testsuites.Capability]bool{
  752. testsuites.CapPersistence: true,
  753. testsuites.CapMultiPODs: true,
  754. testsuites.CapSingleNodeVolume: true,
  755. testsuites.CapTopology: true,
  756. },
  757. },
  758. }
  759. }
  760. func (h *hostPathSymlinkDriver) GetDriverInfo() *testsuites.DriverInfo {
  761. return &h.driverInfo
  762. }
  763. func (h *hostPathSymlinkDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  764. }
  765. func (h *hostPathSymlinkDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  766. hv, ok := volume.(*hostPathSymlinkVolume)
  767. framework.ExpectEqual(ok, true, "Failed to cast test volume to Hostpath Symlink test volume")
  768. // hostPathSymlink doesn't support readOnly volume
  769. if readOnly {
  770. return nil
  771. }
  772. return &v1.VolumeSource{
  773. HostPath: &v1.HostPathVolumeSource{
  774. Path: hv.targetPath,
  775. },
  776. }
  777. }
  778. func (h *hostPathSymlinkDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  779. return &testsuites.PerTestConfig{
  780. Driver: h,
  781. Prefix: "hostpathsymlink",
  782. Framework: f,
  783. }, func() {}
  784. }
  785. func (h *hostPathSymlinkDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  786. f := config.Framework
  787. cs := f.ClientSet
  788. sourcePath := fmt.Sprintf("/tmp/%v", f.Namespace.Name)
  789. targetPath := fmt.Sprintf("/tmp/%v-link", f.Namespace.Name)
  790. volumeName := "test-volume"
  791. // pods should be scheduled on the node
  792. node, err := e2enode.GetRandomReadySchedulableNode(cs)
  793. framework.ExpectNoError(err)
  794. config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name}
  795. cmd := fmt.Sprintf("mkdir %v -m 777 && ln -s %v %v", sourcePath, sourcePath, targetPath)
  796. privileged := true
  797. // Launch pod to initialize hostPath directory and symlink
  798. prepPod := &v1.Pod{
  799. ObjectMeta: metav1.ObjectMeta{
  800. Name: fmt.Sprintf("hostpath-symlink-prep-%s", f.Namespace.Name),
  801. },
  802. Spec: v1.PodSpec{
  803. Containers: []v1.Container{
  804. {
  805. Name: fmt.Sprintf("init-volume-%s", f.Namespace.Name),
  806. Image: imageutils.GetE2EImage(imageutils.BusyBox),
  807. Command: []string{"/bin/sh", "-ec", cmd},
  808. VolumeMounts: []v1.VolumeMount{
  809. {
  810. Name: volumeName,
  811. MountPath: "/tmp",
  812. },
  813. },
  814. SecurityContext: &v1.SecurityContext{
  815. Privileged: &privileged,
  816. },
  817. },
  818. },
  819. RestartPolicy: v1.RestartPolicyNever,
  820. Volumes: []v1.Volume{
  821. {
  822. Name: volumeName,
  823. VolumeSource: v1.VolumeSource{
  824. HostPath: &v1.HostPathVolumeSource{
  825. Path: "/tmp",
  826. },
  827. },
  828. },
  829. },
  830. NodeName: node.Name,
  831. },
  832. }
  833. // h.prepPod will be reused in cleanupDriver.
  834. pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(context.TODO(), prepPod, metav1.CreateOptions{})
  835. framework.ExpectNoError(err, "while creating hostPath init pod")
  836. err = e2epod.WaitForPodSuccessInNamespace(f.ClientSet, pod.Name, pod.Namespace)
  837. framework.ExpectNoError(err, "while waiting for hostPath init pod to succeed")
  838. err = e2epod.DeletePodWithWait(f.ClientSet, pod)
  839. framework.ExpectNoError(err, "while deleting hostPath init pod")
  840. return &hostPathSymlinkVolume{
  841. sourcePath: sourcePath,
  842. targetPath: targetPath,
  843. prepPod: prepPod,
  844. f: f,
  845. }
  846. }
  847. func (v *hostPathSymlinkVolume) DeleteVolume() {
  848. f := v.f
  849. cmd := fmt.Sprintf("rm -rf %v&& rm -rf %v", v.targetPath, v.sourcePath)
  850. v.prepPod.Spec.Containers[0].Command = []string{"/bin/sh", "-ec", cmd}
  851. pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(context.TODO(), v.prepPod, metav1.CreateOptions{})
  852. framework.ExpectNoError(err, "while creating hostPath teardown pod")
  853. err = e2epod.WaitForPodSuccessInNamespace(f.ClientSet, pod.Name, pod.Namespace)
  854. framework.ExpectNoError(err, "while waiting for hostPath teardown pod to succeed")
  855. err = e2epod.DeletePodWithWait(f.ClientSet, pod)
  856. framework.ExpectNoError(err, "while deleting hostPath teardown pod")
  857. }
  858. // emptydir
  859. type emptydirDriver struct {
  860. driverInfo testsuites.DriverInfo
  861. }
  862. var _ testsuites.TestDriver = &emptydirDriver{}
  863. var _ testsuites.PreprovisionedVolumeTestDriver = &emptydirDriver{}
  864. var _ testsuites.InlineVolumeTestDriver = &emptydirDriver{}
  865. // InitEmptydirDriver returns emptydirDriver that implements TestDriver interface
  866. func InitEmptydirDriver() testsuites.TestDriver {
  867. return &emptydirDriver{
  868. driverInfo: testsuites.DriverInfo{
  869. Name: "emptydir",
  870. InTreePluginName: "kubernetes.io/empty-dir",
  871. MaxFileSize: testpatterns.FileSizeMedium,
  872. SupportedFsType: sets.NewString(
  873. "", // Default fsType
  874. ),
  875. Capabilities: map[testsuites.Capability]bool{
  876. testsuites.CapExec: true,
  877. testsuites.CapSingleNodeVolume: true,
  878. },
  879. },
  880. }
  881. }
  882. func (e *emptydirDriver) GetDriverInfo() *testsuites.DriverInfo {
  883. return &e.driverInfo
  884. }
  885. func (e *emptydirDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  886. }
  887. func (e *emptydirDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  888. // emptydir doesn't support readOnly volume
  889. if readOnly {
  890. return nil
  891. }
  892. return &v1.VolumeSource{
  893. EmptyDir: &v1.EmptyDirVolumeSource{},
  894. }
  895. }
  896. func (e *emptydirDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  897. return nil
  898. }
  899. func (e *emptydirDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  900. return &testsuites.PerTestConfig{
  901. Driver: e,
  902. Prefix: "emptydir",
  903. Framework: f,
  904. }, func() {}
  905. }
  906. // Cinder
  907. // This driver assumes that OpenStack client tools are installed
  908. // (/usr/bin/nova, /usr/bin/cinder and /usr/bin/keystone)
  909. // and that the usual OpenStack authentication env. variables are set
  910. // (OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME at least).
  911. type cinderDriver struct {
  912. driverInfo testsuites.DriverInfo
  913. }
  914. type cinderVolume struct {
  915. volumeName string
  916. volumeID string
  917. }
  918. var _ testsuites.TestDriver = &cinderDriver{}
  919. var _ testsuites.PreprovisionedVolumeTestDriver = &cinderDriver{}
  920. var _ testsuites.InlineVolumeTestDriver = &cinderDriver{}
  921. var _ testsuites.PreprovisionedPVTestDriver = &cinderDriver{}
  922. var _ testsuites.DynamicPVTestDriver = &cinderDriver{}
  923. // InitCinderDriver returns cinderDriver that implements TestDriver interface
  924. func InitCinderDriver() testsuites.TestDriver {
  925. return &cinderDriver{
  926. driverInfo: testsuites.DriverInfo{
  927. Name: "cinder",
  928. InTreePluginName: "kubernetes.io/cinder",
  929. MaxFileSize: testpatterns.FileSizeMedium,
  930. SupportedSizeRange: volume.SizeRange{
  931. Min: "5Gi",
  932. },
  933. SupportedFsType: sets.NewString(
  934. "", // Default fsType
  935. "ext3",
  936. ),
  937. TopologyKeys: []string{v1.LabelZoneFailureDomain},
  938. Capabilities: map[testsuites.Capability]bool{
  939. testsuites.CapPersistence: true,
  940. testsuites.CapFsGroup: true,
  941. testsuites.CapExec: true,
  942. testsuites.CapBlock: true,
  943. // Cinder supports volume limits, but the test creates large
  944. // number of volumes and times out test suites.
  945. testsuites.CapVolumeLimits: false,
  946. testsuites.CapTopology: true,
  947. },
  948. },
  949. }
  950. }
  951. func (c *cinderDriver) GetDriverInfo() *testsuites.DriverInfo {
  952. return &c.driverInfo
  953. }
  954. func (c *cinderDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  955. e2eskipper.SkipUnlessProviderIs("openstack")
  956. }
  957. func (c *cinderDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  958. cv, ok := volume.(*cinderVolume)
  959. framework.ExpectEqual(ok, true, "Failed to cast test volume to Cinder test volume")
  960. volSource := v1.VolumeSource{
  961. Cinder: &v1.CinderVolumeSource{
  962. VolumeID: cv.volumeID,
  963. ReadOnly: readOnly,
  964. },
  965. }
  966. if fsType != "" {
  967. volSource.Cinder.FSType = fsType
  968. }
  969. return &volSource
  970. }
  971. func (c *cinderDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  972. cv, ok := volume.(*cinderVolume)
  973. framework.ExpectEqual(ok, true, "Failed to cast test volume to Cinder test volume")
  974. pvSource := v1.PersistentVolumeSource{
  975. Cinder: &v1.CinderPersistentVolumeSource{
  976. VolumeID: cv.volumeID,
  977. ReadOnly: readOnly,
  978. },
  979. }
  980. if fsType != "" {
  981. pvSource.Cinder.FSType = fsType
  982. }
  983. return &pvSource, nil
  984. }
  985. func (c *cinderDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  986. provisioner := "kubernetes.io/cinder"
  987. parameters := map[string]string{}
  988. if fsType != "" {
  989. parameters["fsType"] = fsType
  990. }
  991. ns := config.Framework.Namespace.Name
  992. suffix := fmt.Sprintf("%s-sc", c.driverInfo.Name)
  993. return testsuites.GetStorageClass(provisioner, parameters, nil, ns, suffix)
  994. }
  995. func (c *cinderDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  996. return &testsuites.PerTestConfig{
  997. Driver: c,
  998. Prefix: "cinder",
  999. Framework: f,
  1000. }, func() {}
  1001. }
  1002. func (c *cinderDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  1003. f := config.Framework
  1004. ns := f.Namespace
  1005. // We assume that namespace.Name is a random string
  1006. volumeName := ns.Name
  1007. ginkgo.By("creating a test Cinder volume")
  1008. output, err := exec.Command("cinder", "create", "--display-name="+volumeName, "1").CombinedOutput()
  1009. outputString := string(output[:])
  1010. framework.Logf("cinder output:\n%s", outputString)
  1011. framework.ExpectNoError(err)
  1012. // Parse 'id'' from stdout. Expected format:
  1013. // | attachments | [] |
  1014. // | availability_zone | nova |
  1015. // ...
  1016. // | id | 1d6ff08f-5d1c-41a4-ad72-4ef872cae685 |
  1017. volumeID := ""
  1018. for _, line := range strings.Split(outputString, "\n") {
  1019. fields := strings.Fields(line)
  1020. if len(fields) != 5 {
  1021. continue
  1022. }
  1023. if fields[1] != "id" {
  1024. continue
  1025. }
  1026. volumeID = fields[3]
  1027. break
  1028. }
  1029. framework.Logf("Volume ID: %s", volumeID)
  1030. framework.ExpectNotEqual(volumeID, "")
  1031. return &cinderVolume{
  1032. volumeName: volumeName,
  1033. volumeID: volumeID,
  1034. }
  1035. }
  1036. func (v *cinderVolume) DeleteVolume() {
  1037. name := v.volumeName
  1038. // Try to delete the volume for several seconds - it takes
  1039. // a while for the plugin to detach it.
  1040. var output []byte
  1041. var err error
  1042. timeout := time.Second * 120
  1043. framework.Logf("Waiting up to %v for removal of cinder volume %s", timeout, name)
  1044. for start := time.Now(); time.Since(start) < timeout; time.Sleep(5 * time.Second) {
  1045. output, err = exec.Command("cinder", "delete", name).CombinedOutput()
  1046. if err == nil {
  1047. framework.Logf("Cinder volume %s deleted", name)
  1048. return
  1049. }
  1050. framework.Logf("Failed to delete volume %s: %v", name, err)
  1051. }
  1052. framework.Logf("Giving up deleting volume %s: %v\n%s", name, err, string(output[:]))
  1053. }
  1054. // GCE
  1055. type gcePdDriver struct {
  1056. driverInfo testsuites.DriverInfo
  1057. }
  1058. type gcePdVolume struct {
  1059. volumeName string
  1060. }
  1061. var _ testsuites.TestDriver = &gcePdDriver{}
  1062. var _ testsuites.PreprovisionedVolumeTestDriver = &gcePdDriver{}
  1063. var _ testsuites.InlineVolumeTestDriver = &gcePdDriver{}
  1064. var _ testsuites.PreprovisionedPVTestDriver = &gcePdDriver{}
  1065. var _ testsuites.DynamicPVTestDriver = &gcePdDriver{}
  1066. // InitGcePdDriver returns gcePdDriver that implements TestDriver interface
  1067. func InitGcePdDriver() testsuites.TestDriver {
  1068. // In current test structure, it first initialize the driver and then set up
  1069. // the new framework, so we cannot get the correct OS here. So here set to
  1070. // support all fs types including both linux and windows. We have code to check Node OS later
  1071. // during test.
  1072. supportedTypes := sets.NewString(
  1073. "", // Default fsType
  1074. "ext2",
  1075. "ext3",
  1076. "ext4",
  1077. "xfs",
  1078. "ntfs",
  1079. )
  1080. return &gcePdDriver{
  1081. driverInfo: testsuites.DriverInfo{
  1082. Name: "gcepd",
  1083. InTreePluginName: "kubernetes.io/gce-pd",
  1084. MaxFileSize: testpatterns.FileSizeMedium,
  1085. SupportedSizeRange: volume.SizeRange{
  1086. Min: "5Gi",
  1087. },
  1088. SupportedFsType: supportedTypes,
  1089. SupportedMountOption: sets.NewString("debug", "nouid32"),
  1090. TopologyKeys: []string{v1.LabelZoneFailureDomain},
  1091. Capabilities: map[testsuites.Capability]bool{
  1092. testsuites.CapPersistence: true,
  1093. testsuites.CapFsGroup: true,
  1094. testsuites.CapBlock: true,
  1095. testsuites.CapExec: true,
  1096. testsuites.CapMultiPODs: true,
  1097. testsuites.CapControllerExpansion: true,
  1098. testsuites.CapNodeExpansion: true,
  1099. // GCE supports volume limits, but the test creates large
  1100. // number of volumes and times out test suites.
  1101. testsuites.CapVolumeLimits: false,
  1102. testsuites.CapTopology: true,
  1103. },
  1104. },
  1105. }
  1106. }
  1107. func (g *gcePdDriver) GetDriverInfo() *testsuites.DriverInfo {
  1108. return &g.driverInfo
  1109. }
  1110. func (g *gcePdDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  1111. e2eskipper.SkipUnlessProviderIs("gce", "gke")
  1112. if pattern.FeatureTag == "[sig-windows]" {
  1113. e2eskipper.SkipUnlessNodeOSDistroIs("windows")
  1114. }
  1115. }
  1116. func (g *gcePdDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  1117. gv, ok := volume.(*gcePdVolume)
  1118. framework.ExpectEqual(ok, true, "Failed to cast test volume to GCE PD test volume")
  1119. volSource := v1.VolumeSource{
  1120. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  1121. PDName: gv.volumeName,
  1122. ReadOnly: readOnly,
  1123. },
  1124. }
  1125. if fsType != "" {
  1126. volSource.GCEPersistentDisk.FSType = fsType
  1127. }
  1128. return &volSource
  1129. }
  1130. func (g *gcePdDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  1131. gv, ok := volume.(*gcePdVolume)
  1132. framework.ExpectEqual(ok, true, "Failed to cast test volume to GCE PD test volume")
  1133. pvSource := v1.PersistentVolumeSource{
  1134. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  1135. PDName: gv.volumeName,
  1136. ReadOnly: readOnly,
  1137. },
  1138. }
  1139. if fsType != "" {
  1140. pvSource.GCEPersistentDisk.FSType = fsType
  1141. }
  1142. return &pvSource, nil
  1143. }
  1144. func (g *gcePdDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  1145. provisioner := "kubernetes.io/gce-pd"
  1146. parameters := map[string]string{}
  1147. if fsType != "" {
  1148. parameters["fsType"] = fsType
  1149. }
  1150. ns := config.Framework.Namespace.Name
  1151. suffix := fmt.Sprintf("%s-sc", g.driverInfo.Name)
  1152. delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer
  1153. return testsuites.GetStorageClass(provisioner, parameters, &delayedBinding, ns, suffix)
  1154. }
  1155. func (g *gcePdDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  1156. config := &testsuites.PerTestConfig{
  1157. Driver: g,
  1158. Prefix: "gcepd",
  1159. Framework: f,
  1160. }
  1161. if framework.NodeOSDistroIs("windows") {
  1162. config.ClientNodeSelection = e2epod.NodeSelection{
  1163. Selector: map[string]string{
  1164. "kubernetes.io/os": "windows",
  1165. },
  1166. }
  1167. }
  1168. return config, func() {}
  1169. }
  1170. func (g *gcePdDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  1171. if volType == testpatterns.InlineVolume {
  1172. // PD will be created in framework.TestContext.CloudConfig.Zone zone,
  1173. // so pods should be also scheduled there.
  1174. config.ClientNodeSelection = e2epod.NodeSelection{
  1175. Selector: map[string]string{
  1176. v1.LabelZoneFailureDomain: framework.TestContext.CloudConfig.Zone,
  1177. },
  1178. }
  1179. }
  1180. ginkgo.By("creating a test gce pd volume")
  1181. vname, err := e2epv.CreatePDWithRetry()
  1182. framework.ExpectNoError(err)
  1183. return &gcePdVolume{
  1184. volumeName: vname,
  1185. }
  1186. }
  1187. func (v *gcePdVolume) DeleteVolume() {
  1188. e2epv.DeletePDWithRetry(v.volumeName)
  1189. }
  1190. // vSphere
  1191. type vSphereDriver struct {
  1192. driverInfo testsuites.DriverInfo
  1193. }
  1194. type vSphereVolume struct {
  1195. volumePath string
  1196. nodeInfo *vspheretest.NodeInfo
  1197. }
  1198. var _ testsuites.TestDriver = &vSphereDriver{}
  1199. var _ testsuites.PreprovisionedVolumeTestDriver = &vSphereDriver{}
  1200. var _ testsuites.InlineVolumeTestDriver = &vSphereDriver{}
  1201. var _ testsuites.PreprovisionedPVTestDriver = &vSphereDriver{}
  1202. var _ testsuites.DynamicPVTestDriver = &vSphereDriver{}
  1203. // InitVSphereDriver returns vSphereDriver that implements TestDriver interface
  1204. func InitVSphereDriver() testsuites.TestDriver {
  1205. return &vSphereDriver{
  1206. driverInfo: testsuites.DriverInfo{
  1207. Name: "vsphere",
  1208. InTreePluginName: "kubernetes.io/vsphere-volume",
  1209. MaxFileSize: testpatterns.FileSizeMedium,
  1210. SupportedSizeRange: volume.SizeRange{
  1211. Min: "5Gi",
  1212. },
  1213. SupportedFsType: sets.NewString(
  1214. "", // Default fsType
  1215. "ext4",
  1216. ),
  1217. TopologyKeys: []string{v1.LabelZoneFailureDomain},
  1218. Capabilities: map[testsuites.Capability]bool{
  1219. testsuites.CapPersistence: true,
  1220. testsuites.CapFsGroup: true,
  1221. testsuites.CapExec: true,
  1222. testsuites.CapMultiPODs: true,
  1223. testsuites.CapTopology: true,
  1224. },
  1225. },
  1226. }
  1227. }
  1228. func (v *vSphereDriver) GetDriverInfo() *testsuites.DriverInfo {
  1229. return &v.driverInfo
  1230. }
  1231. func (v *vSphereDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  1232. e2eskipper.SkipUnlessProviderIs("vsphere")
  1233. }
  1234. func (v *vSphereDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  1235. vsv, ok := volume.(*vSphereVolume)
  1236. framework.ExpectEqual(ok, true, "Failed to cast test volume to vSphere test volume")
  1237. // vSphere driver doesn't seem to support readOnly volume
  1238. // TODO: check if it is correct
  1239. if readOnly {
  1240. return nil
  1241. }
  1242. volSource := v1.VolumeSource{
  1243. VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
  1244. VolumePath: vsv.volumePath,
  1245. },
  1246. }
  1247. if fsType != "" {
  1248. volSource.VsphereVolume.FSType = fsType
  1249. }
  1250. return &volSource
  1251. }
  1252. func (v *vSphereDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  1253. vsv, ok := volume.(*vSphereVolume)
  1254. framework.ExpectEqual(ok, true, "Failed to cast test volume to vSphere test volume")
  1255. // vSphere driver doesn't seem to support readOnly volume
  1256. // TODO: check if it is correct
  1257. if readOnly {
  1258. return nil, nil
  1259. }
  1260. pvSource := v1.PersistentVolumeSource{
  1261. VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
  1262. VolumePath: vsv.volumePath,
  1263. },
  1264. }
  1265. if fsType != "" {
  1266. pvSource.VsphereVolume.FSType = fsType
  1267. }
  1268. return &pvSource, nil
  1269. }
  1270. func (v *vSphereDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  1271. provisioner := "kubernetes.io/vsphere-volume"
  1272. parameters := map[string]string{}
  1273. if fsType != "" {
  1274. parameters["fsType"] = fsType
  1275. }
  1276. ns := config.Framework.Namespace.Name
  1277. suffix := fmt.Sprintf("%s-sc", v.driverInfo.Name)
  1278. return testsuites.GetStorageClass(provisioner, parameters, nil, ns, suffix)
  1279. }
  1280. func (v *vSphereDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  1281. return &testsuites.PerTestConfig{
  1282. Driver: v,
  1283. Prefix: "vsphere",
  1284. Framework: f,
  1285. }, func() {}
  1286. }
  1287. func (v *vSphereDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  1288. f := config.Framework
  1289. vspheretest.Bootstrap(f)
  1290. nodeInfo := vspheretest.GetReadySchedulableRandomNodeInfo()
  1291. volumePath, err := nodeInfo.VSphere.CreateVolume(&vspheretest.VolumeOptions{}, nodeInfo.DataCenterRef)
  1292. framework.ExpectNoError(err)
  1293. return &vSphereVolume{
  1294. volumePath: volumePath,
  1295. nodeInfo: nodeInfo,
  1296. }
  1297. }
  1298. func (v *vSphereVolume) DeleteVolume() {
  1299. v.nodeInfo.VSphere.DeleteVolume(v.volumePath, v.nodeInfo.DataCenterRef)
  1300. }
  1301. // Azure Disk
  1302. type azureDiskDriver struct {
  1303. driverInfo testsuites.DriverInfo
  1304. }
  1305. type azureDiskVolume struct {
  1306. volumeName string
  1307. }
  1308. var _ testsuites.TestDriver = &azureDiskDriver{}
  1309. var _ testsuites.PreprovisionedVolumeTestDriver = &azureDiskDriver{}
  1310. var _ testsuites.InlineVolumeTestDriver = &azureDiskDriver{}
  1311. var _ testsuites.PreprovisionedPVTestDriver = &azureDiskDriver{}
  1312. var _ testsuites.DynamicPVTestDriver = &azureDiskDriver{}
  1313. // InitAzureDiskDriver returns azureDiskDriver that implements TestDriver interface
  1314. func InitAzureDiskDriver() testsuites.TestDriver {
  1315. return &azureDiskDriver{
  1316. driverInfo: testsuites.DriverInfo{
  1317. Name: "azure-disk",
  1318. InTreePluginName: "kubernetes.io/azure-disk",
  1319. MaxFileSize: testpatterns.FileSizeMedium,
  1320. SupportedSizeRange: volume.SizeRange{
  1321. Min: "5Gi",
  1322. },
  1323. SupportedFsType: sets.NewString(
  1324. "", // Default fsType
  1325. "ext3",
  1326. "ext4",
  1327. "xfs",
  1328. ),
  1329. TopologyKeys: []string{v1.LabelZoneFailureDomain},
  1330. Capabilities: map[testsuites.Capability]bool{
  1331. testsuites.CapPersistence: true,
  1332. testsuites.CapFsGroup: true,
  1333. testsuites.CapBlock: true,
  1334. testsuites.CapExec: true,
  1335. testsuites.CapMultiPODs: true,
  1336. // Azure supports volume limits, but the test creates large
  1337. // number of volumes and times out test suites.
  1338. testsuites.CapVolumeLimits: false,
  1339. testsuites.CapTopology: true,
  1340. },
  1341. },
  1342. }
  1343. }
  1344. func (a *azureDiskDriver) GetDriverInfo() *testsuites.DriverInfo {
  1345. return &a.driverInfo
  1346. }
  1347. func (a *azureDiskDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  1348. e2eskipper.SkipUnlessProviderIs("azure")
  1349. }
  1350. func (a *azureDiskDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  1351. av, ok := volume.(*azureDiskVolume)
  1352. framework.ExpectEqual(ok, true, "Failed to cast test volume to Azure test volume")
  1353. diskName := av.volumeName[(strings.LastIndex(av.volumeName, "/") + 1):]
  1354. kind := v1.AzureManagedDisk
  1355. volSource := v1.VolumeSource{
  1356. AzureDisk: &v1.AzureDiskVolumeSource{
  1357. DiskName: diskName,
  1358. DataDiskURI: av.volumeName,
  1359. Kind: &kind,
  1360. ReadOnly: &readOnly,
  1361. },
  1362. }
  1363. if fsType != "" {
  1364. volSource.AzureDisk.FSType = &fsType
  1365. }
  1366. return &volSource
  1367. }
  1368. func (a *azureDiskDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  1369. av, ok := volume.(*azureDiskVolume)
  1370. framework.ExpectEqual(ok, true, "Failed to cast test volume to Azure test volume")
  1371. diskName := av.volumeName[(strings.LastIndex(av.volumeName, "/") + 1):]
  1372. kind := v1.AzureManagedDisk
  1373. pvSource := v1.PersistentVolumeSource{
  1374. AzureDisk: &v1.AzureDiskVolumeSource{
  1375. DiskName: diskName,
  1376. DataDiskURI: av.volumeName,
  1377. Kind: &kind,
  1378. ReadOnly: &readOnly,
  1379. },
  1380. }
  1381. if fsType != "" {
  1382. pvSource.AzureDisk.FSType = &fsType
  1383. }
  1384. return &pvSource, nil
  1385. }
  1386. func (a *azureDiskDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  1387. provisioner := "kubernetes.io/azure-disk"
  1388. parameters := map[string]string{}
  1389. if fsType != "" {
  1390. parameters["fsType"] = fsType
  1391. }
  1392. ns := config.Framework.Namespace.Name
  1393. suffix := fmt.Sprintf("%s-sc", a.driverInfo.Name)
  1394. return testsuites.GetStorageClass(provisioner, parameters, nil, ns, suffix)
  1395. }
  1396. func (a *azureDiskDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  1397. return &testsuites.PerTestConfig{
  1398. Driver: a,
  1399. Prefix: "azure",
  1400. Framework: f,
  1401. }, func() {}
  1402. }
  1403. func (a *azureDiskDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  1404. ginkgo.By("creating a test azure disk volume")
  1405. volumeName, err := e2epv.CreatePDWithRetry()
  1406. framework.ExpectNoError(err)
  1407. return &azureDiskVolume{
  1408. volumeName: volumeName,
  1409. }
  1410. }
  1411. func (v *azureDiskVolume) DeleteVolume() {
  1412. e2epv.DeletePDWithRetry(v.volumeName)
  1413. }
  1414. // AWS
  1415. type awsDriver struct {
  1416. driverInfo testsuites.DriverInfo
  1417. }
  1418. type awsVolume struct {
  1419. volumeName string
  1420. }
  1421. var _ testsuites.TestDriver = &awsDriver{}
  1422. var _ testsuites.PreprovisionedVolumeTestDriver = &awsDriver{}
  1423. var _ testsuites.InlineVolumeTestDriver = &awsDriver{}
  1424. var _ testsuites.PreprovisionedPVTestDriver = &awsDriver{}
  1425. var _ testsuites.DynamicPVTestDriver = &awsDriver{}
  1426. // InitAwsDriver returns awsDriver that implements TestDriver interface
  1427. func InitAwsDriver() testsuites.TestDriver {
  1428. return &awsDriver{
  1429. driverInfo: testsuites.DriverInfo{
  1430. Name: "aws",
  1431. InTreePluginName: "kubernetes.io/aws-ebs",
  1432. MaxFileSize: testpatterns.FileSizeMedium,
  1433. SupportedSizeRange: volume.SizeRange{
  1434. Min: "5Gi",
  1435. },
  1436. SupportedFsType: sets.NewString(
  1437. "", // Default fsType
  1438. "ext2",
  1439. "ext3",
  1440. "ext4",
  1441. "xfs",
  1442. "ntfs",
  1443. ),
  1444. SupportedMountOption: sets.NewString("debug", "nouid32"),
  1445. TopologyKeys: []string{v1.LabelZoneFailureDomain},
  1446. Capabilities: map[testsuites.Capability]bool{
  1447. testsuites.CapPersistence: true,
  1448. testsuites.CapFsGroup: true,
  1449. testsuites.CapBlock: true,
  1450. testsuites.CapExec: true,
  1451. testsuites.CapMultiPODs: true,
  1452. testsuites.CapControllerExpansion: true,
  1453. testsuites.CapNodeExpansion: true,
  1454. // AWS supports volume limits, but the test creates large
  1455. // number of volumes and times out test suites.
  1456. testsuites.CapVolumeLimits: false,
  1457. testsuites.CapTopology: true,
  1458. },
  1459. },
  1460. }
  1461. }
  1462. func (a *awsDriver) GetDriverInfo() *testsuites.DriverInfo {
  1463. return &a.driverInfo
  1464. }
  1465. func (a *awsDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  1466. e2eskipper.SkipUnlessProviderIs("aws")
  1467. }
  1468. func (a *awsDriver) GetVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) *v1.VolumeSource {
  1469. av, ok := volume.(*awsVolume)
  1470. framework.ExpectEqual(ok, true, "Failed to cast test volume to AWS test volume")
  1471. volSource := v1.VolumeSource{
  1472. AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
  1473. VolumeID: av.volumeName,
  1474. ReadOnly: readOnly,
  1475. },
  1476. }
  1477. if fsType != "" {
  1478. volSource.AWSElasticBlockStore.FSType = fsType
  1479. }
  1480. return &volSource
  1481. }
  1482. func (a *awsDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  1483. av, ok := volume.(*awsVolume)
  1484. framework.ExpectEqual(ok, true, "Failed to cast test volume to AWS test volume")
  1485. pvSource := v1.PersistentVolumeSource{
  1486. AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
  1487. VolumeID: av.volumeName,
  1488. ReadOnly: readOnly,
  1489. },
  1490. }
  1491. if fsType != "" {
  1492. pvSource.AWSElasticBlockStore.FSType = fsType
  1493. }
  1494. return &pvSource, nil
  1495. }
  1496. func (a *awsDriver) GetDynamicProvisionStorageClass(config *testsuites.PerTestConfig, fsType string) *storagev1.StorageClass {
  1497. provisioner := "kubernetes.io/aws-ebs"
  1498. parameters := map[string]string{}
  1499. if fsType != "" {
  1500. parameters["fsType"] = fsType
  1501. }
  1502. ns := config.Framework.Namespace.Name
  1503. suffix := fmt.Sprintf("%s-sc", a.driverInfo.Name)
  1504. delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer
  1505. return testsuites.GetStorageClass(provisioner, parameters, &delayedBinding, ns, suffix)
  1506. }
  1507. func (a *awsDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  1508. config := &testsuites.PerTestConfig{
  1509. Driver: a,
  1510. Prefix: "aws",
  1511. Framework: f,
  1512. }
  1513. if framework.NodeOSDistroIs("windows") {
  1514. config.ClientNodeSelection = e2epod.NodeSelection{
  1515. Selector: map[string]string{
  1516. "kubernetes.io/os": "windows",
  1517. },
  1518. }
  1519. }
  1520. return config, func() {}
  1521. }
  1522. func (a *awsDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  1523. if volType == testpatterns.InlineVolume {
  1524. // PD will be created in framework.TestContext.CloudConfig.Zone zone,
  1525. // so pods should be also scheduled there.
  1526. config.ClientNodeSelection = e2epod.NodeSelection{
  1527. Selector: map[string]string{
  1528. v1.LabelZoneFailureDomain: framework.TestContext.CloudConfig.Zone,
  1529. },
  1530. }
  1531. }
  1532. ginkgo.By("creating a test aws volume")
  1533. vname, err := e2epv.CreatePDWithRetry()
  1534. framework.ExpectNoError(err)
  1535. return &awsVolume{
  1536. volumeName: vname,
  1537. }
  1538. }
  1539. func (v *awsVolume) DeleteVolume() {
  1540. e2epv.DeletePDWithRetry(v.volumeName)
  1541. }
  1542. // local
  1543. type localDriver struct {
  1544. driverInfo testsuites.DriverInfo
  1545. node *v1.Node
  1546. hostExec utils.HostExec
  1547. // volumeType represents local volume type we are testing, e.g. tmpfs,
  1548. // directory, block device.
  1549. volumeType utils.LocalVolumeType
  1550. ltrMgr utils.LocalTestResourceManager
  1551. }
  1552. type localVolume struct {
  1553. ltrMgr utils.LocalTestResourceManager
  1554. ltr *utils.LocalTestResource
  1555. }
  1556. var (
  1557. // capabilities
  1558. defaultLocalVolumeCapabilities = map[testsuites.Capability]bool{
  1559. testsuites.CapPersistence: true,
  1560. testsuites.CapFsGroup: true,
  1561. testsuites.CapBlock: false,
  1562. testsuites.CapExec: true,
  1563. testsuites.CapMultiPODs: true,
  1564. testsuites.CapSingleNodeVolume: true,
  1565. }
  1566. localVolumeCapabitilies = map[utils.LocalVolumeType]map[testsuites.Capability]bool{
  1567. utils.LocalVolumeBlock: {
  1568. testsuites.CapPersistence: true,
  1569. testsuites.CapFsGroup: true,
  1570. testsuites.CapBlock: true,
  1571. testsuites.CapExec: true,
  1572. testsuites.CapMultiPODs: true,
  1573. testsuites.CapSingleNodeVolume: true,
  1574. },
  1575. }
  1576. // fstype
  1577. defaultLocalVolumeSupportedFsTypes = sets.NewString("")
  1578. localVolumeSupportedFsTypes = map[utils.LocalVolumeType]sets.String{
  1579. utils.LocalVolumeBlock: sets.NewString(
  1580. "", // Default fsType
  1581. "ext2",
  1582. "ext3",
  1583. "ext4",
  1584. //"xfs", disabled see issue https://github.com/kubernetes/kubernetes/issues/74095
  1585. ),
  1586. }
  1587. // max file size
  1588. defaultLocalVolumeMaxFileSize = testpatterns.FileSizeSmall
  1589. localVolumeMaxFileSizes = map[utils.LocalVolumeType]int64{}
  1590. )
  1591. var _ testsuites.TestDriver = &localDriver{}
  1592. var _ testsuites.PreprovisionedVolumeTestDriver = &localDriver{}
  1593. var _ testsuites.PreprovisionedPVTestDriver = &localDriver{}
  1594. // InitLocalDriverWithVolumeType initializes the local driver based on the volume type.
  1595. func InitLocalDriverWithVolumeType(volumeType utils.LocalVolumeType) func() testsuites.TestDriver {
  1596. maxFileSize := defaultLocalVolumeMaxFileSize
  1597. if maxFileSizeByVolType, ok := localVolumeMaxFileSizes[volumeType]; ok {
  1598. maxFileSize = maxFileSizeByVolType
  1599. }
  1600. supportedFsTypes := defaultLocalVolumeSupportedFsTypes
  1601. if supportedFsTypesByType, ok := localVolumeSupportedFsTypes[volumeType]; ok {
  1602. supportedFsTypes = supportedFsTypesByType
  1603. }
  1604. capabilities := defaultLocalVolumeCapabilities
  1605. if capabilitiesByType, ok := localVolumeCapabitilies[volumeType]; ok {
  1606. capabilities = capabilitiesByType
  1607. }
  1608. return func() testsuites.TestDriver {
  1609. // custom tag to distinguish from tests of other volume types
  1610. featureTag := fmt.Sprintf("[LocalVolumeType: %s]", volumeType)
  1611. // For GCE Local SSD volumes, we must run serially
  1612. if volumeType == utils.LocalVolumeGCELocalSSD {
  1613. featureTag += " [Serial]"
  1614. }
  1615. return &localDriver{
  1616. driverInfo: testsuites.DriverInfo{
  1617. Name: "local",
  1618. InTreePluginName: "kubernetes.io/local-volume",
  1619. FeatureTag: featureTag,
  1620. MaxFileSize: maxFileSize,
  1621. SupportedFsType: supportedFsTypes,
  1622. Capabilities: capabilities,
  1623. },
  1624. volumeType: volumeType,
  1625. }
  1626. }
  1627. }
  1628. func (l *localDriver) GetDriverInfo() *testsuites.DriverInfo {
  1629. return &l.driverInfo
  1630. }
  1631. func (l *localDriver) SkipUnsupportedTest(pattern testpatterns.TestPattern) {
  1632. }
  1633. func (l *localDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
  1634. var err error
  1635. l.node, err = e2enode.GetRandomReadySchedulableNode(f.ClientSet)
  1636. framework.ExpectNoError(err)
  1637. l.hostExec = utils.NewHostExec(f)
  1638. l.ltrMgr = utils.NewLocalResourceManager("local-driver", l.hostExec, "/tmp")
  1639. // This can't be done in SkipUnsupportedTest because the test framework is not initialized yet
  1640. if l.volumeType == utils.LocalVolumeGCELocalSSD {
  1641. ssdInterface := "scsi"
  1642. filesystemType := "fs"
  1643. ssdCmd := fmt.Sprintf("ls -1 /mnt/disks/by-uuid/google-local-ssds-%s-%s/ | wc -l", ssdInterface, filesystemType)
  1644. res, err := l.hostExec.IssueCommandWithResult(ssdCmd, l.node)
  1645. framework.ExpectNoError(err)
  1646. num, err := strconv.Atoi(strings.TrimSpace(res))
  1647. framework.ExpectNoError(err)
  1648. if num < 1 {
  1649. e2eskipper.Skipf("Requires at least 1 %s %s localSSD ", ssdInterface, filesystemType)
  1650. }
  1651. }
  1652. return &testsuites.PerTestConfig{
  1653. Driver: l,
  1654. Prefix: "local",
  1655. Framework: f,
  1656. ClientNodeSelection: e2epod.NodeSelection{Name: l.node.Name},
  1657. }, func() {
  1658. l.hostExec.Cleanup()
  1659. }
  1660. }
  1661. func (l *localDriver) CreateVolume(config *testsuites.PerTestConfig, volType testpatterns.TestVolType) testsuites.TestVolume {
  1662. switch volType {
  1663. case testpatterns.PreprovisionedPV:
  1664. node := l.node
  1665. // assign this to schedule pod on this node
  1666. config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name}
  1667. return &localVolume{
  1668. ltrMgr: l.ltrMgr,
  1669. ltr: l.ltrMgr.Create(node, l.volumeType, nil),
  1670. }
  1671. default:
  1672. framework.Failf("Unsupported volType: %v is specified", volType)
  1673. }
  1674. return nil
  1675. }
  1676. func (v *localVolume) DeleteVolume() {
  1677. v.ltrMgr.Remove(v.ltr)
  1678. }
  1679. func (l *localDriver) nodeAffinityForNode(node *v1.Node) *v1.VolumeNodeAffinity {
  1680. nodeKey := "kubernetes.io/hostname"
  1681. if node.Labels == nil {
  1682. framework.Failf("Node does not have labels")
  1683. }
  1684. nodeValue, found := node.Labels[nodeKey]
  1685. if !found {
  1686. framework.Failf("Node does not have required label %q", nodeKey)
  1687. }
  1688. return &v1.VolumeNodeAffinity{
  1689. Required: &v1.NodeSelector{
  1690. NodeSelectorTerms: []v1.NodeSelectorTerm{
  1691. {
  1692. MatchExpressions: []v1.NodeSelectorRequirement{
  1693. {
  1694. Key: nodeKey,
  1695. Operator: v1.NodeSelectorOpIn,
  1696. Values: []string{nodeValue},
  1697. },
  1698. },
  1699. },
  1700. },
  1701. },
  1702. }
  1703. }
  1704. func (l *localDriver) GetPersistentVolumeSource(readOnly bool, fsType string, volume testsuites.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  1705. lv, ok := volume.(*localVolume)
  1706. framework.ExpectEqual(ok, true, "Failed to cast test volume to local test volume")
  1707. return &v1.PersistentVolumeSource{
  1708. Local: &v1.LocalVolumeSource{
  1709. Path: lv.ltr.Path,
  1710. FSType: &fsType,
  1711. },
  1712. }, l.nodeAffinityForNode(lv.ltr.Node)
  1713. }
  1714. // cleanUpVolumeServer is a wrapper of cleanup function for volume server without secret created by specific CreateStorageServer function.
  1715. func cleanUpVolumeServer(f *framework.Framework, serverPod *v1.Pod) {
  1716. cleanUpVolumeServerWithSecret(f, serverPod, nil)
  1717. }
  1718. // cleanUpVolumeServerWithSecret is a wrapper of cleanup function for volume server with secret created by specific CreateStorageServer function.
  1719. func cleanUpVolumeServerWithSecret(f *framework.Framework, serverPod *v1.Pod, secret *v1.Secret) {
  1720. cs := f.ClientSet
  1721. ns := f.Namespace
  1722. if secret != nil {
  1723. framework.Logf("Deleting server secret %q...", secret.Name)
  1724. err := cs.CoreV1().Secrets(ns.Name).Delete(context.TODO(), secret.Name, &metav1.DeleteOptions{})
  1725. if err != nil {
  1726. framework.Logf("Delete secret failed: %v", err)
  1727. }
  1728. }
  1729. framework.Logf("Deleting server pod %q...", serverPod.Name)
  1730. err := e2epod.DeletePodWithWait(cs, serverPod)
  1731. if err != nil {
  1732. framework.Logf("Server pod delete failed: %v", err)
  1733. }
  1734. }