1
16
17 package csi
18
19 import (
20 "errors"
21 "fmt"
22
23 "google.golang.org/grpc/codes"
24 "google.golang.org/grpc/status"
25 api "k8s.io/api/core/v1"
26 "k8s.io/klog/v2"
27 "k8s.io/kubernetes/pkg/volume"
28 "k8s.io/kubernetes/pkg/volume/util"
29 volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
30 )
31
32 var _ volume.NodeExpandableVolumePlugin = &csiPlugin{}
33
34 func (c *csiPlugin) RequiresFSResize() bool {
35 return true
36 }
37
38 func (c *csiPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) {
39 klog.V(4).Infof(log("Expander.NodeExpand(%s)", resizeOptions.DeviceMountPath))
40 csiSource, err := getCSISourceFromSpec(resizeOptions.VolumeSpec)
41 if err != nil {
42 return false, errors.New(log("Expander.NodeExpand failed to get CSI persistent source: %v", err))
43 }
44
45 csClient, err := newCsiDriverClient(csiDriverName(csiSource.Driver))
46 if err != nil {
47
48
49 return false, volumetypes.NewTransientOperationFailure(err.Error())
50 }
51 fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec)
52 if err != nil {
53 return false, errors.New(log("Expander.NodeExpand failed to check VolumeMode of source: %v", err))
54 }
55
56 return c.nodeExpandWithClient(resizeOptions, csiSource, csClient, fsVolume)
57 }
58
59 func (c *csiPlugin) nodeExpandWithClient(
60 resizeOptions volume.NodeResizeOptions,
61 csiSource *api.CSIPersistentVolumeSource,
62 csClient csiClient,
63 fsVolume bool) (bool, error) {
64 driverName := csiSource.Driver
65
66 ctx, cancel := createCSIOperationContext(resizeOptions.VolumeSpec, csiTimeout)
67 defer cancel()
68
69 nodeExpandSet, err := csClient.NodeSupportsNodeExpand(ctx)
70 if err != nil {
71 return false, fmt.Errorf("Expander.NodeExpand failed to check if node supports expansion : %v", err)
72 }
73
74 if !nodeExpandSet {
75 return false, volumetypes.NewOperationNotSupportedError(fmt.Sprintf("NodeExpand is not supported by the CSI driver %s", driverName))
76 }
77
78 pv := resizeOptions.VolumeSpec.PersistentVolume
79 if pv == nil {
80 return false, fmt.Errorf("Expander.NodeExpand failed to find associated PersistentVolume for plugin %s", c.GetPluginName())
81 }
82 nodeExpandSecrets := map[string]string{}
83 expandClient := c.host.GetKubeClient()
84
85 if csiSource.NodeExpandSecretRef != nil {
86 nodeExpandSecrets, err = getCredentialsFromSecret(expandClient, csiSource.NodeExpandSecretRef)
87 if err != nil {
88 return false, fmt.Errorf("expander.NodeExpand failed to get NodeExpandSecretRef %s/%s: %v",
89 csiSource.NodeExpandSecretRef.Namespace, csiSource.NodeExpandSecretRef.Name, err)
90 }
91 }
92
93 opts := csiResizeOptions{
94 volumePath: resizeOptions.DeviceMountPath,
95 stagingTargetPath: resizeOptions.DeviceStagePath,
96 volumeID: csiSource.VolumeHandle,
97 newSize: resizeOptions.NewSize,
98 fsType: csiSource.FSType,
99 accessMode: api.ReadWriteOnce,
100 mountOptions: pv.Spec.MountOptions,
101 secrets: nodeExpandSecrets,
102 }
103
104 if !fsVolume {
105
106
107
108 opts.volumePath = resizeOptions.DevicePath
109 opts.fsType = fsTypeBlockName
110 }
111
112 if pv.Spec.AccessModes != nil {
113 opts.accessMode = pv.Spec.AccessModes[0]
114 }
115
116 _, err = csClient.NodeExpandVolume(ctx, opts)
117 if err != nil {
118 if inUseError(err) {
119 failedConditionErr := fmt.Errorf("Expander.NodeExpand failed to expand the volume : %w", volumetypes.NewFailedPreconditionError(err.Error()))
120 return false, failedConditionErr
121 }
122 return false, fmt.Errorf("Expander.NodeExpand failed to expand the volume : %w", err)
123 }
124 return true, nil
125 }
126
127 func inUseError(err error) bool {
128 st, ok := status.FromError(err)
129 if !ok {
130
131 return false
132 }
133
134
135
136 return st.Code() == codes.FailedPrecondition
137 }
138
View as plain text