2014-07-01 20:01:39 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-07-01 20:01:39 +00: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 22:48:41 +00:00
package validation
2014-07-01 20:01:39 +00:00
import (
2015-05-06 14:09:18 +00:00
"encoding/json"
2014-10-31 06:03:52 +00:00
"fmt"
2015-11-20 02:42:02 +00:00
"math"
2015-03-16 21:36:30 +00:00
"net"
2015-02-20 05:36:23 +00:00
"os"
2015-02-10 19:00:11 +00:00
"path"
2015-05-08 17:53:00 +00:00
"reflect"
2015-05-11 19:03:10 +00:00
"regexp"
2014-07-08 04:32:56 +00:00
"strings"
2014-07-01 20:01:39 +00:00
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/capabilities"
"k8s.io/kubernetes/pkg/labels"
2015-11-10 06:28:45 +00:00
"k8s.io/kubernetes/pkg/util/intstr"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2015-09-10 22:48:28 +00:00
"k8s.io/kubernetes/pkg/util/validation"
2015-11-06 23:30:52 +00:00
"k8s.io/kubernetes/pkg/util/validation/field"
2014-12-12 05:39:56 +00:00
"github.com/golang/glog"
2014-07-01 20:01:39 +00:00
)
2015-10-06 22:58:46 +00:00
// TODO: delete this global variable when we enable the validation of common
// fields by default.
var RepairMalformedUpdates bool = true
2015-11-14 20:26:04 +00:00
const isNegativeErrorMsg string = ` must be greater than or equal to 0 `
2015-10-12 03:26:17 +00:00
const fieldImmutableErrorMsg string = ` field is immutable `
const cIdentifierErrorMsg string = ` must be a C identifier (matching regex ` + validation . CIdentifierFmt + ` ): e.g. "my_name" or "MyName" `
2015-10-13 21:27:56 +00:00
const isNotIntegerErrorMsg string = ` must be an integer `
2015-02-05 00:36:27 +00:00
2015-11-14 16:44:50 +00:00
func InclusiveRangeErrorMsg ( lo , hi int ) string {
return fmt . Sprintf ( ` must be between %d and %d, inclusive ` , lo , hi )
2015-02-05 00:36:27 +00:00
}
2015-09-10 22:48:28 +00:00
var labelValueErrorMsg string = fmt . Sprintf ( ` must have at most %d characters, matching regex %s: e.g. "MyValue" or "" ` , validation . LabelValueMaxLength , validation . LabelValueFmt )
var qualifiedNameErrorMsg string = fmt . Sprintf ( ` must be a qualified name (at most %d characters, matching regex %s), with an optional DNS subdomain prefix (at most %d characters, matching regex %s) and slash (/): e.g. "MyName" or "example.com/MyName" ` , validation . QualifiedNameMaxLength , validation . QualifiedNameFmt , validation . DNS1123SubdomainMaxLength , validation . DNS1123SubdomainFmt )
var DNSSubdomainErrorMsg string = fmt . Sprintf ( ` must be a DNS subdomain (at most %d characters, matching regex %s): e.g. "example.com" ` , validation . DNS1123SubdomainMaxLength , validation . DNS1123SubdomainFmt )
var DNS1123LabelErrorMsg string = fmt . Sprintf ( ` must be a DNS label (at most %d characters, matching regex %s): e.g. "my-name" ` , validation . DNS1123LabelMaxLength , validation . DNS1123LabelFmt )
var DNS952LabelErrorMsg string = fmt . Sprintf ( ` must be a DNS 952 label (at most %d characters, matching regex %s): e.g. "my-name" ` , validation . DNS952LabelMaxLength , validation . DNS952LabelFmt )
2015-11-14 16:44:50 +00:00
var pdPartitionErrorMsg string = InclusiveRangeErrorMsg ( 1 , 255 )
var PortRangeErrorMsg string = InclusiveRangeErrorMsg ( 1 , 65535 )
2015-11-20 02:42:02 +00:00
var IdRangeErrorMsg string = InclusiveRangeErrorMsg ( 0 , math . MaxInt32 )
2015-10-12 03:26:17 +00:00
var PortNameErrorMsg string = fmt . Sprintf ( ` must be an IANA_SVC_NAME (at most 15 characters, matching regex %s, it must contain at least one letter [a-z], and hyphens cannot be adjacent to other hyphens): e.g. "http" ` , validation . IdentifierNoHyphensBeginEndFmt )
2015-02-05 00:36:27 +00:00
2015-10-21 22:22:47 +00:00
const totalAnnotationSizeLimitB int = 256 * ( 1 << 10 ) // 256 kB
2015-03-02 13:41:13 +00:00
2015-11-06 23:30:52 +00:00
func ValidateLabelName ( labelName string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsQualifiedName ( labelName ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , labelName , qualifiedNameErrorMsg ) )
2015-08-25 19:07:03 +00:00
}
return allErrs
}
2015-02-02 00:03:04 +00:00
// ValidateLabels validates that a set of labels are correctly defined.
2015-11-06 23:30:52 +00:00
func ValidateLabels ( labels map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-02-27 15:08:02 +00:00
for k , v := range labels {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateLabelName ( k , fldPath ) ... )
2015-09-10 22:48:28 +00:00
if ! validation . IsValidLabelValue ( v ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , v , labelValueErrorMsg ) )
2015-02-27 15:08:02 +00:00
}
2015-02-02 00:03:04 +00:00
}
return allErrs
}
// ValidateAnnotations validates that a set of annotations are correctly defined.
2015-11-06 23:30:52 +00:00
func ValidateAnnotations ( annotations map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-02-27 15:08:02 +00:00
var totalSize int64
for k , v := range annotations {
2015-09-10 22:48:28 +00:00
if ! validation . IsQualifiedName ( strings . ToLower ( k ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , k , qualifiedNameErrorMsg ) )
2015-02-02 00:03:04 +00:00
}
2015-02-27 15:08:02 +00:00
totalSize += ( int64 ) ( len ( k ) ) + ( int64 ) ( len ( v ) )
}
2015-03-02 13:41:13 +00:00
if totalSize > ( int64 ) ( totalAnnotationSizeLimitB ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . TooLong ( fldPath , "" , totalAnnotationSizeLimitB ) )
2015-02-02 00:03:04 +00:00
}
return allErrs
}
2015-01-27 23:55:54 +00:00
// ValidateNameFunc validates that the provided name is valid for a given resource type.
2015-01-27 23:56:38 +00:00
// Not all resources have the same validation rules for names. Prefix is true if the
// name will have a value appended to it.
type ValidateNameFunc func ( name string , prefix bool ) ( bool , string )
// maskTrailingDash replaces the final character of a string with a subdomain safe
// value if is a dash.
func maskTrailingDash ( name string ) string {
if strings . HasSuffix ( name , "-" ) {
return name [ : len ( name ) - 2 ] + "a"
}
return name
}
2015-01-27 23:55:54 +00:00
2015-01-28 04:50:01 +00: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.
func ValidatePodName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-01-28 04:50:01 +00: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.
func ValidateReplicationControllerName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-01-28 04:50:01 +00: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.
func ValidateServiceName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNS952Label ( name , prefix )
2015-01-28 04:50:01 +00: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.
func ValidateNodeName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-01-28 04:50:01 +00:00
}
2015-01-19 21:50:00 +00: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.
func ValidateNamespaceName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSLabel ( name , prefix )
2015-01-19 21:50:00 +00:00
}
2015-02-20 06:03:36 +00: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.
func ValidateLimitRangeName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-02-20 06:03:36 +00: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.
func ValidateResourceQuotaName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-02-20 06:03:36 +00: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.
func ValidateSecretName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-02-20 06:03:36 +00:00
}
2015-04-27 22:53:28 +00: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.
func ValidateServiceAccountName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-04-27 22:53:28 +00:00
}
2015-03-15 06:03:46 +00: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.
func ValidateEndpointsName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-03-15 06:03:46 +00:00
}
2015-06-09 12:53:30 +00:00
// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
func NameIsDNSSubdomain ( name string , prefix bool ) ( bool , string ) {
2015-01-27 23:56:38 +00:00
if prefix {
name = maskTrailingDash ( name )
}
2015-09-10 22:48:28 +00:00
if validation . IsDNS1123Subdomain ( name ) {
2015-01-27 23:55:54 +00:00
return true , ""
}
2015-06-09 12:53:30 +00:00
return false , DNSSubdomainErrorMsg
2015-01-27 23:55:54 +00:00
}
2015-06-09 12:53:30 +00:00
// NameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label.
func NameIsDNSLabel ( name string , prefix bool ) ( bool , string ) {
2015-05-12 22:13:03 +00:00
if prefix {
name = maskTrailingDash ( name )
}
2015-09-10 22:48:28 +00:00
if validation . IsDNS1123Label ( name ) {
2015-05-12 22:13:03 +00:00
return true , ""
}
2015-06-09 12:53:30 +00:00
return false , DNS1123LabelErrorMsg
2015-05-12 22:13:03 +00:00
}
2015-06-09 12:53:30 +00:00
// NameIsDNS952Label is a ValidateNameFunc for names that must be a DNS 952 label.
func NameIsDNS952Label ( name string , prefix bool ) ( bool , string ) {
2015-01-27 23:56:38 +00:00
if prefix {
name = maskTrailingDash ( name )
}
2015-09-10 22:48:28 +00:00
if validation . IsDNS952Label ( name ) {
2015-01-27 23:55:54 +00:00
return true , ""
}
2015-06-09 12:53:30 +00:00
return false , DNS952LabelErrorMsg
2015-01-27 23:55:54 +00:00
}
2015-08-25 19:07:03 +00:00
// Validates that given value is not negative.
2015-11-06 23:30:52 +00:00
func ValidatePositiveField ( value int64 , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-08-25 19:07:03 +00:00
if value < 0 {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , value , isNegativeErrorMsg ) )
2015-08-25 19:07:03 +00:00
}
return allErrs
}
2015-09-24 14:15:40 +00:00
// Validates that a Quantity is not negative
2015-11-06 23:30:52 +00:00
func ValidatePositiveQuantity ( value resource . Quantity , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-09-24 14:15:40 +00:00
if value . Cmp ( resource . Quantity { } ) < 0 {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , value . String ( ) , isNegativeErrorMsg ) )
2015-09-24 14:15:40 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func ValidateImmutableField ( newVal , oldVal interface { } , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-11-04 21:52:14 +00:00
if ! api . Semantic . DeepEqual ( oldVal , newVal ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , newVal , fieldImmutableErrorMsg ) )
2015-10-12 03:26:17 +00:00
}
return allErrs
}
2015-01-27 23:56:38 +00:00
// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
// been performed.
2015-11-03 03:00:01 +00:00
// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
2015-10-06 22:58:46 +00:00
// TODO: Remove calls to this method scattered in validations of specific resources, e.g., ValidatePodUpdate.
2015-11-06 23:30:52 +00:00
func ValidateObjectMeta ( meta * api . ObjectMeta , requiresNamespace bool , nameFn ValidateNameFunc , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-01-27 23:56:38 +00:00
if len ( meta . GenerateName ) != 0 {
if ok , qualifier := nameFn ( meta . GenerateName , true ) ; ! ok {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "generateName" ) , meta . GenerateName , qualifier ) )
2015-01-27 23:56:38 +00:00
}
}
2015-06-09 22:19:04 +00:00
// If the generated name validates, but the calculated value does not, it's a problem with generation, and we
2015-01-27 23:56:38 +00:00
// report it here. This may confuse users, but indicates a programming bug and still must be validated.
2015-08-08 21:29:57 +00:00
// If there are multiple fields out of which one is required then add a or as a separator
2015-01-27 23:55:54 +00:00
if len ( meta . Name ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "name or generateName is required" ) )
2015-01-27 23:55:54 +00:00
} else {
2015-01-27 23:56:38 +00:00
if ok , qualifier := nameFn ( meta . Name , false ) ; ! ok {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , meta . Name , qualifier ) )
2015-01-27 23:55:54 +00:00
}
}
if requiresNamespace {
if len ( meta . Namespace ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "namespace" ) , "" ) )
2015-05-12 22:13:03 +00:00
} else if ok , _ := ValidateNamespaceName ( meta . Namespace , false ) ; ! ok {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "namespace" ) , meta . Namespace , DNS1123LabelErrorMsg ) )
2015-01-27 23:55:54 +00:00
}
} else {
if len ( meta . Namespace ) != 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( fldPath , "not allowed on this type" ) )
2015-01-27 23:55:54 +00:00
}
}
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , ValidatePositiveField ( meta . Generation , fldPath . Child ( "generation" ) ) ... )
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateLabels ( meta . Labels , fldPath . Child ( "labels" ) ) ... )
allErrs = append ( allErrs , ValidateAnnotations ( meta . Annotations , fldPath . Child ( "annotations" ) ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
}
// ValidateObjectMetaUpdate validates an object's metadata when updated
2015-11-06 23:30:52 +00:00
func ValidateObjectMetaUpdate ( newMeta , oldMeta * api . ObjectMeta , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-01-27 23:55:54 +00:00
2015-11-04 21:52:14 +00:00
if ! RepairMalformedUpdates && newMeta . UID != oldMeta . UID {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "uid" ) , newMeta . UID , "field is immutable" ) )
2015-08-19 23:59:43 +00:00
}
2015-10-06 22:58:46 +00:00
// in the event it is left empty, set it, to allow clients more flexibility
// TODO: remove the following code that repairs the update request when we retire the clients that modify the immutable fields.
// Please do not copy this pattern elsewhere; validation functions should not be modifying the objects they are passed!
if RepairMalformedUpdates {
2015-11-04 21:52:14 +00:00
if len ( newMeta . UID ) == 0 {
newMeta . UID = oldMeta . UID
2015-10-06 22:58:46 +00:00
}
// ignore changes to timestamp
2015-11-04 21:52:14 +00:00
if oldMeta . CreationTimestamp . IsZero ( ) {
oldMeta . CreationTimestamp = newMeta . CreationTimestamp
2015-10-06 22:58:46 +00:00
} else {
2015-11-04 21:52:14 +00:00
newMeta . CreationTimestamp = oldMeta . CreationTimestamp
2015-10-06 22:58:46 +00:00
}
// an object can never remove a deletion timestamp or clear/change grace period seconds
2015-11-04 21:52:14 +00:00
if ! oldMeta . DeletionTimestamp . IsZero ( ) {
newMeta . DeletionTimestamp = oldMeta . DeletionTimestamp
2015-10-06 22:58:46 +00:00
}
2015-11-04 21:52:14 +00:00
if oldMeta . DeletionGracePeriodSeconds != nil && newMeta . DeletionGracePeriodSeconds == nil {
newMeta . DeletionGracePeriodSeconds = oldMeta . DeletionGracePeriodSeconds
2015-10-06 22:58:46 +00:00
}
2015-08-19 23:59:43 +00:00
}
2015-10-06 22:58:46 +00:00
2015-11-04 21:52:14 +00:00
// TODO: needs to check if newMeta==nil && oldMeta !=nil after the repair logic is removed.
if newMeta . DeletionGracePeriodSeconds != nil && oldMeta . DeletionGracePeriodSeconds != nil && * newMeta . DeletionGracePeriodSeconds != * oldMeta . DeletionGracePeriodSeconds {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "deletionGracePeriodSeconds" ) , newMeta . DeletionGracePeriodSeconds , "field is immutable; may only be changed via deletion" ) )
2015-08-19 23:59:43 +00:00
}
2015-01-27 23:55:54 +00:00
2015-03-20 00:51:07 +00:00
// Reject updates that don't specify a resource version
2015-12-09 04:39:18 +00:00
if len ( newMeta . ResourceVersion ) == 0 {
2015-11-14 16:44:50 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "resourceVersion" ) , newMeta . ResourceVersion , "must be specified for an update" ) )
2015-03-20 00:51:07 +00:00
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateImmutableField ( newMeta . Name , oldMeta . Name , fldPath . Child ( "name" ) ) ... )
allErrs = append ( allErrs , ValidateImmutableField ( newMeta . Namespace , oldMeta . Namespace , fldPath . Child ( "namespace" ) ) ... )
allErrs = append ( allErrs , ValidateImmutableField ( newMeta . UID , oldMeta . UID , fldPath . Child ( "uid" ) ) ... )
allErrs = append ( allErrs , ValidateImmutableField ( newMeta . CreationTimestamp , oldMeta . CreationTimestamp , fldPath . Child ( "creationTimestamp" ) ) ... )
2015-01-27 23:55:54 +00:00
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateLabels ( newMeta . Labels , fldPath . Child ( "labels" ) ) ... )
allErrs = append ( allErrs , ValidateAnnotations ( newMeta . Annotations , fldPath . Child ( "annotations" ) ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
2014-10-31 06:03:52 +00:00
}
2015-11-06 23:30:52 +00:00
func validateVolumes ( volumes [ ] api . Volume , fldPath * field . Path ) ( sets . String , field . ErrorList ) {
allErrs := field . ErrorList { }
2014-07-08 06:20:30 +00:00
2015-09-09 17:45:01 +00:00
allNames := sets . String { }
2015-01-26 17:52:50 +00:00
for i , vol := range volumes {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
el := validateVolumeSource ( & vol . VolumeSource , idxPath )
2014-08-20 02:57:48 +00:00
if len ( vol . Name ) == 0 {
2015-11-14 20:26:04 +00:00
el = append ( el , field . Required ( idxPath . Child ( "name" ) , "" ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsDNS1123Label ( vol . Name ) {
2015-11-10 20:59:41 +00:00
el = append ( el , field . Invalid ( idxPath . Child ( "name" ) , vol . Name , DNS1123LabelErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( vol . Name ) {
2015-11-10 20:59:41 +00:00
el = append ( el , field . Duplicate ( idxPath . Child ( "name" ) , vol . Name ) )
2014-07-16 19:32:59 +00:00
}
2014-08-14 20:46:22 +00:00
if len ( el ) == 0 {
2014-07-08 06:20:30 +00:00
allNames . Insert ( vol . Name )
2014-07-16 19:32:59 +00:00
} else {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , el ... )
2014-07-01 21:40:36 +00:00
}
2015-11-04 21:52:14 +00:00
2014-07-01 21:40:36 +00:00
}
2014-07-08 06:20:30 +00:00
return allNames , allErrs
2014-07-01 21:40:36 +00:00
}
2015-11-06 23:30:52 +00:00
func validateVolumeSource ( source * api . VolumeSource , fldPath * field . Path ) field . ErrorList {
2014-07-16 19:32:59 +00:00
numVolumes := 0
2015-11-06 23:30:52 +00:00
allErrs := field . ErrorList { }
2014-10-01 20:35:21 +00:00
if source . EmptyDir != nil {
2014-07-16 19:32:59 +00:00
numVolumes ++
2014-11-24 04:03:11 +00:00
// EmptyDirs have nothing to validate
}
2015-11-15 06:36:25 +00: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-24 04:03:11 +00:00
if source . GitRepo != nil {
2015-11-15 06:36:25 +00: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 19:32:59 +00:00
}
2014-08-05 17:58:43 +00:00
if source . GCEPersistentDisk != nil {
2015-11-15 06:36:25 +00: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 17:58:43 +00:00
}
2015-04-07 21:16:36 +00:00
if source . AWSElasticBlockStore != nil {
2015-11-15 06:36:25 +00: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 14:26:39 +00:00
}
2015-02-18 01:24:50 +00:00
if source . Secret != nil {
2015-11-15 06:36:25 +00: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-18 01:24:50 +00:00
}
2015-02-10 19:00:11 +00:00
if source . NFS != nil {
2015-11-15 06:36:25 +00: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 19:00:11 +00:00
}
2015-03-13 21:31:13 +00:00
if source . ISCSI != nil {
2015-11-15 06:36:25 +00: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" ) ) ... )
}
2015-03-13 21:31:13 +00:00
}
2015-03-26 18:53:21 +00:00
if source . Glusterfs != nil {
2015-11-15 06:36:25 +00:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "glusterfs" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateGlusterfs ( source . Glusterfs , fldPath . Child ( "glusterfs" ) ) ... )
}
2015-03-26 18:53:21 +00:00
}
2015-09-25 19:22:23 +00:00
if source . Flocker != nil {
2015-11-15 06:36:25 +00: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 19:22:23 +00:00
}
2015-06-10 20:57:33 +00:00
if source . PersistentVolumeClaim != nil {
2015-11-15 06:36:25 +00: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 11:16:07 +00:00
}
2015-04-07 17:22:23 +00:00
if source . RBD != nil {
2015-11-15 06:36:25 +00: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 17:22:23 +00:00
}
2015-04-10 16:54:01 +00:00
if source . Cinder != nil {
2015-11-15 06:36:25 +00: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 16:54:01 +00:00
}
2015-04-09 18:05:24 +00:00
if source . CephFS != nil {
2015-11-15 06:36:25 +00: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 18:05:24 +00:00
}
2015-02-20 05:36:23 +00:00
if source . DownwardAPI != nil {
2015-11-15 06:36:25 +00: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 05:36:23 +00:00
}
2015-08-11 15:19:29 +00:00
if source . FC != nil {
2015-11-15 06:36:25 +00: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 15:19:29 +00:00
}
2015-09-30 18:31:53 +00:00
if source . FlexVolume != nil {
numVolumes ++
2015-12-31 23:37:12 +00:00
allErrs = append ( allErrs , validateFlexVolumeSource ( source . FlexVolume , fldPath . Child ( "flexVolume" ) ) ... )
2015-09-30 18:31:53 +00:00
}
2015-11-14 20:26:04 +00:00
if numVolumes == 0 {
allErrs = append ( allErrs , field . Required ( fldPath , "must specify a volume type" ) )
2014-07-16 19:32:59 +00:00
}
2015-04-07 17:22:23 +00:00
2014-07-16 19:32:59 +00:00
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateHostPathVolumeSource ( hostPath * api . HostPathVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( hostPath . Path ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2014-07-15 01:39:30 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateGitRepoVolumeSource ( gitRepo * api . GitRepoVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-10-10 10:10:45 +00:00
if len ( gitRepo . Repository ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "repository" ) , "" ) )
2014-11-24 04:03:11 +00:00
}
2015-10-10 10:10:45 +00:00
2015-11-04 21:52:14 +00:00
pathErrs := validateVolumeSourcePath ( gitRepo . Directory , fldPath . Child ( "directory" ) )
2015-10-10 10:10:45 +00:00
allErrs = append ( allErrs , pathErrs ... )
2014-11-24 04:03:11 +00:00
return allErrs
}
2014-07-08 04:32:56 +00:00
2015-11-06 23:30:52 +00:00
func validateISCSIVolumeSource ( iscsi * api . ISCSIVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( iscsi . TargetPortal ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "targetPortal" ) , "" ) )
2015-03-13 21:31:13 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( iscsi . IQN ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "iqn" ) , "" ) )
2015-03-13 21:31:13 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( iscsi . FSType ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fsType" ) , "" ) )
2015-03-13 21:31:13 +00:00
}
if iscsi . Lun < 0 || iscsi . Lun > 255 {
2015-11-14 16:44:50 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "lun" ) , iscsi . Lun , InclusiveRangeErrorMsg ( 0 , 255 ) ) )
2015-03-13 21:31:13 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateFCVolumeSource ( fc * api . FCVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-08-11 15:19:29 +00:00
if len ( fc . TargetWWNs ) < 1 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "targetWWNs" ) , "" ) )
2015-08-11 15:19:29 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( fc . FSType ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fsType" ) , "" ) )
2015-08-11 15:19:29 +00:00
}
if fc . Lun == nil {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "lun" ) , "" ) )
2015-08-11 15:19:29 +00:00
} else {
if * fc . Lun < 0 || * fc . Lun > 255 {
2015-11-14 16:44:50 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "lun" ) , fc . Lun , InclusiveRangeErrorMsg ( 0 , 255 ) ) )
2015-08-11 15:19:29 +00:00
}
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateGCEPersistentDiskVolumeSource ( pd * api . GCEPersistentDiskVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( pd . PDName ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "pdName" ) , "" ) )
2014-08-05 17:58:43 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( pd . FSType ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fsType" ) , "" ) )
2014-08-05 17:58:43 +00:00
}
2015-11-04 21:52:14 +00:00
if pd . Partition < 0 || pd . Partition > 255 {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "partition" ) , pd . Partition , pdPartitionErrorMsg ) )
2014-08-05 17:58:43 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateAWSElasticBlockStoreVolumeSource ( PD * api . AWSElasticBlockStoreVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( PD . VolumeID ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeID" ) , "" ) )
2015-03-06 14:26:39 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( PD . FSType ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fsType" ) , "" ) )
2015-03-06 14:26:39 +00:00
}
if PD . Partition < 0 || PD . Partition > 255 {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "partition" ) , PD . Partition , pdPartitionErrorMsg ) )
2015-03-06 14:26:39 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateSecretVolumeSource ( secretSource * api . SecretVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( secretSource . SecretName ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "secretName" ) , "" ) )
2015-02-18 01:24:50 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validatePersistentClaimVolumeSource ( claim * api . PersistentVolumeClaimVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( claim . ClaimName ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "claimName" ) , "" ) )
2015-04-18 11:16:07 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateNFSVolumeSource ( nfs * api . NFSVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( nfs . Server ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "server" ) , "" ) )
2015-02-10 19:00:11 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( nfs . Path ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2015-02-10 19:00:11 +00:00
}
if ! path . IsAbs ( nfs . Path ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "path" ) , nfs . Path , "must be an absolute path" ) )
2015-02-10 19:00:11 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateGlusterfs ( glusterfs * api . GlusterfsVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( glusterfs . EndpointsName ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "endpoints" ) , "" ) )
2015-03-26 18:53:21 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( glusterfs . Path ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2015-03-26 18:53:21 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateFlockerVolumeSource ( flocker * api . FlockerVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( flocker . DatasetName ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "datasetName" ) , "" ) )
2015-09-25 19:22:23 +00:00
}
if strings . Contains ( flocker . DatasetName , "/" ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "datasetName" ) , flocker . DatasetName , "must not contain '/'" ) )
2015-09-25 19:22:23 +00:00
}
return allErrs
}
2015-09-09 17:45:01 +00:00
var validDownwardAPIFieldPathExpressions = sets . NewString ( "metadata.name" , "metadata.namespace" , "metadata.labels" , "metadata.annotations" )
2015-02-20 05:36:23 +00:00
2015-11-06 23:30:52 +00:00
func validateDownwardAPIVolumeSource ( downwardAPIVolume * api . DownwardAPIVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-02-20 05:36:23 +00:00
for _ , downwardAPIVolumeFile := range downwardAPIVolume . Items {
if len ( downwardAPIVolumeFile . Path ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "path" ) , "" ) )
2015-02-20 05:36:23 +00:00
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateVolumeSourcePath ( downwardAPIVolumeFile . Path , fldPath . Child ( "path" ) ) ... )
allErrs = append ( allErrs , validateObjectFieldSelector ( & downwardAPIVolumeFile . FieldRef , & validDownwardAPIFieldPathExpressions , fldPath . Child ( "fieldRef" ) ) ... )
2015-02-20 05:36:23 +00:00
}
return allErrs
}
2015-10-10 10:10:45 +00:00
// This validate will make sure targetPath:
// 1. is not abs path
// 2. does not contain '..'
// 3. does not start with '..'
2015-11-06 23:30:52 +00:00
func validateVolumeSourcePath ( targetPath string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-10-10 10:10:45 +00:00
if path . IsAbs ( targetPath ) {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , targetPath , "must be a relative path" ) )
2015-10-10 10:10:45 +00:00
}
// TODO assume OS of api server & nodes are the same for now
items := strings . Split ( targetPath , string ( os . PathSeparator ) )
for _ , item := range items {
if item == ".." {
2015-11-14 16:44:50 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , targetPath , "must not contain '..'" ) )
2015-10-10 10:10:45 +00:00
}
}
if strings . HasPrefix ( items [ 0 ] , ".." ) && len ( items [ 0 ] ) > 2 {
2015-11-14 16:44:50 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , targetPath , "must not start with '..'" ) )
2015-10-10 10:10:45 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
2015-11-06 23:30:52 +00:00
func validateRBDVolumeSource ( rbd * api . RBDVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-04-07 17:22:23 +00:00
if len ( rbd . CephMonitors ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "monitors" ) , "" ) )
2015-04-07 17:22:23 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( rbd . RBDImage ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "image" ) , "" ) )
2015-04-07 17:22:23 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( rbd . FSType ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fsType" ) , "" ) )
2015-04-07 17:22:23 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateCinderVolumeSource ( cd * api . CinderVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if len ( cd . VolumeID ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "volumeID" ) , "" ) )
2015-04-10 16:54:01 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( cd . FSType ) == 0 || ( cd . FSType != "ext3" && cd . FSType != "ext4" ) {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fsType" ) , "" ) )
2015-04-10 16:54:01 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateCephFSVolumeSource ( cephfs * api . CephFSVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-04-09 18:05:24 +00:00
if len ( cephfs . Monitors ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "monitors" ) , "" ) )
2015-04-09 18:05:24 +00:00
}
return allErrs
}
2015-09-30 18:31:53 +00:00
func validateFlexVolumeSource ( fv * api . FlexVolumeSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( fv . Driver ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "driver" ) , "" ) )
}
if len ( fv . FSType ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fsType" ) , "" ) )
}
return allErrs
}
2015-03-23 18:18:11 +00:00
func ValidatePersistentVolumeName ( name string , prefix bool ) ( bool , string ) {
2015-06-09 12:53:30 +00:00
return NameIsDNSSubdomain ( name , prefix )
2015-03-23 18:18:11 +00:00
}
2015-11-04 21:52:14 +00:00
var supportedAccessModes = sets . NewString ( string ( api . ReadWriteOnce ) , string ( api . ReadOnlyMany ) , string ( api . ReadWriteMany ) )
2015-11-06 23:30:52 +00:00
func ValidatePersistentVolume ( pv * api . PersistentVolume ) field . ErrorList {
allErrs := ValidateObjectMeta ( & pv . ObjectMeta , false , ValidatePersistentVolumeName , field . NewPath ( "metadata" ) )
2015-03-23 18:18:11 +00:00
2015-11-06 23:30:52 +00:00
specPath := field . NewPath ( "spec" )
2015-05-13 00:44:29 +00:00
if len ( pv . Spec . AccessModes ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "accessModes" ) , "" ) )
2015-05-13 00:44:29 +00:00
}
2015-07-24 19:55:54 +00:00
for _ , mode := range pv . Spec . AccessModes {
2015-11-04 21:52:14 +00:00
if ! supportedAccessModes . Has ( string ( mode ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "accessModes" ) , mode , supportedAccessModes . List ( ) ) )
2015-07-24 19:55:54 +00:00
}
}
2015-03-23 18:18:11 +00:00
if len ( pv . Spec . Capacity ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "capacity" ) , "" ) )
2015-03-23 18:18:11 +00:00
}
if _ , ok := pv . Spec . Capacity [ api . ResourceStorage ] ; ! ok || len ( pv . Spec . Capacity ) > 1 {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "capacity" ) , pv . Spec . Capacity , [ ] string { string ( api . ResourceStorage ) } ) )
2015-03-23 18:18:11 +00:00
}
2015-11-04 21:52:14 +00:00
capPath := specPath . Child ( "capacity" )
for r , qty := range pv . Spec . Capacity {
allErrs = append ( allErrs , validateBasicResource ( qty , capPath . Key ( string ( r ) ) ) ... )
2015-05-13 00:44:29 +00:00
}
2015-03-23 18:18:11 +00:00
numVolumes := 0
if pv . Spec . HostPath != nil {
2015-11-15 06:36:25 +00: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 18:18:11 +00:00
}
if pv . Spec . GCEPersistentDisk != nil {
2015-11-15 06:36:25 +00: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 18:18:11 +00:00
}
2015-04-07 21:16:36 +00:00
if pv . Spec . AWSElasticBlockStore != nil {
2015-11-15 06:36:25 +00: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 14:26:39 +00:00
}
2015-04-18 13:31:24 +00:00
if pv . Spec . Glusterfs != nil {
2015-11-15 06:36:25 +00:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "glusterfs" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateGlusterfs ( pv . Spec . Glusterfs , specPath . Child ( "glusterfs" ) ) ... )
}
2015-04-18 13:31:24 +00:00
}
2015-09-25 19:22:23 +00:00
if pv . Spec . Flocker != nil {
2015-11-15 06:36:25 +00: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 19:22:23 +00:00
}
2015-04-30 16:16:51 +00:00
if pv . Spec . NFS != nil {
2015-11-15 06:36:25 +00: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 16:16:51 +00:00
}
2015-04-07 17:22:23 +00:00
if pv . Spec . RBD != nil {
2015-11-15 06:36:25 +00:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "rbd" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateRBDVolumeSource ( pv . Spec . RBD , specPath . Child ( "rbd" ) ) ... )
}
2015-04-07 17:22:23 +00:00
}
2015-04-09 18:05:24 +00:00
if pv . Spec . CephFS != nil {
2015-11-15 06:36:25 +00:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "cephFS" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateCephFSVolumeSource ( pv . Spec . CephFS , specPath . Child ( "cephfs" ) ) ... )
}
2015-04-09 18:05:24 +00:00
}
2015-05-12 20:40:31 +00:00
if pv . Spec . ISCSI != nil {
2015-11-15 06:36:25 +00:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "iscsi" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateISCSIVolumeSource ( pv . Spec . ISCSI , specPath . Child ( "iscsi" ) ) ... )
}
2015-05-12 20:40:31 +00:00
}
2015-04-10 16:54:01 +00:00
if pv . Spec . Cinder != nil {
2015-11-15 06:36:25 +00:00
if numVolumes > 0 {
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "cinder" ) , "may not specify more than 1 volume type" ) )
} else {
numVolumes ++
allErrs = append ( allErrs , validateCinderVolumeSource ( pv . Spec . Cinder , specPath . Child ( "cinder" ) ) ... )
}
2015-04-10 16:54:01 +00:00
}
2015-08-11 15:19:29 +00:00
if pv . Spec . FC != nil {
2015-11-15 06:36:25 +00: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 15:19:29 +00:00
}
2015-09-30 18:31:53 +00:00
if pv . Spec . FlexVolume != nil {
numVolumes ++
allErrs = append ( allErrs , validateFlexVolumeSource ( pv . Spec . FlexVolume , specPath . Child ( "flexVolume" ) ) ... )
}
2015-11-14 20:26:04 +00:00
if numVolumes == 0 {
allErrs = append ( allErrs , field . Required ( specPath , "must specify a volume type" ) )
2015-03-23 18:18:11 +00:00
}
return allErrs
}
2015-03-26 19:50:36 +00: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.
2015-11-06 23:30:52 +00:00
func ValidatePersistentVolumeUpdate ( newPv , oldPv * api . PersistentVolume ) field . ErrorList {
allErrs := field . ErrorList { }
2015-03-26 19:50:36 +00:00
allErrs = ValidatePersistentVolume ( newPv )
newPv . Status = oldPv . Status
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.
2015-11-06 23:30:52 +00:00
func ValidatePersistentVolumeStatusUpdate ( newPv , oldPv * api . PersistentVolume ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newPv . ObjectMeta , & oldPv . ObjectMeta , field . NewPath ( "metadata" ) )
2015-12-09 04:39:18 +00:00
if len ( newPv . ResourceVersion ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "resourceVersion" ) , "" ) )
2015-03-26 19:50:36 +00:00
}
newPv . Spec = oldPv . Spec
return allErrs
}
2015-11-06 23:30:52 +00:00
func ValidatePersistentVolumeClaim ( pvc * api . PersistentVolumeClaim ) field . ErrorList {
allErrs := ValidateObjectMeta ( & pvc . ObjectMeta , true , ValidatePersistentVolumeName , field . NewPath ( "metadata" ) )
specPath := field . NewPath ( "spec" )
2015-03-23 18:18:11 +00:00
if len ( pvc . Spec . AccessModes ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "accessModes" ) , "at least 1 accessMode is required" ) )
2015-03-23 18:18:11 +00:00
}
2015-07-24 19:55:54 +00:00
for _ , mode := range pvc . Spec . AccessModes {
if mode != api . ReadWriteOnce && mode != api . ReadOnlyMany && mode != api . ReadWriteMany {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "accessModes" ) , mode , supportedAccessModes . List ( ) ) )
2015-07-24 19:55:54 +00:00
}
}
2015-03-31 15:37:09 +00:00
if _ , ok := pvc . Spec . Resources . Requests [ api . ResourceStorage ] ; ! ok {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "resources" ) . Key ( string ( api . ResourceStorage ) ) , "" ) )
2015-03-23 18:18:11 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func ValidatePersistentVolumeClaimUpdate ( newPvc , oldPvc * api . PersistentVolumeClaim ) field . ErrorList {
allErrs := field . ErrorList { }
2015-03-26 19:50:36 +00:00
allErrs = ValidatePersistentVolumeClaim ( newPvc )
newPvc . Status = oldPvc . Status
return allErrs
}
2015-11-06 23:30:52 +00:00
func ValidatePersistentVolumeClaimStatusUpdate ( newPvc , oldPvc * api . PersistentVolumeClaim ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newPvc . ObjectMeta , & oldPvc . ObjectMeta , field . NewPath ( "metadata" ) )
2015-12-09 04:39:18 +00:00
if len ( newPvc . ResourceVersion ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "resourceVersion" ) , "" ) )
2015-03-26 19:50:36 +00:00
}
2015-05-13 00:44:29 +00:00
if len ( newPvc . Spec . AccessModes ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "Spec" , "accessModes" ) , "" ) )
2015-05-13 00:44:29 +00:00
}
2015-11-06 23:30:52 +00:00
capPath := field . NewPath ( "status" , "capacity" )
2015-11-04 21:52:14 +00:00
for r , qty := range newPvc . Status . Capacity {
allErrs = append ( allErrs , validateBasicResource ( qty , capPath . Key ( string ( r ) ) ) ... )
2015-05-13 00:44:29 +00:00
}
2015-03-26 19:50:36 +00:00
newPvc . Spec = oldPvc . Spec
return allErrs
}
2015-09-09 17:45:01 +00:00
var supportedPortProtocols = sets . NewString ( string ( api . ProtocolTCP ) , string ( api . ProtocolUDP ) )
2014-11-24 04:03:11 +00:00
2015-11-06 23:30:52 +00:00
func validateContainerPorts ( ports [ ] api . ContainerPort , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2014-07-08 06:20:30 +00:00
2015-09-09 17:45:01 +00:00
allNames := sets . String { }
2015-01-26 17:52:50 +00:00
for i , port := range ports {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
2014-07-08 04:32:56 +00:00
if len ( port . Name ) > 0 {
2015-09-10 22:48:28 +00:00
if ! validation . IsValidPortName ( port . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "name" ) , port . Name , PortNameErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( port . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "name" ) , port . Name ) )
2014-07-08 06:20:30 +00:00
} else {
allNames . Insert ( port . Name )
2014-07-08 04:32:56 +00:00
}
}
2014-08-20 02:57:48 +00:00
if port . ContainerPort == 0 {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "containerPort" ) , port . ContainerPort , PortRangeErrorMsg ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsValidPortNum ( port . ContainerPort ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "containerPort" ) , port . ContainerPort , PortRangeErrorMsg ) )
2014-07-08 04:32:56 +00:00
}
2015-09-10 22:48:28 +00:00
if port . HostPort != 0 && ! validation . IsValidPortNum ( port . HostPort ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "hostPort" ) , port . HostPort , PortRangeErrorMsg ) )
2014-07-08 04:32:56 +00:00
}
if len ( port . Protocol ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "protocol" ) , "" ) )
2015-06-17 00:28:11 +00:00
} else if ! supportedPortProtocols . Has ( string ( port . Protocol ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( idxPath . Child ( "protocol" ) , port . Protocol , supportedPortProtocols . List ( ) ) )
2014-07-08 04:32:56 +00:00
}
}
2014-07-08 06:20:30 +00:00
return allErrs
2014-07-08 04:32:56 +00:00
}
2015-11-06 23:30:52 +00:00
func validateEnv ( vars [ ] api . EnvVar , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2014-07-08 06:20:30 +00:00
2015-01-26 17:52:50 +00:00
for i , ev := range vars {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
2014-07-01 22:56:30 +00:00
if len ( ev . Name ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "name" ) , "" ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsCIdentifier ( ev . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "name" ) , ev . Name , cIdentifierErrorMsg ) )
2014-07-01 22:56:30 +00:00
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateEnvVarValueFrom ( ev , idxPath . Child ( "valueFrom" ) ) ... )
2014-07-01 22:56:30 +00:00
}
2014-07-08 06:20:30 +00:00
return allErrs
2014-07-01 22:56:30 +00:00
}
2015-09-09 17:45:01 +00:00
var validFieldPathExpressionsEnv = sets . NewString ( "metadata.name" , "metadata.namespace" , "status.podIP" )
2015-02-20 05:36:23 +00:00
2015-11-06 23:30:52 +00:00
func validateEnvVarValueFrom ( ev api . EnvVar , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-04-23 20:57:30 +00:00
if ev . ValueFrom == nil {
return allErrs
}
numSources := 0
switch {
2015-05-04 17:31:36 +00:00
case ev . ValueFrom . FieldRef != nil :
2015-04-23 20:57:30 +00:00
numSources ++
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateObjectFieldSelector ( ev . ValueFrom . FieldRef , & validFieldPathExpressionsEnv , fldPath . Child ( "fieldRef" ) ) ... )
2015-04-23 20:57:30 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( ev . Value ) != 0 && numSources != 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , "" , "may not be specified when `value` is not empty" ) )
2015-04-23 20:57:30 +00:00
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateObjectFieldSelector ( fs * api . ObjectFieldSelector , expressions * sets . String , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-04-23 20:57:30 +00:00
2015-12-09 04:39:18 +00:00
if len ( fs . APIVersion ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "apiVersion" ) , "" ) )
2015-12-09 04:39:18 +00:00
} else if len ( fs . FieldPath ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "fieldPath" ) , "" ) )
2015-04-23 20:57:30 +00:00
} else {
internalFieldPath , _ , err := api . Scheme . ConvertFieldLabel ( fs . APIVersion , "Pod" , fs . FieldPath , "" )
if err != nil {
2015-11-14 16:44:50 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "fieldPath" ) , fs . FieldPath , fmt . Sprintf ( "error converting fieldPath: %v" , err ) ) )
2015-02-20 05:36:23 +00:00
} else if ! expressions . Has ( internalFieldPath ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "fieldPath" ) , internalFieldPath , expressions . List ( ) ) )
2015-04-23 20:57:30 +00:00
}
}
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateVolumeMounts ( mounts [ ] api . VolumeMount , volumes sets . String , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2014-07-08 06:20:30 +00:00
2015-01-26 17:52:50 +00:00
for i , mnt := range mounts {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
2014-07-05 02:46:56 +00:00
if len ( mnt . Name ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "name" ) , "" ) )
2014-07-08 06:20:30 +00:00
} else if ! volumes . Has ( mnt . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotFound ( idxPath . Child ( "name" ) , mnt . Name ) )
2014-07-05 02:46:56 +00:00
}
if len ( mnt . MountPath ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "mountPath" ) , "" ) )
2014-07-15 01:39:30 +00:00
}
2014-07-05 02:46:56 +00:00
}
2014-07-08 06:20:30 +00:00
return allErrs
2014-07-05 02:46:56 +00:00
}
2015-11-06 23:30:52 +00:00
func validateProbe ( probe * api . Probe , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-02-15 07:02:07 +00:00
if probe == nil {
return allErrs
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateHandler ( & probe . Handler , fldPath ) ... )
2015-11-18 18:15:16 +00:00
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( probe . InitialDelaySeconds ) , fldPath . Child ( "initialDelaySeconds" ) ) ... )
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( probe . TimeoutSeconds ) , fldPath . Child ( "timeoutSeconds" ) ) ... )
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( probe . PeriodSeconds ) , fldPath . Child ( "periodSeconds" ) ) ... )
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( probe . SuccessThreshold ) , fldPath . Child ( "successThreshold" ) ) ... )
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( probe . FailureThreshold ) , fldPath . Child ( "failureThreshold" ) ) ... )
2015-02-15 07:02:07 +00:00
return allErrs
}
2015-05-19 17:17:53 +00:00
// AccumulateUniqueHostPorts extracts each HostPort of each Container,
2014-07-08 04:32:56 +00:00
// accumulating the results and returning an error if any ports conflict.
2015-11-06 23:30:52 +00:00
func AccumulateUniqueHostPorts ( containers [ ] api . Container , accumulator * sets . String , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2014-07-08 06:20:30 +00:00
2015-01-26 17:52:50 +00:00
for ci , ctr := range containers {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( ci )
portsPath := idxPath . Child ( "ports" )
2014-07-08 04:32:56 +00:00
for pi := range ctr . Ports {
2015-11-04 21:52:14 +00:00
idxPath := portsPath . Index ( pi )
2015-05-19 05:18:40 +00:00
port := ctr . Ports [ pi ] . HostPort
2014-08-19 22:18:49 +00:00
if port == 0 {
continue
}
2015-05-19 05:18:40 +00:00
str := fmt . Sprintf ( "%d/%s" , port , ctr . Ports [ pi ] . Protocol )
2015-05-19 17:17:53 +00:00
if accumulator . Has ( str ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "hostPort" ) , str ) )
2014-07-08 06:20:30 +00:00
} else {
2015-05-19 17:17:53 +00:00
accumulator . Insert ( str )
2014-07-08 04:32:56 +00:00
}
}
}
2014-07-08 06:20:30 +00:00
return allErrs
2014-07-08 04:32:56 +00:00
}
2014-09-02 10:00:28 +00:00
// checkHostPortConflicts checks for colliding Port.HostPort values across
// a slice of containers.
2015-11-06 23:30:52 +00:00
func checkHostPortConflicts ( containers [ ] api . Container , fldPath * field . Path ) field . ErrorList {
2015-09-09 17:45:01 +00:00
allPorts := sets . String { }
2015-11-04 21:52:14 +00:00
return AccumulateUniqueHostPorts ( containers , & allPorts , fldPath )
2014-07-08 04:32:56 +00:00
}
2015-11-06 23:30:52 +00:00
func validateExecAction ( exec * api . ExecAction , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2014-09-12 23:04:10 +00:00
if len ( exec . Command ) == 0 {
2015-11-14 20:26:04 +00:00
allErrors = append ( allErrors , field . Required ( fldPath . Child ( "command" ) , "" ) )
2014-09-12 23:04:10 +00:00
}
return allErrors
}
2015-11-06 23:30:52 +00:00
func validateHTTPGetAction ( http * api . HTTPGetAction , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2014-09-12 23:04:10 +00:00
if len ( http . Path ) == 0 {
2015-11-14 20:26:04 +00:00
allErrors = append ( allErrors , field . Required ( fldPath . Child ( "path" ) , "" ) )
2014-09-12 23:04:10 +00:00
}
2015-11-18 18:15:16 +00:00
if http . Port . Type == intstr . Int && ! validation . IsValidPortNum ( http . Port . IntValue ( ) ) {
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . Invalid ( fldPath . Child ( "port" ) , http . Port , PortRangeErrorMsg ) )
2015-11-10 06:28:45 +00:00
} else if http . Port . Type == intstr . String && ! validation . IsValidPortName ( http . Port . StrVal ) {
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . Invalid ( fldPath . Child ( "port" ) , http . Port . StrVal , PortNameErrorMsg ) )
2015-02-28 01:33:58 +00:00
}
2015-09-09 17:45:01 +00:00
supportedSchemes := sets . NewString ( string ( api . URISchemeHTTP ) , string ( api . URISchemeHTTPS ) )
2015-06-25 17:53:41 +00:00
if ! supportedSchemes . Has ( string ( http . Scheme ) ) {
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . Invalid ( fldPath . Child ( "scheme" ) , http . Scheme , fmt . Sprintf ( "must be one of %v" , supportedSchemes . List ( ) ) ) )
2015-06-25 17:53:41 +00:00
}
2015-02-28 01:33:58 +00:00
return allErrors
}
2015-11-06 23:30:52 +00:00
func validateTCPSocketAction ( tcp * api . TCPSocketAction , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2015-11-18 18:15:16 +00:00
if tcp . Port . Type == intstr . Int && ! validation . IsValidPortNum ( tcp . Port . IntValue ( ) ) {
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . Invalid ( fldPath . Child ( "port" ) , tcp . Port , PortRangeErrorMsg ) )
2015-11-10 06:28:45 +00:00
} else if tcp . Port . Type == intstr . String && ! validation . IsValidPortName ( tcp . Port . StrVal ) {
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . Invalid ( fldPath . Child ( "port" ) , tcp . Port . StrVal , PortNameErrorMsg ) )
2015-02-28 01:33:58 +00:00
}
2014-09-12 23:04:10 +00:00
return allErrors
}
2015-11-06 23:30:52 +00:00
func validateHandler ( handler * api . Handler , fldPath * field . Path ) field . ErrorList {
2014-11-20 22:24:10 +00:00
numHandlers := 0
2015-11-06 23:30:52 +00:00
allErrors := field . ErrorList { }
2014-09-12 23:04:10 +00:00
if handler . Exec != nil {
2015-11-15 06:36:25 +00: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 22:24:10 +00:00
}
if handler . HTTPGet != nil {
2015-11-15 06:36:25 +00: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 22:24:10 +00:00
}
2015-02-28 01:33:58 +00:00
if handler . TCPSocket != nil {
2015-11-15 06:36:25 +00: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-28 01:33:58 +00:00
}
2015-11-14 20:26:04 +00:00
if numHandlers == 0 {
allErrors = append ( allErrors , field . Required ( fldPath , "must specify a handler type" ) )
2014-09-12 23:04:10 +00:00
}
return allErrors
}
2015-11-06 23:30:52 +00:00
func validateLifecycle ( lifecycle * api . Lifecycle , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2014-09-12 23:04:10 +00:00
if lifecycle . PostStart != nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateHandler ( lifecycle . PostStart , fldPath . Child ( "postStart" ) ) ... )
2014-09-12 23:04:10 +00:00
}
if lifecycle . PreStop != nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateHandler ( lifecycle . PreStop , fldPath . Child ( "preStop" ) ) ... )
2014-09-12 23:04:10 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
var supportedPullPolicies = sets . NewString ( string ( api . PullAlways ) , string ( api . PullIfNotPresent ) , string ( api . PullNever ) )
2015-11-06 23:30:52 +00:00
func validatePullPolicy ( policy api . PullPolicy , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2015-01-16 23:02:36 +00:00
2015-11-04 21:52:14 +00:00
switch policy {
2015-01-16 23:02:36 +00:00
case api . PullAlways , api . PullIfNotPresent , api . PullNever :
break
2015-01-26 17:52:50 +00:00
case "" :
2015-11-14 20:26:04 +00:00
allErrors = append ( allErrors , field . Required ( fldPath , "" ) )
2015-01-16 23:02:36 +00:00
default :
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . NotSupported ( fldPath , policy , supportedPullPolicies . List ( ) ) )
2015-01-16 23:02:36 +00:00
}
return allErrors
}
2015-11-06 23:30:52 +00:00
func validateContainers ( containers [ ] api . Container , volumes sets . String , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2014-07-08 06:20:30 +00:00
2015-03-18 15:00:18 +00:00
if len ( containers ) == 0 {
2015-11-14 20:26:04 +00:00
return append ( allErrs , field . Required ( fldPath , "" ) )
2015-03-18 15:00:18 +00:00
}
2015-09-09 17:45:01 +00:00
allNames := sets . String { }
2015-01-26 17:52:50 +00:00
for i , ctr := range containers {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
2014-08-20 02:57:48 +00:00
if len ( ctr . Name ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "name" ) , "" ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsDNS1123Label ( ctr . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "name" ) , ctr . Name , DNS1123LabelErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( ctr . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "name" ) , ctr . Name ) )
2014-07-08 06:20:30 +00:00
} else {
allNames . Insert ( ctr . Name )
2014-07-01 22:14:25 +00:00
}
if len ( ctr . Image ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "image" ) , "" ) )
2014-07-05 02:46:56 +00:00
}
2014-09-12 23:04:10 +00:00
if ctr . Lifecycle != nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateLifecycle ( ctr . Lifecycle , idxPath . Child ( "lifecycle" ) ) ... )
2014-09-12 23:04:10 +00:00
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateProbe ( ctr . LivenessProbe , idxPath . Child ( "livenessProbe" ) ) ... )
2015-11-05 23:38:46 +00:00
// Liveness-specific validation
if ctr . LivenessProbe != nil && ctr . LivenessProbe . SuccessThreshold != 1 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "livenessProbe" , "successThreshold" ) , ctr . LivenessProbe . SuccessThreshold , "must be 1" ) )
2015-11-05 23:38:46 +00:00
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateProbe ( ctr . ReadinessProbe , idxPath . Child ( "readinessProbe" ) ) ... )
allErrs = append ( allErrs , validateContainerPorts ( ctr . Ports , idxPath . Child ( "ports" ) ) ... )
allErrs = append ( allErrs , validateEnv ( ctr . Env , idxPath . Child ( "env" ) ) ... )
allErrs = append ( allErrs , validateVolumeMounts ( ctr . VolumeMounts , volumes , idxPath . Child ( "volumeMounts" ) ) ... )
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 22:14:25 +00:00
}
2014-07-08 04:32:56 +00:00
// Check for colliding ports across all containers.
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , checkHostPortConflicts ( containers , fldPath ) ... )
2014-07-09 00:33:15 +00:00
2014-07-10 11:46:35 +00:00
return allErrs
2014-07-01 22:14:25 +00:00
}
2015-11-06 23:30:52 +00:00
func validateRestartPolicy ( restartPolicy * api . RestartPolicy , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2015-03-14 01:38:07 +00:00
switch * restartPolicy {
case api . RestartPolicyAlways , api . RestartPolicyOnFailure , api . RestartPolicyNever :
break
case "" :
2015-11-14 20:26:04 +00:00
allErrors = append ( allErrors , field . Required ( fldPath , "" ) )
2015-03-14 01:38:07 +00:00
default :
2015-06-24 22:09:43 +00:00
validValues := [ ] string { string ( api . RestartPolicyAlways ) , string ( api . RestartPolicyOnFailure ) , string ( api . RestartPolicyNever ) }
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . NotSupported ( fldPath , * restartPolicy , validValues ) )
2014-08-26 18:25:17 +00:00
}
2015-03-14 01:38:07 +00:00
2014-08-26 18:25:17 +00:00
return allErrors
}
2014-07-22 18:45:12 +00:00
2015-11-06 23:30:52 +00:00
func validateDNSPolicy ( dnsPolicy * api . DNSPolicy , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2014-11-12 05:21:40 +00:00
switch * dnsPolicy {
case api . DNSClusterFirst , api . DNSDefault :
break
2015-01-26 17:52:50 +00:00
case "" :
2015-11-14 20:26:04 +00:00
allErrors = append ( allErrors , field . Required ( fldPath , "" ) )
2014-11-12 05:21:40 +00:00
default :
2015-06-24 22:09:43 +00:00
validValues := [ ] string { string ( api . DNSClusterFirst ) , string ( api . DNSDefault ) }
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . NotSupported ( fldPath , dnsPolicy , validValues ) )
2014-11-12 05:21:40 +00:00
}
return allErrors
}
2015-11-06 23:30:52 +00:00
func validateHostNetwork ( hostNetwork bool , containers [ ] api . Container , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2015-03-23 23:34:35 +00:00
if hostNetwork {
2015-11-04 21:52:14 +00: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 23:34:35 +00:00
if port . HostPort != port . ContainerPort {
2015-11-14 20:26:04 +00:00
allErrors = append ( allErrors , field . Invalid ( idxPath . Child ( "containerPort" ) , port . ContainerPort , "must match `hostPort` when `hostNetwork` is true" ) )
2015-03-23 23:34:35 +00:00
}
}
}
}
return allErrors
}
2015-11-04 21:52:14 +00: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.
2015-11-06 23:30:52 +00:00
func validateImagePullSecrets ( imagePullSecrets [ ] api . LocalObjectReference , fldPath * field . Path ) field . ErrorList {
allErrors := field . ErrorList { }
2015-05-08 17:53:00 +00:00
for i , currPullSecret := range imagePullSecrets {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
2015-05-19 12:39:46 +00:00
strippedRef := api . LocalObjectReference { Name : currPullSecret . Name }
2015-05-08 17:53:00 +00:00
if ! reflect . DeepEqual ( strippedRef , currPullSecret ) {
2015-11-10 20:59:41 +00:00
allErrors = append ( allErrors , field . Invalid ( idxPath , currPullSecret , "only name may be set" ) )
2015-05-08 17:53:00 +00:00
}
}
return allErrors
}
2014-09-02 10:00:28 +00:00
// ValidatePod tests if required fields in the pod are set.
2015-11-06 23:30:52 +00:00
func ValidatePod ( pod * api . Pod ) field . ErrorList {
allErrs := ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidatePodSpec ( & pod . Spec , field . NewPath ( "spec" ) ) ... )
2014-10-23 20:14:13 +00:00
return allErrs
}
2014-11-07 02:08:46 +00: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.
2015-11-06 23:30:52 +00:00
func ValidatePodSpec ( spec * api . PodSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2014-11-07 02:08:46 +00:00
2015-11-04 21:52:14 +00:00
allVolumes , vErrs := validateVolumes ( spec . Volumes , fldPath . Child ( "volumes" ) )
allErrs = append ( allErrs , vErrs ... )
allErrs = append ( allErrs , validateContainers ( spec . Containers , allVolumes , fldPath . Child ( "containers" ) ) ... )
allErrs = append ( allErrs , validateRestartPolicy ( & spec . RestartPolicy , fldPath . Child ( "restartPolicy" ) ) ... )
allErrs = append ( allErrs , validateDNSPolicy ( & spec . DNSPolicy , fldPath . Child ( "dnsPolicy" ) ) ... )
allErrs = append ( allErrs , ValidateLabels ( spec . NodeSelector , fldPath . Child ( "nodeSelector" ) ) ... )
allErrs = append ( allErrs , ValidatePodSecurityContext ( spec . SecurityContext , spec , fldPath , fldPath . Child ( "securityContext" ) ) ... )
allErrs = append ( allErrs , validateImagePullSecrets ( spec . ImagePullSecrets , fldPath . Child ( "imagePullSecrets" ) ) ... )
2015-06-19 02:35:42 +00:00
if len ( spec . ServiceAccountName ) > 0 {
if ok , msg := ValidateServiceAccountName ( spec . ServiceAccountName , false ) ; ! ok {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "serviceAccountName" ) , spec . ServiceAccountName , msg ) )
2015-06-11 21:16:58 +00:00
}
}
2015-05-09 05:01:43 +00:00
2015-11-30 19:35:34 +00:00
if len ( spec . NodeName ) > 0 {
if ok , msg := ValidateNodeName ( spec . NodeName , false ) ; ! ok {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "nodeName" ) , spec . NodeName , msg ) )
2015-11-30 19:35:34 +00:00
}
}
2015-05-09 05:01:43 +00:00
if spec . ActiveDeadlineSeconds != nil {
if * spec . ActiveDeadlineSeconds <= 0 {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "activeDeadlineSeconds" ) , spec . ActiveDeadlineSeconds , "must be greater than 0" ) )
2015-05-09 05:01:43 +00:00
}
}
2014-11-07 02:08:46 +00:00
return allErrs
}
2015-09-14 21:56:51 +00:00
// ValidatePodSecurityContext test that the specified PodSecurityContext has valid data.
2015-11-06 23:30:52 +00:00
func ValidatePodSecurityContext ( securityContext * api . PodSecurityContext , spec * api . PodSpec , specPath , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-09-14 21:56:51 +00:00
if securityContext != nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateHostNetwork ( securityContext . HostNetwork , spec . Containers , specPath . Child ( "containers" ) ) ... )
2015-11-20 02:42:02 +00:00
if securityContext . FSGroup != nil && ! validation . IsValidGroupId ( * securityContext . FSGroup ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "fsGroup" ) , * ( securityContext . FSGroup ) , IdRangeErrorMsg ) )
}
if securityContext . RunAsUser != nil && ! validation . IsValidUserId ( * securityContext . RunAsUser ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "runAsUser" ) , * ( securityContext . RunAsUser ) , IdRangeErrorMsg ) )
}
for i , gid := range securityContext . SupplementalGroups {
if ! validation . IsValidGroupId ( gid ) {
supplementalGroup := fmt . Sprintf ( ` supplementalGroups[%d] ` , i )
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( supplementalGroup ) , gid , IdRangeErrorMsg ) )
}
}
2015-09-14 21:56:51 +00:00
}
return allErrs
}
2015-02-11 23:32:54 +00: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.
2015-11-06 23:30:52 +00:00
func ValidatePodUpdate ( newPod , oldPod * api . Pod ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newPod . ObjectMeta , & oldPod . ObjectMeta , field . NewPath ( "metadata" ) )
2014-10-10 03:30:34 +00:00
2015-11-06 23:30:52 +00:00
specPath := field . NewPath ( "spec" )
2014-11-13 15:52:13 +00:00
if len ( newPod . Spec . Containers ) != len ( oldPod . Spec . Containers ) {
2015-07-31 23:43:39 +00:00
//TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( specPath . Child ( "containers" ) , "pod updates may not add or remove containers" ) )
2014-10-10 03:30:34 +00:00
return allErrs
}
pod := * newPod
// Tricky, we need to copy the container list so that we don't overwrite the update
var newContainers [ ] api . Container
2014-11-13 15:52:13 +00:00
for ix , container := range pod . Spec . Containers {
container . Image = oldPod . Spec . Containers [ ix ] . Image
2014-10-10 03:30:34 +00:00
newContainers = append ( newContainers , container )
}
2014-11-13 15:52:13 +00:00
pod . Spec . Containers = newContainers
2015-01-05 21:38:39 +00:00
if ! api . Semantic . DeepEqual ( pod . Spec , oldPod . Spec ) {
2015-07-31 23:43:39 +00:00
//TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( specPath , "pod updates may not change fields other than `containers[*].image`" ) )
2014-10-10 03:30:34 +00:00
}
2015-01-27 23:55:54 +00:00
2015-02-11 23:32:54 +00:00
newPod . Status = oldPod . Status
2014-10-10 03:30:34 +00:00
return allErrs
}
2015-02-24 05:42:27 +00: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.
2015-11-06 23:30:52 +00:00
func ValidatePodStatusUpdate ( newPod , oldPod * api . Pod ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newPod . ObjectMeta , & oldPod . ObjectMeta , field . NewPath ( "metadata" ) )
2015-02-24 05:42:27 +00:00
// TODO: allow change when bindings are properly decoupled from pods
2015-05-22 23:40:57 +00:00
if newPod . Spec . NodeName != oldPod . Spec . NodeName {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "status" , "nodeName" ) , "may not be changed directly" ) )
2015-02-24 05:42:27 +00:00
}
2015-03-09 14:23:52 +00:00
// For status update we ignore changes to pod spec.
2015-02-24 05:42:27 +00:00
newPod . Spec = oldPod . Spec
return allErrs
}
2015-12-10 14:08:21 +00:00
// ValidatePodBinding tests if required fields in the pod binding are legal.
func ValidatePodBinding ( binding * api . Binding ) field . ErrorList {
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 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "target" , "name" ) , "" ) )
2015-12-10 14:08:21 +00:00
}
return allErrs
}
2015-03-04 00:54:17 +00:00
// ValidatePodTemplate tests if required fields in the pod template are set.
2015-11-06 23:30:52 +00:00
func ValidatePodTemplate ( pod * api . PodTemplate ) field . ErrorList {
allErrs := ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & pod . Template , field . NewPath ( "template" ) ) ... )
2015-03-04 00:54:17 +00: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.
2015-11-06 23:30:52 +00:00
func ValidatePodTemplateUpdate ( newPod , oldPod * api . PodTemplate ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & oldPod . ObjectMeta , & newPod . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & newPod . Template , field . NewPath ( "template" ) ) ... )
2015-03-04 00:54:17 +00:00
return allErrs
}
2015-09-09 17:45:01 +00:00
var supportedSessionAffinityType = sets . NewString ( string ( api . ServiceAffinityClientIP ) , string ( api . ServiceAffinityNone ) )
var supportedServiceType = sets . NewString ( string ( api . ServiceTypeClusterIP ) , string ( api . ServiceTypeNodePort ) ,
2015-05-22 21:49:26 +00:00
string ( api . ServiceTypeLoadBalancer ) )
2014-12-17 12:52:11 +00:00
2014-07-15 13:53:39 +00:00
// ValidateService tests if required fields in the service are set.
2015-11-06 23:30:52 +00:00
func ValidateService ( service * api . Service ) field . ErrorList {
allErrs := ValidateObjectMeta ( & service . ObjectMeta , true , ValidateServiceName , field . NewPath ( "metadata" ) )
2015-01-27 23:55:54 +00:00
2015-11-06 23:30:52 +00:00
specPath := field . NewPath ( "spec" )
2015-11-06 17:53:57 +00:00
if len ( service . Spec . Ports ) == 0 && service . Spec . ClusterIP != api . ClusterIPNone {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "ports" ) , "" ) )
2014-08-22 21:44:21 +00:00
}
2015-07-09 05:02:10 +00:00
if service . Spec . Type == api . ServiceTypeLoadBalancer {
for ix := range service . Spec . Ports {
port := & service . Spec . Ports [ ix ]
2015-11-14 20:26:04 +00:00
// This is a workaround for broken cloud environments that
// over-open firewalls. Hopefully it can go away when more clouds
// understand containers better.
2015-07-09 05:02:10 +00:00
if port . Port == 10250 {
2015-11-04 21:52:14 +00:00
portPath := specPath . Child ( "ports" ) . Index ( ix )
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( portPath , port . Port , "may not expose port 10250 externally since it is used by kubelet" ) )
2015-07-09 05:02:10 +00:00
}
}
}
2015-11-27 08:09:13 +00:00
isHeadlessService := service . Spec . ClusterIP == api . ClusterIPNone
2015-09-09 17:45:01 +00:00
allPortNames := sets . String { }
2015-11-04 21:52:14 +00:00
portsPath := specPath . Child ( "ports" )
2015-03-13 15:16:41 +00:00
for i := range service . Spec . Ports {
2015-11-04 21:52:14 +00:00
portPath := portsPath . Index ( i )
allErrs = append ( allErrs , validateServicePort ( & service . Spec . Ports [ i ] , len ( service . Spec . Ports ) > 1 , isHeadlessService , & allPortNames , portPath ) ... )
2015-02-28 01:33:58 +00:00
}
2014-11-18 17:49:00 +00:00
if service . Spec . Selector != nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateLabels ( service . Spec . Selector , specPath . Child ( "selector" ) ) ... )
2014-07-10 19:45:01 +00:00
}
2014-11-18 17:49:00 +00:00
2015-12-09 04:39:18 +00:00
if len ( service . Spec . SessionAffinity ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "sessionAffinity" ) , "" ) )
2014-12-29 22:39:09 +00:00
} else if ! supportedSessionAffinityType . Has ( string ( service . Spec . SessionAffinity ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "sessionAffinity" ) , service . Spec . SessionAffinity , supportedSessionAffinityType . List ( ) ) )
2014-12-17 12:52:11 +00:00
}
2015-03-16 21:36:30 +00:00
if api . IsServiceIPSet ( service ) {
2015-05-23 20:41:11 +00:00
if ip := net . ParseIP ( service . Spec . ClusterIP ) ; ip == nil {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( specPath . Child ( "clusterIP" ) , service . Spec . ClusterIP , "must be empty, 'None', or a valid IP address" ) )
2015-03-16 21:36:30 +00:00
}
}
2015-11-04 21:52:14 +00:00
ipPath := specPath . Child ( "externalIPs" )
for i , ip := range service . Spec . ExternalIPs {
idxPath := ipPath . Index ( i )
2015-03-16 14:03:05 +00:00
if ip == "0.0.0.0" {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath , ip , "must be a valid IP address" ) )
2015-03-16 14:03:05 +00:00
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateIpIsNotLinkLocalOrLoopback ( ip , idxPath ) ... )
2015-03-16 14:03:05 +00:00
}
2015-12-09 04:39:18 +00:00
if len ( service . Spec . Type ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( specPath . Child ( "type" ) , "" ) )
2015-05-22 21:49:26 +00:00
} else if ! supportedServiceType . Has ( string ( service . Spec . Type ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( specPath . Child ( "type" ) , service . Spec . Type , supportedServiceType . List ( ) ) )
2015-05-22 21:49:26 +00:00
}
if service . Spec . Type == api . ServiceTypeLoadBalancer {
2015-11-04 21:52:14 +00:00
portsPath := specPath . Child ( "ports" )
2015-04-03 19:06:25 +00:00
for i := range service . Spec . Ports {
2015-11-04 21:52:14 +00:00
portPath := portsPath . Index ( i )
2015-09-28 20:57:58 +00:00
if ! supportedPortProtocols . Has ( string ( service . Spec . Ports [ i ] . Protocol ) ) {
2015-12-06 21:23:56 +00:00
allErrs = append ( allErrs , field . Invalid ( portPath . Child ( "protocol" ) , service . Spec . Ports [ i ] . Protocol , "cannot create an external load balancer with non-TCP/UDP ports" ) )
2015-04-03 19:06:25 +00:00
}
}
}
2015-05-20 15:59:34 +00:00
if service . Spec . Type == api . ServiceTypeClusterIP {
2015-11-04 21:52:14 +00:00
portsPath := specPath . Child ( "ports" )
2015-05-20 15:59:34 +00:00
for i := range service . Spec . Ports {
2015-11-04 21:52:14 +00:00
portPath := portsPath . Index ( i )
2015-05-20 15:59:34 +00:00
if service . Spec . Ports [ i ] . NodePort != 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( portPath . Child ( "nodePort" ) , service . Spec . Ports [ i ] . NodePort , "may not be used when `type` is 'ClusterIP'" ) )
2015-05-20 15:59:34 +00:00
}
}
}
2015-05-22 21:54:19 +00:00
// Check for duplicate NodePorts, considering (protocol,port) pairs
2015-11-04 21:52:14 +00:00
portsPath = specPath . Child ( "ports" )
2015-05-22 21:54:19 +00:00
nodePorts := make ( map [ api . ServicePort ] bool )
for i := range service . Spec . Ports {
port := & service . Spec . Ports [ i ]
if port . NodePort == 0 {
continue
}
2015-11-04 21:52:14 +00:00
portPath := portsPath . Index ( i )
2015-05-22 21:54:19 +00:00
var key api . ServicePort
key . Protocol = port . Protocol
key . NodePort = port . NodePort
_ , found := nodePorts [ key ]
if found {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Duplicate ( portPath . Child ( "nodePort" ) , port . NodePort ) )
2015-05-22 21:54:19 +00:00
}
nodePorts [ key ] = true
}
2014-08-16 20:48:48 +00:00
return allErrs
2014-07-10 19:45:01 +00:00
}
2014-07-25 16:15:17 +00:00
2015-11-06 23:30:52 +00:00
func validateServicePort ( sp * api . ServicePort , requireName , isHeadlessService bool , allNames * sets . String , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-03-13 15:16:41 +00:00
2015-12-09 04:39:18 +00:00
if requireName && len ( sp . Name ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
2015-12-09 04:39:18 +00:00
} else if len ( sp . Name ) != 0 {
2015-09-10 22:48:28 +00:00
if ! validation . IsDNS1123Label ( sp . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , sp . Name , DNS1123LabelErrorMsg ) )
2015-05-05 18:51:51 +00:00
} else if allNames . Has ( sp . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Duplicate ( fldPath . Child ( "name" ) , sp . Name ) )
2015-04-29 02:08:42 +00:00
} else {
allNames . Insert ( sp . Name )
2015-03-13 15:16:41 +00:00
}
}
2015-09-10 22:48:28 +00:00
if ! validation . IsValidPortNum ( sp . Port ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "port" ) , sp . Port , PortRangeErrorMsg ) )
2015-03-13 15:16:41 +00:00
}
if len ( sp . Protocol ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "protocol" ) , "" ) )
2015-06-17 00:28:11 +00:00
} else if ! supportedPortProtocols . Has ( string ( sp . Protocol ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "protocol" ) , sp . Protocol , supportedPortProtocols . List ( ) ) )
2015-03-13 15:16:41 +00:00
}
2015-11-18 18:15:16 +00:00
if sp . TargetPort . Type == intstr . Int && ! validation . IsValidPortNum ( sp . TargetPort . IntValue ( ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "targetPort" ) , sp . TargetPort , PortRangeErrorMsg ) )
2015-06-12 16:33:11 +00:00
}
2015-11-10 06:28:45 +00:00
if sp . TargetPort . Type == intstr . String && ! validation . IsValidPortName ( sp . TargetPort . StrVal ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "targetPort" ) , sp . TargetPort , PortNameErrorMsg ) )
2015-03-13 15:16:41 +00:00
}
2015-11-27 08:09:13 +00:00
if isHeadlessService {
if sp . TargetPort . Type == intstr . String || ( sp . TargetPort . Type == intstr . Int && sp . Port != sp . TargetPort . IntValue ( ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "port" ) , sp . Port , "must be equal to targetPort when clusterIP = None" ) )
2015-11-27 08:09:13 +00:00
}
}
2015-03-13 15:16:41 +00:00
return allErrs
}
2015-01-27 23:55:54 +00:00
// ValidateServiceUpdate tests if required fields in the service are set during an update
2015-11-06 23:30:52 +00:00
func ValidateServiceUpdate ( service , oldService * api . Service ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & service . ObjectMeta , & oldService . ObjectMeta , field . NewPath ( "metadata" ) )
2015-01-27 23:55:54 +00:00
2015-10-12 03:26:17 +00:00
if api . IsServiceIPSet ( oldService ) {
2015-11-06 23:30:52 +00:00
allErrs = append ( allErrs , ValidateImmutableField ( service . Spec . ClusterIP , oldService . Spec . ClusterIP , field . NewPath ( "spec" , "clusterIP" ) ) ... )
2015-01-27 23:55:54 +00:00
}
2015-03-31 16:30:56 +00:00
allErrs = append ( allErrs , ValidateService ( service ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
}
2014-07-25 16:15:17 +00:00
// ValidateReplicationController tests if required fields in the replication controller are set.
2015-11-06 23:30:52 +00:00
func ValidateReplicationController ( controller * api . ReplicationController ) field . ErrorList {
allErrs := ValidateObjectMeta ( & controller . ObjectMeta , true , ValidateReplicationControllerName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec , field . NewPath ( "spec" ) ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
}
// ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
2015-11-06 23:30:52 +00:00
func ValidateReplicationControllerUpdate ( controller , oldController * api . ReplicationController ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec , field . NewPath ( "spec" ) ) ... )
2014-09-16 16:54:38 +00:00
return allErrs
}
2015-09-28 19:39:57 +00:00
// ValidateReplicationControllerStatusUpdate tests if required fields in the replication controller are set.
2015-11-06 23:30:52 +00:00
func ValidateReplicationControllerStatusUpdate ( controller , oldController * api . ReplicationController ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta , field . NewPath ( "metadata" ) )
statusPath := field . NewPath ( "status" )
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( controller . Status . Replicas ) , statusPath . Child ( "replicas" ) ) ... )
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( controller . Status . ObservedGeneration ) , statusPath . Child ( "observedGeneration" ) ) ... )
2015-09-28 19:39:57 +00:00
return allErrs
}
2015-08-25 19:07:03 +00:00
// Validates that the given selector is non-empty.
2015-11-06 23:30:52 +00:00
func ValidateNonEmptySelector ( selectorMap map [ string ] string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-08-25 19:07:03 +00:00
selector := labels . Set ( selectorMap ) . AsSelector ( )
2014-11-07 02:08:46 +00:00
if selector . Empty ( ) {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath , "" ) )
2014-08-22 00:02:39 +00:00
}
2015-08-25 19:07:03 +00:00
return allErrs
}
2014-11-07 02:08:46 +00:00
2015-08-25 19:07:03 +00:00
// Validates the given template and ensures that it is in accordance with the desrired selector and replicas.
2015-11-06 23:30:52 +00:00
func ValidatePodTemplateSpecForRC ( template * api . PodTemplateSpec , selectorMap map [ string ] string , replicas int , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-08-25 19:07:03 +00:00
if template == nil {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath , "" ) )
2014-11-07 02:08:46 +00:00
} else {
2015-08-25 19:07:03 +00: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 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "metadata" , "labels" ) , template . Labels , "`selector` does not match template `labels`" ) )
2015-08-25 19:07:03 +00:00
}
2015-07-25 19:08:34 +00:00
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidatePodTemplateSpec ( template , fldPath ) ... )
2015-08-25 19:07:03 +00:00
if replicas > 1 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateReadOnlyPersistentDisks ( template . Spec . Volumes , fldPath . Child ( "spec" , "volumes" ) ) ... )
2015-07-25 19:08:34 +00:00
}
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
2015-08-25 19:07:03 +00:00
if template . Spec . RestartPolicy != api . RestartPolicyAlways {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "spec" , "restartPolicy" ) , template . Spec . RestartPolicy , [ ] string { string ( api . RestartPolicyAlways ) } ) )
2014-11-18 04:08:23 +00:00
}
2014-08-04 19:02:51 +00:00
}
2014-08-05 17:58:43 +00:00
return allErrs
}
2014-11-07 02:08:46 +00:00
2015-08-25 19:07:03 +00:00
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
2015-11-06 23:30:52 +00:00
func ValidateReplicationControllerSpec ( spec * api . ReplicationControllerSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateNonEmptySelector ( spec . Selector , fldPath . Child ( "selector" ) ) ... )
allErrs = append ( allErrs , ValidatePositiveField ( int64 ( spec . Replicas ) , fldPath . Child ( "replicas" ) ) ... )
allErrs = append ( allErrs , ValidatePodTemplateSpecForRC ( spec . Template , spec . Selector , spec . Replicas , fldPath . Child ( "template" ) ) ... )
2015-08-25 19:07:03 +00:00
return allErrs
}
2014-11-07 02:08:46 +00:00
// ValidatePodTemplateSpec validates the spec of a pod template
2015-11-06 23:30:52 +00:00
func ValidatePodTemplateSpec ( spec * api . PodTemplateSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateLabels ( spec . Labels , fldPath . Child ( "labels" ) ) ... )
allErrs = append ( allErrs , ValidateAnnotations ( spec . Annotations , fldPath . Child ( "annotations" ) ) ... )
allErrs = append ( allErrs , ValidatePodSpec ( & spec . Spec , fldPath . Child ( "spec" ) ) ... )
2014-11-07 02:08:46 +00:00
return allErrs
}
2015-11-06 23:30:52 +00:00
func ValidateReadOnlyPersistentDisks ( volumes [ ] api . Volume , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-11-04 21:52:14 +00:00
for i := range volumes {
vol := & volumes [ i ]
idxPath := fldPath . Index ( i )
2015-03-03 22:48:55 +00:00
if vol . GCEPersistentDisk != nil {
if vol . GCEPersistentDisk . ReadOnly == false {
2015-11-14 20:26:04 +00: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 17:58:43 +00:00
}
}
2015-03-06 14:26:39 +00:00
// TODO: What to do for AWS? It doesn't support replicas
2014-08-05 17:58:43 +00:00
}
2014-08-16 20:48:48 +00:00
return allErrs
2014-07-25 16:15:17 +00:00
}
2014-10-08 19:56:02 +00:00
2015-04-22 17:55:05 +00:00
// ValidateNode tests if required fields in the node are set.
2015-11-06 23:30:52 +00:00
func ValidateNode ( node * api . Node ) field . ErrorList {
allErrs := ValidateObjectMeta ( & node . ObjectMeta , false , ValidateNodeName , field . NewPath ( "metadata" ) )
2015-04-01 23:11:33 +00:00
// Only validate spec. All status fields are optional and can be updated later.
2015-03-24 17:24:07 +00:00
// external ID is required.
if len ( node . Spec . ExternalID ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "spec" , "externalID" ) , "" ) )
2015-03-24 17:24:07 +00:00
}
// TODO(rjnagal): Ignore PodCIDR till its completely implemented.
2014-11-12 17:38:15 +00:00
return allErrs
}
2014-11-17 18:22:27 +00:00
2015-04-22 17:55:05 +00:00
// ValidateNodeUpdate tests to make sure a node update can be applied. Modifies oldNode.
2015-11-06 23:30:52 +00:00
func ValidateNodeUpdate ( node , oldNode * api . Node ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & node . ObjectMeta , & oldNode . ObjectMeta , field . NewPath ( "metadata" ) )
2014-12-12 05:39:56 +00:00
2015-01-16 22:28:20 +00:00
// TODO: Enable the code once we have better api object.status update model. Currently,
// anyone can update node status.
2015-04-22 17:55:05 +00:00
// if !api.Semantic.DeepEqual(node.Status, api.NodeStatus{}) {
2015-11-14 16:44:50 +00:00
// allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty"))
2015-01-16 22:28:20 +00:00
// }
2014-12-12 05:39:56 +00:00
2015-04-22 17:55:05 +00:00
// Validte no duplicate addresses in node status.
addresses := make ( map [ api . NodeAddress ] bool )
2015-11-04 21:52:14 +00:00
for i , address := range node . Status . Addresses {
2015-04-22 17:55:05 +00:00
if _ , ok := addresses [ address ] ; ok {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Duplicate ( field . NewPath ( "status" , "addresses" ) . Index ( i ) , address ) )
2015-04-22 17:55:05 +00:00
}
addresses [ address ] = true
}
2015-01-27 23:55:54 +00:00
// TODO: move reset function to its own location
// Ignore metadata changes now that they have been tested
2015-04-22 17:55:05 +00:00
oldNode . ObjectMeta = node . ObjectMeta
2015-01-27 23:55:54 +00:00
// Allow users to update capacity
2015-04-22 17:55:05 +00:00
oldNode . Status . Capacity = node . Status . Capacity
2015-05-20 21:21:03 +00:00
// Allow the controller manager to assign a CIDR to a node.
oldNode . Spec . PodCIDR = node . Spec . PodCIDR
2015-02-17 20:03:14 +00:00
// Allow users to unschedule node
2015-04-22 17:55:05 +00:00
oldNode . Spec . Unschedulable = node . Spec . Unschedulable
2014-12-22 19:04:57 +00:00
// Clear status
2015-04-22 17:55:05 +00:00
oldNode . Status = node . Status
2014-12-12 05:39:56 +00:00
2015-11-03 21:35:11 +00:00
// TODO: Add a 'real' error type for this error and provide print actual diffs.
2015-04-22 17:55:05 +00:00
if ! api . Semantic . DeepEqual ( oldNode , node ) {
glog . V ( 4 ) . Infof ( "Update failed validation %#v vs %#v" , oldNode , node )
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "" ) , "node updates may only change labels or capacity" ) )
2014-11-17 18:22:27 +00:00
}
2015-01-27 23:55:54 +00:00
2014-11-17 18:22:27 +00:00
return allErrs
}
2015-01-17 00:34:47 +00:00
2015-01-25 04:19:36 +00:00
// Validate compute resource typename.
2015-09-17 10:26:01 +00:00
// Refer to docs/design/resources.md for more details.
2015-11-06 23:30:52 +00:00
func validateResourceName ( value string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsQualifiedName ( value ) {
2015-11-14 20:26:04 +00:00
return append ( allErrs , field . Invalid ( fldPath , value , qualifiedNameErrorMsg ) )
2015-01-17 00:34:47 +00:00
}
2015-02-05 00:36:27 +00:00
if len ( strings . Split ( value , "/" ) ) == 1 {
if ! api . IsStandardResourceName ( value ) {
2015-11-14 20:26:04 +00:00
return append ( allErrs , field . Invalid ( fldPath , value , "must be a standard resource type or fully qualified" ) )
2015-01-17 00:34:47 +00:00
}
}
2015-11-06 23:30:52 +00:00
return field . ErrorList { }
2015-01-17 00:34:47 +00:00
}
2015-01-22 21:52:40 +00:00
// ValidateLimitRange tests if required fields in the LimitRange are set.
2015-11-06 23:30:52 +00:00
func ValidateLimitRange ( limitRange * api . LimitRange ) field . ErrorList {
allErrs := ValidateObjectMeta ( & limitRange . ObjectMeta , true , ValidateLimitRangeName , field . NewPath ( "metadata" ) )
2015-02-20 06:03:36 +00:00
2015-09-17 10:26:01 +00:00
// ensure resource names are properly qualified per docs/design/resources.md
2015-06-16 20:16:34 +00:00
limitTypeSet := map [ api . LimitType ] bool { }
2015-11-06 23:30:52 +00:00
fldPath := field . NewPath ( "spec" , "limits" )
2015-01-22 21:52:40 +00:00
for i := range limitRange . Spec . Limits {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
limit := & limitRange . Spec . Limits [ i ]
2015-06-16 20:16:34 +00:00
_ , found := limitTypeSet [ limit . Type ]
if found {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Duplicate ( idxPath . Child ( "type" ) , limit . Type ) )
2015-06-16 20:16:34 +00:00
}
limitTypeSet [ limit . Type ] = true
2015-09-09 17:45:01 +00:00
keys := sets . String { }
2015-09-08 18:49:54 +00:00
min := map [ string ] resource . Quantity { }
max := map [ string ] resource . Quantity { }
defaults := map [ string ] resource . Quantity { }
defaultRequests := map [ string ] resource . Quantity { }
2015-09-11 07:38:38 +00:00
maxLimitRequestRatios := map [ string ] resource . Quantity { }
2015-06-16 20:16:34 +00:00
2015-09-08 18:49:54 +00:00
for k , q := range limit . Max {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , idxPath . Child ( "max" ) . Key ( string ( k ) ) ) ... )
2015-06-16 20:16:34 +00:00
keys . Insert ( string ( k ) )
2015-09-08 18:49:54 +00:00
max [ string ( k ) ] = q
2015-01-22 21:52:40 +00:00
}
2015-09-08 18:49:54 +00:00
for k , q := range limit . Min {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , idxPath . Child ( "min" ) . Key ( string ( k ) ) ) ... )
2015-06-16 20:16:34 +00:00
keys . Insert ( string ( k ) )
2015-09-08 18:49:54 +00:00
min [ string ( k ) ] = q
2015-06-16 20:16:34 +00:00
}
2015-09-10 20:39:59 +00:00
if limit . Type == api . LimitTypePod {
if len ( limit . Default ) > 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( idxPath . Child ( "default" ) , "may not be specified when `type` is 'Pod'" ) )
2015-09-10 20:39:59 +00:00
}
if len ( limit . DefaultRequest ) > 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( idxPath . Child ( "defaultRequest" ) , "may not be specified when `type` is 'Pod'" ) )
2015-09-10 20:39:59 +00:00
}
} else {
for k , q := range limit . Default {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , idxPath . Child ( "default" ) . Key ( string ( k ) ) ) ... )
2015-09-10 20:39:59 +00:00
keys . Insert ( string ( k ) )
defaults [ string ( k ) ] = q
}
for k , q := range limit . DefaultRequest {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , idxPath . Child ( "defaultRequest" ) . Key ( string ( k ) ) ) ... )
2015-09-10 20:39:59 +00:00
keys . Insert ( string ( k ) )
defaultRequests [ string ( k ) ] = q
}
2015-08-28 16:26:36 +00:00
}
2015-09-10 20:39:59 +00:00
2015-09-11 07:38:38 +00:00
for k , q := range limit . MaxLimitRequestRatio {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , idxPath . Child ( "maxLimitRequestRatio" ) . Key ( string ( k ) ) ) ... )
2015-09-11 07:38:38 +00:00
keys . Insert ( string ( k ) )
maxLimitRequestRatios [ string ( k ) ] = q
2015-08-28 16:26:36 +00:00
}
2015-06-16 20:16:34 +00:00
for k := range keys {
2015-09-08 18:49:54 +00:00
minQuantity , minQuantityFound := min [ k ]
maxQuantity , maxQuantityFound := max [ k ]
defaultQuantity , defaultQuantityFound := defaults [ k ]
defaultRequestQuantity , defaultRequestQuantityFound := defaultRequests [ k ]
2015-09-11 07:38:38 +00:00
maxRatio , maxRatioFound := maxLimitRequestRatios [ k ]
2015-06-16 20:16:34 +00:00
2015-09-08 18:49:54 +00:00
if minQuantityFound && maxQuantityFound && minQuantity . Cmp ( maxQuantity ) > 0 {
2015-11-10 20:59:41 +00: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 16:26:36 +00:00
}
2015-09-08 18:49:54 +00:00
if defaultRequestQuantityFound && minQuantityFound && minQuantity . Cmp ( defaultRequestQuantity ) > 0 {
2015-11-10 20:59:41 +00: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 16:26:36 +00:00
}
2015-09-08 18:49:54 +00:00
if defaultRequestQuantityFound && maxQuantityFound && defaultRequestQuantity . Cmp ( maxQuantity ) > 0 {
2015-11-10 20:59:41 +00: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 16:26:36 +00:00
}
2015-09-08 18:49:54 +00:00
if defaultRequestQuantityFound && defaultQuantityFound && defaultRequestQuantity . Cmp ( defaultQuantity ) > 0 {
2015-11-10 20:59:41 +00: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 20:16:34 +00:00
}
2015-09-08 18:49:54 +00:00
if defaultQuantityFound && minQuantityFound && minQuantity . Cmp ( defaultQuantity ) > 0 {
2015-11-10 20:59:41 +00: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 20:16:34 +00:00
}
2015-09-08 18:49:54 +00:00
if defaultQuantityFound && maxQuantityFound && defaultQuantity . Cmp ( maxQuantity ) > 0 {
2015-11-10 20:59:41 +00: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 20:16:34 +00:00
}
2015-09-11 07:38:38 +00:00
if maxRatioFound && maxRatio . Cmp ( * resource . NewQuantity ( 1 , resource . DecimalSI ) ) < 0 {
2015-11-10 20:59:41 +00: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 07:38:38 +00: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 20:59:41 +00: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 07:38:38 +00:00
}
}
2015-01-25 04:19:36 +00:00
}
}
2015-06-16 20:16:34 +00:00
2015-01-25 04:19:36 +00:00
return allErrs
}
2015-04-27 22:53:28 +00:00
// ValidateServiceAccount tests if required fields in the ServiceAccount are set.
2015-11-06 23:30:52 +00:00
func ValidateServiceAccount ( serviceAccount * api . ServiceAccount ) field . ErrorList {
allErrs := ValidateObjectMeta ( & serviceAccount . ObjectMeta , true , ValidateServiceAccountName , field . NewPath ( "metadata" ) )
2015-04-27 22:53:28 +00:00
return allErrs
}
// ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set.
2015-11-06 23:30:52 +00:00
func ValidateServiceAccountUpdate ( newServiceAccount , oldServiceAccount * api . ServiceAccount ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newServiceAccount . ObjectMeta , & oldServiceAccount . ObjectMeta , field . NewPath ( "metadata" ) )
2015-04-27 22:53:28 +00:00
allErrs = append ( allErrs , ValidateServiceAccount ( newServiceAccount ) ... )
return allErrs
}
2015-09-10 22:48:28 +00:00
const SecretKeyFmt string = "\\.?" + validation . DNS1123LabelFmt + "(\\." + validation . DNS1123LabelFmt + ")*"
2015-05-11 19:03:10 +00:00
var secretKeyRegexp = regexp . MustCompile ( "^" + SecretKeyFmt + "$" )
// IsSecretKey tests for a string that conforms to the definition of a
// subdomain in DNS (RFC 1123), except that a leading dot is allowed
func IsSecretKey ( value string ) bool {
2015-09-10 22:48:28 +00:00
return len ( value ) <= validation . DNS1123SubdomainMaxLength && secretKeyRegexp . MatchString ( value )
2015-05-11 19:03:10 +00:00
}
2015-02-18 01:24:50 +00:00
// ValidateSecret tests if required fields in the Secret are set.
2015-11-06 23:30:52 +00:00
func ValidateSecret ( secret * api . Secret ) field . ErrorList {
allErrs := ValidateObjectMeta ( & secret . ObjectMeta , true , ValidateSecretName , field . NewPath ( "metadata" ) )
2015-02-18 01:24:50 +00:00
2015-11-06 23:30:52 +00:00
dataPath := field . NewPath ( "data" )
2015-02-18 01:24:50 +00:00
totalSize := 0
2015-02-18 01:26:41 +00:00
for key , value := range secret . Data {
2015-05-11 19:03:10 +00:00
if ! IsSecretKey ( key ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( dataPath . Key ( key ) , key , fmt . Sprintf ( "must have at most %d characters and match regex %s" , validation . DNS1123SubdomainMaxLength , SecretKeyFmt ) ) )
2015-02-18 01:26:41 +00:00
}
2015-02-18 01:24:50 +00:00
totalSize += len ( value )
}
if totalSize > api . MaxSecretSize {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . TooLong ( dataPath , "" , api . MaxSecretSize ) )
2015-02-18 01:24:50 +00:00
}
2015-04-28 03:50:56 +00:00
switch secret . Type {
2015-04-28 03:51:20 +00:00
case api . SecretTypeServiceAccountToken :
// 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
if value := secret . Annotations [ api . ServiceAccountNameKey ] ; len ( value ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "metadata" , "annotations" ) . Key ( api . ServiceAccountNameKey ) , "" ) )
2015-04-28 03:51:20 +00:00
}
2015-04-28 03:50:56 +00:00
case api . SecretTypeOpaque , "" :
2015-05-20 15:59:34 +00:00
// no-op
2015-05-06 14:09:18 +00:00
case api . SecretTypeDockercfg :
dockercfgBytes , exists := secret . Data [ api . DockerConfigKey ]
if ! exists {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( dataPath . Key ( api . DockerConfigKey ) , "" ) )
2015-05-06 14:09:18 +00:00
break
}
// make sure that the content is well-formed json.
if err := json . Unmarshal ( dockercfgBytes , & map [ string ] interface { } { } ) ; err != nil {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( dataPath . Key ( api . DockerConfigKey ) , "<secret contents redacted>" , err . Error ( ) ) )
2015-05-06 14:09:18 +00:00
}
2015-04-28 03:50:56 +00:00
default :
// no-op
}
return allErrs
}
// ValidateSecretUpdate tests if required fields in the Secret are set.
2015-11-06 23:30:52 +00:00
func ValidateSecretUpdate ( newSecret , oldSecret * api . Secret ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newSecret . ObjectMeta , & oldSecret . ObjectMeta , field . NewPath ( "metadata" ) )
2015-04-28 03:50:56 +00:00
if len ( newSecret . Type ) == 0 {
newSecret . Type = oldSecret . Type
}
2015-10-12 03:26:17 +00:00
2015-11-06 23:30:52 +00:00
allErrs = append ( allErrs , ValidateImmutableField ( newSecret . Type , oldSecret . Type , field . NewPath ( "type" ) ) ... )
2015-04-28 03:50:56 +00:00
allErrs = append ( allErrs , ValidateSecret ( newSecret ) ... )
2015-02-18 01:24:50 +00:00
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateBasicResource ( quantity resource . Quantity , fldPath * field . Path ) field . ErrorList {
2015-01-25 04:19:36 +00:00
if quantity . Value ( ) < 0 {
2015-11-10 20:59:41 +00:00
return field . ErrorList { field . Invalid ( fldPath , quantity . Value ( ) , "must be a valid resource quantity" ) }
2015-01-25 04:19:36 +00:00
}
2015-11-06 23:30:52 +00:00
return field . ErrorList { }
2015-01-25 04:19:36 +00:00
}
// Validates resource requirement spec.
2015-11-06 23:30:52 +00:00
func ValidateResourceRequirements ( requirements * api . ResourceRequirements , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-11-04 21:52:14 +00:00
limPath := fldPath . Child ( "limits" )
2015-04-20 18:56:15 +00:00
for resourceName , quantity := range requirements . Limits {
2015-11-04 21:52:14 +00:00
fldPath := limPath . Key ( string ( resourceName ) )
2015-01-25 04:19:36 +00:00
// Validate resource name.
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( resourceName ) , fldPath ) ... )
if api . IsStandardResourceName ( string ( resourceName ) ) {
allErrs = append ( allErrs , validateBasicResource ( quantity , fldPath . Key ( string ( resourceName ) ) ) ... )
2015-07-30 19:59:22 +00:00
}
// Check that request <= limit.
requestQuantity , exists := requirements . Requests [ resourceName ]
if exists {
var requestValue , limitValue int64
requestValue = requestQuantity . Value ( )
limitValue = quantity . Value ( )
// Do a more precise comparison if possible (if the value won't overflow).
if requestValue <= resource . MaxMilliValue && limitValue <= resource . MaxMilliValue {
requestValue = requestQuantity . MilliValue ( )
limitValue = quantity . MilliValue ( )
}
if limitValue < requestValue {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , quantity . String ( ) , "must be greater than or equal to request" ) )
2015-07-30 19:59:22 +00:00
}
2015-01-22 21:52:40 +00:00
}
}
2015-11-04 21:52:14 +00:00
reqPath := fldPath . Child ( "requests" )
2015-04-20 18:56:15 +00:00
for resourceName , quantity := range requirements . Requests {
2015-11-04 21:52:14 +00:00
fldPath := reqPath . Key ( string ( resourceName ) )
2015-04-20 18:56:15 +00:00
// Validate resource name.
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( resourceName ) , fldPath ) ... )
if api . IsStandardResourceName ( string ( resourceName ) ) {
allErrs = append ( allErrs , validateBasicResource ( quantity , fldPath . Key ( string ( resourceName ) ) ) ... )
2015-04-20 18:56:15 +00:00
}
}
2015-01-22 21:52:40 +00:00
return allErrs
}
2015-01-23 17:38:30 +00:00
// ValidateResourceQuota tests if required fields in the ResourceQuota are set.
2015-11-06 23:30:52 +00:00
func ValidateResourceQuota ( resourceQuota * api . ResourceQuota ) field . ErrorList {
allErrs := ValidateObjectMeta ( & resourceQuota . ObjectMeta , true , ValidateResourceQuotaName , field . NewPath ( "metadata" ) )
2015-02-20 06:03:36 +00:00
2015-11-06 23:30:52 +00:00
fldPath := field . NewPath ( "spec" , "hard" )
2015-09-24 14:15:40 +00:00
for k , v := range resourceQuota . Spec . Hard {
2015-11-04 21:52:14 +00:00
resPath := fldPath . Key ( string ( k ) )
allErrs = append ( allErrs , validateResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , validateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-01-23 17:38:30 +00:00
}
2015-11-06 23:30:52 +00:00
fldPath = field . NewPath ( "status" , "hard" )
2015-09-24 14:15:40 +00:00
for k , v := range resourceQuota . Status . Hard {
2015-11-04 21:52:14 +00:00
resPath := fldPath . Key ( string ( k ) )
allErrs = append ( allErrs , validateResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , validateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-01-23 17:38:30 +00:00
}
2015-11-06 23:30:52 +00:00
fldPath = field . NewPath ( "status" , "used" )
2015-09-24 14:15:40 +00:00
for k , v := range resourceQuota . Status . Used {
2015-11-04 21:52:14 +00:00
resPath := fldPath . Key ( string ( k ) )
allErrs = append ( allErrs , validateResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , validateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-10-13 21:27:56 +00:00
}
return allErrs
}
// validateResourceQuantityValue enforces that specified quantity is valid for specified resource
2015-11-06 23:30:52 +00:00
func validateResourceQuantityValue ( resource string , value resource . Quantity , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidatePositiveQuantity ( value , fldPath ) ... )
2015-10-13 21:27:56 +00:00
if api . IsIntegerResourceName ( resource ) {
if value . MilliValue ( ) % int64 ( 1000 ) != int64 ( 0 ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , value , isNotIntegerErrorMsg ) )
2015-10-13 21:27:56 +00:00
}
2015-01-23 17:38:30 +00:00
}
return allErrs
}
2015-01-19 21:50:00 +00:00
2015-03-13 19:15:04 +00: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.
2015-11-06 23:30:52 +00:00
func ValidateResourceQuotaUpdate ( newResourceQuota , oldResourceQuota * api . ResourceQuota ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newResourceQuota . ObjectMeta , & oldResourceQuota . ObjectMeta , field . NewPath ( "metadata" ) )
fldPath := field . NewPath ( "spec" , "hard" )
2015-10-13 21:27:56 +00:00
for k , v := range newResourceQuota . Spec . Hard {
2015-11-04 21:52:14 +00:00
resPath := fldPath . Key ( string ( k ) )
allErrs = append ( allErrs , validateResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , validateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-03-13 19:15:04 +00: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.
2015-11-06 23:30:52 +00:00
func ValidateResourceQuotaStatusUpdate ( newResourceQuota , oldResourceQuota * api . ResourceQuota ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newResourceQuota . ObjectMeta , & oldResourceQuota . ObjectMeta , field . NewPath ( "metadata" ) )
2015-12-09 04:39:18 +00:00
if len ( newResourceQuota . ResourceVersion ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( field . NewPath ( "resourceVersion" ) , "" ) )
2015-03-13 19:15:04 +00:00
}
2015-11-06 23:30:52 +00:00
fldPath := field . NewPath ( "status" , "hard" )
2015-10-13 21:27:56 +00:00
for k , v := range newResourceQuota . Status . Hard {
2015-11-04 21:52:14 +00:00
resPath := fldPath . Key ( string ( k ) )
allErrs = append ( allErrs , validateResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , validateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-03-13 19:15:04 +00:00
}
2015-11-06 23:30:52 +00:00
fldPath = field . NewPath ( "status" , "used" )
2015-10-13 21:27:56 +00:00
for k , v := range newResourceQuota . Status . Used {
2015-11-04 21:52:14 +00:00
resPath := fldPath . Key ( string ( k ) )
allErrs = append ( allErrs , validateResourceName ( string ( k ) , resPath ) ... )
allErrs = append ( allErrs , validateResourceQuantityValue ( string ( k ) , v , resPath ) ... )
2015-03-13 19:15:04 +00:00
}
newResourceQuota . Spec = oldResourceQuota . Spec
return allErrs
}
2015-01-19 21:50:00 +00:00
// ValidateNamespace tests if required fields are set.
2015-11-06 23:30:52 +00:00
func ValidateNamespace ( namespace * api . Namespace ) field . ErrorList {
allErrs := ValidateObjectMeta ( & namespace . ObjectMeta , false , ValidateNamespaceName , field . NewPath ( "metadata" ) )
2015-03-20 16:48:12 +00:00
for i := range namespace . Spec . Finalizers {
2015-11-06 23:30:52 +00:00
allErrs = append ( allErrs , validateFinalizerName ( string ( namespace . Spec . Finalizers [ i ] ) , field . NewPath ( "spec" , "finalizers" ) ) ... )
2015-03-20 16:48:12 +00:00
}
2015-01-19 21:50:00 +00:00
return allErrs
}
2015-03-20 16:48:12 +00:00
// Validate finalizer names
2015-11-06 23:30:52 +00:00
func validateFinalizerName ( stringValue string , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsQualifiedName ( stringValue ) {
2015-11-10 20:59:41 +00:00
return append ( allErrs , field . Invalid ( fldPath , stringValue , qualifiedNameErrorMsg ) )
2015-03-20 16:48:12 +00:00
}
if len ( strings . Split ( stringValue , "/" ) ) == 1 {
if ! api . IsStandardFinalizerName ( stringValue ) {
2015-11-10 20:59:41 +00:00
return append ( allErrs , field . Invalid ( fldPath , stringValue , fmt . Sprintf ( "name is neither a standard finalizer name nor is it fully qualified" ) ) )
2015-03-20 16:48:12 +00:00
}
}
2015-11-06 23:30:52 +00:00
return field . ErrorList { }
2015-03-20 16:48:12 +00:00
}
2015-03-31 21:00:04 +00:00
// ValidateNamespaceUpdate tests to make sure a namespace update can be applied.
// newNamespace is updated with fields that cannot be changed
2015-11-06 23:30:52 +00:00
func ValidateNamespaceUpdate ( newNamespace * api . Namespace , oldNamespace * api . Namespace ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , field . NewPath ( "metadata" ) )
2015-03-31 21:00:04 +00:00
newNamespace . Spec . Finalizers = oldNamespace . Spec . Finalizers
newNamespace . Status = oldNamespace . Status
2015-01-19 21:50:00 +00:00
return allErrs
}
2015-03-12 15:08:06 +00: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.
2015-11-06 23:30:52 +00:00
func ValidateNamespaceStatusUpdate ( newNamespace , oldNamespace * api . Namespace ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , field . NewPath ( "metadata" ) )
2015-03-12 15:08:06 +00:00
newNamespace . Spec = oldNamespace . Spec
2015-04-10 15:41:18 +00:00
if newNamespace . DeletionTimestamp . IsZero ( ) {
if newNamespace . Status . Phase != api . NamespaceActive {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "status" , "Phase" ) , newNamespace . Status . Phase , "may only be 'Active' if `deletionTimestamp` is empty" ) )
2015-04-10 15:41:18 +00:00
}
} else {
if newNamespace . Status . Phase != api . NamespaceTerminating {
2015-11-14 20:26:04 +00: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 15:41:18 +00:00
}
}
2015-03-12 15:08:06 +00:00
return allErrs
}
2015-03-20 16:48:12 +00:00
2015-03-31 21:00:04 +00: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.
2015-11-06 23:30:52 +00:00
func ValidateNamespaceFinalizeUpdate ( newNamespace , oldNamespace * api . Namespace ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , field . NewPath ( "metadata" ) )
2015-11-04 21:52:14 +00:00
2015-11-06 23:30:52 +00:00
fldPath := field . NewPath ( "spec" , "finalizers" )
2015-03-20 16:48:12 +00:00
for i := range newNamespace . Spec . Finalizers {
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
allErrs = append ( allErrs , validateFinalizerName ( string ( newNamespace . Spec . Finalizers [ i ] ) , idxPath ) ... )
2015-03-20 16:48:12 +00:00
}
newNamespace . Status = oldNamespace . Status
return allErrs
}
2015-03-15 06:03:46 +00:00
// ValidateEndpoints tests if required fields are set.
2015-11-06 23:30:52 +00:00
func ValidateEndpoints ( endpoints * api . Endpoints ) field . ErrorList {
allErrs := ValidateObjectMeta ( & endpoints . ObjectMeta , true , ValidateEndpointsName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , validateEndpointSubsets ( endpoints . Subsets , field . NewPath ( "subsets" ) ) ... )
2015-03-20 21:24:43 +00:00
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateEndpointSubsets ( subsets [ ] api . EndpointSubset , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-03-20 21:24:43 +00:00
for i := range subsets {
ss := & subsets [ i ]
2015-11-04 21:52:14 +00:00
idxPath := fldPath . Index ( i )
2015-03-20 21:24:43 +00:00
2015-09-22 08:05:21 +00:00
if len ( ss . Addresses ) == 0 && len ( ss . NotReadyAddresses ) == 0 {
2015-11-04 21:52:14 +00:00
//TODO: consider adding a RequiredOneOf() error for this and similar cases
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath , "must specify `addresses` or `notReadyAddresses`" ) )
2015-03-20 21:24:43 +00:00
}
if len ( ss . Ports ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( idxPath . Child ( "ports" ) , "" ) )
2015-03-20 21:24:43 +00:00
}
2015-03-20 14:40:20 +00:00
for addr := range ss . Addresses {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateEndpointAddress ( & ss . Addresses [ addr ] , idxPath . Child ( "addresses" ) . Index ( addr ) ) ... )
2015-03-20 14:40:20 +00:00
}
for port := range ss . Ports {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateEndpointPort ( & ss . Ports [ port ] , len ( ss . Ports ) > 1 , idxPath . Child ( "ports" ) . Index ( port ) ) ... )
2015-03-20 14:40:20 +00:00
}
2015-03-20 21:24:43 +00:00
}
2015-03-15 06:03:46 +00:00
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateEndpointAddress ( address * api . EndpointAddress , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsValidIPv4 ( address . IP ) {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "ip" ) , address . IP , "must be a valid IPv4 address" ) )
2015-07-04 04:05:15 +00:00
return allErrs
2015-03-20 14:40:20 +00:00
}
2015-11-04 21:52:14 +00:00
return validateIpIsNotLinkLocalOrLoopback ( address . IP , fldPath . Child ( "ip" ) )
2015-08-12 00:18:21 +00:00
}
2015-11-06 23:30:52 +00:00
func validateIpIsNotLinkLocalOrLoopback ( ipAddress string , fldPath * field . Path ) field . ErrorList {
2015-08-12 00:18:21 +00:00
// We disallow some IPs as endpoints or external-ips. Specifically, loopback addresses are
// nonsensical and link-local addresses tend to be used for node-centric purposes (e.g. metadata service).
2015-11-06 23:30:52 +00:00
allErrs := field . ErrorList { }
2015-08-12 00:18:21 +00:00
ip := net . ParseIP ( ipAddress )
if ip == nil {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "must be a valid IP address" ) )
2015-08-12 00:18:21 +00:00
return allErrs
}
2015-07-04 04:05:15 +00:00
if ip . IsLoopback ( ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "may not be in the loopback range (127.0.0.0/8)" ) )
2015-07-04 04:05:15 +00:00
}
if ip . IsLinkLocalUnicast ( ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath , ipAddress , "may not be in the link-local range (169.254.0.0/16)" ) )
2015-06-03 04:49:51 +00:00
}
2015-07-04 04:05:15 +00:00
if ip . IsLinkLocalMulticast ( ) {
2015-11-10 20:59:41 +00: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 04:05:15 +00:00
}
2015-03-20 14:40:20 +00:00
return allErrs
}
2015-11-06 23:30:52 +00:00
func validateEndpointPort ( port * api . EndpointPort , requireName bool , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-12-09 04:39:18 +00:00
if requireName && len ( port . Name ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
2015-12-09 04:39:18 +00:00
} else if len ( port . Name ) != 0 {
2015-09-10 22:48:28 +00:00
if ! validation . IsDNS1123Label ( port . Name ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , port . Name , DNS1123LabelErrorMsg ) )
2015-03-20 14:40:20 +00:00
}
}
2015-09-10 22:48:28 +00:00
if ! validation . IsValidPortNum ( port . Port ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "port" ) , port . Port , PortRangeErrorMsg ) )
2015-03-20 14:40:20 +00:00
}
if len ( port . Protocol ) == 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "protocol" ) , "" ) )
2015-06-17 00:28:11 +00:00
} else if ! supportedPortProtocols . Has ( string ( port . Protocol ) ) {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "protocol" ) , port . Protocol , supportedPortProtocols . List ( ) ) )
2015-03-20 14:40:20 +00:00
}
return allErrs
}
2015-03-15 06:03:46 +00:00
// ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
2015-11-06 23:30:52 +00:00
func ValidateEndpointsUpdate ( newEndpoints , oldEndpoints * api . Endpoints ) field . ErrorList {
allErrs := ValidateObjectMetaUpdate ( & newEndpoints . ObjectMeta , & oldEndpoints . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , validateEndpointSubsets ( newEndpoints . Subsets , field . NewPath ( "subsets" ) ) ... )
2015-03-15 06:03:46 +00:00
return allErrs
}
2015-05-05 23:02:13 +00:00
// ValidateSecurityContext ensure the security context contains valid settings
2015-11-06 23:30:52 +00:00
func ValidateSecurityContext ( sc * api . SecurityContext , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-05-05 23:02:13 +00:00
//this should only be true for testing since SecurityContext is defaulted by the api
if sc == nil {
return allErrs
}
if sc . Privileged != nil {
if * sc . Privileged && ! capabilities . Get ( ) . AllowPrivileged {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "privileged" ) , "disallowed by policy" ) )
2015-05-05 23:02:13 +00:00
}
}
if sc . RunAsUser != nil {
if * sc . RunAsUser < 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "runAsUser" ) , * sc . RunAsUser , isNegativeErrorMsg ) )
2015-05-05 23:02:13 +00:00
}
}
return allErrs
}
2015-07-27 19:49:06 +00:00
2015-11-06 23:30:52 +00:00
func ValidatePodLogOptions ( opts * api . PodLogOptions ) field . ErrorList {
allErrs := field . ErrorList { }
2015-09-10 03:46:11 +00:00
if opts . TailLines != nil && * opts . TailLines < 0 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "tailLines" ) , * opts . TailLines , isNegativeErrorMsg ) )
2015-09-10 03:46:11 +00:00
}
if opts . LimitBytes != nil && * opts . LimitBytes < 1 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "limitBytes" ) , * opts . LimitBytes , "must be greater than 0" ) )
2015-09-10 03:46:11 +00:00
}
switch {
case opts . SinceSeconds != nil && opts . SinceTime != nil :
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "" ) , "at most one of `sinceTime` or `sinceSeconds` may be specified" ) )
2015-09-10 03:46:11 +00:00
case opts . SinceSeconds != nil :
if * opts . SinceSeconds < 1 {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "sinceSeconds" ) , * opts . SinceSeconds , "must be greater than 0" ) )
2015-09-10 03:46:11 +00:00
}
}
return allErrs
}
2015-10-12 19:09:20 +00:00
// ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus
2015-11-06 23:30:52 +00:00
func ValidateLoadBalancerStatus ( status * api . LoadBalancerStatus , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2015-11-04 21:52:14 +00:00
for i , ingress := range status . Ingress {
idxPath := fldPath . Child ( "ingress" ) . Index ( i )
2015-10-12 19:09:20 +00:00
if len ( ingress . IP ) > 0 {
if isIP := ( net . ParseIP ( ingress . IP ) != nil ) ; ! isIP {
2015-11-14 20:26:04 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "ip" ) , ingress . IP , "must be a valid IP address" ) )
2015-10-12 19:09:20 +00:00
}
}
if len ( ingress . Hostname ) > 0 {
if valid , errMsg := NameIsDNSSubdomain ( ingress . Hostname , false ) ; ! valid {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "hostname" ) , ingress . Hostname , errMsg ) )
2015-10-12 19:09:20 +00:00
}
if isIP := ( net . ParseIP ( ingress . Hostname ) != nil ) ; isIP {
2015-11-10 20:59:41 +00:00
allErrs = append ( allErrs , field . Invalid ( idxPath . Child ( "hostname" ) , ingress . Hostname , "must be a DNS name, not an IP address" ) )
2015-10-12 19:09:20 +00:00
}
}
}
return allErrs
}