/pkg/volume/rbd/rbd.go
Go | 1127 lines | 924 code | 123 blank | 80 comment | 284 complexity | 520cafd5cd51f2b3d1f34aa707f19b21 MD5 | raw file
- /*
- Copyright 2014 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 rbd
- import (
- "fmt"
- "os"
- "path/filepath"
- "regexp"
- dstrings "strings"
- "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"
- "k8s.io/apimachinery/pkg/util/sets"
- "k8s.io/apimachinery/pkg/util/uuid"
- clientset "k8s.io/client-go/kubernetes"
- "k8s.io/kubernetes/pkg/util/mount"
- "k8s.io/kubernetes/pkg/util/strings"
- "k8s.io/kubernetes/pkg/volume"
- volutil "k8s.io/kubernetes/pkg/volume/util"
- "k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
- )
- var (
- supportedFeatures = sets.NewString("layering")
- )
- // This is the primary entrypoint for volume plugins.
- func ProbeVolumePlugins() []volume.VolumePlugin {
- return []volume.VolumePlugin{&rbdPlugin{}}
- }
- // rbdPlugin implements Volume.VolumePlugin.
- type rbdPlugin struct {
- host volume.VolumeHost
- }
- var _ volume.VolumePlugin = &rbdPlugin{}
- var _ volume.PersistentVolumePlugin = &rbdPlugin{}
- var _ volume.DeletableVolumePlugin = &rbdPlugin{}
- var _ volume.ProvisionableVolumePlugin = &rbdPlugin{}
- var _ volume.AttachableVolumePlugin = &rbdPlugin{}
- var _ volume.ExpandableVolumePlugin = &rbdPlugin{}
- var _ volume.BlockVolumePlugin = &rbdPlugin{}
- const (
- rbdPluginName = "kubernetes.io/rbd"
- secretKeyName = "key" // key name used in secret
- rbdImageFormat1 = "1"
- rbdImageFormat2 = "2"
- rbdDefaultAdminId = "admin"
- rbdDefaultAdminSecretNamespace = "default"
- rbdDefaultPool = "rbd"
- rbdDefaultUserId = rbdDefaultAdminId
- )
- func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
- return host.GetPodVolumeDir(uid, strings.EscapeQualifiedNameForDisk(rbdPluginName), volName)
- }
- func (plugin *rbdPlugin) Init(host volume.VolumeHost) error {
- plugin.host = host
- return nil
- }
- func (plugin *rbdPlugin) GetPluginName() string {
- return rbdPluginName
- }
- func (plugin *rbdPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
- pool, err := getVolumeSourcePool(spec)
- if err != nil {
- return "", err
- }
- img, err := getVolumeSourceImage(spec)
- if err != nil {
- return "", err
- }
- return fmt.Sprintf(
- "%v:%v",
- pool,
- img), nil
- }
- func (plugin *rbdPlugin) CanSupport(spec *volume.Spec) bool {
- return (spec.Volume != nil && spec.Volume.RBD != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD != nil)
- }
- func (plugin *rbdPlugin) RequiresRemount() bool {
- return false
- }
- func (plugin *rbdPlugin) SupportsMountOption() bool {
- return true
- }
- func (plugin *rbdPlugin) SupportsBulkVolumeVerification() bool {
- return false
- }
- func (plugin *rbdPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
- return []v1.PersistentVolumeAccessMode{
- v1.ReadWriteOnce,
- v1.ReadOnlyMany,
- }
- }
- type rbdVolumeExpander struct {
- *rbdMounter
- }
- func (plugin *rbdPlugin) getAdminAndSecret(spec *volume.Spec) (string, string, error) {
- class, err := volutil.GetClassForVolume(plugin.host.GetKubeClient(), spec.PersistentVolume)
- if err != nil {
- return "", "", err
- }
- adminSecretName := ""
- adminSecretNamespace := rbdDefaultAdminSecretNamespace
- admin := ""
- for k, v := range class.Parameters {
- switch dstrings.ToLower(k) {
- case "adminid":
- admin = v
- case "adminsecretname":
- adminSecretName = v
- case "adminsecretnamespace":
- adminSecretNamespace = v
- }
- }
- if admin == "" {
- admin = rbdDefaultAdminId
- }
- secret, err := parsePVSecret(adminSecretNamespace, adminSecretName, plugin.host.GetKubeClient())
- if err != nil {
- return admin, "", fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err)
- }
- return admin, secret, nil
- }
- func (plugin *rbdPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
- if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD == nil {
- return oldSize, fmt.Errorf("spec.PersistentVolumeSource.Spec.RBD is nil")
- }
- // get admin and secret
- admin, secret, err := plugin.getAdminAndSecret(spec)
- if err != nil {
- return oldSize, err
- }
- expander := &rbdVolumeExpander{
- rbdMounter: &rbdMounter{
- rbd: &rbd{
- volName: spec.Name(),
- Image: spec.PersistentVolume.Spec.RBD.RBDImage,
- Pool: spec.PersistentVolume.Spec.RBD.RBDPool,
- plugin: plugin,
- manager: &RBDUtil{},
- mounter: &mount.SafeFormatAndMount{Interface: plugin.host.GetMounter(plugin.GetPluginName())},
- exec: plugin.host.GetExec(plugin.GetPluginName()),
- },
- Mon: spec.PersistentVolume.Spec.RBD.CephMonitors,
- adminId: admin,
- adminSecret: secret,
- },
- }
- expandedSize, err := expander.ResizeImage(oldSize, newSize)
- if err != nil {
- return oldSize, err
- } else {
- return expandedSize, nil
- }
- }
- func (expander *rbdVolumeExpander) ResizeImage(oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error) {
- return expander.manager.ExpandImage(expander, oldSize, newSize)
- }
- func (plugin *rbdPlugin) RequiresFSResize() bool {
- return true
- }
- func (plugin *rbdPlugin) createMounterFromVolumeSpecAndPod(spec *volume.Spec, pod *v1.Pod) (*rbdMounter, error) {
- var err error
- mon, err := getVolumeSourceMonitors(spec)
- if err != nil {
- return nil, err
- }
- img, err := getVolumeSourceImage(spec)
- if err != nil {
- return nil, err
- }
- fstype, err := getVolumeSourceFSType(spec)
- if err != nil {
- return nil, err
- }
- pool, err := getVolumeSourcePool(spec)
- if err != nil {
- return nil, err
- }
- id, err := getVolumeSourceUser(spec)
- if err != nil {
- return nil, err
- }
- keyring, err := getVolumeSourceKeyRing(spec)
- if err != nil {
- return nil, err
- }
- ro, err := getVolumeSourceReadOnly(spec)
- if err != nil {
- return nil, err
- }
- ams, err := getVolumeAccessModes(spec)
- if err != nil {
- return nil, err
- }
- secretName, secretNs, err := getSecretNameAndNamespace(spec, pod.Namespace)
- if err != nil {
- return nil, err
- }
- secret := ""
- if len(secretName) > 0 && len(secretNs) > 0 {
- // if secret is provideded, retrieve it
- kubeClient := plugin.host.GetKubeClient()
- if kubeClient == nil {
- return nil, fmt.Errorf("Cannot get kube client")
- }
- secrets, err := kubeClient.CoreV1().Secrets(secretNs).Get(secretName, metav1.GetOptions{})
- if err != nil {
- err = fmt.Errorf("Couldn't get secret %v/%v err: %v", secretNs, secretName, err)
- return nil, err
- }
- for _, data := range secrets.Data {
- secret = string(data)
- }
- }
- return &rbdMounter{
- rbd: newRBD("", spec.Name(), img, pool, ro, plugin, &RBDUtil{}),
- Mon: mon,
- Id: id,
- Keyring: keyring,
- Secret: secret,
- fsType: fstype,
- accessModes: ams,
- }, nil
- }
- func (plugin *rbdPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
- secretName, secretNs, err := getSecretNameAndNamespace(spec, pod.Namespace)
- if err != nil {
- return nil, err
- }
- secret := ""
- if len(secretName) > 0 && len(secretNs) > 0 {
- // if secret is provideded, retrieve it
- kubeClient := plugin.host.GetKubeClient()
- if kubeClient == nil {
- return nil, fmt.Errorf("Cannot get kube client")
- }
- secrets, err := kubeClient.CoreV1().Secrets(secretNs).Get(secretName, metav1.GetOptions{})
- if err != nil {
- err = fmt.Errorf("Couldn't get secret %v/%v err: %v", secretNs, secretName, err)
- return nil, err
- }
- for _, data := range secrets.Data {
- secret = string(data)
- }
- }
- // Inject real implementations here, test through the internal function.
- return plugin.newMounterInternal(spec, pod.UID, &RBDUtil{}, secret)
- }
- func (plugin *rbdPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, secret string) (volume.Mounter, error) {
- mon, err := getVolumeSourceMonitors(spec)
- if err != nil {
- return nil, err
- }
- img, err := getVolumeSourceImage(spec)
- if err != nil {
- return nil, err
- }
- fstype, err := getVolumeSourceFSType(spec)
- if err != nil {
- return nil, err
- }
- pool, err := getVolumeSourcePool(spec)
- if err != nil {
- return nil, err
- }
- id, err := getVolumeSourceUser(spec)
- if err != nil {
- return nil, err
- }
- keyring, err := getVolumeSourceKeyRing(spec)
- if err != nil {
- return nil, err
- }
- ro, err := getVolumeSourceReadOnly(spec)
- if err != nil {
- return nil, err
- }
- ams, err := getVolumeAccessModes(spec)
- if err != nil {
- return nil, err
- }
- return &rbdMounter{
- rbd: newRBD(podUID, spec.Name(), img, pool, ro, plugin, manager),
- Mon: mon,
- Id: id,
- Keyring: keyring,
- Secret: secret,
- fsType: fstype,
- mountOptions: volutil.MountOptionFromSpec(spec),
- accessModes: ams,
- }, nil
- }
- func (plugin *rbdPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
- // Inject real implementations here, test through the internal function.
- return plugin.newUnmounterInternal(volName, podUID, &RBDUtil{})
- }
- func (plugin *rbdPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager) (volume.Unmounter, error) {
- return &rbdUnmounter{
- rbdMounter: &rbdMounter{
- rbd: newRBD(podUID, volName, "", "", false, plugin, manager),
- Mon: make([]string, 0),
- },
- }, nil
- }
- func (plugin *rbdPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
- mounter := plugin.host.GetMounter(plugin.GetPluginName())
- pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
- sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
- if err != nil {
- return nil, err
- }
- s := dstrings.Split(sourceName, "-image-")
- if len(s) != 2 {
- // The mountPath parameter is the volume mount path for a specific pod, its format
- // is /var/lib/kubelet/pods/{podUID}/volumes/{volumePluginName}/{volumeName}.
- // mounter.GetDeviceNameFromMount will find the device path(such as /dev/rbd0) by
- // mountPath first, and then try to find the global device mount path from the mounted
- // path list of this device. sourceName is extracted from this global device mount path.
- // mounter.GetDeviceNameFromMount expects the global device mount path conforms to canonical
- // format: /var/lib/kubelet/plugins/kubernetes.io/rbd/mounts/{pool}-image-{image}.
- // If this assertion failed, it means that the global device mount path is created by
- // the deprecated format: /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd/{pool}-image-{image}.
- // So we will try to check whether this old style global device mount path exist or not.
- // If existed, extract the sourceName from this old style path, otherwise return an error.
- glog.V(3).Infof("SourceName %s wrong, fallback to old format", sourceName)
- sourceName, err = plugin.getDeviceNameFromOldMountPath(mounter, mountPath)
- if err != nil {
- return nil, err
- }
- s = dstrings.Split(sourceName, "-image-")
- if len(s) != 2 {
- return nil, fmt.Errorf("sourceName %s wrong, should be pool+\"-image-\"+imageName", sourceName)
- }
- }
- rbdVolume := &v1.Volume{
- Name: volumeName,
- VolumeSource: v1.VolumeSource{
- RBD: &v1.RBDVolumeSource{
- RBDPool: s[0],
- RBDImage: s[1],
- },
- },
- }
- return volume.NewSpecFromVolume(rbdVolume), nil
- }
- func (plugin *rbdPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, mapPath string) (*volume.Spec, error) {
- pluginDir := plugin.host.GetVolumeDevicePluginDir(rbdPluginName)
- blkutil := volumepathhandler.NewBlockVolumePathHandler()
- globalMapPathUUID, err := blkutil.FindGlobalMapPathUUIDFromPod(pluginDir, mapPath, podUID)
- if err != nil {
- return nil, err
- }
- glog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err)
- globalMapPath := filepath.Dir(globalMapPathUUID)
- if len(globalMapPath) == 1 {
- return nil, fmt.Errorf("failed to retrieve volume plugin information from globalMapPathUUID: %v", globalMapPathUUID)
- }
- return getVolumeSpecFromGlobalMapPath(globalMapPath)
- }
- func getVolumeSpecFromGlobalMapPath(globalMapPath string) (*volume.Spec, error) {
- // Retrieve volume spec information from globalMapPath
- // globalMapPath example:
- // plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}
- pool, image, err := getPoolAndImageFromMapPath(globalMapPath)
- if err != nil {
- return nil, err
- }
- block := v1.PersistentVolumeBlock
- rbdVolume := &v1.PersistentVolume{
- Spec: v1.PersistentVolumeSpec{
- PersistentVolumeSource: v1.PersistentVolumeSource{
- RBD: &v1.RBDPersistentVolumeSource{
- RBDImage: image,
- RBDPool: pool,
- },
- },
- VolumeMode: &block,
- },
- }
- return volume.NewSpecFromPersistentVolume(rbdVolume, true), nil
- }
- func (plugin *rbdPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) {
- var uid types.UID
- if pod != nil {
- uid = pod.UID
- }
- secret := ""
- if pod != nil {
- secretName, secretNs, err := getSecretNameAndNamespace(spec, pod.Namespace)
- if err != nil {
- return nil, err
- }
- if len(secretName) > 0 && len(secretNs) > 0 {
- // if secret is provideded, retrieve it
- kubeClient := plugin.host.GetKubeClient()
- if kubeClient == nil {
- return nil, fmt.Errorf("Cannot get kube client")
- }
- secrets, err := kubeClient.Core().Secrets(secretNs).Get(secretName, metav1.GetOptions{})
- if err != nil {
- err = fmt.Errorf("Couldn't get secret %v/%v err: %v", secretNs, secretName, err)
- return nil, err
- }
- for _, data := range secrets.Data {
- secret = string(data)
- }
- }
- }
- return plugin.newBlockVolumeMapperInternal(spec, uid, &RBDUtil{}, secret, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
- }
- func (plugin *rbdPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, secret string, mounter mount.Interface, exec mount.Exec) (volume.BlockVolumeMapper, error) {
- mon, err := getVolumeSourceMonitors(spec)
- if err != nil {
- return nil, err
- }
- img, err := getVolumeSourceImage(spec)
- if err != nil {
- return nil, err
- }
- pool, err := getVolumeSourcePool(spec)
- if err != nil {
- return nil, err
- }
- id, err := getVolumeSourceUser(spec)
- if err != nil {
- return nil, err
- }
- keyring, err := getVolumeSourceKeyRing(spec)
- if err != nil {
- return nil, err
- }
- ro, err := getVolumeSourceReadOnly(spec)
- if err != nil {
- return nil, err
- }
- return &rbdDiskMapper{
- rbd: newRBD(podUID, spec.Name(), img, pool, ro, plugin, manager),
- mon: mon,
- id: id,
- keyring: keyring,
- secret: secret,
- }, nil
- }
- func (plugin *rbdPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) {
- return plugin.newUnmapperInternal(volName, podUID, &RBDUtil{})
- }
- func (plugin *rbdPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager) (volume.BlockVolumeUnmapper, error) {
- return &rbdDiskUnmapper{
- rbdDiskMapper: &rbdDiskMapper{
- rbd: newRBD(podUID, volName, "", "", false, plugin, manager),
- mon: make([]string, 0),
- },
- }, nil
- }
- func (plugin *rbdPlugin) getDeviceNameFromOldMountPath(mounter mount.Interface, mountPath string) (string, error) {
- refs, err := mounter.GetMountRefs(mountPath)
- if err != nil {
- return "", err
- }
- // baseMountPath is the prefix of deprecated device global mounted path,
- // such as: /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd
- baseMountPath := filepath.Join(plugin.host.GetPluginDir(rbdPluginName), "rbd")
- for _, ref := range refs {
- if dstrings.HasPrefix(ref, baseMountPath) {
- return filepath.Rel(baseMountPath, ref)
- }
- }
- return "", fmt.Errorf("can't find source name from mounted path: %s", mountPath)
- }
- func (plugin *rbdPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
- if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD == nil {
- return nil, fmt.Errorf("spec.PersistentVolumeSource.Spec.RBD is nil")
- }
- admin, secret, err := plugin.getAdminAndSecret(spec)
- if err != nil {
- return nil, err
- }
- return plugin.newDeleterInternal(spec, admin, secret, &RBDUtil{})
- }
- func (plugin *rbdPlugin) newDeleterInternal(spec *volume.Spec, admin, secret string, manager diskManager) (volume.Deleter, error) {
- return &rbdVolumeDeleter{
- rbdMounter: &rbdMounter{
- rbd: newRBD("", spec.Name(), spec.PersistentVolume.Spec.RBD.RBDImage, spec.PersistentVolume.Spec.RBD.RBDPool, false, plugin, manager),
- Mon: spec.PersistentVolume.Spec.RBD.CephMonitors,
- adminId: admin,
- adminSecret: secret,
- }}, nil
- }
- func (plugin *rbdPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
- return plugin.newProvisionerInternal(options, &RBDUtil{})
- }
- func (plugin *rbdPlugin) newProvisionerInternal(options volume.VolumeOptions, manager diskManager) (volume.Provisioner, error) {
- return &rbdVolumeProvisioner{
- rbdMounter: &rbdMounter{
- rbd: newRBD("", "", "", "", false, plugin, manager),
- },
- options: options,
- }, nil
- }
- // rbdVolumeProvisioner implements volume.Provisioner interface.
- type rbdVolumeProvisioner struct {
- *rbdMounter
- options volume.VolumeOptions
- }
- var _ volume.Provisioner = &rbdVolumeProvisioner{}
- func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
- if !volutil.AccessModesContainedInAll(r.plugin.GetAccessModes(), r.options.PVC.Spec.AccessModes) {
- return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", r.options.PVC.Spec.AccessModes, r.plugin.GetAccessModes())
- }
- if r.options.PVC.Spec.Selector != nil {
- return nil, fmt.Errorf("claim Selector is not supported")
- }
- var err error
- adminSecretName := ""
- adminSecretNamespace := rbdDefaultAdminSecretNamespace
- secret := ""
- secretName := ""
- secretNamespace := ""
- keyring := ""
- imageFormat := rbdImageFormat2
- fstype := ""
- for k, v := range r.options.Parameters {
- switch dstrings.ToLower(k) {
- case "monitors":
- arr := dstrings.Split(v, ",")
- r.Mon = append(r.Mon, arr...)
- case "adminid":
- r.adminId = v
- case "adminsecretname":
- adminSecretName = v
- case "adminsecretnamespace":
- adminSecretNamespace = v
- case "userid":
- r.Id = v
- case "pool":
- r.Pool = v
- case "usersecretname":
- secretName = v
- case "usersecretnamespace":
- secretNamespace = v
- case "keyring":
- keyring = v
- case "imageformat":
- imageFormat = v
- case "imagefeatures":
- arr := dstrings.Split(v, ",")
- for _, f := range arr {
- if !supportedFeatures.Has(f) {
- return nil, fmt.Errorf("invalid feature %q for volume plugin %s, supported features are: %v", f, r.plugin.GetPluginName(), supportedFeatures)
- } else {
- r.imageFeatures = append(r.imageFeatures, f)
- }
- }
- case volume.VolumeParameterFSType:
- fstype = v
- default:
- return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, r.plugin.GetPluginName())
- }
- }
- // sanity check
- if imageFormat != rbdImageFormat1 && imageFormat != rbdImageFormat2 {
- return nil, fmt.Errorf("invalid ceph imageformat %s, expecting %s or %s",
- imageFormat, rbdImageFormat1, rbdImageFormat2)
- }
- r.imageFormat = imageFormat
- if adminSecretName == "" {
- return nil, fmt.Errorf("missing Ceph admin secret name")
- }
- if secret, err = parsePVSecret(adminSecretNamespace, adminSecretName, r.plugin.host.GetKubeClient()); err != nil {
- return nil, fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err)
- }
- r.adminSecret = secret
- if len(r.Mon) < 1 {
- return nil, fmt.Errorf("missing Ceph monitors")
- }
- if secretName == "" && keyring == "" {
- return nil, fmt.Errorf("must specify either keyring or user secret name")
- }
- if r.adminId == "" {
- r.adminId = rbdDefaultAdminId
- }
- if r.Pool == "" {
- r.Pool = rbdDefaultPool
- }
- if r.Id == "" {
- r.Id = r.adminId
- }
- // create random image name
- image := fmt.Sprintf("kubernetes-dynamic-pvc-%s", uuid.NewUUID())
- r.rbdMounter.Image = image
- rbd, sizeMB, err := r.manager.CreateImage(r)
- if err != nil {
- glog.Errorf("rbd: create volume failed, err: %v", err)
- return nil, err
- }
- glog.Infof("successfully created rbd image %q", image)
- pv := new(v1.PersistentVolume)
- metav1.SetMetaDataAnnotation(&pv.ObjectMeta, volutil.VolumeDynamicallyCreatedByKey, "rbd-dynamic-provisioner")
- if secretName != "" {
- rbd.SecretRef = new(v1.SecretReference)
- rbd.SecretRef.Name = secretName
- rbd.SecretRef.Namespace = secretNamespace
- } else {
- var filePathRegex = regexp.MustCompile(`^(?:/[^/!;` + "`" + ` ]+)+$`)
- if keyring != "" && !filePathRegex.MatchString(keyring) {
- return nil, fmt.Errorf("keyring field must contain a path to a file")
- }
- rbd.Keyring = keyring
- }
- rbd.RadosUser = r.Id
- rbd.FSType = fstype
- pv.Spec.PersistentVolumeSource.RBD = rbd
- pv.Spec.PersistentVolumeReclaimPolicy = r.options.PersistentVolumeReclaimPolicy
- pv.Spec.AccessModes = r.options.PVC.Spec.AccessModes
- if len(pv.Spec.AccessModes) == 0 {
- pv.Spec.AccessModes = r.plugin.GetAccessModes()
- }
- pv.Spec.Capacity = v1.ResourceList{
- v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dMi", sizeMB)),
- }
- pv.Spec.MountOptions = r.options.MountOptions
- return pv, nil
- }
- // rbdVolumeDeleter implements volume.Deleter interface.
- type rbdVolumeDeleter struct {
- *rbdMounter
- }
- var _ volume.Deleter = &rbdVolumeDeleter{}
- func (r *rbdVolumeDeleter) GetPath() string {
- return getPath(r.podUID, r.volName, r.plugin.host)
- }
- func (r *rbdVolumeDeleter) Delete() error {
- return r.manager.DeleteImage(r)
- }
- // rbd implmenets volume.Volume interface.
- // It's embedded in Mounter/Unmounter/Deleter.
- type rbd struct {
- volName string
- podUID types.UID
- Pool string
- Image string
- ReadOnly bool
- plugin *rbdPlugin
- mounter *mount.SafeFormatAndMount
- exec mount.Exec
- // Utility interface that provides API calls to the provider to attach/detach disks.
- manager diskManager
- volume.MetricsProvider `json:"-"`
- }
- var _ volume.Volume = &rbd{}
- func (rbd *rbd) GetPath() string {
- // safe to use PodVolumeDir now: volume teardown occurs before pod is cleaned up
- return getPath(rbd.podUID, rbd.volName, rbd.plugin.host)
- }
- // newRBD creates a new rbd.
- func newRBD(podUID types.UID, volName string, image string, pool string, readOnly bool, plugin *rbdPlugin, manager diskManager) *rbd {
- return &rbd{
- podUID: podUID,
- volName: volName,
- Image: image,
- Pool: pool,
- ReadOnly: readOnly,
- plugin: plugin,
- mounter: volutil.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
- exec: plugin.host.GetExec(plugin.GetPluginName()),
- manager: manager,
- MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
- }
- }
- // rbdMounter implements volume.Mounter interface.
- // It contains information which need to be persisted in whole life cycle of PV
- // on the node. It is persisted at the very beginning in the pod mount point
- // directory.
- // Note: Capitalized field names of this struct determines the information
- // persisted on the disk, DO NOT change them. (TODO: refactoring to use a dedicated struct?)
- type rbdMounter struct {
- *rbd
- // capitalized so they can be exported in persistRBD()
- Mon []string
- Id string
- Keyring string
- Secret string
- fsType string
- adminSecret string
- adminId string
- mountOptions []string
- imageFormat string
- imageFeatures []string
- accessModes []v1.PersistentVolumeAccessMode
- }
- var _ volume.Mounter = &rbdMounter{}
- func (b *rbd) 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 *rbdMounter) CanMount() error {
- return nil
- }
- func (b *rbdMounter) SetUp(fsGroup *int64) error {
- return b.SetUpAt(b.GetPath(), fsGroup)
- }
- func (b *rbdMounter) SetUpAt(dir string, fsGroup *int64) error {
- // diskSetUp checks mountpoints and prevent repeated calls
- glog.V(4).Infof("rbd: attempting to setup at %s", dir)
- err := diskSetUp(b.manager, *b, dir, b.mounter, fsGroup)
- if err != nil {
- glog.Errorf("rbd: failed to setup at %s %v", dir, err)
- }
- glog.V(3).Infof("rbd: successfully setup at %s", dir)
- return err
- }
- // rbdUnmounter implements volume.Unmounter interface.
- type rbdUnmounter struct {
- *rbdMounter
- }
- var _ volume.Unmounter = &rbdUnmounter{}
- // Unmounts the bind mount, and detaches the disk only if the disk
- // resource was the last reference to that disk on the kubelet.
- func (c *rbdUnmounter) TearDown() error {
- return c.TearDownAt(c.GetPath())
- }
- func (c *rbdUnmounter) TearDownAt(dir string) error {
- glog.V(4).Infof("rbd: attempting to teardown at %s", dir)
- if pathExists, pathErr := volutil.PathExists(dir); pathErr != nil {
- return fmt.Errorf("Error checking if path exists: %v", pathErr)
- } else if !pathExists {
- glog.Warningf("Warning: Unmount skipped because path does not exist: %v", dir)
- return nil
- }
- err := diskTearDown(c.manager, *c, dir, c.mounter)
- if err != nil {
- return err
- }
- glog.V(3).Infof("rbd: successfully teardown at %s", dir)
- return nil
- }
- var _ volume.BlockVolumeMapper = &rbdDiskMapper{}
- type rbdDiskMapper struct {
- *rbd
- mon []string
- id string
- keyring string
- secret string
- adminSecret string
- adminId string
- imageFormat string
- imageFeatures []string
- }
- var _ volume.BlockVolumeUnmapper = &rbdDiskUnmapper{}
- // GetGlobalMapPath returns global map path and error
- // path: plugins/kubernetes.io/{PluginName}/volumeDevices/{rbd pool}-image-{rbd image-name}/{podUid}
- func (rbd *rbd) GetGlobalMapPath(spec *volume.Spec) (string, error) {
- return rbd.rbdGlobalMapPath(spec)
- }
- // GetPodDeviceMapPath returns pod device map path and volume name
- // path: pods/{podUid}/volumeDevices/kubernetes.io~rbd
- // volumeName: pv0001
- func (rbd *rbd) GetPodDeviceMapPath() (string, string) {
- return rbd.rbdPodDeviceMapPath()
- }
- func (rbd *rbdDiskMapper) SetUpDevice() (string, error) {
- return "", nil
- }
- func (rbd *rbd) rbdGlobalMapPath(spec *volume.Spec) (string, error) {
- mon, err := getVolumeSourceMonitors(spec)
- if err != nil {
- return "", err
- }
- img, err := getVolumeSourceImage(spec)
- if err != nil {
- return "", err
- }
- pool, err := getVolumeSourcePool(spec)
- if err != nil {
- return "", err
- }
- ro, err := getVolumeSourceReadOnly(spec)
- if err != nil {
- return "", err
- }
- mounter := &rbdMounter{
- rbd: newRBD("", spec.Name(), img, pool, ro, rbd.plugin, &RBDUtil{}),
- Mon: mon,
- }
- return rbd.manager.MakeGlobalVDPDName(*mounter.rbd), nil
- }
- func (rbd *rbd) rbdPodDeviceMapPath() (string, string) {
- name := rbdPluginName
- return rbd.plugin.host.GetPodVolumeDeviceDir(rbd.podUID, strings.EscapeQualifiedNameForDisk(name)), rbd.volName
- }
- type rbdDiskUnmapper struct {
- *rbdDiskMapper
- }
- func getPoolAndImageFromMapPath(mapPath string) (string, string, error) {
- pathParts := dstrings.Split(mapPath, "/")
- if len(pathParts) < 2 {
- return "", "", fmt.Errorf("corrupted mapPath")
- }
- rbdParts := dstrings.Split(pathParts[len(pathParts)-1], "-image-")
- if len(rbdParts) < 2 {
- return "", "", fmt.Errorf("corrupted mapPath")
- }
- return string(rbdParts[0]), string(rbdParts[1]), nil
- }
- func getBlockVolumeDevice(mapPath string) (string, error) {
- pool, image, err := getPoolAndImageFromMapPath(mapPath)
- if err != nil {
- return "", err
- }
- // Getting full device path
- device, found := getDevFromImageAndPool(pool, image)
- if !found {
- return "", err
- }
- return device, nil
- }
- func (rbd *rbdDiskUnmapper) TearDownDevice(mapPath, _ string) error {
- device, err := getBlockVolumeDevice(mapPath)
- if err != nil {
- return fmt.Errorf("rbd: failed to get loopback for device: %v, err: %v", device, err)
- }
- // Get loopback device which takes fd lock for device beofore detaching a volume from node.
- // TODO: This is a workaround for issue #54108
- // Currently local attach plugins such as FC, iSCSI, RBD can't obtain devicePath during
- // GenerateUnmapDeviceFunc() in operation_generator. As a result, these plugins fail to get
- // and remove loopback device then it will be remained on kubelet node. To avoid the problem,
- // local attach plugins needs to remove loopback device during TearDownDevice().
- blkUtil := volumepathhandler.NewBlockVolumePathHandler()
- loop, err := volumepathhandler.BlockVolumePathHandler.GetLoopDevice(blkUtil, device)
- if err != nil {
- if err.Error() != volumepathhandler.ErrDeviceNotFound {
- return fmt.Errorf("rbd: failed to get loopback for device: %v, err: %v", device, err)
- }
- glog.Warning("rbd: loopback for device: % not found", device)
- } else {
- if len(loop) != 0 {
- // Remove loop device before detaching volume since volume detach operation gets busy if volume is opened by loopback.
- err = volumepathhandler.BlockVolumePathHandler.RemoveLoopDevice(blkUtil, loop)
- if err != nil {
- return fmt.Errorf("rbd: failed to remove loopback :%v, err: %v", loop, err)
- }
- glog.V(4).Infof("rbd: successfully removed loop device: %s", loop)
- }
- }
- err = rbd.manager.DetachBlockDisk(*rbd, mapPath)
- if err != nil {
- return fmt.Errorf("rbd: failed to detach disk: %s\nError: %v", mapPath, err)
- }
- glog.V(4).Infof("rbd: %q is unmapped, deleting the directory", mapPath)
- err = os.RemoveAll(mapPath)
- if err != nil {
- return fmt.Errorf("rbd: failed to delete the directory: %s\nError: %v", mapPath, err)
- }
- glog.V(4).Infof("rbd: successfully detached disk: %s", mapPath)
- return nil
- }
- func getVolumeSourceMonitors(spec *volume.Spec) ([]string, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- return spec.Volume.RBD.CephMonitors, nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- return spec.PersistentVolume.Spec.RBD.CephMonitors, nil
- }
- return nil, fmt.Errorf("Spec does not reference a RBD volume type")
- }
- func getVolumeSourceImage(spec *volume.Spec) (string, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- return spec.Volume.RBD.RBDImage, nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- return spec.PersistentVolume.Spec.RBD.RBDImage, nil
- }
- return "", fmt.Errorf("Spec does not reference a RBD volume type")
- }
- func getVolumeSourceFSType(spec *volume.Spec) (string, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- return spec.Volume.RBD.FSType, nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- return spec.PersistentVolume.Spec.RBD.FSType, nil
- }
- return "", fmt.Errorf("Spec does not reference a RBD volume type")
- }
- func getVolumeSourcePool(spec *volume.Spec) (string, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- return spec.Volume.RBD.RBDPool, nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- return spec.PersistentVolume.Spec.RBD.RBDPool, nil
- }
- return "", fmt.Errorf("Spec does not reference a RBD volume type")
- }
- func getVolumeSourceUser(spec *volume.Spec) (string, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- return spec.Volume.RBD.RadosUser, nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- return spec.PersistentVolume.Spec.RBD.RadosUser, nil
- }
- return "", fmt.Errorf("Spec does not reference a RBD volume type")
- }
- func getVolumeSourceKeyRing(spec *volume.Spec) (string, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- return spec.Volume.RBD.Keyring, nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- return spec.PersistentVolume.Spec.RBD.Keyring, nil
- }
- return "", fmt.Errorf("Spec does not reference a RBD volume type")
- }
- func getVolumeSourceReadOnly(spec *volume.Spec) (bool, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- return spec.Volume.RBD.ReadOnly, nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- // rbd volumes used as a PersistentVolume gets the ReadOnly flag indirectly through
- // the persistent-claim volume used to mount the PV
- return spec.ReadOnly, nil
- }
- return false, fmt.Errorf("Spec does not reference a RBD volume type")
- }
- func getVolumeAccessModes(spec *volume.Spec) ([]v1.PersistentVolumeAccessMode, error) {
- // Only PersistentVolumeSpec has AccessModes
- if spec.PersistentVolume != nil {
- if spec.PersistentVolume.Spec.RBD != nil {
- return spec.PersistentVolume.Spec.AccessModes, nil
- } else {
- return nil, fmt.Errorf("Spec does not reference a RBD volume type")
- }
- }
- return nil, nil
- }
- func parsePodSecret(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (string, error) {
- secret, err := volutil.GetSecretForPod(pod, secretName, kubeClient)
- if err != nil {
- glog.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
- return "", fmt.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
- }
- return parseSecretMap(secret)
- }
- func parsePVSecret(namespace, secretName string, kubeClient clientset.Interface) (string, error) {
- secret, err := volutil.GetSecretForPV(namespace, secretName, rbdPluginName, kubeClient)
- if err != nil {
- glog.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
- return "", fmt.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
- }
- return parseSecretMap(secret)
- }
- // parseSecretMap locates the secret by key name.
- func parseSecretMap(secretMap map[string]string) (string, error) {
- if len(secretMap) == 0 {
- return "", fmt.Errorf("empty secret map")
- }
- secret := ""
- for k, v := range secretMap {
- if k == secretKeyName {
- return v, nil
- }
- secret = v
- }
- // If not found, the last secret in the map wins as done before
- return secret, nil
- }
- func getSecretNameAndNamespace(spec *volume.Spec, defaultNamespace string) (string, string, error) {
- if spec.Volume != nil && spec.Volume.RBD != nil {
- localSecretRef := spec.Volume.RBD.SecretRef
- if localSecretRef != nil {
- return localSecretRef.Name, defaultNamespace, nil
- }
- return "", "", nil
- } else if spec.PersistentVolume != nil &&
- spec.PersistentVolume.Spec.RBD != nil {
- secretRef := spec.PersistentVolume.Spec.RBD.SecretRef
- secretNs := defaultNamespace
- if secretRef != nil {
- if len(secretRef.Namespace) != 0 {
- secretNs = secretRef.Namespace
- }
- return secretRef.Name, secretNs, nil
- }
- return "", "", nil
- }
- return "", "", fmt.Errorf("Spec does not reference an RBD volume type")
- }