123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package storageos
- import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "k8s.io/klog"
- utilexec "k8s.io/utils/exec"
- "k8s.io/utils/mount"
- utilstrings "k8s.io/utils/strings"
- v1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/resource"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
- clientset "k8s.io/client-go/kubernetes"
- volumehelpers "k8s.io/cloud-provider/volume/helpers"
- "k8s.io/kubernetes/pkg/volume"
- "k8s.io/kubernetes/pkg/volume/util"
- )
- // ProbeVolumePlugins is the primary entrypoint for volume plugins.
- func ProbeVolumePlugins() []volume.VolumePlugin {
- return []volume.VolumePlugin{&storageosPlugin{nil}}
- }
- type storageosPlugin struct {
- host volume.VolumeHost
- }
- var _ volume.VolumePlugin = &storageosPlugin{}
- var _ volume.PersistentVolumePlugin = &storageosPlugin{}
- var _ volume.DeletableVolumePlugin = &storageosPlugin{}
- var _ volume.ProvisionableVolumePlugin = &storageosPlugin{}
- const (
- storageosPluginName = "kubernetes.io/storageos"
- defaultDeviceDir = "/var/lib/storageos/volumes"
- defaultAPIAddress = "tcp://localhost:5705"
- defaultAPIUser = "storageos"
- defaultAPIPassword = "storageos"
- defaultAPIVersion = "1"
- defaultFSType = "ext4"
- defaultNamespace = "default"
- )
- func getPath(uid types.UID, volNamespace string, volName string, pvName string, host volume.VolumeHost) string {
- if len(volNamespace) != 0 && len(volName) != 0 && strings.Count(volName, ".") == 0 {
- return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(storageosPluginName), pvName+"."+volNamespace+"."+volName)
- }
- return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(storageosPluginName), pvName)
- }
- func (plugin *storageosPlugin) Init(host volume.VolumeHost) error {
- plugin.host = host
- return nil
- }
- func (plugin *storageosPlugin) GetPluginName() string {
- return storageosPluginName
- }
- func (plugin *storageosPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
- volumeSource, _, err := getVolumeSource(spec)
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%s/%s", volumeSource.VolumeNamespace, volumeSource.VolumeName), nil
- }
- func (plugin *storageosPlugin) CanSupport(spec *volume.Spec) bool {
- return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS != nil) ||
- (spec.Volume != nil && spec.Volume.StorageOS != nil)
- }
- func (plugin *storageosPlugin) RequiresRemount() bool {
- return false
- }
- func (plugin *storageosPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
- return []v1.PersistentVolumeAccessMode{
- v1.ReadWriteOnce,
- v1.ReadOnlyMany,
- }
- }
- func (plugin *storageosPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
- apiCfg, err := getAPICfg(spec, pod, plugin.host.GetKubeClient())
- if err != nil {
- return nil, err
- }
- return plugin.newMounterInternal(spec, pod, apiCfg, &storageosUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
- }
- func (plugin *storageosPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, apiCfg *storageosAPIConfig, manager storageosManager, mounter mount.Interface, exec utilexec.Interface) (volume.Mounter, error) {
- volName, volNamespace, fsType, readOnly, err := getVolumeInfoFromSpec(spec)
- if err != nil {
- return nil, err
- }
- return &storageosMounter{
- storageos: &storageos{
- podUID: pod.UID,
- podNamespace: pod.GetNamespace(),
- pvName: spec.Name(),
- volName: volName,
- volNamespace: volNamespace,
- fsType: fsType,
- readOnly: readOnly,
- apiCfg: apiCfg,
- manager: manager,
- mounter: mounter,
- exec: exec,
- plugin: plugin,
- MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, volNamespace, volName, spec.Name(), plugin.host)),
- },
- diskMounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec},
- mountOptions: util.MountOptionFromSpec(spec),
- }, nil
- }
- func (plugin *storageosPlugin) NewUnmounter(pvName string, podUID types.UID) (volume.Unmounter, error) {
- return plugin.newUnmounterInternal(pvName, podUID, &storageosUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
- }
- func (plugin *storageosPlugin) newUnmounterInternal(pvName string, podUID types.UID, manager storageosManager, mounter mount.Interface, exec utilexec.Interface) (volume.Unmounter, error) {
- // Parse volume namespace & name from mountpoint if mounted
- volNamespace, volName, err := getVolumeInfo(pvName, podUID, plugin.host)
- if err != nil {
- return nil, err
- }
- return &storageosUnmounter{
- storageos: &storageos{
- podUID: podUID,
- pvName: pvName,
- volName: volName,
- volNamespace: volNamespace,
- manager: manager,
- mounter: mounter,
- exec: exec,
- plugin: plugin,
- MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volNamespace, volName, pvName, plugin.host)),
- },
- }, nil
- }
- func (plugin *storageosPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
- if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS == nil {
- return nil, fmt.Errorf("spec.PersistentVolumeSource.StorageOS is nil")
- }
- class, err := util.GetClassForVolume(plugin.host.GetKubeClient(), spec.PersistentVolume)
- if err != nil {
- return nil, err
- }
- var adminSecretName, adminSecretNamespace string
- for k, v := range class.Parameters {
- switch strings.ToLower(k) {
- case "adminsecretname":
- adminSecretName = v
- case "adminsecretnamespace":
- adminSecretNamespace = v
- }
- }
- apiCfg, err := parsePVSecret(adminSecretNamespace, adminSecretName, plugin.host.GetKubeClient())
- if err != nil {
- return nil, fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err)
- }
- return plugin.newDeleterInternal(spec, apiCfg, &storageosUtil{})
- }
- func (plugin *storageosPlugin) newDeleterInternal(spec *volume.Spec, apiCfg *storageosAPIConfig, manager storageosManager) (volume.Deleter, error) {
- return &storageosDeleter{
- storageosMounter: &storageosMounter{
- storageos: &storageos{
- pvName: spec.Name(),
- volName: spec.PersistentVolume.Spec.StorageOS.VolumeName,
- volNamespace: spec.PersistentVolume.Spec.StorageOS.VolumeNamespace,
- apiCfg: apiCfg,
- manager: manager,
- plugin: plugin,
- },
- },
- pvUID: spec.PersistentVolume.UID,
- }, nil
- }
- func (plugin *storageosPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
- return plugin.newProvisionerInternal(options, &storageosUtil{})
- }
- func (plugin *storageosPlugin) newProvisionerInternal(options volume.VolumeOptions, manager storageosManager) (volume.Provisioner, error) {
- return &storageosProvisioner{
- storageosMounter: &storageosMounter{
- storageos: &storageos{
- manager: manager,
- plugin: plugin,
- },
- },
- options: options,
- }, nil
- }
- func (plugin *storageosPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
- volNamespace, volName, err := getVolumeFromRef(volumeName)
- if err != nil {
- volNamespace = defaultNamespace
- volName = volumeName
- }
- storageosVolume := &v1.Volume{
- Name: volumeName,
- VolumeSource: v1.VolumeSource{
- StorageOS: &v1.StorageOSVolumeSource{
- VolumeName: volName,
- VolumeNamespace: volNamespace,
- },
- },
- }
- return volume.NewSpecFromVolume(storageosVolume), nil
- }
- func (plugin *storageosPlugin) SupportsMountOption() bool {
- return true
- }
- func (plugin *storageosPlugin) SupportsBulkVolumeVerification() bool {
- return false
- }
- func getVolumeSource(spec *volume.Spec) (*v1.StorageOSVolumeSource, bool, error) {
- if spec.Volume != nil && spec.Volume.StorageOS != nil {
- return spec.Volume.StorageOS, spec.Volume.StorageOS.ReadOnly, nil
- }
- return nil, false, fmt.Errorf("Spec does not reference a StorageOS volume type")
- }
- func getPersistentVolumeSource(spec *volume.Spec) (*v1.StorageOSPersistentVolumeSource, bool, error) {
- if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS != nil {
- return spec.PersistentVolume.Spec.StorageOS, spec.ReadOnly, nil
- }
- return nil, false, fmt.Errorf("Spec does not reference a StorageOS persistent volume type")
- }
- // storageosManager is the abstract interface to StorageOS volume ops.
- type storageosManager interface {
- // Connects to the StorageOS API using the supplied configuration.
- NewAPI(apiCfg *storageosAPIConfig) error
- // Creates a StorageOS volume.
- CreateVolume(provisioner *storageosProvisioner) (*storageosVolume, error)
- // Attaches the disk to the kubelet's host machine.
- AttachVolume(mounter *storageosMounter) (string, error)
- // Attaches the device to the host at a mount path.
- AttachDevice(mounter *storageosMounter, deviceMountPath string) error
- // Detaches the disk from the kubelet's host machine.
- DetachVolume(unmounter *storageosUnmounter, dir string) error
- // Mounts the disk on the Kubelet's host machine.
- MountVolume(mounter *storageosMounter, mnt, dir string) error
- // Unmounts the disk from the Kubelet's host machine.
- UnmountVolume(unounter *storageosUnmounter) error
- // Deletes the storageos volume. All data will be lost.
- DeleteVolume(deleter *storageosDeleter) error
- // Gets the node's device path.
- DeviceDir(mounter *storageosMounter) string
- }
- // storageos volumes represent a bare host directory mount of an StorageOS export.
- type storageos struct {
- podUID types.UID
- podNamespace string
- pvName string
- volName string
- volNamespace string
- secretName string
- readOnly bool
- description string
- pool string
- fsType string
- sizeGB int
- labels map[string]string
- apiCfg *storageosAPIConfig
- manager storageosManager
- mounter mount.Interface
- exec utilexec.Interface
- plugin *storageosPlugin
- volume.MetricsProvider
- }
- type storageosMounter struct {
- *storageos
- // The directory containing the StorageOS devices
- deviceDir string
- // Interface used to mount the file or block device
- diskMounter *mount.SafeFormatAndMount
- mountOptions []string
- }
- var _ volume.Mounter = &storageosMounter{}
- func (b *storageosMounter) GetAttributes() volume.Attributes {
- return volume.Attributes{
- ReadOnly: b.readOnly,
- Managed: !b.readOnly,
- SupportsSELinux: true,
- }
- }
- // Checks prior to mount operations to verify that the required components (binaries, etc.)
- // to mount the volume are available on the underlying node.
- // If not, it returns an error
- func (b *storageosMounter) CanMount() error {
- return nil
- }
- // SetUp attaches the disk and bind mounts to the volume path.
- func (b *storageosMounter) SetUp(mounterArgs volume.MounterArgs) error {
- // Need a namespace to find the volume, try pod's namespace if not set.
- if b.volNamespace == "" {
- klog.V(2).Infof("Setting StorageOS volume namespace to pod namespace: %s", b.podNamespace)
- b.volNamespace = b.podNamespace
- }
- targetPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
- // Attach the device to the host.
- if err := b.manager.AttachDevice(b, targetPath); err != nil {
- klog.Errorf("Failed to attach device at %s: %s", targetPath, err.Error())
- return err
- }
- // Attach the StorageOS volume as a block device
- devicePath, err := b.manager.AttachVolume(b)
- if err != nil {
- klog.Errorf("Failed to attach StorageOS volume %s: %s", b.volName, err.Error())
- return err
- }
- // Mount the loop device into the plugin's disk global mount dir.
- err = b.manager.MountVolume(b, devicePath, targetPath)
- if err != nil {
- return err
- }
- klog.V(4).Infof("Successfully mounted StorageOS volume %s into global mount directory", b.volName)
- // Bind mount the volume into the pod
- return b.SetUpAt(b.GetPath(), mounterArgs)
- }
- // SetUp bind mounts the disk global mount to the give volume path.
- func (b *storageosMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
- notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
- klog.V(4).Infof("StorageOS volume set up: %s %v %v", dir, !notMnt, err)
- if err != nil && !os.IsNotExist(err) {
- klog.Errorf("Cannot validate mount point: %s %v", dir, err)
- return err
- }
- if !notMnt {
- return nil
- }
- if err = os.MkdirAll(dir, 0750); err != nil {
- klog.Errorf("mkdir failed on disk %s (%v)", dir, err)
- return err
- }
- // Perform a bind mount to the full path to allow duplicate mounts of the same PD.
- options := []string{"bind"}
- if b.readOnly {
- options = append(options, "ro")
- }
- mountOptions := util.JoinMountOptions(b.mountOptions, options)
- globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
- klog.V(4).Infof("Attempting to bind mount to pod volume at %s", dir)
- err = b.mounter.Mount(globalPDPath, dir, "", mountOptions)
- if err != nil {
- notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
- if mntErr != nil {
- klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
- return err
- }
- if !notMnt {
- if mntErr = b.mounter.Unmount(dir); mntErr != nil {
- klog.Errorf("Failed to unmount: %v", mntErr)
- return err
- }
- notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
- if mntErr != nil {
- klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
- return err
- }
- if !notMnt {
- klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir)
- return err
- }
- }
- os.Remove(dir)
- klog.Errorf("Mount of disk %s failed: %v", dir, err)
- return err
- }
- if !b.readOnly {
- volume.SetVolumeOwnership(b, mounterArgs.FsGroup)
- }
- klog.V(4).Infof("StorageOS volume setup complete on %s", dir)
- return nil
- }
- func makeGlobalPDName(host volume.VolumeHost, pvName, volNamespace, volName string) string {
- return filepath.Join(host.GetPluginDir(utilstrings.EscapeQualifiedName(storageosPluginName)), util.MountsInGlobalPDPath, pvName+"."+volNamespace+"."+volName)
- }
- // Given the pod id and PV name, finds the volume's namespace and name from the
- // name or volume mount. We mount as volNamespace.pvName, but k8s will specify
- // only the pvName to unmount.
- // Will return empty volNamespace/pvName if the volume is not mounted.
- func getVolumeInfo(pvName string, podUID types.UID, host volume.VolumeHost) (string, string, error) {
- if volNamespace, volName, err := getVolumeFromRef(pvName); err == nil {
- return volNamespace, volName, nil
- }
- volumeDir := filepath.Dir(host.GetPodVolumeDir(podUID, utilstrings.EscapeQualifiedName(storageosPluginName), pvName))
- files, err := ioutil.ReadDir(volumeDir)
- if err != nil {
- return "", "", fmt.Errorf("Could not read mounts from pod volume dir: %s", err)
- }
- for _, f := range files {
- if f.Mode().IsDir() && strings.HasPrefix(f.Name(), pvName+".") {
- if volNamespace, volName, err := getVolumeFromRef(f.Name()); err == nil {
- return volNamespace, volName, nil
- }
- }
- }
- return "", "", fmt.Errorf("Could not get info from unmounted pv %q at %q", pvName, volumeDir)
- }
- // Splits the volume ref on "." to return the volNamespace and pvName. Neither
- // namespaces nor service names allow "." in their names.
- func getVolumeFromRef(ref string) (volNamespace string, volName string, err error) {
- refParts := strings.Split(ref, ".")
- switch len(refParts) {
- case 2:
- return refParts[0], refParts[1], nil
- case 3:
- return refParts[1], refParts[2], nil
- }
- return "", "", fmt.Errorf("ref not in format volNamespace.volName or pvName.volNamespace.volName")
- }
- // GetPath returns the path to the user specific mount of a StorageOS volume
- func (storageosVolume *storageos) GetPath() string {
- return getPath(storageosVolume.podUID, storageosVolume.volNamespace, storageosVolume.volName, storageosVolume.pvName, storageosVolume.plugin.host)
- }
- type storageosUnmounter struct {
- *storageos
- }
- var _ volume.Unmounter = &storageosUnmounter{}
- func (b *storageosUnmounter) GetPath() string {
- return getPath(b.podUID, b.volNamespace, b.volName, b.pvName, b.plugin.host)
- }
- // Unmounts the bind mount, and detaches the disk only if the PD
- // resource was the last reference to that disk on the kubelet.
- func (b *storageosUnmounter) TearDown() error {
- if len(b.volNamespace) == 0 || len(b.volName) == 0 {
- klog.Warningf("volNamespace: %q, volName: %q not set, skipping TearDown", b.volNamespace, b.volName)
- return fmt.Errorf("pvName not specified for TearDown, waiting for next sync loop")
- }
- // Unmount from pod
- mountPath := b.GetPath()
- err := b.TearDownAt(mountPath)
- if err != nil {
- klog.Errorf("Unmount from pod failed: %v", err)
- return err
- }
- // Find device name from global mount
- globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
- devicePath, _, err := mount.GetDeviceNameFromMount(b.mounter, globalPDPath)
- if err != nil {
- klog.Errorf("Detach failed when getting device from global mount: %v", err)
- return err
- }
- // Unmount from plugin's disk global mount dir.
- err = b.TearDownAt(globalPDPath)
- if err != nil {
- klog.Errorf("Detach failed during unmount: %v", err)
- return err
- }
- // Detach loop device
- err = b.manager.DetachVolume(b, devicePath)
- if err != nil {
- klog.Errorf("Detach device %s failed for volume %s: %v", devicePath, b.pvName, err)
- return err
- }
- klog.V(4).Infof("Successfully unmounted StorageOS volume %s and detached devices", b.pvName)
- return nil
- }
- // Unmounts the bind mount, and detaches the disk only if the PD
- // resource was the last reference to that disk on the kubelet.
- func (b *storageosUnmounter) TearDownAt(dir string) error {
- if err := mount.CleanupMountPoint(dir, b.mounter, false); err != nil {
- klog.V(4).Infof("Unmounted StorageOS volume %s failed with: %v", b.pvName, err)
- }
- if err := b.manager.UnmountVolume(b); err != nil {
- klog.V(4).Infof("Mount reference for volume %s could not be removed from StorageOS: %v", b.pvName, err)
- }
- return nil
- }
- type storageosDeleter struct {
- *storageosMounter
- pvUID types.UID
- }
- var _ volume.Deleter = &storageosDeleter{}
- func (d *storageosDeleter) GetPath() string {
- return getPath(d.podUID, d.volNamespace, d.volName, d.pvName, d.plugin.host)
- }
- func (d *storageosDeleter) Delete() error {
- return d.manager.DeleteVolume(d)
- }
- type storageosProvisioner struct {
- *storageosMounter
- options volume.VolumeOptions
- }
- var _ volume.Provisioner = &storageosProvisioner{}
- func (c *storageosProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
- if !util.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) {
- return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes())
- }
- if util.CheckPersistentVolumeClaimModeBlock(c.options.PVC) {
- return nil, fmt.Errorf("%s does not support block volume provisioning", c.plugin.GetPluginName())
- }
- var adminSecretName, adminSecretNamespace string
- // Apply ProvisionerParameters (case-insensitive). We leave validation of
- // the values to the cloud provider.
- for k, v := range c.options.Parameters {
- switch strings.ToLower(k) {
- case "adminsecretname":
- adminSecretName = v
- case "adminsecretnamespace":
- adminSecretNamespace = v
- case "volumenamespace":
- c.volNamespace = v
- case "description":
- c.description = v
- case "pool":
- c.pool = v
- case "fstype":
- c.fsType = v
- default:
- return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, c.plugin.GetPluginName())
- }
- }
- // Set from PVC
- c.podNamespace = c.options.PVC.Namespace
- c.volName = c.options.PVName
- if c.volNamespace == "" {
- c.volNamespace = c.options.PVC.Namespace
- }
- c.labels = make(map[string]string)
- for k, v := range c.options.PVC.Labels {
- c.labels[k] = v
- }
- capacity := c.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
- var err error
- c.sizeGB, err = volumehelpers.RoundUpToGiBInt(capacity)
- if err != nil {
- return nil, err
- }
- apiCfg, err := parsePVSecret(adminSecretNamespace, adminSecretName, c.plugin.host.GetKubeClient())
- if err != nil {
- return nil, err
- }
- c.apiCfg = apiCfg
- vol, err := c.manager.CreateVolume(c)
- if err != nil {
- klog.Errorf("failed to create volume: %v", err)
- return nil, err
- }
- if vol.FSType == "" {
- vol.FSType = defaultFSType
- }
- pv := &v1.PersistentVolume{
- ObjectMeta: metav1.ObjectMeta{
- Name: vol.Name,
- Labels: map[string]string{},
- Annotations: map[string]string{
- util.VolumeDynamicallyCreatedByKey: "storageos-dynamic-provisioner",
- },
- },
- Spec: v1.PersistentVolumeSpec{
- PersistentVolumeReclaimPolicy: c.options.PersistentVolumeReclaimPolicy,
- AccessModes: c.options.PVC.Spec.AccessModes,
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", vol.SizeGB)),
- },
- PersistentVolumeSource: v1.PersistentVolumeSource{
- StorageOS: &v1.StorageOSPersistentVolumeSource{
- VolumeName: vol.Name,
- VolumeNamespace: vol.Namespace,
- FSType: vol.FSType,
- ReadOnly: false,
- SecretRef: &v1.ObjectReference{
- Name: adminSecretName,
- Namespace: adminSecretNamespace,
- },
- },
- },
- MountOptions: c.options.MountOptions,
- },
- }
- if len(c.options.PVC.Spec.AccessModes) == 0 {
- pv.Spec.AccessModes = c.plugin.GetAccessModes()
- }
- if len(vol.Labels) != 0 {
- if pv.Labels == nil {
- pv.Labels = make(map[string]string)
- }
- for k, v := range vol.Labels {
- pv.Labels[k] = v
- }
- }
- return pv, nil
- }
- // Returns StorageOS volume name, namespace, fstype and readonly from spec
- func getVolumeInfoFromSpec(spec *volume.Spec) (string, string, string, bool, error) {
- if spec.PersistentVolume != nil {
- source, readOnly, err := getPersistentVolumeSource(spec)
- if err != nil {
- return "", "", "", false, err
- }
- return source.VolumeName, source.VolumeNamespace, source.FSType, readOnly, nil
- }
- if spec.Volume != nil {
- source, readOnly, err := getVolumeSource(spec)
- if err != nil {
- return "", "", "", false, err
- }
- return source.VolumeName, source.VolumeNamespace, source.FSType, readOnly, nil
- }
- return "", "", "", false, fmt.Errorf("spec not Volume or PersistentVolume")
- }
- // Returns API config if secret set, otherwise empty struct so defaults can be
- // attempted.
- func getAPICfg(spec *volume.Spec, pod *v1.Pod, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
- if spec.PersistentVolume != nil {
- source, _, err := getPersistentVolumeSource(spec)
- if err != nil {
- return nil, err
- }
- if source.SecretRef == nil {
- return nil, nil
- }
- return parsePVSecret(source.SecretRef.Namespace, source.SecretRef.Name, kubeClient)
- }
- if spec.Volume != nil {
- source, _, err := getVolumeSource(spec)
- if err != nil {
- return nil, err
- }
- if source.SecretRef == nil {
- return nil, nil
- }
- return parsePodSecret(pod, source.SecretRef.Name, kubeClient)
- }
- return nil, fmt.Errorf("spec not Volume or PersistentVolume")
- }
- func parsePodSecret(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
- secret, err := util.GetSecretForPod(pod, secretName, kubeClient)
- if err != nil {
- klog.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
- return nil, fmt.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
- }
- return parseAPIConfig(secret)
- }
- // Important: Only to be called with data from a PV to avoid secrets being
- // loaded from a user-suppler namespace.
- func parsePVSecret(namespace, secretName string, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
- secret, err := util.GetSecretForPV(namespace, secretName, storageosPluginName, kubeClient)
- if err != nil {
- klog.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
- return nil, fmt.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
- }
- return parseAPIConfig(secret)
- }
- // Parse API configuration from parameters or secret
- func parseAPIConfig(params map[string]string) (*storageosAPIConfig, error) {
- if len(params) == 0 {
- return nil, fmt.Errorf("empty API config")
- }
- c := &storageosAPIConfig{}
- for name, data := range params {
- switch strings.ToLower(name) {
- case "apiaddress":
- c.apiAddr = string(data)
- case "apiusername":
- c.apiUser = string(data)
- case "apipassword":
- c.apiPass = string(data)
- case "apiversion":
- c.apiVersion = string(data)
- }
- }
- return c, nil
- }
|