csi_client_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. /*
  2. Copyright 2017 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. "errors"
  17. "io"
  18. "os"
  19. "path/filepath"
  20. "reflect"
  21. "testing"
  22. csipbv1 "github.com/container-storage-interface/spec/lib/go/csi"
  23. api "k8s.io/api/core/v1"
  24. "k8s.io/apimachinery/pkg/api/resource"
  25. utiltesting "k8s.io/client-go/util/testing"
  26. "k8s.io/kubernetes/pkg/volume"
  27. "k8s.io/kubernetes/pkg/volume/csi/fake"
  28. volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
  29. )
  30. type fakeCsiDriverClient struct {
  31. t *testing.T
  32. nodeClient *fake.NodeClient
  33. }
  34. func newFakeCsiDriverClient(t *testing.T, stagingCapable bool) *fakeCsiDriverClient {
  35. return &fakeCsiDriverClient{
  36. t: t,
  37. nodeClient: fake.NewNodeClient(stagingCapable),
  38. }
  39. }
  40. func newFakeCsiDriverClientWithExpansion(t *testing.T, stagingCapable bool, expansionSet bool) *fakeCsiDriverClient {
  41. return &fakeCsiDriverClient{
  42. t: t,
  43. nodeClient: fake.NewNodeClientWithExpansion(stagingCapable, expansionSet),
  44. }
  45. }
  46. func newFakeCsiDriverClientWithVolumeStats(t *testing.T, volumeStatsSet bool) *fakeCsiDriverClient {
  47. return &fakeCsiDriverClient{
  48. t: t,
  49. nodeClient: fake.NewNodeClientWithVolumeStats(volumeStatsSet),
  50. }
  51. }
  52. func (c *fakeCsiDriverClient) NodeGetInfo(ctx context.Context) (
  53. nodeID string,
  54. maxVolumePerNode int64,
  55. accessibleTopology map[string]string,
  56. err error) {
  57. resp, err := c.nodeClient.NodeGetInfo(ctx, &csipbv1.NodeGetInfoRequest{})
  58. topology := resp.GetAccessibleTopology()
  59. if topology != nil {
  60. accessibleTopology = topology.Segments
  61. }
  62. return resp.GetNodeId(), resp.GetMaxVolumesPerNode(), accessibleTopology, err
  63. }
  64. func (c *fakeCsiDriverClient) NodeGetVolumeStats(ctx context.Context, volID string, targetPath string) (
  65. usageCountMap *volume.Metrics, err error) {
  66. c.t.Log("calling fake.NodeGetVolumeStats...")
  67. req := &csipbv1.NodeGetVolumeStatsRequest{
  68. VolumeId: volID,
  69. VolumePath: targetPath,
  70. }
  71. resp, err := c.nodeClient.NodeGetVolumeStats(ctx, req)
  72. usages := resp.GetUsage()
  73. metrics := &volume.Metrics{}
  74. if usages == nil {
  75. return nil, nil
  76. }
  77. for _, usage := range usages {
  78. if usage == nil {
  79. continue
  80. }
  81. unit := usage.GetUnit()
  82. switch unit {
  83. case csipbv1.VolumeUsage_BYTES:
  84. metrics.Available = resource.NewQuantity(usage.GetAvailable(), resource.BinarySI)
  85. metrics.Capacity = resource.NewQuantity(usage.GetTotal(), resource.BinarySI)
  86. metrics.Used = resource.NewQuantity(usage.GetUsed(), resource.BinarySI)
  87. case csipbv1.VolumeUsage_INODES:
  88. metrics.InodesFree = resource.NewQuantity(usage.GetAvailable(), resource.BinarySI)
  89. metrics.Inodes = resource.NewQuantity(usage.GetTotal(), resource.BinarySI)
  90. metrics.InodesUsed = resource.NewQuantity(usage.GetUsed(), resource.BinarySI)
  91. }
  92. }
  93. return metrics, nil
  94. }
  95. func (c *fakeCsiDriverClient) NodeSupportsVolumeStats(ctx context.Context) (bool, error) {
  96. c.t.Log("calling fake.NodeSupportsVolumeStats...")
  97. req := &csipbv1.NodeGetCapabilitiesRequest{}
  98. resp, err := c.nodeClient.NodeGetCapabilities(ctx, req)
  99. if err != nil {
  100. return false, err
  101. }
  102. capabilities := resp.GetCapabilities()
  103. if capabilities == nil {
  104. return false, nil
  105. }
  106. for _, capability := range capabilities {
  107. if capability.GetRpc().GetType() == csipbv1.NodeServiceCapability_RPC_GET_VOLUME_STATS {
  108. return true, nil
  109. }
  110. }
  111. return false, nil
  112. }
  113. func (c *fakeCsiDriverClient) NodePublishVolume(
  114. ctx context.Context,
  115. volID string,
  116. readOnly bool,
  117. stagingTargetPath string,
  118. targetPath string,
  119. accessMode api.PersistentVolumeAccessMode,
  120. publishContext map[string]string,
  121. volumeContext map[string]string,
  122. secrets map[string]string,
  123. fsType string,
  124. mountOptions []string,
  125. ) error {
  126. c.t.Log("calling fake.NodePublishVolume...")
  127. req := &csipbv1.NodePublishVolumeRequest{
  128. VolumeId: volID,
  129. TargetPath: targetPath,
  130. StagingTargetPath: stagingTargetPath,
  131. Readonly: readOnly,
  132. PublishContext: publishContext,
  133. VolumeContext: volumeContext,
  134. Secrets: secrets,
  135. VolumeCapability: &csipbv1.VolumeCapability{
  136. AccessMode: &csipbv1.VolumeCapability_AccessMode{
  137. Mode: asCSIAccessModeV1(accessMode),
  138. },
  139. },
  140. }
  141. if fsType == fsTypeBlockName {
  142. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Block{
  143. Block: &csipbv1.VolumeCapability_BlockVolume{},
  144. }
  145. } else {
  146. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Mount{
  147. Mount: &csipbv1.VolumeCapability_MountVolume{
  148. FsType: fsType,
  149. MountFlags: mountOptions,
  150. },
  151. }
  152. }
  153. _, err := c.nodeClient.NodePublishVolume(ctx, req)
  154. if err != nil && !isFinalError(err) {
  155. return volumetypes.NewUncertainProgressError(err.Error())
  156. }
  157. return err
  158. }
  159. func (c *fakeCsiDriverClient) NodeUnpublishVolume(ctx context.Context, volID string, targetPath string) error {
  160. c.t.Log("calling fake.NodeUnpublishVolume...")
  161. req := &csipbv1.NodeUnpublishVolumeRequest{
  162. VolumeId: volID,
  163. TargetPath: targetPath,
  164. }
  165. _, err := c.nodeClient.NodeUnpublishVolume(ctx, req)
  166. return err
  167. }
  168. func (c *fakeCsiDriverClient) NodeStageVolume(ctx context.Context,
  169. volID string,
  170. publishContext map[string]string,
  171. stagingTargetPath string,
  172. fsType string,
  173. accessMode api.PersistentVolumeAccessMode,
  174. secrets map[string]string,
  175. volumeContext map[string]string,
  176. mountOptions []string,
  177. ) error {
  178. c.t.Log("calling fake.NodeStageVolume...")
  179. req := &csipbv1.NodeStageVolumeRequest{
  180. VolumeId: volID,
  181. PublishContext: publishContext,
  182. StagingTargetPath: stagingTargetPath,
  183. VolumeCapability: &csipbv1.VolumeCapability{
  184. AccessMode: &csipbv1.VolumeCapability_AccessMode{
  185. Mode: asCSIAccessModeV1(accessMode),
  186. },
  187. },
  188. Secrets: secrets,
  189. VolumeContext: volumeContext,
  190. }
  191. if fsType == fsTypeBlockName {
  192. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Block{
  193. Block: &csipbv1.VolumeCapability_BlockVolume{},
  194. }
  195. } else {
  196. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Mount{
  197. Mount: &csipbv1.VolumeCapability_MountVolume{
  198. FsType: fsType,
  199. MountFlags: mountOptions,
  200. },
  201. }
  202. }
  203. _, err := c.nodeClient.NodeStageVolume(ctx, req)
  204. if err != nil && !isFinalError(err) {
  205. return volumetypes.NewUncertainProgressError(err.Error())
  206. }
  207. return err
  208. }
  209. func (c *fakeCsiDriverClient) NodeUnstageVolume(ctx context.Context, volID, stagingTargetPath string) error {
  210. c.t.Log("calling fake.NodeUnstageVolume...")
  211. req := &csipbv1.NodeUnstageVolumeRequest{
  212. VolumeId: volID,
  213. StagingTargetPath: stagingTargetPath,
  214. }
  215. _, err := c.nodeClient.NodeUnstageVolume(ctx, req)
  216. return err
  217. }
  218. func (c *fakeCsiDriverClient) NodeSupportsNodeExpand(ctx context.Context) (bool, error) {
  219. c.t.Log("calling fake.NodeSupportsNodeExpand...")
  220. req := &csipbv1.NodeGetCapabilitiesRequest{}
  221. resp, err := c.nodeClient.NodeGetCapabilities(ctx, req)
  222. if err != nil {
  223. return false, err
  224. }
  225. capabilities := resp.GetCapabilities()
  226. if capabilities == nil {
  227. return false, nil
  228. }
  229. for _, capability := range capabilities {
  230. if capability.GetRpc().GetType() == csipbv1.NodeServiceCapability_RPC_EXPAND_VOLUME {
  231. return true, nil
  232. }
  233. }
  234. return false, nil
  235. }
  236. func (c *fakeCsiDriverClient) NodeSupportsStageUnstage(ctx context.Context) (bool, error) {
  237. c.t.Log("calling fake.NodeGetCapabilities for NodeSupportsStageUnstage...")
  238. req := &csipbv1.NodeGetCapabilitiesRequest{}
  239. resp, err := c.nodeClient.NodeGetCapabilities(ctx, req)
  240. if err != nil {
  241. return false, err
  242. }
  243. capabilities := resp.GetCapabilities()
  244. stageUnstageSet := false
  245. if capabilities == nil {
  246. return false, nil
  247. }
  248. for _, capability := range capabilities {
  249. if capability.GetRpc().GetType() == csipbv1.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME {
  250. stageUnstageSet = true
  251. }
  252. }
  253. return stageUnstageSet, nil
  254. }
  255. func (c *fakeCsiDriverClient) NodeExpandVolume(ctx context.Context, volumeid, volumePath string, newSize resource.Quantity) (resource.Quantity, error) {
  256. c.t.Log("calling fake.NodeExpandVolume")
  257. req := &csipbv1.NodeExpandVolumeRequest{
  258. VolumeId: volumeid,
  259. VolumePath: volumePath,
  260. CapacityRange: &csipbv1.CapacityRange{RequiredBytes: newSize.Value()},
  261. }
  262. resp, err := c.nodeClient.NodeExpandVolume(ctx, req)
  263. if err != nil {
  264. return newSize, err
  265. }
  266. updatedQuantity := resource.NewQuantity(resp.CapacityBytes, resource.BinarySI)
  267. return *updatedQuantity, nil
  268. }
  269. func setupClient(t *testing.T, stageUnstageSet bool) csiClient {
  270. return newFakeCsiDriverClient(t, stageUnstageSet)
  271. }
  272. func setupClientWithExpansion(t *testing.T, stageUnstageSet bool, expansionSet bool) csiClient {
  273. return newFakeCsiDriverClientWithExpansion(t, stageUnstageSet, expansionSet)
  274. }
  275. func setupClientWithVolumeStats(t *testing.T, volumeStatsSet bool) csiClient {
  276. return newFakeCsiDriverClientWithVolumeStats(t, volumeStatsSet)
  277. }
  278. func checkErr(t *testing.T, expectedAnError bool, actualError error) {
  279. t.Helper()
  280. errOccurred := actualError != nil
  281. if expectedAnError && !errOccurred {
  282. t.Error("expected an error")
  283. }
  284. if !expectedAnError && errOccurred {
  285. t.Errorf("expected no error, got: %v", actualError)
  286. }
  287. }
  288. func TestClientNodeGetInfo(t *testing.T) {
  289. testCases := []struct {
  290. name string
  291. expectedNodeID string
  292. expectedMaxVolumePerNode int64
  293. expectedAccessibleTopology map[string]string
  294. mustFail bool
  295. err error
  296. }{
  297. {
  298. name: "test ok",
  299. expectedNodeID: "node1",
  300. expectedMaxVolumePerNode: 16,
  301. expectedAccessibleTopology: map[string]string{"com.example.csi-topology/zone": "zone1"},
  302. },
  303. {
  304. name: "grpc error",
  305. mustFail: true,
  306. err: errors.New("grpc error"),
  307. },
  308. }
  309. for _, tc := range testCases {
  310. t.Logf("test case: %s", tc.name)
  311. fakeCloser := fake.NewCloser(t)
  312. client := &csiDriverClient{
  313. driverName: "Fake Driver Name",
  314. nodeV1ClientCreator: func(addr csiAddr) (csipbv1.NodeClient, io.Closer, error) {
  315. nodeClient := fake.NewNodeClient(false /* stagingCapable */)
  316. nodeClient.SetNextError(tc.err)
  317. nodeClient.SetNodeGetInfoResp(&csipbv1.NodeGetInfoResponse{
  318. NodeId: tc.expectedNodeID,
  319. MaxVolumesPerNode: tc.expectedMaxVolumePerNode,
  320. AccessibleTopology: &csipbv1.Topology{
  321. Segments: tc.expectedAccessibleTopology,
  322. },
  323. })
  324. return nodeClient, fakeCloser, nil
  325. },
  326. }
  327. nodeID, maxVolumePerNode, accessibleTopology, err := client.NodeGetInfo(context.Background())
  328. checkErr(t, tc.mustFail, err)
  329. if nodeID != tc.expectedNodeID {
  330. t.Errorf("expected nodeID: %v; got: %v", tc.expectedNodeID, nodeID)
  331. }
  332. if maxVolumePerNode != tc.expectedMaxVolumePerNode {
  333. t.Errorf("expected maxVolumePerNode: %v; got: %v", tc.expectedMaxVolumePerNode, maxVolumePerNode)
  334. }
  335. if !reflect.DeepEqual(accessibleTopology, tc.expectedAccessibleTopology) {
  336. t.Errorf("expected accessibleTopology: %v; got: %v", tc.expectedAccessibleTopology, accessibleTopology)
  337. }
  338. if !tc.mustFail {
  339. fakeCloser.Check()
  340. }
  341. }
  342. }
  343. func TestClientNodePublishVolume(t *testing.T) {
  344. tmpDir, err := utiltesting.MkTmpdir("csi-test")
  345. if err != nil {
  346. t.Fatalf("can't create temp dir: %v", err)
  347. }
  348. defer os.RemoveAll(tmpDir)
  349. testPath := filepath.Join(tmpDir, "path")
  350. testCases := []struct {
  351. name string
  352. volID string
  353. targetPath string
  354. fsType string
  355. mustFail bool
  356. err error
  357. }{
  358. {name: "test ok", volID: "vol-test", targetPath: testPath},
  359. {name: "missing volID", targetPath: testPath, mustFail: true},
  360. {name: "missing target path", volID: "vol-test", mustFail: true},
  361. {name: "bad fs", volID: "vol-test", targetPath: testPath, fsType: "badfs", mustFail: true},
  362. {name: "grpc error", volID: "vol-test", targetPath: testPath, mustFail: true, err: errors.New("grpc error")},
  363. }
  364. for _, tc := range testCases {
  365. t.Logf("test case: %s", tc.name)
  366. fakeCloser := fake.NewCloser(t)
  367. client := &csiDriverClient{
  368. driverName: "Fake Driver Name",
  369. nodeV1ClientCreator: func(addr csiAddr) (csipbv1.NodeClient, io.Closer, error) {
  370. nodeClient := fake.NewNodeClient(false /* stagingCapable */)
  371. nodeClient.SetNextError(tc.err)
  372. return nodeClient, fakeCloser, nil
  373. },
  374. }
  375. err := client.NodePublishVolume(
  376. context.Background(),
  377. tc.volID,
  378. false,
  379. "",
  380. tc.targetPath,
  381. api.ReadWriteOnce,
  382. map[string]string{"device": "/dev/null"},
  383. map[string]string{"attr0": "val0"},
  384. map[string]string{},
  385. tc.fsType,
  386. []string{},
  387. )
  388. checkErr(t, tc.mustFail, err)
  389. if !tc.mustFail {
  390. fakeCloser.Check()
  391. }
  392. }
  393. }
  394. func TestClientNodeUnpublishVolume(t *testing.T) {
  395. tmpDir, err := utiltesting.MkTmpdir("csi-test")
  396. if err != nil {
  397. t.Fatalf("can't create temp dir: %v", err)
  398. }
  399. defer os.RemoveAll(tmpDir)
  400. testPath := filepath.Join(tmpDir, "path")
  401. testCases := []struct {
  402. name string
  403. volID string
  404. targetPath string
  405. mustFail bool
  406. err error
  407. }{
  408. {name: "test ok", volID: "vol-test", targetPath: testPath},
  409. {name: "missing volID", targetPath: testPath, mustFail: true},
  410. {name: "missing target path", volID: testPath, mustFail: true},
  411. {name: "grpc error", volID: "vol-test", targetPath: testPath, mustFail: true, err: errors.New("grpc error")},
  412. }
  413. for _, tc := range testCases {
  414. t.Logf("test case: %s", tc.name)
  415. fakeCloser := fake.NewCloser(t)
  416. client := &csiDriverClient{
  417. driverName: "Fake Driver Name",
  418. nodeV1ClientCreator: func(addr csiAddr) (csipbv1.NodeClient, io.Closer, error) {
  419. nodeClient := fake.NewNodeClient(false /* stagingCapable */)
  420. nodeClient.SetNextError(tc.err)
  421. return nodeClient, fakeCloser, nil
  422. },
  423. }
  424. err := client.NodeUnpublishVolume(context.Background(), tc.volID, tc.targetPath)
  425. checkErr(t, tc.mustFail, err)
  426. if !tc.mustFail {
  427. fakeCloser.Check()
  428. }
  429. }
  430. }
  431. func TestClientNodeStageVolume(t *testing.T) {
  432. tmpDir, err := utiltesting.MkTmpdir("csi-test")
  433. if err != nil {
  434. t.Fatalf("can't create temp dir: %v", err)
  435. }
  436. defer os.RemoveAll(tmpDir)
  437. testPath := filepath.Join(tmpDir, "/test/path")
  438. testCases := []struct {
  439. name string
  440. volID string
  441. stagingTargetPath string
  442. fsType string
  443. secrets map[string]string
  444. mountOptions []string
  445. mustFail bool
  446. err error
  447. }{
  448. {name: "test ok", volID: "vol-test", stagingTargetPath: testPath, fsType: "ext4", mountOptions: []string{"unvalidated"}},
  449. {name: "missing volID", stagingTargetPath: testPath, mustFail: true},
  450. {name: "missing target path", volID: "vol-test", mustFail: true},
  451. {name: "bad fs", volID: "vol-test", stagingTargetPath: testPath, fsType: "badfs", mustFail: true},
  452. {name: "grpc error", volID: "vol-test", stagingTargetPath: testPath, mustFail: true, err: errors.New("grpc error")},
  453. }
  454. for _, tc := range testCases {
  455. t.Logf("Running test case: %s", tc.name)
  456. fakeCloser := fake.NewCloser(t)
  457. client := &csiDriverClient{
  458. driverName: "Fake Driver Name",
  459. nodeV1ClientCreator: func(addr csiAddr) (csipbv1.NodeClient, io.Closer, error) {
  460. nodeClient := fake.NewNodeClient(false /* stagingCapable */)
  461. nodeClient.SetNextError(tc.err)
  462. return nodeClient, fakeCloser, nil
  463. },
  464. }
  465. err := client.NodeStageVolume(
  466. context.Background(),
  467. tc.volID,
  468. map[string]string{"device": "/dev/null"},
  469. tc.stagingTargetPath,
  470. tc.fsType,
  471. api.ReadWriteOnce,
  472. tc.secrets,
  473. map[string]string{"attr0": "val0"},
  474. tc.mountOptions,
  475. )
  476. checkErr(t, tc.mustFail, err)
  477. if !tc.mustFail {
  478. fakeCloser.Check()
  479. }
  480. }
  481. }
  482. func TestClientNodeUnstageVolume(t *testing.T) {
  483. tmpDir, err := utiltesting.MkTmpdir("csi-test")
  484. if err != nil {
  485. t.Fatalf("can't create temp dir: %v", err)
  486. }
  487. defer os.RemoveAll(tmpDir)
  488. testPath := filepath.Join(tmpDir, "/test/path")
  489. testCases := []struct {
  490. name string
  491. volID string
  492. stagingTargetPath string
  493. mustFail bool
  494. err error
  495. }{
  496. {name: "test ok", volID: "vol-test", stagingTargetPath: testPath},
  497. {name: "missing volID", stagingTargetPath: testPath, mustFail: true},
  498. {name: "missing target path", volID: "vol-test", mustFail: true},
  499. {name: "grpc error", volID: "vol-test", stagingTargetPath: testPath, mustFail: true, err: errors.New("grpc error")},
  500. }
  501. for _, tc := range testCases {
  502. t.Logf("Running test case: %s", tc.name)
  503. fakeCloser := fake.NewCloser(t)
  504. client := &csiDriverClient{
  505. driverName: "Fake Driver Name",
  506. nodeV1ClientCreator: func(addr csiAddr) (csipbv1.NodeClient, io.Closer, error) {
  507. nodeClient := fake.NewNodeClient(false /* stagingCapable */)
  508. nodeClient.SetNextError(tc.err)
  509. return nodeClient, fakeCloser, nil
  510. },
  511. }
  512. err := client.NodeUnstageVolume(
  513. context.Background(),
  514. tc.volID, tc.stagingTargetPath,
  515. )
  516. checkErr(t, tc.mustFail, err)
  517. if !tc.mustFail {
  518. fakeCloser.Check()
  519. }
  520. }
  521. }
  522. func TestNodeExpandVolume(t *testing.T) {
  523. testCases := []struct {
  524. name string
  525. volID string
  526. volumePath string
  527. newSize resource.Quantity
  528. mustFail bool
  529. err error
  530. }{
  531. {
  532. name: "with all correct values",
  533. volID: "vol-abcde",
  534. volumePath: "/foo/bar",
  535. newSize: resource.MustParse("10Gi"),
  536. mustFail: false,
  537. },
  538. {
  539. name: "with missing volume-id",
  540. volumePath: "/foo/bar",
  541. newSize: resource.MustParse("10Gi"),
  542. mustFail: true,
  543. },
  544. {
  545. name: "with missing volume path",
  546. volID: "vol-1234",
  547. newSize: resource.MustParse("10Gi"),
  548. mustFail: true,
  549. },
  550. {
  551. name: "with invalid quantity",
  552. volID: "vol-1234",
  553. volumePath: "/foo/bar",
  554. newSize: *resource.NewQuantity(-10, resource.DecimalSI),
  555. mustFail: true,
  556. },
  557. }
  558. for _, tc := range testCases {
  559. t.Logf("Running test cases : %s", tc.name)
  560. fakeCloser := fake.NewCloser(t)
  561. client := &csiDriverClient{
  562. driverName: "Fake Driver Name",
  563. nodeV1ClientCreator: func(addr csiAddr) (csipbv1.NodeClient, io.Closer, error) {
  564. nodeClient := fake.NewNodeClient(false /* stagingCapable */)
  565. nodeClient.SetNextError(tc.err)
  566. return nodeClient, fakeCloser, nil
  567. },
  568. }
  569. _, err := client.NodeExpandVolume(context.Background(), tc.volID, tc.volumePath, tc.newSize)
  570. checkErr(t, tc.mustFail, err)
  571. if !tc.mustFail {
  572. fakeCloser.Check()
  573. }
  574. }
  575. }
  576. type VolumeStatsOptions struct {
  577. VolumeSpec *volume.Spec
  578. // this just could be volumeID
  579. VolumeID string
  580. // DeviceMountPath location where device is mounted on the node. If volume type
  581. // is attachable - this would be global mount path otherwise
  582. // it would be location where volume was mounted for the pod
  583. DeviceMountPath string
  584. }
  585. func TestVolumeStats(t *testing.T) {
  586. spec := volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "metrics", "test-vol"), false)
  587. tests := []struct {
  588. name string
  589. volumeStatsSet bool
  590. volumeData VolumeStatsOptions
  591. success bool
  592. }{
  593. {
  594. name: "when nodeVolumeStats=on, VolumeID=on, DeviceMountPath=on",
  595. volumeStatsSet: true,
  596. volumeData: VolumeStatsOptions{
  597. VolumeSpec: spec,
  598. VolumeID: "volume1",
  599. DeviceMountPath: "/foo/bar",
  600. },
  601. success: true,
  602. },
  603. {
  604. name: "when nodeVolumeStats=off, VolumeID=on, DeviceMountPath=on",
  605. volumeStatsSet: false,
  606. volumeData: VolumeStatsOptions{
  607. VolumeSpec: spec,
  608. VolumeID: "volume1",
  609. DeviceMountPath: "/foo/bar",
  610. },
  611. success: false,
  612. },
  613. {
  614. name: "when nodeVolumeStats=on, VolumeID=off, DeviceMountPath=on",
  615. volumeStatsSet: true,
  616. volumeData: VolumeStatsOptions{
  617. VolumeSpec: spec,
  618. VolumeID: "",
  619. DeviceMountPath: "/foo/bar",
  620. },
  621. success: false,
  622. },
  623. {
  624. name: "when nodeVolumeStats=on, VolumeID=on, DeviceMountPath=off",
  625. volumeStatsSet: true,
  626. volumeData: VolumeStatsOptions{
  627. VolumeSpec: spec,
  628. VolumeID: "volume1",
  629. DeviceMountPath: "",
  630. },
  631. success: false,
  632. },
  633. {
  634. name: "when nodeVolumeStats=on, VolumeID=on, DeviceMountPath=off",
  635. volumeStatsSet: true,
  636. volumeData: VolumeStatsOptions{
  637. VolumeSpec: spec,
  638. VolumeID: "",
  639. DeviceMountPath: "",
  640. },
  641. success: false,
  642. },
  643. }
  644. for _, tc := range tests {
  645. t.Run(tc.name, func(t *testing.T) {
  646. ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
  647. defer cancel()
  648. csiSource, _ := getCSISourceFromSpec(tc.volumeData.VolumeSpec)
  649. csClient := setupClientWithVolumeStats(t, tc.volumeStatsSet)
  650. _, err := csClient.NodeGetVolumeStats(ctx, csiSource.VolumeHandle, tc.volumeData.DeviceMountPath)
  651. if err != nil && tc.success {
  652. t.Errorf("For %s : expected %v got %v", tc.name, tc.success, err)
  653. }
  654. })
  655. }
  656. }