Update ingress godeps
This commit is contained in:
parent
d43021b3f1
commit
28db8fb16d
1068 changed files with 461467 additions and 117300 deletions
57
vendor/k8s.io/kubernetes/pkg/api/validation/events.go
generated
vendored
57
vendor/k8s.io/kubernetes/pkg/api/validation/events.go
generated
vendored
|
|
@ -17,7 +17,13 @@ limitations under the License.
|
|||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
apiutil "k8s.io/kubernetes/pkg/api/util"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
|
|
@ -25,21 +31,50 @@ import (
|
|||
// ValidateEvent makes sure that the event makes sense.
|
||||
func ValidateEvent(event *api.Event) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
// There is no namespace required for node or persistent volume.
|
||||
// However, older client code accidentally sets event.Namespace
|
||||
// to api.NamespaceDefault, so we accept that too, but "" is preferred.
|
||||
if (event.InvolvedObject.Kind == "Node" || event.InvolvedObject.Kind == "PersistentVolume") &&
|
||||
event.Namespace != api.NamespaceDefault &&
|
||||
event.Namespace != "" {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "not allowed for node"))
|
||||
|
||||
// Make sure event.Namespace and the involvedObject.Namespace agree
|
||||
if len(event.InvolvedObject.Namespace) == 0 {
|
||||
// event.Namespace must also be empty (or "default", for compatibility with old clients)
|
||||
if event.Namespace != api.NamespaceNone && event.Namespace != api.NamespaceDefault {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace"))
|
||||
}
|
||||
} else {
|
||||
// event namespace must match
|
||||
if event.Namespace != event.InvolvedObject.Namespace {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace"))
|
||||
}
|
||||
}
|
||||
if event.InvolvedObject.Kind != "Node" &&
|
||||
event.InvolvedObject.Kind != "PersistentVolume" &&
|
||||
event.Namespace != event.InvolvedObject.Namespace {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match involvedObject"))
|
||||
|
||||
// For kinds we recognize, make sure involvedObject.Namespace is set for namespaced kinds
|
||||
if namespaced, err := isNamespacedKind(event.InvolvedObject.Kind, event.InvolvedObject.APIVersion); err == nil {
|
||||
if namespaced && len(event.InvolvedObject.Namespace) == 0 {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("involvedObject", "namespace"), fmt.Sprintf("required for kind %s", event.InvolvedObject.Kind)))
|
||||
}
|
||||
if !namespaced && len(event.InvolvedObject.Namespace) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, fmt.Sprintf("not allowed for kind %s", event.InvolvedObject.Kind)))
|
||||
}
|
||||
}
|
||||
|
||||
for _, msg := range validation.IsDNS1123Subdomain(event.Namespace) {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("namespace"), event.Namespace, msg))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// Check whether the kind in groupVersion is scoped at the root of the api hierarchy
|
||||
func isNamespacedKind(kind, groupVersion string) (bool, error) {
|
||||
group := apiutil.GetGroup(groupVersion)
|
||||
g, err := registered.Group(group)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
restMapping, err := g.RESTMapper.RESTMapping(unversioned.GroupKind{Group: group, Kind: kind}, apiutil.GetVersion(groupVersion))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
scopeName := restMapping.Scope.Name()
|
||||
if scopeName == meta.RESTScopeNameNamespace {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
|
|
|||
544
vendor/k8s.io/kubernetes/pkg/api/validation/validation.go
generated
vendored
544
vendor/k8s.io/kubernetes/pkg/api/validation/validation.go
generated
vendored
|
|
@ -23,6 +23,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
|
@ -36,6 +37,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/capabilities"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
|
|
@ -52,6 +54,7 @@ const fieldImmutableErrorMsg string = `field is immutable`
|
|||
const isNotIntegerErrorMsg string = `must be an integer`
|
||||
|
||||
var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
|
||||
var volumeModeErrorMsg string = "must be a number between 0 and 0777 (octal), both inclusive"
|
||||
|
||||
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
|
||||
|
||||
|
|
@ -100,7 +103,16 @@ func ValidateDNS1123Label(value string, fldPath *field.Path) field.ErrorList {
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func ValidatePodSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||
// ValidateDNS1123Subdomain validates that a name is a proper DNS subdomain.
|
||||
func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for _, msg := range validation.IsDNS1123Subdomain(value) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidatePodSpecificAnnotations(annotations map[string]string, spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if annotations[api.AffinityAnnotationKey] != "" {
|
||||
allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...)
|
||||
|
|
@ -119,7 +131,50 @@ func ValidatePodSpecificAnnotations(annotations map[string]string, fldPath *fiel
|
|||
}
|
||||
|
||||
allErrs = append(allErrs, ValidateSeccompPodAnnotations(annotations, fldPath)...)
|
||||
allErrs = append(allErrs, ValidateAppArmorPodAnnotations(annotations, spec, fldPath)...)
|
||||
|
||||
sysctls, err := api.SysctlsFromPodAnnotation(annotations[api.SysctlsPodAnnotationKey])
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Key(api.SysctlsPodAnnotationKey), annotations[api.SysctlsPodAnnotationKey], err.Error()))
|
||||
} else {
|
||||
allErrs = append(allErrs, validateSysctls(sysctls, fldPath.Key(api.SysctlsPodAnnotationKey))...)
|
||||
}
|
||||
unsafeSysctls, err := api.SysctlsFromPodAnnotation(annotations[api.UnsafeSysctlsPodAnnotationKey])
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Key(api.UnsafeSysctlsPodAnnotationKey), annotations[api.UnsafeSysctlsPodAnnotationKey], err.Error()))
|
||||
} else {
|
||||
allErrs = append(allErrs, validateSysctls(unsafeSysctls, fldPath.Key(api.UnsafeSysctlsPodAnnotationKey))...)
|
||||
}
|
||||
inBoth := sysctlIntersection(sysctls, unsafeSysctls)
|
||||
if len(inBoth) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Key(api.UnsafeSysctlsPodAnnotationKey), strings.Join(inBoth, ", "), "can not be safe and unsafe"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *api.Pod, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
newAnnotations := newPod.Annotations
|
||||
oldAnnotations := oldPod.Annotations
|
||||
for k, oldVal := range oldAnnotations {
|
||||
if newAnnotations[k] == oldVal {
|
||||
continue // No change.
|
||||
}
|
||||
if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not update AppArmor annotations"))
|
||||
}
|
||||
}
|
||||
// Check for removals.
|
||||
for k := range newAnnotations {
|
||||
if _, ok := oldAnnotations[k]; ok {
|
||||
continue // No change.
|
||||
}
|
||||
if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove AppArmor annotations"))
|
||||
}
|
||||
}
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(newAnnotations, &newPod.Spec, fldPath)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +259,7 @@ var ValidateReplicationControllerName = NameIsDNSSubdomain
|
|||
// ValidateServiceName can be used to check whether the given service name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case
|
||||
// trailing dashes are allowed.
|
||||
var ValidateServiceName = NameIsDNS952Label
|
||||
var ValidateServiceName = NameIsDNS1035Label
|
||||
|
||||
// ValidateNodeName can be used to check whether the given node name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case
|
||||
|
|
@ -242,6 +297,9 @@ var ValidateServiceAccountName = NameIsDNSSubdomain
|
|||
// trailing dashes are allowed.
|
||||
var ValidateEndpointsName = NameIsDNSSubdomain
|
||||
|
||||
// ValidateClusterName can be used to check whether the given cluster name is valid.
|
||||
var ValidateClusterName = NameIsDNS1035Label
|
||||
|
||||
// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
|
||||
func NameIsDNSSubdomain(name string, prefix bool) []string {
|
||||
if prefix {
|
||||
|
|
@ -258,12 +316,12 @@ func NameIsDNSLabel(name string, prefix bool) []string {
|
|||
return validation.IsDNS1123Label(name)
|
||||
}
|
||||
|
||||
// NameIsDNS952Label is a ValidateNameFunc for names that must be a DNS 952 label.
|
||||
func NameIsDNS952Label(name string, prefix bool) []string {
|
||||
// NameIsDNS1035Label is a ValidateNameFunc for names that must be a DNS 952 label.
|
||||
func NameIsDNS1035Label(name string, prefix bool) []string {
|
||||
if prefix {
|
||||
name = maskTrailingDash(name)
|
||||
}
|
||||
return validation.IsDNS952Label(name)
|
||||
return validation.IsDNS1035Label(name)
|
||||
}
|
||||
|
||||
// Validates that given value is not negative.
|
||||
|
|
@ -327,6 +385,11 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val
|
|||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "not allowed on this type"))
|
||||
}
|
||||
}
|
||||
if len(meta.ClusterName) != 0 {
|
||||
for _, msg := range ValidateClusterName(meta.ClusterName, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterName"), meta.ClusterName, msg))
|
||||
}
|
||||
}
|
||||
allErrs = append(allErrs, ValidateNonnegativeField(meta.Generation, fldPath.Child("generation"))...)
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(meta.Labels, fldPath.Child("labels"))...)
|
||||
allErrs = append(allErrs, ValidateAnnotations(meta.Annotations, fldPath.Child("annotations"))...)
|
||||
|
|
@ -388,6 +451,7 @@ func ValidateObjectMetaUpdate(newMeta, oldMeta *api.ObjectMeta, fldPath *field.P
|
|||
allErrs = append(allErrs, ValidateImmutableField(newMeta.Namespace, oldMeta.Namespace, fldPath.Child("namespace"))...)
|
||||
allErrs = append(allErrs, ValidateImmutableField(newMeta.UID, oldMeta.UID, fldPath.Child("uid"))...)
|
||||
allErrs = append(allErrs, ValidateImmutableField(newMeta.CreationTimestamp, oldMeta.CreationTimestamp, fldPath.Child("creationTimestamp"))...)
|
||||
allErrs = append(allErrs, ValidateImmutableField(newMeta.ClusterName, oldMeta.ClusterName, fldPath.Child("clusterName"))...)
|
||||
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(newMeta.Labels, fldPath.Child("labels"))...)
|
||||
allErrs = append(allErrs, ValidateAnnotations(newMeta.Annotations, fldPath.Child("annotations"))...)
|
||||
|
|
@ -533,6 +597,14 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
|
|||
allErrs = append(allErrs, validateCephFSVolumeSource(source.CephFS, fldPath.Child("cephfs"))...)
|
||||
}
|
||||
}
|
||||
if source.Quobyte != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("quobyte"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateQuobyteVolumeSource(source.Quobyte, fldPath.Child("quobyte"))...)
|
||||
}
|
||||
}
|
||||
if source.DownwardAPI != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("downwarAPI"), "may not specify more than 1 volume type"))
|
||||
|
|
@ -577,6 +649,11 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
|
|||
allErrs = append(allErrs, validateVsphereVolumeSource(source.VsphereVolume, fldPath.Child("vsphereVolume"))...)
|
||||
}
|
||||
}
|
||||
if source.AzureDisk != nil {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateAzureDisk(source.AzureDisk, fldPath.Child("azureDisk"))...)
|
||||
}
|
||||
|
||||
if numVolumes == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
|
||||
}
|
||||
|
|
@ -598,7 +675,7 @@ func validateGitRepoVolumeSource(gitRepo *api.GitRepoVolumeSource, fldPath *fiel
|
|||
allErrs = append(allErrs, field.Required(fldPath.Child("repository"), ""))
|
||||
}
|
||||
|
||||
pathErrs := validateVolumeSourcePath(gitRepo.Directory, fldPath.Child("directory"))
|
||||
pathErrs := validateLocalDescendingPath(gitRepo.Directory, fldPath.Child("directory"))
|
||||
allErrs = append(allErrs, pathErrs...)
|
||||
return allErrs
|
||||
}
|
||||
|
|
@ -660,6 +737,17 @@ func validateSecretVolumeSource(secretSource *api.SecretVolumeSource, fldPath *f
|
|||
if len(secretSource.SecretName) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
|
||||
}
|
||||
|
||||
secretMode := secretSource.DefaultMode
|
||||
if secretMode != nil && (*secretMode > 0777 || *secretMode < 0) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *secretMode, volumeModeErrorMsg))
|
||||
}
|
||||
|
||||
itemsPath := fldPath.Child("items")
|
||||
for i, kp := range secretSource.Items {
|
||||
itemPath := itemsPath.Index(i)
|
||||
allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
|
@ -668,6 +756,33 @@ func validateConfigMapVolumeSource(configMapSource *api.ConfigMapVolumeSource, f
|
|||
if len(configMapSource.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
|
||||
configMapMode := configMapSource.DefaultMode
|
||||
if configMapMode != nil && (*configMapMode > 0777 || *configMapMode < 0) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *configMapMode, volumeModeErrorMsg))
|
||||
}
|
||||
|
||||
itemsPath := fldPath.Child("items")
|
||||
for i, kp := range configMapSource.Items {
|
||||
itemPath := itemsPath.Index(i)
|
||||
allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateKeyToPath(kp *api.KeyToPath, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(kp.Key) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
|
||||
}
|
||||
if len(kp.Path) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
|
@ -693,6 +808,24 @@ func validateNFSVolumeSource(nfs *api.NFSVolumeSource, fldPath *field.Path) fiel
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func validateQuobyteVolumeSource(quobyte *api.QuobyteVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(quobyte.Registry) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("registry"), "must be a host:port pair or multiple pairs seperated by commas"))
|
||||
} else {
|
||||
for _, hostPortPair := range strings.Split(quobyte.Registry, ",") {
|
||||
if _, _, err := net.SplitHostPort(hostPortPair); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("registry"), quobyte.Registry, "must be a host:port pair or multiple pairs seperated by commas"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(quobyte.Volume) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("volume"), ""))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateGlusterfs(glusterfs *api.GlusterfsVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(glusterfs.EndpointsName) == 0 {
|
||||
|
|
@ -715,69 +848,71 @@ func validateFlockerVolumeSource(flocker *api.FlockerVolumeSource, fldPath *fiel
|
|||
return allErrs
|
||||
}
|
||||
|
||||
var validDownwardAPIFieldPathExpressions = sets.NewString("metadata.name", "metadata.namespace", "metadata.labels", "metadata.annotations")
|
||||
var validDownwardAPIFieldPathExpressions = sets.NewString(
|
||||
"metadata.name",
|
||||
"metadata.namespace",
|
||||
"metadata.labels",
|
||||
"metadata.annotations")
|
||||
|
||||
func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for _, downwardAPIVolumeFile := range downwardAPIVolume.Items {
|
||||
if len(downwardAPIVolumeFile.Path) == 0 {
|
||||
|
||||
downwardAPIMode := downwardAPIVolume.DefaultMode
|
||||
if downwardAPIMode != nil && (*downwardAPIMode > 0777 || *downwardAPIMode < 0) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *downwardAPIMode, volumeModeErrorMsg))
|
||||
}
|
||||
|
||||
for _, file := range downwardAPIVolume.Items {
|
||||
if len(file.Path) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
||||
}
|
||||
allErrs = append(allErrs, validateVolumeSourcePath(downwardAPIVolumeFile.Path, fldPath.Child("path"))...)
|
||||
if downwardAPIVolumeFile.FieldRef != nil {
|
||||
allErrs = append(allErrs, validateObjectFieldSelector(downwardAPIVolumeFile.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
|
||||
if downwardAPIVolumeFile.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, validateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
|
||||
if file.FieldRef != nil {
|
||||
allErrs = append(allErrs, validateObjectFieldSelector(file.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
|
||||
if file.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
|
||||
}
|
||||
} else if downwardAPIVolumeFile.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, validateContainerResourceFieldSelector(downwardAPIVolumeFile.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
|
||||
} else if file.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// This validate will make sure targetPath:
|
||||
// 1. is not abs path
|
||||
// 2. does not start with '../'
|
||||
// 3. does not contain '/../'
|
||||
// 4. does not end with '/..'
|
||||
func validateSubPath(targetPath string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if path.IsAbs(targetPath) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
|
||||
}
|
||||
if strings.HasPrefix(targetPath, "../") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '../'"))
|
||||
}
|
||||
if strings.Contains(targetPath, "/../") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '/../'"))
|
||||
}
|
||||
if strings.HasSuffix(targetPath, "/..") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not end with '/..'"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// This validate will make sure targetPath:
|
||||
// 1. is not abs path
|
||||
// 2. does not contain '..'
|
||||
// 3. does not start with '..'
|
||||
func validateVolumeSourcePath(targetPath string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if path.IsAbs(targetPath) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
|
||||
}
|
||||
// TODO assume OS of api server & nodes are the same for now
|
||||
items := strings.Split(targetPath, string(os.PathSeparator))
|
||||
|
||||
for _, item := range items {
|
||||
if item == ".." {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '..'"))
|
||||
if file.Mode != nil && (*file.Mode > 0777 || *file.Mode < 0) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, volumeModeErrorMsg))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(items[0], "..") && len(items[0]) > 2 {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// This validate will make sure targetPath:
|
||||
// 1. is not abs path
|
||||
// 2. does not have any element which is ".."
|
||||
func validateLocalDescendingPath(targetPath string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if path.IsAbs(targetPath) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
|
||||
}
|
||||
|
||||
// TODO: this assumes the OS of apiserver & nodes are the same
|
||||
parts := strings.Split(targetPath, string(os.PathSeparator))
|
||||
for _, item := range parts {
|
||||
if item == ".." {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '..'"))
|
||||
break // even for `../../..`, one error is sufficient to make the point
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// This validate will make sure targetPath:
|
||||
// 1. is not abs path
|
||||
// 2. does not contain any '..' elements
|
||||
// 3. does not start with '..'
|
||||
func validateLocalNonReservedPath(targetPath string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, validateLocalDescendingPath(targetPath, fldPath)...)
|
||||
// Don't report this error if the check for .. elements already caught it.
|
||||
if strings.HasPrefix(targetPath, "..") && !strings.HasPrefix(targetPath, "../") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '..'"))
|
||||
}
|
||||
return allErrs
|
||||
|
|
@ -829,6 +964,22 @@ func validateAzureFile(azure *api.AzureFileVolumeSource, fldPath *field.Path) fi
|
|||
return allErrs
|
||||
}
|
||||
|
||||
var supportedCachingModes = sets.NewString(string(api.AzureDataDiskCachingNone), string(api.AzureDataDiskCachingReadOnly), string(api.AzureDataDiskCachingReadWrite))
|
||||
|
||||
func validateAzureDisk(azure *api.AzureDiskVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if azure.DiskName == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("diskName"), ""))
|
||||
}
|
||||
if azure.DataDiskURI == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("diskURI"), ""))
|
||||
}
|
||||
if azure.CachingMode != nil && !supportedCachingModes.Has(string(*azure.CachingMode)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("cachingMode"), *azure.CachingMode, supportedCachingModes.List()))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateVsphereVolumeSource(cd *api.VsphereVirtualDiskVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(cd.VolumePath) == 0 {
|
||||
|
|
@ -925,6 +1076,14 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
|
|||
allErrs = append(allErrs, validateRBDVolumeSource(pv.Spec.RBD, specPath.Child("rbd"))...)
|
||||
}
|
||||
}
|
||||
if pv.Spec.Quobyte != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(specPath.Child("quobyte"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateQuobyteVolumeSource(pv.Spec.Quobyte, specPath.Child("quobyte"))...)
|
||||
}
|
||||
}
|
||||
if pv.Spec.CephFS != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(specPath.Child("cephFS"), "may not specify more than 1 volume type"))
|
||||
|
|
@ -973,6 +1132,11 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
|
|||
allErrs = append(allErrs, validateVsphereVolumeSource(pv.Spec.VsphereVolume, specPath.Child("vsphereVolume"))...)
|
||||
}
|
||||
}
|
||||
if pv.Spec.AzureDisk != nil {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateAzureDisk(pv.Spec.AzureDisk, specPath.Child("azureDisk"))...)
|
||||
}
|
||||
|
||||
if numVolumes == 0 {
|
||||
allErrs = append(allErrs, field.Required(specPath, "must specify a volume type"))
|
||||
}
|
||||
|
|
@ -999,26 +1163,37 @@ func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *api.PersistentVolume) fi
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
|
||||
func ValidatePersistentVolumeClaim(pvc *api.PersistentVolumeClaim) field.ErrorList {
|
||||
allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName, field.NewPath("metadata"))
|
||||
specPath := field.NewPath("spec")
|
||||
if len(pvc.Spec.AccessModes) == 0 {
|
||||
allErrs = append(allErrs, field.Required(specPath.Child("accessModes"), "at least 1 accessMode is required"))
|
||||
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&pvc.Spec, field.NewPath("spec"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePersistentVolumeClaimSpec validates a PersistentVolumeClaimSpec
|
||||
func ValidatePersistentVolumeClaimSpec(spec *api.PersistentVolumeClaimSpec, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(spec.AccessModes) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), "at least 1 access mode is required"))
|
||||
}
|
||||
if pvc.Spec.Selector != nil {
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(pvc.Spec.Selector, specPath.Child("selector"))...)
|
||||
if spec.Selector != nil {
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
|
||||
}
|
||||
for _, mode := range pvc.Spec.AccessModes {
|
||||
for _, mode := range spec.AccessModes {
|
||||
if mode != api.ReadWriteOnce && mode != api.ReadOnlyMany && mode != api.ReadWriteMany {
|
||||
allErrs = append(allErrs, field.NotSupported(specPath.Child("accessModes"), mode, supportedAccessModes.List()))
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, supportedAccessModes.List()))
|
||||
}
|
||||
}
|
||||
if _, ok := pvc.Spec.Resources.Requests[api.ResourceStorage]; !ok {
|
||||
allErrs = append(allErrs, field.Required(specPath.Child("resources").Key(string(api.ResourceStorage)), ""))
|
||||
storageValue, ok := spec.Resources.Requests[api.ResourceStorage]
|
||||
if !ok {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("resources").Key(string(api.ResourceStorage)), ""))
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidateResourceQuantityValue(string(api.ResourceStorage), storageValue, fldPath.Child("resources").Key(string(api.ResourceStorage)))...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePersistentVolumeClaimUpdate validates an update to a PeristentVolumeClaim
|
||||
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *api.PersistentVolumeClaim) field.ErrorList {
|
||||
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidatePersistentVolumeClaim(newPvc)...)
|
||||
|
|
@ -1039,6 +1214,7 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *api.PersistentVolumeCla
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePersistentVolumeClaimStatusUpdate validates an update to status of a PeristentVolumeClaim
|
||||
func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *api.PersistentVolumeClaim) field.ErrorList {
|
||||
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
|
||||
if len(newPvc.ResourceVersion) == 0 {
|
||||
|
|
@ -1112,7 +1288,7 @@ func validateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList {
|
|||
return allErrs
|
||||
}
|
||||
|
||||
var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "status.podIP")
|
||||
var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "spec.nodeName", "spec.serviceAccountName", "status.podIP")
|
||||
var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "requests.cpu", "requests.memory")
|
||||
|
||||
func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList {
|
||||
|
|
@ -1197,11 +1373,11 @@ func validateContainerResourceDivisor(rName string, divisor resource.Quantity, f
|
|||
switch rName {
|
||||
case "limits.cpu", "requests.cpu":
|
||||
if !validContainerResourceDivisorForCPU.Has(divisor.String()) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, fmt.Sprintf("only divisor's values 1m and 1 are supported with the cpu resource")))
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1m and 1 are supported with the cpu resource"))
|
||||
}
|
||||
case "limits.memory", "requests.memory":
|
||||
if !validContainerResourceDivisorForMemory.Has(divisor.String()) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, fmt.Sprintf("only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the memory resource")))
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the memory resource"))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
|
|
@ -1262,7 +1438,7 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath
|
|||
}
|
||||
mountpoints.Insert(mnt.MountPath)
|
||||
if len(mnt.SubPath) > 0 {
|
||||
allErrs = append(allErrs, validateSubPath(mnt.SubPath, fldPath.Child("subPath"))...)
|
||||
allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...)
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
|
|
@ -1621,7 +1797,7 @@ func validateTolerations(tolerations []api.Toleration, fldPath *field.Path) fiel
|
|||
func ValidatePod(pod *api.Pod) field.ErrorList {
|
||||
fldPath := field.NewPath("metadata")
|
||||
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, field.NewPath("spec"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
|
@ -1724,6 +1900,41 @@ func ValidateNodeSelector(nodeSelector *api.NodeSelector, fldPath *field.Path) f
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateAvoidPodsInNodeAnnotations tests that the serialized AvoidPods in Node.Annotations has valid data
|
||||
func ValidateAvoidPodsInNodeAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
avoids, err := api.GetAvoidPodsFromNodeAnnotations(annotations)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("AvoidPods"), api.PreferAvoidPodsAnnotationKey, err.Error()))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if len(avoids.PreferAvoidPods) != 0 {
|
||||
for i, pa := range avoids.PreferAvoidPods {
|
||||
idxPath := fldPath.Child(api.PreferAvoidPodsAnnotationKey).Index(i)
|
||||
allErrs = append(allErrs, validatePreferAvoidPodsEntry(pa, idxPath)...)
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePreferAvoidPodsEntry tests if given PreferAvoidPodsEntry has valid data.
|
||||
func validatePreferAvoidPodsEntry(avoidPodEntry api.PreferAvoidPodsEntry, fldPath *field.Path) field.ErrorList {
|
||||
allErrors := field.ErrorList{}
|
||||
if avoidPodEntry.PodSignature.PodController == nil {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("PodSignature"), ""))
|
||||
} else {
|
||||
if *(avoidPodEntry.PodSignature.PodController.Controller) != true {
|
||||
allErrors = append(allErrors,
|
||||
field.Invalid(fldPath.Child("PodSignature").Child("PodController").Child("Controller"),
|
||||
*(avoidPodEntry.PodSignature.PodController.Controller), "must point to a controller"))
|
||||
}
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// ValidatePreferredSchedulingTerms tests that the specified SoftNodeAffinity fields has valid data
|
||||
func ValidatePreferredSchedulingTerms(terms []api.PreferredSchedulingTerm, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
|
@ -1828,6 +2039,9 @@ func ValidateAffinityInPodAnnotations(annotations map[string]string, fldPath *fi
|
|||
allErrs = append(allErrs, field.Invalid(fldPath, api.AffinityAnnotationKey, err.Error()))
|
||||
return allErrs
|
||||
}
|
||||
if affinity == nil {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
affinityFldPath := fldPath.Child(api.AffinityAnnotationKey)
|
||||
if affinity.NodeAffinity != nil {
|
||||
|
|
@ -1880,7 +2094,7 @@ func validateSeccompProfile(p string, fldPath *field.Path) field.ErrorList {
|
|||
return nil
|
||||
}
|
||||
if strings.HasPrefix(p, "localhost/") {
|
||||
return validateSubPath(strings.TrimPrefix(p, "localhost/"), fldPath)
|
||||
return validateLocalDescendingPath(strings.TrimPrefix(p, "localhost/"), fldPath)
|
||||
}
|
||||
return field.ErrorList{field.Invalid(fldPath, p, "must be a valid seccomp profile")}
|
||||
}
|
||||
|
|
@ -1899,6 +2113,73 @@ func ValidateSeccompPodAnnotations(annotations map[string]string, fldPath *field
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateAppArmorPodAnnotations(annotations map[string]string, spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for k, p := range annotations {
|
||||
if !strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
|
||||
continue
|
||||
}
|
||||
containerName := strings.TrimPrefix(k, apparmor.ContainerAnnotationKeyPrefix)
|
||||
if !podSpecHasContainer(spec, containerName) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child(k), containerName, "container not found"))
|
||||
}
|
||||
|
||||
if err := apparmor.ValidateProfileFormat(p); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child(k), p, err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func podSpecHasContainer(spec *api.PodSpec, containerName string) bool {
|
||||
for _, c := range spec.InitContainers {
|
||||
if c.Name == containerName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, c := range spec.Containers {
|
||||
if c.Name == containerName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
// a sysctl segment regex, concatenated with dots to form a sysctl name
|
||||
SysctlSegmentFmt string = "[a-z0-9]([-_a-z0-9]*[a-z0-9])?"
|
||||
|
||||
// a sysctl name regex
|
||||
SysctlFmt string = "(" + SysctlSegmentFmt + "\\.)*" + SysctlSegmentFmt
|
||||
|
||||
// the maximal length of a sysctl name
|
||||
SysctlMaxLength int = 253
|
||||
)
|
||||
|
||||
var sysctlRegexp = regexp.MustCompile("^" + SysctlFmt + "$")
|
||||
|
||||
// IsValidSysctlName checks that the given string is a valid sysctl name,
|
||||
// i.e. matches SysctlFmt.
|
||||
func IsValidSysctlName(name string) bool {
|
||||
if len(name) > SysctlMaxLength {
|
||||
return false
|
||||
}
|
||||
return sysctlRegexp.MatchString(name)
|
||||
}
|
||||
|
||||
func validateSysctls(sysctls []api.Sysctl, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for i, s := range sysctls {
|
||||
if len(s.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("name"), ""))
|
||||
} else if !IsValidSysctlName(s.Name) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, fmt.Sprintf("must have at most %d characters and match regex %s", SysctlMaxLength, SysctlFmt)))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodSecurityContext test that the specified PodSecurityContext has valid data.
|
||||
func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *api.PodSpec, specPath, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
|
@ -1925,28 +2206,45 @@ func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *a
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateContainerUpdates(newContainers, oldContainers []api.Container, fldPath *field.Path) (allErrs field.ErrorList, stop bool) {
|
||||
allErrs = field.ErrorList{}
|
||||
if len(newContainers) != len(oldContainers) {
|
||||
//TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath, "pod updates may not add or remove containers"))
|
||||
return allErrs, true
|
||||
}
|
||||
|
||||
// validate updated container images
|
||||
for i, ctr := range newContainers {
|
||||
if len(ctr.Image) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("image"), ""))
|
||||
}
|
||||
}
|
||||
return allErrs, false
|
||||
}
|
||||
|
||||
// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
|
||||
// that cannot be changed.
|
||||
func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
|
||||
fldPath := field.NewPath("metadata")
|
||||
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(newPod.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
|
||||
specPath := field.NewPath("spec")
|
||||
if len(newPod.Spec.Containers) != len(oldPod.Spec.Containers) {
|
||||
//TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff
|
||||
allErrs = append(allErrs, field.Forbidden(specPath.Child("containers"), "pod updates may not add or remove containers"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validate updateable fields:
|
||||
// 1. containers[*].image
|
||||
// 2. spec.activeDeadlineSeconds
|
||||
// 2. initContainers[*].image
|
||||
// 3. spec.activeDeadlineSeconds
|
||||
|
||||
// validate updated container images
|
||||
for i, ctr := range newPod.Spec.Containers {
|
||||
if len(ctr.Image) == 0 {
|
||||
allErrs = append(allErrs, field.Required(specPath.Child("containers").Index(i).Child("image"), ""))
|
||||
}
|
||||
containerErrs, stop := ValidateContainerUpdates(newPod.Spec.Containers, oldPod.Spec.Containers, specPath.Child("containers"))
|
||||
allErrs = append(allErrs, containerErrs...)
|
||||
if stop {
|
||||
return allErrs
|
||||
}
|
||||
containerErrs, stop = ValidateContainerUpdates(newPod.Spec.InitContainers, oldPod.Spec.InitContainers, specPath.Child("initContainers"))
|
||||
allErrs = append(allErrs, containerErrs...)
|
||||
if stop {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validate updated spec.activeDeadlineSeconds. two types of updates are allowed:
|
||||
|
|
@ -1978,6 +2276,13 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
|
|||
newContainers = append(newContainers, container)
|
||||
}
|
||||
mungedPod.Spec.Containers = newContainers
|
||||
// munge initContainers[*].image
|
||||
var newInitContainers []api.Container
|
||||
for ix, container := range mungedPod.Spec.InitContainers {
|
||||
container.Image = oldPod.Spec.InitContainers[ix].Image
|
||||
newInitContainers = append(newInitContainers, container)
|
||||
}
|
||||
mungedPod.Spec.InitContainers = newInitContainers
|
||||
// munge spec.activeDeadlineSeconds
|
||||
mungedPod.Spec.ActiveDeadlineSeconds = nil
|
||||
if oldPod.Spec.ActiveDeadlineSeconds != nil {
|
||||
|
|
@ -2041,17 +2346,19 @@ func ValidatePodTemplateUpdate(newPod, oldPod *api.PodTemplate) field.ErrorList
|
|||
|
||||
var supportedSessionAffinityType = sets.NewString(string(api.ServiceAffinityClientIP), string(api.ServiceAffinityNone))
|
||||
var supportedServiceType = sets.NewString(string(api.ServiceTypeClusterIP), string(api.ServiceTypeNodePort),
|
||||
string(api.ServiceTypeLoadBalancer))
|
||||
string(api.ServiceTypeLoadBalancer), string(api.ServiceTypeExternalName))
|
||||
|
||||
// ValidateService tests if required fields in the service are set.
|
||||
func ValidateService(service *api.Service) field.ErrorList {
|
||||
allErrs := ValidateObjectMeta(&service.ObjectMeta, true, ValidateServiceName, field.NewPath("metadata"))
|
||||
|
||||
specPath := field.NewPath("spec")
|
||||
if len(service.Spec.Ports) == 0 && service.Spec.ClusterIP != api.ClusterIPNone {
|
||||
isHeadlessService := service.Spec.ClusterIP == api.ClusterIPNone
|
||||
if len(service.Spec.Ports) == 0 && !isHeadlessService && service.Spec.Type != api.ServiceTypeExternalName {
|
||||
allErrs = append(allErrs, field.Required(specPath.Child("ports"), ""))
|
||||
}
|
||||
if service.Spec.Type == api.ServiceTypeLoadBalancer {
|
||||
switch service.Spec.Type {
|
||||
case api.ServiceTypeLoadBalancer:
|
||||
for ix := range service.Spec.Ports {
|
||||
port := &service.Spec.Ports[ix]
|
||||
// This is a workaround for broken cloud environments that
|
||||
|
|
@ -2062,9 +2369,17 @@ func ValidateService(service *api.Service) field.ErrorList {
|
|||
allErrs = append(allErrs, field.Invalid(portPath, port.Port, "may not expose port 10250 externally since it is used by kubelet"))
|
||||
}
|
||||
}
|
||||
case api.ServiceTypeExternalName:
|
||||
if service.Spec.ClusterIP != "" {
|
||||
allErrs = append(allErrs, field.Invalid(specPath.Child("clusterIP"), service.Spec.ClusterIP, "must be empty for ExternalName services"))
|
||||
}
|
||||
if len(service.Spec.ExternalName) > 0 {
|
||||
allErrs = append(allErrs, ValidateDNS1123Subdomain(service.Spec.ExternalName, specPath.Child("externalName"))...)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(specPath.Child("externalName"), ""))
|
||||
}
|
||||
}
|
||||
|
||||
isHeadlessService := service.Spec.ClusterIP == api.ClusterIPNone
|
||||
allPortNames := sets.String{}
|
||||
portsPath := specPath.Child("ports")
|
||||
for i := range service.Spec.Ports {
|
||||
|
|
@ -2305,7 +2620,7 @@ func ValidatePodTemplateSpec(spec *api.PodTemplateSpec, fldPath *field.Path) fie
|
|||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.Labels, fldPath.Child("labels"))...)
|
||||
allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, fldPath.Child("spec"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
|
@ -2328,6 +2643,9 @@ func ValidateReadOnlyPersistentDisks(volumes []api.Volume, fldPath *field.Path)
|
|||
// validateTaints tests if given taints have valid data.
|
||||
func validateTaints(taints []api.Taint, fldPath *field.Path) field.ErrorList {
|
||||
allErrors := field.ErrorList{}
|
||||
|
||||
uniqueTaints := map[api.TaintEffect]sets.String{}
|
||||
|
||||
for i, currTaint := range taints {
|
||||
idxPath := fldPath.Index(i)
|
||||
// validate the taint key
|
||||
|
|
@ -2338,6 +2656,20 @@ func validateTaints(taints []api.Taint, fldPath *field.Path) field.ErrorList {
|
|||
}
|
||||
// validate the taint effect
|
||||
allErrors = append(allErrors, validateTaintEffect(&currTaint.Effect, false, idxPath.Child("effect"))...)
|
||||
|
||||
// validate if taint is unique by <key, effect>
|
||||
if len(uniqueTaints[currTaint.Effect]) > 0 && uniqueTaints[currTaint.Effect].Has(currTaint.Key) {
|
||||
duplicatedError := field.Duplicate(idxPath, currTaint)
|
||||
duplicatedError.Detail = "taints must be unique by key and effect pair"
|
||||
allErrors = append(allErrors, duplicatedError)
|
||||
continue
|
||||
}
|
||||
|
||||
// add taint to existingTaints for uniqueness check
|
||||
if len(uniqueTaints[currTaint.Effect]) == 0 {
|
||||
uniqueTaints[currTaint.Effect] = sets.String{}
|
||||
}
|
||||
uniqueTaints[currTaint.Effect].Insert(currTaint.Key)
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
|
@ -2359,10 +2691,14 @@ func ValidateTaintsInNodeAnnotations(annotations map[string]string, fldPath *fie
|
|||
}
|
||||
|
||||
func ValidateNodeSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||
if annotations[api.TaintsAnnotationKey] != "" {
|
||||
return ValidateTaintsInNodeAnnotations(annotations, fldPath)
|
||||
allErrs := field.ErrorList{}
|
||||
if annotations[api.PreferAvoidPodsAnnotationKey] != "" {
|
||||
allErrs = append(allErrs, ValidateAvoidPodsInNodeAnnotations(annotations, fldPath)...)
|
||||
}
|
||||
return field.ErrorList{}
|
||||
if annotations[api.TaintsAnnotationKey] != "" {
|
||||
allErrs = append(allErrs, ValidateTaintsInNodeAnnotations(annotations, fldPath)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateNode tests if required fields in the node are set.
|
||||
|
|
@ -2780,11 +3116,11 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat
|
|||
// Check that request <= limit.
|
||||
requestQuantity, exists := requirements.Requests[resourceName]
|
||||
if exists {
|
||||
// For GPUs, require that no request be set.
|
||||
if resourceName == api.ResourceNvidiaGPU {
|
||||
allErrs = append(allErrs, field.Invalid(reqPath, requestQuantity.String(), "cannot be set"))
|
||||
// For GPUs, not only requests can't exceed limits, they also can't be lower, i.e. must be equal.
|
||||
if resourceName == api.ResourceNvidiaGPU && quantity.Cmp(requestQuantity) != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(reqPath, requestQuantity.String(), fmt.Sprintf("must be equal to %s limit", api.ResourceNvidiaGPU)))
|
||||
} else if quantity.Cmp(requestQuantity) < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, quantity.String(), "must be greater than or equal to request"))
|
||||
allErrs = append(allErrs, field.Invalid(limPath, quantity.String(), fmt.Sprintf("must be greater than or equal to %s request", resourceName)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2957,7 +3293,7 @@ func validateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorL
|
|||
|
||||
if len(strings.Split(stringValue, "/")) == 1 {
|
||||
if !api.IsStandardFinalizerName(stringValue) {
|
||||
return append(allErrs, field.Invalid(fldPath, stringValue, fmt.Sprintf("name is neither a standard finalizer name nor is it fully qualified")))
|
||||
return append(allErrs, field.Invalid(fldPath, stringValue, "name is neither a standard finalizer name nor is it fully qualified"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3191,3 +3527,17 @@ func isValidHostnamesMap(serializedPodHostNames string) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func sysctlIntersection(a []api.Sysctl, b []api.Sysctl) []string {
|
||||
lookup := make(map[string]struct{}, len(a))
|
||||
result := []string{}
|
||||
for i := range a {
|
||||
lookup[a[i].Name] = struct{}{}
|
||||
}
|
||||
for i := range b {
|
||||
if _, found := lookup[b[i].Name]; found {
|
||||
result = append(result, b[i].Name)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue