1/* 2Copyright 2017 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package flexvolume 18 19import ( 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 36const ( 37 flexVolumePluginName = "kubernetes.io/flexvolume" 38) 39 40// FlexVolumePlugin object. 41type 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 52type flexVolumeAttachablePlugin struct { 53 *flexVolumePlugin 54} 55 56var _ volume.AttachableVolumePlugin = &flexVolumeAttachablePlugin{} 57var _ volume.PersistentVolumePlugin = &flexVolumePlugin{} 58var _ volume.NodeExpandableVolumePlugin = &flexVolumePlugin{} 59var _ volume.ExpandableVolumePlugin = &flexVolumePlugin{} 60 61var _ volume.DeviceMountableVolumePlugin = &flexVolumeAttachablePlugin{} 62 63// PluginFactory create flex volume plugin 64type PluginFactory interface { 65 NewFlexVolumePlugin(pluginDir, driverName string, runner exec.Interface) (volume.VolumePlugin, error) 66} 67 68type pluginFactory struct{} 69 70func (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. 98func (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 104func (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. 115func (plugin *flexVolumePlugin) GetPluginName() string { 116 return plugin.driverName 117} 118 119// GetVolumeName is part of the volume.VolumePlugin interface. 120func (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. 142func (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. 151func (plugin *flexVolumePlugin) RequiresRemount(spec *volume.Spec) bool { 152 return false 153} 154 155// GetAccessModes gets the allowed access modes for this plugin. 156func (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. 164func (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. 169func (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. 208func (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. 213func (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. 237func (plugin *flexVolumeAttachablePlugin) NewAttacher() (volume.Attacher, error) { 238 return &flexVolumeAttacher{plugin}, nil 239} 240 241func (plugin *flexVolumeAttachablePlugin) NewDeviceMounter() (volume.DeviceMounter, error) { 242 return plugin.NewAttacher() 243} 244 245// NewDetacher is part of the volume.AttachableVolumePlugin interface. 246func (plugin *flexVolumeAttachablePlugin) NewDetacher() (volume.Detacher, error) { 247 return &flexVolumeDetacher{plugin}, nil 248} 249 250func (plugin *flexVolumeAttachablePlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { 251 return plugin.NewDetacher() 252} 253 254func (plugin *flexVolumeAttachablePlugin) CanAttach(spec *volume.Spec) (bool, error) { 255 return true, nil 256} 257 258func (plugin *flexVolumeAttachablePlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { 259 return true, nil 260} 261 262// ConstructVolumeSpec is part of the volume.AttachableVolumePlugin interface. 263func (plugin *flexVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, 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.NewSpecFromVolume(flexVolume), nil 273} 274 275func (plugin *flexVolumePlugin) SupportsMountOption() bool { 276 return false 277} 278 279// Mark the given commands as unsupported. 280func (plugin *flexVolumePlugin) unsupported(commands ...string) { 281 plugin.Lock() 282 defer plugin.Unlock() 283 plugin.unsupportedCommands = append(plugin.unsupportedCommands, commands...) 284} 285 286func (plugin *flexVolumePlugin) SupportsBulkVolumeVerification() bool { 287 return false 288} 289 290// Returns true iff the given command is known to be unsupported. 291func (plugin *flexVolumePlugin) isUnsupported(command string) bool { 292 plugin.Lock() 293 defer plugin.Unlock() 294 for _, unsupportedCommand := range plugin.unsupportedCommands { 295 if command == unsupportedCommand { 296 return true 297 } 298 } 299 return false 300} 301 302func (plugin *flexVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { 303 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 304 return mounter.GetMountRefs(deviceMountPath) 305} 306 307func (plugin *flexVolumePlugin) getDeviceMountPath(spec *volume.Spec) (string, error) { 308 volumeName, err := plugin.GetVolumeName(spec) 309 if err != nil { 310 return "", fmt.Errorf("GetVolumeName failed from getDeviceMountPath: %s", err) 311 } 312 313 mountsDir := filepath.Join(plugin.host.GetPluginDir(flexVolumePluginName), plugin.driverName, "mounts") 314 return filepath.Join(mountsDir, volumeName), nil 315} 316 317func (plugin *flexVolumePlugin) RequiresFSResize() bool { 318 return plugin.capabilities.RequiresFSResize 319} 320