csi_block_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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. package csi
  14. import (
  15. "context"
  16. "fmt"
  17. "os"
  18. "path/filepath"
  19. "testing"
  20. api "k8s.io/api/core/v1"
  21. "k8s.io/api/storage/v1beta1"
  22. meta "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. utilfeature "k8s.io/apiserver/pkg/util/feature"
  25. fakeclient "k8s.io/client-go/kubernetes/fake"
  26. featuregatetesting "k8s.io/component-base/featuregate/testing"
  27. "k8s.io/kubernetes/pkg/features"
  28. "k8s.io/kubernetes/pkg/volume"
  29. )
  30. func prepareBlockMapperTest(plug *csiPlugin, specVolumeName string, t *testing.T) (*csiBlockMapper, *volume.Spec, *api.PersistentVolume, error) {
  31. registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
  32. pv := makeTestPV(specVolumeName, 10, testDriver, testVol)
  33. spec := volume.NewSpecFromPersistentVolume(pv, pv.Spec.PersistentVolumeSource.CSI.ReadOnly)
  34. mapper, err := plug.NewBlockVolumeMapper(
  35. spec,
  36. &api.Pod{ObjectMeta: meta.ObjectMeta{UID: testPodUID, Namespace: testns}},
  37. volume.VolumeOptions{},
  38. )
  39. if err != nil {
  40. return nil, nil, nil, fmt.Errorf("Failed to make a new Mapper: %v", err)
  41. }
  42. csiMapper := mapper.(*csiBlockMapper)
  43. return csiMapper, spec, pv, nil
  44. }
  45. func prepareBlockUnmapperTest(plug *csiPlugin, specVolumeName string, t *testing.T) (*csiBlockMapper, *volume.Spec, *api.PersistentVolume, error) {
  46. registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
  47. pv := makeTestPV(specVolumeName, 10, testDriver, testVol)
  48. spec := volume.NewSpecFromPersistentVolume(pv, pv.Spec.PersistentVolumeSource.CSI.ReadOnly)
  49. // save volume data
  50. dir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host)
  51. if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) {
  52. t.Errorf("failed to create dir [%s]: %v", dir, err)
  53. }
  54. if err := saveVolumeData(
  55. dir,
  56. volDataFileName,
  57. map[string]string{
  58. volDataKey.specVolID: pv.ObjectMeta.Name,
  59. volDataKey.driverName: testDriver,
  60. volDataKey.volHandle: testVol,
  61. },
  62. ); err != nil {
  63. t.Fatalf("failed to save volume data: %v", err)
  64. }
  65. unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID)
  66. if err != nil {
  67. t.Fatalf("failed to make a new Unmapper: %v", err)
  68. }
  69. csiUnmapper := unmapper.(*csiBlockMapper)
  70. csiUnmapper.csiClient = setupClient(t, true)
  71. return csiUnmapper, spec, pv, nil
  72. }
  73. func TestBlockMapperGetGlobalMapPath(t *testing.T) {
  74. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  75. plug, tmpDir := newTestPlugin(t, nil)
  76. defer os.RemoveAll(tmpDir)
  77. // TODO (vladimirvivien) specName with slashes will not work
  78. testCases := []struct {
  79. name string
  80. specVolumeName string
  81. path string
  82. }{
  83. {
  84. name: "simple specName",
  85. specVolumeName: "spec-0",
  86. path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/%s/%s", "spec-0", "dev")),
  87. },
  88. {
  89. name: "specName with dots",
  90. specVolumeName: "test.spec.1",
  91. path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/%s/%s", "test.spec.1", "dev")),
  92. },
  93. }
  94. for _, tc := range testCases {
  95. t.Logf("test case: %s", tc.name)
  96. csiMapper, spec, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t)
  97. if err != nil {
  98. t.Fatalf("Failed to make a new Mapper: %v", err)
  99. }
  100. path, err := csiMapper.GetGlobalMapPath(spec)
  101. if err != nil {
  102. t.Errorf("mapper GetGlobalMapPath failed: %v", err)
  103. }
  104. if tc.path != path {
  105. t.Errorf("expecting path %s, got %s", tc.path, path)
  106. }
  107. }
  108. }
  109. func TestBlockMapperGetStagingPath(t *testing.T) {
  110. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  111. plug, tmpDir := newTestPlugin(t, nil)
  112. defer os.RemoveAll(tmpDir)
  113. testCases := []struct {
  114. name string
  115. specVolumeName string
  116. path string
  117. }{
  118. {
  119. name: "simple specName",
  120. specVolumeName: "spec-0",
  121. path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/staging/%s", "spec-0")),
  122. },
  123. {
  124. name: "specName with dots",
  125. specVolumeName: "test.spec.1",
  126. path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/staging/%s", "test.spec.1")),
  127. },
  128. }
  129. for _, tc := range testCases {
  130. t.Logf("test case: %s", tc.name)
  131. csiMapper, _, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t)
  132. if err != nil {
  133. t.Fatalf("Failed to make a new Mapper: %v", err)
  134. }
  135. path := csiMapper.getStagingPath()
  136. if tc.path != path {
  137. t.Errorf("expecting path %s, got %s", tc.path, path)
  138. }
  139. }
  140. }
  141. func TestBlockMapperGetPublishPath(t *testing.T) {
  142. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  143. plug, tmpDir := newTestPlugin(t, nil)
  144. defer os.RemoveAll(tmpDir)
  145. testCases := []struct {
  146. name string
  147. specVolumeName string
  148. path string
  149. }{
  150. {
  151. name: "simple specName",
  152. specVolumeName: "spec-0",
  153. path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/publish/%s/%s", "spec-0", testPodUID)),
  154. },
  155. {
  156. name: "specName with dots",
  157. specVolumeName: "test.spec.1",
  158. path: filepath.Join(tmpDir, fmt.Sprintf("plugins/kubernetes.io/csi/volumeDevices/publish/%s/%s", "test.spec.1", testPodUID)),
  159. },
  160. }
  161. for _, tc := range testCases {
  162. t.Logf("test case: %s", tc.name)
  163. csiMapper, _, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t)
  164. if err != nil {
  165. t.Fatalf("Failed to make a new Mapper: %v", err)
  166. }
  167. path := csiMapper.getPublishPath()
  168. if tc.path != path {
  169. t.Errorf("expecting path %s, got %s", tc.path, path)
  170. }
  171. }
  172. }
  173. func TestBlockMapperGetDeviceMapPath(t *testing.T) {
  174. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  175. plug, tmpDir := newTestPlugin(t, nil)
  176. defer os.RemoveAll(tmpDir)
  177. testCases := []struct {
  178. name string
  179. specVolumeName string
  180. path string
  181. }{
  182. {
  183. name: "simple specName",
  184. specVolumeName: "spec-0",
  185. path: filepath.Join(tmpDir, fmt.Sprintf("pods/%s/volumeDevices/kubernetes.io~csi", testPodUID)),
  186. },
  187. {
  188. name: "specName with dots",
  189. specVolumeName: "test.spec.1",
  190. path: filepath.Join(tmpDir, fmt.Sprintf("pods/%s/volumeDevices/kubernetes.io~csi", testPodUID)),
  191. },
  192. }
  193. for _, tc := range testCases {
  194. t.Logf("test case: %s", tc.name)
  195. csiMapper, _, _, err := prepareBlockMapperTest(plug, tc.specVolumeName, t)
  196. if err != nil {
  197. t.Fatalf("Failed to make a new Mapper: %v", err)
  198. }
  199. path, volName := csiMapper.GetPodDeviceMapPath()
  200. if tc.path != path {
  201. t.Errorf("expecting path %s, got %s", tc.path, path)
  202. }
  203. if tc.specVolumeName != volName {
  204. t.Errorf("expecting volName %s, got %s", tc.specVolumeName, volName)
  205. }
  206. }
  207. }
  208. func TestBlockMapperSetupDevice(t *testing.T) {
  209. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  210. plug, tmpDir := newTestPlugin(t, nil)
  211. defer os.RemoveAll(tmpDir)
  212. csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t)
  213. if err != nil {
  214. t.Fatalf("Failed to make a new Mapper: %v", err)
  215. }
  216. pvName := pv.GetName()
  217. nodeName := string(plug.host.GetNodeName())
  218. csiMapper.csiClient = setupClient(t, true)
  219. attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), string(nodeName))
  220. attachment := makeTestAttachment(attachID, nodeName, pvName)
  221. attachment.Status.Attached = true
  222. _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.TODO(), attachment, metav1.CreateOptions{})
  223. if err != nil {
  224. t.Fatalf("failed to setup VolumeAttachment: %v", err)
  225. }
  226. t.Log("created attachement ", attachID)
  227. err = csiMapper.SetUpDevice()
  228. if err != nil {
  229. t.Fatalf("mapper failed to SetupDevice: %v", err)
  230. }
  231. // Check if NodeStageVolume staged to the right path
  232. stagingPath := csiMapper.getStagingPath()
  233. svols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodeStagedVolumes()
  234. svol, ok := svols[csiMapper.volumeID]
  235. if !ok {
  236. t.Error("csi server may not have received NodeStageVolume call")
  237. }
  238. if svol.Path != stagingPath {
  239. t.Errorf("csi server expected device path %s, got %s", stagingPath, svol.Path)
  240. }
  241. }
  242. func TestBlockMapperMapPodDevice(t *testing.T) {
  243. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  244. plug, tmpDir := newTestPlugin(t, nil)
  245. defer os.RemoveAll(tmpDir)
  246. csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t)
  247. if err != nil {
  248. t.Fatalf("Failed to make a new Mapper: %v", err)
  249. }
  250. pvName := pv.GetName()
  251. nodeName := string(plug.host.GetNodeName())
  252. csiMapper.csiClient = setupClient(t, true)
  253. attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), string(nodeName))
  254. attachment := makeTestAttachment(attachID, nodeName, pvName)
  255. attachment.Status.Attached = true
  256. _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.TODO(), attachment, metav1.CreateOptions{})
  257. if err != nil {
  258. t.Fatalf("failed to setup VolumeAttachment: %v", err)
  259. }
  260. t.Log("created attachement ", attachID)
  261. // Map device to global and pod device map path
  262. path, err := csiMapper.MapPodDevice()
  263. if err != nil {
  264. t.Fatalf("mapper failed to GetGlobalMapPath: %v", err)
  265. }
  266. // Check if NodePublishVolume published to the right path
  267. pvols := csiMapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes()
  268. pvol, ok := pvols[csiMapper.volumeID]
  269. if !ok {
  270. t.Error("csi server may not have received NodePublishVolume call")
  271. }
  272. publishPath := csiMapper.getPublishPath()
  273. if pvol.Path != publishPath {
  274. t.Errorf("csi server expected path %s, got %s", publishPath, pvol.Path)
  275. }
  276. if path != publishPath {
  277. t.Errorf("csi server expected path %s, but MapPodDevice returned %s", publishPath, path)
  278. }
  279. }
  280. func TestBlockMapperMapPodDeviceNotSupportAttach(t *testing.T) {
  281. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  282. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIDriverRegistry, true)()
  283. fakeClient := fakeclient.NewSimpleClientset()
  284. attachRequired := false
  285. fakeDriver := &v1beta1.CSIDriver{
  286. ObjectMeta: meta.ObjectMeta{
  287. Name: testDriver,
  288. },
  289. Spec: v1beta1.CSIDriverSpec{
  290. AttachRequired: &attachRequired,
  291. },
  292. }
  293. _, err := fakeClient.StorageV1beta1().CSIDrivers().Create(context.TODO(), fakeDriver, metav1.CreateOptions{})
  294. if err != nil {
  295. t.Fatalf("Failed to create a fakeDriver: %v", err)
  296. }
  297. // after the driver is created, create the plugin. newTestPlugin waits for the informer to sync,
  298. // such that csiMapper.SetUpDevice below sees the VolumeAttachment object in the lister.
  299. plug, tmpDir := newTestPlugin(t, fakeClient)
  300. defer os.RemoveAll(tmpDir)
  301. csiMapper, _, _, err := prepareBlockMapperTest(plug, "test-pv", t)
  302. if err != nil {
  303. t.Fatalf("Failed to make a new Mapper: %v", err)
  304. }
  305. csiMapper.csiClient = setupClient(t, true)
  306. // Map device to global and pod device map path
  307. path, err := csiMapper.MapPodDevice()
  308. if err != nil {
  309. t.Fatalf("mapper failed to GetGlobalMapPath: %v", err)
  310. }
  311. publishPath := csiMapper.getPublishPath()
  312. if path != publishPath {
  313. t.Errorf("path %s and %s doesn't match", path, publishPath)
  314. }
  315. }
  316. func TestBlockMapperTearDownDevice(t *testing.T) {
  317. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()
  318. plug, tmpDir := newTestPlugin(t, nil)
  319. defer os.RemoveAll(tmpDir)
  320. _, spec, pv, err := prepareBlockMapperTest(plug, "test-pv", t)
  321. if err != nil {
  322. t.Fatalf("Failed to make a new Mapper: %v", err)
  323. }
  324. // save volume data
  325. dir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host)
  326. if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) {
  327. t.Errorf("failed to create dir [%s]: %v", dir, err)
  328. }
  329. if err := saveVolumeData(
  330. dir,
  331. volDataFileName,
  332. map[string]string{
  333. volDataKey.specVolID: pv.ObjectMeta.Name,
  334. volDataKey.driverName: testDriver,
  335. volDataKey.volHandle: testVol,
  336. },
  337. ); err != nil {
  338. t.Fatalf("failed to save volume data: %v", err)
  339. }
  340. unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID)
  341. if err != nil {
  342. t.Fatalf("failed to make a new Unmapper: %v", err)
  343. }
  344. csiUnmapper := unmapper.(*csiBlockMapper)
  345. csiUnmapper.csiClient = setupClient(t, true)
  346. globalMapPath, err := csiUnmapper.GetGlobalMapPath(spec)
  347. if err != nil {
  348. t.Fatalf("unmapper failed to GetGlobalMapPath: %v", err)
  349. }
  350. err = csiUnmapper.TearDownDevice(globalMapPath, "/dev/test")
  351. if err != nil {
  352. t.Fatal(err)
  353. }
  354. // ensure csi client call and node unpblished
  355. pubs := csiUnmapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes()
  356. if _, ok := pubs[csiUnmapper.volumeID]; ok {
  357. t.Error("csi server may not have received NodeUnpublishVolume call")
  358. }
  359. // ensure csi client call and node unstaged
  360. vols := csiUnmapper.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodeStagedVolumes()
  361. if _, ok := vols[csiUnmapper.volumeID]; ok {
  362. t.Error("csi server may not have received NodeUnstageVolume call")
  363. }
  364. }