in_tree.go 52 KB

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