summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/kubernetes/pkg/volume/plugins.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/k8s.io/kubernetes/pkg/volume/plugins.go')
-rw-r--r--vendor/k8s.io/kubernetes/pkg/volume/plugins.go242
1 files changed, 223 insertions, 19 deletions
diff --git a/vendor/k8s.io/kubernetes/pkg/volume/plugins.go b/vendor/k8s.io/kubernetes/pkg/volume/plugins.go
index 41721d1ee..ec4ec5791 100644
--- a/vendor/k8s.io/kubernetes/pkg/volume/plugins.go
+++ b/vendor/k8s.io/kubernetes/pkg/volume/plugins.go
@@ -23,15 +23,25 @@ import (
"sync"
"github.com/golang/glog"
+ "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/validation"
- "k8s.io/kubernetes/pkg/api/v1"
- "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
+ clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/util/io"
"k8s.io/kubernetes/pkg/util/mount"
+ "k8s.io/kubernetes/pkg/volume/util/recyclerclient"
+)
+
+const (
+ // Common parameter which can be specified in StorageClass to specify the desired FSType
+ // Provisioners SHOULD implement support for this if they are block device based
+ // Must be a filesystem type supported by the host operating system.
+ // Ex. "ext4", "xfs", "ntfs". Default value depends on the provisioner
+ VolumeParameterFSType = "fstype"
)
// VolumeOptions contains option information about a volume.
@@ -42,6 +52,8 @@ type VolumeOptions struct {
// Reclamation policy for a persistent volume
PersistentVolumeReclaimPolicy v1.PersistentVolumeReclaimPolicy
+ // Mount options for a persistent volume
+ MountOptions []string
// Suggested PV.Name of the PersistentVolume to provision.
// This is a generated name guaranteed to be unique in Kubernetes cluster.
// If you choose not to use it as volume name, ensure uniqueness by either
@@ -60,6 +72,17 @@ type VolumeOptions struct {
Parameters map[string]string
}
+type DynamicPluginProber interface {
+ Init() error
+
+ // If an update has occurred since the last probe, updated = true
+ // and the list of probed plugins is returned.
+ // Otherwise, update = false and probedPlugins = nil.
+ //
+ // If an error occurs, updated and probedPlugins are undefined.
+ Probe() (updated bool, probedPlugins []VolumePlugin, err error)
+}
+
// VolumePlugin is an interface to volume plugins that can be used on a
// kubernetes node (e.g. by kubelet) to instantiate and manage volumes.
type VolumePlugin interface {
@@ -139,7 +162,7 @@ type RecyclableVolumePlugin interface {
// Recycle will use the provided recorder to write any events that might be
// interesting to user. It's expected that caller will pass these events to
// the PV being recycled.
- Recycle(pvName string, spec *Spec, eventRecorder RecycleEventRecorder) error
+ Recycle(pvName string, spec *Spec, eventRecorder recyclerclient.RecycleEventRecorder) error
}
// DeletableVolumePlugin is an extended interface of VolumePlugin and is used
@@ -178,6 +201,34 @@ type AttachableVolumePlugin interface {
GetDeviceMountRefs(deviceMountPath string) ([]string, error)
}
+// ExpandableVolumePlugin is an extended interface of VolumePlugin and is used for volumes that can be
+// expanded
+type ExpandableVolumePlugin interface {
+ VolumePlugin
+ ExpandVolumeDevice(spec *Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error)
+ RequiresFSResize() bool
+}
+
+// BlockVolumePlugin is an extend interface of VolumePlugin and is used for block volumes support.
+type BlockVolumePlugin interface {
+ VolumePlugin
+ // NewBlockVolumeMapper creates a new volume.BlockVolumeMapper from an API specification.
+ // Ownership of the spec pointer in *not* transferred.
+ // - spec: The v1.Volume spec
+ // - pod: The enclosing pod
+ NewBlockVolumeMapper(spec *Spec, podRef *v1.Pod, opts VolumeOptions) (BlockVolumeMapper, error)
+ // NewBlockVolumeUnmapper creates a new volume.BlockVolumeUnmapper from recoverable state.
+ // - name: The volume name, as per the v1.Volume spec.
+ // - podUID: The UID of the enclosing pod
+ NewBlockVolumeUnmapper(name string, podUID types.UID) (BlockVolumeUnmapper, error)
+ // ConstructBlockVolumeSpec constructs a volume spec based on the given
+ // podUID, volume name and a pod device map path.
+ // The spec may have incomplete information due to limited information
+ // from input. This function is used by volume manager to reconstruct
+ // volume spec by reading the volume directories from disk.
+ ConstructBlockVolumeSpec(podUID types.UID, volumeName, mountPath string) (*Spec, error)
+}
+
// VolumeHost is an interface that plugins can use to access the kubelet.
type VolumeHost interface {
// GetPluginDir returns the absolute path to a directory under which
@@ -186,6 +237,11 @@ type VolumeHost interface {
// GetPodPluginDir().
GetPluginDir(pluginName string) string
+ // GetVolumeDevicePluginDir returns the absolute path to a directory
+ // under which a given plugin may store data.
+ // ex. plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}/
+ GetVolumeDevicePluginDir(pluginName string) string
+
// GetPodVolumeDir returns the absolute path a directory which
// represents the named volume under the named plugin for the given
// pod. If the specified pod does not exist, the result of this call
@@ -198,6 +254,13 @@ type VolumeHost interface {
// directory might not actually exist on disk yet.
GetPodPluginDir(podUID types.UID, pluginName string) string
+ // GetPodVolumeDeviceDir returns the absolute path a directory which
+ // represents the named plugin for the given pod.
+ // If the specified pod does not exist, the result of this call
+ // might not exist.
+ // ex. pods/{podUid}/{DefaultKubeletVolumeDevicesDirName}/{escapeQualifiedPluginName}/
+ GetPodVolumeDeviceDir(podUID types.UID, pluginName string) string
+
// GetKubeClient returns a client interface
GetKubeClient() clientset.Interface
@@ -216,7 +279,7 @@ type VolumeHost interface {
GetCloudProvider() cloudprovider.Interface
// Get mounter interface.
- GetMounter() mount.Interface
+ GetMounter(pluginName string) mount.Interface
// Get writer interface for writing data to disk.
GetWriter() io.Writer
@@ -236,15 +299,23 @@ type VolumeHost interface {
// Returns a function that returns a configmap.
GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error)
+ // Returns an interface that should be used to execute any utilities in volume plugins
+ GetExec(pluginName string) mount.Exec
+
// Returns the labels on the node
GetNodeLabels() (map[string]string, error)
+
+ // Returns the name of the node
+ GetNodeName() types.NodeName
}
// VolumePluginMgr tracks registered plugins.
type VolumePluginMgr struct {
- mutex sync.Mutex
- plugins map[string]VolumePlugin
- Host VolumeHost
+ mutex sync.Mutex
+ plugins map[string]VolumePlugin
+ prober DynamicPluginProber
+ probedPlugins []VolumePlugin
+ Host VolumeHost
}
// Spec is an internal representation of a volume. All API volume types translate to Spec.
@@ -339,11 +410,24 @@ func NewSpecFromPersistentVolume(pv *v1.PersistentVolume, readOnly bool) *Spec {
// InitPlugins initializes each plugin. All plugins must have unique names.
// This must be called exactly once before any New* methods are called on any
// plugins.
-func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, host VolumeHost) error {
+func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, prober DynamicPluginProber, host VolumeHost) error {
pm.mutex.Lock()
defer pm.mutex.Unlock()
pm.Host = host
+
+ if prober == nil {
+ // Use a dummy prober to prevent nil deference.
+ pm.prober = &dummyPluginProber{}
+ } else {
+ pm.prober = prober
+ }
+ if err := pm.prober.Init(); err != nil {
+ // Prober init failure should not affect the initialization of other plugins.
+ glog.Errorf("Error initializing dynamic plugin prober: %s", err)
+ pm.prober = &dummyPluginProber{}
+ }
+
if pm.plugins == nil {
pm.plugins = map[string]VolumePlugin{}
}
@@ -362,7 +446,7 @@ func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, host VolumeHost)
}
err := plugin.Init(host)
if err != nil {
- glog.Errorf("Failed to load volume plugin %s, error: %s", plugin, err.Error())
+ glog.Errorf("Failed to load volume plugin %s, error: %s", name, err.Error())
allErrs = append(allErrs, err)
continue
}
@@ -372,6 +456,21 @@ func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, host VolumeHost)
return utilerrors.NewAggregate(allErrs)
}
+func (pm *VolumePluginMgr) initProbedPlugin(probedPlugin VolumePlugin) error {
+ name := probedPlugin.GetPluginName()
+ if errs := validation.IsQualifiedName(name); len(errs) != 0 {
+ return fmt.Errorf("volume plugin has invalid name: %q: %s", name, strings.Join(errs, ";"))
+ }
+
+ err := probedPlugin.Init(pm.Host)
+ if err != nil {
+ return fmt.Errorf("Failed to load volume plugin %s, error: %s", name, err.Error())
+ }
+
+ glog.V(1).Infof("Loaded volume plugin %q", name)
+ return nil
+}
+
// FindPluginBySpec looks for a plugin that can support a given volume
// specification. If no plugins can support or more than one plugin can
// support it, return error.
@@ -379,19 +478,34 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
pm.mutex.Lock()
defer pm.mutex.Unlock()
- matches := []string{}
+ if spec == nil {
+ return nil, fmt.Errorf("Could not find plugin because volume spec is nil")
+ }
+
+ matchedPluginNames := []string{}
+ matches := []VolumePlugin{}
for k, v := range pm.plugins {
if v.CanSupport(spec) {
- matches = append(matches, k)
+ matchedPluginNames = append(matchedPluginNames, k)
+ matches = append(matches, v)
+ }
+ }
+
+ pm.refreshProbedPlugins()
+ for _, plugin := range pm.probedPlugins {
+ if plugin.CanSupport(spec) {
+ matchedPluginNames = append(matchedPluginNames, plugin.GetPluginName())
+ matches = append(matches, plugin)
}
}
+
if len(matches) == 0 {
return nil, fmt.Errorf("no volume plugin matched")
}
if len(matches) > 1 {
- return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matches, ","))
+ return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matchedPluginNames, ","))
}
- return pm.plugins[matches[0]], nil
+ return matches[0], nil
}
// FindPluginByName fetches a plugin by name or by legacy name. If no plugin
@@ -401,19 +515,52 @@ func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) {
defer pm.mutex.Unlock()
// Once we can get rid of legacy names we can reduce this to a map lookup.
- matches := []string{}
+ matchedPluginNames := []string{}
+ matches := []VolumePlugin{}
for k, v := range pm.plugins {
if v.GetPluginName() == name {
- matches = append(matches, k)
+ matchedPluginNames = append(matchedPluginNames, k)
+ matches = append(matches, v)
}
}
+
+ pm.refreshProbedPlugins()
+ for _, plugin := range pm.probedPlugins {
+ if plugin.GetPluginName() == name {
+ matchedPluginNames = append(matchedPluginNames, plugin.GetPluginName())
+ matches = append(matches, plugin)
+ }
+ }
+
if len(matches) == 0 {
return nil, fmt.Errorf("no volume plugin matched")
}
if len(matches) > 1 {
- return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matches, ","))
+ return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matchedPluginNames, ","))
+ }
+ return matches[0], nil
+}
+
+// Check if probedPlugin cache update is required.
+// If it is, initialize all probed plugins and replace the cache with them.
+func (pm *VolumePluginMgr) refreshProbedPlugins() {
+ updated, plugins, err := pm.prober.Probe()
+ if err != nil {
+ glog.Errorf("Error dynamically probing plugins: %s", err)
+ return // Use cached plugins upon failure.
+ }
+
+ if updated {
+ pm.probedPlugins = []VolumePlugin{}
+ for _, plugin := range plugins {
+ if err := pm.initProbedPlugin(plugin); err != nil {
+ glog.Errorf("Error initializing dynamically probed plugin %s; error: %s",
+ plugin.GetPluginName(), err)
+ continue
+ }
+ pm.probedPlugins = append(pm.probedPlugins, plugin)
+ }
}
- return pm.plugins[matches[0]], nil
}
// FindPersistentPluginBySpec looks for a persistent volume plugin that can
@@ -508,7 +655,7 @@ func (pm *VolumePluginMgr) FindCreatablePluginBySpec(spec *Spec) (ProvisionableV
return nil, fmt.Errorf("no creatable volume plugin matched")
}
-// FindAttachablePluginBySpec fetches a persistent volume plugin by name.
+// FindAttachablePluginBySpec fetches a persistent volume plugin by spec.
// Unlike the other "FindPlugin" methods, this does not return error if no
// plugin is found. All volumes require a mounter and unmounter, but not
// every volume will have an attacher/detacher.
@@ -538,6 +685,58 @@ func (pm *VolumePluginMgr) FindAttachablePluginByName(name string) (AttachableVo
return nil, nil
}
+// FindExpandablePluginBySpec fetches a persistent volume plugin by spec.
+func (pm *VolumePluginMgr) FindExpandablePluginBySpec(spec *Spec) (ExpandableVolumePlugin, error) {
+ volumePlugin, err := pm.FindPluginBySpec(spec)
+ if err != nil {
+ return nil, err
+ }
+
+ if expandableVolumePlugin, ok := volumePlugin.(ExpandableVolumePlugin); ok {
+ return expandableVolumePlugin, nil
+ }
+ return nil, nil
+}
+
+// FindExpandablePluginBySpec fetches a persistent volume plugin by name.
+func (pm *VolumePluginMgr) FindExpandablePluginByName(name string) (ExpandableVolumePlugin, error) {
+ volumePlugin, err := pm.FindPluginByName(name)
+ if err != nil {
+ return nil, err
+ }
+
+ if expandableVolumePlugin, ok := volumePlugin.(ExpandableVolumePlugin); ok {
+ return expandableVolumePlugin, nil
+ }
+ return nil, nil
+}
+
+// FindMapperPluginBySpec fetches a block volume plugin by spec.
+func (pm *VolumePluginMgr) FindMapperPluginBySpec(spec *Spec) (BlockVolumePlugin, error) {
+ volumePlugin, err := pm.FindPluginBySpec(spec)
+ if err != nil {
+ return nil, err
+ }
+
+ if blockVolumePlugin, ok := volumePlugin.(BlockVolumePlugin); ok {
+ return blockVolumePlugin, nil
+ }
+ return nil, nil
+}
+
+// FindMapperPluginByName fetches a block volume plugin by name.
+func (pm *VolumePluginMgr) FindMapperPluginByName(name string) (BlockVolumePlugin, error) {
+ volumePlugin, err := pm.FindPluginByName(name)
+ if err != nil {
+ return nil, err
+ }
+
+ if blockVolumePlugin, ok := volumePlugin.(BlockVolumePlugin); ok {
+ return blockVolumePlugin, nil
+ }
+ return nil, nil
+}
+
// NewPersistentVolumeRecyclerPodTemplate creates a template for a recycler
// pod. By default, a recycler pod simply runs "rm -rf" on a volume and tests
// for emptiness. Most attributes of the template will be correct for most
@@ -574,7 +773,7 @@ func NewPersistentVolumeRecyclerPodTemplate() *v1.Pod {
Containers: []v1.Container{
{
Name: "pv-recycler",
- Image: "gcr.io/google_containers/busybox",
+ Image: "busybox:1.27",
Command: []string{"/bin/sh"},
Args: []string{"-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/* && test -z \"$(ls -A /scrub)\" || exit 1"},
VolumeMounts: []v1.VolumeMount{
@@ -601,3 +800,8 @@ func ValidateRecyclerPodTemplate(pod *v1.Pod) error {
}
return nil
}
+
+type dummyPluginProber struct{}
+
+func (*dummyPluginProber) Init() error { return nil }
+func (*dummyPluginProber) Probe() (bool, []VolumePlugin, error) { return false, nil, nil }