csi_client.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  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. "fmt"
  18. "io"
  19. "net"
  20. "sync"
  21. "time"
  22. csipbv1 "github.com/container-storage-interface/spec/lib/go/csi"
  23. "google.golang.org/grpc"
  24. "google.golang.org/grpc/codes"
  25. "google.golang.org/grpc/status"
  26. api "k8s.io/api/core/v1"
  27. "k8s.io/apimachinery/pkg/api/resource"
  28. "k8s.io/klog"
  29. "k8s.io/kubernetes/pkg/volume"
  30. volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
  31. )
  32. type csiClient interface {
  33. NodeGetInfo(ctx context.Context) (
  34. nodeID string,
  35. maxVolumePerNode int64,
  36. accessibleTopology map[string]string,
  37. err error)
  38. NodePublishVolume(
  39. ctx context.Context,
  40. volumeid string,
  41. readOnly bool,
  42. stagingTargetPath string,
  43. targetPath string,
  44. accessMode api.PersistentVolumeAccessMode,
  45. publishContext map[string]string,
  46. volumeContext map[string]string,
  47. secrets map[string]string,
  48. fsType string,
  49. mountOptions []string,
  50. ) error
  51. NodeExpandVolume(ctx context.Context, volumeid, volumePath string, newSize resource.Quantity) (resource.Quantity, error)
  52. NodeUnpublishVolume(
  53. ctx context.Context,
  54. volID string,
  55. targetPath string,
  56. ) error
  57. NodeStageVolume(ctx context.Context,
  58. volID string,
  59. publishVolumeInfo map[string]string,
  60. stagingTargetPath string,
  61. fsType string,
  62. accessMode api.PersistentVolumeAccessMode,
  63. secrets map[string]string,
  64. volumeContext map[string]string,
  65. mountOptions []string,
  66. ) error
  67. NodeGetVolumeStats(
  68. ctx context.Context,
  69. volID string,
  70. targetPath string,
  71. ) (*volume.Metrics, error)
  72. NodeUnstageVolume(ctx context.Context, volID, stagingTargetPath string) error
  73. NodeSupportsStageUnstage(ctx context.Context) (bool, error)
  74. NodeSupportsNodeExpand(ctx context.Context) (bool, error)
  75. NodeSupportsVolumeStats(ctx context.Context) (bool, error)
  76. }
  77. // Strongly typed address
  78. type csiAddr string
  79. // Strongly typed driver name
  80. type csiDriverName string
  81. // csiClient encapsulates all csi-plugin methods
  82. type csiDriverClient struct {
  83. driverName csiDriverName
  84. addr csiAddr
  85. nodeV1ClientCreator nodeV1ClientCreator
  86. }
  87. var _ csiClient = &csiDriverClient{}
  88. type nodeV1ClientCreator func(addr csiAddr) (
  89. nodeClient csipbv1.NodeClient,
  90. closer io.Closer,
  91. err error,
  92. )
  93. const (
  94. initialDuration = 1 * time.Second
  95. factor = 2.0
  96. steps = 5
  97. )
  98. // newV1NodeClient creates a new NodeClient with the internally used gRPC
  99. // connection set up. It also returns a closer which must to be called to close
  100. // the gRPC connection when the NodeClient is not used anymore.
  101. // This is the default implementation for the nodeV1ClientCreator, used in
  102. // newCsiDriverClient.
  103. func newV1NodeClient(addr csiAddr) (nodeClient csipbv1.NodeClient, closer io.Closer, err error) {
  104. var conn *grpc.ClientConn
  105. conn, err = newGrpcConn(addr)
  106. if err != nil {
  107. return nil, nil, err
  108. }
  109. nodeClient = csipbv1.NewNodeClient(conn)
  110. return nodeClient, conn, nil
  111. }
  112. func newCsiDriverClient(driverName csiDriverName) (*csiDriverClient, error) {
  113. if driverName == "" {
  114. return nil, fmt.Errorf("driver name is empty")
  115. }
  116. existingDriver, driverExists := csiDrivers.Get(string(driverName))
  117. if !driverExists {
  118. return nil, fmt.Errorf("driver name %s not found in the list of registered CSI drivers", driverName)
  119. }
  120. nodeV1ClientCreator := newV1NodeClient
  121. return &csiDriverClient{
  122. driverName: driverName,
  123. addr: csiAddr(existingDriver.endpoint),
  124. nodeV1ClientCreator: nodeV1ClientCreator,
  125. }, nil
  126. }
  127. func (c *csiDriverClient) NodeGetInfo(ctx context.Context) (
  128. nodeID string,
  129. maxVolumePerNode int64,
  130. accessibleTopology map[string]string,
  131. err error) {
  132. klog.V(4).Info(log("calling NodeGetInfo rpc"))
  133. var getNodeInfoError error
  134. nodeID, maxVolumePerNode, accessibleTopology, getNodeInfoError = c.nodeGetInfoV1(ctx)
  135. if getNodeInfoError != nil {
  136. klog.Warningf("Error calling CSI NodeGetInfo(): %v", getNodeInfoError.Error())
  137. }
  138. return nodeID, maxVolumePerNode, accessibleTopology, getNodeInfoError
  139. }
  140. func (c *csiDriverClient) nodeGetInfoV1(ctx context.Context) (
  141. nodeID string,
  142. maxVolumePerNode int64,
  143. accessibleTopology map[string]string,
  144. err error) {
  145. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  146. if err != nil {
  147. return "", 0, nil, err
  148. }
  149. defer closer.Close()
  150. res, err := nodeClient.NodeGetInfo(ctx, &csipbv1.NodeGetInfoRequest{})
  151. if err != nil {
  152. return "", 0, nil, err
  153. }
  154. topology := res.GetAccessibleTopology()
  155. if topology != nil {
  156. accessibleTopology = topology.Segments
  157. }
  158. return res.GetNodeId(), res.GetMaxVolumesPerNode(), accessibleTopology, nil
  159. }
  160. func (c *csiDriverClient) NodePublishVolume(
  161. ctx context.Context,
  162. volID string,
  163. readOnly bool,
  164. stagingTargetPath string,
  165. targetPath string,
  166. accessMode api.PersistentVolumeAccessMode,
  167. publishContext map[string]string,
  168. volumeContext map[string]string,
  169. secrets map[string]string,
  170. fsType string,
  171. mountOptions []string,
  172. ) error {
  173. klog.V(4).Info(log("calling NodePublishVolume rpc [volid=%s,target_path=%s]", volID, targetPath))
  174. if volID == "" {
  175. return errors.New("missing volume id")
  176. }
  177. if targetPath == "" {
  178. return errors.New("missing target path")
  179. }
  180. if c.nodeV1ClientCreator == nil {
  181. return errors.New("failed to call NodePublishVolume. nodeV1ClientCreator is nil")
  182. }
  183. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  184. if err != nil {
  185. return err
  186. }
  187. defer closer.Close()
  188. req := &csipbv1.NodePublishVolumeRequest{
  189. VolumeId: volID,
  190. TargetPath: targetPath,
  191. Readonly: readOnly,
  192. PublishContext: publishContext,
  193. VolumeContext: volumeContext,
  194. Secrets: secrets,
  195. VolumeCapability: &csipbv1.VolumeCapability{
  196. AccessMode: &csipbv1.VolumeCapability_AccessMode{
  197. Mode: asCSIAccessModeV1(accessMode),
  198. },
  199. },
  200. }
  201. if stagingTargetPath != "" {
  202. req.StagingTargetPath = stagingTargetPath
  203. }
  204. if fsType == fsTypeBlockName {
  205. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Block{
  206. Block: &csipbv1.VolumeCapability_BlockVolume{},
  207. }
  208. } else {
  209. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Mount{
  210. Mount: &csipbv1.VolumeCapability_MountVolume{
  211. FsType: fsType,
  212. MountFlags: mountOptions,
  213. },
  214. }
  215. }
  216. _, err = nodeClient.NodePublishVolume(ctx, req)
  217. if err != nil && !isFinalError(err) {
  218. return volumetypes.NewUncertainProgressError(err.Error())
  219. }
  220. return err
  221. }
  222. func (c *csiDriverClient) NodeExpandVolume(ctx context.Context, volumeID, volumePath string, newSize resource.Quantity) (resource.Quantity, error) {
  223. if c.nodeV1ClientCreator == nil {
  224. return newSize, fmt.Errorf("version of CSI driver does not support volume expansion")
  225. }
  226. if volumeID == "" {
  227. return newSize, errors.New("missing volume id")
  228. }
  229. if volumePath == "" {
  230. return newSize, errors.New("missing volume path")
  231. }
  232. if newSize.Value() < 0 {
  233. return newSize, errors.New("size can not be less than 0")
  234. }
  235. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  236. if err != nil {
  237. return newSize, err
  238. }
  239. defer closer.Close()
  240. req := &csipbv1.NodeExpandVolumeRequest{
  241. VolumeId: volumeID,
  242. VolumePath: volumePath,
  243. CapacityRange: &csipbv1.CapacityRange{RequiredBytes: newSize.Value()},
  244. }
  245. resp, err := nodeClient.NodeExpandVolume(ctx, req)
  246. if err != nil {
  247. return newSize, err
  248. }
  249. updatedQuantity := resource.NewQuantity(resp.CapacityBytes, resource.BinarySI)
  250. return *updatedQuantity, nil
  251. }
  252. func (c *csiDriverClient) NodeUnpublishVolume(ctx context.Context, volID string, targetPath string) error {
  253. klog.V(4).Info(log("calling NodeUnpublishVolume rpc: [volid=%s, target_path=%s", volID, targetPath))
  254. if volID == "" {
  255. return errors.New("missing volume id")
  256. }
  257. if targetPath == "" {
  258. return errors.New("missing target path")
  259. }
  260. if c.nodeV1ClientCreator == nil {
  261. return errors.New("nodeV1ClientCreate is nil")
  262. }
  263. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  264. if err != nil {
  265. return err
  266. }
  267. defer closer.Close()
  268. req := &csipbv1.NodeUnpublishVolumeRequest{
  269. VolumeId: volID,
  270. TargetPath: targetPath,
  271. }
  272. _, err = nodeClient.NodeUnpublishVolume(ctx, req)
  273. return err
  274. }
  275. func (c *csiDriverClient) NodeStageVolume(ctx context.Context,
  276. volID string,
  277. publishContext map[string]string,
  278. stagingTargetPath string,
  279. fsType string,
  280. accessMode api.PersistentVolumeAccessMode,
  281. secrets map[string]string,
  282. volumeContext map[string]string,
  283. mountOptions []string,
  284. ) error {
  285. klog.V(4).Info(log("calling NodeStageVolume rpc [volid=%s,staging_target_path=%s]", volID, stagingTargetPath))
  286. if volID == "" {
  287. return errors.New("missing volume id")
  288. }
  289. if stagingTargetPath == "" {
  290. return errors.New("missing staging target path")
  291. }
  292. if c.nodeV1ClientCreator == nil {
  293. return errors.New("nodeV1ClientCreate is nil")
  294. }
  295. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  296. if err != nil {
  297. return err
  298. }
  299. defer closer.Close()
  300. req := &csipbv1.NodeStageVolumeRequest{
  301. VolumeId: volID,
  302. PublishContext: publishContext,
  303. StagingTargetPath: stagingTargetPath,
  304. VolumeCapability: &csipbv1.VolumeCapability{
  305. AccessMode: &csipbv1.VolumeCapability_AccessMode{
  306. Mode: asCSIAccessModeV1(accessMode),
  307. },
  308. },
  309. Secrets: secrets,
  310. VolumeContext: volumeContext,
  311. }
  312. if fsType == fsTypeBlockName {
  313. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Block{
  314. Block: &csipbv1.VolumeCapability_BlockVolume{},
  315. }
  316. } else {
  317. req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Mount{
  318. Mount: &csipbv1.VolumeCapability_MountVolume{
  319. FsType: fsType,
  320. MountFlags: mountOptions,
  321. },
  322. }
  323. }
  324. _, err = nodeClient.NodeStageVolume(ctx, req)
  325. if err != nil && !isFinalError(err) {
  326. return volumetypes.NewUncertainProgressError(err.Error())
  327. }
  328. return err
  329. }
  330. func (c *csiDriverClient) NodeUnstageVolume(ctx context.Context, volID, stagingTargetPath string) error {
  331. klog.V(4).Info(log("calling NodeUnstageVolume rpc [volid=%s,staging_target_path=%s]", volID, stagingTargetPath))
  332. if volID == "" {
  333. return errors.New("missing volume id")
  334. }
  335. if stagingTargetPath == "" {
  336. return errors.New("missing staging target path")
  337. }
  338. if c.nodeV1ClientCreator == nil {
  339. return errors.New("nodeV1ClientCreate is nil")
  340. }
  341. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  342. if err != nil {
  343. return err
  344. }
  345. defer closer.Close()
  346. req := &csipbv1.NodeUnstageVolumeRequest{
  347. VolumeId: volID,
  348. StagingTargetPath: stagingTargetPath,
  349. }
  350. _, err = nodeClient.NodeUnstageVolume(ctx, req)
  351. return err
  352. }
  353. func (c *csiDriverClient) NodeSupportsNodeExpand(ctx context.Context) (bool, error) {
  354. klog.V(4).Info(log("calling NodeGetCapabilities rpc to determine if Node has EXPAND_VOLUME capability"))
  355. if c.nodeV1ClientCreator == nil {
  356. return false, errors.New("nodeV1ClientCreate is nil")
  357. }
  358. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  359. if err != nil {
  360. return false, err
  361. }
  362. defer closer.Close()
  363. req := &csipbv1.NodeGetCapabilitiesRequest{}
  364. resp, err := nodeClient.NodeGetCapabilities(ctx, req)
  365. if err != nil {
  366. return false, err
  367. }
  368. capabilities := resp.GetCapabilities()
  369. if capabilities == nil {
  370. return false, nil
  371. }
  372. for _, capability := range capabilities {
  373. if capability.GetRpc().GetType() == csipbv1.NodeServiceCapability_RPC_EXPAND_VOLUME {
  374. return true, nil
  375. }
  376. }
  377. return false, nil
  378. }
  379. func (c *csiDriverClient) NodeSupportsStageUnstage(ctx context.Context) (bool, error) {
  380. klog.V(4).Info(log("calling NodeGetCapabilities rpc to determine if NodeSupportsStageUnstage"))
  381. if c.nodeV1ClientCreator == nil {
  382. return false, errors.New("nodeV1ClientCreate is nil")
  383. }
  384. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  385. if err != nil {
  386. return false, err
  387. }
  388. defer closer.Close()
  389. req := &csipbv1.NodeGetCapabilitiesRequest{}
  390. resp, err := nodeClient.NodeGetCapabilities(ctx, req)
  391. if err != nil {
  392. return false, err
  393. }
  394. capabilities := resp.GetCapabilities()
  395. stageUnstageSet := false
  396. if capabilities == nil {
  397. return false, nil
  398. }
  399. for _, capability := range capabilities {
  400. if capability.GetRpc().GetType() == csipbv1.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME {
  401. stageUnstageSet = true
  402. break
  403. }
  404. }
  405. return stageUnstageSet, nil
  406. }
  407. func asCSIAccessModeV1(am api.PersistentVolumeAccessMode) csipbv1.VolumeCapability_AccessMode_Mode {
  408. switch am {
  409. case api.ReadWriteOnce:
  410. return csipbv1.VolumeCapability_AccessMode_SINGLE_NODE_WRITER
  411. case api.ReadOnlyMany:
  412. return csipbv1.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY
  413. case api.ReadWriteMany:
  414. return csipbv1.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER
  415. }
  416. return csipbv1.VolumeCapability_AccessMode_UNKNOWN
  417. }
  418. func newGrpcConn(addr csiAddr) (*grpc.ClientConn, error) {
  419. network := "unix"
  420. klog.V(4).Infof(log("creating new gRPC connection for [%s://%s]", network, addr))
  421. return grpc.Dial(
  422. string(addr),
  423. grpc.WithInsecure(),
  424. grpc.WithContextDialer(func(ctx context.Context, target string) (net.Conn, error) {
  425. return (&net.Dialer{}).DialContext(ctx, network, target)
  426. }),
  427. )
  428. }
  429. // CSI client getter with cache.
  430. // This provides a method to initialize CSI client with driver name and caches
  431. // it for later use. When CSI clients have not been discovered yet (e.g.
  432. // on kubelet restart), client initialization will fail. Users of CSI client (e.g.
  433. // mounter manager and block mapper) can use this to delay CSI client
  434. // initialization until needed.
  435. type csiClientGetter struct {
  436. sync.RWMutex
  437. csiClient csiClient
  438. driverName csiDriverName
  439. }
  440. func (c *csiClientGetter) Get() (csiClient, error) {
  441. c.RLock()
  442. if c.csiClient != nil {
  443. c.RUnlock()
  444. return c.csiClient, nil
  445. }
  446. c.RUnlock()
  447. c.Lock()
  448. defer c.Unlock()
  449. // Double-checking locking criterion.
  450. if c.csiClient != nil {
  451. return c.csiClient, nil
  452. }
  453. csi, err := newCsiDriverClient(c.driverName)
  454. if err != nil {
  455. return nil, err
  456. }
  457. c.csiClient = csi
  458. return c.csiClient, nil
  459. }
  460. func (c *csiDriverClient) NodeSupportsVolumeStats(ctx context.Context) (bool, error) {
  461. klog.V(5).Info(log("calling NodeGetCapabilities rpc to determine if NodeSupportsVolumeStats"))
  462. if c.nodeV1ClientCreator == nil {
  463. return false, errors.New("nodeV1ClientCreate is nil")
  464. }
  465. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  466. if err != nil {
  467. return false, err
  468. }
  469. defer closer.Close()
  470. req := &csipbv1.NodeGetCapabilitiesRequest{}
  471. resp, err := nodeClient.NodeGetCapabilities(ctx, req)
  472. if err != nil {
  473. return false, err
  474. }
  475. capabilities := resp.GetCapabilities()
  476. if capabilities == nil {
  477. return false, nil
  478. }
  479. for _, capability := range capabilities {
  480. if capability.GetRpc().GetType() == csipbv1.NodeServiceCapability_RPC_GET_VOLUME_STATS {
  481. return true, nil
  482. }
  483. }
  484. return false, nil
  485. }
  486. func (c *csiDriverClient) NodeGetVolumeStats(ctx context.Context, volID string, targetPath string) (*volume.Metrics, error) {
  487. klog.V(4).Info(log("calling NodeGetVolumeStats rpc: [volid=%s, target_path=%s", volID, targetPath))
  488. if volID == "" {
  489. return nil, errors.New("missing volume id")
  490. }
  491. if targetPath == "" {
  492. return nil, errors.New("missing target path")
  493. }
  494. if c.nodeV1ClientCreator == nil {
  495. return nil, errors.New("nodeV1ClientCreate is nil")
  496. }
  497. nodeClient, closer, err := c.nodeV1ClientCreator(c.addr)
  498. if err != nil {
  499. return nil, err
  500. }
  501. defer closer.Close()
  502. req := &csipbv1.NodeGetVolumeStatsRequest{
  503. VolumeId: volID,
  504. VolumePath: targetPath,
  505. }
  506. resp, err := nodeClient.NodeGetVolumeStats(ctx, req)
  507. if err != nil {
  508. return nil, err
  509. }
  510. usages := resp.GetUsage()
  511. if usages == nil {
  512. return nil, fmt.Errorf("failed to get usage from response. usage is nil")
  513. }
  514. metrics := &volume.Metrics{
  515. Used: resource.NewQuantity(int64(0), resource.BinarySI),
  516. Capacity: resource.NewQuantity(int64(0), resource.BinarySI),
  517. Available: resource.NewQuantity(int64(0), resource.BinarySI),
  518. InodesUsed: resource.NewQuantity(int64(0), resource.BinarySI),
  519. Inodes: resource.NewQuantity(int64(0), resource.BinarySI),
  520. InodesFree: resource.NewQuantity(int64(0), resource.BinarySI),
  521. }
  522. for _, usage := range usages {
  523. if usage == nil {
  524. continue
  525. }
  526. unit := usage.GetUnit()
  527. switch unit {
  528. case csipbv1.VolumeUsage_BYTES:
  529. metrics.Available = resource.NewQuantity(usage.GetAvailable(), resource.BinarySI)
  530. metrics.Capacity = resource.NewQuantity(usage.GetTotal(), resource.BinarySI)
  531. metrics.Used = resource.NewQuantity(usage.GetUsed(), resource.BinarySI)
  532. case csipbv1.VolumeUsage_INODES:
  533. metrics.InodesFree = resource.NewQuantity(usage.GetAvailable(), resource.BinarySI)
  534. metrics.Inodes = resource.NewQuantity(usage.GetTotal(), resource.BinarySI)
  535. metrics.InodesUsed = resource.NewQuantity(usage.GetUsed(), resource.BinarySI)
  536. default:
  537. klog.Errorf("unknown key %s in usage", unit.String())
  538. }
  539. }
  540. return metrics, nil
  541. }
  542. func isFinalError(err error) bool {
  543. // Sources:
  544. // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
  545. // https://github.com/container-storage-interface/spec/blob/master/spec.md
  546. st, ok := status.FromError(err)
  547. if !ok {
  548. // This is not gRPC error. The operation must have failed before gRPC
  549. // method was called, otherwise we would get gRPC error.
  550. // We don't know if any previous volume operation is in progress, be on the safe side.
  551. return false
  552. }
  553. switch st.Code() {
  554. case codes.Canceled, // gRPC: Client Application cancelled the request
  555. codes.DeadlineExceeded, // gRPC: Timeout
  556. codes.Unavailable, // gRPC: Server shutting down, TCP connection broken - previous volume operation may be still in progress.
  557. codes.ResourceExhausted, // gRPC: Server temporarily out of resources - previous volume operation may be still in progress.
  558. codes.Aborted: // CSI: Operation pending for volume
  559. return false
  560. }
  561. // All other errors mean that operation either did not
  562. // even start or failed. It is for sure not in progress.
  563. return true
  564. }