From 12d6b87894b898f08dc7042120e7aa8dee0521e7 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Thu, 9 Mar 2017 11:46:22 -0500 Subject: [PATCH] Validation PVs for mount options We are going to move the validation in its own package and we will be calling validation for individual volume types as needed. --- hack/.linted_packages | 1 + pkg/api/validation/BUILD | 26 ----- pkg/api/validation/validation.go | 20 ---- pkg/api/validation/validation_test.go | 44 -------- pkg/api/validation/volume_plugins.go | 105 ------------------ pkg/registry/core/persistentvolume/BUILD | 1 + .../core/persistentvolume/strategy.go | 10 +- pkg/volume/BUILD | 2 +- pkg/volume/util.go | 11 -- pkg/volume/validation/BUILD | 44 ++++++++ pkg/volume/validation/pv_validation.go | 58 ++++++++++ pkg/volume/validation/pv_validation_test.go | 86 ++++++++++++++ 12 files changed, 198 insertions(+), 210 deletions(-) delete mode 100644 pkg/api/validation/volume_plugins.go create mode 100644 pkg/volume/validation/BUILD create mode 100644 pkg/volume/validation/pv_validation.go create mode 100644 pkg/volume/validation/pv_validation_test.go diff --git a/hack/.linted_packages b/hack/.linted_packages index 8c2dc547ab..3fc4d3b4ed 100644 --- a/hack/.linted_packages +++ b/hack/.linted_packages @@ -262,6 +262,7 @@ pkg/volume/util/nestedpendingoperations pkg/volume/util/operationexecutor pkg/volume/util/types pkg/volume/util/volumehelper +pkg/volume/validation pkg/watch pkg/watch/json pkg/watch/versioned diff --git a/pkg/api/validation/BUILD b/pkg/api/validation/BUILD index 3ac5a3defe..261f78db26 100644 --- a/pkg/api/validation/BUILD +++ b/pkg/api/validation/BUILD @@ -15,7 +15,6 @@ go_library( "events.go", "schema.go", "validation.go", - "volume_plugins.go", ], tags = ["automanaged"], deps = [ @@ -27,30 +26,6 @@ go_library( "//pkg/capabilities:go_default_library", "//pkg/features:go_default_library", "//pkg/security/apparmor:go_default_library", - "//pkg/volume:go_default_library", - "//pkg/volume/aws_ebs:go_default_library", - "//pkg/volume/azure_dd:go_default_library", - "//pkg/volume/azure_file:go_default_library", - "//pkg/volume/cephfs:go_default_library", - "//pkg/volume/cinder:go_default_library", - "//pkg/volume/configmap:go_default_library", - "//pkg/volume/downwardapi:go_default_library", - "//pkg/volume/empty_dir:go_default_library", - "//pkg/volume/fc:go_default_library", - "//pkg/volume/flexvolume:go_default_library", - "//pkg/volume/flocker:go_default_library", - "//pkg/volume/gce_pd:go_default_library", - "//pkg/volume/git_repo:go_default_library", - "//pkg/volume/glusterfs:go_default_library", - "//pkg/volume/host_path:go_default_library", - "//pkg/volume/iscsi:go_default_library", - "//pkg/volume/nfs:go_default_library", - "//pkg/volume/photon_pd:go_default_library", - "//pkg/volume/projected:go_default_library", - "//pkg/volume/quobyte:go_default_library", - "//pkg/volume/rbd:go_default_library", - "//pkg/volume/secret:go_default_library", - "//pkg/volume/vsphere_volume:go_default_library", "//vendor:github.com/emicklei/go-restful/swagger", "//vendor:github.com/exponent-io/jsonpath", "//vendor:github.com/golang/glog", @@ -100,7 +75,6 @@ go_test( "//pkg/apis/extensions/v1beta1:go_default_library", "//pkg/capabilities:go_default_library", "//pkg/security/apparmor:go_default_library", - "//pkg/volume:go_default_library", "//vendor:github.com/ghodss/yaml", "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/api/testing", diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 498118222e..1dd8845200 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -47,7 +47,6 @@ import ( "k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/security/apparmor" - "k8s.io/kubernetes/pkg/volume" ) // TODO: delete this global variable when we enable the validation of common @@ -64,11 +63,6 @@ var volumeModeErrorMsg string = "must be a number between 0 and 0777 (octal), bo // BannedOwners is a black list of object that are not allowed to be owners. var BannedOwners = genericvalidation.BannedOwners -var volumePlugins []volume.VolumePlugin - -func init() { - volumePlugins = probeVolumePlugins() -} // ValidateHasLabel requires that metav1.ObjectMeta has a Label with key and expectedValue func ValidateHasLabel(meta metav1.ObjectMeta, fldPath *field.Path, key, expectedValue string) field.ErrorList { @@ -1064,20 +1058,6 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList { } } - volumePlugin := findPluginBySpec(volumePlugins, pv) - mountOptions := volume.MountOptionFromApiPV(pv) - - metaField := field.NewPath("metadata") - if volumePlugin == nil && len(mountOptions) > 0 { - allErrs = append(allErrs, field.Forbidden(metaField.Child("annotations", volume.MountOptionAnnotation), "may not specify mount options for this volume type")) - } - - if volumePlugin != nil { - if !volumePlugin.SupportsMountOption() && len(mountOptions) > 0 { - allErrs = append(allErrs, field.Forbidden(metaField.Child("annotations", volume.MountOptionAnnotation), "may not specify mount options for this volume type")) - } - } - numVolumes := 0 if pv.Spec.HostPath != nil { if numVolumes > 0 { diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index dc64a4e071..f131cc16cd 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -31,7 +31,6 @@ import ( "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/security/apparmor" - "k8s.io/kubernetes/pkg/volume" ) const ( @@ -207,30 +206,6 @@ func TestValidatePersistentVolumes(t *testing.T) { PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle, }), }, - "volume with valid mount option for nfs": { - isExpectedFailure: false, - volume: testVolumeWithMountOption("good-nfs-mount-volume", "", "ro,nfsvers=3", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, - PersistentVolumeSource: api.PersistentVolumeSource{ - NFS: &api.NFSVolumeSource{Server: "localhost", Path: "/srv", ReadOnly: false}, - }, - }), - }, - "volume with mount option for host path": { - isExpectedFailure: true, - volume: testVolumeWithMountOption("bad-hostpath-mount-volume", "", "ro,nfsvers=3", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/a/.."}, - }, - }), - }, "invalid-storage-class-name": { isExpectedFailure: true, volume: testVolume("invalid-storage-class-name", "", api.PersistentVolumeSpec{ @@ -280,25 +255,6 @@ func testVolumeClaimStorageClass(name string, namespace string, annval string, s } } -func testVolumeWithMountOption(name string, namespace string, mountOptions string, spec api.PersistentVolumeSpec) *api.PersistentVolume { - annotations := map[string]string{ - volume.MountOptionAnnotation: mountOptions, - } - objMeta := metav1.ObjectMeta{ - Name: name, - Annotations: annotations, - } - - if namespace != "" { - objMeta.Namespace = namespace - } - - return &api.PersistentVolume{ - ObjectMeta: objMeta, - Spec: spec, - } -} - func testVolumeClaimAnnotation(name string, namespace string, ann string, annval string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim { annotations := map[string]string{ ann: annval, diff --git a/pkg/api/validation/volume_plugins.go b/pkg/api/validation/volume_plugins.go deleted file mode 100644 index d147b711c7..0000000000 --- a/pkg/api/validation/volume_plugins.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -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 validation - -import ( - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/volume" - "k8s.io/kubernetes/pkg/volume/aws_ebs" - "k8s.io/kubernetes/pkg/volume/azure_dd" - "k8s.io/kubernetes/pkg/volume/azure_file" - "k8s.io/kubernetes/pkg/volume/cephfs" - "k8s.io/kubernetes/pkg/volume/cinder" - "k8s.io/kubernetes/pkg/volume/configmap" - "k8s.io/kubernetes/pkg/volume/downwardapi" - "k8s.io/kubernetes/pkg/volume/empty_dir" - "k8s.io/kubernetes/pkg/volume/fc" - "k8s.io/kubernetes/pkg/volume/flexvolume" - "k8s.io/kubernetes/pkg/volume/flocker" - "k8s.io/kubernetes/pkg/volume/gce_pd" - "k8s.io/kubernetes/pkg/volume/git_repo" - "k8s.io/kubernetes/pkg/volume/glusterfs" - "k8s.io/kubernetes/pkg/volume/host_path" - "k8s.io/kubernetes/pkg/volume/iscsi" - "k8s.io/kubernetes/pkg/volume/nfs" - "k8s.io/kubernetes/pkg/volume/photon_pd" - "k8s.io/kubernetes/pkg/volume/projected" - "k8s.io/kubernetes/pkg/volume/quobyte" - "k8s.io/kubernetes/pkg/volume/rbd" - "k8s.io/kubernetes/pkg/volume/secret" - "k8s.io/kubernetes/pkg/volume/vsphere_volume" -) - -func probeVolumePlugins() []volume.VolumePlugin { - allPlugins := []volume.VolumePlugin{} - - // list of volume plugins to probe for - allPlugins = append(allPlugins, aws_ebs.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, empty_dir.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, git_repo.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, host_path.ProbeVolumePlugins(volume.VolumeConfig{})...) - allPlugins = append(allPlugins, nfs.ProbeVolumePlugins(volume.VolumeConfig{})...) - allPlugins = append(allPlugins, secret.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, iscsi.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, quobyte.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, cephfs.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, downwardapi.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins("")...) - allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, configmap.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, projected.ProbeVolumePlugins()...) - return allPlugins -} - -func findPluginBySpec(volumePlugins []volume.VolumePlugin, pv *api.PersistentVolume) volume.VolumePlugin { - matches := []volume.VolumePlugin{} - v1Pv := &v1.PersistentVolume{} - err := v1.Convert_api_PersistentVolume_To_v1_PersistentVolume(pv, v1Pv, nil) - if err != nil { - glog.Errorf("Error converting to v1.PersistentVolume: %v", err) - return nil - } - volumeSpec := &volume.Spec{PersistentVolume: v1Pv} - for _, plugin := range volumePlugins { - if plugin.CanSupport(volumeSpec) { - matches = append(matches, plugin) - } - } - - if len(matches) == 0 { - glog.V(5).Infof("No matching plugin found for : %s", pv.Name) - return nil - } - - if len(matches) > 1 { - glog.V(3).Infof("multiple volume plugins matched for : %s ", pv.Name) - return nil - } - - return matches[0] -} diff --git a/pkg/registry/core/persistentvolume/BUILD b/pkg/registry/core/persistentvolume/BUILD index 1c73e93082..851690ae19 100644 --- a/pkg/registry/core/persistentvolume/BUILD +++ b/pkg/registry/core/persistentvolume/BUILD @@ -18,6 +18,7 @@ go_library( deps = [ "//pkg/api:go_default_library", "//pkg/api/validation:go_default_library", + "//pkg/volume/validation:go_default_library", "//vendor:k8s.io/apimachinery/pkg/fields", "//vendor:k8s.io/apimachinery/pkg/labels", "//vendor:k8s.io/apimachinery/pkg/runtime", diff --git a/pkg/registry/core/persistentvolume/strategy.go b/pkg/registry/core/persistentvolume/strategy.go index 7157617cd0..4d40ec5be8 100644 --- a/pkg/registry/core/persistentvolume/strategy.go +++ b/pkg/registry/core/persistentvolume/strategy.go @@ -29,6 +29,7 @@ import ( "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" + volumevalidation "k8s.io/kubernetes/pkg/volume/validation" ) // persistentvolumeStrategy implements behavior for PersistentVolume objects @@ -53,7 +54,8 @@ func (persistentvolumeStrategy) PrepareForCreate(ctx genericapirequest.Context, func (persistentvolumeStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList { persistentvolume := obj.(*api.PersistentVolume) - return validation.ValidatePersistentVolume(persistentvolume) + errorList := validation.ValidatePersistentVolume(persistentvolume) + return append(errorList, volumevalidation.ValidatePersistentVolume(persistentvolume)...) } // Canonicalize normalizes the object after validation. @@ -72,8 +74,10 @@ func (persistentvolumeStrategy) PrepareForUpdate(ctx genericapirequest.Context, } func (persistentvolumeStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { - errorList := validation.ValidatePersistentVolume(obj.(*api.PersistentVolume)) - return append(errorList, validation.ValidatePersistentVolumeUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume))...) + newPv := obj.(*api.PersistentVolume) + errorList := validation.ValidatePersistentVolume(newPv) + errorList = append(errorList, volumevalidation.ValidatePersistentVolume(newPv)...) + return append(errorList, validation.ValidatePersistentVolumeUpdate(newPv, old.(*api.PersistentVolume))...) } func (persistentvolumeStrategy) AllowUnconditionalUpdate() bool { diff --git a/pkg/volume/BUILD b/pkg/volume/BUILD index 5940143e75..822ee8e2e4 100644 --- a/pkg/volume/BUILD +++ b/pkg/volume/BUILD @@ -24,7 +24,6 @@ go_library( ], tags = ["automanaged"], deps = [ - "//pkg/api:go_default_library", "//pkg/api/v1:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library", "//pkg/cloudprovider:go_default_library", @@ -119,6 +118,7 @@ filegroup( "//pkg/volume/secret:all-srcs", "//pkg/volume/testing:all-srcs", "//pkg/volume/util:all-srcs", + "//pkg/volume/validation:all-srcs", "//pkg/volume/vsphere_volume:all-srcs", ], tags = ["automanaged"], diff --git a/pkg/volume/util.go b/pkg/volume/util.go index ec959e8c03..2c423e4da1 100644 --- a/pkg/volume/util.go +++ b/pkg/volume/util.go @@ -23,7 +23,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/watch" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" @@ -388,16 +387,6 @@ func MountOptionFromSpec(spec *Spec, options ...string) []string { return options } -// MountOptionFromApiPV extracts mount options from api.PersistentVolume -func MountOptionFromApiPV(pv *api.PersistentVolume) []string { - mountOptions := []string{} - if mo, ok := pv.Annotations[MountOptionAnnotation]; ok { - moList := strings.Split(mo, ",") - return JoinMountOptions(moList, mountOptions) - } - return mountOptions -} - // JoinMountOptions joins mount options eliminating duplicates func JoinMountOptions(userOptions []string, systemOptions []string) []string { allMountOptions := sets.NewString() diff --git a/pkg/volume/validation/BUILD b/pkg/volume/validation/BUILD new file mode 100644 index 0000000000..6200aabf3a --- /dev/null +++ b/pkg/volume/validation/BUILD @@ -0,0 +1,44 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = ["pv_validation_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//pkg/api:go_default_library", + "//vendor:k8s.io/apimachinery/pkg/api/resource", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + ], +) + +go_library( + name = "go_default_library", + srcs = ["pv_validation.go"], + tags = ["automanaged"], + deps = [ + "//pkg/api:go_default_library", + "//vendor:k8s.io/apimachinery/pkg/util/validation/field", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/volume/validation/pv_validation.go b/pkg/volume/validation/pv_validation.go new file mode 100644 index 0000000000..322b91bff9 --- /dev/null +++ b/pkg/volume/validation/pv_validation.go @@ -0,0 +1,58 @@ +/* +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 validation + +import ( + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/kubernetes/pkg/api" +) + +// MountOptionAnnotation defines mount option annotation used in PVs +const MountOptionAnnotation = "volume.beta.kubernetes.io/mount-options" + +// ValidatePersistentVolume validates PV object for plugin specific validation +// We can put here validations which are specific to volume types. +func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList { + return checkMountOption(pv) +} + +func checkMountOption(pv *api.PersistentVolume) field.ErrorList { + allErrs := field.ErrorList{} + // if PV is of these types we don't return errors + // since mount options is supported + if pv.Spec.GCEPersistentDisk != nil || + pv.Spec.AWSElasticBlockStore != nil || + pv.Spec.Glusterfs != nil || + pv.Spec.NFS != nil || + pv.Spec.RBD != nil || + pv.Spec.Quobyte != nil || + pv.Spec.ISCSI != nil || + pv.Spec.Cinder != nil || + pv.Spec.CephFS != nil || + pv.Spec.AzureFile != nil || + pv.Spec.VsphereVolume != nil || + pv.Spec.AzureDisk != nil || + pv.Spec.PhotonPersistentDisk != nil { + return allErrs + } + // any other type if mount option is present lets return error + if _, ok := pv.Annotations[MountOptionAnnotation]; ok { + metaField := field.NewPath("metadata") + allErrs = append(allErrs, field.Forbidden(metaField.Child("annotations", MountOptionAnnotation), "may not specify mount options for this volume type")) + } + return allErrs +} diff --git a/pkg/volume/validation/pv_validation_test.go b/pkg/volume/validation/pv_validation_test.go new file mode 100644 index 0000000000..968b0db713 --- /dev/null +++ b/pkg/volume/validation/pv_validation_test.go @@ -0,0 +1,86 @@ +/* +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 validation + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/api" +) + +func TestValidatePersistentVolumes(t *testing.T) { + scenarios := map[string]struct { + isExpectedFailure bool + volume *api.PersistentVolume + }{ + "volume with valid mount option for nfs": { + isExpectedFailure: false, + volume: testVolumeWithMountOption("good-nfs-mount-volume", "", "ro,nfsvers=3", api.PersistentVolumeSpec{ + Capacity: api.ResourceList{ + api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), + }, + AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, + PersistentVolumeSource: api.PersistentVolumeSource{ + NFS: &api.NFSVolumeSource{Server: "localhost", Path: "/srv", ReadOnly: false}, + }, + }), + }, + "volume with mount option for host path": { + isExpectedFailure: true, + volume: testVolumeWithMountOption("bad-hostpath-mount-volume", "", "ro,nfsvers=3", api.PersistentVolumeSpec{ + Capacity: api.ResourceList{ + api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), + }, + AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, + PersistentVolumeSource: api.PersistentVolumeSource{ + HostPath: &api.HostPathVolumeSource{Path: "/a/.."}, + }, + }), + }, + } + + for name, scenario := range scenarios { + errs := ValidatePersistentVolume(scenario.volume) + if len(errs) == 0 && scenario.isExpectedFailure { + t.Errorf("Unexpected success for scenario: %s", name) + } + if len(errs) > 0 && !scenario.isExpectedFailure { + t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs) + } + } +} + +func testVolumeWithMountOption(name string, namespace string, mountOptions string, spec api.PersistentVolumeSpec) *api.PersistentVolume { + annotations := map[string]string{ + MountOptionAnnotation: mountOptions, + } + objMeta := metav1.ObjectMeta{ + Name: name, + Annotations: annotations, + } + + if namespace != "" { + objMeta.Namespace = namespace + } + + return &api.PersistentVolume{ + ObjectMeta: objMeta, + Spec: spec, + } +}