diff --git a/pkg/api/fuzzer/fuzzer.go b/pkg/api/fuzzer/fuzzer.go index ea9a8f7a97e..45575fc90e6 100644 --- a/pkg/api/fuzzer/fuzzer.go +++ b/pkg/api/fuzzer/fuzzer.go @@ -355,6 +355,20 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { r.Keyring = "/etc/ceph/keyring" } }, + func(r *api.RBDPersistentVolumeSource, c fuzz.Continue) { + r.RBDPool = c.RandString() + if r.RBDPool == "" { + r.RBDPool = "rbd" + } + r.RadosUser = c.RandString() + if r.RadosUser == "" { + r.RadosUser = "admin" + } + r.Keyring = c.RandString() + if r.Keyring == "" { + r.Keyring = "/etc/ceph/keyring" + } + }, func(obj *api.HostPathVolumeSource, c fuzz.Continue) { c.FuzzNoCustom(obj) types := []api.HostPathType{api.HostPathUnset, api.HostPathDirectoryOrCreate, api.HostPathDirectory, diff --git a/pkg/api/persistentvolume/util.go b/pkg/api/persistentvolume/util.go index 0a8de7c5e68..f05d8cf1b92 100644 --- a/pkg/api/persistentvolume/util.go +++ b/pkg/api/persistentvolume/util.go @@ -64,8 +64,16 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool { return false } case source.RBD != nil: - if source.RBD.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.RBD.SecretRef.Name) { - return false + if source.RBD.SecretRef != nil { + // previously persisted PV objects use claimRef namespace + ns := getClaimRefNamespace(pv) + if len(source.RBD.SecretRef.Namespace) > 0 { + // use the secret namespace if namespace is set + ns = source.RBD.SecretRef.Namespace + } + if !visitor(ns, source.RBD.SecretRef.Name) { + return false + } } case source.ScaleIO != nil: if source.ScaleIO.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.ScaleIO.SecretRef.Name) { diff --git a/pkg/api/persistentvolume/util_test.go b/pkg/api/persistentvolume/util_test.go index 8e83f0644c6..b4e0a736212 100644 --- a/pkg/api/persistentvolume/util_test.go +++ b/pkg/api/persistentvolume/util_test.go @@ -65,9 +65,16 @@ func TestPVSecrets(t *testing.T) { {Spec: api.PersistentVolumeSpec{ ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"}, PersistentVolumeSource: api.PersistentVolumeSource{ - RBD: &api.RBDVolumeSource{ - SecretRef: &api.LocalObjectReference{ + RBD: &api.RBDPersistentVolumeSource{ + SecretRef: &api.SecretReference{ Name: "Spec.PersistentVolumeSource.RBD.SecretRef"}}}}}, + {Spec: api.PersistentVolumeSpec{ + ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"}, + PersistentVolumeSource: api.PersistentVolumeSource{ + RBD: &api.RBDPersistentVolumeSource{ + SecretRef: &api.SecretReference{ + Name: "Spec.PersistentVolumeSource.RBD.SecretRef", + Namespace: "rbdns"}}}}}, {Spec: api.PersistentVolumeSpec{ ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"}, PersistentVolumeSource: api.PersistentVolumeSource{ @@ -141,6 +148,7 @@ func TestPVSecrets(t *testing.T) { "cephfs/Spec.PersistentVolumeSource.CephFS.SecretRef", "claimrefns/Spec.PersistentVolumeSource.FlexVolume.SecretRef", "claimrefns/Spec.PersistentVolumeSource.RBD.SecretRef", + "rbdns/Spec.PersistentVolumeSource.RBD.SecretRef", "claimrefns/Spec.PersistentVolumeSource.ScaleIO.SecretRef", "claimrefns/Spec.PersistentVolumeSource.ISCSI.SecretRef", "storageosns/Spec.PersistentVolumeSource.StorageOS.SecretRef", diff --git a/pkg/api/types.go b/pkg/api/types.go index d04612291e9..2fbcf5d37ba 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -343,7 +343,7 @@ type PersistentVolumeSource struct { NFS *NFSVolumeSource // RBD represents a Rados Block Device mount on the host that shares a pod's lifetime // +optional - RBD *RBDVolumeSource + RBD *RBDPersistentVolumeSource // Quobyte represents a Quobyte mount on the host that shares a pod's lifetime // +optional Quobyte *QuobyteVolumeSource @@ -1006,6 +1006,37 @@ type RBDVolumeSource struct { ReadOnly bool } +// Represents a Rados Block Device mount that lasts the lifetime of a pod. +// RBD volumes support ownership management and SELinux relabeling. +type RBDPersistentVolumeSource struct { + // Required: CephMonitors is a collection of Ceph monitors + CephMonitors []string + // Required: RBDImage is the rados image name + RBDImage string + // Filesystem type to mount. + // Must be a filesystem type supported by the host operating system. + // Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + // TODO: how do we prevent errors in the filesystem from compromising the machine + // +optional + FSType string + // Optional: RadosPool is the rados pool name,default is rbd + // +optional + RBDPool string + // Optional: RBDUser is the rados user name, default is admin + // +optional + RadosUser string + // Optional: Keyring is the path to key ring for RBDUser, default is /etc/ceph/keyring + // +optional + Keyring string + // Optional: SecretRef is reference to the authentication secret for User, default is empty. + // +optional + SecretRef *SecretReference + // Optional: Defaults to false (read/write). ReadOnly here will force + // the ReadOnly setting in VolumeMounts. + // +optional + ReadOnly bool +} + // Represents a cinder volume resource in Openstack. A Cinder volume // must exist before mounting to a container. The volume must also be // in the same region as the kubelet. Cinder volumes support ownership diff --git a/pkg/api/v1/defaults.go b/pkg/api/v1/defaults.go index 9bf082d3d9a..8b57b76f1c2 100644 --- a/pkg/api/v1/defaults.go +++ b/pkg/api/v1/defaults.go @@ -368,6 +368,18 @@ func SetDefaults_RBDVolumeSource(obj *v1.RBDVolumeSource) { } } +func SetDefaults_RBDPersistentVolumeSource(obj *v1.RBDPersistentVolumeSource) { + if obj.RBDPool == "" { + obj.RBDPool = "rbd" + } + if obj.RadosUser == "" { + obj.RadosUser = "admin" + } + if obj.Keyring == "" { + obj.Keyring = "/etc/ceph/keyring" + } +} + func SetDefaults_ScaleIOVolumeSource(obj *v1.ScaleIOVolumeSource) { if obj.ProtectionDomain == "" { obj.ProtectionDomain = "default" diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index fc7c8a0f04a..73645c8db19 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -1088,6 +1088,17 @@ func validateRBDVolumeSource(rbd *api.RBDVolumeSource, fldPath *field.Path) fiel return allErrs } +func validateRBDPersistentVolumeSource(rbd *api.RBDPersistentVolumeSource, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if len(rbd.CephMonitors) == 0 { + allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), "")) + } + if len(rbd.RBDImage) == 0 { + allErrs = append(allErrs, field.Required(fldPath.Child("image"), "")) + } + return allErrs +} + func validateCinderVolumeSource(cd *api.CinderVolumeSource, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(cd.VolumeID) == 0 { @@ -1380,7 +1391,7 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList { allErrs = append(allErrs, field.Forbidden(specPath.Child("rbd"), "may not specify more than 1 volume type")) } else { numVolumes++ - allErrs = append(allErrs, validateRBDVolumeSource(pv.Spec.RBD, specPath.Child("rbd"))...) + allErrs = append(allErrs, validateRBDPersistentVolumeSource(pv.Spec.RBD, specPath.Child("rbd"))...) } } if pv.Spec.Quobyte != nil { diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index ca8dc4fde86..0c74c9a633f 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -898,6 +898,19 @@ func printRBDVolumeSource(rbd *api.RBDVolumeSource, w PrefixWriter) { rbd.CephMonitors, rbd.RBDImage, rbd.FSType, rbd.RBDPool, rbd.RadosUser, rbd.Keyring, rbd.SecretRef, rbd.ReadOnly) } +func printRBDPersistentVolumeSource(rbd *api.RBDPersistentVolumeSource, w PrefixWriter) { + w.Write(LEVEL_2, "Type:\tRBD (a Rados Block Device mount on the host that shares a pod's lifetime)\n"+ + " CephMonitors:\t%v\n"+ + " RBDImage:\t%v\n"+ + " FSType:\t%v\n"+ + " RBDPool:\t%v\n"+ + " RadosUser:\t%v\n"+ + " Keyring:\t%v\n"+ + " SecretRef:\t%v\n"+ + " ReadOnly:\t%v\n", + rbd.CephMonitors, rbd.RBDImage, rbd.FSType, rbd.RBDPool, rbd.RadosUser, rbd.Keyring, rbd.SecretRef, rbd.ReadOnly) +} + func printDownwardAPIVolumeSource(d *api.DownwardAPIVolumeSource, w PrefixWriter) { w.Write(LEVEL_2, "Type:\tDownwardAPI (a volume populated by information about the pod)\n Items:\n") for _, mapping := range d.Items { @@ -1108,7 +1121,7 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) ( case pv.Spec.Glusterfs != nil: printGlusterfsVolumeSource(pv.Spec.Glusterfs, w) case pv.Spec.RBD != nil: - printRBDVolumeSource(pv.Spec.RBD, w) + printRBDPersistentVolumeSource(pv.Spec.RBD, w) case pv.Spec.Quobyte != nil: printQuobyteVolumeSource(pv.Spec.Quobyte, w) case pv.Spec.VsphereVolume != nil: diff --git a/pkg/printers/internalversion/describe_test.go b/pkg/printers/internalversion/describe_test.go index 5fb87952868..b07db28c1ef 100644 --- a/pkg/printers/internalversion/describe_test.go +++ b/pkg/printers/internalversion/describe_test.go @@ -776,7 +776,7 @@ func TestPersistentVolumeDescriber(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "bar"}, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ - RBD: &api.RBDVolumeSource{}, + RBD: &api.RBDPersistentVolumeSource{}, }, }, }, diff --git a/pkg/volume/rbd/disk_manager.go b/pkg/volume/rbd/disk_manager.go index d219d62ab1b..24a090109fd 100644 --- a/pkg/volume/rbd/disk_manager.go +++ b/pkg/volume/rbd/disk_manager.go @@ -39,7 +39,7 @@ type diskManager interface { // Detaches the disk from the kubelet's host machine. DetachDisk(disk rbdUnmounter, mntPath string) error // Creates a rbd image - CreateImage(provisioner *rbdVolumeProvisioner) (r *v1.RBDVolumeSource, volumeSizeGB int, err error) + CreateImage(provisioner *rbdVolumeProvisioner) (r *v1.RBDPersistentVolumeSource, volumeSizeGB int, err error) // Deletes a rbd image DeleteImage(deleter *rbdVolumeDeleter) error } diff --git a/pkg/volume/rbd/rbd.go b/pkg/volume/rbd/rbd.go index cc345b22d70..87ca9992f19 100644 --- a/pkg/volume/rbd/rbd.go +++ b/pkg/volume/rbd/rbd.go @@ -78,15 +78,19 @@ func (plugin *rbdPlugin) GetPluginName() string { } func (plugin *rbdPlugin) GetVolumeName(spec *volume.Spec) (string, error) { - volumeSource, _, err := getVolumeSource(spec) + mon, err := getVolumeSourceMonitors(spec) + if err != nil { + return "", err + } + img, err := getVolumeSourceImage(spec) if err != nil { return "", err } return fmt.Sprintf( "%v:%v", - volumeSource.CephMonitors, - volumeSource.RBDImage), nil + mon, + img), nil } func (plugin *rbdPlugin) CanSupport(spec *volume.Spec) bool { @@ -117,55 +121,79 @@ func (plugin *rbdPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode { } func (plugin *rbdPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { - var secret string - var err error - source, _ := plugin.getRBDVolumeSource(spec) - - if source.SecretRef != nil { - if secret, err = parsePodSecret(pod, source.SecretRef.Name, plugin.host.GetKubeClient()); err != nil { - glog.Errorf("Couldn't get secret from %v/%v", pod.Namespace, source.SecretRef) + 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.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) + } } // Inject real implementations here, test through the internal function. return plugin.newMounterInternal(spec, pod.UID, &RBDUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret) } -func (plugin *rbdPlugin) getRBDVolumeSource(spec *volume.Spec) (*v1.RBDVolumeSource, bool) { - // rbd volumes used directly in a pod have a ReadOnly flag set by the pod author. - // rbd volumes used as a PersistentVolume gets the ReadOnly flag indirectly through the persistent-claim volume used to mount the PV - if spec.Volume != nil && spec.Volume.RBD != nil { - return spec.Volume.RBD, spec.Volume.RBD.ReadOnly - } else { - return spec.PersistentVolume.Spec.RBD, spec.ReadOnly - } -} - func (plugin *rbdPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec, secret string) (volume.Mounter, error) { - source, readOnly := plugin.getRBDVolumeSource(spec) - pool := source.RBDPool - id := source.RadosUser - keyring := source.Keyring + 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 + } return &rbdMounter{ rbd: &rbd{ podUID: podUID, volName: spec.Name(), - Image: source.RBDImage, + Image: img, Pool: pool, - ReadOnly: readOnly, + ReadOnly: ro, manager: manager, mounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec}, exec: exec, plugin: plugin, MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, spec.Name(), plugin.host)), }, - Mon: source.CephMonitors, + Mon: mon, Id: id, Keyring: keyring, Secret: secret, - fsType: source.FSType, + fsType: fstype, mountOptions: volume.MountOptionFromSpec(spec), }, nil } @@ -289,8 +317,9 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { var err error adminSecretName := "" adminSecretNamespace := rbdDefaultAdminSecretNamespace - secretName := "" secret := "" + secretName := "" + secretNamespace := "" imageFormat := rbdImageFormat2 fstype := "" @@ -313,6 +342,8 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { r.Pool = v case "usersecretname": secretName = v + case "usersecretnamespace": + secretNamespace = v case "imageformat": imageFormat = v case "imagefeatures": @@ -370,8 +401,9 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { glog.Infof("successfully created rbd image %q", image) pv := new(v1.PersistentVolume) metav1.SetMetaDataAnnotation(&pv.ObjectMeta, volumehelper.VolumeDynamicallyCreatedByKey, "rbd-dynamic-provisioner") - rbd.SecretRef = new(v1.LocalObjectReference) + rbd.SecretRef = new(v1.SecretReference) rbd.SecretRef.Name = secretName + rbd.SecretRef.Namespace = secretNamespace rbd.RadosUser = r.Id rbd.FSType = fstype pv.Spec.PersistentVolumeSource.RBD = rbd @@ -486,16 +518,83 @@ func (c *rbdUnmounter) TearDownAt(dir string) error { return diskTearDown(c.manager, *c, dir, c.mounter) } -func getVolumeSource( - spec *volume.Spec) (*v1.RBDVolumeSource, bool, error) { +func getVolumeSourceMonitors(spec *volume.Spec) ([]string, error) { if spec.Volume != nil && spec.Volume.RBD != nil { - return spec.Volume.RBD, spec.Volume.RBD.ReadOnly, nil + return spec.Volume.RBD.CephMonitors, nil } else if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD != nil { - return spec.PersistentVolume.Spec.RBD, spec.ReadOnly, nil + return spec.PersistentVolume.Spec.RBD.CephMonitors, nil } - return nil, false, fmt.Errorf("Spec does not reference a RBD volume type") + 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 parsePodSecret(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (string, error) { @@ -531,3 +630,26 @@ func parseSecretMap(secretMap map[string]string) (string, error) { // 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") +} diff --git a/pkg/volume/rbd/rbd_test.go b/pkg/volume/rbd/rbd_test.go index 02f5d324c31..8a0a098de36 100644 --- a/pkg/volume/rbd/rbd_test.go +++ b/pkg/volume/rbd/rbd_test.go @@ -22,6 +22,7 @@ import ( "os" "path/filepath" "reflect" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -92,7 +93,7 @@ func (fake *fakeDiskManager) DetachDisk(c rbdUnmounter, mntPath string) error { return nil } -func (fake *fakeDiskManager) CreateImage(provisioner *rbdVolumeProvisioner) (r *v1.RBDVolumeSource, volumeSizeGB int, err error) { +func (fake *fakeDiskManager) CreateImage(provisioner *rbdVolumeProvisioner) (r *v1.RBDPersistentVolumeSource, volumeSizeGB int, err error) { return nil, 0, fmt.Errorf("not implemented") } @@ -180,7 +181,7 @@ func TestPluginPersistentVolume(t *testing.T) { }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ - RBD: &v1.RBDVolumeSource{ + RBD: &v1.RBDPersistentVolumeSource{ CephMonitors: []string{"a", "b"}, RBDImage: "bar", FSType: "ext4", @@ -205,7 +206,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ - RBD: &v1.RBDVolumeSource{ + RBD: &v1.RBDPersistentVolumeSource{ CephMonitors: []string{"a", "b"}, RBDImage: "bar", FSType: "ext4", @@ -329,3 +330,35 @@ func TestPersistAndLoadRBD(t *testing.T) { } } } + +func TestGetSecretNameAndNamespace(t *testing.T) { + secretName := "test-secret-name" + secretNamespace := "test-secret-namespace" + + volSpec := &volume.Spec{ + PersistentVolume: &v1.PersistentVolume{ + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeSource: v1.PersistentVolumeSource{ + RBD: &v1.RBDPersistentVolumeSource{ + CephMonitors: []string{"a", "b"}, + RBDImage: "bar", + FSType: "ext4", + }, + }, + }, + }, + } + + secretRef := new(v1.SecretReference) + secretRef.Name = secretName + secretRef.Namespace = secretNamespace + volSpec.PersistentVolume.Spec.PersistentVolumeSource.RBD.SecretRef = secretRef + + foundSecretName, foundSecretNamespace, err := getSecretNameAndNamespace(volSpec, "default") + if err != nil { + t.Errorf("getSecretNameAndNamespace failed to get Secret's name and namespace: %v", err) + } + if strings.Compare(secretName, foundSecretName) != 0 || strings.Compare(secretNamespace, foundSecretNamespace) != 0 { + t.Errorf("getSecretNameAndNamespace returned incorrect values, expected %s and %s but got %s and %s", secretName, secretNamespace, foundSecretName, foundSecretNamespace) + } +} diff --git a/pkg/volume/rbd/rbd_util.go b/pkg/volume/rbd/rbd_util.go index 1f06f16a4e8..89ec3125b83 100644 --- a/pkg/volume/rbd/rbd_util.go +++ b/pkg/volume/rbd/rbd_util.go @@ -362,7 +362,7 @@ func (util *RBDUtil) DetachDisk(c rbdUnmounter, mntPath string) error { return nil } -func (util *RBDUtil) CreateImage(p *rbdVolumeProvisioner) (r *v1.RBDVolumeSource, size int, err error) { +func (util *RBDUtil) CreateImage(p *rbdVolumeProvisioner) (r *v1.RBDPersistentVolumeSource, size int, err error) { var output []byte capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] volSizeBytes := capacity.Value() @@ -400,7 +400,7 @@ func (util *RBDUtil) CreateImage(p *rbdVolumeProvisioner) (r *v1.RBDVolumeSource return nil, 0, fmt.Errorf("failed to create rbd image: %v, command output: %s", err, string(output)) } - return &v1.RBDVolumeSource{ + return &v1.RBDPersistentVolumeSource{ CephMonitors: p.rbdMounter.Mon, RBDImage: p.rbdMounter.Image, RBDPool: p.rbdMounter.Pool, diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index b9cbf2d4b5f..b58da87bc8a 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -398,7 +398,7 @@ type PersistentVolumeSource struct { // RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md // +optional - RBD *RBDVolumeSource `json:"rbd,omitempty" protobuf:"bytes,6,opt,name=rbd"` + RBD *RBDPersistentVolumeSource `json:"rbd,omitempty" protobuf:"bytes,6,opt,name=rbd"` // ISCSI represents an ISCSI Disk resource that is attached to a // kubelet's host machine and then exposed to the pod. Provisioned by an admin. // +optional @@ -838,6 +838,50 @@ type RBDVolumeSource struct { ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,8,opt,name=readOnly"` } +// Represents a Rados Block Device mount that lasts the lifetime of a pod. +// RBD volumes support ownership management and SELinux relabeling. +type RBDPersistentVolumeSource struct { + // A collection of Ceph monitors. + // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it + CephMonitors []string `json:"monitors" protobuf:"bytes,1,rep,name=monitors"` + // The rados image name. + // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it + RBDImage string `json:"image" protobuf:"bytes,2,opt,name=image"` + // Filesystem type of the volume that you want to mount. + // Tip: Ensure that the filesystem type is supported by the host operating system. + // Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + // More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + // TODO: how do we prevent errors in the filesystem from compromising the machine + // +optional + FSType string `json:"fsType,omitempty" protobuf:"bytes,3,opt,name=fsType"` + // The rados pool name. + // Default is rbd. + // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it + // +optional + RBDPool string `json:"pool,omitempty" protobuf:"bytes,4,opt,name=pool"` + // The rados user name. + // Default is admin. + // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it + // +optional + RadosUser string `json:"user,omitempty" protobuf:"bytes,5,opt,name=user"` + // Keyring is the path to key ring for RBDUser. + // Default is /etc/ceph/keyring. + // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it + // +optional + Keyring string `json:"keyring,omitempty" protobuf:"bytes,6,opt,name=keyring"` + // SecretRef is name of the authentication secret for RBDUser. If provided + // overrides keyring. + // Default is nil. + // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it + // +optional + SecretRef *SecretReference `json:"secretRef,omitempty" protobuf:"bytes,7,opt,name=secretRef"` + // ReadOnly here will force the ReadOnly setting in VolumeMounts. + // Defaults to false. + // More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it + // +optional + ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,8,opt,name=readOnly"` +} + // Represents a cinder volume resource in Openstack. // A Cinder volume must exist before mounting to a container. // The volume must also be in the same region as the kubelet.