2014-07-01 16:01:39 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2014 The Kubernetes Authors .
2014-07-01 16:01:39 -04:00
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 .
* /
2014-08-29 18:48:41 -04:00
package validation
2014-07-01 16:01:39 -04:00
import (
2015-05-06 10:09:18 -04:00
"encoding/json"
2014-10-31 02:03:52 -04:00
"fmt"
2017-08-01 12:09:37 -04:00
"math"
2015-03-16 17:36:30 -04:00
"net"
2015-02-10 14:00:11 -05:00
"path"
2017-06-10 09:48:42 -04:00
"path/filepath"
2015-05-08 13:53:00 -04:00
"reflect"
2016-06-14 09:09:53 -04:00
"regexp"
2014-07-08 00:32:56 -04:00
"strings"
2014-07-01 16:01:39 -04:00
2018-11-09 13:49:10 -05:00
"k8s.io/klog"
2017-01-04 12:09:24 -05:00
2017-06-22 13:25:57 -04:00
"k8s.io/api/core/v1"
2017-01-25 08:39:54 -05:00
apiequality "k8s.io/apimachinery/pkg/api/equality"
2017-01-25 08:13:07 -05:00
"k8s.io/apimachinery/pkg/api/resource"
2017-01-16 11:00:37 -05:00
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
2017-01-11 09:09:48 -05:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-01-12 09:43:36 -05:00
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
2017-01-11 09:09:48 -05:00
"k8s.io/apimachinery/pkg/labels"
2018-09-07 14:07:47 -04:00
"k8s.io/apimachinery/pkg/runtime/schema"
2017-08-18 08:46:32 -04:00
"k8s.io/apimachinery/pkg/util/diff"
2017-01-27 15:42:17 -05:00
"k8s.io/apimachinery/pkg/util/intstr"
2017-01-11 09:09:48 -05:00
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
2017-01-20 08:05:41 -05:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2016-02-29 20:56:39 -05:00
apiservice "k8s.io/kubernetes/pkg/api/service"
2017-10-09 11:58:37 -04:00
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
2017-11-22 14:02:20 -05:00
podshelper "k8s.io/kubernetes/pkg/apis/core/pods"
2017-10-09 11:58:37 -04:00
corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
2015-08-05 18:03:47 -04:00
"k8s.io/kubernetes/pkg/capabilities"
2017-01-20 08:05:41 -05:00
"k8s.io/kubernetes/pkg/features"
2017-02-17 11:37:15 -05:00
"k8s.io/kubernetes/pkg/fieldpath"
2017-12-01 14:47:49 -05:00
"k8s.io/kubernetes/pkg/master/ports"
2016-08-16 19:41:05 -04:00
"k8s.io/kubernetes/pkg/security/apparmor"
2014-07-01 16:01:39 -04:00
)
2017-01-16 11:00:37 -05:00
const isNegativeErrorMsg string = apimachineryvalidation . IsNegativeErrorMsg
2016-02-22 11:14:11 -05:00
const isInvalidQuotaResource string = ` must be a standard resource for quota `
2017-11-13 22:36:32 -05:00
const fieldImmutableErrorMsg string = apimachineryvalidation . FieldImmutableErrorMsg
2015-10-13 17:27:56 -04:00
const isNotIntegerErrorMsg string = ` must be an integer `
2017-11-11 18:39:37 -05:00
const isNotPositiveErrorMsg string = ` must be greater than zero `
2015-02-04 19:36:27 -05:00
2016-01-04 11:33:26 -05:00
var pdPartitionErrorMsg string = validation . InclusiveRangeError ( 1 , 255 )
2019-04-01 00:13:36 -04:00
var fileModeErrorMsg = "must be a number between 0 and 0777 (octal), both inclusive"
2015-02-04 19:36:27 -05:00
2016-04-06 13:16:15 -04:00
// BannedOwners is a black list of object that are not allowed to be owners.
2017-11-13 22:36:32 -05:00
var BannedOwners = apimachineryvalidation . BannedOwners
2016-04-06 13:16:15 -04:00
2017-07-11 23:37:48 -04:00
var iscsiInitiatorIqnRegex = regexp . MustCompile ( ` iqn\.\d { 4}-\d { 2}\.([[:alnum:]-.]+)(:[^,;*&$|\s]+)$ ` )
var iscsiInitiatorEuiRegex = regexp . MustCompile ( ` ^eui.[[:alnum:]] { 16}$ ` )
var iscsiInitiatorNaaRegex = regexp . MustCompile ( ` ^naa.[[:alnum:]] { 32}$ ` )
2017-01-16 22:38:19 -05:00
// 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 {
2016-02-12 17:38:22 -05:00
allErrs := field . ErrorList { }
actualValue , found := meta . Labels [ key ]
if ! found {
2016-04-29 00:09:23 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "labels" ) . Key ( key ) ,
fmt . Sprintf ( "must be '%s'" , expectedValue ) ) )
2016-02-12 17:38:22 -05:00
return allErrs
}
if actualValue != expectedValue {
2016-04-29 00:09:23 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "labels" ) . Key ( key ) , meta . Labels ,
fmt . Sprintf ( "must be '%s'" , expectedValue ) ) )
2016-02-12 17:38:22 -05:00
}
return allErrs
}
2015-02-01 19:03:04 -05:00
// ValidateAnnotations validates that a set of annotations are correctly defined.
2015-11-06 18:30:52 -05:00
func ValidateAnnotations ( annotations map [ string ] string , fldPath * field . Path ) field . ErrorList {
2017-11-13 22:36:32 -05:00
return apimachineryvalidation . ValidateAnnotations ( annotations , fldPath )
2016-02-02 13:59:54 -05:00
}
2016-01-26 18:03:18 -05:00
2016-04-29 10:58:46 -04:00
func ValidateDNS1123Label ( value string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
for _ , msg := range validation . IsDNS1123Label ( value ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , value , msg ) )
}
return allErrs
}
2016-08-19 12:09:14 -04:00
// 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
}
2017-10-09 11:58:37 -04:00
func ValidatePodSpecificAnnotations ( annotations map [ string ] string , spec * core . PodSpec , fldPath * field . Path ) field . ErrorList {
2016-02-02 13:59:54 -05:00
allErrs := field . ErrorList { }
2016-03-30 23:42:57 -04:00
2017-10-09 11:58:37 -04:00
if value , isMirror := annotations [ core . MirrorPodAnnotationKey ] ; isMirror {
2017-05-13 15:48:44 -04:00
if len ( spec . NodeName ) == 0 {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Key ( core . MirrorPodAnnotationKey ) , value , "must set spec.nodeName if mirror pod annotation is set" ) )
2017-05-13 15:48:44 -04:00
}
}
2017-10-09 11:58:37 -04:00
if annotations [ core . TolerationsAnnotationKey ] != "" {
2017-03-22 00:53:34 -04:00
allErrs = append ( allErrs , ValidateTolerationsInPodAnnotations ( annotations , fldPath ) ... )
}
2016-06-10 04:02:36 -04:00
allErrs = append ( allErrs , ValidateSeccompPodAnnotations ( annotations , fldPath ) ... )
2016-08-16 19:41:05 -04:00
allErrs = append ( allErrs , ValidateAppArmorPodAnnotations ( annotations , spec , fldPath ) ... )
2016-06-10 04:02:36 -04:00
2016-02-02 13:59:54 -05:00
return allErrs
}
2017-03-22 00:53:34 -04:00
// ValidateTolerationsInPodAnnotations tests that the serialized tolerations in Pod.Annotations has valid data
func ValidateTolerationsInPodAnnotations ( annotations map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2017-04-10 13:49:54 -04:00
tolerations , err := helper . GetTolerationsFromPodAnnotations ( annotations )
2017-03-22 00:53:34 -04:00
if err != nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath , core . TolerationsAnnotationKey , err . Error ( ) ) )
2017-03-22 00:53:34 -04:00
return allErrs
}
if len ( tolerations ) > 0 {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , ValidateTolerations ( tolerations , fldPath . Child ( core . TolerationsAnnotationKey ) ) ... )
2017-03-22 00:53:34 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidatePodSpecificAnnotationUpdates ( newPod , oldPod * core . Pod , fldPath * field . Path ) field . ErrorList {
2016-08-16 19:41:05 -04:00
allErrs := field . ErrorList { }
newAnnotations := newPod . Annotations
oldAnnotations := oldPod . Annotations
for k , oldVal := range oldAnnotations {
2017-05-13 15:48:44 -04:00
if newVal , exists := newAnnotations [ k ] ; exists && newVal == oldVal {
2016-08-16 19:41:05 -04:00
continue // No change.
}
if strings . HasPrefix ( k , apparmor . ContainerAnnotationKeyPrefix ) {
2017-05-13 15:48:44 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Key ( k ) , "may not remove or update AppArmor annotations" ) )
}
2017-10-09 11:58:37 -04:00
if k == core . MirrorPodAnnotationKey {
2017-05-13 15:48:44 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Key ( k ) , "may not remove or update mirror pod annotation" ) )
2016-08-16 19:41:05 -04:00
}
}
2017-05-13 15:48:44 -04:00
// Check for additions
2016-08-16 19:41:05 -04:00
for k := range newAnnotations {
if _ , ok := oldAnnotations [ k ] ; ok {
continue // No change.
}
if strings . HasPrefix ( k , apparmor . ContainerAnnotationKeyPrefix ) {
2017-05-13 15:48:44 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Key ( k ) , "may not add AppArmor annotations" ) )
}
2017-10-09 11:58:37 -04:00
if k == core . MirrorPodAnnotationKey {
2017-05-13 15:48:44 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Key ( k ) , "may not add mirror pod annotation" ) )
2016-08-16 19:41:05 -04:00
}
}
allErrs = append ( allErrs , ValidatePodSpecificAnnotations ( newAnnotations , & newPod . Spec , fldPath ) ... )
return allErrs
}
2016-02-02 13:59:54 -05:00
func ValidateEndpointsSpecificAnnotations ( annotations map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-02-01 19:03:04 -05:00
return allErrs
}
2015-01-27 18:55:54 -05:00
// ValidateNameFunc validates that the provided name is valid for a given resource type.
2015-12-16 01:03:20 -05:00
// Not all resources have the same validation rules for names. Prefix is true
2015-12-16 02:49:58 -05:00
// if the name will have a value appended to it. If the name is not valid,
2015-12-16 01:03:20 -05:00
// this returns a list of descriptions of individual characteristics of the
// value that were not valid. Otherwise this returns an empty list or nil.
2017-01-16 11:00:37 -05:00
type ValidateNameFunc apimachineryvalidation . ValidateNameFunc
2015-01-27 18:56:38 -05:00
2015-01-27 23:50:01 -05:00
// ValidatePodName can be used to check whether the given pod name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidatePodName = apimachineryvalidation . NameIsDNSSubdomain
2015-01-27 23:50:01 -05:00
// ValidateReplicationControllerName can be used to check whether the given replication
// controller name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidateReplicationControllerName = apimachineryvalidation . NameIsDNSSubdomain
2015-01-27 23:50:01 -05:00
// 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.
2018-08-11 10:17:29 -04:00
var ValidateServiceName = apimachineryvalidation . NameIsDNS1035Label
2015-01-27 23:50:01 -05:00
// 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
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidateNodeName = apimachineryvalidation . NameIsDNSSubdomain
2015-01-27 23:50:01 -05:00
2015-01-19 16:50:00 -05:00
// ValidateNamespaceName can be used to check whether the given namespace name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2017-01-16 11:00:37 -05:00
var ValidateNamespaceName = apimachineryvalidation . ValidateNamespaceName
2015-01-19 16:50:00 -05:00
2015-02-20 01:03:36 -05:00
// ValidateLimitRangeName can be used to check whether the given limit range name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidateLimitRangeName = apimachineryvalidation . NameIsDNSSubdomain
2015-02-20 01:03:36 -05:00
// ValidateResourceQuotaName can be used to check whether the given
// resource quota name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidateResourceQuotaName = apimachineryvalidation . NameIsDNSSubdomain
2015-02-20 01:03:36 -05:00
// ValidateSecretName can be used to check whether the given secret name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidateSecretName = apimachineryvalidation . NameIsDNSSubdomain
2015-02-20 01:03:36 -05:00
2015-04-27 18:53:28 -04:00
// ValidateServiceAccountName can be used to check whether the given service account name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2017-01-16 11:00:37 -05:00
var ValidateServiceAccountName = apimachineryvalidation . ValidateServiceAccountName
2015-04-27 18:53:28 -04:00
2015-03-15 02:03:46 -04:00
// ValidateEndpointsName can be used to check whether the given endpoints name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidateEndpointsName = apimachineryvalidation . NameIsDNSSubdomain
2015-03-15 02:03:46 -04:00
2016-07-12 22:50:18 -04:00
// ValidateClusterName can be used to check whether the given cluster name is valid.
2017-11-13 22:36:32 -05:00
var ValidateClusterName = apimachineryvalidation . ValidateClusterName
2016-07-12 22:50:18 -04:00
2017-03-02 04:23:57 -05:00
// ValidateClassName can be used to check whether the given class name is valid.
// It is defined here to avoid import cycle between pkg/apis/storage/validation
// (where it should be) and this file.
2018-08-11 10:17:29 -04:00
var ValidateClassName = apimachineryvalidation . NameIsDNSSubdomain
2017-03-02 04:23:57 -05:00
2017-05-09 21:25:34 -04:00
// ValidatePiorityClassName can be used to check whether the given priority
// class name is valid.
2018-08-11 10:17:29 -04:00
var ValidatePriorityClassName = apimachineryvalidation . NameIsDNSSubdomain
2015-01-27 18:55:54 -05:00
2018-08-22 16:57:07 -04:00
// ValidateRuntimeClassName can be used to check whether the given RuntimeClass name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-23 16:14:49 -04:00
func ValidateRuntimeClassName ( name string , fldPath * field . Path ) field . ErrorList {
var allErrs field . ErrorList
for _ , msg := range apimachineryvalidation . NameIsDNSSubdomain ( name , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , name , msg ) )
}
return allErrs
}
2018-08-22 16:57:07 -04:00
2015-08-25 15:07:03 -04:00
// Validates that given value is not negative.
2015-10-08 16:02:23 -04:00
func ValidateNonnegativeField ( value int64 , fldPath * field . Path ) field . ErrorList {
2017-01-16 11:00:37 -05:00
return apimachineryvalidation . ValidateNonnegativeField ( value , fldPath )
2015-08-25 15:07:03 -04:00
}
2015-09-24 10:15:40 -04:00
// Validates that a Quantity is not negative
2015-10-08 16:02:23 -04:00
func ValidateNonnegativeQuantity ( value resource . Quantity , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-09-24 10:15:40 -04:00
if value . Cmp ( resource . Quantity { } ) < 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , value . String ( ) , isNegativeErrorMsg ) )
2015-09-24 10:15:40 -04:00
}
return allErrs
}
2017-11-11 18:39:37 -05:00
// Validates that a Quantity is positive
func ValidatePositiveQuantityValue ( value resource . Quantity , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if value . Cmp ( resource . Quantity { } ) <= 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath , value . String ( ) , isNotPositiveErrorMsg ) )
}
return allErrs
}
2015-11-06 18:30:52 -05:00
func ValidateImmutableField ( newVal , oldVal interface { } , fldPath * field . Path ) field . ErrorList {
2017-11-13 22:36:32 -05:00
return apimachineryvalidation . ValidateImmutableField ( newVal , oldVal , fldPath )
2015-10-11 23:26:17 -04:00
}
2016-11-04 14:06:34 -04:00
func ValidateImmutableAnnotation ( newVal string , oldVal string , annotation string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if oldVal != newVal {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "annotations" , annotation ) , newVal , fieldImmutableErrorMsg ) )
}
return allErrs
}
2015-01-27 18:56:38 -05:00
// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
// been performed.
2015-11-02 22:00:01 -05:00
// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
2015-10-06 18:58:46 -04:00
// TODO: Remove calls to this method scattered in validations of specific resources, e.g., ValidatePodUpdate.
2017-01-16 22:38:19 -05:00
func ValidateObjectMeta ( meta * metav1 . ObjectMeta , requiresNamespace bool , nameFn ValidateNameFunc , fldPath * field . Path ) field . ErrorList {
2017-11-13 22:36:32 -05:00
allErrs := apimachineryvalidation . ValidateObjectMeta ( meta , requiresNamespace , apimachineryvalidation . ValidateNameFunc ( nameFn ) , fldPath )
2017-02-01 10:46:26 -05:00
// run additional checks for the finalizer name
for i := range meta . Finalizers {
allErrs = append ( allErrs , validateKubeFinalizerName ( string ( meta . Finalizers [ i ] ) , fldPath . Child ( "finalizers" ) . Index ( i ) ) ... )
}
return allErrs
2015-01-27 18:55:54 -05:00
}
// ValidateObjectMetaUpdate validates an object's metadata when updated
2017-01-16 22:38:19 -05:00
func ValidateObjectMetaUpdate ( newMeta , oldMeta * metav1 . ObjectMeta , fldPath * field . Path ) field . ErrorList {
2017-11-13 22:36:32 -05:00
allErrs := apimachineryvalidation . ValidateObjectMetaUpdate ( newMeta , oldMeta , fldPath )
2017-02-01 10:46:26 -05:00
// run additional checks for the finalizer name
for i := range newMeta . Finalizers {
allErrs = append ( allErrs , validateKubeFinalizerName ( string ( newMeta . Finalizers [ i ] ) , fldPath . Child ( "finalizers" ) . Index ( i ) ) ... )
}
return allErrs
2014-10-31 02:03:52 -04:00
}
2017-08-09 13:51:46 -04:00
func ValidateVolumes ( volumes [ ] core . Volume , fldPath * field . Path ) ( map [ string ] core . VolumeSource , field . ErrorList ) {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-07-08 02:20:30 -04:00
2015-09-09 13:45:01 -04:00
allNames := sets . String { }
2017-08-09 13:51:46 -04:00
vols := make ( map [ string ] core . VolumeSource )
2015-01-26 12:52:50 -05:00
for i , vol := range volumes {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
2016-04-29 10:58:46 -04:00
namePath := idxPath . Child ( "name" )
2017-07-11 23:37:48 -04:00
el := validateVolumeSource ( & vol . VolumeSource , idxPath , vol . Name )
2014-08-19 22:57:48 -04:00
if len ( vol . Name ) == 0 {
2016-04-29 10:58:46 -04:00
el = append ( el , field . Required ( namePath , "" ) )
} else {
el = append ( el , ValidateDNS1123Label ( vol . Name , namePath ) ... )
}
if allNames . Has ( vol . Name ) {
el = append ( el , field . Duplicate ( namePath , vol . Name ) )
2014-07-16 15:32:59 -04:00
}
2014-08-14 16:46:22 -04:00
if len ( el ) == 0 {
2014-07-08 02:20:30 -04:00
allNames . Insert ( vol . Name )
2017-08-09 13:51:46 -04:00
vols [ vol . Name ] = vol . VolumeSource
2014-07-16 15:32:59 -04:00
} else {
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , el ... )
2014-07-01 17:40:36 -04:00
}
2015-11-04 16:52:14 -05:00
2014-07-01 17:40:36 -04:00
}
2017-08-09 13:51:46 -04:00
return vols , allErrs
}
func IsMatchedVolume ( name string , volumes map [ string ] core . VolumeSource ) bool {
if _ , ok := volumes [ name ] ; ok {
return true
}
2018-04-26 02:55:10 -04:00
return false
2017-08-09 13:51:46 -04:00
}
func isMatchedDevice ( name string , volumes map [ string ] core . VolumeSource ) ( bool , bool ) {
if source , ok := volumes [ name ] ; ok {
if source . PersistentVolumeClaim != nil {
return true , true
}
2018-04-26 02:55:10 -04:00
return true , false
2017-08-09 13:51:46 -04:00
}
2018-04-26 02:55:10 -04:00
return false , false
2017-08-09 13:51:46 -04:00
}
func mountNameAlreadyExists ( name string , devices map [ string ] string ) bool {
if _ , ok := devices [ name ] ; ok {
return true
}
2018-04-26 02:55:10 -04:00
return false
2017-08-09 13:51:46 -04:00
}
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
}
2018-04-26 02:55:10 -04:00
return false
2017-08-09 13:51:46 -04:00
}
func devicePathAlreadyExists ( devicePath string , mounts map [ string ] string ) bool {
for _ , mountPath := range mounts {
if mountPath == devicePath {
return true
}
}
return false
2014-07-01 17:40:36 -04:00
}
2017-10-09 11:58:37 -04:00
func validateVolumeSource ( source * core . VolumeSource , fldPath * field . Path , volName string ) field . ErrorList {
2014-07-16 15:32:59 -04:00
numVolumes := 0
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-10-01 16:35:21 -04:00
if source . EmptyDir != nil {
2014-07-16 15:32:59 -04:00
numVolumes ++
2018-12-27 10:30:37 -05:00
if source . EmptyDir . SizeLimit != nil && source . EmptyDir . SizeLimit . Cmp ( resource . Quantity { } ) < 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "emptyDir" ) . Child ( "sizeLimit" ) , "SizeLimit field must be a valid resource quantity" ) )
2017-05-25 16:53:40 -04:00
}
2014-11-23 23:03:11 -05:00
}
2015-11-15 01:36:25 -05:00
if source . HostPath != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "hostPath" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateHostPathVolumeSource ( source . HostPath , fldPath . Child ( "hostPath" ) ) ... )
}
}
2014-11-23 23:03:11 -05:00
if source . GitRepo != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "gitRepo" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateGitRepoVolumeSource ( source . GitRepo , fldPath . Child ( "gitRepo" ) ) ... )
}
2014-07-16 15:32:59 -04:00
}
2014-08-05 13:58:43 -04:00
if source . GCEPersistentDisk != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "gcePersistentDisk" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateGCEPersistentDiskVolumeSource ( source . GCEPersistentDisk , fldPath . Child ( "persistentDisk" ) ) ... )
}
2014-08-05 13:58:43 -04:00
}
2015-04-07 17:16:36 -04:00
if source . AWSElasticBlockStore != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "awsElasticBlockStore" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateAWSElasticBlockStoreVolumeSource ( source . AWSElasticBlockStore , fldPath . Child ( "awsElasticBlockStore" ) ) ... )
}
2015-03-06 09:26:39 -05:00
}
2015-02-17 20:24:50 -05:00
if source . Secret != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "secret" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateSecretVolumeSource ( source . Secret , fldPath . Child ( "secret" ) ) ... )
}
2015-02-17 20:24:50 -05:00
}
2015-02-10 14:00:11 -05:00
if source . NFS != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "nfs" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateNFSVolumeSource ( source . NFS , fldPath . Child ( "nfs" ) ) ... )
}
2015-02-10 14:00:11 -05:00
}
2015-03-13 17:31:13 -04:00
if source . ISCSI != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "iscsi" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateISCSIVolumeSource ( source . ISCSI , fldPath . Child ( "iscsi" ) ) ... )
}
2017-07-11 23:37:48 -04:00
if source . ISCSI . InitiatorName != nil && len ( volName + ":" + source . ISCSI . TargetPortal ) > 64 {
tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , volName , tooLongErr ) )
}
2015-03-13 17:31:13 -04:00
}
2015-03-26 14:53:21 -04:00
if source . Glusterfs != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "glusterfs" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2017-08-22 05:11:25 -04:00
allErrs = append ( allErrs , validateGlusterfsVolumeSource ( source . Glusterfs , fldPath . Child ( "glusterfs" ) ) ... )
2015-11-15 01:36:25 -05:00
}
2015-03-26 14:53:21 -04:00
}
2015-09-25 15:22:23 -04:00
if source . Flocker != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "flocker" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateFlockerVolumeSource ( source . Flocker , fldPath . Child ( "flocker" ) ) ... )
}
2015-09-25 15:22:23 -04:00
}
2015-06-10 16:57:33 -04:00
if source . PersistentVolumeClaim != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "persistentVolumeClaim" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validatePersistentClaimVolumeSource ( source . PersistentVolumeClaim , fldPath . Child ( "persistentVolumeClaim" ) ) ... )
}
2015-04-18 07:16:07 -04:00
}
2015-04-07 13:22:23 -04:00
if source . RBD != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "rbd" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateRBDVolumeSource ( source . RBD , fldPath . Child ( "rbd" ) ) ... )
}
2015-04-07 13:22:23 -04:00
}
2015-04-10 12:54:01 -04:00
if source . Cinder != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "cinder" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateCinderVolumeSource ( source . Cinder , fldPath . Child ( "cinder" ) ) ... )
}
2015-04-10 12:54:01 -04:00
}
2015-04-09 14:05:24 -04:00
if source . CephFS != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "cephFS" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateCephFSVolumeSource ( source . CephFS , fldPath . Child ( "cephfs" ) ) ... )
}
2015-04-09 14:05:24 -04:00
}
2016-04-20 04:38:19 -04:00
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" ) ) ... )
}
}
2015-02-20 00:36:23 -05:00
if source . DownwardAPI != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "downwarAPI" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateDownwardAPIVolumeSource ( source . DownwardAPI , fldPath . Child ( "downwardAPI" ) ) ... )
}
2015-02-20 00:36:23 -05:00
}
2015-08-11 11:19:29 -04:00
if source . FC != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "fc" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateFCVolumeSource ( source . FC , fldPath . Child ( "fc" ) ) ... )
}
2015-08-11 11:19:29 -04:00
}
2015-09-30 14:31:53 -04:00
if source . FlexVolume != nil {
2016-01-25 14:41:16 -05:00
if numVolumes > 0 {
2016-08-23 06:41:36 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "flexVolume" ) , "may not specify more than 1 volume type" ) )
2016-01-25 14:41:16 -05:00
} else {
numVolumes ++
allErrs = append ( allErrs , validateFlexVolumeSource ( source . FlexVolume , fldPath . Child ( "flexVolume" ) ) ... )
}
}
if source . ConfigMap != nil {
if numVolumes > 0 {
2016-08-23 06:41:36 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "configMap" ) , "may not specify more than 1 volume type" ) )
2016-01-25 14:41:16 -05:00
} else {
numVolumes ++
allErrs = append ( allErrs , validateConfigMapVolumeSource ( source . ConfigMap , fldPath . Child ( "configMap" ) ) ... )
}
2015-09-30 14:31:53 -04:00
}
2017-05-16 12:50:58 -04:00
2015-11-13 11:47:04 -05:00
if source . AzureFile != nil {
2017-05-16 12:50:58 -04:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "azureFile" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateAzureFile ( source . AzureFile , fldPath . Child ( "azureFile" ) ) ... )
}
2015-11-13 11:47:04 -05:00
}
2017-05-16 12:50:58 -04:00
2016-05-23 17:39:09 -04:00
if source . VsphereVolume != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "vsphereVolume" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateVsphereVolumeSource ( source . VsphereVolume , fldPath . Child ( "vsphereVolume" ) ) ... )
}
}
2016-11-03 01:31:47 -04:00
if source . PhotonPersistentDisk != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "photonPersistentDisk" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validatePhotonPersistentDiskVolumeSource ( source . PhotonPersistentDisk , fldPath . Child ( "photonPersistentDisk" ) ) ... )
}
}
2016-12-19 18:17:11 -05:00
if source . PortworxVolume != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "portworxVolume" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validatePortworxVolumeSource ( source . PortworxVolume , fldPath . Child ( "portworxVolume" ) ) ... )
}
}
2016-07-27 14:07:34 -04:00
if source . AzureDisk != nil {
2017-05-16 12:50:58 -04:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "azureDisk" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateAzureDisk ( source . AzureDisk , fldPath . Child ( "azureDisk" ) ) ... )
}
2016-07-27 14:07:34 -04:00
}
2017-02-24 10:47:40 -05:00
if source . StorageOS != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "storageos" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateStorageOSVolumeSource ( source . StorageOS , fldPath . Child ( "storageos" ) ) ... )
}
}
2016-11-10 17:33:06 -05:00
if source . Projected != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "projected" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateProjectedVolumeSource ( source . Projected , fldPath . Child ( "projected" ) ) ... )
}
}
2016-11-19 15:46:23 -05:00
if source . ScaleIO != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "scaleIO" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateScaleIOVolumeSource ( source . ScaleIO , fldPath . Child ( "scaleIO" ) ) ... )
}
}
2018-08-14 17:00:25 -04:00
if source . CSI != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "csi" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateCSIVolumeSource ( source . CSI , fldPath . Child ( "csi" ) ) ... )
}
}
2016-07-27 14:07:34 -04:00
2015-11-14 15:26:04 -05:00
if numVolumes == 0 {
allErrs = append ( allErrs , field . Required ( fldPath , "must specify a volume type" ) )
2014-07-16 15:32:59 -04:00
}
2015-04-07 13:22:23 -04:00
2014-07-16 15:32:59 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateHostPathVolumeSource ( hostPath * core . HostPathVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( hostPath . Path ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2017-06-10 09:48:42 -04:00
return allErrs
2014-07-14 21:39:30 -04:00
}
2017-06-10 09:48:42 -04:00
allErrs = append ( allErrs , validatePathNoBacksteps ( hostPath . Path , fldPath . Child ( "path" ) ) ... )
2017-06-07 01:10:09 -04:00
allErrs = append ( allErrs , validateHostPathType ( hostPath . Type , fldPath . Child ( "type" ) ) ... )
2014-07-14 21:39:30 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateGitRepoVolumeSource ( gitRepo * core . GitRepoVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-10-10 06:10:45 -04:00
if len ( gitRepo . Repository ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "repository" ) , "" ) )
2014-11-23 23:03:11 -05:00
}
2015-10-10 06:10:45 -04:00
2016-07-30 01:41:20 -04:00
pathErrs := validateLocalDescendingPath ( gitRepo . Directory , fldPath . Child ( "directory" ) )
2015-10-10 06:10:45 -04:00
allErrs = append ( allErrs , pathErrs ... )
2014-11-23 23:03:11 -05:00
return allErrs
}
2014-07-08 00:32:56 -04:00
2017-10-09 11:58:37 -04:00
func validateISCSIVolumeSource ( iscsi * core . ISCSIVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-08-28 14:22:01 -04:00
allErrs := field . ErrorList { }
if len ( iscsi . TargetPortal ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "targetPortal" ) , "" ) )
}
if len ( iscsi . IQN ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "iqn" ) , "" ) )
} else {
if ! strings . HasPrefix ( iscsi . IQN , "iqn" ) && ! strings . HasPrefix ( iscsi . IQN , "eui" ) && ! strings . HasPrefix ( iscsi . IQN , "naa" ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format starting with iqn, eui, or naa" ) )
} else if strings . HasPrefix ( iscsi . IQN , "iqn" ) && ! iscsiInitiatorIqnRegex . MatchString ( iscsi . IQN ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format" ) )
} else if strings . HasPrefix ( iscsi . IQN , "eui" ) && ! iscsiInitiatorEuiRegex . MatchString ( iscsi . IQN ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format" ) )
} else if strings . HasPrefix ( iscsi . IQN , "naa" ) && ! iscsiInitiatorNaaRegex . MatchString ( iscsi . IQN ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format" ) )
}
}
if iscsi . Lun < 0 || iscsi . Lun > 255 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "lun" ) , iscsi . Lun , validation . InclusiveRangeError ( 0 , 255 ) ) )
}
if ( iscsi . DiscoveryCHAPAuth || iscsi . SessionCHAPAuth ) && iscsi . SecretRef == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" ) , "" ) )
}
if iscsi . InitiatorName != nil {
initiator := * iscsi . InitiatorName
if ! strings . HasPrefix ( initiator , "iqn" ) && ! strings . HasPrefix ( initiator , "eui" ) && ! strings . HasPrefix ( initiator , "naa" ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format starting with iqn, eui, or naa" ) )
}
if strings . HasPrefix ( initiator , "iqn" ) && ! iscsiInitiatorIqnRegex . MatchString ( initiator ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format" ) )
} else if strings . HasPrefix ( initiator , "eui" ) && ! iscsiInitiatorEuiRegex . MatchString ( initiator ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format" ) )
} else if strings . HasPrefix ( initiator , "naa" ) && ! iscsiInitiatorNaaRegex . MatchString ( initiator ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format" ) )
}
}
return allErrs
}
func validateISCSIPersistentVolumeSource ( iscsi * core . ISCSIPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( iscsi . TargetPortal ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "targetPortal" ) , "" ) )
2015-03-13 17:31:13 -04:00
}
2015-12-08 23:39:18 -05:00
if len ( iscsi . IQN ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "iqn" ) , "" ) )
2017-07-11 23:37:48 -04:00
} else {
if ! strings . HasPrefix ( iscsi . IQN , "iqn" ) && ! strings . HasPrefix ( iscsi . IQN , "eui" ) && ! strings . HasPrefix ( iscsi . IQN , "naa" ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format" ) )
} else if strings . HasPrefix ( iscsi . IQN , "iqn" ) && ! iscsiInitiatorIqnRegex . MatchString ( iscsi . IQN ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format" ) )
} else if strings . HasPrefix ( iscsi . IQN , "eui" ) && ! iscsiInitiatorEuiRegex . MatchString ( iscsi . IQN ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format" ) )
} else if strings . HasPrefix ( iscsi . IQN , "naa" ) && ! iscsiInitiatorNaaRegex . MatchString ( iscsi . IQN ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "iqn" ) , iscsi . IQN , "must be valid format" ) )
}
2015-03-13 17:31:13 -04:00
}
if iscsi . Lun < 0 || iscsi . Lun > 255 {
2016-01-04 11:33:26 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "lun" ) , iscsi . Lun , validation . InclusiveRangeError ( 0 , 255 ) ) )
2015-03-13 17:31:13 -04:00
}
2017-03-17 15:50:39 -04:00
if ( iscsi . DiscoveryCHAPAuth || iscsi . SessionCHAPAuth ) && iscsi . SecretRef == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" ) , "" ) )
}
2017-08-28 14:22:01 -04:00
if iscsi . SecretRef != nil {
if len ( iscsi . SecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" , "name" ) , "" ) )
}
}
2017-07-11 23:37:48 -04:00
if iscsi . InitiatorName != nil {
initiator := * iscsi . InitiatorName
if ! strings . HasPrefix ( initiator , "iqn" ) && ! strings . HasPrefix ( initiator , "eui" ) && ! strings . HasPrefix ( initiator , "naa" ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format" ) )
}
if strings . HasPrefix ( initiator , "iqn" ) && ! iscsiInitiatorIqnRegex . MatchString ( initiator ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format" ) )
} else if strings . HasPrefix ( initiator , "eui" ) && ! iscsiInitiatorEuiRegex . MatchString ( initiator ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format" ) )
} else if strings . HasPrefix ( initiator , "naa" ) && ! iscsiInitiatorNaaRegex . MatchString ( initiator ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "initiatorname" ) , initiator , "must be valid format" ) )
}
}
2015-03-13 17:31:13 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateFCVolumeSource ( fc * core . FCVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2017-07-10 19:51:24 -04:00
if len ( fc . TargetWWNs ) < 1 && len ( fc . WWIDs ) < 1 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "targetWWNs" ) , "must specify either targetWWNs or wwids, but not both" ) )
2015-08-11 11:19:29 -04:00
}
2015-12-08 23:39:18 -05:00
2017-07-10 19:51:24 -04:00
if len ( fc . TargetWWNs ) != 0 && len ( fc . WWIDs ) != 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "targetWWNs" ) , fc . TargetWWNs , "targetWWNs and wwids can not be specified simultaneously" ) )
}
if len ( fc . TargetWWNs ) != 0 {
if fc . Lun == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "lun" ) , "lun is required if targetWWNs is specified" ) )
} else {
if * fc . Lun < 0 || * fc . Lun > 255 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "lun" ) , fc . Lun , validation . InclusiveRangeError ( 0 , 255 ) ) )
}
2015-08-11 11:19:29 -04:00
}
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateGCEPersistentDiskVolumeSource ( pd * core . GCEPersistentDiskVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( pd . PDName ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "pdName" ) , "" ) )
2014-08-05 13:58:43 -04:00
}
2015-11-04 16:52:14 -05:00
if pd . Partition < 0 || pd . Partition > 255 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "partition" ) , pd . Partition , pdPartitionErrorMsg ) )
2014-08-05 13:58:43 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateAWSElasticBlockStoreVolumeSource ( PD * core . AWSElasticBlockStoreVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( PD . VolumeID ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeID" ) , "" ) )
2015-03-06 09:26:39 -05:00
}
if PD . Partition < 0 || PD . Partition > 255 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "partition" ) , PD . Partition , pdPartitionErrorMsg ) )
2015-03-06 09:26:39 -05:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateSecretVolumeSource ( secretSource * core . SecretVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( secretSource . SecretName ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretName" ) , "" ) )
2015-02-17 20:24:50 -05:00
}
2016-07-10 20:48:28 -04:00
secretMode := secretSource . DefaultMode
if secretMode != nil && ( * secretMode > 0777 || * secretMode < 0 ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "defaultMode" ) , * secretMode , fileModeErrorMsg ) )
2016-07-10 20:48:28 -04:00
}
2016-07-30 01:41:20 -04:00
itemsPath := fldPath . Child ( "items" )
for i , kp := range secretSource . Items {
itemPath := itemsPath . Index ( i )
allErrs = append ( allErrs , validateKeyToPath ( & kp , itemPath ) ... )
}
2015-02-17 20:24:50 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateConfigMapVolumeSource ( configMapSource * core . ConfigMapVolumeSource , fldPath * field . Path ) field . ErrorList {
2016-01-25 14:41:16 -05:00
allErrs := field . ErrorList { }
if len ( configMapSource . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
}
2016-07-10 20:48:28 -04:00
configMapMode := configMapSource . DefaultMode
if configMapMode != nil && ( * configMapMode > 0777 || * configMapMode < 0 ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "defaultMode" ) , * configMapMode , fileModeErrorMsg ) )
2016-07-10 20:48:28 -04:00
}
2016-07-30 01:41:20 -04:00
itemsPath := fldPath . Child ( "items" )
for i , kp := range configMapSource . Items {
itemPath := itemsPath . Index ( i )
allErrs = append ( allErrs , validateKeyToPath ( & kp , itemPath ) ... )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateKeyToPath ( kp * core . KeyToPath , fldPath * field . Path ) field . ErrorList {
2016-07-30 01:41:20 -04:00
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" ) ) ... )
2016-07-10 20:48:28 -04:00
if kp . Mode != nil && ( * kp . Mode > 0777 || * kp . Mode < 0 ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "mode" ) , * kp . Mode , fileModeErrorMsg ) )
2016-07-10 20:48:28 -04:00
}
2016-01-25 14:41:16 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validatePersistentClaimVolumeSource ( claim * core . PersistentVolumeClaimVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( claim . ClaimName ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "claimName" ) , "" ) )
2015-04-18 07:16:07 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateNFSVolumeSource ( nfs * core . NFSVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( nfs . Server ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "server" ) , "" ) )
2015-02-10 14:00:11 -05:00
}
2015-12-08 23:39:18 -05:00
if len ( nfs . Path ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2015-02-10 14:00:11 -05:00
}
if ! path . IsAbs ( nfs . Path ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "path" ) , nfs . Path , "must be an absolute path" ) )
2015-02-10 14:00:11 -05:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateQuobyteVolumeSource ( quobyte * core . QuobyteVolumeSource , fldPath * field . Path ) field . ErrorList {
2016-04-20 04:38:19 -04:00
allErrs := field . ErrorList { }
if len ( quobyte . Registry ) == 0 {
2016-08-23 06:41:36 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "registry" ) , "must be a host:port pair or multiple pairs separated by commas" ) )
2018-07-25 04:22:02 -04:00
} else if len ( quobyte . Tenant ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "tenant" ) , "must be a UUID provided by the configuration and may not be omitted " ) )
} else if len ( quobyte . Tenant ) >= 65 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "tenant" ) , "must be a UUID and may not exceed a length of 64 characters" ) )
2016-04-20 04:38:19 -04:00
} else {
for _ , hostPortPair := range strings . Split ( quobyte . Registry , "," ) {
if _ , _ , err := net . SplitHostPort ( hostPortPair ) ; err != nil {
2016-08-23 06:41:36 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "registry" ) , quobyte . Registry , "must be a host:port pair or multiple pairs separated by commas" ) )
2016-04-20 04:38:19 -04:00
}
}
}
if len ( quobyte . Volume ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volume" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateGlusterfsVolumeSource ( glusterfs * core . GlusterfsVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( glusterfs . EndpointsName ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "endpoints" ) , "" ) )
2015-03-26 14:53:21 -04:00
}
2015-12-08 23:39:18 -05:00
if len ( glusterfs . Path ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2015-03-26 14:53:21 -04:00
}
return allErrs
}
2018-11-06 05:21:01 -05:00
func validateGlusterfsPersistentVolumeSource ( glusterfs * core . GlusterfsPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( glusterfs . EndpointsName ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "endpoints" ) , "" ) )
}
if len ( glusterfs . Path ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
}
if glusterfs . EndpointsNamespace != nil {
endpointNs := glusterfs . EndpointsNamespace
if * endpointNs == "" {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "endpointsNamespace" ) , * endpointNs , "if the endpointnamespace is set, it must be a valid namespace name" ) )
} else {
for _ , msg := range ValidateNamespaceName ( * endpointNs , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "endpointsNamespace" ) , * endpointNs , msg ) )
}
}
}
return allErrs
}
2015-03-26 14:53:21 -04:00
2017-10-09 11:58:37 -04:00
func validateFlockerVolumeSource ( flocker * core . FlockerVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2016-08-01 06:45:03 -04:00
if len ( flocker . DatasetName ) == 0 && len ( flocker . DatasetUUID ) == 0 {
//TODO: consider adding a RequiredOneOf() error for this and similar cases
allErrs = append ( allErrs , field . Required ( fldPath , "one of datasetName and datasetUUID is required" ) )
}
if len ( flocker . DatasetName ) != 0 && len ( flocker . DatasetUUID ) != 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath , "resource" , "datasetName and datasetUUID can not be specified simultaneously" ) )
2015-09-25 15:22:23 -04:00
}
if strings . Contains ( flocker . DatasetName , "/" ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "datasetName" ) , flocker . DatasetName , "must not contain '/'" ) )
2015-09-25 15:22:23 -04:00
}
return allErrs
}
2017-02-17 11:37:15 -05:00
var validVolumeDownwardAPIFieldPathExpressions = sets . NewString (
2016-07-30 02:07:44 -04:00
"metadata.name" ,
"metadata.namespace" ,
"metadata.labels" ,
2017-06-27 04:54:35 -04:00
"metadata.annotations" ,
"metadata.uid" )
2015-02-20 00:36:23 -05:00
2017-10-09 11:58:37 -04:00
func validateDownwardAPIVolumeFile ( file * core . DownwardAPIVolumeFile , fldPath * field . Path ) field . ErrorList {
2016-11-10 17:33:06 -05:00
allErrs := field . ErrorList { }
if len ( file . Path ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
}
allErrs = append ( allErrs , validateLocalNonReservedPath ( file . Path , fldPath . Child ( "path" ) ) ... )
if file . FieldRef != nil {
2017-02-17 11:37:15 -05:00
allErrs = append ( allErrs , validateObjectFieldSelector ( file . FieldRef , & validVolumeDownwardAPIFieldPathExpressions , fldPath . Child ( "fieldRef" ) ) ... )
2016-11-10 17:33:06 -05:00
if file . ResourceFieldRef != nil {
allErrs = append ( allErrs , field . Invalid ( fldPath , "resource" , "fieldRef and resourceFieldRef can not be specified simultaneously" ) )
}
} 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" ) )
}
if file . Mode != nil && ( * file . Mode > 0777 || * file . Mode < 0 ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "mode" ) , * file . Mode , fileModeErrorMsg ) )
2016-11-10 17:33:06 -05:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateDownwardAPIVolumeSource ( downwardAPIVolume * core . DownwardAPIVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2016-07-10 20:48:28 -04:00
downwardAPIMode := downwardAPIVolume . DefaultMode
if downwardAPIMode != nil && ( * downwardAPIMode > 0777 || * downwardAPIMode < 0 ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "defaultMode" ) , * downwardAPIMode , fileModeErrorMsg ) )
2016-07-10 20:48:28 -04:00
}
2016-07-30 02:07:44 -04:00
for _ , file := range downwardAPIVolume . Items {
2016-11-10 17:33:06 -05:00
allErrs = append ( allErrs , validateDownwardAPIVolumeFile ( & file , fldPath ) ... )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateProjectionSources ( projection * core . ProjectedVolumeSource , projectionMode * int32 , fldPath * field . Path ) field . ErrorList {
2016-11-10 17:33:06 -05:00
allErrs := field . ErrorList { }
allPaths := sets . String { }
2018-03-31 17:13:35 -04:00
for i , source := range projection . Sources {
2016-11-10 17:33:06 -05:00
numSources := 0
2018-03-31 17:13:35 -04:00
srcPath := fldPath . Child ( "sources" ) . Index ( i )
if projPath := srcPath . Child ( "secret" ) ; source . Secret != nil {
numSources ++
if len ( source . Secret . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( projPath . Child ( "name" ) , "" ) )
}
itemsPath := projPath . Child ( "items" )
for i , kp := range source . Secret . Items {
itemPath := itemsPath . Index ( i )
allErrs = append ( allErrs , validateKeyToPath ( & kp , itemPath ) ... )
if len ( kp . Path ) > 0 {
curPath := kp . Path
if ! allPaths . Has ( curPath ) {
allPaths . Insert ( curPath )
} else {
allErrs = append ( allErrs , field . Invalid ( fldPath , source . Secret . Name , "conflicting duplicate paths" ) )
2016-11-10 17:33:06 -05:00
}
}
}
}
2018-03-31 17:13:35 -04:00
if projPath := srcPath . Child ( "configMap" ) ; source . ConfigMap != nil {
numSources ++
if len ( source . ConfigMap . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( projPath . Child ( "name" ) , "" ) )
}
itemsPath := projPath . Child ( "items" )
for i , kp := range source . ConfigMap . Items {
itemPath := itemsPath . Index ( i )
allErrs = append ( allErrs , validateKeyToPath ( & kp , itemPath ) ... )
if len ( kp . Path ) > 0 {
curPath := kp . Path
if ! allPaths . Has ( curPath ) {
allPaths . Insert ( curPath )
} else {
allErrs = append ( allErrs , field . Invalid ( fldPath , source . ConfigMap . Name , "conflicting duplicate paths" ) )
2016-11-10 17:33:06 -05:00
}
}
2016-05-23 18:08:22 -04:00
}
}
2018-03-31 17:13:35 -04:00
if projPath := srcPath . Child ( "downwardAPI" ) ; source . DownwardAPI != nil {
numSources ++
for _ , file := range source . DownwardAPI . Items {
allErrs = append ( allErrs , validateDownwardAPIVolumeFile ( & file , projPath ) ... )
if len ( file . Path ) > 0 {
curPath := file . Path
if ! allPaths . Has ( curPath ) {
allPaths . Insert ( curPath )
} else {
allErrs = append ( allErrs , field . Invalid ( fldPath , curPath , "conflicting duplicate paths" ) )
2016-11-10 17:33:06 -05:00
}
}
}
2016-07-10 20:48:28 -04:00
}
2018-08-02 03:51:13 -04:00
if projPath := srcPath . Child ( "serviceAccountToken" ) ; source . ServiceAccountToken != nil {
2018-05-14 21:56:23 -04:00
numSources ++
if source . ServiceAccountToken . ExpirationSeconds < 10 * 60 {
allErrs = append ( allErrs , field . Invalid ( projPath . Child ( "expirationSeconds" ) , source . ServiceAccountToken . ExpirationSeconds , "may not specify a duration less than 10 minutes" ) )
}
if source . ServiceAccountToken . ExpirationSeconds > 1 << 32 {
allErrs = append ( allErrs , field . Invalid ( projPath . Child ( "expirationSeconds" ) , source . ServiceAccountToken . ExpirationSeconds , "may not specify a duration larger than 2^32 seconds" ) )
}
if source . ServiceAccountToken . Path == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
}
}
2018-03-31 17:13:35 -04:00
if numSources > 1 {
allErrs = append ( allErrs , field . Forbidden ( srcPath , "may not specify more than 1 volume type" ) )
}
2015-02-20 00:36:23 -05:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateProjectedVolumeSource ( projection * core . ProjectedVolumeSource , fldPath * field . Path ) field . ErrorList {
2016-11-10 17:33:06 -05:00
allErrs := field . ErrorList { }
projectionMode := projection . DefaultMode
if projectionMode != nil && ( * projectionMode > 0777 || * projectionMode < 0 ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "defaultMode" ) , * projectionMode , fileModeErrorMsg ) )
2016-11-10 17:33:06 -05:00
}
allErrs = append ( allErrs , validateProjectionSources ( projection , projectionMode , fldPath ) ... )
return allErrs
}
2017-06-07 01:10:09 -04:00
var supportedHostPathTypes = sets . NewString (
2017-10-09 11:58:37 -04:00
string ( core . HostPathUnset ) ,
string ( core . HostPathDirectoryOrCreate ) ,
string ( core . HostPathDirectory ) ,
string ( core . HostPathFileOrCreate ) ,
string ( core . HostPathFile ) ,
string ( core . HostPathSocket ) ,
string ( core . HostPathCharDev ) ,
string ( core . HostPathBlockDev ) )
func validateHostPathType ( hostPathType * core . HostPathType , fldPath * field . Path ) field . ErrorList {
2017-06-07 01:10:09 -04:00
allErrs := field . ErrorList { }
2017-09-10 08:23:07 -04:00
if hostPathType != nil && ! supportedHostPathTypes . Has ( string ( * hostPathType ) ) {
2017-06-07 01:10:09 -04:00
allErrs = append ( allErrs , field . NotSupported ( fldPath , hostPathType , supportedHostPathTypes . List ( ) ) )
}
return allErrs
}
2016-03-04 20:40:15 -05:00
// This validate will make sure targetPath:
// 1. is not abs path
2016-07-30 01:41:20 -04:00
// 2. does not have any element which is ".."
func validateLocalDescendingPath ( targetPath string , fldPath * field . Path ) field . ErrorList {
2016-03-04 20:40:15 -05:00
allErrs := field . ErrorList { }
if path . IsAbs ( targetPath ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , targetPath , "must be a relative path" ) )
}
2016-07-30 01:41:20 -04:00
2017-06-10 09:48:42 -04:00
allErrs = append ( allErrs , validatePathNoBacksteps ( targetPath , fldPath ) ... )
return allErrs
}
// validatePathNoBacksteps makes sure the targetPath does not have any `..` path elements when split
//
// This assumes the OS of the apiserver and the nodes are the same. The same check should be done
// on the node to ensure there are no backsteps.
func validatePathNoBacksteps ( targetPath string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
parts := strings . Split ( filepath . ToSlash ( targetPath ) , "/" )
2016-07-30 01:41:20 -04:00
for _ , item := range parts {
if item == ".." {
allErrs = append ( allErrs , field . Invalid ( fldPath , targetPath , "must not contain '..'" ) )
2016-07-30 02:52:25 -04:00
break // even for `../../..`, one error is sufficient to make the point
2016-07-30 01:41:20 -04:00
}
2016-03-04 20:40:15 -05:00
}
return allErrs
}
2017-09-01 06:05:55 -04:00
// validateMountPropagation verifies that MountPropagation field is valid and
// allowed for given container.
2017-10-09 11:58:37 -04:00
func validateMountPropagation ( mountPropagation * core . MountPropagationMode , container * core . Container , fldPath * field . Path ) field . ErrorList {
2017-09-01 06:05:55 -04:00
allErrs := field . ErrorList { }
if mountPropagation == nil {
return allErrs
}
2018-04-12 07:57:54 -04:00
supportedMountPropagations := sets . NewString ( string ( core . MountPropagationBidirectional ) , string ( core . MountPropagationHostToContainer ) , string ( core . MountPropagationNone ) )
2017-09-01 06:05:55 -04:00
if ! supportedMountPropagations . Has ( string ( * mountPropagation ) ) {
allErrs = append ( allErrs , field . NotSupported ( fldPath , * mountPropagation , supportedMountPropagations . List ( ) ) )
}
if container == nil {
// The container is not available yet, e.g. during validation of
// PodPreset. Stop validation now, Pod validation will refuse final
// Pods with Bidirectional propagation in non-privileged containers.
return allErrs
}
privileged := container . SecurityContext != nil && container . SecurityContext . Privileged != nil && * container . SecurityContext . Privileged
2017-10-09 11:58:37 -04:00
if * mountPropagation == core . MountPropagationBidirectional && ! privileged {
2017-09-01 06:05:55 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath , "Bidirectional mount propagation is available only to privileged containers" ) )
}
return allErrs
}
2015-10-10 06:10:45 -04:00
// This validate will make sure targetPath:
// 1. is not abs path
2016-07-30 01:41:20 -04:00
// 2. does not contain any '..' elements
2015-10-10 06:10:45 -04:00
// 3. does not start with '..'
2016-07-30 01:41:20 -04:00
func validateLocalNonReservedPath ( targetPath string , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2016-07-30 01:41:20 -04:00
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 , "../" ) {
2015-11-14 11:44:50 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , targetPath , "must not start with '..'" ) )
2015-10-10 06:10:45 -04:00
}
return allErrs
}
2015-11-04 16:52:14 -05:00
2017-10-09 11:58:37 -04:00
func validateRBDVolumeSource ( rbd * core . RBDVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-04-07 13:22:23 -04:00
if len ( rbd . CephMonitors ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "monitors" ) , "" ) )
2015-04-07 13:22:23 -04:00
}
2015-12-08 23:39:18 -05:00
if len ( rbd . RBDImage ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "image" ) , "" ) )
2015-04-07 13:22:23 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateRBDPersistentVolumeSource ( rbd * core . RBDPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-10-23 16:59:34 -04:00
allErrs := field . ErrorList { }
if len ( rbd . CephMonitors ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "monitors" ) , "" ) )
}
if len ( rbd . RBDImage ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "image" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateCinderVolumeSource ( cd * core . CinderVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if len ( cd . VolumeID ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeID" ) , "" ) )
2015-04-10 12:54:01 -04:00
}
2018-05-14 17:09:05 -04:00
if cd . SecretRef != nil {
if len ( cd . SecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" , "name" ) , "" ) )
}
}
return allErrs
}
func validateCinderPersistentVolumeSource ( cd * core . CinderPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( cd . VolumeID ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeID" ) , "" ) )
}
if cd . SecretRef != nil {
if len ( cd . SecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" , "name" ) , "" ) )
}
if len ( cd . SecretRef . Namespace ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" , "namespace" ) , "" ) )
}
}
2015-04-10 12:54:01 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateCephFSVolumeSource ( cephfs * core . CephFSVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-04-09 14:05:24 -04:00
if len ( cephfs . Monitors ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "monitors" ) , "" ) )
2015-04-09 14:05:24 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateCephFSPersistentVolumeSource ( cephfs * core . CephFSPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-07-24 10:53:49 -04:00
allErrs := field . ErrorList { }
if len ( cephfs . Monitors ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "monitors" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateFlexVolumeSource ( fv * core . FlexVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-09-30 14:31:53 -04:00
allErrs := field . ErrorList { }
if len ( fv . Driver ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "driver" ) , "" ) )
}
2017-01-05 15:45:59 -05:00
// Make sure user-specified options don't use kubernetes namespaces
for k := range fv . Options {
namespace := k
if parts := strings . SplitN ( k , "/" , 2 ) ; len ( parts ) == 2 {
namespace = parts [ 0 ]
}
normalized := "." + strings . ToLower ( namespace )
if strings . HasSuffix ( normalized , ".kubernetes.io" ) || strings . HasSuffix ( normalized , ".k8s.io" ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "options" ) . Key ( k ) , k , "kubernetes.io and k8s.io namespaces are reserved" ) )
}
}
2015-09-30 14:31:53 -04:00
return allErrs
}
2017-11-27 23:19:38 -05:00
func validateFlexPersistentVolumeSource ( fv * core . FlexPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( fv . Driver ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "driver" ) , "" ) )
}
// Make sure user-specified options don't use kubernetes namespaces
for k := range fv . Options {
namespace := k
if parts := strings . SplitN ( k , "/" , 2 ) ; len ( parts ) == 2 {
namespace = parts [ 0 ]
}
normalized := "." + strings . ToLower ( namespace )
if strings . HasSuffix ( normalized , ".kubernetes.io" ) || strings . HasSuffix ( normalized , ".k8s.io" ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "options" ) . Key ( k ) , k , "kubernetes.io and k8s.io namespaces are reserved" ) )
}
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateAzureFile ( azure * core . AzureFileVolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-13 11:47:04 -05:00
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" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateAzureFilePV ( azure * core . AzureFilePersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-06-16 11:23:22 -04:00
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
}
2017-10-09 11:58:37 -04:00
func validateAzureDisk ( azure * core . AzureDiskVolumeSource , fldPath * field . Path ) field . ErrorList {
var supportedCachingModes = sets . NewString ( string ( core . AzureDataDiskCachingNone ) , string ( core . AzureDataDiskCachingReadOnly ) , string ( core . AzureDataDiskCachingReadWrite ) )
var supportedDiskKinds = sets . NewString ( string ( core . AzureSharedBlobDisk ) , string ( core . AzureDedicatedBlobDisk ) , string ( core . AzureManagedDisk ) )
2017-05-16 12:50:58 -04:00
2019-04-01 00:13:36 -04:00
diskURISupportedManaged := [ ] string { "/subscriptions/{sub-id}/resourcegroups/{group-name}/providers/microsoft.compute/disks/{disk-id}" }
diskURISupportedblob := [ ] string { "https://{account-name}.blob.core.windows.net/{container-name}/{disk-name}.vhd" }
2017-05-16 12:50:58 -04:00
2016-07-27 14:07:34 -04:00
allErrs := field . ErrorList { }
if azure . DiskName == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "diskName" ) , "" ) )
}
2017-05-16 12:50:58 -04:00
2016-07-27 14:07:34 -04:00
if azure . DataDiskURI == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "diskURI" ) , "" ) )
}
2017-05-16 12:50:58 -04:00
2016-07-27 14:07:34 -04:00
if azure . CachingMode != nil && ! supportedCachingModes . Has ( string ( * azure . CachingMode ) ) {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "cachingMode" ) , * azure . CachingMode , supportedCachingModes . List ( ) ) )
}
2017-05-16 12:50:58 -04:00
if azure . Kind != nil && ! supportedDiskKinds . Has ( string ( * azure . Kind ) ) {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "kind" ) , * azure . Kind , supportedDiskKinds . List ( ) ) )
}
// validate that DiskUri is the correct format
2017-10-09 11:58:37 -04:00
if azure . Kind != nil && * azure . Kind == core . AzureManagedDisk && strings . Index ( azure . DataDiskURI , "/subscriptions/" ) != 0 {
2019-04-01 00:13:36 -04:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "diskURI" ) , azure . DataDiskURI , diskURISupportedManaged ) )
2017-05-16 12:50:58 -04:00
}
2017-10-09 11:58:37 -04:00
if azure . Kind != nil && * azure . Kind != core . AzureManagedDisk && strings . Index ( azure . DataDiskURI , "https://" ) != 0 {
2019-04-01 00:13:36 -04:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "diskURI" ) , azure . DataDiskURI , diskURISupportedblob ) )
2017-05-16 12:50:58 -04:00
}
2016-07-27 14:07:34 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateVsphereVolumeSource ( cd * core . VsphereVirtualDiskVolumeSource , fldPath * field . Path ) field . ErrorList {
2016-05-23 17:39:09 -04:00
allErrs := field . ErrorList { }
if len ( cd . VolumePath ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumePath" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validatePhotonPersistentDiskVolumeSource ( cd * core . PhotonPersistentDiskVolumeSource , fldPath * field . Path ) field . ErrorList {
2016-11-03 01:31:47 -04:00
allErrs := field . ErrorList { }
if len ( cd . PdID ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "pdID" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validatePortworxVolumeSource ( pwx * core . PortworxVolumeSource , fldPath * field . Path ) field . ErrorList {
2016-12-19 18:17:11 -05:00
allErrs := field . ErrorList { }
if len ( pwx . VolumeID ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeID" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateScaleIOVolumeSource ( sio * core . ScaleIOVolumeSource , fldPath * field . Path ) field . ErrorList {
2016-11-19 15:46:23 -05:00
allErrs := field . ErrorList { }
if sio . Gateway == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "gateway" ) , "" ) )
}
if sio . System == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "system" ) , "" ) )
}
if sio . VolumeName == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeName" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateScaleIOPersistentVolumeSource ( sio * core . ScaleIOPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-10-13 22:57:37 -04:00
allErrs := field . ErrorList { }
if sio . Gateway == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "gateway" ) , "" ) )
}
if sio . System == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "system" ) , "" ) )
}
if sio . VolumeName == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeName" ) , "" ) )
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateLocalVolumeSource ( ls * core . LocalVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-04-18 01:24:24 -04:00
allErrs := field . ErrorList { }
if ls . Path == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2017-06-17 00:57:01 -04:00
return allErrs
2017-04-18 01:24:24 -04:00
}
2017-06-17 00:57:01 -04:00
allErrs = append ( allErrs , validatePathNoBacksteps ( ls . Path , fldPath . Child ( "path" ) ) ... )
2017-04-18 01:24:24 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateStorageOSVolumeSource ( storageos * core . StorageOSVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-02-24 10:47:40 -05:00
allErrs := field . ErrorList { }
if len ( storageos . VolumeName ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeName" ) , "" ) )
} else {
allErrs = append ( allErrs , ValidateDNS1123Label ( storageos . VolumeName , fldPath . Child ( "volumeName" ) ) ... )
}
if len ( storageos . VolumeNamespace ) > 0 {
allErrs = append ( allErrs , ValidateDNS1123Label ( storageos . VolumeNamespace , fldPath . Child ( "volumeNamespace" ) ) ... )
}
if storageos . SecretRef != nil {
if len ( storageos . SecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" , "name" ) , "" ) )
}
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateStorageOSPersistentVolumeSource ( storageos * core . StorageOSPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
2017-02-24 10:47:40 -05:00
allErrs := field . ErrorList { }
if len ( storageos . VolumeName ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeName" ) , "" ) )
} else {
allErrs = append ( allErrs , ValidateDNS1123Label ( storageos . VolumeName , fldPath . Child ( "volumeName" ) ) ... )
}
if len ( storageos . VolumeNamespace ) > 0 {
allErrs = append ( allErrs , ValidateDNS1123Label ( storageos . VolumeNamespace , fldPath . Child ( "volumeNamespace" ) ) ... )
}
if storageos . SecretRef != nil {
if len ( storageos . SecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" , "name" ) , "" ) )
}
if len ( storageos . SecretRef . Namespace ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretRef" , "namespace" ) , "" ) )
}
}
return allErrs
}
2018-11-14 10:46:59 -05:00
func ValidateCSIDriverName ( driverName string , fldPath * field . Path ) field . ErrorList {
2017-10-24 22:44:48 -04:00
allErrs := field . ErrorList { }
2018-11-14 10:46:59 -05:00
if len ( driverName ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath , "" ) )
2017-10-24 22:44:48 -04:00
}
2018-11-14 10:46:59 -05:00
if len ( driverName ) > 63 {
allErrs = append ( allErrs , field . TooLong ( fldPath , driverName , 63 ) )
2017-10-24 22:44:48 -04:00
}
2019-02-08 11:06:07 -05:00
for _ , msg := range validation . IsDNS1123Subdomain ( strings . ToLower ( driverName ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , driverName , msg ) )
2018-02-01 02:55:02 -05:00
}
2019-02-08 11:06:07 -05:00
2018-11-14 10:46:59 -05:00
return allErrs
}
2018-02-01 02:55:02 -05:00
2018-11-14 10:46:59 -05:00
func validateCSIPersistentVolumeSource ( csi * core . CSIPersistentVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidateCSIDriverName ( csi . Driver , fldPath . Child ( "driver" ) ) ... )
2017-10-24 22:44:48 -04:00
if len ( csi . VolumeHandle ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeHandle" ) , "" ) )
}
2018-02-23 16:50:43 -05:00
if csi . ControllerPublishSecretRef != nil {
if len ( csi . ControllerPublishSecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "controllerPublishSecretRef" , "name" ) , "" ) )
} else {
allErrs = append ( allErrs , ValidateDNS1123Label ( csi . ControllerPublishSecretRef . Name , fldPath . Child ( "name" ) ) ... )
}
if len ( csi . ControllerPublishSecretRef . Namespace ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "controllerPublishSecretRef" , "namespace" ) , "" ) )
} else {
allErrs = append ( allErrs , ValidateDNS1123Label ( csi . ControllerPublishSecretRef . Namespace , fldPath . Child ( "namespace" ) ) ... )
}
}
if csi . NodePublishSecretRef != nil {
if len ( csi . NodePublishSecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "nodePublishSecretRef " , "name" ) , "" ) )
} else {
allErrs = append ( allErrs , ValidateDNS1123Label ( csi . NodePublishSecretRef . Name , fldPath . Child ( "name" ) ) ... )
}
if len ( csi . NodePublishSecretRef . Namespace ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "nodePublishSecretRef " , "namespace" ) , "" ) )
} else {
allErrs = append ( allErrs , ValidateDNS1123Label ( csi . NodePublishSecretRef . Namespace , fldPath . Child ( "namespace" ) ) ... )
}
}
2018-08-14 17:00:25 -04:00
return allErrs
}
func validateCSIVolumeSource ( csi * core . CSIVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidateCSIDriverName ( csi . Driver , fldPath . Child ( "driver" ) ) ... )
if csi . NodePublishSecretRef != nil {
if len ( csi . NodePublishSecretRef . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "nodePublishSecretRef " , "name" ) , "" ) )
2018-02-23 16:50:43 -05:00
} else {
2018-08-14 17:00:25 -04:00
for _ , msg := range ValidateSecretName ( csi . NodePublishSecretRef . Name , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , csi . NodePublishSecretRef . Name , msg ) )
}
2018-02-23 16:50:43 -05:00
}
}
2017-10-24 22:44:48 -04:00
return allErrs
}
2015-12-16 01:03:20 -05:00
// ValidatePersistentVolumeName checks that a name is appropriate for a
// PersistentVolumeName object.
2018-08-11 10:17:29 -04:00
var ValidatePersistentVolumeName = apimachineryvalidation . NameIsDNSSubdomain
2015-03-23 14:18:11 -04:00
2017-10-09 11:58:37 -04:00
var supportedAccessModes = sets . NewString ( string ( core . ReadWriteOnce ) , string ( core . ReadOnlyMany ) , string ( core . ReadWriteMany ) )
2015-11-04 16:52:14 -05:00
2017-10-09 11:58:37 -04:00
var supportedReclaimPolicy = sets . NewString ( string ( core . PersistentVolumeReclaimDelete ) , string ( core . PersistentVolumeReclaimRecycle ) , string ( core . PersistentVolumeReclaimRetain ) )
2016-08-09 15:32:53 -04:00
2017-08-09 13:51:46 -04:00
var supportedVolumeModes = sets . NewString ( string ( core . PersistentVolumeBlock ) , string ( core . PersistentVolumeFilesystem ) )
2018-09-07 14:07:47 -04:00
var supportedDataSourceAPIGroupKinds = map [ schema . GroupKind ] bool {
{ Group : "snapshot.storage.k8s.io" , Kind : "VolumeSnapshot" } : true ,
}
2018-08-24 15:46:32 -04:00
2017-10-09 11:58:37 -04:00
func ValidatePersistentVolume ( pv * core . PersistentVolume ) field . ErrorList {
2017-04-18 01:24:24 -04:00
metaPath := field . NewPath ( "metadata" )
allErrs := ValidateObjectMeta ( & pv . ObjectMeta , false , ValidatePersistentVolumeName , metaPath )
2015-03-23 14:18:11 -04:00
2015-11-06 18:30:52 -05:00
specPath := field . NewPath ( "spec" )
2015-05-12 20:44:29 -04:00
if len ( pv . Spec . AccessModes ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "accessModes" ) , "" ) )
2015-05-12 20:44:29 -04:00
}
2015-07-24 15:55:54 -04:00
for _ , mode := range pv . Spec . AccessModes {
2015-11-04 16:52:14 -05:00
if ! supportedAccessModes . Has ( string ( mode ) ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "accessModes" ) , mode , supportedAccessModes . List ( ) ) )
2015-07-24 15:55:54 -04:00
}
}
2015-03-23 14:18:11 -04:00
if len ( pv . Spec . Capacity ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "capacity" ) , "" ) )
2015-03-23 14:18:11 -04:00
}
2017-10-09 11:58:37 -04:00
if _ , ok := pv . Spec . Capacity [ core . ResourceStorage ] ; ! ok || len ( pv . Spec . Capacity ) > 1 {
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "capacity" ) , pv . Spec . Capacity , [ ] string { string ( core . ResourceStorage ) } ) )
2015-03-23 14:18:11 -04:00
}
2015-11-04 16:52:14 -05:00
capPath := specPath . Child ( "capacity" )
for r , qty := range pv . Spec . Capacity {
allErrs = append ( allErrs , validateBasicResource ( qty , capPath . Key ( string ( r ) ) ) ... )
2017-11-11 18:39:37 -05:00
allErrs = append ( allErrs , ValidatePositiveQuantityValue ( qty , capPath . Key ( string ( r ) ) ) ... )
2015-05-12 20:44:29 -04:00
}
2016-08-09 15:32:53 -04:00
if len ( string ( pv . Spec . PersistentVolumeReclaimPolicy ) ) > 0 {
if ! supportedReclaimPolicy . Has ( string ( pv . Spec . PersistentVolumeReclaimPolicy ) ) {
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "persistentVolumeReclaimPolicy" ) , pv . Spec . PersistentVolumeReclaimPolicy , supportedReclaimPolicy . List ( ) ) )
}
}
2015-05-12 20:44:29 -04:00
2018-03-28 02:45:06 -04:00
nodeAffinitySpecified , errs := validateVolumeNodeAffinity ( pv . Spec . NodeAffinity , specPath . Child ( "nodeAffinity" ) )
2017-04-18 01:24:24 -04:00
allErrs = append ( allErrs , errs ... )
2015-03-23 14:18:11 -04:00
numVolumes := 0
if pv . Spec . HostPath != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "hostPath" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateHostPathVolumeSource ( pv . Spec . HostPath , specPath . Child ( "hostPath" ) ) ... )
}
2015-03-23 14:18:11 -04:00
}
if pv . Spec . GCEPersistentDisk != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "gcePersistentDisk" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateGCEPersistentDiskVolumeSource ( pv . Spec . GCEPersistentDisk , specPath . Child ( "persistentDisk" ) ) ... )
}
2015-03-23 14:18:11 -04:00
}
2015-04-07 17:16:36 -04:00
if pv . Spec . AWSElasticBlockStore != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "awsElasticBlockStore" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateAWSElasticBlockStoreVolumeSource ( pv . Spec . AWSElasticBlockStore , specPath . Child ( "awsElasticBlockStore" ) ) ... )
}
2015-03-06 09:26:39 -05:00
}
2015-04-18 09:31:24 -04:00
if pv . Spec . Glusterfs != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "glusterfs" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2018-11-06 05:21:01 -05:00
allErrs = append ( allErrs , validateGlusterfsPersistentVolumeSource ( pv . Spec . Glusterfs , specPath . Child ( "glusterfs" ) ) ... )
2015-11-15 01:36:25 -05:00
}
2015-04-18 09:31:24 -04:00
}
2015-09-25 15:22:23 -04:00
if pv . Spec . Flocker != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "flocker" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateFlockerVolumeSource ( pv . Spec . Flocker , specPath . Child ( "flocker" ) ) ... )
}
2015-09-25 15:22:23 -04:00
}
2015-04-30 12:16:51 -04:00
if pv . Spec . NFS != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "nfs" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateNFSVolumeSource ( pv . Spec . NFS , specPath . Child ( "nfs" ) ) ... )
}
2015-04-30 12:16:51 -04:00
}
2015-04-07 13:22:23 -04:00
if pv . Spec . RBD != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "rbd" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2017-10-23 16:59:34 -04:00
allErrs = append ( allErrs , validateRBDPersistentVolumeSource ( pv . Spec . RBD , specPath . Child ( "rbd" ) ) ... )
2015-11-15 01:36:25 -05:00
}
2015-04-07 13:22:23 -04:00
}
2016-04-20 04:38:19 -04:00
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" ) ) ... )
}
}
2015-04-09 14:05:24 -04:00
if pv . Spec . CephFS != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "cephFS" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2017-07-24 10:53:49 -04:00
allErrs = append ( allErrs , validateCephFSPersistentVolumeSource ( pv . Spec . CephFS , specPath . Child ( "cephfs" ) ) ... )
2015-11-15 01:36:25 -05:00
}
2015-04-09 14:05:24 -04:00
}
2015-05-12 16:40:31 -04:00
if pv . Spec . ISCSI != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "iscsi" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2017-08-28 14:22:01 -04:00
allErrs = append ( allErrs , validateISCSIPersistentVolumeSource ( pv . Spec . ISCSI , specPath . Child ( "iscsi" ) ) ... )
2015-11-15 01:36:25 -05:00
}
2017-07-11 23:37:48 -04:00
if pv . Spec . ISCSI . InitiatorName != nil && len ( pv . ObjectMeta . Name + ":" + pv . Spec . ISCSI . TargetPortal ) > 64 {
tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
allErrs = append ( allErrs , field . Invalid ( metaPath . Child ( "name" ) , pv . ObjectMeta . Name , tooLongErr ) )
}
2015-05-12 16:40:31 -04:00
}
2015-04-10 12:54:01 -04:00
if pv . Spec . Cinder != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "cinder" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2018-05-14 17:09:05 -04:00
allErrs = append ( allErrs , validateCinderPersistentVolumeSource ( pv . Spec . Cinder , specPath . Child ( "cinder" ) ) ... )
2015-11-15 01:36:25 -05:00
}
2015-04-10 12:54:01 -04:00
}
2015-08-11 11:19:29 -04:00
if pv . Spec . FC != nil {
2015-11-15 01:36:25 -05:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "fc" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateFCVolumeSource ( pv . Spec . FC , specPath . Child ( "fc" ) ) ... )
}
2015-08-11 11:19:29 -04:00
}
2015-09-30 14:31:53 -04:00
if pv . Spec . FlexVolume != nil {
numVolumes ++
2017-11-27 23:19:38 -05:00
allErrs = append ( allErrs , validateFlexPersistentVolumeSource ( pv . Spec . FlexVolume , specPath . Child ( "flexVolume" ) ) ... )
2015-09-30 14:31:53 -04:00
}
2015-11-13 11:47:04 -05:00
if pv . Spec . AzureFile != nil {
2017-05-16 12:50:58 -04:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "azureFile" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2017-06-16 11:23:22 -04:00
allErrs = append ( allErrs , validateAzureFilePV ( pv . Spec . AzureFile , specPath . Child ( "azureFile" ) ) ... )
2017-05-16 12:50:58 -04:00
}
2015-11-13 11:47:04 -05:00
}
2017-05-16 12:50:58 -04:00
2016-05-23 17:39:09 -04:00
if pv . Spec . VsphereVolume != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "vsphereVolume" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateVsphereVolumeSource ( pv . Spec . VsphereVolume , specPath . Child ( "vsphereVolume" ) ) ... )
}
}
2016-11-03 01:31:47 -04:00
if pv . Spec . PhotonPersistentDisk != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "photonPersistentDisk" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validatePhotonPersistentDiskVolumeSource ( pv . Spec . PhotonPersistentDisk , specPath . Child ( "photonPersistentDisk" ) ) ... )
}
}
2016-12-19 18:17:11 -05:00
if pv . Spec . PortworxVolume != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "portworxVolume" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validatePortworxVolumeSource ( pv . Spec . PortworxVolume , specPath . Child ( "portworxVolume" ) ) ... )
}
}
2016-07-27 14:07:34 -04:00
if pv . Spec . AzureDisk != nil {
2017-05-16 12:50:58 -04:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "azureDisk" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateAzureDisk ( pv . Spec . AzureDisk , specPath . Child ( "azureDisk" ) ) ... )
}
2016-07-27 14:07:34 -04:00
}
2016-11-19 15:46:23 -05:00
if pv . Spec . ScaleIO != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "scaleIO" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
2017-10-13 22:57:37 -04:00
allErrs = append ( allErrs , validateScaleIOPersistentVolumeSource ( pv . Spec . ScaleIO , specPath . Child ( "scaleIO" ) ) ... )
2016-11-19 15:46:23 -05:00
}
}
2017-04-18 01:24:24 -04:00
if pv . Spec . Local != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "local" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateLocalVolumeSource ( pv . Spec . Local , specPath . Child ( "local" ) ) ... )
// NodeAffinity is required
if ! nodeAffinitySpecified {
allErrs = append ( allErrs , field . Required ( metaPath . Child ( "annotations" ) , "Local volume requires node affinity" ) )
}
}
}
2017-02-24 10:47:40 -05:00
if pv . Spec . StorageOS != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "storageos" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateStorageOSPersistentVolumeSource ( pv . Spec . StorageOS , specPath . Child ( "storageos" ) ) ... )
}
}
2016-07-27 14:07:34 -04:00
2017-10-24 22:44:48 -04:00
if pv . Spec . CSI != nil {
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "csi" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateCSIPersistentVolumeSource ( pv . Spec . CSI , specPath . Child ( "csi" ) ) ... )
}
}
2015-11-14 15:26:04 -05:00
if numVolumes == 0 {
allErrs = append ( allErrs , field . Required ( specPath , "must specify a volume type" ) )
2015-03-23 14:18:11 -04:00
}
2016-08-22 14:45:46 -04:00
// do not allow hostPath mounts of '/' to have a 'recycle' reclaim policy
2017-10-09 11:58:37 -04:00
if pv . Spec . HostPath != nil && path . Clean ( pv . Spec . HostPath . Path ) == "/" && pv . Spec . PersistentVolumeReclaimPolicy == core . PersistentVolumeReclaimRecycle {
2016-08-22 14:45:46 -04:00
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "persistentVolumeReclaimPolicy" ) , "may not be 'recycle' for a hostPath mount of '/'" ) )
}
2017-03-02 04:23:57 -05:00
if len ( pv . Spec . StorageClassName ) > 0 {
for _ , msg := range ValidateClassName ( pv . Spec . StorageClassName , false ) {
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "storageClassName" ) , pv . Spec . StorageClassName , msg ) )
}
}
2018-12-26 14:28:53 -05:00
if pv . Spec . VolumeMode != nil && ! supportedVolumeModes . Has ( string ( * pv . Spec . VolumeMode ) ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "volumeMode" ) , * pv . Spec . VolumeMode , supportedVolumeModes . List ( ) ) )
}
2015-03-23 14:18:11 -04:00
return allErrs
}
2015-03-26 15:50:36 -04:00
// ValidatePersistentVolumeUpdate tests to see if the update is legal for an end user to make.
// newPv is updated with fields that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidatePersistentVolumeUpdate ( newPv , oldPv * core . PersistentVolume ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-03-26 15:50:36 -04:00
allErrs = ValidatePersistentVolume ( newPv )
2017-10-29 10:09:00 -04:00
// PersistentVolumeSource should be immutable after creation.
if ! apiequality . Semantic . DeepEqual ( newPv . Spec . PersistentVolumeSource , oldPv . Spec . PersistentVolumeSource ) {
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "spec" , "persistentvolumesource" ) , "is immutable after creation" ) )
}
2015-03-26 15:50:36 -04:00
newPv . Status = oldPv . Status
2017-08-09 13:51:46 -04:00
2018-12-26 14:28:53 -05:00
allErrs = append ( allErrs , ValidateImmutableField ( newPv . Spec . VolumeMode , oldPv . Spec . VolumeMode , field . NewPath ( "volumeMode" ) ) ... )
2017-08-09 13:51:46 -04:00
2018-12-27 17:45:04 -05:00
// Allow setting NodeAffinity if oldPv NodeAffinity was not set
if oldPv . Spec . NodeAffinity != nil {
allErrs = append ( allErrs , ValidateImmutableField ( newPv . Spec . NodeAffinity , oldPv . Spec . NodeAffinity , field . NewPath ( "nodeAffinity" ) ) ... )
2018-01-30 18:41:57 -05:00
}
2015-03-26 15:50:36 -04:00
return allErrs
}
// ValidatePersistentVolumeStatusUpdate tests to see if the status update is legal for an end user to make.
// newPv is updated with fields that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidatePersistentVolumeStatusUpdate ( newPv , oldPv * core . PersistentVolume ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newPv . ObjectMeta , & oldPv . ObjectMeta , field . NewPath ( "metadata" ) )
2015-12-08 23:39:18 -05:00
if len ( newPv . ResourceVersion ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "resourceVersion" ) , "" ) )
2015-03-26 15:50:36 -04:00
}
newPv . Spec = oldPv . Spec
return allErrs
}
2016-08-10 14:18:37 -04:00
// ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
2017-10-09 11:58:37 -04:00
func ValidatePersistentVolumeClaim ( pvc * core . PersistentVolumeClaim ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & pvc . ObjectMeta , true , ValidatePersistentVolumeName , field . NewPath ( "metadata" ) )
2016-08-10 14:18:37 -04:00
allErrs = append ( allErrs , ValidatePersistentVolumeClaimSpec ( & pvc . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
// ValidatePersistentVolumeClaimSpec validates a PersistentVolumeClaimSpec
2017-10-09 11:58:37 -04:00
func ValidatePersistentVolumeClaimSpec ( spec * core . PersistentVolumeClaimSpec , fldPath * field . Path ) field . ErrorList {
2016-08-10 14:18:37 -04:00
allErrs := field . ErrorList { }
if len ( spec . AccessModes ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "accessModes" ) , "at least 1 access mode is required" ) )
2015-03-23 14:18:11 -04:00
}
2016-08-10 14:18:37 -04:00
if spec . Selector != nil {
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelSelector ( spec . Selector , fldPath . Child ( "selector" ) ) ... )
2016-05-06 23:18:54 -04:00
}
2016-08-10 14:18:37 -04:00
for _ , mode := range spec . AccessModes {
2017-10-09 11:58:37 -04:00
if mode != core . ReadWriteOnce && mode != core . ReadOnlyMany && mode != core . ReadWriteMany {
2016-08-10 14:18:37 -04:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "accessModes" ) , mode , supportedAccessModes . List ( ) ) )
2015-07-24 15:55:54 -04:00
}
}
2017-10-09 11:58:37 -04:00
storageValue , ok := spec . Resources . Requests [ core . ResourceStorage ]
2016-08-10 14:18:37 -04:00
if ! ok {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "resources" ) . Key ( string ( core . ResourceStorage ) ) , "" ) )
2016-08-10 14:18:37 -04:00
} else {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( core . ResourceStorage ) , storageValue , fldPath . Child ( "resources" ) . Key ( string ( core . ResourceStorage ) ) ) ... )
2017-11-11 18:39:37 -05:00
allErrs = append ( allErrs , ValidatePositiveQuantityValue ( storageValue , fldPath . Child ( "resources" ) . Key ( string ( core . ResourceStorage ) ) ) ... )
2015-03-23 14:18:11 -04:00
}
2017-03-02 04:23:57 -05:00
if spec . StorageClassName != nil && len ( * spec . StorageClassName ) > 0 {
for _ , msg := range ValidateClassName ( * spec . StorageClassName , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "storageClassName" ) , * spec . StorageClassName , msg ) )
}
}
2018-12-26 14:28:53 -05:00
if spec . VolumeMode != nil && ! supportedVolumeModes . Has ( string ( * spec . VolumeMode ) ) {
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "volumeMode" ) , * spec . VolumeMode , supportedVolumeModes . List ( ) ) )
}
2018-08-23 23:17:22 -04:00
2019-01-07 17:32:05 -05:00
if spec . DataSource != nil {
2018-08-23 23:17:22 -04:00
if len ( spec . DataSource . Name ) == 0 {
2018-08-24 15:46:32 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "dataSource" , "name" ) , "" ) )
2018-09-07 14:07:47 -04:00
}
groupKind := schema . GroupKind { Group : "" , Kind : spec . DataSource . Kind }
if spec . DataSource . APIGroup != nil {
groupKind . Group = string ( * spec . DataSource . APIGroup )
}
groupKindList := make ( [ ] string , 0 , len ( supportedDataSourceAPIGroupKinds ) )
for grp := range supportedDataSourceAPIGroupKinds {
groupKindList = append ( groupKindList , grp . String ( ) )
}
if ! supportedDataSourceAPIGroupKinds [ groupKind ] {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "dataSource" ) , groupKind . String ( ) , groupKindList ) )
2018-08-23 23:17:22 -04:00
}
}
2015-03-23 14:18:11 -04:00
return allErrs
}
2017-05-21 12:22:05 -04:00
// ValidatePersistentVolumeClaimUpdate validates an update to a PersistentVolumeClaim
2017-10-09 11:58:37 -04:00
func ValidatePersistentVolumeClaimUpdate ( newPvc , oldPvc * core . PersistentVolumeClaim ) field . ErrorList {
2016-02-09 15:28:37 -05:00
allErrs := ValidateObjectMetaUpdate ( & newPvc . ObjectMeta , & oldPvc . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidatePersistentVolumeClaim ( newPvc ) ... )
2017-12-14 16:54:32 -05:00
newPvcClone := newPvc . DeepCopy ( )
oldPvcClone := oldPvc . DeepCopy ( )
2016-07-08 12:08:15 -04:00
// PVController needs to update PVC.Spec w/ VolumeName.
// Claims are immutable in order to enforce quota, range limits, etc. without gaming the system.
if len ( oldPvc . Spec . VolumeName ) == 0 {
// volumeName changes are allowed once.
2017-12-14 16:54:32 -05:00
oldPvcClone . Spec . VolumeName = newPvcClone . Spec . VolumeName
2016-07-08 12:08:15 -04:00
}
2017-09-04 03:02:34 -04:00
2018-01-14 05:01:19 -05:00
if validateStorageClassUpgrade ( oldPvcClone . Annotations , newPvcClone . Annotations ,
oldPvcClone . Spec . StorageClassName , newPvcClone . Spec . StorageClassName ) {
newPvcClone . Spec . StorageClassName = nil
metav1 . SetMetaDataAnnotation ( & newPvcClone . ObjectMeta , core . BetaStorageClassAnnotation , oldPvcClone . Annotations [ core . BetaStorageClassAnnotation ] )
} else {
// storageclass annotation should be immutable after creation
// 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" ) ) ... )
}
2017-09-04 03:02:34 -04:00
if utilfeature . DefaultFeatureGate . Enabled ( features . ExpandPersistentVolumes ) {
// lets make sure storage values are same.
2017-12-14 16:54:32 -05:00
if newPvc . Status . Phase == core . ClaimBound && newPvcClone . Spec . Resources . Requests != nil {
newPvcClone . Spec . Resources . Requests [ "storage" ] = oldPvc . Spec . Resources . Requests [ "storage" ]
2017-09-04 03:02:34 -04:00
}
oldSize := oldPvc . Spec . Resources . Requests [ "storage" ]
newSize := newPvc . Spec . Resources . Requests [ "storage" ]
2017-12-14 16:54:32 -05:00
if ! apiequality . Semantic . DeepEqual ( newPvcClone . Spec , oldPvcClone . Spec ) {
2017-09-04 03:02:34 -04:00
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "spec" ) , "is immutable after creation except resources.requests for bound claims" ) )
}
if newSize . Cmp ( oldSize ) < 0 {
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "spec" , "resources" , "requests" , "storage" ) , "field can not be less than previous value" ) )
}
} else {
// changes to Spec are not allowed, but updates to label/and some annotations are OK.
// no-op updates pass validation.
2017-12-14 16:54:32 -05:00
if ! apiequality . Semantic . DeepEqual ( newPvcClone . Spec , oldPvcClone . Spec ) {
2017-09-04 03:02:34 -04:00
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "spec" ) , "field is immutable after creation" ) )
}
2016-02-09 15:28:37 -05:00
}
2016-11-04 14:06:34 -04:00
2018-12-26 14:28:53 -05:00
allErrs = append ( allErrs , ValidateImmutableField ( newPvc . Spec . VolumeMode , oldPvc . Spec . VolumeMode , field . NewPath ( "volumeMode" ) ) ... )
2015-03-26 15:50:36 -04:00
return allErrs
}
2018-01-14 05:01:19 -05:00
// Provide an upgrade path from PVC with storage class specified in beta
// annotation to storage class specified in attribute. We allow update of
// StorageClassName only if following four conditions are met at the same time:
// 1. The old pvc's StorageClassAnnotation is set
// 2. The old pvc's StorageClassName is not set
// 3. The new pvc's StorageClassName is set and equal to the old value in annotation
// 4. If the new pvc's StorageClassAnnotation is set,it must be equal to the old pv/pvc's StorageClassAnnotation
func validateStorageClassUpgrade ( oldAnnotations , newAnnotations map [ string ] string , oldScName , newScName * string ) bool {
oldSc , oldAnnotationExist := oldAnnotations [ core . BetaStorageClassAnnotation ]
newScInAnnotation , newAnnotationExist := newAnnotations [ core . BetaStorageClassAnnotation ]
return oldAnnotationExist /* condition 1 */ &&
oldScName == nil /* condition 2*/ &&
( newScName != nil && * newScName == oldSc ) /* condition 3 */ &&
( ! newAnnotationExist || newScInAnnotation == oldSc ) /* condition 4 */
}
2017-05-21 12:22:05 -04:00
// ValidatePersistentVolumeClaimStatusUpdate validates an update to status of a PersistentVolumeClaim
2017-10-09 11:58:37 -04:00
func ValidatePersistentVolumeClaimStatusUpdate ( newPvc , oldPvc * core . PersistentVolumeClaim ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newPvc . ObjectMeta , & oldPvc . ObjectMeta , field . NewPath ( "metadata" ) )
2015-12-08 23:39:18 -05:00
if len ( newPvc . ResourceVersion ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "resourceVersion" ) , "" ) )
2015-03-26 15:50:36 -04:00
}
2015-05-12 20:44:29 -04:00
if len ( newPvc . Spec . AccessModes ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "Spec" , "accessModes" ) , "" ) )
2015-05-12 20:44:29 -04:00
}
2015-11-06 18:30:52 -05:00
capPath := field . NewPath ( "status" , "capacity" )
2015-11-04 16:52:14 -05:00
for r , qty := range newPvc . Status . Capacity {
allErrs = append ( allErrs , validateBasicResource ( qty , capPath . Key ( string ( r ) ) ) ... )
2015-05-12 20:44:29 -04:00
}
2015-03-26 15:50:36 -04:00
newPvc . Spec = oldPvc . Spec
return allErrs
}
2018-08-25 16:26:25 -04:00
var supportedPortProtocols = sets . NewString ( string ( core . ProtocolTCP ) , string ( core . ProtocolUDP ) , string ( core . ProtocolSCTP ) )
2014-11-23 23:03:11 -05:00
2017-10-09 11:58:37 -04:00
func validateContainerPorts ( ports [ ] core . ContainerPort , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-07-08 02:20:30 -04:00
2015-09-09 13:45:01 -04:00
allNames := sets . String { }
2015-01-26 12:52:50 -05:00
for i , port := range ports {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
2014-07-08 00:32:56 -04:00
if len ( port . Name ) > 0 {
2016-01-04 11:33:26 -05:00
if msgs := validation . IsValidPortName ( port . Name ) ; len ( msgs ) != 0 {
for i = range msgs {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "name" ) , port . Name , msgs [ i ] ) )
}
2014-07-08 02:20:30 -04:00
} else if allNames . Has ( port . Name ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "name" ) , port . Name ) )
2014-07-08 02:20:30 -04:00
} else {
allNames . Insert ( port . Name )
2014-07-08 00:32:56 -04:00
}
}
2014-08-19 22:57:48 -04:00
if port . ContainerPort == 0 {
2016-01-04 11:33:26 -05:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "containerPort" ) , "" ) )
} else {
for _ , msg := range validation . IsValidPortNum ( int ( port . ContainerPort ) ) {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "containerPort" ) , port . ContainerPort , msg ) )
}
2014-07-08 00:32:56 -04:00
}
2016-01-04 11:33:26 -05:00
if port . HostPort != 0 {
for _ , msg := range validation . IsValidPortNum ( int ( port . HostPort ) ) {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "hostPort" ) , port . HostPort , msg ) )
}
2014-07-08 00:32:56 -04:00
}
if len ( port . Protocol ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "protocol" ) , "" ) )
2015-06-16 20:28:11 -04:00
} else if ! supportedPortProtocols . Has ( string ( port . Protocol ) ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . NotSupported ( idxPath . Child ( "protocol" ) , port . Protocol , supportedPortProtocols . List ( ) ) )
2014-07-08 00:32:56 -04:00
}
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-08 00:32:56 -04:00
}
2017-06-03 11:43:46 -04:00
// ValidateEnv validates env vars
2017-10-09 11:58:37 -04:00
func ValidateEnv ( vars [ ] core . EnvVar , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-07-08 02:20:30 -04:00
2015-01-26 12:52:50 -05:00
for i , ev := range vars {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
2014-07-01 18:56:30 -04:00
if len ( ev . Name ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "name" ) , "" ) )
2015-12-20 01:52:48 -05:00
} else {
2017-06-17 18:34:22 -04:00
for _ , msg := range validation . IsEnvVarName ( ev . Name ) {
2015-12-20 01:52:48 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "name" ) , ev . Name , msg ) )
}
2014-07-01 18:56:30 -04:00
}
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateEnvVarValueFrom ( ev , idxPath . Child ( "valueFrom" ) ) ... )
2014-07-01 18:56:30 -04:00
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-01 18:56:30 -04:00
}
2017-02-17 11:37:15 -05:00
var validEnvDownwardAPIFieldPathExpressions = sets . NewString (
"metadata.name" ,
"metadata.namespace" ,
"metadata.uid" ,
"spec.nodeName" ,
"spec.serviceAccountName" ,
"status.hostIP" ,
"status.podIP" )
2017-08-10 03:26:07 -04:00
var validContainerResourceFieldPathExpressions = sets . NewString ( "limits.cpu" , "limits.memory" , "limits.ephemeral-storage" , "requests.cpu" , "requests.memory" , "requests.ephemeral-storage" )
2015-02-20 00:36:23 -05:00
2017-10-09 11:58:37 -04:00
func validateEnvVarValueFrom ( ev core . EnvVar , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-04-23 16:57:30 -04:00
if ev . ValueFrom == nil {
return allErrs
}
numSources := 0
2016-01-18 15:07:48 -05:00
if ev . ValueFrom . FieldRef != nil {
2015-04-23 16:57:30 -04:00
numSources ++
2017-02-17 11:37:15 -05:00
allErrs = append ( allErrs , validateObjectFieldSelector ( ev . ValueFrom . FieldRef , & validEnvDownwardAPIFieldPathExpressions , fldPath . Child ( "fieldRef" ) ) ... )
2016-01-18 15:07:48 -05:00
}
2016-05-23 18:08:22 -04:00
if ev . ValueFrom . ResourceFieldRef != nil {
numSources ++
allErrs = append ( allErrs , validateContainerResourceFieldSelector ( ev . ValueFrom . ResourceFieldRef , & validContainerResourceFieldPathExpressions , fldPath . Child ( "resourceFieldRef" ) , false ) ... )
}
2016-01-18 15:07:48 -05:00
if ev . ValueFrom . ConfigMapKeyRef != nil {
2015-12-17 15:51:51 -05:00
numSources ++
allErrs = append ( allErrs , validateConfigMapKeySelector ( ev . ValueFrom . ConfigMapKeyRef , fldPath . Child ( "configMapKeyRef" ) ) ... )
2015-04-23 16:57:30 -04:00
}
2016-01-18 15:07:48 -05:00
if ev . ValueFrom . SecretKeyRef != nil {
numSources ++
allErrs = append ( allErrs , validateSecretKeySelector ( ev . ValueFrom . SecretKeyRef , fldPath . Child ( "secretKeyRef" ) ) ... )
}
2015-04-23 16:57:30 -04:00
2016-11-08 11:48:12 -05:00
if numSources == 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath , "" , "must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`" ) )
} else if len ( ev . Value ) != 0 {
2016-01-18 15:07:48 -05:00
if numSources != 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath , "" , "may not be specified when `value` is not empty" ) )
}
2016-11-08 11:48:12 -05:00
} else if numSources > 1 {
2016-01-18 15:07:48 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , "" , "may not have more than one field specified at a time" ) )
2015-04-23 16:57:30 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateObjectFieldSelector ( fs * core . ObjectFieldSelector , expressions * sets . String , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-04-23 16:57:30 -04:00
2015-12-08 23:39:18 -05:00
if len ( fs . APIVersion ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "apiVersion" ) , "" ) )
2017-02-17 11:37:15 -05:00
return allErrs
}
if len ( fs . FieldPath ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fieldPath" ) , "" ) )
2017-02-17 11:37:15 -05:00
return allErrs
}
2017-11-22 14:02:20 -05:00
internalFieldPath , _ , err := podshelper . ConvertDownwardAPIFieldLabel ( fs . APIVersion , fs . FieldPath , "" )
2017-02-17 11:37:15 -05:00
if err != nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "fieldPath" ) , fs . FieldPath , fmt . Sprintf ( "error converting fieldPath: %v" , err ) ) )
return allErrs
}
2017-11-22 15:38:09 -05:00
if path , subscript , ok := fieldpath . SplitMaybeSubscriptedPath ( internalFieldPath ) ; ok {
switch path {
case "metadata.annotations" :
for _ , msg := range validation . IsQualifiedName ( strings . ToLower ( subscript ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , subscript , msg ) )
}
case "metadata.labels" :
for _ , msg := range validation . IsQualifiedName ( subscript ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , subscript , msg ) )
}
default :
allErrs = append ( allErrs , field . Invalid ( fldPath , path , "does not support subscript" ) )
2015-04-23 16:57:30 -04:00
}
2017-11-22 15:38:09 -05:00
} else if ! expressions . Has ( path ) {
2017-02-17 11:37:15 -05:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "fieldPath" ) , path , expressions . List ( ) ) )
return allErrs
2015-04-23 16:57:30 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateContainerResourceFieldSelector ( fs * core . ResourceFieldSelector , expressions * sets . String , fldPath * field . Path , volume bool ) field . ErrorList {
2016-05-23 18:08:22 -04:00
allErrs := field . ErrorList { }
if volume && len ( fs . ContainerName ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "containerName" ) , "" ) )
} else if len ( fs . Resource ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "resource" ) , "" ) )
} else if ! expressions . Has ( fs . Resource ) {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "resource" ) , fs . Resource , expressions . List ( ) ) )
}
allErrs = append ( allErrs , validateContainerResourceDivisor ( fs . Resource , fs . Divisor , fldPath ) ... )
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateEnvFrom ( vars [ ] core . EnvFromSource , fldPath * field . Path ) field . ErrorList {
2016-11-29 21:57:35 -05:00
allErrs := field . ErrorList { }
for i , ev := range vars {
idxPath := fldPath . Index ( i )
if len ( ev . Prefix ) > 0 {
2017-06-17 18:34:22 -04:00
for _ , msg := range validation . IsEnvVarName ( ev . Prefix ) {
2016-11-29 21:57:35 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "prefix" ) , ev . Prefix , msg ) )
}
}
2017-01-04 15:50:11 -05:00
numSources := 0
2016-11-29 21:57:35 -05:00
if ev . ConfigMapRef != nil {
2017-01-04 15:50:11 -05:00
numSources ++
2016-11-29 21:57:35 -05:00
allErrs = append ( allErrs , validateConfigMapEnvSource ( ev . ConfigMapRef , idxPath . Child ( "configMapRef" ) ) ... )
}
2017-01-04 15:50:11 -05:00
if ev . SecretRef != nil {
numSources ++
allErrs = append ( allErrs , validateSecretEnvSource ( ev . SecretRef , idxPath . Child ( "secretRef" ) ) ... )
}
if numSources == 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath , "" , "must specify one of: `configMapRef` or `secretRef`" ) )
} else if numSources > 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath , "" , "may not have more than one field specified at a time" ) )
}
2016-11-29 21:57:35 -05:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateConfigMapEnvSource ( configMapSource * core . ConfigMapEnvSource , fldPath * field . Path ) field . ErrorList {
2016-11-29 21:57:35 -05:00
allErrs := field . ErrorList { }
if len ( configMapSource . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
2017-02-24 13:07:20 -05:00
} else {
for _ , msg := range ValidateConfigMapName ( configMapSource . Name , true ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , configMapSource . Name , msg ) )
}
2016-11-29 21:57:35 -05:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateSecretEnvSource ( secretSource * core . SecretEnvSource , fldPath * field . Path ) field . ErrorList {
2017-01-04 15:50:11 -05:00
allErrs := field . ErrorList { }
if len ( secretSource . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
2017-02-24 13:07:20 -05:00
} else {
for _ , msg := range ValidateSecretName ( secretSource . Name , true ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , secretSource . Name , msg ) )
}
2017-01-04 15:50:11 -05:00
}
return allErrs
}
2016-05-23 18:08:22 -04:00
var validContainerResourceDivisorForCPU = sets . NewString ( "1m" , "1" )
2016-06-15 15:32:59 -04:00
var validContainerResourceDivisorForMemory = sets . NewString ( "1" , "1k" , "1M" , "1G" , "1T" , "1P" , "1E" , "1Ki" , "1Mi" , "1Gi" , "1Ti" , "1Pi" , "1Ei" )
2017-08-10 03:26:07 -04:00
var validContainerResourceDivisorForEphemeralStorage = sets . NewString ( "1" , "1k" , "1M" , "1G" , "1T" , "1P" , "1E" , "1Ki" , "1Mi" , "1Gi" , "1Ti" , "1Pi" , "1Ei" )
2016-05-23 18:08:22 -04:00
func validateContainerResourceDivisor ( rName string , divisor resource . Quantity , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
unsetDivisor := resource . Quantity { }
if unsetDivisor . Cmp ( divisor ) == 0 {
return allErrs
}
switch rName {
case "limits.cpu" , "requests.cpu" :
if ! validContainerResourceDivisorForCPU . Has ( divisor . String ( ) ) {
2016-07-10 16:09:16 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "divisor" ) , rName , "only divisor's values 1m and 1 are supported with the cpu resource" ) )
2016-05-23 18:08:22 -04:00
}
case "limits.memory" , "requests.memory" :
if ! validContainerResourceDivisorForMemory . Has ( divisor . String ( ) ) {
2016-07-10 16:09:16 -04:00
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" ) )
2016-05-23 18:08:22 -04:00
}
2017-08-10 03:26:07 -04:00
case "limits.ephemeral-storage" , "requests.ephemeral-storage" :
if ! validContainerResourceDivisorForEphemeralStorage . Has ( divisor . String ( ) ) {
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 local ephemeral storage resource" ) )
}
2016-05-23 18:08:22 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateConfigMapKeySelector ( s * core . ConfigMapKeySelector , fldPath * field . Path ) field . ErrorList {
2016-01-18 15:07:48 -05:00
allErrs := field . ErrorList { }
2017-06-03 11:43:46 -04:00
nameFn := ValidateNameFunc ( ValidateSecretName )
for _ , msg := range nameFn ( s . Name , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , s . Name , msg ) )
2016-01-18 15:07:48 -05:00
}
if len ( s . Key ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "key" ) , "" ) )
2016-04-28 23:34:48 -04:00
} else {
for _ , msg := range validation . IsConfigMapKey ( s . Key ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "key" ) , s . Key , msg ) )
}
2016-01-18 15:07:48 -05:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateSecretKeySelector ( s * core . SecretKeySelector , fldPath * field . Path ) field . ErrorList {
2015-12-17 15:51:51 -05:00
allErrs := field . ErrorList { }
2017-06-03 11:43:46 -04:00
nameFn := ValidateNameFunc ( ValidateSecretName )
for _ , msg := range nameFn ( s . Name , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , s . Name , msg ) )
2015-12-17 15:51:51 -05:00
}
if len ( s . Key ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "key" ) , "" ) )
2016-04-28 23:34:48 -04:00
} else {
for _ , msg := range validation . IsConfigMapKey ( s . Key ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "key" ) , s . Key , msg ) )
}
2015-12-17 15:51:51 -05:00
}
return allErrs
}
2017-08-09 13:51:46 -04:00
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 {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2016-03-17 23:52:34 -04:00
mountpoints := sets . NewString ( )
2014-07-08 02:20:30 -04:00
2015-01-26 12:52:50 -05:00
for i , mnt := range mounts {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
2014-07-04 22:46:56 -04:00
if len ( mnt . Name ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "name" ) , "" ) )
2017-08-09 13:51:46 -04:00
}
if ! IsMatchedVolume ( mnt . Name , volumes ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . NotFound ( idxPath . Child ( "name" ) , mnt . Name ) )
2014-07-04 22:46:56 -04:00
}
if len ( mnt . MountPath ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "mountPath" ) , "" ) )
2014-07-14 21:39:30 -04:00
}
2016-03-17 23:52:34 -04:00
if mountpoints . Has ( mnt . MountPath ) {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "mountPath" ) , mnt . MountPath , "must be unique" ) )
}
mountpoints . Insert ( mnt . MountPath )
2017-08-09 13:51:46 -04:00
// 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" ) )
}
2016-03-04 20:40:15 -05:00
if len ( mnt . SubPath ) > 0 {
2018-12-18 10:11:29 -05:00
allErrs = append ( allErrs , validateLocalDescendingPath ( mnt . SubPath , fldPath . Child ( "subPath" ) ) ... )
2016-03-04 20:40:15 -05:00
}
2017-09-01 06:05:55 -04:00
2018-11-20 08:05:19 -05:00
if len ( mnt . SubPathExpr ) > 0 {
if len ( mnt . SubPath ) > 0 {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "subPathExpr" ) , mnt . SubPathExpr , "subPathExpr and subPath are mutually exclusive" ) )
}
allErrs = append ( allErrs , validateLocalDescendingPath ( mnt . SubPathExpr , fldPath . Child ( "subPathExpr" ) ) ... )
}
2017-09-01 06:05:55 -04:00
if mnt . MountPropagation != nil {
allErrs = append ( allErrs , validateMountPropagation ( mnt . MountPropagation , container , fldPath . Child ( "mountPropagation" ) ) ... )
}
2014-07-04 22:46:56 -04:00
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-04 22:46:56 -04:00
}
2017-08-09 13:51:46 -04:00
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 {
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
}
2017-10-09 11:58:37 -04:00
func validateProbe ( probe * core . Probe , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-02-15 02:02:07 -05:00
if probe == nil {
return allErrs
}
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateHandler ( & probe . Handler , fldPath ) ... )
2015-11-18 13:15:16 -05:00
2015-10-08 16:02:23 -04:00
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( probe . InitialDelaySeconds ) , fldPath . Child ( "initialDelaySeconds" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( probe . TimeoutSeconds ) , fldPath . Child ( "timeoutSeconds" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( probe . PeriodSeconds ) , fldPath . Child ( "periodSeconds" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( probe . SuccessThreshold ) , fldPath . Child ( "successThreshold" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( probe . FailureThreshold ) , fldPath . Child ( "failureThreshold" ) ) ... )
2015-02-15 02:02:07 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateClientIPAffinityConfig ( config * core . SessionAffinityConfig , fldPath * field . Path ) field . ErrorList {
2017-08-01 12:09:37 -04:00
allErrs := field . ErrorList { }
if config == nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Required ( fldPath , fmt . Sprintf ( "when session affinity type is %s" , core . ServiceAffinityClientIP ) ) )
2017-08-01 12:09:37 -04:00
return allErrs
}
if config . ClientIP == nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "clientIP" ) , fmt . Sprintf ( "when session affinity type is %s" , core . ServiceAffinityClientIP ) ) )
2017-08-01 12:09:37 -04:00
return allErrs
}
if config . ClientIP . TimeoutSeconds == nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "clientIP" ) . Child ( "timeoutSeconds" ) , fmt . Sprintf ( "when session affinity type is %s" , core . ServiceAffinityClientIP ) ) )
2017-08-01 12:09:37 -04:00
return allErrs
}
allErrs = append ( allErrs , validateAffinityTimeout ( config . ClientIP . TimeoutSeconds , fldPath . Child ( "clientIP" ) . Child ( "timeoutSeconds" ) ) ... )
return allErrs
}
func validateAffinityTimeout ( timeout * int32 , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2017-10-09 11:58:37 -04:00
if * timeout <= 0 || * timeout > core . MaxClientIPServiceAffinitySeconds {
allErrs = append ( allErrs , field . Invalid ( fldPath , timeout , fmt . Sprintf ( "must be greater than 0 and less than %d" , core . MaxClientIPServiceAffinitySeconds ) ) )
2017-08-01 12:09:37 -04:00
}
return allErrs
}
2015-05-19 13:17:53 -04:00
// AccumulateUniqueHostPorts extracts each HostPort of each Container,
2014-07-08 00:32:56 -04:00
// accumulating the results and returning an error if any ports conflict.
2017-10-09 11:58:37 -04:00
func AccumulateUniqueHostPorts ( containers [ ] core . Container , accumulator * sets . String , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-07-08 02:20:30 -04:00
2015-01-26 12:52:50 -05:00
for ci , ctr := range containers {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( ci )
portsPath := idxPath . Child ( "ports" )
2014-07-08 00:32:56 -04:00
for pi := range ctr . Ports {
2015-11-04 16:52:14 -05:00
idxPath := portsPath . Index ( pi )
2015-05-19 01:18:40 -04:00
port := ctr . Ports [ pi ] . HostPort
2014-08-19 18:18:49 -04:00
if port == 0 {
continue
}
2017-01-03 07:53:11 -05:00
str := fmt . Sprintf ( "%s/%s/%d" , ctr . Ports [ pi ] . Protocol , ctr . Ports [ pi ] . HostIP , port )
2015-05-19 13:17:53 -04:00
if accumulator . Has ( str ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "hostPort" ) , str ) )
2014-07-08 02:20:30 -04:00
} else {
2015-05-19 13:17:53 -04:00
accumulator . Insert ( str )
2014-07-08 00:32:56 -04:00
}
}
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-08 00:32:56 -04:00
}
2014-09-02 06:00:28 -04:00
// checkHostPortConflicts checks for colliding Port.HostPort values across
// a slice of containers.
2017-10-09 11:58:37 -04:00
func checkHostPortConflicts ( containers [ ] core . Container , fldPath * field . Path ) field . ErrorList {
2015-09-09 13:45:01 -04:00
allPorts := sets . String { }
2015-11-04 16:52:14 -05:00
return AccumulateUniqueHostPorts ( containers , & allPorts , fldPath )
2014-07-08 00:32:56 -04:00
}
2017-10-09 11:58:37 -04:00
func validateExecAction ( exec * core . ExecAction , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2014-09-12 19:04:10 -04:00
if len ( exec . Command ) == 0 {
2015-11-14 15:26:04 -05:00
allErrors = append ( allErrors , field . Required ( fldPath . Child ( "command" ) , "" ) )
2014-09-12 19:04:10 -04:00
}
return allErrors
}
2017-10-09 11:58:37 -04:00
var supportedHTTPSchemes = sets . NewString ( string ( core . URISchemeHTTP ) , string ( core . URISchemeHTTPS ) )
2016-01-04 11:33:26 -05:00
2017-10-09 11:58:37 -04:00
func validateHTTPGetAction ( http * core . HTTPGetAction , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2014-09-12 19:04:10 -04:00
if len ( http . Path ) == 0 {
2015-11-14 15:26:04 -05:00
allErrors = append ( allErrors , field . Required ( fldPath . Child ( "path" ) , "" ) )
2014-09-12 19:04:10 -04:00
}
2016-01-04 11:33:26 -05:00
allErrors = append ( allErrors , ValidatePortNumOrName ( http . Port , fldPath . Child ( "port" ) ) ... )
if ! supportedHTTPSchemes . Has ( string ( http . Scheme ) ) {
allErrors = append ( allErrors , field . NotSupported ( fldPath . Child ( "scheme" ) , http . Scheme , supportedHTTPSchemes . List ( ) ) )
2015-06-25 13:53:41 -04:00
}
2016-02-02 10:03:50 -05:00
for _ , header := range http . HTTPHeaders {
2016-02-14 16:03:30 -05:00
for _ , msg := range validation . IsHTTPHeaderName ( header . Name ) {
allErrors = append ( allErrors , field . Invalid ( fldPath . Child ( "httpHeaders" ) , header . Name , msg ) )
2016-02-02 10:03:50 -05:00
}
}
2015-02-27 20:33:58 -05:00
return allErrors
}
2016-01-04 11:33:26 -05:00
func ValidatePortNumOrName ( port intstr . IntOrString , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if port . Type == intstr . Int {
for _ , msg := range validation . IsValidPortNum ( port . IntValue ( ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , port . IntValue ( ) , msg ) )
}
} else if port . Type == intstr . String {
for _ , msg := range validation . IsValidPortName ( port . StrVal ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , port . StrVal , msg ) )
}
} else {
allErrs = append ( allErrs , field . InternalError ( fldPath , fmt . Errorf ( "unknown type: %v" , port . Type ) ) )
2015-02-27 20:33:58 -05:00
}
2016-01-04 11:33:26 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateTCPSocketAction ( tcp * core . TCPSocketAction , fldPath * field . Path ) field . ErrorList {
2016-01-04 11:33:26 -05:00
return ValidatePortNumOrName ( tcp . Port , fldPath . Child ( "port" ) )
2014-09-12 19:04:10 -04:00
}
2017-10-09 11:58:37 -04:00
func validateHandler ( handler * core . Handler , fldPath * field . Path ) field . ErrorList {
2014-11-20 17:24:10 -05:00
numHandlers := 0
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2014-09-12 19:04:10 -04:00
if handler . Exec != nil {
2015-11-15 01:36:25 -05:00
if numHandlers > 0 {
allErrors = append ( allErrors , field . Forbidden ( fldPath . Child ( "exec" ) , "may not specify more than 1 handler type" ) )
} else {
numHandlers ++
allErrors = append ( allErrors , validateExecAction ( handler . Exec , fldPath . Child ( "exec" ) ) ... )
}
2014-11-20 17:24:10 -05:00
}
if handler . HTTPGet != nil {
2015-11-15 01:36:25 -05:00
if numHandlers > 0 {
allErrors = append ( allErrors , field . Forbidden ( fldPath . Child ( "httpGet" ) , "may not specify more than 1 handler type" ) )
} else {
numHandlers ++
allErrors = append ( allErrors , validateHTTPGetAction ( handler . HTTPGet , fldPath . Child ( "httpGet" ) ) ... )
}
2014-11-20 17:24:10 -05:00
}
2015-02-27 20:33:58 -05:00
if handler . TCPSocket != nil {
2015-11-15 01:36:25 -05:00
if numHandlers > 0 {
allErrors = append ( allErrors , field . Forbidden ( fldPath . Child ( "tcpSocket" ) , "may not specify more than 1 handler type" ) )
} else {
numHandlers ++
allErrors = append ( allErrors , validateTCPSocketAction ( handler . TCPSocket , fldPath . Child ( "tcpSocket" ) ) ... )
}
2015-02-27 20:33:58 -05:00
}
2015-11-14 15:26:04 -05:00
if numHandlers == 0 {
allErrors = append ( allErrors , field . Required ( fldPath , "must specify a handler type" ) )
2014-09-12 19:04:10 -04:00
}
return allErrors
}
2017-10-09 11:58:37 -04:00
func validateLifecycle ( lifecycle * core . Lifecycle , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-09-12 19:04:10 -04:00
if lifecycle . PostStart != nil {
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateHandler ( lifecycle . PostStart , fldPath . Child ( "postStart" ) ) ... )
2014-09-12 19:04:10 -04:00
}
if lifecycle . PreStop != nil {
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateHandler ( lifecycle . PreStop , fldPath . Child ( "preStop" ) ) ... )
2014-09-12 19:04:10 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
var supportedPullPolicies = sets . NewString ( string ( core . PullAlways ) , string ( core . PullIfNotPresent ) , string ( core . PullNever ) )
2015-11-04 16:52:14 -05:00
2017-10-09 11:58:37 -04:00
func validatePullPolicy ( policy core . PullPolicy , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2015-01-16 18:02:36 -05:00
2015-11-04 16:52:14 -05:00
switch policy {
2017-10-09 11:58:37 -04:00
case core . PullAlways , core . PullIfNotPresent , core . PullNever :
2015-01-16 18:02:36 -05:00
break
2015-01-26 12:52:50 -05:00
case "" :
2015-11-14 15:26:04 -05:00
allErrors = append ( allErrors , field . Required ( fldPath , "" ) )
2015-01-16 18:02:36 -05:00
default :
2015-11-10 15:59:41 -05:00
allErrors = append ( allErrors , field . NotSupported ( fldPath , policy , supportedPullPolicies . List ( ) ) )
2015-01-16 18:02:36 -05:00
}
return allErrors
}
2017-08-09 13:51:46 -04:00
func validateInitContainers ( containers , otherContainers [ ] core . Container , deviceVolumes map [ string ] core . VolumeSource , fldPath * field . Path ) field . ErrorList {
2016-03-28 22:13:16 -04:00
var allErrs field . ErrorList
if len ( containers ) > 0 {
2017-12-20 09:32:10 -05:00
allErrs = append ( allErrs , validateContainers ( containers , true , deviceVolumes , fldPath ) ... )
2016-03-28 22:13:16 -04:00
}
allNames := sets . String { }
for _ , ctr := range otherContainers {
allNames . Insert ( ctr . Name )
}
for i , ctr := range containers {
idxPath := fldPath . Index ( i )
if allNames . Has ( ctr . Name ) {
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "name" ) , ctr . Name ) )
}
if len ( ctr . Name ) > 0 {
allNames . Insert ( ctr . Name )
}
if ctr . Lifecycle != nil {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "lifecycle" ) , ctr . Lifecycle , "must not be set for init containers" ) )
}
if ctr . LivenessProbe != nil {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "livenessProbe" ) , ctr . LivenessProbe , "must not be set for init containers" ) )
}
if ctr . ReadinessProbe != nil {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "readinessProbe" ) , ctr . ReadinessProbe , "must not be set for init containers" ) )
}
}
return allErrs
}
2017-12-20 09:32:10 -05:00
func validateContainers ( containers [ ] core . Container , isInitContainers bool , volumes map [ string ] core . VolumeSource , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-07-08 02:20:30 -04:00
2015-03-18 11:00:18 -04:00
if len ( containers ) == 0 {
2015-11-14 15:26:04 -05:00
return append ( allErrs , field . Required ( fldPath , "" ) )
2015-03-18 11:00:18 -04:00
}
2015-09-09 13:45:01 -04:00
allNames := sets . String { }
2015-01-26 12:52:50 -05:00
for i , ctr := range containers {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
2016-04-29 10:58:46 -04:00
namePath := idxPath . Child ( "name" )
2017-08-09 13:51:46 -04:00
volMounts := GetVolumeMountMap ( ctr . VolumeMounts )
volDevices := GetVolumeDeviceMap ( ctr . VolumeDevices )
2014-08-19 22:57:48 -04:00
if len ( ctr . Name ) == 0 {
2016-04-29 10:58:46 -04:00
allErrs = append ( allErrs , field . Required ( namePath , "" ) )
} else {
allErrs = append ( allErrs , ValidateDNS1123Label ( ctr . Name , namePath ) ... )
}
if allNames . Has ( ctr . Name ) {
allErrs = append ( allErrs , field . Duplicate ( namePath , ctr . Name ) )
2014-07-08 02:20:30 -04:00
} else {
allNames . Insert ( ctr . Name )
2014-07-01 18:14:25 -04:00
}
2017-06-14 19:52:31 -04:00
// TODO: do not validate leading and trailing whitespace to preserve backward compatibility.
// for example: https://github.com/openshift/origin/issues/14659 image = " " is special token in pod template
// others may have done similar
2014-07-01 18:14:25 -04:00
if len ( ctr . Image ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "image" ) , "" ) )
2014-07-04 22:46:56 -04:00
}
2014-09-12 19:04:10 -04:00
if ctr . Lifecycle != nil {
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateLifecycle ( ctr . Lifecycle , idxPath . Child ( "lifecycle" ) ) ... )
2014-09-12 19:04:10 -04:00
}
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateProbe ( ctr . LivenessProbe , idxPath . Child ( "livenessProbe" ) ) ... )
2015-11-05 18:38:46 -05:00
// Liveness-specific validation
if ctr . LivenessProbe != nil && ctr . LivenessProbe . SuccessThreshold != 1 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "livenessProbe" , "successThreshold" ) , ctr . LivenessProbe . SuccessThreshold , "must be 1" ) )
2015-11-05 18:38:46 -05:00
}
2016-12-21 23:54:12 -05:00
switch ctr . TerminationMessagePolicy {
2017-10-09 11:58:37 -04:00
case core . TerminationMessageReadFile , core . TerminationMessageFallbackToLogsOnError :
2016-12-21 23:54:12 -05:00
case "" :
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "terminationMessagePolicy" ) , "must be 'File' or 'FallbackToLogsOnError'" ) )
default :
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "terminationMessagePolicy" ) , ctr . TerminationMessagePolicy , "must be 'File' or 'FallbackToLogsOnError'" ) )
}
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateProbe ( ctr . ReadinessProbe , idxPath . Child ( "readinessProbe" ) ) ... )
allErrs = append ( allErrs , validateContainerPorts ( ctr . Ports , idxPath . Child ( "ports" ) ) ... )
2017-02-24 14:08:15 -05:00
allErrs = append ( allErrs , ValidateEnv ( ctr . Env , idxPath . Child ( "env" ) ) ... )
2017-06-03 11:43:46 -04:00
allErrs = append ( allErrs , ValidateEnvFrom ( ctr . EnvFrom , idxPath . Child ( "envFrom" ) ) ... )
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , ValidateVolumeMounts ( ctr . VolumeMounts , volDevices , volumes , & ctr , idxPath . Child ( "volumeMounts" ) ) ... )
allErrs = append ( allErrs , ValidateVolumeDevices ( ctr . VolumeDevices , volMounts , volumes , idxPath . Child ( "volumeDevices" ) ) ... )
2015-11-04 16:52:14 -05:00
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" ) ) ... )
2014-07-01 18:14:25 -04:00
}
2017-12-20 09:32:10 -05:00
if isInitContainers {
// check initContainers one by one since they are running in sequential order.
for _ , initContainer := range containers {
allErrs = append ( allErrs , checkHostPortConflicts ( [ ] core . Container { initContainer } , fldPath ) ... )
}
} else {
// Check for colliding ports across all containers.
allErrs = append ( allErrs , checkHostPortConflicts ( containers , fldPath ) ... )
}
2014-07-08 20:33:15 -04:00
2014-07-10 07:46:35 -04:00
return allErrs
2014-07-01 18:14:25 -04:00
}
2017-10-09 11:58:37 -04:00
func validateRestartPolicy ( restartPolicy * core . RestartPolicy , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2015-03-13 21:38:07 -04:00
switch * restartPolicy {
2017-10-09 11:58:37 -04:00
case core . RestartPolicyAlways , core . RestartPolicyOnFailure , core . RestartPolicyNever :
2015-03-13 21:38:07 -04:00
break
case "" :
2015-11-14 15:26:04 -05:00
allErrors = append ( allErrors , field . Required ( fldPath , "" ) )
2015-03-13 21:38:07 -04:00
default :
2017-10-09 11:58:37 -04:00
validValues := [ ] string { string ( core . RestartPolicyAlways ) , string ( core . RestartPolicyOnFailure ) , string ( core . RestartPolicyNever ) }
2015-11-10 15:59:41 -05:00
allErrors = append ( allErrors , field . NotSupported ( fldPath , * restartPolicy , validValues ) )
2014-08-26 14:25:17 -04:00
}
2015-03-13 21:38:07 -04:00
2014-08-26 14:25:17 -04:00
return allErrors
}
2014-07-22 14:45:12 -04:00
2017-10-09 11:58:37 -04:00
func validateDNSPolicy ( dnsPolicy * core . DNSPolicy , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2014-11-12 00:21:40 -05:00
switch * dnsPolicy {
2019-01-11 13:39:54 -05:00
case core . DNSClusterFirstWithHostNet , core . DNSClusterFirst , core . DNSDefault , core . DNSNone :
2015-01-26 12:52:50 -05:00
case "" :
2015-11-14 15:26:04 -05:00
allErrors = append ( allErrors , field . Required ( fldPath , "" ) )
2014-11-12 00:21:40 -05:00
default :
2019-01-11 13:39:54 -05:00
validValues := [ ] string { string ( core . DNSClusterFirstWithHostNet ) , string ( core . DNSClusterFirst ) , string ( core . DNSDefault ) , string ( core . DNSNone ) }
2015-11-10 15:59:41 -05:00
allErrors = append ( allErrors , field . NotSupported ( fldPath , dnsPolicy , validValues ) )
2014-11-12 00:21:40 -05:00
}
return allErrors
}
2017-11-16 00:57:36 -05:00
const (
// Limits on various DNS parameters. These are derived from
// restrictions in Linux libc name resolution handling.
// Max number of DNS name servers.
MaxDNSNameservers = 3
// Max number of domains in search path.
MaxDNSSearchPaths = 6
// Max number of characters in search path.
MaxDNSSearchListChars = 256
)
2018-05-18 18:47:08 -04:00
func validateReadinessGates ( readinessGates [ ] core . PodReadinessGate , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
for i , value := range readinessGates {
for _ , msg := range validation . IsQualifiedName ( string ( value . ConditionType ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Index ( i ) . Child ( "conditionType" ) , string ( value . ConditionType ) , msg ) )
}
}
return allErrs
}
2017-11-16 00:57:36 -05:00
func validatePodDNSConfig ( dnsConfig * core . PodDNSConfig , dnsPolicy * core . DNSPolicy , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
// Validate DNSNone case. Must provide at least one DNS name server.
2019-01-11 13:39:54 -05:00
if dnsPolicy != nil && * dnsPolicy == core . DNSNone {
2017-11-16 00:57:36 -05:00
if dnsConfig == nil {
return append ( allErrs , field . Required ( fldPath , fmt . Sprintf ( "must provide `dnsConfig` when `dnsPolicy` is %s" , core . DNSNone ) ) )
}
if len ( dnsConfig . Nameservers ) == 0 {
return append ( allErrs , field . Required ( fldPath . Child ( "nameservers" ) , fmt . Sprintf ( "must provide at least one DNS nameserver when `dnsPolicy` is %s" , core . DNSNone ) ) )
}
}
if dnsConfig != nil {
// Validate nameservers.
if len ( dnsConfig . Nameservers ) > MaxDNSNameservers {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "nameservers" ) , dnsConfig . Nameservers , fmt . Sprintf ( "must not have more than %v nameservers" , MaxDNSNameservers ) ) )
}
for i , ns := range dnsConfig . Nameservers {
if ip := net . ParseIP ( ns ) ; ip == nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "nameservers" ) . Index ( i ) , ns , "must be valid IP address" ) )
}
}
// Validate searches.
if len ( dnsConfig . Searches ) > MaxDNSSearchPaths {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "searches" ) , dnsConfig . Searches , fmt . Sprintf ( "must not have more than %v search paths" , MaxDNSSearchPaths ) ) )
}
// Include the space between search paths.
if len ( strings . Join ( dnsConfig . Searches , " " ) ) > MaxDNSSearchListChars {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "searches" ) , dnsConfig . Searches , "must not have more than 256 characters (including spaces) in the search list" ) )
}
for i , search := range dnsConfig . Searches {
2019-02-27 17:16:54 -05:00
// it is fine to have a trailing dot
if strings . HasSuffix ( search , "." ) {
search = search [ 0 : len ( search ) - 1 ]
}
2017-11-16 00:57:36 -05:00
allErrs = append ( allErrs , ValidateDNS1123Subdomain ( search , fldPath . Child ( "searches" ) . Index ( i ) ) ... )
}
// Validate options.
for i , option := range dnsConfig . Options {
if len ( option . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "options" ) . Index ( i ) , "must not be empty" ) )
}
}
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateHostNetwork ( hostNetwork bool , containers [ ] core . Container , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2015-03-23 19:34:35 -04:00
if hostNetwork {
2015-11-04 16:52:14 -05:00
for i , container := range containers {
portsPath := fldPath . Index ( i ) . Child ( "ports" )
for i , port := range container . Ports {
idxPath := portsPath . Index ( i )
2015-03-23 19:34:35 -04:00
if port . HostPort != port . ContainerPort {
2015-11-14 15:26:04 -05:00
allErrors = append ( allErrors , field . Invalid ( idxPath . Child ( "containerPort" ) , port . ContainerPort , "must match `hostPort` when `hostNetwork` is true" ) )
2015-03-23 19:34:35 -04:00
}
}
}
}
return allErrors
}
2015-11-04 16:52:14 -05:00
// validateImagePullSecrets checks to make sure the pull secrets are well
// formed. Right now, we only expect name to be set (it's the only field). If
// this ever changes and someone decides to set those fields, we'd like to
// know.
2017-10-09 11:58:37 -04:00
func validateImagePullSecrets ( imagePullSecrets [ ] core . LocalObjectReference , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrors := field . ErrorList { }
2015-05-08 13:53:00 -04:00
for i , currPullSecret := range imagePullSecrets {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
2017-10-09 11:58:37 -04:00
strippedRef := core . LocalObjectReference { Name : currPullSecret . Name }
2015-05-08 13:53:00 -04:00
if ! reflect . DeepEqual ( strippedRef , currPullSecret ) {
2015-11-10 15:59:41 -05:00
allErrors = append ( allErrors , field . Invalid ( idxPath , currPullSecret , "only name may be set" ) )
2015-05-08 13:53:00 -04:00
}
}
return allErrors
}
2016-11-30 11:51:12 -05:00
// validateAffinity checks if given affinities are valid
2017-10-09 11:58:37 -04:00
func validateAffinity ( affinity * core . Affinity , fldPath * field . Path ) field . ErrorList {
2016-11-30 11:51:12 -05:00
allErrs := field . ErrorList { }
if affinity != nil {
2017-07-31 09:33:52 -04:00
if affinity . NodeAffinity != nil {
allErrs = append ( allErrs , validateNodeAffinity ( affinity . NodeAffinity , fldPath . Child ( "nodeAffinity" ) ) ... )
2016-11-30 11:51:12 -05:00
}
2016-12-08 10:14:21 -05:00
if affinity . PodAffinity != nil {
allErrs = append ( allErrs , validatePodAffinity ( affinity . PodAffinity , fldPath . Child ( "podAffinity" ) ) ... )
}
if affinity . PodAntiAffinity != nil {
allErrs = append ( allErrs , validatePodAntiAffinity ( affinity . PodAntiAffinity , fldPath . Child ( "podAntiAffinity" ) ) ... )
}
2016-11-30 11:51:12 -05:00
}
2016-12-08 10:14:21 -05:00
2016-11-30 11:51:12 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateTaintEffect ( effect * core . TaintEffect , allowEmpty bool , fldPath * field . Path ) field . ErrorList {
2016-03-30 23:42:57 -04:00
if ! allowEmpty && len ( * effect ) == 0 {
return field . ErrorList { field . Required ( fldPath , "" ) }
}
allErrors := field . ErrorList { }
switch * effect {
2017-01-04 06:45:50 -05:00
// TODO: Replace next line with subsequent commented-out line when implement TaintEffectNoScheduleNoAdmit.
2017-10-09 11:58:37 -04:00
case core . TaintEffectNoSchedule , core . TaintEffectPreferNoSchedule , core . TaintEffectNoExecute :
// case core.TaintEffectNoSchedule, core.TaintEffectPreferNoSchedule, core.TaintEffectNoScheduleNoAdmit, core.TaintEffectNoExecute:
2016-03-30 23:42:57 -04:00
default :
validValues := [ ] string {
2017-10-09 11:58:37 -04:00
string ( core . TaintEffectNoSchedule ) ,
string ( core . TaintEffectPreferNoSchedule ) ,
string ( core . TaintEffectNoExecute ) ,
2017-01-04 06:45:50 -05:00
// TODO: Uncomment this block when implement TaintEffectNoScheduleNoAdmit.
2017-10-09 11:58:37 -04:00
// string(core.TaintEffectNoScheduleNoAdmit),
2016-03-30 23:42:57 -04:00
}
2018-05-25 06:03:22 -04:00
allErrors = append ( allErrors , field . NotSupported ( fldPath , * effect , validValues ) )
2016-03-30 23:42:57 -04:00
}
return allErrors
}
2017-02-21 13:03:13 -05:00
// validateOnlyAddedTolerations validates updated pod tolerations.
2017-10-09 11:58:37 -04:00
func validateOnlyAddedTolerations ( newTolerations [ ] core . Toleration , oldTolerations [ ] core . Toleration , fldPath * field . Path ) field . ErrorList {
2017-02-21 13:03:13 -05:00
allErrs := field . ErrorList { }
for _ , old := range oldTolerations {
found := false
old . TolerationSeconds = nil
for _ , new := range newTolerations {
new . TolerationSeconds = nil
if reflect . DeepEqual ( old , new ) {
found = true
break
}
}
if ! found {
allErrs = append ( allErrs , field . Forbidden ( fldPath , "existing toleration can not be modified except its tolerationSeconds" ) )
return allErrs
}
}
2017-02-27 13:34:46 -05:00
allErrs = append ( allErrs , ValidateTolerations ( newTolerations , fldPath ) ... )
2017-02-21 13:03:13 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateHostAliases ( hostAliases [ ] core . HostAlias , fldPath * field . Path ) field . ErrorList {
2017-04-26 16:22:09 -04:00
allErrs := field . ErrorList { }
for _ , hostAlias := range hostAliases {
if ip := net . ParseIP ( hostAlias . IP ) ; ip == nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "ip" ) , hostAlias . IP , "must be valid IP address" ) )
}
for _ , hostname := range hostAlias . Hostnames {
2017-06-01 19:46:00 -04:00
allErrs = append ( allErrs , ValidateDNS1123Subdomain ( hostname , fldPath . Child ( "hostnames" ) ) ... )
2017-04-26 16:22:09 -04:00
}
}
return allErrs
}
2017-02-27 13:34:46 -05:00
// ValidateTolerations tests if given tolerations have valid data.
2017-10-09 11:58:37 -04:00
func ValidateTolerations ( tolerations [ ] core . Toleration , fldPath * field . Path ) field . ErrorList {
2016-03-30 23:42:57 -04:00
allErrors := field . ErrorList { }
for i , toleration := range tolerations {
idxPath := fldPath . Index ( i )
// validate the toleration key
2017-01-04 06:45:50 -05:00
if len ( toleration . Key ) > 0 {
allErrors = append ( allErrors , unversionedvalidation . ValidateLabelName ( toleration . Key , idxPath . Child ( "key" ) ) ... )
}
// empty toleration key with Exists operator and empty value means match all taints
2017-10-09 11:58:37 -04:00
if len ( toleration . Key ) == 0 && toleration . Operator != core . TolerationOpExists {
2017-01-04 06:45:50 -05:00
allErrors = append ( allErrors , field . Invalid ( idxPath . Child ( "operator" ) , toleration . Operator ,
"operator must be Exists when `key` is empty, which means \"match all values and all keys\"" ) )
}
2017-10-09 11:58:37 -04:00
if toleration . TolerationSeconds != nil && toleration . Effect != core . TaintEffectNoExecute {
2017-01-04 06:45:50 -05:00
allErrors = append ( allErrors , field . Invalid ( idxPath . Child ( "effect" ) , toleration . Effect ,
"effect must be 'NoExecute' when `tolerationSeconds` is set" ) )
}
2016-03-30 23:42:57 -04:00
// validate toleration operator and value
switch toleration . Operator {
2017-01-04 06:45:50 -05:00
// empty operator means Equal
2017-10-09 11:58:37 -04:00
case core . TolerationOpEqual , "" :
2016-03-30 23:42:57 -04:00
if errs := validation . IsValidLabelValue ( toleration . Value ) ; len ( errs ) != 0 {
allErrors = append ( allErrors , field . Invalid ( idxPath . Child ( "operator" ) , toleration . Value , strings . Join ( errs , ";" ) ) )
}
2017-10-09 11:58:37 -04:00
case core . TolerationOpExists :
2016-03-30 23:42:57 -04:00
if len ( toleration . Value ) > 0 {
allErrors = append ( allErrors , field . Invalid ( idxPath . Child ( "operator" ) , toleration , "value must be empty when `operator` is 'Exists'" ) )
}
default :
2017-10-09 11:58:37 -04:00
validValues := [ ] string { string ( core . TolerationOpEqual ) , string ( core . TolerationOpExists ) }
2016-03-30 23:42:57 -04:00
allErrors = append ( allErrors , field . NotSupported ( idxPath . Child ( "operator" ) , toleration . Operator , validValues ) )
}
2017-01-04 06:45:50 -05:00
// validate toleration effect, empty toleration effect means match all taint effects
2016-03-30 23:42:57 -04:00
if len ( toleration . Effect ) > 0 {
allErrors = append ( allErrors , validateTaintEffect ( & toleration . Effect , true , idxPath . Child ( "effect" ) ) ... )
}
}
return allErrors
}
2017-10-09 11:58:37 -04:00
func toResourceNames ( resources core . ResourceList ) [ ] core . ResourceName {
result := [ ] core . ResourceName { }
2017-08-17 14:16:37 -04:00
for resourceName := range resources {
result = append ( result , resourceName )
}
return result
}
2017-10-09 11:58:37 -04:00
func toSet ( resourceNames [ ] core . ResourceName ) sets . String {
2017-08-17 14:16:37 -04:00
result := sets . NewString ( )
for _ , resourceName := range resourceNames {
result . Insert ( string ( resourceName ) )
}
return result
}
2017-10-09 11:58:37 -04:00
func toContainerResourcesSet ( ctr * core . Container ) sets . String {
2017-08-17 14:16:37 -04:00
resourceNames := toResourceNames ( ctr . Resources . Requests )
resourceNames = append ( resourceNames , toResourceNames ( ctr . Resources . Limits ) ... )
return toSet ( resourceNames )
}
2017-06-14 19:52:31 -04:00
// validateContainersOnlyForPod does additional validation for containers on a pod versus a pod template
// it only does additive validation of fields not covered in validateContainers
2017-10-09 11:58:37 -04:00
func validateContainersOnlyForPod ( containers [ ] core . Container , fldPath * field . Path ) field . ErrorList {
2017-06-14 19:52:31 -04:00
allErrs := field . ErrorList { }
for i , ctr := range containers {
idxPath := fldPath . Index ( i )
if len ( ctr . Image ) != len ( strings . TrimSpace ( ctr . Image ) ) {
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "image" ) , ctr . Image , "must not have leading or trailing whitespace" ) )
}
}
return allErrs
}
2014-09-02 06:00:28 -04:00
// ValidatePod tests if required fields in the pod are set.
2017-10-09 11:58:37 -04:00
func ValidatePod ( pod * core . Pod ) field . ErrorList {
2016-02-02 13:59:54 -05:00
fldPath := field . NewPath ( "metadata" )
allErrs := ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName , fldPath )
2016-08-16 19:41:05 -04:00
allErrs = append ( allErrs , ValidatePodSpecificAnnotations ( pod . ObjectMeta . Annotations , & pod . Spec , fldPath . Child ( "annotations" ) ) ... )
2015-11-06 18:30:52 -05:00
allErrs = append ( allErrs , ValidatePodSpec ( & pod . Spec , field . NewPath ( "spec" ) ) ... )
2017-06-14 19:52:31 -04:00
// we do additional validation only pertinent for pods and not pod templates
// this was done to preserve backwards compatibility
specPath := field . NewPath ( "spec" )
2018-07-26 22:52:36 -04:00
if pod . Spec . ServiceAccountName == "" {
for vi , volume := range pod . Spec . Volumes {
path := specPath . Child ( "volumes" ) . Index ( vi ) . Child ( "projected" )
if volume . Projected != nil {
for si , source := range volume . Projected . Sources {
saPath := path . Child ( "sources" ) . Index ( si ) . Child ( "serviceAccountToken" )
if source . ServiceAccountToken != nil {
allErrs = append ( allErrs , field . Forbidden ( saPath , "must not be specified when serviceAccountName is not set" ) )
}
}
}
}
}
2017-06-14 19:52:31 -04:00
allErrs = append ( allErrs , validateContainersOnlyForPod ( pod . Spec . Containers , specPath . Child ( "containers" ) ) ... )
allErrs = append ( allErrs , validateContainersOnlyForPod ( pod . Spec . InitContainers , specPath . Child ( "initContainers" ) ) ... )
2019-01-10 13:01:23 -05:00
hugePageResources := sets . NewString ( )
for i := range pod . Spec . Containers {
resourceSet := toContainerResourcesSet ( & pod . Spec . Containers [ i ] )
for resourceStr := range resourceSet {
if v1helper . IsHugePageResourceName ( v1 . ResourceName ( resourceStr ) ) {
hugePageResources . Insert ( resourceStr )
2017-08-17 14:16:37 -04:00
}
}
2019-01-10 13:01:23 -05:00
}
if len ( hugePageResources ) > 1 {
allErrs = append ( allErrs , field . Invalid ( specPath , hugePageResources , "must use a single hugepage size in a pod spec" ) )
2017-08-17 14:16:37 -04:00
}
2014-10-23 16:14:13 -04:00
return allErrs
}
2014-11-06 21:08:46 -05:00
// ValidatePodSpec tests that the specified PodSpec has valid data.
// This includes checking formatting and uniqueness. It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility
// tricks.
2017-10-09 11:58:37 -04:00
func ValidatePodSpec ( spec * core . PodSpec , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2014-11-06 21:08:46 -05:00
2017-08-09 13:51:46 -04:00
vols , vErrs := ValidateVolumes ( spec . Volumes , fldPath . Child ( "volumes" ) )
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , vErrs ... )
2017-12-20 09:32:10 -05:00
allErrs = append ( allErrs , validateContainers ( spec . Containers , false , vols , fldPath . Child ( "containers" ) ) ... )
2017-08-09 13:51:46 -04:00
allErrs = append ( allErrs , validateInitContainers ( spec . InitContainers , spec . Containers , vols , fldPath . Child ( "initContainers" ) ) ... )
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateRestartPolicy ( & spec . RestartPolicy , fldPath . Child ( "restartPolicy" ) ) ... )
allErrs = append ( allErrs , validateDNSPolicy ( & spec . DNSPolicy , fldPath . Child ( "dnsPolicy" ) ) ... )
2016-05-04 02:50:31 -04:00
allErrs = append ( allErrs , unversionedvalidation . ValidateLabels ( spec . NodeSelector , fldPath . Child ( "nodeSelector" ) ) ... )
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , ValidatePodSecurityContext ( spec . SecurityContext , spec , fldPath , fldPath . Child ( "securityContext" ) ) ... )
allErrs = append ( allErrs , validateImagePullSecrets ( spec . ImagePullSecrets , fldPath . Child ( "imagePullSecrets" ) ) ... )
2016-11-30 11:51:12 -05:00
allErrs = append ( allErrs , validateAffinity ( spec . Affinity , fldPath . Child ( "affinity" ) ) ... )
2017-11-16 00:57:36 -05:00
allErrs = append ( allErrs , validatePodDNSConfig ( spec . DNSConfig , & spec . DNSPolicy , fldPath . Child ( "dnsConfig" ) ) ... )
2018-05-18 18:47:08 -04:00
allErrs = append ( allErrs , validateReadinessGates ( spec . ReadinessGates , fldPath . Child ( "readinessGates" ) ) ... )
2015-06-18 22:35:42 -04:00
if len ( spec . ServiceAccountName ) > 0 {
2015-12-16 01:03:20 -05:00
for _ , msg := range ValidateServiceAccountName ( spec . ServiceAccountName , false ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "serviceAccountName" ) , spec . ServiceAccountName , msg ) )
2015-06-11 17:16:58 -04:00
}
}
2015-05-09 01:01:43 -04:00
2015-11-30 14:35:34 -05:00
if len ( spec . NodeName ) > 0 {
2015-12-16 01:03:20 -05:00
for _ , msg := range ValidateNodeName ( spec . NodeName , false ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "nodeName" ) , spec . NodeName , msg ) )
2015-11-30 14:35:34 -05:00
}
}
2015-05-09 01:01:43 -04:00
if spec . ActiveDeadlineSeconds != nil {
2017-05-31 12:08:52 -04:00
value := * spec . ActiveDeadlineSeconds
2017-05-31 16:10:22 -04:00
if value < 1 || value > math . MaxInt32 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "activeDeadlineSeconds" ) , value , validation . InclusiveRangeError ( 1 , math . MaxInt32 ) ) )
2015-05-09 01:01:43 -04:00
}
}
2016-04-14 13:45:29 -04:00
2015-12-16 02:49:58 -05:00
if len ( spec . Hostname ) > 0 {
2016-04-29 10:58:46 -04:00
allErrs = append ( allErrs , ValidateDNS1123Label ( spec . Hostname , fldPath . Child ( "hostname" ) ) ... )
2016-04-14 13:45:29 -04:00
}
2015-12-16 02:49:58 -05:00
if len ( spec . Subdomain ) > 0 {
2016-04-29 10:58:46 -04:00
allErrs = append ( allErrs , ValidateDNS1123Label ( spec . Subdomain , fldPath . Child ( "subdomain" ) ) ... )
2016-04-14 13:45:29 -04:00
}
2017-02-20 11:43:05 -05:00
if len ( spec . Tolerations ) > 0 {
2017-02-27 13:34:46 -05:00
allErrs = append ( allErrs , ValidateTolerations ( spec . Tolerations , fldPath . Child ( "tolerations" ) ) ... )
2017-02-20 11:43:05 -05:00
}
2017-04-26 16:22:09 -04:00
if len ( spec . HostAliases ) > 0 {
allErrs = append ( allErrs , ValidateHostAliases ( spec . HostAliases , fldPath . Child ( "hostAliases" ) ) ... )
}
2017-05-09 21:25:34 -04:00
if len ( spec . PriorityClassName ) > 0 {
2018-12-19 14:55:34 -05:00
for _ , msg := range ValidatePriorityClassName ( spec . PriorityClassName , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "priorityClassName" ) , spec . PriorityClassName , msg ) )
2017-05-09 21:25:34 -04:00
}
}
2018-12-19 15:01:28 -05:00
if spec . RuntimeClassName != nil {
2018-08-23 16:14:49 -04:00
allErrs = append ( allErrs , ValidateRuntimeClassName ( * spec . RuntimeClassName , fldPath . Child ( "runtimeClassName" ) ) ... )
2018-08-22 16:57:07 -04:00
}
2014-11-06 21:08:46 -05:00
return allErrs
2016-01-26 18:03:18 -05:00
}
// ValidateNodeSelectorRequirement tests that the specified NodeSelectorRequirement fields has valid data
2017-10-09 11:58:37 -04:00
func ValidateNodeSelectorRequirement ( rq core . NodeSelectorRequirement , fldPath * field . Path ) field . ErrorList {
2016-01-26 18:03:18 -05:00
allErrs := field . ErrorList { }
switch rq . Operator {
2017-10-09 11:58:37 -04:00
case core . NodeSelectorOpIn , core . NodeSelectorOpNotIn :
2016-01-26 18:03:18 -05:00
if len ( rq . Values ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "values" ) , "must be specified when `operator` is 'In' or 'NotIn'" ) )
}
2017-10-09 11:58:37 -04:00
case core . NodeSelectorOpExists , core . NodeSelectorOpDoesNotExist :
2016-01-26 18:03:18 -05:00
if len ( rq . Values ) > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "values" ) , "may not be specified when `operator` is 'Exists' or 'DoesNotExist'" ) )
}
2017-10-09 11:58:37 -04:00
case core . NodeSelectorOpGt , core . NodeSelectorOpLt :
2016-01-26 18:03:18 -05:00
if len ( rq . Values ) != 1 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "values" ) , "must be specified single value when `operator` is 'Lt' or 'Gt'" ) )
}
default :
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "operator" ) , rq . Operator , "not a valid selector operator" ) )
}
2018-04-21 00:21:18 -04:00
2016-05-04 02:50:31 -04:00
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelName ( rq . Key , fldPath . Child ( "key" ) ) ... )
2018-04-21 00:21:18 -04:00
return allErrs
}
var nodeFieldSelectorValidators = map [ string ] func ( string , bool ) [ ] string {
core . ObjectNameField : ValidateNodeName ,
}
// ValidateNodeFieldSelectorRequirement tests that the specified NodeSelectorRequirement fields has valid data
func ValidateNodeFieldSelectorRequirement ( req core . NodeSelectorRequirement , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
switch req . Operator {
case core . NodeSelectorOpIn , core . NodeSelectorOpNotIn :
if len ( req . Values ) != 1 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "values" ) ,
"must be only one value when `operator` is 'In' or 'NotIn' for node field selector" ) )
}
default :
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "operator" ) , req . Operator , "not a valid selector operator" ) )
}
if vf , found := nodeFieldSelectorValidators [ req . Key ] ; ! found {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "key" ) , req . Key , "not a valid field selector key" ) )
} else {
for i , v := range req . Values {
for _ , msg := range vf ( v , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "values" ) . Index ( i ) , v , msg ) )
}
}
}
2016-01-26 18:03:18 -05:00
return allErrs
}
// ValidateNodeSelectorTerm tests that the specified node selector term has valid data
2017-10-09 11:58:37 -04:00
func ValidateNodeSelectorTerm ( term core . NodeSelectorTerm , fldPath * field . Path ) field . ErrorList {
2016-01-26 18:03:18 -05:00
allErrs := field . ErrorList { }
for j , req := range term . MatchExpressions {
allErrs = append ( allErrs , ValidateNodeSelectorRequirement ( req , fldPath . Child ( "matchExpressions" ) . Index ( j ) ) ... )
}
2018-04-21 00:21:18 -04:00
for j , req := range term . MatchFields {
allErrs = append ( allErrs , ValidateNodeFieldSelectorRequirement ( req , fldPath . Child ( "matchFields" ) . Index ( j ) ) ... )
}
2016-01-26 18:03:18 -05:00
return allErrs
}
// ValidateNodeSelector tests that the specified nodeSelector fields has valid data
2017-10-09 11:58:37 -04:00
func ValidateNodeSelector ( nodeSelector * core . NodeSelector , fldPath * field . Path ) field . ErrorList {
2016-01-26 18:03:18 -05:00
allErrs := field . ErrorList { }
termFldPath := fldPath . Child ( "nodeSelectorTerms" )
if len ( nodeSelector . NodeSelectorTerms ) == 0 {
return append ( allErrs , field . Required ( termFldPath , "must have at least one node selector term" ) )
}
for i , term := range nodeSelector . NodeSelectorTerms {
allErrs = append ( allErrs , ValidateNodeSelectorTerm ( term , termFldPath . Index ( i ) ) ... )
}
return allErrs
}
2018-07-31 21:47:21 -04:00
// validateTopologySelectorLabelRequirement tests that the specified TopologySelectorLabelRequirement fields has valid data,
// and constructs a set containing all of its Values.
func validateTopologySelectorLabelRequirement ( rq core . TopologySelectorLabelRequirement , fldPath * field . Path ) ( sets . String , field . ErrorList ) {
2018-05-22 23:24:31 -04:00
allErrs := field . ErrorList { }
2018-07-31 21:47:21 -04:00
valueSet := make ( sets . String )
valuesPath := fldPath . Child ( "values" )
2018-05-22 23:24:31 -04:00
if len ( rq . Values ) == 0 {
2018-07-31 21:47:21 -04:00
allErrs = append ( allErrs , field . Required ( valuesPath , "" ) )
2018-05-22 23:24:31 -04:00
}
2018-07-31 21:47:21 -04:00
// Validate set property of Values field
for i , value := range rq . Values {
if valueSet . Has ( value ) {
allErrs = append ( allErrs , field . Duplicate ( valuesPath . Index ( i ) , value ) )
}
valueSet . Insert ( value )
}
2018-05-22 23:24:31 -04:00
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelName ( rq . Key , fldPath . Child ( "key" ) ) ... )
2018-07-31 21:47:21 -04:00
return valueSet , allErrs
2018-05-22 23:24:31 -04:00
}
2018-07-31 21:47:21 -04:00
// ValidateTopologySelectorTerm tests that the specified topology selector term has valid data,
// and constructs a map representing the term in raw form.
func ValidateTopologySelectorTerm ( term core . TopologySelectorTerm , fldPath * field . Path ) ( map [ string ] sets . String , field . ErrorList ) {
2018-05-22 23:24:31 -04:00
allErrs := field . ErrorList { }
2018-07-31 21:47:21 -04:00
exprMap := make ( map [ string ] sets . String )
exprPath := fldPath . Child ( "matchLabelExpressions" )
2018-05-22 23:24:31 -04:00
2018-12-27 17:45:04 -05:00
// Allow empty MatchLabelExpressions, in case this field becomes optional in the future.
for i , req := range term . MatchLabelExpressions {
idxPath := exprPath . Index ( i )
valueSet , exprErrs := validateTopologySelectorLabelRequirement ( req , idxPath )
allErrs = append ( allErrs , exprErrs ... )
2018-07-31 21:47:21 -04:00
2018-12-27 17:45:04 -05:00
// Validate no duplicate keys exist.
if _ , exists := exprMap [ req . Key ] ; exists {
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "key" ) , req . Key ) )
2018-05-22 23:24:31 -04:00
}
2018-12-27 17:45:04 -05:00
exprMap [ req . Key ] = valueSet
2018-05-22 23:24:31 -04:00
}
2018-07-31 21:47:21 -04:00
return exprMap , allErrs
2018-05-22 23:24:31 -04:00
}
2016-02-25 04:30:31 -05:00
// 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 { }
2017-04-13 16:19:08 -04:00
v1Avoids , err := v1helper . GetAvoidPodsFromNodeAnnotations ( annotations )
2016-02-25 04:30:31 -05:00
if err != nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "AvoidPods" ) , core . PreferAvoidPodsAnnotationKey , err . Error ( ) ) )
2016-02-25 04:30:31 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
var avoids core . AvoidPods
if err := corev1 . Convert_v1_AvoidPods_To_core_AvoidPods ( & v1Avoids , & avoids , nil ) ; err != nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "AvoidPods" ) , core . PreferAvoidPodsAnnotationKey , err . Error ( ) ) )
2017-01-12 23:18:34 -05:00
return allErrs
}
2016-02-25 04:30:31 -05:00
if len ( avoids . PreferAvoidPods ) != 0 {
for i , pa := range avoids . PreferAvoidPods {
2017-10-09 11:58:37 -04:00
idxPath := fldPath . Child ( core . PreferAvoidPodsAnnotationKey ) . Index ( i )
2016-02-25 04:30:31 -05:00
allErrs = append ( allErrs , validatePreferAvoidPodsEntry ( pa , idxPath ) ... )
}
}
return allErrs
}
// validatePreferAvoidPodsEntry tests if given PreferAvoidPodsEntry has valid data.
2017-10-09 11:58:37 -04:00
func validatePreferAvoidPodsEntry ( avoidPodEntry core . PreferAvoidPodsEntry , fldPath * field . Path ) field . ErrorList {
2016-02-25 04:30:31 -05:00
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
}
2016-01-26 18:03:18 -05:00
// ValidatePreferredSchedulingTerms tests that the specified SoftNodeAffinity fields has valid data
2017-10-09 11:58:37 -04:00
func ValidatePreferredSchedulingTerms ( terms [ ] core . PreferredSchedulingTerm , fldPath * field . Path ) field . ErrorList {
2016-01-26 18:03:18 -05:00
allErrs := field . ErrorList { }
for i , term := range terms {
if term . Weight <= 0 || term . Weight > 100 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Index ( i ) . Child ( "weight" ) , term . Weight , "must be in the range 1-100" ) )
}
allErrs = append ( allErrs , ValidateNodeSelectorTerm ( term . Preference , fldPath . Index ( i ) . Child ( "preference" ) ) ... )
}
return allErrs
}
2016-05-04 02:50:31 -04:00
// validatePodAffinityTerm tests that the specified podAffinityTerm fields have valid data
2017-10-09 11:58:37 -04:00
func validatePodAffinityTerm ( podAffinityTerm core . PodAffinityTerm , fldPath * field . Path ) field . ErrorList {
2016-05-04 02:50:31 -04:00
allErrs := field . ErrorList { }
2017-06-21 10:00:23 -04:00
2016-05-04 02:50:31 -04:00
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelSelector ( podAffinityTerm . LabelSelector , fldPath . Child ( "matchExpressions" ) ) ... )
for _ , name := range podAffinityTerm . Namespaces {
2015-12-16 01:03:20 -05:00
for _ , msg := range ValidateNamespaceName ( name , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "namespace" ) , name , msg ) )
2016-05-04 02:50:31 -04:00
}
}
2017-08-01 15:03:51 -04:00
if len ( podAffinityTerm . TopologyKey ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "topologyKey" ) , "can not be empty" ) )
2016-05-04 02:50:31 -04:00
}
2017-08-01 15:03:51 -04:00
return append ( allErrs , unversionedvalidation . ValidateLabelName ( podAffinityTerm . TopologyKey , fldPath . Child ( "topologyKey" ) ) ... )
2016-05-04 02:50:31 -04:00
}
// validatePodAffinityTerms tests that the specified podAffinityTerms fields have valid data
2017-10-09 11:58:37 -04:00
func validatePodAffinityTerms ( podAffinityTerms [ ] core . PodAffinityTerm , fldPath * field . Path ) field . ErrorList {
2016-05-04 02:50:31 -04:00
allErrs := field . ErrorList { }
for i , podAffinityTerm := range podAffinityTerms {
2017-08-01 15:03:51 -04:00
allErrs = append ( allErrs , validatePodAffinityTerm ( podAffinityTerm , fldPath . Index ( i ) ) ... )
2016-05-04 02:50:31 -04:00
}
return allErrs
}
// validateWeightedPodAffinityTerms tests that the specified weightedPodAffinityTerms fields have valid data
2017-10-09 11:58:37 -04:00
func validateWeightedPodAffinityTerms ( weightedPodAffinityTerms [ ] core . WeightedPodAffinityTerm , fldPath * field . Path ) field . ErrorList {
2016-05-04 02:50:31 -04:00
allErrs := field . ErrorList { }
for j , weightedTerm := range weightedPodAffinityTerms {
if weightedTerm . Weight <= 0 || weightedTerm . Weight > 100 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Index ( j ) . Child ( "weight" ) , weightedTerm . Weight , "must be in the range 1-100" ) )
}
2017-08-01 15:03:51 -04:00
allErrs = append ( allErrs , validatePodAffinityTerm ( weightedTerm . PodAffinityTerm , fldPath . Index ( j ) . Child ( "podAffinityTerm" ) ) ... )
2016-05-04 02:50:31 -04:00
}
return allErrs
}
// validatePodAntiAffinity tests that the specified podAntiAffinity fields have valid data
2017-10-09 11:58:37 -04:00
func validatePodAntiAffinity ( podAntiAffinity * core . PodAntiAffinity , fldPath * field . Path ) field . ErrorList {
2016-05-04 02:50:31 -04:00
allErrs := field . ErrorList { }
// TODO:Uncomment below code once RequiredDuringSchedulingRequiredDuringExecution is implemented.
// if podAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution != nil {
// allErrs = append(allErrs, validatePodAffinityTerms(podAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution, false,
// fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
//}
if podAntiAffinity . RequiredDuringSchedulingIgnoredDuringExecution != nil {
2017-08-01 15:03:51 -04:00
allErrs = append ( allErrs , validatePodAffinityTerms ( podAntiAffinity . RequiredDuringSchedulingIgnoredDuringExecution ,
2016-05-04 02:50:31 -04:00
fldPath . Child ( "requiredDuringSchedulingIgnoredDuringExecution" ) ) ... )
}
if podAntiAffinity . PreferredDuringSchedulingIgnoredDuringExecution != nil {
2017-08-01 15:03:51 -04:00
allErrs = append ( allErrs , validateWeightedPodAffinityTerms ( podAntiAffinity . PreferredDuringSchedulingIgnoredDuringExecution ,
2016-05-04 02:50:31 -04:00
fldPath . Child ( "preferredDuringSchedulingIgnoredDuringExecution" ) ) ... )
}
return allErrs
}
2017-07-31 09:33:52 -04:00
// validateNodeAffinity tests that the specified nodeAffinity fields have valid data
func validateNodeAffinity ( na * core . NodeAffinity , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
// TODO: Uncomment the next three lines once RequiredDuringSchedulingRequiredDuringExecution is implemented.
// if na.RequiredDuringSchedulingRequiredDuringExecution != nil {
// allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingRequiredDuringExecution, fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
// }
if na . RequiredDuringSchedulingIgnoredDuringExecution != nil {
allErrs = append ( allErrs , ValidateNodeSelector ( na . RequiredDuringSchedulingIgnoredDuringExecution , fldPath . Child ( "requiredDuringSchedulingIgnoredDuringExecution" ) ) ... )
}
if len ( na . PreferredDuringSchedulingIgnoredDuringExecution ) > 0 {
allErrs = append ( allErrs , ValidatePreferredSchedulingTerms ( na . PreferredDuringSchedulingIgnoredDuringExecution , fldPath . Child ( "preferredDuringSchedulingIgnoredDuringExecution" ) ) ... )
}
return allErrs
}
2016-05-04 02:50:31 -04:00
// validatePodAffinity tests that the specified podAffinity fields have valid data
2017-10-09 11:58:37 -04:00
func validatePodAffinity ( podAffinity * core . PodAffinity , fldPath * field . Path ) field . ErrorList {
2016-05-04 02:50:31 -04:00
allErrs := field . ErrorList { }
// TODO:Uncomment below code once RequiredDuringSchedulingRequiredDuringExecution is implemented.
// if podAffinity.RequiredDuringSchedulingRequiredDuringExecution != nil {
// allErrs = append(allErrs, validatePodAffinityTerms(podAffinity.RequiredDuringSchedulingRequiredDuringExecution, false,
// fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
//}
if podAffinity . RequiredDuringSchedulingIgnoredDuringExecution != nil {
2017-08-01 15:03:51 -04:00
allErrs = append ( allErrs , validatePodAffinityTerms ( podAffinity . RequiredDuringSchedulingIgnoredDuringExecution ,
2016-05-04 02:50:31 -04:00
fldPath . Child ( "requiredDuringSchedulingIgnoredDuringExecution" ) ) ... )
}
if podAffinity . PreferredDuringSchedulingIgnoredDuringExecution != nil {
2017-08-01 15:03:51 -04:00
allErrs = append ( allErrs , validateWeightedPodAffinityTerms ( podAffinity . PreferredDuringSchedulingIgnoredDuringExecution ,
2016-05-04 02:50:31 -04:00
fldPath . Child ( "preferredDuringSchedulingIgnoredDuringExecution" ) ) ... )
}
return allErrs
}
2016-08-23 12:52:27 -04:00
func ValidateSeccompProfile ( p string , fldPath * field . Path ) field . ErrorList {
2018-04-19 13:39:53 -04:00
if p == core . SeccompProfileRuntimeDefault || p == core . DeprecatedSeccompProfileDockerDefault {
2016-06-10 04:02:36 -04:00
return nil
}
if p == "unconfined" {
return nil
}
if strings . HasPrefix ( p , "localhost/" ) {
2016-07-30 01:41:20 -04:00
return validateLocalDescendingPath ( strings . TrimPrefix ( p , "localhost/" ) , fldPath )
2016-06-10 04:02:36 -04:00
}
return field . ErrorList { field . Invalid ( fldPath , p , "must be a valid seccomp profile" ) }
}
func ValidateSeccompPodAnnotations ( annotations map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2017-10-09 11:58:37 -04:00
if p , exists := annotations [ core . SeccompPodAnnotationKey ] ; exists {
allErrs = append ( allErrs , ValidateSeccompProfile ( p , fldPath . Child ( core . SeccompPodAnnotationKey ) ) ... )
2016-06-10 04:02:36 -04:00
}
for k , p := range annotations {
2017-10-09 11:58:37 -04:00
if strings . HasPrefix ( k , core . SeccompContainerAnnotationKeyPrefix ) {
2016-08-23 12:52:27 -04:00
allErrs = append ( allErrs , ValidateSeccompProfile ( p , fldPath . Child ( k ) ) ... )
2016-06-10 04:02:36 -04:00
}
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateAppArmorPodAnnotations ( annotations map [ string ] string , spec * core . PodSpec , fldPath * field . Path ) field . ErrorList {
2016-08-16 19:41:05 -04:00
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 ) {
2016-08-25 20:40:18 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Key ( k ) , containerName , "container not found" ) )
2016-08-16 19:41:05 -04:00
}
if err := apparmor . ValidateProfileFormat ( p ) ; err != nil {
2016-08-25 20:40:18 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Key ( k ) , p , err . Error ( ) ) )
2016-08-16 19:41:05 -04:00
}
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func podSpecHasContainer ( spec * core . PodSpec , containerName string ) bool {
2016-08-16 19:41:05 -04:00
for _ , c := range spec . InitContainers {
if c . Name == containerName {
return true
}
}
for _ , c := range spec . Containers {
if c . Name == containerName {
return true
}
}
return false
}
2016-06-14 09:09:53 -04:00
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 )
}
2017-10-09 11:58:37 -04:00
func validateSysctls ( sysctls [ ] core . Sysctl , fldPath * field . Path ) field . ErrorList {
2016-06-14 09:09:53 -04:00
allErrs := field . ErrorList { }
2018-05-11 09:58:29 -04:00
names := make ( map [ string ] struct { } )
2016-06-14 09:09:53 -04:00
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 ) ) )
2018-05-11 09:58:29 -04:00
} else if _ , ok := names [ s . Name ] ; ok {
allErrs = append ( allErrs , field . Duplicate ( fldPath . Index ( i ) . Child ( "name" ) , s . Name ) )
2016-06-14 09:09:53 -04:00
}
2018-05-11 09:58:29 -04:00
names [ s . Name ] = struct { } { }
2016-06-14 09:09:53 -04:00
}
return allErrs
}
2015-09-14 17:56:51 -04:00
// ValidatePodSecurityContext test that the specified PodSecurityContext has valid data.
2017-10-09 11:58:37 -04:00
func ValidatePodSecurityContext ( securityContext * core . PodSecurityContext , spec * core . PodSpec , specPath , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-09-14 17:56:51 -04:00
if securityContext != nil {
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateHostNetwork ( securityContext . HostNetwork , spec . Containers , specPath . Child ( "containers" ) ) ... )
2016-01-29 03:05:34 -05:00
if securityContext . FSGroup != nil {
2017-04-20 06:57:07 -04:00
for _ , msg := range validation . IsValidGroupID ( * securityContext . FSGroup ) {
2016-01-29 03:05:34 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "fsGroup" ) , * ( securityContext . FSGroup ) , msg ) )
}
2015-11-19 21:42:02 -05:00
}
2016-01-29 03:05:34 -05:00
if securityContext . RunAsUser != nil {
2017-04-20 06:57:07 -04:00
for _ , msg := range validation . IsValidUserID ( * securityContext . RunAsUser ) {
2016-01-29 03:05:34 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "runAsUser" ) , * ( securityContext . RunAsUser ) , msg ) )
}
2015-11-19 21:42:02 -05:00
}
2017-05-13 02:29:25 -04:00
if securityContext . RunAsGroup != nil {
for _ , msg := range validation . IsValidGroupID ( * securityContext . RunAsGroup ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "runAsGroup" ) , * ( securityContext . RunAsGroup ) , msg ) )
}
}
2016-01-29 03:05:34 -05:00
for g , gid := range securityContext . SupplementalGroups {
2017-04-20 06:57:07 -04:00
for _ , msg := range validation . IsValidGroupID ( gid ) {
2016-01-29 03:05:34 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "supplementalGroups" ) . Index ( g ) , gid , msg ) )
2015-11-19 21:42:02 -05:00
}
}
2019-01-08 17:26:21 -05:00
if securityContext . ShareProcessNamespace != nil && securityContext . HostPID && * securityContext . ShareProcessNamespace {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "shareProcessNamespace" ) , * securityContext . ShareProcessNamespace , "ShareProcessNamespace and HostPID cannot both be enabled" ) )
2018-01-23 12:40:39 -05:00
}
2018-05-11 09:58:29 -04:00
if len ( securityContext . Sysctls ) != 0 {
2019-01-09 17:17:50 -05:00
allErrs = append ( allErrs , validateSysctls ( securityContext . Sysctls , fldPath . Child ( "sysctls" ) ) ... )
2018-05-11 09:58:29 -04:00
}
2015-09-14 17:56:51 -04:00
}
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateContainerUpdates ( newContainers , oldContainers [ ] core . Container , fldPath * field . Path ) ( allErrs field . ErrorList , stop bool ) {
2016-07-04 16:30:54 -04:00
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" ) , "" ) )
}
2017-06-14 19:52:31 -04:00
// this is only called from ValidatePodUpdate so its safe to check leading/trailing whitespace.
if len ( strings . TrimSpace ( ctr . Image ) ) != len ( ctr . Image ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Index ( i ) . Child ( "image" ) , ctr . Image , "must not have leading or trailing whitespace" ) )
}
2016-07-04 16:30:54 -04:00
}
return allErrs , false
}
2015-02-11 18:32:54 -05:00
// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
// that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidatePodUpdate ( newPod , oldPod * core . Pod ) field . ErrorList {
2016-02-02 13:59:54 -05:00
fldPath := field . NewPath ( "metadata" )
allErrs := ValidateObjectMetaUpdate ( & newPod . ObjectMeta , & oldPod . ObjectMeta , fldPath )
2016-08-16 19:41:05 -04:00
allErrs = append ( allErrs , ValidatePodSpecificAnnotationUpdates ( newPod , oldPod , fldPath . Child ( "annotations" ) ) ... )
2015-11-06 18:30:52 -05:00
specPath := field . NewPath ( "spec" )
2016-01-26 15:52:14 -05:00
// validate updateable fields:
2017-02-16 11:25:12 -05:00
// 1. spec.containers[*].image
// 2. spec.initContainers[*].image
2016-07-04 16:30:54 -04:00
// 3. spec.activeDeadlineSeconds
2016-01-26 15:52:14 -05:00
2016-08-16 18:58:17 -04:00
containerErrs , stop := ValidateContainerUpdates ( newPod . Spec . Containers , oldPod . Spec . Containers , specPath . Child ( "containers" ) )
2016-07-04 16:30:54 -04:00
allErrs = append ( allErrs , containerErrs ... )
if stop {
return allErrs
}
2016-08-16 18:58:17 -04:00
containerErrs , stop = ValidateContainerUpdates ( newPod . Spec . InitContainers , oldPod . Spec . InitContainers , specPath . Child ( "initContainers" ) )
2016-07-04 16:30:54 -04:00
allErrs = append ( allErrs , containerErrs ... )
if stop {
return allErrs
2016-01-26 15:52:14 -05:00
}
// validate updated spec.activeDeadlineSeconds. two types of updates are allowed:
// 1. from nil to a positive value
// 2. from a positive value to a lesser, non-negative value
if newPod . Spec . ActiveDeadlineSeconds != nil {
newActiveDeadlineSeconds := * newPod . Spec . ActiveDeadlineSeconds
2017-05-31 16:10:22 -04:00
if newActiveDeadlineSeconds < 0 || newActiveDeadlineSeconds > math . MaxInt32 {
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "activeDeadlineSeconds" ) , newActiveDeadlineSeconds , validation . InclusiveRangeError ( 0 , math . MaxInt32 ) ) )
2016-01-26 15:52:14 -05:00
return allErrs
}
if oldPod . Spec . ActiveDeadlineSeconds != nil {
oldActiveDeadlineSeconds := * oldPod . Spec . ActiveDeadlineSeconds
if oldActiveDeadlineSeconds < newActiveDeadlineSeconds {
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "activeDeadlineSeconds" ) , newActiveDeadlineSeconds , "must be less than or equal to previous value" ) )
return allErrs
}
}
} else if oldPod . Spec . ActiveDeadlineSeconds != nil {
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "activeDeadlineSeconds" ) , newPod . Spec . ActiveDeadlineSeconds , "must not update from a positive integer to nil value" ) )
}
// handle updateable fields by munging those fields prior to deep equal comparison.
mungedPod := * newPod
2017-02-16 11:25:12 -05:00
// munge spec.containers[*].image
2017-10-09 11:58:37 -04:00
var newContainers [ ] core . Container
2016-01-26 15:52:14 -05:00
for ix , container := range mungedPod . Spec . Containers {
2014-11-13 10:52:13 -05:00
container . Image = oldPod . Spec . Containers [ ix ] . Image
2014-10-09 23:30:34 -04:00
newContainers = append ( newContainers , container )
}
2016-01-26 15:52:14 -05:00
mungedPod . Spec . Containers = newContainers
2017-02-16 11:25:12 -05:00
// munge spec.initContainers[*].image
2017-10-09 11:58:37 -04:00
var newInitContainers [ ] core . Container
2016-07-04 16:30:54 -04:00
for ix , container := range mungedPod . Spec . InitContainers {
container . Image = oldPod . Spec . InitContainers [ ix ] . Image
newInitContainers = append ( newInitContainers , container )
}
mungedPod . Spec . InitContainers = newInitContainers
2016-01-26 15:52:14 -05:00
// munge spec.activeDeadlineSeconds
mungedPod . Spec . ActiveDeadlineSeconds = nil
if oldPod . Spec . ActiveDeadlineSeconds != nil {
activeDeadlineSeconds := * oldPod . Spec . ActiveDeadlineSeconds
mungedPod . Spec . ActiveDeadlineSeconds = & activeDeadlineSeconds
}
2017-02-21 13:03:13 -05:00
// Allow only additions to tolerations updates.
mungedPod . Spec . Tolerations = oldPod . Spec . Tolerations
allErrs = append ( allErrs , validateOnlyAddedTolerations ( newPod . Spec . Tolerations , oldPod . Spec . Tolerations , specPath . Child ( "tolerations" ) ) ... )
2017-01-25 08:39:54 -05:00
if ! apiequality . Semantic . DeepEqual ( mungedPod . Spec , oldPod . Spec ) {
2017-08-18 08:46:32 -04:00
// This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is".
2015-07-31 19:43:39 -04:00
//TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
2017-08-18 08:46:32 -04:00
specDiff := diff . ObjectDiff ( mungedPod . Spec , oldPod . Spec )
allErrs = append ( allErrs , field . Forbidden ( specPath , fmt . Sprintf ( "pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)\n%v" , specDiff ) ) )
2014-10-09 23:30:34 -04:00
}
2015-01-27 18:55:54 -05:00
2014-10-09 23:30:34 -04:00
return allErrs
}
2017-10-24 22:08:21 -04:00
// ValidateContainerStateTransition test to if any illegal container state transitions are being attempted
func ValidateContainerStateTransition ( newStatuses , oldStatuses [ ] core . ContainerStatus , fldpath * field . Path , restartPolicy core . RestartPolicy ) field . ErrorList {
allErrs := field . ErrorList { }
// If we should always restart, containers are allowed to leave the terminated state
if restartPolicy == core . RestartPolicyAlways {
return allErrs
}
for i , oldStatus := range oldStatuses {
// Skip any container that is not terminated
if oldStatus . State . Terminated == nil {
continue
}
// Skip any container that failed but is allowed to restart
if oldStatus . State . Terminated . ExitCode != 0 && restartPolicy == core . RestartPolicyOnFailure {
continue
}
for _ , newStatus := range newStatuses {
if oldStatus . Name == newStatus . Name && newStatus . State . Terminated == nil {
allErrs = append ( allErrs , field . Forbidden ( fldpath . Index ( i ) . Child ( "state" ) , "may not be transitioned to non-terminated state" ) )
}
}
}
return allErrs
}
2015-02-24 00:42:27 -05:00
// ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
// that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidatePodStatusUpdate ( newPod , oldPod * core . Pod ) field . ErrorList {
2017-05-13 15:48:44 -04:00
fldPath := field . NewPath ( "metadata" )
allErrs := ValidateObjectMetaUpdate ( & newPod . ObjectMeta , & oldPod . ObjectMeta , fldPath )
allErrs = append ( allErrs , ValidatePodSpecificAnnotationUpdates ( newPod , oldPod , fldPath . Child ( "annotations" ) ) ... )
2018-05-18 18:47:08 -04:00
allErrs = append ( allErrs , validatePodConditions ( newPod . Status . Conditions , fldPath . Child ( "conditions" ) ) ... )
2015-02-24 00:42:27 -05:00
2017-10-24 22:08:21 -04:00
fldPath = field . NewPath ( "status" )
2015-05-22 19:40:57 -04:00
if newPod . Spec . NodeName != oldPod . Spec . NodeName {
2017-10-24 22:08:21 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "nodeName" ) , "may not be changed directly" ) )
2015-02-24 00:42:27 -05:00
}
2018-01-25 16:23:56 -05:00
if newPod . Status . NominatedNodeName != oldPod . Status . NominatedNodeName && len ( newPod . Status . NominatedNodeName ) > 0 {
for _ , msg := range ValidateNodeName ( newPod . Status . NominatedNodeName , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "nominatedNodeName" ) , newPod . Status . NominatedNodeName , msg ) )
}
}
2017-10-24 22:08:21 -04:00
// If pod should not restart, make sure the status update does not transition
// any terminated containers to a non-terminated state.
allErrs = append ( allErrs , ValidateContainerStateTransition ( newPod . Status . ContainerStatuses , oldPod . Status . ContainerStatuses , fldPath . Child ( "containerStatuses" ) , oldPod . Spec . RestartPolicy ) ... )
allErrs = append ( allErrs , ValidateContainerStateTransition ( newPod . Status . InitContainerStatuses , oldPod . Status . InitContainerStatuses , fldPath . Child ( "initContainerStatuses" ) , oldPod . Spec . RestartPolicy ) ... )
2015-03-09 10:23:52 -04:00
// For status update we ignore changes to pod spec.
2015-02-24 00:42:27 -05:00
newPod . Spec = oldPod . Spec
return allErrs
}
2018-05-18 18:47:08 -04:00
// validatePodConditions tests if the custom pod conditions are valid.
func validatePodConditions ( conditions [ ] core . PodCondition , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
systemConditions := sets . NewString ( string ( core . PodScheduled ) , string ( core . PodReady ) , string ( core . PodInitialized ) )
for i , condition := range conditions {
if systemConditions . Has ( string ( condition . Type ) ) {
continue
}
for _ , msg := range validation . IsQualifiedName ( string ( condition . Type ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Index ( i ) . Child ( "Type" ) , string ( condition . Type ) , msg ) )
}
}
return allErrs
}
2015-12-10 09:08:21 -05:00
// ValidatePodBinding tests if required fields in the pod binding are legal.
2017-10-09 11:58:37 -04:00
func ValidatePodBinding ( binding * core . Binding ) field . ErrorList {
2015-12-10 09:08:21 -05:00
allErrs := field . ErrorList { }
if len ( binding . Target . Kind ) != 0 && binding . Target . Kind != "Node" {
// TODO: When validation becomes versioned, this gets more complicated.
allErrs = append ( allErrs , field . NotSupported ( field . NewPath ( "target" , "kind" ) , binding . Target . Kind , [ ] string { "Node" , "<empty>" } ) )
}
if len ( binding . Target . Name ) == 0 {
// TODO: When validation becomes versioned, this gets more complicated.
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "target" , "name" ) , "" ) )
2015-12-10 09:08:21 -05:00
}
return allErrs
}
2015-03-03 19:54:17 -05:00
// ValidatePodTemplate tests if required fields in the pod template are set.
2017-10-09 11:58:37 -04:00
func ValidatePodTemplate ( pod * core . PodTemplate ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & pod . Template , field . NewPath ( "template" ) ) ... )
2015-03-03 19:54:17 -05:00
return allErrs
}
// ValidatePodTemplateUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
// that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidatePodTemplateUpdate ( newPod , oldPod * core . PodTemplate ) field . ErrorList {
2018-03-01 02:47:34 -05:00
allErrs := ValidateObjectMetaUpdate ( & newPod . ObjectMeta , & oldPod . ObjectMeta , field . NewPath ( "metadata" ) )
2015-11-06 18:30:52 -05:00
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & newPod . Template , field . NewPath ( "template" ) ) ... )
2015-03-03 19:54:17 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
var supportedSessionAffinityType = sets . NewString ( string ( core . ServiceAffinityClientIP ) , string ( core . ServiceAffinityNone ) )
var supportedServiceType = sets . NewString ( string ( core . ServiceTypeClusterIP ) , string ( core . ServiceTypeNodePort ) ,
string ( core . ServiceTypeLoadBalancer ) , string ( core . ServiceTypeExternalName ) )
2014-12-17 07:52:11 -05:00
2016-10-18 21:43:13 -04:00
// ValidateService tests if required fields/annotations of a Service are valid.
2017-10-09 11:58:37 -04:00
func ValidateService ( service * core . Service ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & service . ObjectMeta , true , ValidateServiceName , field . NewPath ( "metadata" ) )
2015-01-27 18:55:54 -05:00
2015-11-06 18:30:52 -05:00
specPath := field . NewPath ( "spec" )
2017-10-09 11:58:37 -04:00
isHeadlessService := service . Spec . ClusterIP == core . ClusterIPNone
if len ( service . Spec . Ports ) == 0 && ! isHeadlessService && service . Spec . Type != core . ServiceTypeExternalName {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "ports" ) , "" ) )
2014-08-22 17:44:21 -04:00
}
2016-08-19 12:09:14 -04:00
switch service . Spec . Type {
2017-10-09 11:58:37 -04:00
case core . ServiceTypeLoadBalancer :
2015-07-09 01:02:10 -04:00
for ix := range service . Spec . Ports {
port := & service . Spec . Ports [ ix ]
2015-11-14 15:26:04 -05:00
// This is a workaround for broken cloud environments that
// over-open firewalls. Hopefully it can go away when more clouds
// understand containers better.
2017-12-01 14:47:49 -05:00
if port . Port == ports . KubeletPort {
2015-11-04 16:52:14 -05:00
portPath := specPath . Child ( "ports" ) . Index ( ix )
2017-12-01 14:47:49 -05:00
allErrs = append ( allErrs , field . Invalid ( portPath , port . Port , fmt . Sprintf ( "may not expose port %v externally since it is used by kubelet" , ports . KubeletPort ) ) )
2015-07-09 01:02:10 -04:00
}
}
2016-09-22 10:07:47 -04:00
if service . Spec . ClusterIP == "None" {
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "clusterIP" ) , service . Spec . ClusterIP , "may not be set to 'None' for LoadBalancer services" ) )
}
2017-10-09 11:58:37 -04:00
case core . ServiceTypeNodePort :
2016-08-18 17:50:09 -04:00
if service . Spec . ClusterIP == "None" {
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "clusterIP" ) , service . Spec . ClusterIP , "may not be set to 'None' for NodePort services" ) )
}
2017-10-09 11:58:37 -04:00
case core . ServiceTypeExternalName :
2016-08-19 12:09:14 -04:00
if service . Spec . ClusterIP != "" {
2017-12-01 14:47:49 -05:00
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "clusterIP" ) , "must be empty for ExternalName services" ) )
2016-08-19 12:09:14 -04:00
}
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" ) , "" ) )
}
2015-07-09 01:02:10 -04:00
}
2015-11-27 03:09:13 -05:00
2015-09-09 13:45:01 -04:00
allPortNames := sets . String { }
2015-11-04 16:52:14 -05:00
portsPath := specPath . Child ( "ports" )
2015-03-13 11:16:41 -04:00
for i := range service . Spec . Ports {
2015-11-04 16:52:14 -05:00
portPath := portsPath . Index ( i )
allErrs = append ( allErrs , validateServicePort ( & service . Spec . Ports [ i ] , len ( service . Spec . Ports ) > 1 , isHeadlessService , & allPortNames , portPath ) ... )
2015-02-27 20:33:58 -05:00
}
2014-11-18 12:49:00 -05:00
if service . Spec . Selector != nil {
2016-05-04 02:50:31 -04:00
allErrs = append ( allErrs , unversionedvalidation . ValidateLabels ( service . Spec . Selector , specPath . Child ( "selector" ) ) ... )
2014-07-10 15:45:01 -04:00
}
2014-11-18 12:49:00 -05:00
2015-12-08 23:39:18 -05:00
if len ( service . Spec . SessionAffinity ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "sessionAffinity" ) , "" ) )
2014-12-29 17:39:09 -05:00
} else if ! supportedSessionAffinityType . Has ( string ( service . Spec . SessionAffinity ) ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "sessionAffinity" ) , service . Spec . SessionAffinity , supportedSessionAffinityType . List ( ) ) )
2014-12-17 07:52:11 -05:00
}
2017-10-09 11:58:37 -04:00
if service . Spec . SessionAffinity == core . ServiceAffinityClientIP {
2017-08-01 12:09:37 -04:00
allErrs = append ( allErrs , validateClientIPAffinityConfig ( service . Spec . SessionAffinityConfig , specPath . Child ( "sessionAffinityConfig" ) ) ... )
2017-10-09 11:58:37 -04:00
} else if service . Spec . SessionAffinity == core . ServiceAffinityNone {
2017-08-01 12:09:37 -04:00
if service . Spec . SessionAffinityConfig != nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "sessionAffinityConfig" ) , fmt . Sprintf ( "must not be set when session affinity is %s" , string ( core . ServiceAffinityNone ) ) ) )
2017-08-01 12:09:37 -04:00
}
}
2017-04-10 13:49:54 -04:00
if helper . IsServiceIPSet ( service ) {
2015-05-23 16:41:11 -04:00
if ip := net . ParseIP ( service . Spec . ClusterIP ) ; ip == nil {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "clusterIP" ) , service . Spec . ClusterIP , "must be empty, 'None', or a valid IP address" ) )
2015-03-16 17:36:30 -04:00
}
}
2015-11-04 16:52:14 -05:00
ipPath := specPath . Child ( "externalIPs" )
for i , ip := range service . Spec . ExternalIPs {
idxPath := ipPath . Index ( i )
2016-01-04 12:49:39 -05:00
if msgs := validation . IsValidIP ( ip ) ; len ( msgs ) != 0 {
for i := range msgs {
allErrs = append ( allErrs , field . Invalid ( idxPath , ip , msgs [ i ] ) )
}
} else {
allErrs = append ( allErrs , validateNonSpecialIP ( ip , idxPath ) ... )
2015-03-16 10:03:05 -04:00
}
}
2015-12-08 23:39:18 -05:00
if len ( service . Spec . Type ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "type" ) , "" ) )
2015-05-22 17:49:26 -04:00
} else if ! supportedServiceType . Has ( string ( service . Spec . Type ) ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "type" ) , service . Spec . Type , supportedServiceType . List ( ) ) )
2015-05-22 17:49:26 -04:00
}
2017-10-09 11:58:37 -04:00
if service . Spec . Type == core . ServiceTypeLoadBalancer {
2015-11-04 16:52:14 -05:00
portsPath := specPath . Child ( "ports" )
2016-02-03 17:54:32 -05:00
includeProtocols := sets . NewString ( )
2015-04-03 15:06:25 -04:00
for i := range service . Spec . Ports {
2015-11-04 16:52:14 -05:00
portPath := portsPath . Index ( i )
2019-02-05 18:07:06 -05:00
if ! supportedPortProtocols . Has ( string ( service . Spec . Ports [ i ] . Protocol ) ) {
2018-08-25 16:26:25 -04:00
allErrs = append ( allErrs , field . Invalid ( portPath . Child ( "protocol" ) , service . Spec . Ports [ i ] . Protocol , "cannot create an external load balancer with non-TCP/UDP/SCTP ports" ) )
2016-02-03 17:54:32 -05:00
} else {
includeProtocols . Insert ( string ( service . Spec . Ports [ i ] . Protocol ) )
2015-04-03 15:06:25 -04:00
}
}
2016-02-03 17:54:32 -05:00
if includeProtocols . Len ( ) > 1 {
allErrs = append ( allErrs , field . Invalid ( portsPath , service . Spec . Ports , "cannot create an external load balancer with mix protocols" ) )
}
2015-04-03 15:06:25 -04:00
}
2017-10-09 11:58:37 -04:00
if service . Spec . Type == core . ServiceTypeClusterIP {
2015-11-04 16:52:14 -05:00
portsPath := specPath . Child ( "ports" )
2015-05-20 11:59:34 -04:00
for i := range service . Spec . Ports {
2015-11-04 16:52:14 -05:00
portPath := portsPath . Index ( i )
2015-05-20 11:59:34 -04:00
if service . Spec . Ports [ i ] . NodePort != 0 {
2017-12-01 14:47:49 -05:00
allErrs = append ( allErrs , field . Forbidden ( portPath . Child ( "nodePort" ) , "may not be used when `type` is 'ClusterIP'" ) )
2015-05-20 11:59:34 -04:00
}
}
}
2015-05-22 17:54:19 -04:00
// Check for duplicate NodePorts, considering (protocol,port) pairs
2015-11-04 16:52:14 -05:00
portsPath = specPath . Child ( "ports" )
2017-10-09 11:58:37 -04:00
nodePorts := make ( map [ core . ServicePort ] bool )
2015-05-22 17:54:19 -04:00
for i := range service . Spec . Ports {
port := & service . Spec . Ports [ i ]
if port . NodePort == 0 {
continue
}
2015-11-04 16:52:14 -05:00
portPath := portsPath . Index ( i )
2017-10-09 11:58:37 -04:00
var key core . ServicePort
2015-05-22 17:54:19 -04:00
key . Protocol = port . Protocol
key . NodePort = port . NodePort
_ , found := nodePorts [ key ]
if found {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Duplicate ( portPath . Child ( "nodePort" ) , port . NodePort ) )
2015-05-22 17:54:19 -04:00
}
nodePorts [ key ] = true
}
2017-06-12 02:31:37 -04:00
// Check for duplicate Ports, considering (protocol,port) pairs
portsPath = specPath . Child ( "ports" )
2017-10-09 11:58:37 -04:00
ports := make ( map [ core . ServicePort ] bool )
2017-06-12 02:31:37 -04:00
for i , port := range service . Spec . Ports {
portPath := portsPath . Index ( i )
2017-10-09 11:58:37 -04:00
key := core . ServicePort { Protocol : port . Protocol , Port : port . Port }
2017-06-12 02:31:37 -04:00
_ , found := ports [ key ]
if found {
allErrs = append ( allErrs , field . Duplicate ( portPath , key ) )
}
ports [ key ] = true
}
2016-05-17 19:55:04 -04:00
// Validate SourceRange field and annotation
2017-10-09 11:58:37 -04:00
_ , ok := service . Annotations [ core . AnnotationLoadBalancerSourceRangesKey ]
2016-05-17 19:55:04 -04:00
if len ( service . Spec . LoadBalancerSourceRanges ) > 0 || ok {
var fieldPath * field . Path
var val string
if len ( service . Spec . LoadBalancerSourceRanges ) > 0 {
fieldPath = specPath . Child ( "LoadBalancerSourceRanges" )
val = fmt . Sprintf ( "%v" , service . Spec . LoadBalancerSourceRanges )
} else {
2017-10-09 11:58:37 -04:00
fieldPath = field . NewPath ( "metadata" , "annotations" ) . Key ( core . AnnotationLoadBalancerSourceRangesKey )
val = service . Annotations [ core . AnnotationLoadBalancerSourceRangesKey ]
2016-05-17 19:55:04 -04:00
}
2017-10-09 11:58:37 -04:00
if service . Spec . Type != core . ServiceTypeLoadBalancer {
2017-12-01 14:47:49 -05:00
allErrs = append ( allErrs , field . Forbidden ( fieldPath , "may only be used when `type` is 'LoadBalancer'" ) )
2016-05-17 19:55:04 -04:00
}
_ , err := apiservice . GetLoadBalancerSourceRanges ( service )
if err != nil {
allErrs = append ( allErrs , field . Invalid ( fieldPath , val , "must be a list of IP ranges. For example, 10.240.0.0/24,10.250.0.0/24 " ) )
}
2016-02-29 20:56:39 -05:00
}
2017-04-20 17:12:15 -04:00
2017-05-12 13:56:42 -04:00
allErrs = append ( allErrs , validateServiceExternalTrafficFieldsValue ( service ) ... )
2017-04-20 17:12:15 -04:00
2014-08-16 16:48:48 -04:00
return allErrs
2014-07-10 15:45:01 -04:00
}
2014-07-25 12:15:17 -04:00
2017-10-09 11:58:37 -04:00
func validateServicePort ( sp * core . ServicePort , requireName , isHeadlessService bool , allNames * sets . String , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-03-13 11:16:41 -04:00
2015-12-08 23:39:18 -05:00
if requireName && len ( sp . Name ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
2015-12-08 23:39:18 -05:00
} else if len ( sp . Name ) != 0 {
2016-04-29 10:58:46 -04:00
allErrs = append ( allErrs , ValidateDNS1123Label ( sp . Name , fldPath . Child ( "name" ) ) ... )
if allNames . Has ( sp . Name ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Duplicate ( fldPath . Child ( "name" ) , sp . Name ) )
2015-04-28 22:08:42 -04:00
} else {
allNames . Insert ( sp . Name )
2015-03-13 11:16:41 -04:00
}
}
2016-01-04 11:33:26 -05:00
for _ , msg := range validation . IsValidPortNum ( int ( sp . Port ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "port" ) , sp . Port , msg ) )
2015-03-13 11:16:41 -04:00
}
if len ( sp . Protocol ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "protocol" ) , "" ) )
2015-06-16 20:28:11 -04:00
} else if ! supportedPortProtocols . Has ( string ( sp . Protocol ) ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "protocol" ) , sp . Protocol , supportedPortProtocols . List ( ) ) )
2015-03-13 11:16:41 -04:00
}
2016-01-04 11:33:26 -05:00
allErrs = append ( allErrs , ValidatePortNumOrName ( sp . TargetPort , fldPath . Child ( "targetPort" ) ) ... )
2015-03-13 11:16:41 -04:00
2016-02-22 23:06:16 -05:00
// in the v1 API, targetPorts on headless services were tolerated.
// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
//
// if isHeadlessService {
// if sp.TargetPort.Type == intstr.String || (sp.TargetPort.Type == intstr.Int && sp.Port != sp.TargetPort.IntValue()) {
// allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, "must be equal to the value of 'port' when clusterIP = None"))
// }
// }
2015-11-27 03:09:13 -05:00
2015-03-13 11:16:41 -04:00
return allErrs
}
2017-05-12 13:56:42 -04:00
// validateServiceExternalTrafficFieldsValue validates ExternalTraffic related annotations
2017-04-20 17:12:15 -04:00
// have legal value.
2017-10-09 11:58:37 -04:00
func validateServiceExternalTrafficFieldsValue ( service * core . Service ) field . ErrorList {
2017-04-20 17:12:15 -04:00
allErrs := field . ErrorList { }
2017-05-12 13:56:42 -04:00
// Check first class fields.
if service . Spec . ExternalTrafficPolicy != "" &&
2017-10-09 11:58:37 -04:00
service . Spec . ExternalTrafficPolicy != core . ServiceExternalTrafficPolicyTypeCluster &&
service . Spec . ExternalTrafficPolicy != core . ServiceExternalTrafficPolicyTypeLocal {
2017-05-12 13:56:42 -04:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "spec" ) . Child ( "externalTrafficPolicy" ) , service . Spec . ExternalTrafficPolicy ,
2017-10-09 11:58:37 -04:00
fmt . Sprintf ( "ExternalTrafficPolicy must be empty, %v or %v" , core . ServiceExternalTrafficPolicyTypeCluster , core . ServiceExternalTrafficPolicyTypeLocal ) ) )
2017-05-12 13:56:42 -04:00
}
if service . Spec . HealthCheckNodePort < 0 {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "spec" ) . Child ( "healthCheckNodePort" ) , service . Spec . HealthCheckNodePort ,
"HealthCheckNodePort must be not less than 0" ) )
}
2017-04-20 17:12:15 -04:00
return allErrs
}
2017-05-12 13:56:42 -04:00
// ValidateServiceExternalTrafficFieldsCombination validates if ExternalTrafficPolicy,
// HealthCheckNodePort and Type combination are legal. For update, it should be called
// after clearing externalTraffic related fields for the ease of transitioning between
// different service types.
2017-10-09 11:58:37 -04:00
func ValidateServiceExternalTrafficFieldsCombination ( service * core . Service ) field . ErrorList {
2017-05-12 13:56:42 -04:00
allErrs := field . ErrorList { }
2017-10-09 11:58:37 -04:00
if service . Spec . Type != core . ServiceTypeLoadBalancer &&
service . Spec . Type != core . ServiceTypeNodePort &&
2017-05-12 13:56:42 -04:00
service . Spec . ExternalTrafficPolicy != "" {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "spec" , "externalTrafficPolicy" ) , service . Spec . ExternalTrafficPolicy ,
"ExternalTrafficPolicy can only be set on NodePort and LoadBalancer service" ) )
2016-10-18 21:43:13 -04:00
}
2017-04-20 17:12:15 -04:00
2017-05-12 13:56:42 -04:00
if ! apiservice . NeedsHealthCheck ( service ) &&
service . Spec . HealthCheckNodePort != 0 {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "spec" , "healthCheckNodePort" ) , service . Spec . HealthCheckNodePort ,
"HealthCheckNodePort can only be set on LoadBalancer service with ExternalTrafficPolicy=Local" ) )
2017-04-20 17:12:15 -04:00
}
return allErrs
2016-10-18 21:43:13 -04:00
}
2015-01-27 18:55:54 -05:00
// ValidateServiceUpdate tests if required fields in the service are set during an update
2017-10-09 11:58:37 -04:00
func ValidateServiceUpdate ( service , oldService * core . Service ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & service . ObjectMeta , & oldService . ObjectMeta , field . NewPath ( "metadata" ) )
2015-01-27 18:55:54 -05:00
2016-10-13 04:11:26 -04:00
// ClusterIP should be immutable for services using it (every type other than ExternalName)
// which do not have ClusterIP assigned yet (empty string value)
2017-10-09 11:58:37 -04:00
if service . Spec . Type != core . ServiceTypeExternalName {
if oldService . Spec . Type != core . ServiceTypeExternalName && oldService . Spec . ClusterIP != "" {
2016-10-13 04:11:26 -04:00
allErrs = append ( allErrs , ValidateImmutableField ( service . Spec . ClusterIP , oldService . Spec . ClusterIP , field . NewPath ( "spec" , "clusterIP" ) ) ... )
}
2015-01-27 18:55:54 -05:00
}
2017-04-20 17:12:15 -04:00
allErrs = append ( allErrs , ValidateService ( service ) ... )
2015-01-27 18:55:54 -05:00
return allErrs
}
2016-01-11 19:51:31 -05:00
// ValidateServiceStatusUpdate tests if required fields in the Service are set when updating status.
2017-10-09 11:58:37 -04:00
func ValidateServiceStatusUpdate ( service , oldService * core . Service ) field . ErrorList {
2016-01-11 19:51:31 -05:00
allErrs := ValidateObjectMetaUpdate ( & service . ObjectMeta , & oldService . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateLoadBalancerStatus ( & service . Status . LoadBalancer , field . NewPath ( "status" , "loadBalancer" ) ) ... )
return allErrs
}
2014-07-25 12:15:17 -04:00
// ValidateReplicationController tests if required fields in the replication controller are set.
2017-10-09 11:58:37 -04:00
func ValidateReplicationController ( controller * core . ReplicationController ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & controller . ObjectMeta , true , ValidateReplicationControllerName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec , field . NewPath ( "spec" ) ) ... )
2015-01-27 18:55:54 -05:00
return allErrs
}
// ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
2017-10-09 11:58:37 -04:00
func ValidateReplicationControllerUpdate ( controller , oldController * core . ReplicationController ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec , field . NewPath ( "spec" ) ) ... )
2014-09-16 12:54:38 -04:00
return allErrs
}
2015-09-28 15:39:57 -04:00
// ValidateReplicationControllerStatusUpdate tests if required fields in the replication controller are set.
2017-10-09 11:58:37 -04:00
func ValidateReplicationControllerStatusUpdate ( controller , oldController * core . ReplicationController ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta , field . NewPath ( "metadata" ) )
2017-02-25 07:46:06 -05:00
allErrs = append ( allErrs , ValidateReplicationControllerStatus ( controller . Status , field . NewPath ( "status" ) ) ... )
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateReplicationControllerStatus ( status core . ReplicationControllerStatus , statusPath * field . Path ) field . ErrorList {
2017-02-25 07:46:06 -05:00
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( status . Replicas ) , statusPath . Child ( "replicas" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( status . FullyLabeledReplicas ) , statusPath . Child ( "fullyLabeledReplicas" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( status . ReadyReplicas ) , statusPath . Child ( "readyReplicas" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( status . AvailableReplicas ) , statusPath . Child ( "availableReplicas" ) ) ... )
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( status . ObservedGeneration ) , statusPath . Child ( "observedGeneration" ) ) ... )
2016-11-04 12:26:19 -04:00
msg := "cannot be greater than status.replicas"
2017-02-25 07:46:06 -05:00
if status . FullyLabeledReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( statusPath . Child ( "fullyLabeledReplicas" ) , status . FullyLabeledReplicas , msg ) )
2016-11-04 12:26:19 -04:00
}
2017-02-25 07:46:06 -05:00
if status . ReadyReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( statusPath . Child ( "readyReplicas" ) , status . ReadyReplicas , msg ) )
2016-11-04 12:26:19 -04:00
}
2017-02-25 07:46:06 -05:00
if status . AvailableReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( statusPath . Child ( "availableReplicas" ) , status . AvailableReplicas , msg ) )
2016-11-04 12:26:19 -04:00
}
2017-02-25 07:46:06 -05:00
if status . AvailableReplicas > status . ReadyReplicas {
allErrs = append ( allErrs , field . Invalid ( statusPath . Child ( "availableReplicas" ) , status . AvailableReplicas , "cannot be greater than readyReplicas" ) )
2016-11-04 12:26:19 -04:00
}
2015-09-28 15:39:57 -04:00
return allErrs
}
2015-08-25 15:07:03 -04:00
// Validates that the given selector is non-empty.
2015-11-06 18:30:52 -05:00
func ValidateNonEmptySelector ( selectorMap map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-08-25 15:07:03 -04:00
selector := labels . Set ( selectorMap ) . AsSelector ( )
2014-11-06 21:08:46 -05:00
if selector . Empty ( ) {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath , "" ) )
2014-08-21 20:02:39 -04:00
}
2015-08-25 15:07:03 -04:00
return allErrs
}
2014-11-06 21:08:46 -05:00
2016-06-22 05:40:52 -04:00
// Validates the given template and ensures that it is in accordance with the desired selector and replicas.
2017-10-09 11:58:37 -04:00
func ValidatePodTemplateSpecForRC ( template * core . PodTemplateSpec , selectorMap map [ string ] string , replicas int32 , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-08-25 15:07:03 -04:00
if template == nil {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath , "" ) )
2014-11-06 21:08:46 -05:00
} else {
2015-08-25 15:07:03 -04:00
selector := labels . Set ( selectorMap ) . AsSelector ( )
if ! selector . Empty ( ) {
// Verify that the RC selector matches the labels in template.
labels := labels . Set ( template . Labels )
if ! selector . Matches ( labels ) {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "metadata" , "labels" ) , template . Labels , "`selector` does not match template `labels`" ) )
2015-08-25 15:07:03 -04:00
}
2015-07-25 15:08:34 -04:00
}
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , ValidatePodTemplateSpec ( template , fldPath ) ... )
2015-08-25 15:07:03 -04:00
if replicas > 1 {
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , ValidateReadOnlyPersistentDisks ( template . Spec . Volumes , fldPath . Child ( "spec" , "volumes" ) ) ... )
2015-07-25 15:08:34 -04:00
}
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
2017-10-09 11:58:37 -04:00
if template . Spec . RestartPolicy != core . RestartPolicyAlways {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "spec" , "restartPolicy" ) , template . Spec . RestartPolicy , [ ] string { string ( core . RestartPolicyAlways ) } ) )
2014-11-17 23:08:23 -05:00
}
2016-12-13 21:14:46 -05:00
if template . Spec . ActiveDeadlineSeconds != nil {
2018-11-08 22:29:33 -05:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "spec" , "activeDeadlineSeconds" ) , "activeDeadlineSeconds in ReplicationController is not Supported" ) )
2016-12-13 21:14:46 -05:00
}
2014-08-04 15:02:51 -04:00
}
2014-08-05 13:58:43 -04:00
return allErrs
}
2014-11-06 21:08:46 -05:00
2015-08-25 15:07:03 -04:00
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
2017-10-09 11:58:37 -04:00
func ValidateReplicationControllerSpec ( spec * core . ReplicationControllerSpec , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2016-09-13 12:59:38 -04:00
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( spec . MinReadySeconds ) , fldPath . Child ( "minReadySeconds" ) ) ... )
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , ValidateNonEmptySelector ( spec . Selector , fldPath . Child ( "selector" ) ) ... )
2015-10-08 16:02:23 -04:00
allErrs = append ( allErrs , ValidateNonnegativeField ( int64 ( spec . Replicas ) , fldPath . Child ( "replicas" ) ) ... )
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , ValidatePodTemplateSpecForRC ( spec . Template , spec . Selector , spec . Replicas , fldPath . Child ( "template" ) ) ... )
2015-08-25 15:07:03 -04:00
return allErrs
}
2014-11-06 21:08:46 -05:00
// ValidatePodTemplateSpec validates the spec of a pod template
2017-10-09 11:58:37 -04:00
func ValidatePodTemplateSpec ( spec * core . PodTemplateSpec , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2016-05-04 02:50:31 -04:00
allErrs = append ( allErrs , unversionedvalidation . ValidateLabels ( spec . Labels , fldPath . Child ( "labels" ) ) ... )
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , ValidateAnnotations ( spec . Annotations , fldPath . Child ( "annotations" ) ) ... )
2016-08-16 19:41:05 -04:00
allErrs = append ( allErrs , ValidatePodSpecificAnnotations ( spec . Annotations , & spec . Spec , fldPath . Child ( "annotations" ) ) ... )
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , ValidatePodSpec ( & spec . Spec , fldPath . Child ( "spec" ) ) ... )
2014-11-06 21:08:46 -05:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateReadOnlyPersistentDisks ( volumes [ ] core . Volume , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-11-04 16:52:14 -05:00
for i := range volumes {
vol := & volumes [ i ]
idxPath := fldPath . Index ( i )
2015-03-03 17:48:55 -05:00
if vol . GCEPersistentDisk != nil {
2019-02-09 12:07:18 -05:00
if ! vol . GCEPersistentDisk . ReadOnly {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "gcePersistentDisk" , "readOnly" ) , false , "must be true for replicated pods > 1; GCE PD can only be mounted on multiple machines if it is read-only" ) )
2014-08-05 13:58:43 -04:00
}
}
2015-03-06 09:26:39 -05:00
// TODO: What to do for AWS? It doesn't support replicas
2014-08-05 13:58:43 -04:00
}
2014-08-16 16:48:48 -04:00
return allErrs
2014-07-25 12:15:17 -04:00
}
2014-10-08 15:56:02 -04:00
2017-03-22 01:01:49 -04:00
// ValidateTaintsInNodeAnnotations tests that the serialized taints in Node.Annotations has valid data
func ValidateTaintsInNodeAnnotations ( annotations map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2017-04-10 13:49:54 -04:00
taints , err := helper . GetTaintsFromNodeAnnotations ( annotations )
2017-03-22 01:01:49 -04:00
if err != nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath , core . TaintsAnnotationKey , err . Error ( ) ) )
2017-03-22 01:01:49 -04:00
return allErrs
}
if len ( taints ) > 0 {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , validateNodeTaints ( taints , fldPath . Child ( core . TaintsAnnotationKey ) ) ... )
2017-03-22 01:01:49 -04:00
}
return allErrs
}
2017-02-20 11:43:05 -05:00
// validateNodeTaints tests if given taints have valid data.
2017-10-09 11:58:37 -04:00
func validateNodeTaints ( taints [ ] core . Taint , fldPath * field . Path ) field . ErrorList {
2016-03-30 23:42:57 -04:00
allErrors := field . ErrorList { }
2016-08-12 04:46:40 -04:00
2017-10-09 11:58:37 -04:00
uniqueTaints := map [ core . TaintEffect ] sets . String { }
2016-08-12 04:46:40 -04:00
2016-03-30 23:42:57 -04:00
for i , currTaint := range taints {
idxPath := fldPath . Index ( i )
// validate the taint key
allErrors = append ( allErrors , unversionedvalidation . ValidateLabelName ( currTaint . Key , idxPath . Child ( "key" ) ) ... )
// validate the taint value
if errs := validation . IsValidLabelValue ( currTaint . Value ) ; len ( errs ) != 0 {
allErrors = append ( allErrors , field . Invalid ( idxPath . Child ( "value" ) , currTaint . Value , strings . Join ( errs , ";" ) ) )
}
// validate the taint effect
allErrors = append ( allErrors , validateTaintEffect ( & currTaint . Effect , false , idxPath . Child ( "effect" ) ) ... )
2016-08-12 04:46:40 -04:00
// 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 )
2016-03-30 23:42:57 -04:00
}
return allErrors
}
func ValidateNodeSpecificAnnotations ( annotations map [ string ] string , fldPath * field . Path ) field . ErrorList {
2016-02-25 04:30:31 -05:00
allErrs := field . ErrorList { }
2017-03-22 01:01:49 -04:00
2017-10-09 11:58:37 -04:00
if annotations [ core . TaintsAnnotationKey ] != "" {
2017-03-22 01:01:49 -04:00
allErrs = append ( allErrs , ValidateTaintsInNodeAnnotations ( annotations , fldPath ) ... )
}
2017-10-09 11:58:37 -04:00
if annotations [ core . PreferAvoidPodsAnnotationKey ] != "" {
2016-02-25 04:30:31 -05:00
allErrs = append ( allErrs , ValidateAvoidPodsInNodeAnnotations ( annotations , fldPath ) ... )
}
return allErrs
2016-03-30 23:42:57 -04:00
}
2015-04-22 13:55:05 -04:00
// ValidateNode tests if required fields in the node are set.
2017-10-09 11:58:37 -04:00
func ValidateNode ( node * core . Node ) field . ErrorList {
2016-03-30 23:42:57 -04:00
fldPath := field . NewPath ( "metadata" )
allErrs := ValidateObjectMeta ( & node . ObjectMeta , false , ValidateNodeName , fldPath )
allErrs = append ( allErrs , ValidateNodeSpecificAnnotations ( node . ObjectMeta . Annotations , fldPath . Child ( "annotations" ) ) ... )
2017-02-20 11:43:05 -05:00
if len ( node . Spec . Taints ) > 0 {
allErrs = append ( allErrs , validateNodeTaints ( node . Spec . Taints , fldPath . Child ( "taints" ) ) ... )
}
2015-04-01 19:11:33 -04:00
2017-08-30 14:23:26 -04:00
// Only validate spec.
// All status fields are optional and can be updated later.
// That said, if specified, we need to ensure they are valid.
allErrs = append ( allErrs , ValidateNodeResources ( node ) ... )
2015-03-24 13:24:07 -04:00
2017-11-13 01:46:12 -05:00
if len ( node . Spec . PodCIDR ) != 0 {
_ , err := ValidateCIDR ( node . Spec . PodCIDR )
if err != nil {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "spec" , "podCIDR" ) , node . Spec . PodCIDR , "not a valid CIDR" ) )
}
}
2014-11-12 12:38:15 -05:00
return allErrs
}
2014-11-17 13:22:27 -05:00
2017-08-30 14:23:26 -04:00
// ValidateNodeResources is used to make sure a node has valid capacity and allocatable values.
2017-10-09 11:58:37 -04:00
func ValidateNodeResources ( node * core . Node ) field . ErrorList {
2017-08-30 14:23:26 -04:00
allErrs := field . ErrorList { }
// Validate resource quantities in capacity.
hugePageSizes := sets . NewString ( )
for k , v := range node . Status . Capacity {
resPath := field . NewPath ( "status" , "capacity" , string ( k ) )
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
// track any huge page size that has a positive value
if helper . IsHugePageResourceName ( k ) && v . Value ( ) > int64 ( 0 ) {
hugePageSizes . Insert ( string ( k ) )
}
if len ( hugePageSizes ) > 1 {
allErrs = append ( allErrs , field . Invalid ( resPath , v , "may not have pre-allocated hugepages for multiple page sizes" ) )
}
}
// Validate resource quantities in allocatable.
hugePageSizes = sets . NewString ( )
for k , v := range node . Status . Allocatable {
resPath := field . NewPath ( "status" , "allocatable" , string ( k ) )
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
// track any huge page size that has a positive value
if helper . IsHugePageResourceName ( k ) && v . Value ( ) > int64 ( 0 ) {
hugePageSizes . Insert ( string ( k ) )
}
if len ( hugePageSizes ) > 1 {
allErrs = append ( allErrs , field . Invalid ( resPath , v , "may not have pre-allocated hugepages for multiple page sizes" ) )
}
}
return allErrs
}
2015-04-22 13:55:05 -04:00
// ValidateNodeUpdate tests to make sure a node update can be applied. Modifies oldNode.
2017-10-09 11:58:37 -04:00
func ValidateNodeUpdate ( node , oldNode * core . Node ) field . ErrorList {
2016-03-30 23:42:57 -04:00
fldPath := field . NewPath ( "metadata" )
allErrs := ValidateObjectMetaUpdate ( & node . ObjectMeta , & oldNode . ObjectMeta , fldPath )
allErrs = append ( allErrs , ValidateNodeSpecificAnnotations ( node . ObjectMeta . Annotations , fldPath . Child ( "annotations" ) ) ... )
2014-12-12 00:39:56 -05:00
2017-10-09 11:58:37 -04:00
// TODO: Enable the code once we have better core object.status update model. Currently,
2015-01-16 17:28:20 -05:00
// anyone can update node status.
2017-10-09 11:58:37 -04:00
// if !apiequality.Semantic.DeepEqual(node.Status, core.NodeStatus{}) {
2015-11-14 11:44:50 -05:00
// allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty"))
2015-01-16 17:28:20 -05:00
// }
2014-12-12 00:39:56 -05:00
2017-08-30 14:23:26 -04:00
allErrs = append ( allErrs , ValidateNodeResources ( node ) ... )
2016-09-26 11:11:31 -04:00
2017-04-28 18:08:57 -04:00
// Validate no duplicate addresses in node status.
2017-10-09 11:58:37 -04:00
addresses := make ( map [ core . NodeAddress ] bool )
2015-11-04 16:52:14 -05:00
for i , address := range node . Status . Addresses {
2015-04-22 13:55:05 -04:00
if _ , ok := addresses [ address ] ; ok {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Duplicate ( field . NewPath ( "status" , "addresses" ) . Index ( i ) , address ) )
2015-04-22 13:55:05 -04:00
}
addresses [ address ] = true
}
2016-01-30 22:49:31 -05:00
if len ( oldNode . Spec . PodCIDR ) == 0 {
// Allow the controller manager to assign a CIDR to a node if it doesn't have one.
oldNode . Spec . PodCIDR = node . Spec . PodCIDR
} else {
if oldNode . Spec . PodCIDR != node . Spec . PodCIDR {
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "spec" , "podCIDR" ) , "node updates may not change podCIDR except from \"\" to valid" ) )
}
}
2017-08-31 23:01:26 -04:00
// Allow controller manager updating provider ID when not set
if len ( oldNode . Spec . ProviderID ) == 0 {
oldNode . Spec . ProviderID = node . Spec . ProviderID
} else {
if oldNode . Spec . ProviderID != node . Spec . ProviderID {
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "spec" , "providerID" ) , "node updates may not change providerID except from \"\" to valid" ) )
}
}
2018-12-29 13:19:37 -05:00
if node . Spec . ConfigSource != nil {
allErrs = append ( allErrs , validateNodeConfigSourceSpec ( node . Spec . ConfigSource , field . NewPath ( "spec" , "configSource" ) ) ... )
}
oldNode . Spec . ConfigSource = node . Spec . ConfigSource
if node . Status . Config != nil {
allErrs = append ( allErrs , validateNodeConfigStatus ( node . Status . Config , field . NewPath ( "status" , "config" ) ) ... )
2018-04-27 15:38:27 -04:00
}
2018-12-29 13:19:37 -05:00
oldNode . Status . Config = node . Status . Config
2018-04-27 15:38:27 -04:00
2015-01-27 18:55:54 -05:00
// TODO: move reset function to its own location
// Ignore metadata changes now that they have been tested
2015-04-22 13:55:05 -04:00
oldNode . ObjectMeta = node . ObjectMeta
2015-01-27 18:55:54 -05:00
// Allow users to update capacity
2015-04-22 13:55:05 -04:00
oldNode . Status . Capacity = node . Status . Capacity
2015-02-17 15:03:14 -05:00
// Allow users to unschedule node
2015-04-22 13:55:05 -04:00
oldNode . Spec . Unschedulable = node . Spec . Unschedulable
2014-12-22 14:04:57 -05:00
// Clear status
2015-04-22 13:55:05 -04:00
oldNode . Status = node . Status
2014-12-12 00:39:56 -05:00
2017-02-20 11:43:05 -05:00
// update taints
if len ( node . Spec . Taints ) > 0 {
allErrs = append ( allErrs , validateNodeTaints ( node . Spec . Taints , fldPath . Child ( "taints" ) ) ... )
}
oldNode . Spec . Taints = node . Spec . Taints
2017-04-28 18:08:57 -04:00
// We made allowed changes to oldNode, and now we compare oldNode to node. Any remaining differences indicate changes to protected fields.
2015-11-03 16:35:11 -05:00
// TODO: Add a 'real' error type for this error and provide print actual diffs.
2017-01-25 08:39:54 -05:00
if ! apiequality . Semantic . DeepEqual ( oldNode , node ) {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Update failed validation %#v vs %#v" , oldNode , node )
2017-04-28 18:08:57 -04:00
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "" ) , "node updates may only change labels, taints, or capacity (or configSource, if the DynamicKubeletConfig feature gate is enabled)" ) )
2014-11-17 13:22:27 -05:00
}
2015-01-27 18:55:54 -05:00
2014-11-17 13:22:27 -05:00
return allErrs
}
2015-01-16 19:34:47 -05:00
2018-01-29 12:32:48 -05:00
// validation specific to Node.Spec.ConfigSource
func validateNodeConfigSourceSpec ( source * core . NodeConfigSource , fldPath * field . Path ) field . ErrorList {
2018-02-20 14:28:28 -05:00
allErrs := field . ErrorList { }
count := int ( 0 )
2018-01-29 12:32:48 -05:00
if source . ConfigMap != nil {
2018-02-20 14:28:28 -05:00
count ++
2018-01-29 12:32:48 -05:00
allErrs = append ( allErrs , validateConfigMapNodeConfigSourceSpec ( source . ConfigMap , fldPath . Child ( "configMap" ) ) ... )
2018-02-20 14:28:28 -05:00
}
// add more subfields here in the future as they are added to NodeConfigSource
// exactly one reference subfield must be non-nil
if count != 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath , source , "exactly one reference subfield must be non-nil" ) )
}
return allErrs
}
2018-01-29 12:32:48 -05:00
// validation specific to Node.Spec.ConfigSource.ConfigMap
func validateConfigMapNodeConfigSourceSpec ( source * core . ConfigMapNodeConfigSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2018-04-16 18:15:03 -04:00
// uid and resourceVersion must not be set in spec
if string ( source . UID ) != "" {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "uid" ) , "uid must not be set in spec" ) )
2018-01-29 12:32:48 -05:00
}
if source . ResourceVersion != "" {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "resourceVersion" ) , "resourceVersion must not be set in spec" ) )
}
return append ( allErrs , validateConfigMapNodeConfigSource ( source , fldPath ) ... )
}
2018-04-27 15:38:27 -04:00
// validation specififc to Node.Status.Config
func validateNodeConfigStatus ( status * core . NodeConfigStatus , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if status . Assigned != nil {
allErrs = append ( allErrs , validateNodeConfigSourceStatus ( status . Assigned , fldPath . Child ( "assigned" ) ) ... )
}
if status . Active != nil {
allErrs = append ( allErrs , validateNodeConfigSourceStatus ( status . Active , fldPath . Child ( "active" ) ) ... )
}
if status . LastKnownGood != nil {
allErrs = append ( allErrs , validateNodeConfigSourceStatus ( status . LastKnownGood , fldPath . Child ( "lastKnownGood" ) ) ... )
}
return allErrs
}
// validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood)
func validateNodeConfigSourceStatus ( source * core . NodeConfigSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
count := int ( 0 )
if source . ConfigMap != nil {
count ++
allErrs = append ( allErrs , validateConfigMapNodeConfigSourceStatus ( source . ConfigMap , fldPath . Child ( "configMap" ) ) ... )
}
// add more subfields here in the future as they are added to NodeConfigSource
// exactly one reference subfield must be non-nil
if count != 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath , source , "exactly one reference subfield must be non-nil" ) )
}
return allErrs
}
// validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood).ConfigMap
func validateConfigMapNodeConfigSourceStatus ( source * core . ConfigMapNodeConfigSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2018-04-16 18:15:03 -04:00
// uid and resourceVersion must be set in status
2018-04-27 15:38:27 -04:00
if string ( source . UID ) == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "uid" ) , "uid must be set in status" ) )
}
2018-04-16 18:15:03 -04:00
if source . ResourceVersion == "" {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "resourceVersion" ) , "resourceVersion must be set in status" ) )
}
2018-04-27 15:38:27 -04:00
return append ( allErrs , validateConfigMapNodeConfigSource ( source , fldPath ) ... )
}
2018-01-29 12:32:48 -05:00
// common validation
func validateConfigMapNodeConfigSource ( source * core . ConfigMapNodeConfigSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
// validate target configmap namespace
if source . Namespace == "" {
2018-04-27 15:38:27 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "namespace" ) , "namespace must be set" ) )
2018-01-29 12:32:48 -05:00
} else {
for _ , msg := range ValidateNameFunc ( ValidateNamespaceName ) ( source . Namespace , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "namespace" ) , source . Namespace , msg ) )
}
}
// validate target configmap name
if source . Name == "" {
2018-04-27 15:38:27 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "name must be set" ) )
2018-01-29 12:32:48 -05:00
} else {
for _ , msg := range ValidateNameFunc ( ValidateConfigMapName ) ( source . Name , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , source . Name , msg ) )
}
}
// validate kubeletConfigKey against rules for configMap key names
if source . KubeletConfigKey == "" {
2018-04-27 15:38:27 -04:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "kubeletConfigKey" ) , "kubeletConfigKey must be set" ) )
2018-01-29 12:32:48 -05:00
} else {
for _ , msg := range validation . IsConfigMapKey ( source . KubeletConfigKey ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "kubeletConfigKey" ) , source . KubeletConfigKey , msg ) )
}
}
return allErrs
}
2015-01-24 23:19:36 -05:00
// Validate compute resource typename.
2015-09-17 06:26:01 -04:00
// Refer to docs/design/resources.md for more details.
2015-11-06 18:30:52 -05:00
func validateResourceName ( value string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-16 01:03:20 -05:00
for _ , msg := range validation . IsQualifiedName ( value ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , value , msg ) )
2015-12-16 00:28:42 -05:00
}
if len ( allErrs ) != 0 {
return allErrs
2015-01-16 19:34:47 -05:00
}
2015-02-04 19:36:27 -05:00
if len ( strings . Split ( value , "/" ) ) == 1 {
2017-04-10 13:49:54 -04:00
if ! helper . IsStandardResourceName ( value ) {
2015-11-14 15:26:04 -05:00
return append ( allErrs , field . Invalid ( fldPath , value , "must be a standard resource type or fully qualified" ) )
2015-01-16 19:34:47 -05:00
}
}
2017-08-24 04:32:12 -04:00
return allErrs
2015-01-16 19:34:47 -05:00
}
2015-01-22 16:52:40 -05:00
2016-02-22 11:14:11 -05:00
// Validate container resource name
// Refer to docs/design/resources.md for more details.
func validateContainerResourceName ( value string , fldPath * field . Path ) field . ErrorList {
allErrs := validateResourceName ( value , fldPath )
2017-05-30 16:54:15 -04:00
2016-02-22 11:14:11 -05:00
if len ( strings . Split ( value , "/" ) ) == 1 {
2017-04-10 13:49:54 -04:00
if ! helper . IsStandardContainerResourceName ( value ) {
2016-02-22 11:14:11 -05:00
return append ( allErrs , field . Invalid ( fldPath , value , "must be a standard resource for containers" ) )
}
2018-03-28 16:09:23 -04:00
} else if ! helper . IsNativeResource ( core . ResourceName ( value ) ) {
2018-01-29 20:29:22 -05:00
if ! helper . IsExtendedResourceName ( core . ResourceName ( value ) ) {
return append ( allErrs , field . Invalid ( fldPath , value , "doesn't follow extended resource name standard" ) )
}
2016-02-22 11:14:11 -05:00
}
2017-08-24 04:32:12 -04:00
return allErrs
2016-02-22 11:14:11 -05:00
}
// Validate resource names that can go in a resource quota
// Refer to docs/design/resources.md for more details.
2016-05-25 13:43:23 -04:00
func ValidateResourceQuotaResourceName ( value string , fldPath * field . Path ) field . ErrorList {
2016-02-22 11:14:11 -05:00
allErrs := validateResourceName ( value , fldPath )
2018-12-27 10:30:37 -05:00
2016-02-22 11:14:11 -05:00
if len ( strings . Split ( value , "/" ) ) == 1 {
2017-04-10 13:49:54 -04:00
if ! helper . IsStandardQuotaResourceName ( value ) {
2016-02-22 11:14:11 -05:00
return append ( allErrs , field . Invalid ( fldPath , value , isInvalidQuotaResource ) )
}
}
2017-08-24 04:32:12 -04:00
return allErrs
2016-02-22 11:14:11 -05:00
}
2016-03-09 18:11:26 -05:00
// Validate limit range types
func validateLimitRangeTypeName ( value string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-16 01:03:20 -05:00
for _ , msg := range validation . IsQualifiedName ( value ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , value , msg ) )
2015-12-16 00:28:42 -05:00
}
if len ( allErrs ) != 0 {
return allErrs
2016-03-09 18:11:26 -05:00
}
if len ( strings . Split ( value , "/" ) ) == 1 {
2017-04-10 13:49:54 -04:00
if ! helper . IsStandardLimitRangeType ( value ) {
2016-03-09 18:11:26 -05:00
return append ( allErrs , field . Invalid ( fldPath , value , "must be a standard limit type or fully qualified" ) )
}
}
return allErrs
}
// Validate limit range resource name
// limit types (other than Pod/Container) could contain storage not just cpu or memory
2017-10-09 11:58:37 -04:00
func validateLimitRangeResourceName ( limitType core . LimitType , value string , fldPath * field . Path ) field . ErrorList {
2016-03-09 18:11:26 -05:00
switch limitType {
2017-10-09 11:58:37 -04:00
case core . LimitTypePod , core . LimitTypeContainer :
2016-03-09 18:11:26 -05:00
return validateContainerResourceName ( value , fldPath )
default :
return validateResourceName ( value , fldPath )
}
}
2015-01-22 16:52:40 -05:00
// ValidateLimitRange tests if required fields in the LimitRange are set.
2017-10-09 11:58:37 -04:00
func ValidateLimitRange ( limitRange * core . LimitRange ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & limitRange . ObjectMeta , true , ValidateLimitRangeName , field . NewPath ( "metadata" ) )
2015-02-20 01:03:36 -05:00
2015-09-17 06:26:01 -04:00
// ensure resource names are properly qualified per docs/design/resources.md
2017-10-09 11:58:37 -04:00
limitTypeSet := map [ core . LimitType ] bool { }
2015-11-06 18:30:52 -05:00
fldPath := field . NewPath ( "spec" , "limits" )
2015-01-22 16:52:40 -05:00
for i := range limitRange . Spec . Limits {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
limit := & limitRange . Spec . Limits [ i ]
2016-03-09 18:11:26 -05:00
allErrs = append ( allErrs , validateLimitRangeTypeName ( string ( limit . Type ) , idxPath . Child ( "type" ) ) ... )
2015-06-16 16:16:34 -04:00
_ , found := limitTypeSet [ limit . Type ]
if found {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "type" ) , limit . Type ) )
2015-06-16 16:16:34 -04:00
}
limitTypeSet [ limit . Type ] = true
2015-09-09 13:45:01 -04:00
keys := sets . String { }
2015-09-08 14:49:54 -04:00
min := map [ string ] resource . Quantity { }
max := map [ string ] resource . Quantity { }
defaults := map [ string ] resource . Quantity { }
defaultRequests := map [ string ] resource . Quantity { }
2015-09-11 03:38:38 -04:00
maxLimitRequestRatios := map [ string ] resource . Quantity { }
2015-06-16 16:16:34 -04:00
2015-09-08 14:49:54 -04:00
for k , q := range limit . Max {
2016-03-09 18:11:26 -05:00
allErrs = append ( allErrs , validateLimitRangeResourceName ( limit . Type , string ( k ) , idxPath . Child ( "max" ) . Key ( string ( k ) ) ) ... )
2015-06-16 16:16:34 -04:00
keys . Insert ( string ( k ) )
2015-09-08 14:49:54 -04:00
max [ string ( k ) ] = q
2015-01-22 16:52:40 -05:00
}
2015-09-08 14:49:54 -04:00
for k , q := range limit . Min {
2016-03-09 18:11:26 -05:00
allErrs = append ( allErrs , validateLimitRangeResourceName ( limit . Type , string ( k ) , idxPath . Child ( "min" ) . Key ( string ( k ) ) ) ... )
2015-06-16 16:16:34 -04:00
keys . Insert ( string ( k ) )
2015-09-08 14:49:54 -04:00
min [ string ( k ) ] = q
2015-06-16 16:16:34 -04:00
}
2015-09-10 16:39:59 -04:00
2017-10-09 11:58:37 -04:00
if limit . Type == core . LimitTypePod {
2015-09-10 16:39:59 -04:00
if len ( limit . Default ) > 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Forbidden ( idxPath . Child ( "default" ) , "may not be specified when `type` is 'Pod'" ) )
2015-09-10 16:39:59 -04:00
}
if len ( limit . DefaultRequest ) > 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Forbidden ( idxPath . Child ( "defaultRequest" ) , "may not be specified when `type` is 'Pod'" ) )
2015-09-10 16:39:59 -04:00
}
} else {
for k , q := range limit . Default {
2016-03-09 18:11:26 -05:00
allErrs = append ( allErrs , validateLimitRangeResourceName ( limit . Type , string ( k ) , idxPath . Child ( "default" ) . Key ( string ( k ) ) ) ... )
2015-09-10 16:39:59 -04:00
keys . Insert ( string ( k ) )
defaults [ string ( k ) ] = q
}
for k , q := range limit . DefaultRequest {
2016-03-09 18:11:26 -05:00
allErrs = append ( allErrs , validateLimitRangeResourceName ( limit . Type , string ( k ) , idxPath . Child ( "defaultRequest" ) . Key ( string ( k ) ) ) ... )
2015-09-10 16:39:59 -04:00
keys . Insert ( string ( k ) )
defaultRequests [ string ( k ) ] = q
}
2015-08-28 12:26:36 -04:00
}
2015-09-10 16:39:59 -04:00
2017-10-09 11:58:37 -04:00
if limit . Type == core . LimitTypePersistentVolumeClaim {
_ , minQuantityFound := limit . Min [ core . ResourceStorage ]
_ , maxQuantityFound := limit . Max [ core . ResourceStorage ]
2016-10-24 11:01:18 -04:00
if ! minQuantityFound && ! maxQuantityFound {
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "limits" ) , "either minimum or maximum storage value is required, but neither was provided" ) )
2016-08-15 10:19:15 -04:00
}
}
2015-09-11 03:38:38 -04:00
for k , q := range limit . MaxLimitRequestRatio {
2016-03-09 18:11:26 -05:00
allErrs = append ( allErrs , validateLimitRangeResourceName ( limit . Type , string ( k ) , idxPath . Child ( "maxLimitRequestRatio" ) . Key ( string ( k ) ) ) ... )
2015-09-11 03:38:38 -04:00
keys . Insert ( string ( k ) )
maxLimitRequestRatios [ string ( k ) ] = q
2015-08-28 12:26:36 -04:00
}
2015-06-16 16:16:34 -04:00
for k := range keys {
2015-09-08 14:49:54 -04:00
minQuantity , minQuantityFound := min [ k ]
maxQuantity , maxQuantityFound := max [ k ]
defaultQuantity , defaultQuantityFound := defaults [ k ]
defaultRequestQuantity , defaultRequestQuantityFound := defaultRequests [ k ]
2015-09-11 03:38:38 -04:00
maxRatio , maxRatioFound := maxLimitRequestRatios [ k ]
2015-06-16 16:16:34 -04:00
2015-09-08 14:49:54 -04:00
if minQuantityFound && maxQuantityFound && minQuantity . Cmp ( maxQuantity ) > 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "min" ) . Key ( string ( k ) ) , minQuantity , fmt . Sprintf ( "min value %s is greater than max value %s" , minQuantity . String ( ) , maxQuantity . String ( ) ) ) )
2015-08-28 12:26:36 -04:00
}
2015-09-08 14:49:54 -04:00
if defaultRequestQuantityFound && minQuantityFound && minQuantity . Cmp ( defaultRequestQuantity ) > 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "defaultRequest" ) . Key ( string ( k ) ) , defaultRequestQuantity , fmt . Sprintf ( "min value %s is greater than default request value %s" , minQuantity . String ( ) , defaultRequestQuantity . String ( ) ) ) )
2015-08-28 12:26:36 -04:00
}
2015-09-08 14:49:54 -04:00
if defaultRequestQuantityFound && maxQuantityFound && defaultRequestQuantity . Cmp ( maxQuantity ) > 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "defaultRequest" ) . Key ( string ( k ) ) , defaultRequestQuantity , fmt . Sprintf ( "default request value %s is greater than max value %s" , defaultRequestQuantity . String ( ) , maxQuantity . String ( ) ) ) )
2015-08-28 12:26:36 -04:00
}
2015-09-08 14:49:54 -04:00
if defaultRequestQuantityFound && defaultQuantityFound && defaultRequestQuantity . Cmp ( defaultQuantity ) > 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "defaultRequest" ) . Key ( string ( k ) ) , defaultRequestQuantity , fmt . Sprintf ( "default request value %s is greater than default limit value %s" , defaultRequestQuantity . String ( ) , defaultQuantity . String ( ) ) ) )
2015-06-16 16:16:34 -04:00
}
2015-09-08 14:49:54 -04:00
if defaultQuantityFound && minQuantityFound && minQuantity . Cmp ( defaultQuantity ) > 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "default" ) . Key ( string ( k ) ) , minQuantity , fmt . Sprintf ( "min value %s is greater than default value %s" , minQuantity . String ( ) , defaultQuantity . String ( ) ) ) )
2015-06-16 16:16:34 -04:00
}
2015-09-08 14:49:54 -04:00
if defaultQuantityFound && maxQuantityFound && defaultQuantity . Cmp ( maxQuantity ) > 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "default" ) . Key ( string ( k ) ) , maxQuantity , fmt . Sprintf ( "default value %s is greater than max value %s" , defaultQuantity . String ( ) , maxQuantity . String ( ) ) ) )
2015-06-16 16:16:34 -04:00
}
2015-09-11 03:38:38 -04:00
if maxRatioFound && maxRatio . Cmp ( * resource . NewQuantity ( 1 , resource . DecimalSI ) ) < 0 {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "maxLimitRequestRatio" ) . Key ( string ( k ) ) , maxRatio , fmt . Sprintf ( "ratio %s is less than 1" , maxRatio . String ( ) ) ) )
2015-09-11 03:38:38 -04:00
}
if maxRatioFound && minQuantityFound && maxQuantityFound {
maxRatioValue := float64 ( maxRatio . Value ( ) )
minQuantityValue := minQuantity . Value ( )
maxQuantityValue := maxQuantity . Value ( )
if maxRatio . Value ( ) < resource . MaxMilliValue && minQuantityValue < resource . MaxMilliValue && maxQuantityValue < resource . MaxMilliValue {
maxRatioValue = float64 ( maxRatio . MilliValue ( ) ) / 1000
minQuantityValue = minQuantity . MilliValue ( )
maxQuantityValue = maxQuantity . MilliValue ( )
}
maxRatioLimit := float64 ( maxQuantityValue ) / float64 ( minQuantityValue )
if maxRatioValue > maxRatioLimit {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "maxLimitRequestRatio" ) . Key ( string ( k ) ) , maxRatio , fmt . Sprintf ( "ratio %s is greater than max/min = %f" , maxRatio . String ( ) , maxRatioLimit ) ) )
2015-09-11 03:38:38 -04:00
}
}
2017-11-01 04:08:33 -04:00
2018-01-29 20:29:22 -05:00
// for GPU, hugepages and other resources that are not allowed to overcommit,
// the default value and defaultRequest value must match if both are specified
2017-10-09 11:58:37 -04:00
if ! helper . IsOvercommitAllowed ( core . ResourceName ( k ) ) && defaultQuantityFound && defaultRequestQuantityFound && defaultQuantity . Cmp ( defaultRequestQuantity ) != 0 {
2017-11-01 04:08:33 -04:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "defaultRequest" ) . Key ( string ( k ) ) , defaultRequestQuantity , fmt . Sprintf ( "default value %s must equal to defaultRequest value %s in %s" , defaultQuantity . String ( ) , defaultRequestQuantity . String ( ) , k ) ) )
}
2015-01-24 23:19:36 -05:00
}
}
2015-06-16 16:16:34 -04:00
2015-01-24 23:19:36 -05:00
return allErrs
}
2015-04-27 18:53:28 -04:00
// ValidateServiceAccount tests if required fields in the ServiceAccount are set.
2017-10-09 11:58:37 -04:00
func ValidateServiceAccount ( serviceAccount * core . ServiceAccount ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & serviceAccount . ObjectMeta , true , ValidateServiceAccountName , field . NewPath ( "metadata" ) )
2015-04-27 18:53:28 -04:00
return allErrs
}
// ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set.
2017-10-09 11:58:37 -04:00
func ValidateServiceAccountUpdate ( newServiceAccount , oldServiceAccount * core . ServiceAccount ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newServiceAccount . ObjectMeta , & oldServiceAccount . ObjectMeta , field . NewPath ( "metadata" ) )
2015-04-27 18:53:28 -04:00
allErrs = append ( allErrs , ValidateServiceAccount ( newServiceAccount ) ... )
return allErrs
}
2015-02-17 20:24:50 -05:00
// ValidateSecret tests if required fields in the Secret are set.
2017-10-09 11:58:37 -04:00
func ValidateSecret ( secret * core . Secret ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & secret . ObjectMeta , true , ValidateSecretName , field . NewPath ( "metadata" ) )
2015-02-17 20:24:50 -05:00
2015-11-06 18:30:52 -05:00
dataPath := field . NewPath ( "data" )
2015-02-17 20:24:50 -05:00
totalSize := 0
2015-02-17 20:26:41 -05:00
for key , value := range secret . Data {
2016-04-28 23:34:48 -04:00
for _ , msg := range validation . IsConfigMapKey ( key ) {
allErrs = append ( allErrs , field . Invalid ( dataPath . Key ( key ) , key , msg ) )
2015-02-17 20:26:41 -05:00
}
2015-02-17 20:24:50 -05:00
totalSize += len ( value )
}
2017-10-09 11:58:37 -04:00
if totalSize > core . MaxSecretSize {
allErrs = append ( allErrs , field . TooLong ( dataPath , "" , core . MaxSecretSize ) )
2015-02-17 20:24:50 -05:00
}
2015-04-27 23:50:56 -04:00
switch secret . Type {
2017-10-09 11:58:37 -04:00
case core . SecretTypeServiceAccountToken :
2015-04-27 23:51:20 -04:00
// Only require Annotations[kubernetes.io/service-account.name]
// Additional fields (like Annotations[kubernetes.io/service-account.uid] and Data[token]) might be contributed later by a controller loop
2017-10-09 11:58:37 -04:00
if value := secret . Annotations [ core . ServiceAccountNameKey ] ; len ( value ) == 0 {
allErrs = append ( allErrs , field . Required ( field . NewPath ( "metadata" , "annotations" ) . Key ( core . ServiceAccountNameKey ) , "" ) )
2015-04-27 23:51:20 -04:00
}
2017-10-09 11:58:37 -04:00
case core . SecretTypeOpaque , "" :
2015-05-20 11:59:34 -04:00
// no-op
2017-10-09 11:58:37 -04:00
case core . SecretTypeDockercfg :
dockercfgBytes , exists := secret . Data [ core . DockerConfigKey ]
2015-05-06 10:09:18 -04:00
if ! exists {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Required ( dataPath . Key ( core . DockerConfigKey ) , "" ) )
2015-05-06 10:09:18 -04:00
break
}
// make sure that the content is well-formed json.
if err := json . Unmarshal ( dockercfgBytes , & map [ string ] interface { } { } ) ; err != nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Invalid ( dataPath . Key ( core . DockerConfigKey ) , "<secret contents redacted>" , err . Error ( ) ) )
2015-05-06 10:09:18 -04:00
}
2017-10-09 11:58:37 -04:00
case core . SecretTypeDockerConfigJson :
dockerConfigJsonBytes , exists := secret . Data [ core . DockerConfigJsonKey ]
2015-12-16 17:13:18 -05:00
if ! exists {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Required ( dataPath . Key ( core . DockerConfigJsonKey ) , "" ) )
2015-12-16 17:13:18 -05:00
break
}
// make sure that the content is well-formed json.
if err := json . Unmarshal ( dockerConfigJsonBytes , & map [ string ] interface { } { } ) ; err != nil {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Invalid ( dataPath . Key ( core . DockerConfigJsonKey ) , "<secret contents redacted>" , err . Error ( ) ) )
2015-12-16 17:13:18 -05:00
}
2017-10-09 11:58:37 -04:00
case core . SecretTypeBasicAuth :
_ , usernameFieldExists := secret . Data [ core . BasicAuthUsernameKey ]
_ , passwordFieldExists := secret . Data [ core . BasicAuthPasswordKey ]
2015-09-10 11:30:58 -04:00
// username or password might be empty, but the field must be present
if ! usernameFieldExists && ! passwordFieldExists {
2017-10-09 11:58:37 -04:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "data[%s]" ) . Key ( core . BasicAuthUsernameKey ) , "" ) )
allErrs = append ( allErrs , field . Required ( field . NewPath ( "data[%s]" ) . Key ( core . BasicAuthPasswordKey ) , "" ) )
2015-09-10 11:30:58 -04:00
break
}
2017-10-09 11:58:37 -04:00
case core . SecretTypeSSHAuth :
if len ( secret . Data [ core . SSHAuthPrivateKey ] ) == 0 {
allErrs = append ( allErrs , field . Required ( field . NewPath ( "data[%s]" ) . Key ( core . SSHAuthPrivateKey ) , "" ) )
2015-09-10 11:30:58 -04:00
break
}
2015-05-06 10:09:18 -04:00
2017-10-09 11:58:37 -04:00
case core . SecretTypeTLS :
if _ , exists := secret . Data [ core . TLSCertKey ] ; ! exists {
allErrs = append ( allErrs , field . Required ( dataPath . Key ( core . TLSCertKey ) , "" ) )
2016-01-16 19:06:40 -05:00
}
2017-10-09 11:58:37 -04:00
if _ , exists := secret . Data [ core . TLSPrivateKeyKey ] ; ! exists {
allErrs = append ( allErrs , field . Required ( dataPath . Key ( core . TLSPrivateKeyKey ) , "" ) )
2016-01-16 19:06:40 -05:00
}
2016-04-14 13:45:29 -04:00
// TODO: Verify that the key matches the cert.
2015-04-27 23:50:56 -04:00
default :
// no-op
}
return allErrs
}
// ValidateSecretUpdate tests if required fields in the Secret are set.
2017-10-09 11:58:37 -04:00
func ValidateSecretUpdate ( newSecret , oldSecret * core . Secret ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newSecret . ObjectMeta , & oldSecret . ObjectMeta , field . NewPath ( "metadata" ) )
2015-04-27 23:50:56 -04:00
if len ( newSecret . Type ) == 0 {
newSecret . Type = oldSecret . Type
}
2015-10-11 23:26:17 -04:00
2015-11-06 18:30:52 -05:00
allErrs = append ( allErrs , ValidateImmutableField ( newSecret . Type , oldSecret . Type , field . NewPath ( "type" ) ) ... )
2015-04-27 23:50:56 -04:00
allErrs = append ( allErrs , ValidateSecret ( newSecret ) ... )
2015-02-17 20:24:50 -05:00
return allErrs
}
2016-01-15 11:48:36 -05:00
// ValidateConfigMapName can be used to check whether the given ConfigMap name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2018-08-11 10:17:29 -04:00
var ValidateConfigMapName = apimachineryvalidation . NameIsDNSSubdomain
2016-01-15 11:48:36 -05:00
// ValidateConfigMap tests whether required fields in the ConfigMap are set.
2017-10-09 11:58:37 -04:00
func ValidateConfigMap ( cfg * core . ConfigMap ) field . ErrorList {
2016-01-15 11:48:36 -05:00
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidateObjectMeta ( & cfg . ObjectMeta , true , ValidateConfigMapName , field . NewPath ( "metadata" ) ) ... )
2016-01-20 23:14:37 -05:00
totalSize := 0
for key , value := range cfg . Data {
2016-04-28 23:34:48 -04:00
for _ , msg := range validation . IsConfigMapKey ( key ) {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "data" ) . Key ( key ) , key , msg ) )
2016-01-15 11:48:36 -05:00
}
2016-09-23 07:48:32 -04:00
// check if we have a duplicate key in the other bag
if _ , isValue := cfg . BinaryData [ key ] ; isValue {
msg := "duplicate of key present in binaryData"
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "data" ) . Key ( key ) , key , msg ) )
}
totalSize += len ( value )
}
for key , value := range cfg . BinaryData {
for _ , msg := range validation . IsConfigMapKey ( key ) {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "binaryData" ) . Key ( key ) , key , msg ) )
}
2016-01-20 23:14:37 -05:00
totalSize += len ( value )
}
2017-10-09 11:58:37 -04:00
if totalSize > core . MaxSecretSize {
2016-09-23 07:48:32 -04:00
// pass back "" to indicate that the error refers to the whole object.
allErrs = append ( allErrs , field . TooLong ( field . NewPath ( "" ) , cfg , core . MaxSecretSize ) )
2016-01-15 11:48:36 -05:00
}
return allErrs
}
// ValidateConfigMapUpdate tests if required fields in the ConfigMap are set.
2017-10-09 11:58:37 -04:00
func ValidateConfigMapUpdate ( newCfg , oldCfg * core . ConfigMap ) field . ErrorList {
2016-01-15 11:48:36 -05:00
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newCfg . ObjectMeta , & oldCfg . ObjectMeta , field . NewPath ( "metadata" ) ) ... )
allErrs = append ( allErrs , ValidateConfigMap ( newCfg ) ... )
return allErrs
}
2015-11-06 18:30:52 -05:00
func validateBasicResource ( quantity resource . Quantity , fldPath * field . Path ) field . ErrorList {
2015-01-24 23:19:36 -05:00
if quantity . Value ( ) < 0 {
2015-11-10 15:59:41 -05:00
return field . ErrorList { field . Invalid ( fldPath , quantity . Value ( ) , "must be a valid resource quantity" ) }
2015-01-24 23:19:36 -05:00
}
2015-11-06 18:30:52 -05:00
return field . ErrorList { }
2015-01-24 23:19:36 -05:00
}
// Validates resource requirement spec.
2017-10-09 11:58:37 -04:00
func ValidateResourceRequirements ( requirements * core . ResourceRequirements , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-11-04 16:52:14 -05:00
limPath := fldPath . Child ( "limits" )
2016-04-26 20:54:19 -04:00
reqPath := fldPath . Child ( "requests" )
2019-04-01 00:13:36 -04:00
limContainsCPUOrMemory := false
reqContainsCPUOrMemory := false
2017-10-23 00:09:21 -04:00
limContainsHugePages := false
reqContainsHugePages := false
supportedQoSComputeResources := sets . NewString ( string ( core . ResourceCPU ) , string ( core . ResourceMemory ) )
2015-04-20 14:56:15 -04:00
for resourceName , quantity := range requirements . Limits {
2017-10-23 00:09:21 -04:00
2015-11-04 16:52:14 -05:00
fldPath := limPath . Key ( string ( resourceName ) )
2015-01-24 23:19:36 -05:00
// Validate resource name.
2016-02-22 11:14:11 -05:00
allErrs = append ( allErrs , validateContainerResourceName ( string ( resourceName ) , fldPath ) ... )
2016-09-26 11:11:31 -04:00
// Validate resource quantity.
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( resourceName ) , quantity , fldPath ) ... )
2017-10-23 00:09:21 -04:00
if helper . IsHugePageResourceName ( resourceName ) {
2019-02-05 17:36:24 -05:00
limContainsHugePages = true
2017-08-17 14:16:37 -04:00
}
2017-10-23 00:09:21 -04:00
if supportedQoSComputeResources . Has ( string ( resourceName ) ) {
2019-04-01 00:13:36 -04:00
limContainsCPUOrMemory = true
2017-10-23 00:09:21 -04:00
}
2015-01-22 16:52:40 -05:00
}
2015-04-20 14:56:15 -04:00
for resourceName , quantity := range requirements . Requests {
2015-11-04 16:52:14 -05:00
fldPath := reqPath . Key ( string ( resourceName ) )
2015-04-20 14:56:15 -04:00
// Validate resource name.
2016-02-22 11:14:11 -05:00
allErrs = append ( allErrs , validateContainerResourceName ( string ( resourceName ) , fldPath ) ... )
2016-09-26 11:11:31 -04:00
// Validate resource quantity.
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( resourceName ) , quantity , fldPath ) ... )
2017-08-06 22:13:11 -04:00
// Check that request <= limit.
limitQuantity , exists := requirements . Limits [ resourceName ]
if exists {
2017-12-13 20:56:51 -05:00
// For non overcommitable resources, not only requests can't exceed limits, they also can't be lower, i.e. must be equal.
2017-08-06 22:13:11 -04:00
if quantity . Cmp ( limitQuantity ) != 0 && ! helper . IsOvercommitAllowed ( resourceName ) {
2017-10-23 03:59:02 -04:00
allErrs = append ( allErrs , field . Invalid ( reqPath , quantity . String ( ) , fmt . Sprintf ( "must be equal to %s limit" , resourceName ) ) )
2017-08-06 22:13:11 -04:00
} else if quantity . Cmp ( limitQuantity ) > 0 {
allErrs = append ( allErrs , field . Invalid ( reqPath , quantity . String ( ) , fmt . Sprintf ( "must be less than or equal to %s limit" , resourceName ) ) )
}
2017-12-13 20:56:51 -05:00
} else if ! helper . IsOvercommitAllowed ( resourceName ) {
allErrs = append ( allErrs , field . Required ( limPath , "Limit must be set for non overcommitable resources" ) )
2017-08-06 22:13:11 -04:00
}
2017-10-23 00:09:21 -04:00
if helper . IsHugePageResourceName ( resourceName ) {
reqContainsHugePages = true
}
if supportedQoSComputeResources . Has ( string ( resourceName ) ) {
2019-04-01 00:13:36 -04:00
reqContainsCPUOrMemory = true
2017-10-23 00:09:21 -04:00
}
}
2019-04-01 00:13:36 -04:00
if ! limContainsCPUOrMemory && ! reqContainsCPUOrMemory && ( reqContainsHugePages || limContainsHugePages ) {
2017-10-23 00:09:21 -04:00
allErrs = append ( allErrs , field . Forbidden ( fldPath , fmt . Sprintf ( "HugePages require cpu or memory" ) ) )
2015-04-20 14:56:15 -04:00
}
2016-09-26 11:11:31 -04:00
2015-01-22 16:52:40 -05:00
return allErrs
}
2015-01-23 12:38:30 -05:00
2016-02-22 11:14:11 -05:00
// validateResourceQuotaScopes ensures that each enumerated hard resource constraint is valid for set of scopes
2017-10-09 11:58:37 -04:00
func validateResourceQuotaScopes ( resourceQuotaSpec * core . ResourceQuotaSpec , fld * field . Path ) field . ErrorList {
2016-02-22 11:14:11 -05:00
allErrs := field . ErrorList { }
2016-05-25 13:43:23 -04:00
if len ( resourceQuotaSpec . Scopes ) == 0 {
2016-02-22 11:14:11 -05:00
return allErrs
}
hardLimits := sets . NewString ( )
2016-05-25 13:43:23 -04:00
for k := range resourceQuotaSpec . Hard {
2016-02-22 11:14:11 -05:00
hardLimits . Insert ( string ( k ) )
}
2016-05-25 13:43:23 -04:00
fldPath := fld . Child ( "scopes" )
2016-02-22 11:14:11 -05:00
scopeSet := sets . NewString ( )
2016-05-25 13:43:23 -04:00
for _ , scope := range resourceQuotaSpec . Scopes {
2017-04-10 13:49:54 -04:00
if ! helper . IsStandardResourceQuotaScope ( string ( scope ) ) {
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath , resourceQuotaSpec . Scopes , "unsupported scope" ) )
2016-02-22 11:14:11 -05:00
}
for _ , k := range hardLimits . List ( ) {
2017-04-10 13:49:54 -04:00
if helper . IsStandardQuotaResourceName ( k ) && ! helper . IsResourceQuotaScopeValidForResource ( scope , k ) {
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath , resourceQuotaSpec . Scopes , "unsupported scope applied to resource" ) )
2016-02-22 11:14:11 -05:00
}
}
scopeSet . Insert ( string ( scope ) )
}
invalidScopePairs := [ ] sets . String {
2017-10-09 11:58:37 -04:00
sets . NewString ( string ( core . ResourceQuotaScopeBestEffort ) , string ( core . ResourceQuotaScopeNotBestEffort ) ) ,
sets . NewString ( string ( core . ResourceQuotaScopeTerminating ) , string ( core . ResourceQuotaScopeNotTerminating ) ) ,
2016-02-22 11:14:11 -05:00
}
for _ , invalidScopePair := range invalidScopePairs {
if scopeSet . HasAll ( invalidScopePair . List ( ) ... ) {
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , field . Invalid ( fldPath , resourceQuotaSpec . Scopes , "conflicting scopes" ) )
2016-02-22 11:14:11 -05:00
}
}
return allErrs
}
2017-12-22 06:06:29 -05:00
// validateScopedResourceSelectorRequirement tests that the match expressions has valid data
func validateScopedResourceSelectorRequirement ( resourceQuotaSpec * core . ResourceQuotaSpec , fld * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
hardLimits := sets . NewString ( )
for k := range resourceQuotaSpec . Hard {
hardLimits . Insert ( string ( k ) )
}
fldPath := fld . Child ( "matchExpressions" )
scopeSet := sets . NewString ( )
for _ , req := range resourceQuotaSpec . ScopeSelector . MatchExpressions {
if ! helper . IsStandardResourceQuotaScope ( string ( req . ScopeName ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "scopeName" ) , req . ScopeName , "unsupported scope" ) )
}
for _ , k := range hardLimits . List ( ) {
if helper . IsStandardQuotaResourceName ( k ) && ! helper . IsResourceQuotaScopeValidForResource ( req . ScopeName , k ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , resourceQuotaSpec . ScopeSelector , "unsupported scope applied to resource" ) )
}
}
switch req . ScopeName {
case core . ResourceQuotaScopeBestEffort , core . ResourceQuotaScopeNotBestEffort , core . ResourceQuotaScopeTerminating , core . ResourceQuotaScopeNotTerminating :
if req . Operator != core . ScopeSelectorOpExists {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "operator" ) , req . Operator ,
"must be 'Exist' only operator when scope is any of ResourceQuotaScopeTerminating, ResourceQuotaScopeNotTerminating, ResourceQuotaScopeBestEffort and ResourceQuotaScopeNotBestEffort" ) )
}
}
switch req . Operator {
case core . ScopeSelectorOpIn , core . ScopeSelectorOpNotIn :
if len ( req . Values ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "values" ) ,
2018-08-28 05:09:07 -04:00
"must be at least one value when `operator` is 'In' or 'NotIn' for scope selector" ) )
2017-12-22 06:06:29 -05:00
}
case core . ScopeSelectorOpExists , core . ScopeSelectorOpDoesNotExist :
if len ( req . Values ) != 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "values" ) , req . Values ,
"must be no value when `operator` is 'Exist' or 'DoesNotExist' for scope selector" ) )
}
default :
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "operator" ) , req . Operator , "not a valid selector operator" ) )
}
scopeSet . Insert ( string ( req . ScopeName ) )
}
invalidScopePairs := [ ] sets . String {
sets . NewString ( string ( core . ResourceQuotaScopeBestEffort ) , string ( core . ResourceQuotaScopeNotBestEffort ) ) ,
sets . NewString ( string ( core . ResourceQuotaScopeTerminating ) , string ( core . ResourceQuotaScopeNotTerminating ) ) ,
}
for _ , invalidScopePair := range invalidScopePairs {
if scopeSet . HasAll ( invalidScopePair . List ( ) ... ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , resourceQuotaSpec . Scopes , "conflicting scopes" ) )
}
}
return allErrs
}
// validateScopeSelector tests that the specified scope selector has valid data
func validateScopeSelector ( resourceQuotaSpec * core . ResourceQuotaSpec , fld * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if resourceQuotaSpec . ScopeSelector == nil {
return allErrs
}
allErrs = append ( allErrs , validateScopedResourceSelectorRequirement ( resourceQuotaSpec , fld . Child ( "scopeSelector" ) ) ... )
return allErrs
}
2015-01-23 12:38:30 -05:00
// ValidateResourceQuota tests if required fields in the ResourceQuota are set.
2017-10-09 11:58:37 -04:00
func ValidateResourceQuota ( resourceQuota * core . ResourceQuota ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & resourceQuota . ObjectMeta , true , ValidateResourceQuotaName , field . NewPath ( "metadata" ) )
2015-02-20 01:03:36 -05:00
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , ValidateResourceQuotaSpec ( & resourceQuota . Spec , field . NewPath ( "spec" ) ) ... )
allErrs = append ( allErrs , ValidateResourceQuotaStatus ( & resourceQuota . Status , field . NewPath ( "status" ) ) ... )
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateResourceQuotaStatus ( status * core . ResourceQuotaStatus , fld * field . Path ) field . ErrorList {
2016-05-25 13:43:23 -04:00
allErrs := field . ErrorList { }
fldPath := fld . Child ( "hard" )
for k , v := range status . Hard {
2015-11-04 16:52:14 -05:00
resPath := fldPath . Key ( string ( k ) )
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , ValidateResourceQuotaResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-01-23 12:38:30 -05:00
}
2016-05-25 13:43:23 -04:00
fldPath = fld . Child ( "used" )
for k , v := range status . Used {
2015-11-04 16:52:14 -05:00
resPath := fldPath . Key ( string ( k ) )
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , ValidateResourceQuotaResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-01-23 12:38:30 -05:00
}
2016-05-25 13:43:23 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func ValidateResourceQuotaSpec ( resourceQuotaSpec * core . ResourceQuotaSpec , fld * field . Path ) field . ErrorList {
2016-05-25 13:43:23 -04:00
allErrs := field . ErrorList { }
fldPath := fld . Child ( "hard" )
for k , v := range resourceQuotaSpec . Hard {
2015-11-04 16:52:14 -05:00
resPath := fldPath . Key ( string ( k ) )
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , ValidateResourceQuotaResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-10-13 17:27:56 -04:00
}
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , validateResourceQuotaScopes ( resourceQuotaSpec , fld ) ... )
2017-12-22 06:06:29 -05:00
allErrs = append ( allErrs , validateScopeSelector ( resourceQuotaSpec , fld ) ... )
2016-02-22 11:14:11 -05:00
2015-10-13 17:27:56 -04:00
return allErrs
}
2016-05-25 13:43:23 -04:00
// ValidateResourceQuantityValue enforces that specified quantity is valid for specified resource
func ValidateResourceQuantityValue ( resource string , value resource . Quantity , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-10-08 16:02:23 -04:00
allErrs = append ( allErrs , ValidateNonnegativeQuantity ( value , fldPath ) ... )
2017-04-10 13:49:54 -04:00
if helper . IsIntegerResourceName ( resource ) {
2015-10-13 17:27:56 -04:00
if value . MilliValue ( ) % int64 ( 1000 ) != int64 ( 0 ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , value , isNotIntegerErrorMsg ) )
2015-10-13 17:27:56 -04:00
}
2015-01-23 12:38:30 -05:00
}
return allErrs
}
2015-01-19 16:50:00 -05:00
2015-03-13 15:15:04 -04:00
// ValidateResourceQuotaUpdate tests to see if the update is legal for an end user to make.
// newResourceQuota is updated with fields that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidateResourceQuotaUpdate ( newResourceQuota , oldResourceQuota * core . ResourceQuota ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newResourceQuota . ObjectMeta , & oldResourceQuota . ObjectMeta , field . NewPath ( "metadata" ) )
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , ValidateResourceQuotaSpec ( & newResourceQuota . Spec , field . NewPath ( "spec" ) ) ... )
2016-02-22 11:14:11 -05:00
// ensure scopes cannot change, and that resources are still valid for scope
2016-05-25 13:43:23 -04:00
fldPath := field . NewPath ( "spec" , "scopes" )
2016-02-22 11:14:11 -05:00
oldScopes := sets . NewString ( )
newScopes := sets . NewString ( )
for _ , scope := range newResourceQuota . Spec . Scopes {
newScopes . Insert ( string ( scope ) )
}
for _ , scope := range oldResourceQuota . Spec . Scopes {
oldScopes . Insert ( string ( scope ) )
}
if ! oldScopes . Equal ( newScopes ) {
2017-01-05 06:11:27 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , newResourceQuota . Spec . Scopes , fieldImmutableErrorMsg ) )
2016-02-22 11:14:11 -05:00
}
2015-03-13 15:15:04 -04:00
newResourceQuota . Status = oldResourceQuota . Status
return allErrs
}
// ValidateResourceQuotaStatusUpdate tests to see if the status update is legal for an end user to make.
// newResourceQuota is updated with fields that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidateResourceQuotaStatusUpdate ( newResourceQuota , oldResourceQuota * core . ResourceQuota ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newResourceQuota . ObjectMeta , & oldResourceQuota . ObjectMeta , field . NewPath ( "metadata" ) )
2015-12-08 23:39:18 -05:00
if len ( newResourceQuota . ResourceVersion ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "resourceVersion" ) , "" ) )
2015-03-13 15:15:04 -04:00
}
2015-11-06 18:30:52 -05:00
fldPath := field . NewPath ( "status" , "hard" )
2015-10-13 17:27:56 -04:00
for k , v := range newResourceQuota . Status . Hard {
2015-11-04 16:52:14 -05:00
resPath := fldPath . Key ( string ( k ) )
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , ValidateResourceQuotaResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-03-13 15:15:04 -04:00
}
2015-11-06 18:30:52 -05:00
fldPath = field . NewPath ( "status" , "used" )
2015-10-13 17:27:56 -04:00
for k , v := range newResourceQuota . Status . Used {
2015-11-04 16:52:14 -05:00
resPath := fldPath . Key ( string ( k ) )
2016-05-25 13:43:23 -04:00
allErrs = append ( allErrs , ValidateResourceQuotaResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , ValidateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-03-13 15:15:04 -04:00
}
newResourceQuota . Spec = oldResourceQuota . Spec
return allErrs
}
2015-01-19 16:50:00 -05:00
// ValidateNamespace tests if required fields are set.
2017-10-09 11:58:37 -04:00
func ValidateNamespace ( namespace * core . Namespace ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & namespace . ObjectMeta , false , ValidateNamespaceName , field . NewPath ( "metadata" ) )
2015-03-20 12:48:12 -04:00
for i := range namespace . Spec . Finalizers {
2015-11-06 18:30:52 -05:00
allErrs = append ( allErrs , validateFinalizerName ( string ( namespace . Spec . Finalizers [ i ] ) , field . NewPath ( "spec" , "finalizers" ) ) ... )
2015-03-20 12:48:12 -04:00
}
2015-01-19 16:50:00 -05:00
return allErrs
}
2015-03-20 12:48:12 -04:00
// Validate finalizer names
2015-11-06 18:30:52 -05:00
func validateFinalizerName ( stringValue string , fldPath * field . Path ) field . ErrorList {
2017-11-13 22:36:32 -05:00
allErrs := apimachineryvalidation . ValidateFinalizerName ( stringValue , fldPath )
2019-02-09 12:07:18 -05:00
allErrs = append ( allErrs , validateKubeFinalizerName ( stringValue , fldPath ) ... )
2017-02-01 10:46:26 -05:00
return allErrs
}
// validateKubeFinalizerName checks for "standard" names of legacy finalizer
func validateKubeFinalizerName ( stringValue string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( strings . Split ( stringValue , "/" ) ) == 1 {
2017-04-10 13:49:54 -04:00
if ! helper . IsStandardFinalizerName ( stringValue ) {
2017-02-01 10:46:26 -05:00
return append ( allErrs , field . Invalid ( fldPath , stringValue , "name is neither a standard finalizer name nor is it fully qualified" ) )
}
}
return allErrs
2015-03-20 12:48:12 -04:00
}
2015-03-31 17:00:04 -04:00
// ValidateNamespaceUpdate tests to make sure a namespace update can be applied.
// newNamespace is updated with fields that cannot be changed
2017-10-09 11:58:37 -04:00
func ValidateNamespaceUpdate ( newNamespace * core . Namespace , oldNamespace * core . Namespace ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , field . NewPath ( "metadata" ) )
2015-03-31 17:00:04 -04:00
newNamespace . Spec . Finalizers = oldNamespace . Spec . Finalizers
newNamespace . Status = oldNamespace . Status
2015-01-19 16:50:00 -05:00
return allErrs
}
2015-03-12 11:08:06 -04:00
// ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make. newNamespace is updated with fields
// that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidateNamespaceStatusUpdate ( newNamespace , oldNamespace * core . Namespace ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , field . NewPath ( "metadata" ) )
2015-03-12 11:08:06 -04:00
newNamespace . Spec = oldNamespace . Spec
2015-04-10 11:41:18 -04:00
if newNamespace . DeletionTimestamp . IsZero ( ) {
2017-10-09 11:58:37 -04:00
if newNamespace . Status . Phase != core . NamespaceActive {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "status" , "Phase" ) , newNamespace . Status . Phase , "may only be 'Active' if `deletionTimestamp` is empty" ) )
2015-04-10 11:41:18 -04:00
}
} else {
2017-10-09 11:58:37 -04:00
if newNamespace . Status . Phase != core . NamespaceTerminating {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "status" , "Phase" ) , newNamespace . Status . Phase , "may only be 'Terminating' if `deletionTimestamp` is not empty" ) )
2015-04-10 11:41:18 -04:00
}
}
2015-03-12 11:08:06 -04:00
return allErrs
}
2015-03-20 12:48:12 -04:00
2015-03-31 17:00:04 -04:00
// ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make.
// newNamespace is updated with fields that cannot be changed.
2017-10-09 11:58:37 -04:00
func ValidateNamespaceFinalizeUpdate ( newNamespace , oldNamespace * core . Namespace ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , field . NewPath ( "metadata" ) )
2015-11-04 16:52:14 -05:00
2015-11-06 18:30:52 -05:00
fldPath := field . NewPath ( "spec" , "finalizers" )
2015-03-20 12:48:12 -04:00
for i := range newNamespace . Spec . Finalizers {
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
allErrs = append ( allErrs , validateFinalizerName ( string ( newNamespace . Spec . Finalizers [ i ] ) , idxPath ) ... )
2015-03-20 12:48:12 -04:00
}
newNamespace . Status = oldNamespace . Status
return allErrs
}
2015-03-15 02:03:46 -04:00
// ValidateEndpoints tests if required fields are set.
2017-10-09 11:58:37 -04:00
func ValidateEndpoints ( endpoints * core . Endpoints ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMeta ( & endpoints . ObjectMeta , true , ValidateEndpointsName , field . NewPath ( "metadata" ) )
2016-02-02 13:59:54 -05:00
allErrs = append ( allErrs , ValidateEndpointsSpecificAnnotations ( endpoints . Annotations , field . NewPath ( "annotations" ) ) ... )
2018-09-12 15:42:24 -04:00
allErrs = append ( allErrs , validateEndpointSubsets ( endpoints . Subsets , field . NewPath ( "subsets" ) ) ... )
2015-03-20 17:24:43 -04:00
return allErrs
}
2018-09-12 15:42:24 -04:00
func validateEndpointSubsets ( subsets [ ] core . EndpointSubset , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-03-20 17:24:43 -04:00
for i := range subsets {
ss := & subsets [ i ]
2015-11-04 16:52:14 -05:00
idxPath := fldPath . Index ( i )
2015-03-20 17:24:43 -04:00
2017-06-09 11:22:37 -04:00
// EndpointSubsets must include endpoint address. For headless service, we allow its endpoints not to have ports.
2015-09-22 04:05:21 -04:00
if len ( ss . Addresses ) == 0 && len ( ss . NotReadyAddresses ) == 0 {
2015-11-04 16:52:14 -05:00
//TODO: consider adding a RequiredOneOf() error for this and similar cases
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( idxPath , "must specify `addresses` or `notReadyAddresses`" ) )
2015-03-20 17:24:43 -04:00
}
2015-03-20 10:40:20 -04:00
for addr := range ss . Addresses {
2018-09-12 15:42:24 -04:00
allErrs = append ( allErrs , validateEndpointAddress ( & ss . Addresses [ addr ] , idxPath . Child ( "addresses" ) . Index ( addr ) ) ... )
2015-03-20 10:40:20 -04:00
}
2016-04-14 13:45:29 -04:00
for addr := range ss . NotReadyAddresses {
2018-09-12 15:42:24 -04:00
allErrs = append ( allErrs , validateEndpointAddress ( & ss . NotReadyAddresses [ addr ] , idxPath . Child ( "notReadyAddresses" ) . Index ( addr ) ) ... )
2016-04-14 13:45:29 -04:00
}
2015-03-20 10:40:20 -04:00
for port := range ss . Ports {
2015-11-04 16:52:14 -05:00
allErrs = append ( allErrs , validateEndpointPort ( & ss . Ports [ port ] , len ( ss . Ports ) > 1 , idxPath . Child ( "ports" ) . Index ( port ) ) ... )
2015-03-20 10:40:20 -04:00
}
2015-03-20 17:24:43 -04:00
}
2015-03-15 02:03:46 -04:00
return allErrs
}
2018-09-12 15:42:24 -04:00
func validateEndpointAddress ( address * core . EndpointAddress , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2016-01-04 12:49:39 -05:00
for _ , msg := range validation . IsValidIP ( address . IP ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "ip" ) , address . IP , msg ) )
2016-04-14 13:45:29 -04:00
}
2015-12-16 02:49:58 -05:00
if len ( address . Hostname ) > 0 {
2016-04-29 10:58:46 -04:00
allErrs = append ( allErrs , ValidateDNS1123Label ( address . Hostname , fldPath . Child ( "hostname" ) ) ... )
2016-04-14 13:45:29 -04:00
}
2016-09-04 19:23:03 -04:00
// During endpoint update, verify that NodeName is a DNS subdomain and transition rules allow the update
2016-08-24 12:57:38 -04:00
if address . NodeName != nil {
2016-09-04 19:23:03 -04:00
for _ , msg := range ValidateNodeName ( * address . NodeName , false ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "nodeName" ) , * address . NodeName , msg ) )
}
2016-08-24 12:57:38 -04:00
}
2016-01-04 12:49:39 -05:00
allErrs = append ( allErrs , validateNonSpecialIP ( address . IP , fldPath . Child ( "ip" ) ) ... )
return allErrs
2015-08-11 20:18:21 -04:00
}
2016-01-04 12:49:39 -05:00
func validateNonSpecialIP ( ipAddress string , fldPath * field . Path ) field . ErrorList {
// We disallow some IPs as endpoints or external-ips. Specifically,
// unspecified and loopback addresses are nonsensical and link-local
// addresses tend to be used for node-centric purposes (e.g. metadata
// service).
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-08-11 20:18:21 -04:00
ip := net . ParseIP ( ipAddress )
if ip == nil {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "must be a valid IP address" ) )
2015-08-11 20:18:21 -04:00
return allErrs
}
2016-01-04 12:49:39 -05:00
if ip . IsUnspecified ( ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "may not be unspecified (0.0.0.0)" ) )
}
2015-07-04 00:05:15 -04:00
if ip . IsLoopback ( ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "may not be in the loopback range (127.0.0.0/8)" ) )
2015-07-04 00:05:15 -04:00
}
if ip . IsLinkLocalUnicast ( ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "may not be in the link-local range (169.254.0.0/16)" ) )
2015-06-03 00:49:51 -04:00
}
2015-07-04 00:05:15 -04:00
if ip . IsLinkLocalMulticast ( ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "may not be in the link-local multicast range (224.0.0.0/24)" ) )
2015-07-04 00:05:15 -04:00
}
2015-03-20 10:40:20 -04:00
return allErrs
}
2017-10-09 11:58:37 -04:00
func validateEndpointPort ( port * core . EndpointPort , requireName bool , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-12-08 23:39:18 -05:00
if requireName && len ( port . Name ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
2015-12-08 23:39:18 -05:00
} else if len ( port . Name ) != 0 {
2016-04-29 10:58:46 -04:00
allErrs = append ( allErrs , ValidateDNS1123Label ( port . Name , fldPath . Child ( "name" ) ) ... )
2015-03-20 10:40:20 -04:00
}
2016-01-04 11:33:26 -05:00
for _ , msg := range validation . IsValidPortNum ( int ( port . Port ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "port" ) , port . Port , msg ) )
2015-03-20 10:40:20 -04:00
}
if len ( port . Protocol ) == 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "protocol" ) , "" ) )
2015-06-16 20:28:11 -04:00
} else if ! supportedPortProtocols . Has ( string ( port . Protocol ) ) {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "protocol" ) , port . Protocol , supportedPortProtocols . List ( ) ) )
2015-03-20 10:40:20 -04:00
}
return allErrs
}
2015-03-15 02:03:46 -04:00
// ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
2018-09-12 15:42:24 -04:00
// NodeName changes are allowed during update to accommodate the case where nodeIP or PodCIDR is reused.
// An existing endpoint ip will have a different nodeName if this happens.
2017-10-09 11:58:37 -04:00
func ValidateEndpointsUpdate ( newEndpoints , oldEndpoints * core . Endpoints ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := ValidateObjectMetaUpdate ( & newEndpoints . ObjectMeta , & oldEndpoints . ObjectMeta , field . NewPath ( "metadata" ) )
2018-09-12 15:42:24 -04:00
allErrs = append ( allErrs , validateEndpointSubsets ( newEndpoints . Subsets , field . NewPath ( "subsets" ) ) ... )
2016-02-02 13:59:54 -05:00
allErrs = append ( allErrs , ValidateEndpointsSpecificAnnotations ( newEndpoints . Annotations , field . NewPath ( "annotations" ) ) ... )
2015-03-15 02:03:46 -04:00
return allErrs
}
2015-05-05 19:02:13 -04:00
// ValidateSecurityContext ensure the security context contains valid settings
2017-10-09 11:58:37 -04:00
func ValidateSecurityContext ( sc * core . SecurityContext , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2017-10-09 11:58:37 -04:00
//this should only be true for testing since SecurityContext is defaulted by the core
2015-05-05 19:02:13 -04:00
if sc == nil {
return allErrs
}
if sc . Privileged != nil {
if * sc . Privileged && ! capabilities . Get ( ) . AllowPrivileged {
2017-01-05 12:07:50 -05:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "privileged" ) , "disallowed by cluster policy" ) )
2015-05-05 19:02:13 -04:00
}
}
if sc . RunAsUser != nil {
2018-01-09 01:27:29 -05:00
for _ , msg := range validation . IsValidUserID ( * sc . RunAsUser ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "runAsUser" ) , * sc . RunAsUser , msg ) )
2015-05-05 19:02:13 -04:00
}
}
2017-09-21 18:35:06 -04:00
2017-05-13 02:29:25 -04:00
if sc . RunAsGroup != nil {
for _ , msg := range validation . IsValidGroupID ( * sc . RunAsGroup ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "runAsGroup" ) , * sc . RunAsGroup , msg ) )
}
}
2018-12-20 12:28:08 -05:00
if sc . ProcMount != nil {
2018-12-29 00:18:01 -05:00
if err := ValidateProcMountType ( fldPath . Child ( "procMount" ) , * sc . ProcMount ) ; err != nil {
allErrs = append ( allErrs , err )
2018-12-20 12:28:08 -05:00
}
}
2017-09-21 18:35:06 -04:00
if sc . AllowPrivilegeEscalation != nil && ! * sc . AllowPrivilegeEscalation {
if sc . Privileged != nil && * sc . Privileged {
allErrs = append ( allErrs , field . Invalid ( fldPath , sc , "cannot set `allowPrivilegeEscalation` to false and `privileged` to true" ) )
}
if sc . Capabilities != nil {
for _ , cap := range sc . Capabilities . Add {
if string ( cap ) == "CAP_SYS_ADMIN" {
allErrs = append ( allErrs , field . Invalid ( fldPath , sc , "cannot set `allowPrivilegeEscalation` to false and `capabilities.Add` CAP_SYS_ADMIN" ) )
}
}
}
}
2015-05-05 19:02:13 -04:00
return allErrs
}
2015-07-27 15:49:06 -04:00
2017-10-09 11:58:37 -04:00
func ValidatePodLogOptions ( opts * core . PodLogOptions ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-09-09 23:46:11 -04:00
if opts . TailLines != nil && * opts . TailLines < 0 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "tailLines" ) , * opts . TailLines , isNegativeErrorMsg ) )
2015-09-09 23:46:11 -04:00
}
if opts . LimitBytes != nil && * opts . LimitBytes < 1 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "limitBytes" ) , * opts . LimitBytes , "must be greater than 0" ) )
2015-09-09 23:46:11 -04:00
}
switch {
case opts . SinceSeconds != nil && opts . SinceTime != nil :
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "" ) , "at most one of `sinceTime` or `sinceSeconds` may be specified" ) )
2015-09-09 23:46:11 -04:00
case opts . SinceSeconds != nil :
if * opts . SinceSeconds < 1 {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "sinceSeconds" ) , * opts . SinceSeconds , "must be greater than 0" ) )
2015-09-09 23:46:11 -04:00
}
}
return allErrs
}
2015-10-12 15:09:20 -04:00
// ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus
2017-10-09 11:58:37 -04:00
func ValidateLoadBalancerStatus ( status * core . LoadBalancerStatus , fldPath * field . Path ) field . ErrorList {
2015-11-06 18:30:52 -05:00
allErrs := field . ErrorList { }
2015-11-04 16:52:14 -05:00
for i , ingress := range status . Ingress {
idxPath := fldPath . Child ( "ingress" ) . Index ( i )
2015-10-12 15:09:20 -04:00
if len ( ingress . IP ) > 0 {
if isIP := ( net . ParseIP ( ingress . IP ) != nil ) ; ! isIP {
2015-11-14 15:26:04 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "ip" ) , ingress . IP , "must be a valid IP address" ) )
2015-10-12 15:09:20 -04:00
}
}
if len ( ingress . Hostname ) > 0 {
2015-12-17 00:58:09 -05:00
for _ , msg := range validation . IsDNS1123Subdomain ( ingress . Hostname ) {
2015-12-16 01:03:20 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "hostname" ) , ingress . Hostname , msg ) )
2015-10-12 15:09:20 -04:00
}
if isIP := ( net . ParseIP ( ingress . Hostname ) != nil ) ; isIP {
2015-11-10 15:59:41 -05:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "hostname" ) , ingress . Hostname , "must be a DNS name, not an IP address" ) )
2015-10-12 15:09:20 -04:00
}
}
}
return allErrs
}
2016-02-02 13:59:54 -05:00
2018-01-30 18:41:57 -05:00
// validateVolumeNodeAffinity tests that the PersistentVolume.NodeAffinity has valid data
2018-02-20 14:42:46 -05:00
// returns:
// - true if volumeNodeAffinity is set
// - errorList if there are validation errors
2018-01-30 18:41:57 -05:00
func validateVolumeNodeAffinity ( nodeAffinity * core . VolumeNodeAffinity , fldPath * field . Path ) ( bool , field . ErrorList ) {
allErrs := field . ErrorList { }
if nodeAffinity == nil {
return false , allErrs
}
if nodeAffinity . Required != nil {
allErrs = append ( allErrs , ValidateNodeSelector ( nodeAffinity . Required , fldPath . Child ( "required" ) ) ... )
} else {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "required" ) , "must specify required node constraints" ) )
}
return true , allErrs
}
2017-11-13 01:46:12 -05:00
// ValidateCIDR validates whether a CIDR matches the conventions expected by net.ParseCIDR
func ValidateCIDR ( cidr string ) ( * net . IPNet , error ) {
_ , net , err := net . ParseCIDR ( cidr )
if err != nil {
return nil , err
}
return net , nil
}
2018-02-02 02:11:07 -05:00
func IsDecremented ( update , old * int32 ) bool {
if update == nil && old != nil {
return true
}
if update == nil || old == nil {
return false
}
return * update < * old
}
2018-12-20 12:28:08 -05:00
2018-12-29 00:18:01 -05:00
// ValidateProcMountType tests that the argument is a valid ProcMountType.
func ValidateProcMountType ( fldPath * field . Path , procMountType core . ProcMountType ) * field . Error {
2018-12-20 12:28:08 -05:00
switch procMountType {
2018-12-29 00:18:01 -05:00
case core . DefaultProcMount , core . UnmaskedProcMount :
return nil
2018-12-20 12:28:08 -05:00
default :
2018-12-29 00:18:01 -05:00
return field . NotSupported ( fldPath , procMountType , [ ] string { string ( core . DefaultProcMount ) , string ( core . UnmaskedProcMount ) } )
2018-12-20 12:28:08 -05:00
}
}