azure file volume: add secret namespace api

Signed-off-by: Huamin Chen <hchen@redhat.com>
pull/6/head
Huamin Chen 2017-06-16 15:23:22 +00:00
parent 55a20bb901
commit 4525446af2
12 changed files with 238 additions and 45 deletions

View File

@ -37,9 +37,15 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
source := &pv.Spec.PersistentVolumeSource
switch {
case source.AzureFile != nil:
if source.AzureFile.SecretNamespace != nil && len(*source.AzureFile.SecretNamespace) > 0 {
if len(source.AzureFile.SecretName) > 0 && !visitor(*source.AzureFile.SecretNamespace, source.AzureFile.SecretName) {
return false
}
} else {
if len(source.AzureFile.SecretName) > 0 && !visitor(getClaimRefNamespace(pv), source.AzureFile.SecretName) {
return false
}
}
return true
case source.CephFS != nil:
if source.CephFS.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.CephFS.SecretRef.Name) {

View File

@ -30,12 +30,19 @@ import (
func TestPVSecrets(t *testing.T) {
// Stub containing all possible secret references in a PV.
// The names of the referenced secrets match struct paths detected by reflection.
secretNamespace := "Spec.PersistentVolumeSource.AzureFile.SecretNamespace"
pvs := []*api.PersistentVolume{
{Spec: api.PersistentVolumeSpec{
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
PersistentVolumeSource: api.PersistentVolumeSource{
AzureFile: &api.AzureFileVolumeSource{
AzureFile: &api.AzureFilePersistentVolumeSource{
SecretName: "Spec.PersistentVolumeSource.AzureFile.SecretName"}}}},
{Spec: api.PersistentVolumeSpec{
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
PersistentVolumeSource: api.PersistentVolumeSource{
AzureFile: &api.AzureFilePersistentVolumeSource{
SecretName: "Spec.PersistentVolumeSource.AzureFile.SecretName",
SecretNamespace: &secretNamespace}}}},
{Spec: api.PersistentVolumeSpec{
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
PersistentVolumeSource: api.PersistentVolumeSource{
@ -88,6 +95,7 @@ func TestPVSecrets(t *testing.T) {
// excludedSecretPaths holds struct paths to fields with "secret" in the name that are not actually references to secret API objects
excludedSecretPaths := sets.NewString(
"Spec.PersistentVolumeSource.CephFS.SecretFile",
"Spec.PersistentVolumeSource.AzureFile.SecretNamespace",
)
// expectedSecretPaths holds struct paths to fields with "secret" in the name that are references to secret API objects.
// every path here should be represented as an example in the PV stub above, with the secret name set to the path.
@ -122,6 +130,7 @@ func TestPVSecrets(t *testing.T) {
expectedNamespacedNames := sets.NewString(
"claimrefns/Spec.PersistentVolumeSource.AzureFile.SecretName",
"Spec.PersistentVolumeSource.AzureFile.SecretNamespace/Spec.PersistentVolumeSource.AzureFile.SecretName",
"claimrefns/Spec.PersistentVolumeSource.CephFS.SecretRef",
"claimrefns/Spec.PersistentVolumeSource.FlexVolume.SecretRef",
"claimrefns/Spec.PersistentVolumeSource.RBD.SecretRef",

View File

@ -369,7 +369,7 @@ type PersistentVolumeSource struct {
Flocker *FlockerVolumeSource
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
// +optional
AzureFile *AzureFileVolumeSource
AzureFile *AzureFilePersistentVolumeSource
// VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine
// +optional
VsphereVolume *VsphereVirtualDiskVolumeSource
@ -1087,6 +1087,22 @@ type AzureFileVolumeSource struct {
ReadOnly bool
}
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
type AzureFilePersistentVolumeSource struct {
// the name of secret that contains Azure Storage Account Name and Key
SecretName string
// Share Name
ShareName string
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool
// the namespace of the secret that contains Azure Storage Account Name and Key
// default is the same as the Pod
// +optional
SecretNamespace *string
}
// Represents a vSphere volume resource.
type VsphereVirtualDiskVolumeSource struct {
// Path that identifies vSphere volume vmdk

View File

@ -511,6 +511,7 @@ func autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *v1.Az
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
out.SecretNamespace = (*string)(unsafe.Pointer(in.SecretNamespace))
return nil
}
@ -523,6 +524,7 @@ func autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in *api.A
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
out.SecretNamespace = (*string)(unsafe.Pointer(in.SecretNamespace))
return nil
}

View File

@ -1098,6 +1098,22 @@ func validateAzureFile(azure *api.AzureFileVolumeSource, fldPath *field.Path) fi
return allErrs
}
func validateAzureFilePV(azure *api.AzureFilePersistentVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if azure.SecretName == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
}
if azure.ShareName == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("shareName"), ""))
}
if azure.SecretNamespace != nil {
if len(*azure.SecretNamespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("secretNamespace"), ""))
}
}
return allErrs
}
func validateAzureDisk(azure *api.AzureDiskVolumeSource, fldPath *field.Path) field.ErrorList {
var supportedCachingModes = sets.NewString(string(api.AzureDataDiskCachingNone), string(api.AzureDataDiskCachingReadOnly), string(api.AzureDataDiskCachingReadWrite))
var supportedDiskKinds = sets.NewString(string(api.AzureSharedBlobDisk), string(api.AzureDedicatedBlobDisk), string(api.AzureManagedDisk))
@ -1375,7 +1391,7 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
} else {
numVolumes++
allErrs = append(allErrs, validateAzureFile(pv.Spec.AzureFile, specPath.Child("azureFile"))...)
allErrs = append(allErrs, validateAzureFilePV(pv.Spec.AzureFile, specPath.Child("azureFile"))...)
}
}

View File

@ -3680,7 +3680,9 @@ func (in *PersistentVolumeSource) DeepCopyInto(out *PersistentVolumeSource) {
*out = nil
} else {
*out = new(AzureFileVolumeSource)
**out = **in
if err := DeepCopy_api_AzureFileVolumeSource(*in, *out, c); err != nil {
return err
}
}
}
if in.VsphereVolume != nil {
@ -5994,7 +5996,9 @@ func (in *VolumeSource) DeepCopyInto(out *VolumeSource) {
*out = nil
} else {
*out = new(AzureFileVolumeSource)
**out = **in
if err := DeepCopy_api_AzureFileVolumeSource(*in, *out, c); err != nil {
return err
}
}
}
if in.ConfigMap != nil {

View File

@ -993,6 +993,19 @@ func printAzureFileVolumeSource(azureFile *api.AzureFileVolumeSource, w PrefixWr
azureFile.SecretName, azureFile.ShareName, azureFile.ReadOnly)
}
func printAzureFilePersistentVolumeSource(azureFile *api.AzureFilePersistentVolumeSource, w PrefixWriter) {
ns := ""
if azureFile.SecretNamespace != nil {
ns = *azureFile.SecretNamespace
}
w.Write(LEVEL_2, "Type:\tAzureFile (an Azure File Service mount on the host and bind mount to the pod)\n"+
" SecretName:\t%v\n"+
" SecretNamespace:\t%v\n"+
" ShareName:\t%v\n"+
" ReadOnly:\t%v\n",
azureFile.SecretName, ns, azureFile.ShareName, azureFile.ReadOnly)
}
func printFlexVolumeSource(flex *api.FlexVolumeSource, w PrefixWriter) {
w.Write(LEVEL_2, "Type:\tFlexVolume (a generic volume resource that is provisioned/attached using an exec based plugin)\n"+
" Driver:\t%v\n"+
@ -1088,7 +1101,7 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) (
case pv.Spec.FC != nil:
printFCVolumeSource(pv.Spec.FC, w)
case pv.Spec.AzureFile != nil:
printAzureFileVolumeSource(pv.Spec.AzureFile, w)
printAzureFilePersistentVolumeSource(pv.Spec.AzureFile, w)
case pv.Spec.FlexVolume != nil:
printFlexVolumeSource(pv.Spec.FlexVolume, w)
case pv.Spec.Flocker != nil:

View File

@ -63,12 +63,12 @@ func (plugin *azureFilePlugin) GetPluginName() string {
}
func (plugin *azureFilePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
volumeSource, _, err := getVolumeSource(spec)
share, _, err := getVolumeSource(spec)
if err != nil {
return "", err
}
return volumeSource.ShareName, nil
return share, nil
}
func (plugin *azureFilePlugin) CanSupport(spec *volume.Spec) bool {
@ -102,11 +102,11 @@ func (plugin *azureFilePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volu
}
func (plugin *azureFilePlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, util azureUtil, mounter mount.Interface) (volume.Mounter, error) {
source, readOnly, err := getVolumeSource(spec)
share, readOnly, err := getVolumeSource(spec)
if err != nil {
return nil, err
}
secretName, secretNamespace, err := getSecretNameAndNamespace(spec, pod.Namespace)
return &azureFileMounter{
azureFile: &azureFile{
volName: spec.Name(),
@ -116,8 +116,9 @@ func (plugin *azureFilePlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod
MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, spec.Name(), plugin.host)),
},
util: util,
secretName: source.SecretName,
shareName: source.ShareName,
secretNamespace: secretNamespace,
secretName: secretName,
shareName: share,
readOnly: readOnly,
mountOptions: volume.MountOptionFromSpec(spec),
}, nil
@ -168,6 +169,7 @@ type azureFileMounter struct {
*azureFile
util azureUtil
secretName string
secretNamespace string
shareName string
readOnly bool
mountOptions []string
@ -205,7 +207,7 @@ func (b *azureFileMounter) SetUpAt(dir string, fsGroup *int64) error {
return nil
}
var accountKey, accountName string
if accountName, accountKey, err = b.util.GetAzureCredentials(b.plugin.host, b.pod.Namespace, b.secretName); err != nil {
if accountName, accountKey, err = b.util.GetAzureCredentials(b.plugin.host, b.secretNamespace, b.secretName); err != nil {
return err
}
os.MkdirAll(dir, 0700)
@ -260,16 +262,43 @@ func (c *azureFileUnmounter) TearDownAt(dir string) error {
return util.UnmountPath(dir, c.mounter)
}
func getVolumeSource(
spec *volume.Spec) (*v1.AzureFileVolumeSource, bool, error) {
func getVolumeSource(spec *volume.Spec) (string, bool, error) {
if spec.Volume != nil && spec.Volume.AzureFile != nil {
return spec.Volume.AzureFile, spec.Volume.AzureFile.ReadOnly, nil
share := spec.Volume.AzureFile.ShareName
readOnly := spec.Volume.AzureFile.ReadOnly
return share, readOnly, nil
} else if spec.PersistentVolume != nil &&
spec.PersistentVolume.Spec.AzureFile != nil {
return spec.PersistentVolume.Spec.AzureFile, spec.ReadOnly, nil
share := spec.PersistentVolume.Spec.AzureFile.ShareName
readOnly := spec.ReadOnly
return share, readOnly, nil
}
return "", false, fmt.Errorf("Spec does not reference an AzureFile volume type")
}
return nil, false, fmt.Errorf("Spec does not reference an AzureFile volume type")
func getSecretNameAndNamespace(spec *volume.Spec, defaultNamespace string) (string, string, error) {
secretName := ""
secretNamespace := ""
if spec.Volume != nil && spec.Volume.AzureFile != nil {
secretName = spec.Volume.AzureFile.SecretName
secretNamespace = defaultNamespace
} else if spec.PersistentVolume != nil &&
spec.PersistentVolume.Spec.AzureFile != nil {
secretNamespace = defaultNamespace
if spec.PersistentVolume.Spec.AzureFile.SecretNamespace != nil {
secretNamespace = *spec.PersistentVolume.Spec.AzureFile.SecretNamespace
}
secretName = spec.PersistentVolume.Spec.AzureFile.SecretName
} else {
return "", "", fmt.Errorf("Spec does not reference an AzureFile volume type")
}
if len(secretNamespace) == 0 {
return "", "", fmt.Errorf("invalid Azure volume: nil namespace")
}
return secretName, secretNamespace, nil
}
func getAzureCloud(cloudProvider cloudprovider.Interface) (*azure.Cloud, error) {

View File

@ -53,7 +53,7 @@ func TestCanSupport(t *testing.T) {
if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{AzureFile: &v1.AzureFileVolumeSource{}}}}) {
t.Errorf("Expected true")
}
if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{AzureFile: &v1.AzureFileVolumeSource{}}}}}) {
if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{AzureFile: &v1.AzureFilePersistentVolumeSource{}}}}}) {
t.Errorf("Expected true")
}
}
@ -204,7 +204,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFileVolumeSource{},
AzureFile: &v1.AzureFilePersistentVolumeSource{},
},
ClaimRef: &v1.ObjectReference{
Name: "claimA",
@ -287,3 +287,83 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) {
t.Errorf("Volume Unmounter can be type-assert to Mounter")
}
}
type testcase struct {
name string
defaultNs string
spec *volume.Spec
// Expected return of the test
expectedName string
expectedNs string
expectedError error
}
func TestGetSecretNameAndNamespaceForPV(t *testing.T) {
secretNs := "ns"
tests := []testcase{
{
name: "persistent volume source",
defaultNs: "default",
spec: &volume.Spec{
PersistentVolume: &v1.PersistentVolume{
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFilePersistentVolumeSource{
ShareName: "share",
SecretName: "name",
SecretNamespace: &secretNs,
},
},
},
},
},
expectedName: "name",
expectedNs: "ns",
expectedError: nil,
},
{
name: "persistent volume source without namespace",
defaultNs: "default",
spec: &volume.Spec{
PersistentVolume: &v1.PersistentVolume{
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFilePersistentVolumeSource{
ShareName: "share",
SecretName: "name",
},
},
},
},
},
expectedName: "name",
expectedNs: "default",
expectedError: nil,
},
{
name: "pod volume source",
defaultNs: "default",
spec: &volume.Spec{
Volume: &v1.Volume{
VolumeSource: v1.VolumeSource{
AzureFile: &v1.AzureFileVolumeSource{
ShareName: "share",
SecretName: "name",
},
},
},
},
expectedName: "name",
expectedNs: "default",
expectedError: nil,
},
}
for _, testcase := range tests {
resultName, resultNs, err := getSecretNameAndNamespace(testcase.spec, testcase.defaultNs)
if err != testcase.expectedError || resultName != testcase.expectedName || resultNs != testcase.expectedNs {
t.Errorf("%s failed: expected err=%v ns=%q name=%q, got %v/%q/%q", testcase.name, testcase.expectedError, testcase.expectedNs, testcase.expectedName,
err, resultNs, resultName)
}
}
}

View File

@ -63,15 +63,13 @@ func (plugin *azureFilePlugin) newDeleterInternal(spec *volume.Spec, util azureU
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureFile == nil {
return nil, fmt.Errorf("invalid PV spec")
}
pvSpec := spec.PersistentVolume
if pvSpec.Spec.ClaimRef.Namespace == "" {
glog.Errorf("namespace cannot be nil")
return nil, fmt.Errorf("invalid PV spec: nil namespace")
secretName, secretNamespace, err := getSecretNameAndNamespace(spec, spec.PersistentVolume.Spec.ClaimRef.Namespace)
if err != nil {
return nil, err
}
nameSpace := pvSpec.Spec.ClaimRef.Namespace
secretName := pvSpec.Spec.AzureFile.SecretName
shareName := pvSpec.Spec.AzureFile.ShareName
if accountName, accountKey, err := util.GetAzureCredentials(plugin.host, nameSpace, secretName); err != nil {
shareName := spec.PersistentVolume.Spec.AzureFile.ShareName
if accountName, accountKey, err := util.GetAzureCredentials(plugin.host, secretNamespace, secretName); err != nil {
return nil, err
} else {
return &azureFileDeleter{
@ -144,7 +142,7 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) {
capacity := a.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
requestBytes := capacity.Value()
requestGB := int(volume.RoundUpSize(requestBytes, 1024*1024*1024))
secretNamespace := a.options.PVC.Namespace
// Apply ProvisionerParameters (case-insensitive). We leave validation of
// the values to the cloud provider.
for k, v := range a.options.Parameters {
@ -155,6 +153,8 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) {
location = v
case "storageaccount":
account = v
case "secretnamespace":
secretNamespace = v
default:
return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, a.plugin.GetPluginName())
}
@ -168,8 +168,9 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) {
if err != nil {
return nil, err
}
// create a secret for storage account and key
secretName, err := a.util.SetAzureCredentials(a.plugin.host, a.options.PVC.Namespace, account, key)
secretName, err := a.util.SetAzureCredentials(a.plugin.host, secretNamespace, account, key)
if err != nil {
return nil, err
}
@ -189,9 +190,10 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) {
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", requestGB)),
},
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFileVolumeSource{
AzureFile: &v1.AzureFilePersistentVolumeSource{
SecretName: secretName,
ShareName: name,
SecretNamespace: &secretNamespace,
},
},
},

View File

@ -423,7 +423,7 @@ type PersistentVolumeSource struct {
FlexVolume *FlexVolumeSource `json:"flexVolume,omitempty" protobuf:"bytes,12,opt,name=flexVolume"`
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
// +optional
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty" protobuf:"bytes,13,opt,name=azureFile"`
AzureFile *AzureFilePersistentVolumeSource `json:"azureFile,omitempty" protobuf:"bytes,13,opt,name=azureFile"`
// VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine
// +optional
VsphereVolume *VsphereVirtualDiskVolumeSource `json:"vsphereVolume,omitempty" protobuf:"bytes,14,opt,name=vsphereVolume"`
@ -1169,6 +1169,22 @@ type AzureFileVolumeSource struct {
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,3,opt,name=readOnly"`
}
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
type AzureFilePersistentVolumeSource struct {
// the name of secret that contains Azure Storage Account Name and Key
SecretName string `json:"secretName" protobuf:"bytes,1,opt,name=secretName"`
// Share Name
ShareName string `json:"shareName" protobuf:"bytes,2,opt,name=shareName"`
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,3,opt,name=readOnly"`
// the namespace of the secret that contains Azure Storage Account Name and Key
// default is the same as the Pod
// +optional
SecretNamespace *string `json:"secretNamespace" protobuf:"bytes,4,opt,name=secretNamespace"`
}
// Represents a vSphere volume resource.
type VsphereVirtualDiskVolumeSource struct {
// Path that identifies vSphere volume vmdk

View File

@ -136,7 +136,7 @@ func TestNodeAuthorizer(t *testing.T) {
AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
Capacity: api.ResourceList{api.ResourceStorage: resource.MustParse("1")},
ClaimRef: &api.ObjectReference{Namespace: "ns", Name: "mypvc"},
PersistentVolumeSource: api.PersistentVolumeSource{AzureFile: &api.AzureFileVolumeSource{ShareName: "default", SecretName: "mypvsecret"}},
PersistentVolumeSource: api.PersistentVolumeSource{AzureFile: &api.AzureFilePersistentVolumeSource{ShareName: "default", SecretName: "mypvsecret"}},
},
}); err != nil {
t.Fatal(err)