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-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"
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-05-13 04:24:41 +00:00
const isNegativeErrorMsg string = ` must be non-negative `
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-10-12 03:26:17 +00:00
func IntervalErrorMsg ( lo , hi int ) string {
2015-05-13 04:24:41 +00:00
return fmt . Sprintf ( ` must be greater than %d and less than %d ` , 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-10-12 03:26:17 +00:00
var pdPartitionErrorMsg string = IntervalErrorMsg ( 0 , 255 )
var PortRangeErrorMsg string = IntervalErrorMsg ( 0 , 65536 )
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-04 21:52:14 +00:00
func ValidateLabelName ( labelName string , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsQualifiedName ( labelName ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func ValidateLabels ( labels map [ string ] string , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func ValidateAnnotations ( annotations map [ string ] string , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewTooLongError ( 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-04 21:52:14 +00:00
func ValidatePositiveField ( value int64 , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-08-25 19:07:03 +00:00
if value < 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func ValidatePositiveQuantity ( value resource . Quantity , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-09-24 14:15:40 +00:00
if value . Cmp ( resource . Quantity { } ) < 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath , value . String ( ) , isNegativeErrorMsg ) )
2015-09-24 14:15:40 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func ValidateImmutableField ( newVal , oldVal interface { } , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
if ! api . Semantic . DeepEqual ( oldVal , newVal ) {
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func ValidateObjectMeta ( meta * api . ObjectMeta , requiresNamespace bool , nameFn ValidateNameFunc , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-01-27 23:56:38 +00:00
if len ( meta . GenerateName ) != 0 {
if ok , qualifier := nameFn ( meta . GenerateName , true ) ; ! ok {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
requiredErr := validation . NewRequiredError ( fldPath . Child ( "name" ) )
2015-08-12 15:26:23 +00:00
requiredErr . Detail = "name or generateName is required"
allErrs = append ( allErrs , requiredErr )
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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "name" ) , meta . Name , qualifier ) )
2015-01-27 23:55:54 +00:00
}
}
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidatePositiveField ( meta . Generation , fldPath . Child ( "generation" ) ) ... )
2015-01-27 23:55:54 +00:00
if requiresNamespace {
if len ( meta . Namespace ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "namespace" ) ) )
2015-05-12 22:13:03 +00:00
} else if ok , _ := ValidateNamespaceName ( meta . Namespace , false ) ; ! ok {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "namespace" ) , meta . Namespace , DNS1123LabelErrorMsg ) )
2015-01-27 23:55:54 +00:00
}
} else {
if len ( meta . Namespace ) != 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "namespace" ) , meta . Namespace , "namespace is not allowed on this type" ) )
2015-01-27 23:55:54 +00:00
}
}
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-04 21:52:14 +00:00
func ValidateObjectMetaUpdate ( newMeta , oldMeta * api . ObjectMeta , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-01-27 23:55:54 +00:00
2015-11-04 21:52:14 +00:00
if ! RepairMalformedUpdates && newMeta . UID != oldMeta . UID {
allErrs = append ( allErrs , validation . NewInvalidError ( 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 {
allErrs = append ( allErrs , validation . NewInvalidError ( 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-11-04 21:52:14 +00:00
if newMeta . ResourceVersion == "" {
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "resourceVersion" ) , newMeta . ResourceVersion , "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-04 21:52:14 +00:00
func validateVolumes ( volumes [ ] api . Volume , fldPath * validation . FieldPath ) ( sets . String , validation . ErrorList ) {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
el = append ( el , validation . NewRequiredError ( idxPath . Child ( "name" ) ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsDNS1123Label ( vol . Name ) {
2015-11-04 21:52:14 +00:00
el = append ( el , validation . NewInvalidError ( idxPath . Child ( "name" ) , vol . Name , DNS1123LabelErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( vol . Name ) {
2015-11-04 21:52:14 +00:00
el = append ( el , validation . NewDuplicateError ( 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-04 21:52:14 +00:00
func validateVolumeSource ( source * api . VolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2014-07-16 19:32:59 +00:00
numVolumes := 0
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-01-20 23:56:44 +00:00
if source . HostPath != nil {
2014-07-16 19:32:59 +00:00
numVolumes ++
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateHostPathVolumeSource ( source . HostPath , fldPath . Child ( "hostPath" ) ) ... )
2014-07-16 19:32:59 +00:00
}
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
}
if source . GitRepo != nil {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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-03-06 14:26:39 +00:00
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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-04-18 11:16:07 +00:00
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateFCVolumeSource ( source . FC , fldPath . Child ( "fc" ) ) ... )
2015-08-11 15:19:29 +00:00
}
2015-01-26 17:52:50 +00:00
if numVolumes != 1 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath , source , "exactly 1 volume type is required" ) )
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-04 21:52:14 +00:00
func validateHostPathVolumeSource ( hostPath * api . HostPathVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-09-16 14:31:17 +00:00
if hostPath . Path == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "path" ) ) )
2014-07-15 01:39:30 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateGitRepoVolumeSource ( gitRepo * api . GitRepoVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-10-10 10:10:45 +00:00
if len ( gitRepo . Repository ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
func validateISCSIVolumeSource ( iscsi * api . ISCSIVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-03-13 21:31:13 +00:00
if iscsi . TargetPortal == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "targetPortal" ) ) )
2015-03-13 21:31:13 +00:00
}
if iscsi . IQN == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "iqn" ) ) )
2015-03-13 21:31:13 +00:00
}
if iscsi . FSType == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "fsType" ) ) )
2015-03-13 21:31:13 +00:00
}
if iscsi . Lun < 0 || iscsi . Lun > 255 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "lun" ) , iscsi . Lun , "" ) )
2015-03-13 21:31:13 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateFCVolumeSource ( fc * api . FCVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-08-11 15:19:29 +00:00
if len ( fc . TargetWWNs ) < 1 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "targetWWNs" ) ) )
2015-08-11 15:19:29 +00:00
}
if fc . FSType == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "fsType" ) ) )
2015-08-11 15:19:29 +00:00
}
if fc . Lun == nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "lun" ) ) )
2015-08-11 15:19:29 +00:00
} else {
if * fc . Lun < 0 || * fc . Lun > 255 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "lun" ) , fc . Lun , "" ) )
2015-08-11 15:19:29 +00:00
}
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateGCEPersistentDiskVolumeSource ( pd * api . GCEPersistentDiskVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
if pd . PDName == "" {
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "pdName" ) ) )
2014-08-05 17:58:43 +00:00
}
2015-11-04 21:52:14 +00:00
if pd . FSType == "" {
allErrs = append ( allErrs , validation . NewRequiredError ( 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 {
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "partition" ) , pd . Partition , pdPartitionErrorMsg ) )
2014-08-05 17:58:43 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateAWSElasticBlockStoreVolumeSource ( PD * api . AWSElasticBlockStoreVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-04-09 13:34:16 +00:00
if PD . VolumeID == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "volumeID" ) ) )
2015-03-06 14:26:39 +00:00
}
if PD . FSType == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "fsType" ) ) )
2015-03-06 14:26:39 +00:00
}
if PD . Partition < 0 || PD . Partition > 255 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "partition" ) , PD . Partition , pdPartitionErrorMsg ) )
2015-03-06 14:26:39 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateSecretVolumeSource ( secretSource * api . SecretVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-03-24 17:17:14 +00:00
if secretSource . SecretName == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "secretName" ) ) )
2015-02-18 01:24:50 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validatePersistentClaimVolumeSource ( claim * api . PersistentVolumeClaimVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-04-18 11:16:07 +00:00
if claim . ClaimName == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "claimName" ) ) )
2015-04-18 11:16:07 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateNFSVolumeSource ( nfs * api . NFSVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-02-10 19:00:11 +00:00
if nfs . Server == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "server" ) ) )
2015-02-10 19:00:11 +00:00
}
if nfs . Path == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "path" ) ) )
2015-02-10 19:00:11 +00:00
}
if ! path . IsAbs ( nfs . Path ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "path" ) , nfs . Path , "must be an absolute path" ) )
2015-02-10 19:00:11 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateGlusterfs ( glusterfs * api . GlusterfsVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-03-26 18:53:21 +00:00
if glusterfs . EndpointsName == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "endpoints" ) ) )
2015-03-26 18:53:21 +00:00
}
if glusterfs . Path == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "path" ) ) )
2015-03-26 18:53:21 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateFlockerVolumeSource ( flocker * api . FlockerVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-09-25 19:22:23 +00:00
if flocker . DatasetName == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "datasetName" ) ) )
2015-09-25 19:22:23 +00:00
}
if strings . Contains ( flocker . DatasetName , "/" ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func validateDownwardAPIVolumeSource ( downwardAPIVolume * api . DownwardAPIVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-02-20 05:36:23 +00:00
for _ , downwardAPIVolumeFile := range downwardAPIVolume . Items {
if len ( downwardAPIVolumeFile . Path ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
func validateVolumeSourcePath ( targetPath string , fldPath * validation . FieldPath ) validation . ErrorList {
2015-10-10 10:10:45 +00:00
allErrs := validation . ErrorList { }
if path . IsAbs ( targetPath ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewForbiddenError ( fldPath , "must not be an absolute 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath , targetPath , "must not contain \"..\"" ) )
2015-10-10 10:10:45 +00:00
}
}
if strings . HasPrefix ( items [ 0 ] , ".." ) && len ( items [ 0 ] ) > 2 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath , targetPath , "must not start with \"..\"" ) )
2015-10-10 10:10:45 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateRBDVolumeSource ( rbd * api . RBDVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-04-07 17:22:23 +00:00
if len ( rbd . CephMonitors ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "monitors" ) ) )
2015-04-07 17:22:23 +00:00
}
if rbd . RBDImage == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "image" ) ) )
2015-04-07 17:22:23 +00:00
}
if rbd . FSType == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "fsType" ) ) )
2015-04-07 17:22:23 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateCinderVolumeSource ( cd * api . CinderVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-04-10 16:54:01 +00:00
if cd . VolumeID == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "volumeID" ) ) )
2015-04-10 16:54:01 +00:00
}
if cd . FSType == "" || ( cd . FSType != "ext3" && cd . FSType != "ext4" ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "fsType" ) ) )
2015-04-10 16:54:01 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateCephFSVolumeSource ( cephfs * api . CephFSVolumeSource , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-04-09 18:05:24 +00:00
if len ( cephfs . Monitors ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "monitors" ) ) )
2015-04-09 18:05:24 +00:00
}
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-03 21:38:40 +00:00
func ValidatePersistentVolume ( pv * api . PersistentVolume ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & pv . ObjectMeta , false , ValidatePersistentVolumeName , validation . NewFieldPath ( "metadata" ) )
2015-03-23 18:18:11 +00:00
2015-11-04 21:52:14 +00:00
specPath := validation . NewFieldPath ( "spec" )
2015-05-13 00:44:29 +00:00
if len ( pv . Spec . AccessModes ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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 ) ) {
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateHostPathVolumeSource ( pv . Spec . HostPath , specPath . Child ( "hostPath" ) ) ... )
2015-03-23 18:18:11 +00:00
}
if pv . Spec . GCEPersistentDisk != nil {
numVolumes ++
2015-11-04 21:52:14 +00:00
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-03-06 14:26:39 +00:00
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
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 {
numVolumes ++
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateFCVolumeSource ( pv . Spec . FC , specPath . Child ( "fc" ) ) ... )
2015-08-11 15:19:29 +00:00
}
2015-03-23 18:18:11 +00:00
if numVolumes != 1 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( specPath , pv . Spec . PersistentVolumeSource , "exactly 1 volume type is required" ) )
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-03 21:38:40 +00:00
func ValidatePersistentVolumeUpdate ( newPv , oldPv * api . PersistentVolume ) validation . ErrorList {
allErrs := validation . 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-03 21:38:40 +00:00
func ValidatePersistentVolumeStatusUpdate ( newPv , oldPv * api . PersistentVolume ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newPv . ObjectMeta , & oldPv . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
2015-03-26 19:50:36 +00:00
if newPv . ResourceVersion == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( validation . NewFieldPath ( "resourceVersion" ) ) )
2015-03-26 19:50:36 +00:00
}
newPv . Spec = oldPv . Spec
return allErrs
}
2015-11-03 21:38:40 +00:00
func ValidatePersistentVolumeClaim ( pvc * api . PersistentVolumeClaim ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & pvc . ObjectMeta , true , ValidatePersistentVolumeName , validation . NewFieldPath ( "metadata" ) )
specPath := validation . NewFieldPath ( "spec" )
2015-03-23 18:18:11 +00:00
if len ( pvc . Spec . AccessModes ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( specPath . Child ( "accessModes" ) , pvc . Spec . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( specPath . Child ( "resources" ) . Key ( string ( api . ResourceStorage ) ) ) )
2015-03-23 18:18:11 +00:00
}
return allErrs
}
2015-11-03 21:38:40 +00:00
func ValidatePersistentVolumeClaimUpdate ( newPvc , oldPvc * api . PersistentVolumeClaim ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-03-26 19:50:36 +00:00
allErrs = ValidatePersistentVolumeClaim ( newPvc )
newPvc . Status = oldPvc . Status
return allErrs
}
2015-11-03 21:38:40 +00:00
func ValidatePersistentVolumeClaimStatusUpdate ( newPvc , oldPvc * api . PersistentVolumeClaim ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newPvc . ObjectMeta , & oldPvc . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
2015-03-26 19:50:36 +00:00
if newPvc . ResourceVersion == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( validation . NewFieldPath ( "Spec" , "accessModes" ) ) )
2015-05-13 00:44:29 +00:00
}
2015-11-04 21:52:14 +00:00
capPath := validation . NewFieldPath ( "status" , "capacity" )
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-04 21:52:14 +00:00
func validateContainerPorts ( ports [ ] api . ContainerPort , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "name" ) , port . Name , PortNameErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( port . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewDuplicateError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "containerPort" ) , port . ContainerPort , PortRangeErrorMsg ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsValidPortNum ( port . ContainerPort ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "hostPort" ) , port . HostPort , PortRangeErrorMsg ) )
2014-07-08 04:32:56 +00:00
}
if len ( port . Protocol ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( idxPath . Child ( "protocol" ) ) )
2015-06-17 00:28:11 +00:00
} else if ! supportedPortProtocols . Has ( string ( port . Protocol ) ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-04 21:52:14 +00:00
func validateEnv ( vars [ ] api . EnvVar , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( idxPath . Child ( "name" ) ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsCIdentifier ( ev . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func validateEnvVarValueFrom ( ev api . EnvVar , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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
}
if ev . Value != "" && numSources != 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath , "" , "sources cannot be specified when value is not empty" ) )
2015-04-23 20:57:30 +00:00
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateObjectFieldSelector ( fs * api . ObjectFieldSelector , expressions * sets . String , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-04-23 20:57:30 +00:00
if fs . APIVersion == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "apiVersion" ) ) )
2015-04-23 20:57:30 +00:00
} else if fs . FieldPath == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "fieldPath" ) , fs . FieldPath , "error converting fieldPath" ) )
2015-02-20 05:36:23 +00:00
} else if ! expressions . Has ( internalFieldPath ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( fldPath . Child ( "fieldPath" ) , internalFieldPath , expressions . List ( ) ) )
2015-04-23 20:57:30 +00:00
}
}
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateVolumeMounts ( mounts [ ] api . VolumeMount , volumes sets . String , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( idxPath . Child ( "name" ) ) )
2014-07-08 06:20:30 +00:00
} else if ! volumes . Has ( mnt . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotFoundError ( idxPath . Child ( "name" ) , mnt . Name ) )
2014-07-05 02:46:56 +00:00
}
if len ( mnt . MountPath ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
func validateProbe ( probe * api . Probe , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
func AccumulateUniqueHostPorts ( containers [ ] api . Container , accumulator * sets . String , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewDuplicateError ( 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-04 21:52:14 +00:00
func checkHostPortConflicts ( containers [ ] api . Container , fldPath * validation . FieldPath ) validation . 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-04 21:52:14 +00:00
func validateExecAction ( exec * api . ExecAction , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . ErrorList { }
2014-09-12 23:04:10 +00:00
if len ( exec . Command ) == 0 {
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewRequiredError ( fldPath . Child ( "command" ) ) )
2014-09-12 23:04:10 +00:00
}
return allErrors
}
2015-11-04 21:52:14 +00:00
func validateHTTPGetAction ( http * api . HTTPGetAction , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . ErrorList { }
2014-09-12 23:04:10 +00:00
if len ( http . Path ) == 0 {
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func validateTCPSocketAction ( tcp * api . TCPSocketAction , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . ErrorList { }
2015-11-18 18:15:16 +00:00
if tcp . Port . Type == intstr . Int && ! validation . IsValidPortNum ( tcp . Port . IntValue ( ) ) {
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func validateHandler ( handler * api . Handler , fldPath * validation . FieldPath ) validation . ErrorList {
2014-11-20 22:24:10 +00:00
numHandlers := 0
2015-11-03 21:38:40 +00:00
allErrors := validation . ErrorList { }
2014-09-12 23:04:10 +00:00
if handler . Exec != nil {
2014-11-20 22:24:10 +00:00
numHandlers ++
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validateExecAction ( handler . Exec , fldPath . Child ( "exec" ) ) ... )
2014-11-20 22:24:10 +00:00
}
if handler . HTTPGet != nil {
numHandlers ++
2015-11-04 21:52:14 +00:00
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 {
numHandlers ++
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validateTCPSocketAction ( handler . TCPSocket , fldPath . Child ( "tcpSocket" ) ) ... )
2015-02-28 01:33:58 +00:00
}
2014-11-20 22:24:10 +00:00
if numHandlers != 1 {
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( fldPath , handler , "exactly 1 handler type is required" ) )
2014-09-12 23:04:10 +00:00
}
return allErrors
}
2015-11-04 21:52:14 +00:00
func validateLifecycle ( lifecycle * api . Lifecycle , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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 ) )
func validatePullPolicy ( policy api . PullPolicy , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewRequiredError ( fldPath ) )
2015-01-16 23:02:36 +00:00
default :
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewNotSupportedError ( fldPath , policy , supportedPullPolicies . List ( ) ) )
2015-01-16 23:02:36 +00:00
}
return allErrors
}
2015-11-04 21:52:14 +00:00
func validateContainers ( containers [ ] api . Container , volumes sets . String , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2014-07-08 06:20:30 +00:00
2015-03-18 15:00:18 +00:00
if len ( containers ) == 0 {
2015-11-04 21:52:14 +00:00
return append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( idxPath . Child ( "name" ) ) )
2015-09-10 22:48:28 +00:00
} else if ! validation . IsDNS1123Label ( ctr . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "name" ) , ctr . Name , DNS1123LabelErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( ctr . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewDuplicateError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewForbiddenError ( idxPath . Child ( "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-04 21:52:14 +00:00
func validateRestartPolicy ( restartPolicy * api . RestartPolicy , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . ErrorList { }
2015-03-14 01:38:07 +00:00
switch * restartPolicy {
case api . RestartPolicyAlways , api . RestartPolicyOnFailure , api . RestartPolicyNever :
break
case "" :
2015-11-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewNotSupportedError ( 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-04 21:52:14 +00:00
func validateDNSPolicy ( dnsPolicy * api . DNSPolicy , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewNotSupportedError ( fldPath , dnsPolicy , validValues ) )
2014-11-12 05:21:40 +00:00
}
return allErrors
}
2015-11-04 21:52:14 +00:00
func validateHostNetwork ( hostNetwork bool , containers [ ] api . Container , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( idxPath . Child ( "containerPort" ) , port . ContainerPort , "must match hostPort when hostNetwork is set to 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.
func validateImagePullSecrets ( imagePullSecrets [ ] api . LocalObjectReference , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrors := validation . 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-04 21:52:14 +00:00
allErrors = append ( allErrors , validation . NewInvalidError ( 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-03 21:38:40 +00:00
func ValidatePod ( pod * api . Pod ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName , validation . NewFieldPath ( "metadata" ) )
allErrs = append ( allErrs , ValidatePodSpec ( & pod . Spec , validation . NewFieldPath ( "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-04 21:52:14 +00:00
func ValidatePodSpec ( spec * api . PodSpec , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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 {
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "nodeName" ) , spec . NodeName , msg ) )
}
}
2015-05-09 05:01:43 +00:00
if spec . ActiveDeadlineSeconds != nil {
if * spec . ActiveDeadlineSeconds <= 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func ValidatePodSecurityContext ( securityContext * api . PodSecurityContext , spec * api . PodSpec , specPath , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-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-03 21:38:40 +00:00
func ValidatePodUpdate ( newPod , oldPod * api . Pod ) validation . ErrorList {
allErrs := validation . ErrorList { }
2014-10-10 03:30:34 +00:00
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newPod . ObjectMeta , & oldPod . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
2014-10-14 19:28:45 +00:00
2015-11-04 21:52:14 +00:00
specPath := validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( specPath . Child ( "containers" ) , "contents not printed here, please refer to the \"details\"" , "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( specPath , "contents not printed here, please refer to the \"details\"" , "may not update fields other than container.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-03 21:38:40 +00:00
func ValidatePodStatusUpdate ( newPod , oldPod * api . Pod ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-02-24 05:42:27 +00:00
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newPod . ObjectMeta , & oldPod . ObjectMeta , validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "status" , "nodeName" ) , newPod . Spec . NodeName , "cannot 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-03-04 00:54:17 +00:00
// ValidatePodTemplate tests if required fields in the pod template are set.
2015-11-03 21:38:40 +00:00
func ValidatePodTemplate ( pod * api . PodTemplate ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName , validation . NewFieldPath ( "metadata" ) )
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & pod . Template , validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidatePodTemplateUpdate ( newPod , oldPod * api . PodTemplate ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldPod . ObjectMeta , & newPod . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & newPod . Template , validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateService ( service * api . Service ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & service . ObjectMeta , true , ValidateServiceName , validation . NewFieldPath ( "metadata" ) )
2015-01-27 23:55:54 +00:00
2015-11-04 21:52:14 +00:00
specPath := validation . NewFieldPath ( "spec" )
2015-11-06 17:53:57 +00:00
if len ( service . Spec . Ports ) == 0 && service . Spec . ClusterIP != api . ClusterIPNone {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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 ]
if port . Port == 10250 {
2015-11-04 21:52:14 +00:00
portPath := specPath . Child ( "ports" ) . Index ( ix )
allErrs = append ( allErrs , validation . NewInvalidError ( portPath , port . Port , "can 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
2014-12-29 22:39:09 +00:00
if service . Spec . SessionAffinity == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( specPath . Child ( "sessionAffinity" ) ) )
2014-12-29 22:39:09 +00:00
} else if ! supportedSessionAffinityType . Has ( string ( service . Spec . SessionAffinity ) ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath , ip , "is not an 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-05-22 21:49:26 +00:00
if service . Spec . Type == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( specPath . Child ( "type" ) ) )
2015-05-22 21:49:26 +00:00
} else if ! supportedServiceType . Has ( string ( service . Spec . Type ) ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-04-03 19:06:25 +00:00
if service . Spec . Ports [ i ] . Protocol != api . ProtocolTCP {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( portPath . Child ( "protocol" ) , service . Spec . Ports [ i ] . Protocol , "cannot create an external load balancer with non-TCP 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( portPath . Child ( "nodePort" ) , service . Spec . Ports [ i ] . NodePort , "cannot specify a node port with services of type 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( portPath . Child ( "nodePort" ) , port . NodePort , "duplicate nodePort specified" ) )
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-04 21:52:14 +00:00
func validateServicePort ( sp * api . ServicePort , requireName , isHeadlessService bool , allNames * sets . String , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-03-13 15:16:41 +00:00
2015-05-05 18:51:51 +00:00
if requireName && sp . Name == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "name" ) ) )
2015-05-05 18:51:51 +00:00
} else if sp . Name != "" {
2015-09-10 22:48:28 +00:00
if ! validation . IsDNS1123Label ( sp . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "name" ) , sp . Name , DNS1123LabelErrorMsg ) )
2015-05-05 18:51:51 +00:00
} else if allNames . Has ( sp . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewDuplicateError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "port" ) , sp . Port , PortRangeErrorMsg ) )
2015-03-13 15:16:41 +00:00
}
if len ( sp . Protocol ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "protocol" ) ) )
2015-06-17 00:28:11 +00:00
} else if ! supportedPortProtocols . Has ( string ( sp . Protocol ) ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-03 21:38:40 +00:00
func ValidateServiceUpdate ( service , oldService * api . Service ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & service . ObjectMeta , & oldService . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
2015-01-27 23:55:54 +00:00
2015-10-12 03:26:17 +00:00
if api . IsServiceIPSet ( oldService ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateImmutableField ( service . Spec . ClusterIP , oldService . Spec . ClusterIP , validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateReplicationController ( controller * api . ReplicationController ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & controller . ObjectMeta , true , ValidateReplicationControllerName , validation . NewFieldPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec , validation . NewFieldPath ( "spec" ) ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
}
// ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
2015-11-03 21:38:40 +00:00
func ValidateReplicationControllerUpdate ( controller , oldController * api . ReplicationController ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec , validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateReplicationControllerStatusUpdate ( controller , oldController * api . ReplicationController ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
statusPath := validation . NewFieldPath ( "status" )
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-04 21:52:14 +00:00
func ValidateNonEmptySelector ( selectorMap map [ string ] string , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
func ValidatePodTemplateSpecForRC ( template * api . PodTemplateSpec , selectorMap map [ string ] string , replicas int , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-08-25 19:07:03 +00:00
if template == nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "metadata" , "labels" ) , template . Labels , "selector does not match labels in " + fldPath . String ( ) ) )
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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-04 21:52:14 +00:00
func ValidateReplicationControllerSpec ( spec * api . ReplicationControllerSpec , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-08-25 19:07:03 +00:00
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-04 21:52:14 +00:00
func ValidatePodTemplateSpec ( spec * api . PodTemplateSpec , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
func ValidateReadOnlyPersistentDisks ( volumes [ ] api . Volume , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "gcePersistentDisk" , ".readOnly" ) , false , "readOnly must be true for replicated pods > 1, as 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-03 21:38:40 +00:00
func ValidateNode ( node * api . Node ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & node . ObjectMeta , false , ValidateNodeName , validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateNodeUpdate ( node , oldNode * api . Node ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & node . ObjectMeta , & oldNode . ObjectMeta , validation . NewFieldPath ( "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-04 00:17:48 +00:00
// allErrs = append(allErrs, validation.NewInvalidError("status", node.Status, "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewDuplicateError ( validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewForbiddenError ( validation . NewFieldPath ( "" ) , "update contains more than labels or capacity changes" ) )
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-04 21:52:14 +00:00
func validateResourceName ( value string , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsQualifiedName ( value ) {
2015-11-04 21:52:14 +00:00
return append ( allErrs , validation . NewInvalidError ( fldPath , value , "resource typename: " + 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-04 21:52:14 +00:00
return append ( allErrs , validation . NewInvalidError ( fldPath , value , "is neither a standard resource type nor is fully qualified" ) )
2015-01-17 00:34:47 +00:00
}
}
2015-11-03 21:38:40 +00:00
return validation . 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-03 21:38:40 +00:00
func ValidateLimitRange ( limitRange * api . LimitRange ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & limitRange . ObjectMeta , true , ValidateLimitRangeName , validation . NewFieldPath ( "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-04 21:52:14 +00:00
fldPath := validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewDuplicateError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "default" ) , limit . Default , "not supported when limit type is Pod" ) )
2015-09-10 20:39:59 +00:00
}
if len ( limit . DefaultRequest ) > 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "defaultRequest" ) , limit . DefaultRequest , "not supported when limit 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-03 21:38:40 +00:00
func ValidateServiceAccount ( serviceAccount * api . ServiceAccount ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & serviceAccount . ObjectMeta , true , ValidateServiceAccountName , validation . NewFieldPath ( "metadata" ) )
2015-04-27 22:53:28 +00:00
return allErrs
}
// ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set.
2015-11-03 21:38:40 +00:00
func ValidateServiceAccountUpdate ( newServiceAccount , oldServiceAccount * api . ServiceAccount ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newServiceAccount . ObjectMeta , & oldServiceAccount . ObjectMeta , validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateSecret ( secret * api . Secret ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & secret . ObjectMeta , true , ValidateSecretName , validation . NewFieldPath ( "metadata" ) )
2015-02-18 01:24:50 +00:00
2015-11-04 21:52:14 +00:00
dataPath := validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewTooLongError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-03 21:38:40 +00:00
func ValidateSecretUpdate ( newSecret , oldSecret * api . Secret ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newSecret . ObjectMeta , & oldSecret . ObjectMeta , validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateImmutableField ( newSecret . Type , oldSecret . Type , validation . NewFieldPath ( "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-04 21:52:14 +00:00
func validateBasicResource ( quantity resource . Quantity , fldPath * validation . FieldPath ) validation . ErrorList {
2015-01-25 04:19:36 +00:00
if quantity . Value ( ) < 0 {
2015-11-04 21:52:14 +00:00
return validation . ErrorList { validation . NewInvalidError ( fldPath , quantity . Value ( ) , "must be a valid resource quantity" ) }
2015-01-25 04:19:36 +00:00
}
2015-11-03 21:38:40 +00:00
return validation . ErrorList { }
2015-01-25 04:19:36 +00:00
}
// Validates resource requirement spec.
2015-11-04 21:52:14 +00:00
func ValidateResourceRequirements ( requirements * api . ResourceRequirements , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath , quantity . String ( ) , "limit cannot be smaller than 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-03 21:38:40 +00:00
func ValidateResourceQuota ( resourceQuota * api . ResourceQuota ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & resourceQuota . ObjectMeta , true , ValidateResourceQuotaName , validation . NewFieldPath ( "metadata" ) )
2015-02-20 06:03:36 +00:00
2015-11-04 21:52:14 +00:00
fldPath := validation . NewFieldPath ( "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-04 21:52:14 +00:00
fldPath = validation . NewFieldPath ( "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-04 21:52:14 +00:00
fldPath = validation . NewFieldPath ( "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-04 21:52:14 +00:00
func validateResourceQuantityValue ( resource string , value resource . Quantity , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-03 21:38:40 +00:00
func ValidateResourceQuotaUpdate ( newResourceQuota , oldResourceQuota * api . ResourceQuota ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newResourceQuota . ObjectMeta , & oldResourceQuota . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
fldPath := validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateResourceQuotaStatusUpdate ( newResourceQuota , oldResourceQuota * api . ResourceQuota ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newResourceQuota . ObjectMeta , & oldResourceQuota . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
2015-03-13 19:15:04 +00:00
if newResourceQuota . ResourceVersion == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( validation . NewFieldPath ( "resourceVersion" ) ) )
2015-03-13 19:15:04 +00:00
}
2015-11-04 21:52:14 +00:00
fldPath := validation . NewFieldPath ( "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-04 21:52:14 +00:00
fldPath = validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateNamespace ( namespace * api . Namespace ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & namespace . ObjectMeta , false , ValidateNamespaceName , validation . NewFieldPath ( "metadata" ) )
2015-03-20 16:48:12 +00:00
for i := range namespace . Spec . Finalizers {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validateFinalizerName ( string ( namespace . Spec . Finalizers [ i ] ) , validation . NewFieldPath ( "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-04 21:52:14 +00:00
func validateFinalizerName ( stringValue string , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsQualifiedName ( stringValue ) {
2015-11-04 21:52:14 +00:00
return append ( allErrs , validation . NewInvalidError ( fldPath , stringValue , qualifiedNameErrorMsg ) )
2015-03-20 16:48:12 +00:00
}
if len ( strings . Split ( stringValue , "/" ) ) == 1 {
if ! api . IsStandardFinalizerName ( stringValue ) {
2015-11-04 21:52:14 +00:00
return append ( allErrs , validation . NewInvalidError ( 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-03 21:38:40 +00:00
return validation . 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-03 21:38:40 +00:00
func ValidateNamespaceUpdate ( newNamespace * api . Namespace , oldNamespace * api . Namespace ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateNamespaceStatusUpdate ( newNamespace , oldNamespace * api . Namespace ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , validation . NewFieldPath ( "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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "status" , "Phase" ) , newNamespace . Status . Phase , "may only be in active status if it does not have a deletion timestamp." ) )
2015-04-10 15:41:18 +00:00
}
} else {
if newNamespace . Status . Phase != api . NamespaceTerminating {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "status" , "Phase" ) , newNamespace . Status . Phase , "may only be in terminating status if it has a deletion timestamp." ) )
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-03 21:38:40 +00:00
func ValidateNamespaceFinalizeUpdate ( newNamespace , oldNamespace * api . Namespace ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newNamespace . ObjectMeta , & oldNamespace . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
fldPath := validation . NewFieldPath ( "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-03 21:38:40 +00:00
func ValidateEndpoints ( endpoints * api . Endpoints ) validation . ErrorList {
2015-11-04 21:52:14 +00:00
allErrs := ValidateObjectMeta ( & endpoints . ObjectMeta , true , ValidateEndpointsName , validation . NewFieldPath ( "metadata" ) )
allErrs = append ( allErrs , validateEndpointSubsets ( endpoints . Subsets , validation . NewFieldPath ( "subsets" ) ) ... )
2015-03-20 21:24:43 +00:00
return allErrs
}
2015-11-04 21:52:14 +00:00
func validateEndpointSubsets ( subsets [ ] api . EndpointSubset , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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
allErrs = append ( allErrs , validation . NewRequiredError ( idxPath . Child ( "addresses or notReadyAddresses" ) ) )
2015-03-20 21:24:43 +00:00
}
if len ( ss . Ports ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( 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-04 21:52:14 +00:00
func validateEndpointAddress ( address * api . EndpointAddress , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-09-10 22:48:28 +00:00
if ! validation . IsValidIPv4 ( address . IP ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "ip" ) , address . IP , "invalid 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-04 21:52:14 +00:00
func validateIpIsNotLinkLocalOrLoopback ( ipAddress string , fldPath * validation . FieldPath ) validation . 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-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-08-12 00:18:21 +00:00
ip := net . ParseIP ( ipAddress )
if ip == nil {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath , ipAddress , "not 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
func validateEndpointPort ( port * api . EndpointPort , requireName bool , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . ErrorList { }
2015-03-20 14:40:20 +00:00
if requireName && port . Name == "" {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "name" ) ) )
2015-03-20 14:40:20 +00:00
} else if port . Name != "" {
2015-09-10 22:48:28 +00:00
if ! validation . IsDNS1123Label ( port . Name ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "port" ) , port . Port , PortRangeErrorMsg ) )
2015-03-20 14:40:20 +00:00
}
if len ( port . Protocol ) == 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewRequiredError ( fldPath . Child ( "protocol" ) ) )
2015-06-17 00:28:11 +00:00
} else if ! supportedPortProtocols . Has ( string ( port . Protocol ) ) {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewNotSupportedError ( 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-03 21:38:40 +00:00
func ValidateEndpointsUpdate ( newEndpoints , oldEndpoints * api . Endpoints ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & newEndpoints . ObjectMeta , & oldEndpoints . ObjectMeta , validation . NewFieldPath ( "metadata" ) ) ... )
allErrs = append ( allErrs , validateEndpointSubsets ( newEndpoints . Subsets , validation . NewFieldPath ( "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-04 21:52:14 +00:00
func ValidateSecurityContext ( sc * api . SecurityContext , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewForbiddenError ( fldPath . Child ( "privileged" ) , sc . Privileged ) )
2015-05-05 23:02:13 +00:00
}
}
if sc . RunAsUser != nil {
if * sc . RunAsUser < 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( fldPath . Child ( "runAsUser" ) , * sc . RunAsUser , "runAsUser cannot be negative" ) )
2015-05-05 23:02:13 +00:00
}
}
return allErrs
}
2015-07-27 19:49:06 +00:00
2015-11-03 21:38:40 +00:00
func ValidatePodLogOptions ( opts * api . PodLogOptions ) validation . ErrorList {
allErrs := validation . ErrorList { }
2015-09-10 03:46:11 +00:00
if opts . TailLines != nil && * opts . TailLines < 0 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "tailLines" ) , * opts . TailLines , "tailLines must be a non-negative integer or nil" ) )
2015-09-10 03:46:11 +00:00
}
if opts . LimitBytes != nil && * opts . LimitBytes < 1 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "limitBytes" ) , * opts . LimitBytes , "limitBytes must be a positive integer or nil" ) )
2015-09-10 03:46:11 +00:00
}
switch {
case opts . SinceSeconds != nil && opts . SinceTime != nil :
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "sinceSeconds" ) , * opts . SinceSeconds , "only one of sinceTime or sinceSeconds can be provided" ) )
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "sinceTime" ) , * opts . SinceTime , "only one of sinceTime or sinceSeconds can be provided" ) )
2015-09-10 03:46:11 +00:00
case opts . SinceSeconds != nil :
if * opts . SinceSeconds < 1 {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( validation . NewFieldPath ( "sinceSeconds" ) , * opts . SinceSeconds , "sinceSeconds must be a positive integer" ) )
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-04 21:52:14 +00:00
func ValidateLoadBalancerStatus ( status * api . LoadBalancerStatus , fldPath * validation . FieldPath ) validation . ErrorList {
2015-11-03 21:38:40 +00:00
allErrs := validation . 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "ip" ) , ingress . IP , "must be an 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-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "hostname" ) , ingress . Hostname , errMsg ) )
2015-10-12 19:09:20 +00:00
}
if isIP := ( net . ParseIP ( ingress . Hostname ) != nil ) ; isIP {
2015-11-04 21:52:14 +00:00
allErrs = append ( allErrs , validation . NewInvalidError ( idxPath . Child ( "hostname" ) , ingress . Hostname , "must be a DNS name, not an IP address" ) )
2015-10-12 19:09:20 +00:00
}
}
}
return allErrs
}