diff options
Diffstat (limited to 'vendor/k8s.io/kubernetes/pkg/volume/plugins.go')
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/plugins.go | 242 |
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 } |