Add VolumeType api to PV and PVC

This commit is contained in:
Scott Creeley
2017-08-09 13:51:46 -04:00
parent 842518d336
commit 36f30bc689
19 changed files with 1215 additions and 54 deletions

View File

@@ -64,7 +64,7 @@ const isNotIntegerErrorMsg string = `must be an integer`
const isNotPositiveErrorMsg string = `must be greater than zero`
var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
var volumeModeErrorMsg string = "must be a number between 0 and 0777 (octal), both inclusive"
var fileModeErrorMsg string = "must be a number between 0 and 0777 (octal), both inclusive"
// BannedOwners is a black list of object that are not allowed to be owners.
var BannedOwners = apimachineryvalidation.BannedOwners
@@ -364,10 +364,11 @@ func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fld
return apimachineryvalidation.ValidateNoNewFinalizers(newFinalizers, oldFinalizers, fldPath)
}
func ValidateVolumes(volumes []core.Volume, fldPath *field.Path) (sets.String, field.ErrorList) {
func ValidateVolumes(volumes []core.Volume, fldPath *field.Path) (map[string]core.VolumeSource, field.ErrorList) {
allErrs := field.ErrorList{}
allNames := sets.String{}
vols := make(map[string]core.VolumeSource)
for i, vol := range volumes {
idxPath := fldPath.Index(i)
namePath := idxPath.Child("name")
@@ -382,12 +383,69 @@ func ValidateVolumes(volumes []core.Volume, fldPath *field.Path) (sets.String, f
}
if len(el) == 0 {
allNames.Insert(vol.Name)
vols[vol.Name] = vol.VolumeSource
} else {
allErrs = append(allErrs, el...)
}
}
return allNames, allErrs
return vols, allErrs
}
func IsMatchedVolume(name string, volumes map[string]core.VolumeSource) bool {
if _, ok := volumes[name]; ok {
return true
} else {
return false
}
}
func isMatchedDevice(name string, volumes map[string]core.VolumeSource) (bool, bool) {
if source, ok := volumes[name]; ok {
if source.PersistentVolumeClaim != nil {
return true, true
} else {
return true, false
}
} else {
return false, false
}
}
func mountNameAlreadyExists(name string, devices map[string]string) bool {
if _, ok := devices[name]; ok {
return true
} else {
return false
}
}
func mountPathAlreadyExists(mountPath string, devices map[string]string) bool {
for _, devPath := range devices {
if mountPath == devPath {
return true
}
}
return false
}
func deviceNameAlreadyExists(name string, mounts map[string]string) bool {
if _, ok := mounts[name]; ok {
return true
} else {
return false
}
}
func devicePathAlreadyExists(devicePath string, mounts map[string]string) bool {
for _, mountPath := range mounts {
if mountPath == devicePath {
return true
}
}
return false
}
func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volName string) field.ErrorList {
@@ -745,7 +803,7 @@ func validateSecretVolumeSource(secretSource *core.SecretVolumeSource, fldPath *
secretMode := secretSource.DefaultMode
if secretMode != nil && (*secretMode > 0777 || *secretMode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *secretMode, volumeModeErrorMsg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *secretMode, fileModeErrorMsg))
}
itemsPath := fldPath.Child("items")
@@ -764,7 +822,7 @@ func validateConfigMapVolumeSource(configMapSource *core.ConfigMapVolumeSource,
configMapMode := configMapSource.DefaultMode
if configMapMode != nil && (*configMapMode > 0777 || *configMapMode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *configMapMode, volumeModeErrorMsg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *configMapMode, fileModeErrorMsg))
}
itemsPath := fldPath.Child("items")
@@ -785,7 +843,7 @@ func validateKeyToPath(kp *core.KeyToPath, fldPath *field.Path) field.ErrorList
}
allErrs = append(allErrs, validateLocalNonReservedPath(kp.Path, fldPath.Child("path"))...)
if kp.Mode != nil && (*kp.Mode > 0777 || *kp.Mode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *kp.Mode, volumeModeErrorMsg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *kp.Mode, fileModeErrorMsg))
}
return allErrs
@@ -882,7 +940,7 @@ func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *fi
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
}
if file.Mode != nil && (*file.Mode > 0777 || *file.Mode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, volumeModeErrorMsg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, fileModeErrorMsg))
}
return allErrs
@@ -893,7 +951,7 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *core.DownwardAPIVolumeSo
downwardAPIMode := downwardAPIVolume.DefaultMode
if downwardAPIMode != nil && (*downwardAPIMode > 0777 || *downwardAPIMode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *downwardAPIMode, volumeModeErrorMsg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *downwardAPIMode, fileModeErrorMsg))
}
for _, file := range downwardAPIVolume.Items {
@@ -983,7 +1041,7 @@ func validateProjectedVolumeSource(projection *core.ProjectedVolumeSource, fldPa
projectionMode := projection.DefaultMode
if projectionMode != nil && (*projectionMode > 0777 || *projectionMode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *projectionMode, volumeModeErrorMsg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *projectionMode, fileModeErrorMsg))
}
allErrs = append(allErrs, validateProjectionSources(projection, projectionMode, fldPath)...)
@@ -1344,6 +1402,8 @@ var supportedAccessModes = sets.NewString(string(core.ReadWriteOnce), string(cor
var supportedReclaimPolicy = sets.NewString(string(core.PersistentVolumeReclaimDelete), string(core.PersistentVolumeReclaimRecycle), string(core.PersistentVolumeReclaimRetain))
var supportedVolumeModes = sets.NewString(string(core.PersistentVolumeBlock), string(core.PersistentVolumeFilesystem))
func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
metaPath := field.NewPath("metadata")
allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName, metaPath)
@@ -1582,7 +1642,11 @@ func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
allErrs = append(allErrs, field.Invalid(specPath.Child("storageClassName"), pv.Spec.StorageClassName, msg))
}
}
if pv.Spec.VolumeMode != nil && !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
allErrs = append(allErrs, field.Forbidden(specPath.Child("volumeMode"), "PersistentVolume volumeMode is disabled by feature-gate"))
} else if pv.Spec.VolumeMode != nil && !supportedVolumeModes.Has(string(*pv.Spec.VolumeMode)) {
allErrs = append(allErrs, field.NotSupported(specPath.Child("volumeMode"), *pv.Spec.VolumeMode, supportedVolumeModes.List()))
}
return allErrs
}
@@ -1598,6 +1662,11 @@ func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.E
}
newPv.Status = oldPv.Status
if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...)
}
return allErrs
}
@@ -1646,6 +1715,11 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
allErrs = append(allErrs, field.Invalid(fldPath.Child("storageClassName"), *spec.StorageClassName, msg))
}
}
if spec.VolumeMode != nil && !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("volumeMode"), "PersistentVolumeClaim volumeMode is disabled by feature-gate"))
} else if spec.VolumeMode != nil && !supportedVolumeModes.Has(string(*spec.VolumeMode)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumeMode"), *spec.VolumeMode, supportedVolumeModes.List()))
}
return allErrs
}
@@ -1692,6 +1766,10 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl
// TODO: remove Beta when no longer needed
allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], oldPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], v1.BetaStorageClassAnnotation, field.NewPath("metadata"))...)
if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
allErrs = append(allErrs, ValidateImmutableField(newPvc.Spec.VolumeMode, oldPvc.Spec.VolumeMode, field.NewPath("volumeMode"))...)
}
newPvc.Status = oldPvc.Status
return allErrs
}
@@ -1975,7 +2053,27 @@ func validateSecretKeySelector(s *core.SecretKeySelector, fldPath *field.Path) f
return allErrs
}
func ValidateVolumeMounts(mounts []core.VolumeMount, volumes sets.String, container *core.Container, fldPath *field.Path) field.ErrorList {
func GetVolumeMountMap(mounts []core.VolumeMount) map[string]string {
volmounts := make(map[string]string)
for _, mnt := range mounts {
volmounts[mnt.Name] = mnt.MountPath
}
return volmounts
}
func GetVolumeDeviceMap(devices []core.VolumeDevice) map[string]string {
voldevices := make(map[string]string)
for _, dev := range devices {
voldevices[dev.Name] = dev.DevicePath
}
return voldevices
}
func ValidateVolumeMounts(mounts []core.VolumeMount, voldevices map[string]string, volumes map[string]core.VolumeSource, container *core.Container, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
mountpoints := sets.NewString()
@@ -1983,7 +2081,8 @@ func ValidateVolumeMounts(mounts []core.VolumeMount, volumes sets.String, contai
idxPath := fldPath.Index(i)
if len(mnt.Name) == 0 {
allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
} else if !volumes.Has(mnt.Name) {
}
if !IsMatchedVolume(mnt.Name, volumes) {
allErrs = append(allErrs, field.NotFound(idxPath.Child("name"), mnt.Name))
}
if len(mnt.MountPath) == 0 {
@@ -1993,6 +2092,15 @@ func ValidateVolumeMounts(mounts []core.VolumeMount, volumes sets.String, contai
allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique"))
}
mountpoints.Insert(mnt.MountPath)
// check for overlap with VolumeDevice
if mountNameAlreadyExists(mnt.Name, voldevices) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), mnt.Name, "must not already exist in volumeDevices"))
}
if mountPathAlreadyExists(mnt.MountPath, voldevices) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must not already exist as a path in volumeDevices"))
}
if len(mnt.SubPath) > 0 {
allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...)
}
@@ -2004,6 +2112,60 @@ func ValidateVolumeMounts(mounts []core.VolumeMount, volumes sets.String, contai
return allErrs
}
func ValidateVolumeDevices(devices []core.VolumeDevice, volmounts map[string]string, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
devicepath := sets.NewString()
devicename := sets.NewString()
if devices != nil && !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("volumeDevices"), "Container volumeDevices is disabled by feature-gate"))
return allErrs
}
if devices != nil {
for i, dev := range devices {
idxPath := fldPath.Index(i)
devName := dev.Name
devPath := dev.DevicePath
didMatch, isPVC := isMatchedDevice(devName, volumes)
if len(devName) == 0 {
allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
}
if devicename.Has(devName) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "must be unique"))
}
// Must be PersistentVolumeClaim volume source
if didMatch && !isPVC {
allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "can only use volume source type of PersistentVolumeClaim for block mode"))
}
if !didMatch {
allErrs = append(allErrs, field.NotFound(idxPath.Child("name"), devName))
}
if len(devPath) == 0 {
allErrs = append(allErrs, field.Required(idxPath.Child("devicePath"), ""))
}
if devicepath.Has(devPath) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "must be unique"))
}
if len(devPath) > 0 && len(validatePathNoBacksteps(devPath, fldPath.Child("devicePath"))) > 0 {
allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "can not contain backsteps ('..')"))
} else {
devicepath.Insert(devPath)
}
// check for overlap with VolumeMount
if deviceNameAlreadyExists(devName, volmounts) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "must not already exist in volumeMounts"))
}
if devicePathAlreadyExists(devPath, volmounts) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "must not already exist as a path in volumeMounts"))
}
if len(devName) > 0 {
devicename.Insert(devName)
}
}
}
return allErrs
}
func validateProbe(probe *core.Probe, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@@ -2187,10 +2349,10 @@ func validatePullPolicy(policy core.PullPolicy, fldPath *field.Path) field.Error
return allErrors
}
func validateInitContainers(containers, otherContainers []core.Container, volumes sets.String, fldPath *field.Path) field.ErrorList {
func validateInitContainers(containers, otherContainers []core.Container, deviceVolumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if len(containers) > 0 {
allErrs = append(allErrs, validateContainers(containers, volumes, fldPath)...)
allErrs = append(allErrs, validateContainers(containers, deviceVolumes, fldPath)...)
}
allNames := sets.String{}
@@ -2218,7 +2380,7 @@ func validateInitContainers(containers, otherContainers []core.Container, volume
return allErrs
}
func validateContainers(containers []core.Container, volumes sets.String, fldPath *field.Path) field.ErrorList {
func validateContainers(containers []core.Container, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(containers) == 0 {
@@ -2229,6 +2391,9 @@ func validateContainers(containers []core.Container, volumes sets.String, fldPat
for i, ctr := range containers {
idxPath := fldPath.Index(i)
namePath := idxPath.Child("name")
volMounts := GetVolumeMountMap(ctr.VolumeMounts)
volDevices := GetVolumeDeviceMap(ctr.VolumeDevices)
if len(ctr.Name) == 0 {
allErrs = append(allErrs, field.Required(namePath, ""))
} else {
@@ -2266,7 +2431,8 @@ func validateContainers(containers []core.Container, volumes sets.String, fldPat
allErrs = append(allErrs, validateContainerPorts(ctr.Ports, idxPath.Child("ports"))...)
allErrs = append(allErrs, ValidateEnv(ctr.Env, idxPath.Child("env"))...)
allErrs = append(allErrs, ValidateEnvFrom(ctr.EnvFrom, idxPath.Child("envFrom"))...)
allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volumes, &ctr, idxPath.Child("volumeMounts"))...)
allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, &ctr, idxPath.Child("volumeMounts"))...)
allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, idxPath.Child("volumeDevices"))...)
allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, idxPath.Child("imagePullPolicy"))...)
allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"))...)
allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, idxPath.Child("securityContext"))...)
@@ -2546,10 +2712,10 @@ func ValidatePod(pod *core.Pod) field.ErrorList {
func ValidatePodSpec(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allVolumes, vErrs := ValidateVolumes(spec.Volumes, fldPath.Child("volumes"))
vols, vErrs := ValidateVolumes(spec.Volumes, fldPath.Child("volumes"))
allErrs = append(allErrs, vErrs...)
allErrs = append(allErrs, validateContainers(spec.Containers, allVolumes, fldPath.Child("containers"))...)
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, allVolumes, fldPath.Child("initContainers"))...)
allErrs = append(allErrs, validateContainers(spec.Containers, vols, fldPath.Child("containers"))...)
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, fldPath.Child("initContainers"))...)
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)