k8s.io/kubernetes@v1.29.3/pkg/volume/flexvolume/plugin.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package flexvolume 18 19 import ( 20 "fmt" 21 "path/filepath" 22 "runtime" 23 "strings" 24 "sync" 25 26 api "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/types" 28 "k8s.io/klog/v2" 29 "k8s.io/kubernetes/pkg/volume" 30 "k8s.io/kubernetes/pkg/volume/util" 31 "k8s.io/mount-utils" 32 "k8s.io/utils/exec" 33 utilstrings "k8s.io/utils/strings" 34 ) 35 36 const ( 37 flexVolumePluginName = "kubernetes.io/flexvolume" 38 ) 39 40 // FlexVolumePlugin object. 41 type flexVolumePlugin struct { 42 driverName string 43 execPath string 44 host volume.VolumeHost 45 runner exec.Interface 46 47 sync.Mutex 48 unsupportedCommands []string 49 capabilities DriverCapabilities 50 } 51 52 type flexVolumeAttachablePlugin struct { 53 *flexVolumePlugin 54 } 55 56 var _ volume.AttachableVolumePlugin = &flexVolumeAttachablePlugin{} 57 var _ volume.PersistentVolumePlugin = &flexVolumePlugin{} 58 var _ volume.NodeExpandableVolumePlugin = &flexVolumePlugin{} 59 var _ volume.ExpandableVolumePlugin = &flexVolumePlugin{} 60 61 var _ volume.DeviceMountableVolumePlugin = &flexVolumeAttachablePlugin{} 62 63 // PluginFactory create flex volume plugin 64 type PluginFactory interface { 65 NewFlexVolumePlugin(pluginDir, driverName string, runner exec.Interface) (volume.VolumePlugin, error) 66 } 67 68 type pluginFactory struct{} 69 70 func (pluginFactory) NewFlexVolumePlugin(pluginDir, name string, runner exec.Interface) (volume.VolumePlugin, error) { 71 execPath := filepath.Join(pluginDir, name) 72 73 driverName := utilstrings.UnescapeQualifiedName(name) 74 75 flexPlugin := &flexVolumePlugin{ 76 driverName: driverName, 77 execPath: execPath, 78 runner: runner, 79 unsupportedCommands: []string{}, 80 } 81 82 // Initialize the plugin and probe the capabilities 83 call := flexPlugin.NewDriverCall(initCmd) 84 ds, err := call.Run() 85 if err != nil { 86 return nil, err 87 } 88 flexPlugin.capabilities = *ds.Capabilities 89 90 if flexPlugin.capabilities.Attach { 91 // Plugin supports attach/detach, so return flexVolumeAttachablePlugin 92 return &flexVolumeAttachablePlugin{flexVolumePlugin: flexPlugin}, nil 93 } 94 return flexPlugin, nil 95 } 96 97 // Init is part of the volume.VolumePlugin interface. 98 func (plugin *flexVolumePlugin) Init(host volume.VolumeHost) error { 99 plugin.host = host 100 // Hardwired 'success' as any errors from calling init() will be caught by NewFlexVolumePlugin() 101 return nil 102 } 103 104 func (plugin *flexVolumePlugin) getExecutable() string { 105 parts := strings.Split(plugin.driverName, "/") 106 execName := parts[len(parts)-1] 107 execPath := filepath.Join(plugin.execPath, execName) 108 if runtime.GOOS == "windows" { 109 execPath = util.GetWindowsPath(execPath) 110 } 111 return execPath 112 } 113 114 // Name is part of the volume.VolumePlugin interface. 115 func (plugin *flexVolumePlugin) GetPluginName() string { 116 return plugin.driverName 117 } 118 119 // GetVolumeName is part of the volume.VolumePlugin interface. 120 func (plugin *flexVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) { 121 call := plugin.NewDriverCall(getVolumeNameCmd) 122 call.AppendSpec(spec, plugin.host, nil) 123 124 _, err := call.Run() 125 if isCmdNotSupportedErr(err) { 126 return (*pluginDefaults)(plugin).GetVolumeName(spec) 127 } else if err != nil { 128 return "", err 129 } 130 131 name, err := (*pluginDefaults)(plugin).GetVolumeName(spec) 132 if err != nil { 133 return "", err 134 } 135 136 klog.V(4).Info(logPrefix(plugin), "GetVolumeName is not supported yet. Defaulting to PV or volume name: ", name) 137 138 return name, nil 139 } 140 141 // CanSupport is part of the volume.VolumePlugin interface. 142 func (plugin *flexVolumePlugin) CanSupport(spec *volume.Spec) bool { 143 sourceDriver, err := getDriver(spec) 144 if err != nil { 145 return false 146 } 147 return sourceDriver == plugin.driverName 148 } 149 150 // RequiresRemount is part of the volume.VolumePlugin interface. 151 func (plugin *flexVolumePlugin) RequiresRemount(spec *volume.Spec) bool { 152 return false 153 } 154 155 // GetAccessModes gets the allowed access modes for this plugin. 156 func (plugin *flexVolumePlugin) GetAccessModes() []api.PersistentVolumeAccessMode { 157 return []api.PersistentVolumeAccessMode{ 158 api.ReadWriteOnce, 159 api.ReadOnlyMany, 160 } 161 } 162 163 // NewMounter is part of the volume.VolumePlugin interface. 164 func (plugin *flexVolumePlugin) NewMounter(spec *volume.Spec, pod *api.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { 165 return plugin.newMounterInternal(spec, pod, plugin.host.GetMounter(plugin.GetPluginName()), plugin.runner) 166 } 167 168 // newMounterInternal is the internal mounter routine to build the volume. 169 func (plugin *flexVolumePlugin) newMounterInternal(spec *volume.Spec, pod *api.Pod, mounter mount.Interface, runner exec.Interface) (volume.Mounter, error) { 170 sourceDriver, err := getDriver(spec) 171 if err != nil { 172 return nil, err 173 } 174 175 readOnly, err := getReadOnly(spec) 176 if err != nil { 177 return nil, err 178 } 179 180 var metricsProvider volume.MetricsProvider 181 if plugin.capabilities.SupportsMetrics { 182 metricsProvider = volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir( 183 pod.UID, utilstrings.EscapeQualifiedName(sourceDriver), spec.Name())) 184 } else { 185 metricsProvider = &volume.MetricsNil{} 186 } 187 188 return &flexVolumeMounter{ 189 flexVolume: &flexVolume{ 190 driverName: sourceDriver, 191 execPath: plugin.getExecutable(), 192 mounter: mounter, 193 plugin: plugin, 194 podName: pod.Name, 195 podUID: pod.UID, 196 podNamespace: pod.Namespace, 197 podServiceAccountName: pod.Spec.ServiceAccountName, 198 volName: spec.Name(), 199 MetricsProvider: metricsProvider, 200 }, 201 runner: runner, 202 spec: spec, 203 readOnly: readOnly, 204 }, nil 205 } 206 207 // NewUnmounter is part of the volume.VolumePlugin interface. 208 func (plugin *flexVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) { 209 return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()), plugin.runner) 210 } 211 212 // newUnmounterInternal is the internal unmounter routine to clean the volume. 213 func (plugin *flexVolumePlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface, runner exec.Interface) (volume.Unmounter, error) { 214 var metricsProvider volume.MetricsProvider 215 if plugin.capabilities.SupportsMetrics { 216 metricsProvider = volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir( 217 podUID, utilstrings.EscapeQualifiedName(plugin.driverName), volName)) 218 } else { 219 metricsProvider = &volume.MetricsNil{} 220 } 221 222 return &flexVolumeUnmounter{ 223 flexVolume: &flexVolume{ 224 driverName: plugin.driverName, 225 execPath: plugin.getExecutable(), 226 mounter: mounter, 227 plugin: plugin, 228 podUID: podUID, 229 volName: volName, 230 MetricsProvider: metricsProvider, 231 }, 232 runner: runner, 233 }, nil 234 } 235 236 // NewAttacher is part of the volume.AttachableVolumePlugin interface. 237 func (plugin *flexVolumeAttachablePlugin) NewAttacher() (volume.Attacher, error) { 238 return &flexVolumeAttacher{plugin}, nil 239 } 240 241 func (plugin *flexVolumeAttachablePlugin) NewDeviceMounter() (volume.DeviceMounter, error) { 242 return plugin.NewAttacher() 243 } 244 245 // NewDetacher is part of the volume.AttachableVolumePlugin interface. 246 func (plugin *flexVolumeAttachablePlugin) NewDetacher() (volume.Detacher, error) { 247 return &flexVolumeDetacher{plugin}, nil 248 } 249 250 func (plugin *flexVolumeAttachablePlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { 251 return plugin.NewDetacher() 252 } 253 254 func (plugin *flexVolumeAttachablePlugin) CanAttach(spec *volume.Spec) (bool, error) { 255 return true, nil 256 } 257 258 func (plugin *flexVolumeAttachablePlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { 259 return true, nil 260 } 261 262 // ConstructVolumeSpec is part of the volume.AttachableVolumePlugin interface. 263 func (plugin *flexVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) { 264 flexVolume := &api.Volume{ 265 Name: volumeName, 266 VolumeSource: api.VolumeSource{ 267 FlexVolume: &api.FlexVolumeSource{ 268 Driver: plugin.driverName, 269 }, 270 }, 271 } 272 return volume.ReconstructedVolume{ 273 Spec: volume.NewSpecFromVolume(flexVolume), 274 }, nil 275 } 276 277 func (plugin *flexVolumePlugin) SupportsMountOption() bool { 278 return false 279 } 280 281 // Mark the given commands as unsupported. 282 func (plugin *flexVolumePlugin) unsupported(commands ...string) { 283 plugin.Lock() 284 defer plugin.Unlock() 285 plugin.unsupportedCommands = append(plugin.unsupportedCommands, commands...) 286 } 287 288 func (plugin *flexVolumePlugin) SupportsBulkVolumeVerification() bool { 289 return false 290 } 291 292 func (plugin *flexVolumePlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) { 293 return false, nil 294 } 295 296 // Returns true iff the given command is known to be unsupported. 297 func (plugin *flexVolumePlugin) isUnsupported(command string) bool { 298 plugin.Lock() 299 defer plugin.Unlock() 300 for _, unsupportedCommand := range plugin.unsupportedCommands { 301 if command == unsupportedCommand { 302 return true 303 } 304 } 305 return false 306 } 307 308 func (plugin *flexVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { 309 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 310 return mounter.GetMountRefs(deviceMountPath) 311 } 312 313 func (plugin *flexVolumePlugin) getDeviceMountPath(spec *volume.Spec) (string, error) { 314 volumeName, err := plugin.GetVolumeName(spec) 315 if err != nil { 316 return "", fmt.Errorf("GetVolumeName failed from getDeviceMountPath: %s", err) 317 } 318 319 mountsDir := filepath.Join(plugin.host.GetPluginDir(flexVolumePluginName), plugin.driverName, "mounts") 320 return filepath.Join(mountsDir, volumeName), nil 321 } 322 323 func (plugin *flexVolumePlugin) RequiresFSResize() bool { 324 return plugin.capabilities.RequiresFSResize 325 }