1
16
17 package flexvolume
18
19 import (
20 "encoding/json"
21 "errors"
22 "fmt"
23 "time"
24
25 "k8s.io/klog/v2"
26
27 "k8s.io/kubernetes/pkg/volume"
28 )
29
30 const (
31
32 initCmd = "init"
33 getVolumeNameCmd = "getvolumename"
34
35 isAttached = "isattached"
36
37 attachCmd = "attach"
38 waitForAttachCmd = "waitforattach"
39 mountDeviceCmd = "mountdevice"
40
41 detachCmd = "detach"
42 unmountDeviceCmd = "unmountdevice"
43
44 mountCmd = "mount"
45 unmountCmd = "unmount"
46
47 expandVolumeCmd = "expandvolume"
48 expandFSCmd = "expandfs"
49
50
51 optionFSType = "kubernetes.io/fsType"
52 optionReadWrite = "kubernetes.io/readwrite"
53 optionKeySecret = "kubernetes.io/secret"
54 optionFSGroup = "kubernetes.io/mounterArgs.FsGroup"
55 optionPVorVolumeName = "kubernetes.io/pvOrVolumeName"
56
57 optionKeyPodName = "kubernetes.io/pod.name"
58 optionKeyPodNamespace = "kubernetes.io/pod.namespace"
59 optionKeyPodUID = "kubernetes.io/pod.uid"
60
61 optionKeyServiceAccountName = "kubernetes.io/serviceAccount.name"
62 )
63
64 const (
65
66 StatusSuccess = "Success"
67
68 StatusNotSupported = "Not supported"
69 )
70
71 var (
72 errTimeout = fmt.Errorf("timeout")
73 )
74
75
76
77 type DriverCall struct {
78 Command string
79 Timeout time.Duration
80 plugin *flexVolumePlugin
81 args []string
82 }
83
84 func (plugin *flexVolumePlugin) NewDriverCall(command string) *DriverCall {
85 return plugin.NewDriverCallWithTimeout(command, 0)
86 }
87
88 func (plugin *flexVolumePlugin) NewDriverCallWithTimeout(command string, timeout time.Duration) *DriverCall {
89 return &DriverCall{
90 Command: command,
91 Timeout: timeout,
92 plugin: plugin,
93 args: []string{command},
94 }
95 }
96
97
98 func (dc *DriverCall) Append(arg string) {
99 dc.args = append(dc.args, arg)
100 }
101
102
103 func (dc *DriverCall) AppendSpec(spec *volume.Spec, host volume.VolumeHost, extraOptions map[string]string) error {
104 optionsForDriver, err := NewOptionsForDriver(spec, host, extraOptions)
105 if err != nil {
106 return err
107 }
108
109 jsonBytes, err := json.Marshal(optionsForDriver)
110 if err != nil {
111 return fmt.Errorf("failed to marshal spec, error: %s", err.Error())
112 }
113
114 dc.Append(string(jsonBytes))
115 return nil
116 }
117
118
119 func (dc *DriverCall) Run() (*DriverStatus, error) {
120 if dc.plugin.isUnsupported(dc.Command) {
121 return nil, errors.New(StatusNotSupported)
122 }
123 execPath := dc.plugin.getExecutable()
124
125 cmd := dc.plugin.runner.Command(execPath, dc.args...)
126
127 timeout := false
128 if dc.Timeout > 0 {
129 timer := time.AfterFunc(dc.Timeout, func() {
130 timeout = true
131 cmd.Stop()
132 })
133 defer timer.Stop()
134 }
135
136 output, execErr := cmd.CombinedOutput()
137 if execErr != nil {
138 if timeout {
139 return nil, errTimeout
140 }
141 _, err := handleCmdResponse(dc.Command, output)
142 if err == nil {
143 klog.Errorf("FlexVolume: driver bug: %s: exec error (%s) but no error in response.", execPath, execErr)
144 return nil, execErr
145 }
146 if isCmdNotSupportedErr(err) {
147 dc.plugin.unsupported(dc.Command)
148 } else {
149 klog.Warningf("FlexVolume: driver call failed: executable: %s, args: %s, error: %s, output: %q", execPath, dc.args, execErr.Error(), output)
150 }
151 return nil, err
152 }
153
154 status, err := handleCmdResponse(dc.Command, output)
155 if err != nil {
156 if isCmdNotSupportedErr(err) {
157 dc.plugin.unsupported(dc.Command)
158 }
159 return nil, err
160 }
161
162 return status, nil
163 }
164
165
166 type OptionsForDriver map[string]string
167
168
169 func NewOptionsForDriver(spec *volume.Spec, host volume.VolumeHost, extraOptions map[string]string) (OptionsForDriver, error) {
170
171 volSourceFSType, err := getFSType(spec)
172 if err != nil {
173 return nil, err
174 }
175
176 readOnly, err := getReadOnly(spec)
177 if err != nil {
178 return nil, err
179 }
180
181 volSourceOptions, err := getOptions(spec)
182 if err != nil {
183 return nil, err
184 }
185
186 options := map[string]string{}
187
188 options[optionFSType] = volSourceFSType
189
190 if readOnly {
191 options[optionReadWrite] = "ro"
192 } else {
193 options[optionReadWrite] = "rw"
194 }
195
196 options[optionPVorVolumeName] = spec.Name()
197
198 for key, value := range extraOptions {
199 options[key] = value
200 }
201
202 for key, value := range volSourceOptions {
203 options[key] = value
204 }
205
206 return OptionsForDriver(options), nil
207 }
208
209
210 type DriverStatus struct {
211
212 Status string `json:"status"`
213
214 Message string `json:"message,omitempty"`
215
216
217 DevicePath string `json:"device,omitempty"`
218
219 VolumeName string `json:"volumeName,omitempty"`
220
221 Attached bool `json:"attached,omitempty"`
222
223
224
225 Capabilities *DriverCapabilities `json:",omitempty"`
226
227 ActualVolumeSize int64 `json:"volumeNewSize,omitempty"`
228 }
229
230
231 type DriverCapabilities struct {
232 Attach bool `json:"attach"`
233 SELinuxRelabel bool `json:"selinuxRelabel"`
234 SupportsMetrics bool `json:"supportsMetrics"`
235 FSGroup bool `json:"fsGroup"`
236 RequiresFSResize bool `json:"requiresFSResize"`
237 }
238
239 func defaultCapabilities() *DriverCapabilities {
240 return &DriverCapabilities{
241 Attach: true,
242 SELinuxRelabel: true,
243 SupportsMetrics: false,
244 FSGroup: true,
245 RequiresFSResize: true,
246 }
247 }
248
249
250
251 func isCmdNotSupportedErr(err error) bool {
252 return err != nil && err.Error() == StatusNotSupported
253 }
254
255
256
257 func handleCmdResponse(cmd string, output []byte) (*DriverStatus, error) {
258 status := DriverStatus{
259 Capabilities: defaultCapabilities(),
260 }
261 if err := json.Unmarshal(output, &status); err != nil {
262 klog.Errorf("Failed to unmarshal output for command: %s, output: %q, error: %s", cmd, string(output), err.Error())
263 return nil, err
264 } else if status.Status == StatusNotSupported {
265 klog.V(5).Infof("%s command is not supported by the driver", cmd)
266 return nil, errors.New(status.Status)
267 } else if status.Status != StatusSuccess {
268 errMsg := fmt.Sprintf("%s command failed, status: %s, reason: %s", cmd, status.Status, status.Message)
269 klog.Errorf(errMsg)
270 return nil, fmt.Errorf("%s", errMsg)
271 }
272
273 return &status, nil
274 }
275
View as plain text