...

Source file src/k8s.io/kubernetes/pkg/volume/flexvolume/plugin.go

Documentation: k8s.io/kubernetes/pkg/volume/flexvolume

     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  }
   326  

View as plain text