csi_client_test.go 20 KB

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