2014-07-01 20:01:39 +00:00
/ *
Copyright 2014 Google Inc . All rights reserved .
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 (
2014-10-31 06:03:52 +00:00
"fmt"
2015-03-16 21:36:30 +00:00
"net"
2015-02-10 19:00:11 +00:00
"path"
2015-03-30 22:07:53 +00:00
"reflect"
2014-07-08 04:32:56 +00:00
"strings"
2014-07-01 20:01:39 +00:00
2014-08-30 01:20:27 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
2015-01-25 04:19:36 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
2014-09-16 14:04:12 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
2014-07-25 16:15:17 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
2014-07-01 20:01:39 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
2015-03-22 21:40:47 +00:00
errs "github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
2015-03-30 22:07:53 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
2014-12-12 05:39:56 +00:00
"github.com/golang/glog"
2014-07-01 20:01:39 +00:00
)
2015-02-05 00:36:27 +00:00
const cIdentifierErrorMsg string = "must match regex " + util . CIdentifierFmt
const isNegativeErrorMsg string = "value must not be negative"
func intervalErrorMsg ( lo , hi int ) string {
return fmt . Sprintf ( "must be greater than %d and less than %d" , lo , hi )
}
2015-02-27 15:08:02 +00:00
var labelValueErrorMsg string = fmt . Sprintf ( "must have at most %d characters and match regex %s" , util . LabelValueMaxLength , util . LabelValueFmt )
2015-03-11 14:57:19 +00:00
var qualifiedNameErrorMsg string = fmt . Sprintf ( "must have at most %d characters and match regex %s" , util . QualifiedNameMaxLength , util . QualifiedNameFmt )
2015-02-05 00:36:27 +00:00
var dnsSubdomainErrorMsg string = fmt . Sprintf ( "must have at most %d characters and match regex %s" , util . DNS1123SubdomainMaxLength , util . DNS1123SubdomainFmt )
2015-03-11 14:57:19 +00:00
var dns1123LabelErrorMsg string = fmt . Sprintf ( "must have at most %d characters and match regex %s" , util . DNS1123LabelMaxLength , util . DNS1123LabelFmt )
2015-02-05 00:36:27 +00:00
var dns952LabelErrorMsg string = fmt . Sprintf ( "must have at most %d characters and match regex %s" , util . DNS952LabelMaxLength , util . DNS952LabelFmt )
var pdPartitionErrorMsg string = intervalErrorMsg ( 0 , 255 )
var portRangeErrorMsg string = intervalErrorMsg ( 0 , 65536 )
2015-03-02 13:41:13 +00:00
const totalAnnotationSizeLimitB int = 64 * ( 1 << 10 ) // 64 kB
2015-02-02 00:03:04 +00:00
// ValidateLabels validates that a set of labels are correctly defined.
func ValidateLabels ( labels map [ string ] string , field string ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-02-27 15:08:02 +00:00
for k , v := range labels {
2015-02-02 00:03:04 +00:00
if ! util . IsQualifiedName ( k ) {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( field , k , qualifiedNameErrorMsg ) )
2015-02-02 00:03:04 +00:00
}
2015-02-27 15:08:02 +00:00
if ! util . IsValidLabelValue ( v ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( field , v , labelValueErrorMsg ) )
}
2015-02-02 00:03:04 +00:00
}
return allErrs
}
// ValidateAnnotations validates that a set of annotations are correctly defined.
func ValidateAnnotations ( annotations map [ string ] string , field string ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-02-27 15:08:02 +00:00
var totalSize int64
for k , v := range annotations {
2015-02-02 00:03:04 +00:00
if ! util . IsQualifiedName ( strings . ToLower ( k ) ) {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( field , 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-03-23 13:31:42 +00:00
allErrs = append ( allErrs , errs . NewFieldTooLong ( field , "" , 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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
// 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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
// 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 ) {
return nameIsDNS952Label ( name , prefix )
}
// 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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
// 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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
// 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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
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 ) {
return nameIsDNSSubdomain ( name , prefix )
}
2015-01-27 23:55:54 +00:00
// nameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
2015-01-27 23:56:38 +00:00
func nameIsDNSSubdomain ( name string , prefix bool ) ( bool , string ) {
if prefix {
name = maskTrailingDash ( name )
}
2015-03-11 14:57:19 +00:00
if util . IsDNS1123Subdomain ( name ) {
2015-01-27 23:55:54 +00:00
return true , ""
}
2015-02-05 00:36:27 +00:00
return false , dnsSubdomainErrorMsg
2015-01-27 23:55:54 +00:00
}
// nameIsDNS952Label is a ValidateNameFunc for names that must be a DNS 952 label.
2015-01-27 23:56:38 +00:00
func nameIsDNS952Label ( name string , prefix bool ) ( bool , string ) {
if prefix {
name = maskTrailingDash ( name )
}
2015-01-27 23:55:54 +00:00
if util . IsDNS952Label ( name ) {
return true , ""
}
2015-02-05 00:36:27 +00:00
return false , dns952LabelErrorMsg
2015-01-27 23:55:54 +00:00
}
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-01-27 23:55:54 +00:00
func ValidateObjectMeta ( meta * api . ObjectMeta , requiresNamespace bool , nameFn ValidateNameFunc ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-01-27 23:56:38 +00:00
if len ( meta . GenerateName ) != 0 {
if ok , qualifier := nameFn ( meta . GenerateName , true ) ; ! ok {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "generateName" , meta . GenerateName , qualifier ) )
}
}
// if the generated name validates, but the calculated value does not, it's a problem with generation, and we
// report it here. This may confuse users, but indicates a programming bug and still must be validated.
2015-01-27 23:55:54 +00:00
if len ( meta . Name ) == 0 {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" ) )
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-01-27 23:55:54 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , meta . Name , qualifier ) )
}
}
2015-01-27 23:56:38 +00:00
2015-01-27 23:55:54 +00:00
if requiresNamespace {
if len ( meta . Namespace ) == 0 {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" ) )
} else if ! util . IsDNS1123Subdomain ( meta . Namespace ) {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , meta . Namespace , dnsSubdomainErrorMsg ) )
2015-01-27 23:55:54 +00:00
}
} else {
if len ( meta . Namespace ) != 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , meta . Namespace , "namespace is not allowed on this type" ) )
}
}
allErrs = append ( allErrs , ValidateLabels ( meta . Labels , "labels" ) ... )
2015-02-02 00:03:04 +00:00
allErrs = append ( allErrs , ValidateAnnotations ( meta . Annotations , "annotations" ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
}
// ValidateObjectMetaUpdate validates an object's metadata when updated
func ValidateObjectMetaUpdate ( old , meta * api . ObjectMeta ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
// in the event it is left empty, set it, to allow clients more flexibility
if len ( meta . UID ) == 0 {
meta . UID = old . UID
}
2015-03-09 16:04:35 +00:00
// ignore changes to timestamp
if old . CreationTimestamp . IsZero ( ) {
old . CreationTimestamp = meta . CreationTimestamp
} else {
2015-01-27 23:55:54 +00:00
meta . CreationTimestamp = old . CreationTimestamp
}
2015-03-20 00:51:07 +00:00
// Reject updates that don't specify a resource version
if meta . ResourceVersion == "" {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "resourceVersion" , meta . ResourceVersion , "resourceVersion must be specified for an update" ) )
}
2015-01-27 23:55:54 +00:00
if old . Name != meta . Name {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , meta . Name , "field is immutable" ) )
}
if old . Namespace != meta . Namespace {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , meta . Namespace , "field is immutable" ) )
}
if old . UID != meta . UID {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "uid" , meta . UID , "field is immutable" ) )
}
if old . CreationTimestamp != meta . CreationTimestamp {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "creationTimestamp" , meta . CreationTimestamp , "field is immutable" ) )
}
allErrs = append ( allErrs , ValidateLabels ( meta . Labels , "labels" ) ... )
2015-02-02 00:03:04 +00:00
allErrs = append ( allErrs , ValidateAnnotations ( meta . Annotations , "annotations" ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
2014-10-31 06:03:52 +00:00
}
2014-10-24 16:43:14 +00:00
func validateVolumes ( volumes [ ] api . Volume ) ( util . StringSet , errs . ValidationErrorList ) {
allErrs := errs . ValidationErrorList { }
2014-07-08 06:20:30 +00:00
2014-07-01 21:40:36 +00:00
allNames := util . StringSet { }
2015-01-26 17:52:50 +00:00
for i , vol := range volumes {
2015-03-03 22:48:55 +00:00
el := validateSource ( & vol . VolumeSource ) . Prefix ( "source" )
2014-08-20 02:57:48 +00:00
if len ( vol . Name ) == 0 {
2015-03-11 14:57:19 +00:00
el = append ( el , errs . NewFieldRequired ( "name" ) )
} else if ! util . IsDNS1123Label ( vol . Name ) {
el = append ( el , errs . NewFieldInvalid ( "name" , vol . Name , dns1123LabelErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( vol . Name ) {
2014-09-03 21:16:00 +00:00
el = append ( el , errs . NewFieldDuplicate ( "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 {
2014-08-20 03:54:20 +00:00
allErrs = append ( allErrs , el . PrefixIndex ( i ) ... )
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
}
2014-10-24 16:43:14 +00:00
func validateSource ( source * api . VolumeSource ) errs . ValidationErrorList {
2014-07-16 19:32:59 +00:00
numVolumes := 0
2014-10-24 16:43:14 +00:00
allErrs := errs . ValidationErrorList { }
2015-01-20 23:56:44 +00:00
if source . HostPath != nil {
2014-07-16 19:32:59 +00:00
numVolumes ++
2015-02-20 06:27:27 +00:00
allErrs = append ( allErrs , validateHostPathVolumeSource ( source . HostPath ) . Prefix ( "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-02-20 06:27:27 +00:00
allErrs = append ( allErrs , validateGitRepoVolumeSource ( source . GitRepo ) . Prefix ( "gitRepo" ) ... )
2014-07-16 19:32:59 +00:00
}
2014-08-05 17:58:43 +00:00
if source . GCEPersistentDisk != nil {
numVolumes ++
2015-02-20 06:27:27 +00:00
allErrs = append ( allErrs , validateGCEPersistentDiskVolumeSource ( source . GCEPersistentDisk ) . Prefix ( "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-04-07 21:16:36 +00:00
allErrs = append ( allErrs , validateAWSElasticBlockStoreVolumeSource ( source . AWSElasticBlockStore ) . Prefix ( "awsElasticBlockStore" ) ... )
2015-03-06 14:26:39 +00:00
}
2015-02-18 01:24:50 +00:00
if source . Secret != nil {
numVolumes ++
2015-02-20 06:27:27 +00:00
allErrs = append ( allErrs , validateSecretVolumeSource ( source . Secret ) . Prefix ( "secret" ) ... )
2015-02-18 01:24:50 +00:00
}
2015-02-10 19:00:11 +00:00
if source . NFS != nil {
numVolumes ++
allErrs = append ( allErrs , validateNFS ( source . NFS ) . Prefix ( "nfs" ) ... )
}
2015-03-13 21:31:13 +00:00
if source . ISCSI != nil {
numVolumes ++
allErrs = append ( allErrs , validateISCSIVolumeSource ( source . ISCSI ) . Prefix ( "iscsi" ) ... )
}
2015-03-26 18:53:21 +00:00
if source . Glusterfs != nil {
numVolumes ++
allErrs = append ( allErrs , validateGlusterfs ( source . Glusterfs ) . Prefix ( "glusterfs" ) ... )
}
2015-01-26 17:52:50 +00:00
if numVolumes != 1 {
2014-11-20 22:24:10 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "" , source , "exactly 1 volume type is required" ) )
2014-07-16 19:32:59 +00:00
}
return allErrs
}
2015-02-20 06:27:27 +00:00
func validateHostPathVolumeSource ( hostDir * api . HostPathVolumeSource ) errs . ValidationErrorList {
2014-10-24 16:43:14 +00:00
allErrs := errs . ValidationErrorList { }
2014-07-15 01:39:30 +00:00
if hostDir . Path == "" {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "path" ) )
2014-07-15 01:39:30 +00:00
}
return allErrs
}
2015-02-20 06:27:27 +00:00
func validateGitRepoVolumeSource ( gitRepo * api . GitRepoVolumeSource ) errs . ValidationErrorList {
2014-11-24 04:03:11 +00:00
allErrs := errs . ValidationErrorList { }
if gitRepo . Repository == "" {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "repository" ) )
2014-11-24 04:03:11 +00:00
}
return allErrs
}
2014-07-08 04:32:56 +00:00
2015-03-13 21:31:13 +00:00
func validateISCSIVolumeSource ( iscsi * api . ISCSIVolumeSource ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if iscsi . TargetPortal == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "targetPortal" ) )
}
if iscsi . IQN == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "iqn" ) )
}
if iscsi . FSType == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "fsType" ) )
}
if iscsi . Lun < 0 || iscsi . Lun > 255 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "lun" , iscsi . Lun , "" ) )
}
return allErrs
}
2015-02-20 06:27:27 +00:00
func validateGCEPersistentDiskVolumeSource ( PD * api . GCEPersistentDiskVolumeSource ) errs . ValidationErrorList {
2014-10-24 16:43:14 +00:00
allErrs := errs . ValidationErrorList { }
2014-08-05 17:58:43 +00:00
if PD . PDName == "" {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "pdName" ) )
2014-08-05 17:58:43 +00:00
}
if PD . FSType == "" {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "fsType" ) )
2014-08-05 17:58:43 +00:00
}
if PD . Partition < 0 || PD . Partition > 255 {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "partition" , PD . Partition , pdPartitionErrorMsg ) )
2014-08-05 17:58:43 +00:00
}
return allErrs
}
2015-04-07 21:16:36 +00:00
func validateAWSElasticBlockStoreVolumeSource ( PD * api . AWSElasticBlockStoreVolumeSource ) errs . ValidationErrorList {
2015-03-06 14:26:39 +00:00
allErrs := errs . ValidationErrorList { }
2015-04-09 13:34:16 +00:00
if PD . VolumeID == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "volumeID" ) )
2015-03-06 14:26:39 +00:00
}
if PD . FSType == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "fsType" ) )
}
if PD . Partition < 0 || PD . Partition > 255 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "partition" , PD . Partition , pdPartitionErrorMsg ) )
}
return allErrs
}
2015-02-20 06:27:27 +00:00
func validateSecretVolumeSource ( secretSource * api . SecretVolumeSource ) errs . ValidationErrorList {
2015-02-18 01:24:50 +00:00
allErrs := errs . ValidationErrorList { }
2015-03-24 17:17:14 +00:00
if secretSource . SecretName == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "secretName" ) )
2015-02-18 01:24:50 +00:00
}
return allErrs
}
2015-02-10 19:00:11 +00:00
func validateNFS ( nfs * api . NFSVolumeSource ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if nfs . Server == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "server" ) )
}
if nfs . Path == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "path" ) )
}
if ! path . IsAbs ( nfs . Path ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "path" , nfs . Path , "must be an absolute path" ) )
}
return allErrs
}
2015-03-26 18:53:21 +00:00
func validateGlusterfs ( glusterfs * api . GlusterfsVolumeSource ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if glusterfs . EndpointsName == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "endpoints" ) )
}
if glusterfs . Path == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "path" ) )
}
return allErrs
}
2015-03-23 18:18:11 +00:00
func ValidatePersistentVolumeName ( name string , prefix bool ) ( bool , string ) {
2015-03-26 19:50:36 +00:00
return nameIsDNSSubdomain ( name , prefix )
2015-03-23 18:18:11 +00:00
}
func ValidatePersistentVolume ( pv * api . PersistentVolume ) errs . ValidationErrorList {
2015-03-26 19:50:36 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMeta ( & pv . ObjectMeta , false , ValidatePersistentVolumeName ) . Prefix ( "metadata" ) ... )
2015-03-23 18:18:11 +00:00
if len ( pv . Spec . Capacity ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "persistentVolume.Capacity" ) )
}
if _ , ok := pv . Spec . Capacity [ api . ResourceStorage ] ; ! ok || len ( pv . Spec . Capacity ) > 1 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "" , pv . Spec . Capacity , fmt . Sprintf ( "only %s is expected" , api . ResourceStorage ) ) )
}
numVolumes := 0
if pv . Spec . HostPath != nil {
numVolumes ++
allErrs = append ( allErrs , validateHostPathVolumeSource ( pv . Spec . HostPath ) . Prefix ( "hostPath" ) ... )
}
if pv . Spec . GCEPersistentDisk != nil {
numVolumes ++
allErrs = append ( allErrs , validateGCEPersistentDiskVolumeSource ( pv . Spec . GCEPersistentDisk ) . Prefix ( "persistentDisk" ) ... )
}
2015-04-07 21:16:36 +00:00
if pv . Spec . AWSElasticBlockStore != nil {
2015-03-06 14:26:39 +00:00
numVolumes ++
2015-04-07 21:16:36 +00:00
allErrs = append ( allErrs , validateAWSElasticBlockStoreVolumeSource ( pv . Spec . AWSElasticBlockStore ) . Prefix ( "awsElasticBlockStore" ) ... )
2015-03-06 14:26:39 +00:00
}
2015-03-23 18:18:11 +00:00
if numVolumes != 1 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "" , pv . Spec . PersistentVolumeSource , "exactly 1 volume type is required" ) )
}
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.
func ValidatePersistentVolumeUpdate ( newPv , oldPv * api . PersistentVolume ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
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.
func ValidatePersistentVolumeStatusUpdate ( newPv , oldPv * api . PersistentVolume ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldPv . ObjectMeta , & newPv . ObjectMeta ) . Prefix ( "metadata" ) ... )
if newPv . ResourceVersion == "" {
2015-04-21 19:20:06 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "resourceVersion" ) )
2015-03-26 19:50:36 +00:00
}
newPv . Spec = oldPv . Spec
return allErrs
}
2015-03-23 18:18:11 +00:00
func ValidatePersistentVolumeClaim ( pvc * api . PersistentVolumeClaim ) errs . ValidationErrorList {
allErrs := ValidateObjectMeta ( & pvc . ObjectMeta , true , ValidatePersistentVolumeName )
if len ( pvc . Spec . AccessModes ) == 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "persistentVolumeClaim.Spec.AccessModes" , pvc . Spec . AccessModes , "at least 1 AccessModeType is required" ) )
}
2015-03-31 15:37:09 +00:00
if _ , ok := pvc . Spec . Resources . Requests [ api . ResourceStorage ] ; ! ok {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "persistentVolumeClaim.Spec.Resources.Requests" , pvc . Spec . Resources . Requests , "No Storage size specified" ) )
2015-03-23 18:18:11 +00:00
}
return allErrs
}
2015-03-26 19:50:36 +00:00
func ValidatePersistentVolumeClaimUpdate ( newPvc , oldPvc * api . PersistentVolumeClaim ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = ValidatePersistentVolumeClaim ( newPvc )
2015-03-30 22:07:53 +00:00
if oldPvc . Status . VolumeRef != nil {
oldModesAsString := volume . GetAccessModesAsString ( oldPvc . Spec . AccessModes )
newModesAsString := volume . GetAccessModesAsString ( newPvc . Spec . AccessModes )
if oldModesAsString != newModesAsString {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.AccessModes" , oldPvc . Spec . AccessModes , "field is immutable" ) )
}
if ! reflect . DeepEqual ( oldPvc . Spec . Resources , newPvc . Spec . Resources ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.Resources" , oldPvc . Spec . Resources , "field is immutable" ) )
}
}
2015-03-26 19:50:36 +00:00
newPvc . Status = oldPvc . Status
return allErrs
}
func ValidatePersistentVolumeClaimStatusUpdate ( newPvc , oldPvc * api . PersistentVolumeClaim ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldPvc . ObjectMeta , & newPvc . ObjectMeta ) . Prefix ( "metadata" ) ... )
if newPvc . ResourceVersion == "" {
2015-04-21 19:20:06 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "resourceVersion" ) )
2015-03-26 19:50:36 +00:00
}
newPvc . Spec = oldPvc . Spec
return allErrs
}
2014-11-24 04:03:11 +00:00
var supportedPortProtocols = util . NewStringSet ( string ( api . ProtocolTCP ) , string ( api . ProtocolUDP ) )
2015-02-23 22:25:56 +00:00
func validatePorts ( ports [ ] api . ContainerPort ) errs . ValidationErrorList {
2014-10-24 16:43:14 +00:00
allErrs := errs . ValidationErrorList { }
2014-07-08 06:20:30 +00:00
2014-07-08 04:32:56 +00:00
allNames := util . StringSet { }
2015-01-26 17:52:50 +00:00
for i , port := range ports {
2014-10-24 16:43:14 +00:00
pErrs := errs . ValidationErrorList { }
2014-07-08 04:32:56 +00:00
if len ( port . Name ) > 0 {
2015-03-11 14:57:19 +00:00
if len ( port . Name ) > util . DNS1123LabelMaxLength || ! util . IsDNS1123Label ( port . Name ) {
pErrs = append ( pErrs , errs . NewFieldInvalid ( "name" , port . Name , dns1123LabelErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( port . Name ) {
2014-09-03 21:16:00 +00:00
pErrs = append ( pErrs , errs . NewFieldDuplicate ( "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-02-05 00:36:27 +00:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "containerPort" , port . ContainerPort , portRangeErrorMsg ) )
2014-08-20 02:57:48 +00:00
} else if ! util . IsValidPortNum ( port . ContainerPort ) {
2015-02-05 00:36:27 +00:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "containerPort" , port . ContainerPort , portRangeErrorMsg ) )
2014-07-08 04:32:56 +00:00
}
2014-08-19 22:18:49 +00:00
if port . HostPort != 0 && ! util . IsValidPortNum ( port . HostPort ) {
2015-02-05 00:36:27 +00:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "hostPort" , port . HostPort , portRangeErrorMsg ) )
2014-07-08 04:32:56 +00:00
}
if len ( port . Protocol ) == 0 {
2015-03-11 14:57:19 +00:00
pErrs = append ( pErrs , errs . NewFieldRequired ( "protocol" ) )
2014-09-28 03:31:37 +00:00
} else if ! supportedPortProtocols . Has ( strings . ToUpper ( string ( port . Protocol ) ) ) {
2014-09-03 21:16:00 +00:00
pErrs = append ( pErrs , errs . NewFieldNotSupported ( "protocol" , port . Protocol ) )
2014-07-08 04:32:56 +00:00
}
2014-08-20 03:54:20 +00:00
allErrs = append ( allErrs , pErrs . PrefixIndex ( i ) ... )
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-10-24 16:43:14 +00:00
func validateEnv ( vars [ ] api . EnvVar ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 06:20:30 +00:00
2015-01-26 17:52:50 +00:00
for i , ev := range vars {
2014-10-24 16:43:14 +00:00
vErrs := errs . ValidationErrorList { }
2014-07-01 22:56:30 +00:00
if len ( ev . Name ) == 0 {
2015-03-11 14:57:19 +00:00
vErrs = append ( vErrs , errs . NewFieldRequired ( "name" ) )
2014-07-01 22:56:30 +00:00
}
if ! util . IsCIdentifier ( ev . Name ) {
2015-02-05 00:36:27 +00:00
vErrs = append ( vErrs , errs . NewFieldInvalid ( "name" , ev . Name , cIdentifierErrorMsg ) )
2014-07-01 22:56:30 +00:00
}
2014-08-20 03:54:20 +00:00
allErrs = append ( allErrs , vErrs . PrefixIndex ( i ) ... )
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
}
2014-10-24 16:43:14 +00:00
func validateVolumeMounts ( mounts [ ] api . VolumeMount , volumes util . StringSet ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 06:20:30 +00:00
2015-01-26 17:52:50 +00:00
for i , mnt := range mounts {
2014-10-24 16:43:14 +00:00
mErrs := errs . ValidationErrorList { }
2014-07-05 02:46:56 +00:00
if len ( mnt . Name ) == 0 {
2015-03-11 14:57:19 +00:00
mErrs = append ( mErrs , errs . NewFieldRequired ( "name" ) )
2014-07-08 06:20:30 +00:00
} else if ! volumes . Has ( mnt . Name ) {
2014-11-07 02:08:46 +00:00
mErrs = append ( mErrs , errs . NewFieldNotFound ( "name" , mnt . Name ) )
2014-07-05 02:46:56 +00:00
}
if len ( mnt . MountPath ) == 0 {
2015-03-11 14:57:19 +00:00
mErrs = append ( mErrs , errs . NewFieldRequired ( "mountPath" ) )
2014-07-15 01:39:30 +00:00
}
2014-08-20 03:54:20 +00:00
allErrs = append ( allErrs , mErrs . PrefixIndex ( i ) ... )
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-02-15 07:02:07 +00:00
func validateProbe ( probe * api . Probe ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if probe == nil {
return allErrs
}
allErrs = append ( allErrs , validateHandler ( & probe . Handler ) ... )
if probe . InitialDelaySeconds < 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "initialDelay" , probe . InitialDelaySeconds , "may not be less than zero" ) )
}
if probe . TimeoutSeconds < 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "timeout" , probe . TimeoutSeconds , "may not be less than zero" ) )
}
return allErrs
}
2014-07-08 04:32:56 +00:00
// AccumulateUniquePorts runs an extraction function on each Port of each Container,
// accumulating the results and returning an error if any ports conflict.
2015-02-23 22:25:56 +00:00
func AccumulateUniquePorts ( containers [ ] api . Container , accumulator map [ int ] bool , extract func ( * api . ContainerPort ) int ) errs . ValidationErrorList {
2014-10-24 16:43:14 +00:00
allErrs := errs . ValidationErrorList { }
2014-07-08 06:20:30 +00:00
2015-01-26 17:52:50 +00:00
for ci , ctr := range containers {
2014-10-24 16:43:14 +00:00
cErrs := errs . ValidationErrorList { }
2014-07-08 04:32:56 +00:00
for pi := range ctr . Ports {
port := extract ( & ctr . Ports [ pi ] )
2014-08-19 22:18:49 +00:00
if port == 0 {
continue
}
2014-07-08 04:32:56 +00:00
if accumulator [ port ] {
2014-10-03 03:41:02 +00:00
cErrs = append ( cErrs , errs . NewFieldDuplicate ( "port" , port ) )
2014-07-08 06:20:30 +00:00
} else {
accumulator [ port ] = true
2014-07-08 04:32:56 +00:00
}
}
2014-08-20 03:54:20 +00:00
allErrs = append ( allErrs , cErrs . PrefixIndex ( ci ) ... )
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.
2014-10-24 16:43:14 +00:00
func checkHostPortConflicts ( containers [ ] api . Container ) errs . ValidationErrorList {
2014-07-08 04:32:56 +00:00
allPorts := map [ int ] bool { }
2015-02-23 22:25:56 +00:00
return AccumulateUniquePorts ( containers , allPorts , func ( p * api . ContainerPort ) int { return p . HostPort } )
2014-07-08 04:32:56 +00:00
}
2014-10-24 16:43:14 +00:00
func validateExecAction ( exec * api . ExecAction ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
2014-09-12 23:04:10 +00:00
if len ( exec . Command ) == 0 {
2015-03-11 14:57:19 +00:00
allErrors = append ( allErrors , errs . NewFieldRequired ( "command" ) )
2014-09-12 23:04:10 +00:00
}
return allErrors
}
2014-10-24 16:43:14 +00:00
func validateHTTPGetAction ( http * api . HTTPGetAction ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
2014-09-12 23:04:10 +00:00
if len ( http . Path ) == 0 {
2015-03-11 14:57:19 +00:00
allErrors = append ( allErrors , errs . NewFieldRequired ( "path" ) )
2014-09-12 23:04:10 +00:00
}
2015-02-28 01:33:58 +00:00
if http . Port . Kind == util . IntstrInt && ! util . IsValidPortNum ( http . Port . IntVal ) {
allErrors = append ( allErrors , errs . NewFieldInvalid ( "port" , http . Port , portRangeErrorMsg ) )
} else if http . Port . Kind == util . IntstrString && len ( http . Port . StrVal ) == 0 {
2015-03-11 14:57:19 +00:00
allErrors = append ( allErrors , errs . NewFieldRequired ( "port" ) )
2015-02-28 01:33:58 +00:00
}
return allErrors
}
func validateTCPSocketAction ( tcp * api . TCPSocketAction ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
if tcp . Port . Kind == util . IntstrInt && ! util . IsValidPortNum ( tcp . Port . IntVal ) {
allErrors = append ( allErrors , errs . NewFieldInvalid ( "port" , tcp . Port , portRangeErrorMsg ) )
} else if tcp . Port . Kind == util . IntstrString && len ( tcp . Port . StrVal ) == 0 {
2015-03-11 14:57:19 +00:00
allErrors = append ( allErrors , errs . NewFieldRequired ( "port" ) )
2015-02-28 01:33:58 +00:00
}
2014-09-12 23:04:10 +00:00
return allErrors
}
2014-10-24 16:43:14 +00:00
func validateHandler ( handler * api . Handler ) errs . ValidationErrorList {
2014-11-20 22:24:10 +00:00
numHandlers := 0
2014-10-24 16:43:14 +00:00
allErrors := errs . ValidationErrorList { }
2014-09-12 23:04:10 +00:00
if handler . Exec != nil {
2014-11-20 22:24:10 +00:00
numHandlers ++
2014-09-12 23:04:10 +00:00
allErrors = append ( allErrors , validateExecAction ( handler . Exec ) . Prefix ( "exec" ) ... )
2014-11-20 22:24:10 +00:00
}
if handler . HTTPGet != nil {
numHandlers ++
2014-09-12 23:04:10 +00:00
allErrors = append ( allErrors , validateHTTPGetAction ( handler . HTTPGet ) . Prefix ( "httpGet" ) ... )
2014-11-20 22:24:10 +00:00
}
2015-02-28 01:33:58 +00:00
if handler . TCPSocket != nil {
numHandlers ++
allErrors = append ( allErrors , validateTCPSocketAction ( handler . TCPSocket ) . Prefix ( "tcpSocket" ) ... )
}
2014-11-20 22:24:10 +00:00
if numHandlers != 1 {
allErrors = append ( allErrors , errs . NewFieldInvalid ( "" , handler , "exactly 1 handler type is required" ) )
2014-09-12 23:04:10 +00:00
}
return allErrors
}
2014-10-24 16:43:14 +00:00
func validateLifecycle ( lifecycle * api . Lifecycle ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-09-12 23:04:10 +00:00
if lifecycle . PostStart != nil {
allErrs = append ( allErrs , validateHandler ( lifecycle . PostStart ) . Prefix ( "postStart" ) ... )
}
if lifecycle . PreStop != nil {
allErrs = append ( allErrs , validateHandler ( lifecycle . PreStop ) . Prefix ( "preStop" ) ... )
}
return allErrs
}
2015-01-26 17:52:50 +00:00
func validatePullPolicy ( ctr * api . Container ) errs . ValidationErrorList {
2015-01-16 23:02:36 +00:00
allErrors := errs . ValidationErrorList { }
2015-01-16 23:02:36 +00:00
switch ctr . ImagePullPolicy {
case api . PullAlways , api . PullIfNotPresent , api . PullNever :
break
2015-01-26 17:52:50 +00:00
case "" :
2015-03-11 14:57:19 +00:00
allErrors = append ( allErrors , errs . NewFieldRequired ( "" ) )
2015-01-16 23:02:36 +00:00
default :
2015-01-16 23:02:36 +00:00
allErrors = append ( allErrors , errs . NewFieldNotSupported ( "" , ctr . ImagePullPolicy ) )
}
return allErrors
}
2014-10-24 16:43:14 +00:00
func validateContainers ( containers [ ] api . Container , volumes util . StringSet ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 06:20:30 +00:00
2015-03-18 15:00:18 +00:00
if len ( containers ) == 0 {
return append ( allErrs , errs . NewFieldRequired ( "" ) )
}
2014-07-01 22:14:25 +00:00
allNames := util . StringSet { }
2015-01-26 17:52:50 +00:00
for i , ctr := range containers {
2014-10-24 16:43:14 +00:00
cErrs := errs . ValidationErrorList { }
2014-09-16 22:18:33 +00:00
capabilities := capabilities . Get ( )
2014-08-20 02:57:48 +00:00
if len ( ctr . Name ) == 0 {
2015-03-11 14:57:19 +00:00
cErrs = append ( cErrs , errs . NewFieldRequired ( "name" ) )
} else if ! util . IsDNS1123Label ( ctr . Name ) {
cErrs = append ( cErrs , errs . NewFieldInvalid ( "name" , ctr . Name , dns1123LabelErrorMsg ) )
2014-07-08 06:20:30 +00:00
} else if allNames . Has ( ctr . Name ) {
2014-09-03 21:16:00 +00:00
cErrs = append ( cErrs , errs . NewFieldDuplicate ( "name" , ctr . Name ) )
2014-09-16 14:04:12 +00:00
} else if ctr . Privileged && ! capabilities . AllowPrivileged {
2014-10-14 23:14:28 +00:00
cErrs = append ( cErrs , errs . NewFieldForbidden ( "privileged" , ctr . Privileged ) )
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-03-11 14:57:19 +00:00
cErrs = append ( cErrs , errs . NewFieldRequired ( "image" ) )
2014-07-05 02:46:56 +00:00
}
2014-09-12 23:04:10 +00:00
if ctr . Lifecycle != nil {
cErrs = append ( cErrs , validateLifecycle ( ctr . Lifecycle ) . Prefix ( "lifecycle" ) ... )
}
2015-02-15 07:02:07 +00:00
cErrs = append ( cErrs , validateProbe ( ctr . LivenessProbe ) . Prefix ( "livenessProbe" ) ... )
cErrs = append ( cErrs , validateProbe ( ctr . ReadinessProbe ) . Prefix ( "readinessProbe" ) ... )
2014-08-20 03:54:20 +00:00
cErrs = append ( cErrs , validatePorts ( ctr . Ports ) . Prefix ( "ports" ) ... )
cErrs = append ( cErrs , validateEnv ( ctr . Env ) . Prefix ( "env" ) ... )
cErrs = append ( cErrs , validateVolumeMounts ( ctr . VolumeMounts , volumes ) . Prefix ( "volumeMounts" ) ... )
2015-01-26 17:52:50 +00:00
cErrs = append ( cErrs , validatePullPolicy ( & ctr ) . Prefix ( "pullPolicy" ) ... )
2015-04-20 18:56:15 +00:00
cErrs = append ( cErrs , ValidateResourceRequirements ( & ctr . Resources ) . Prefix ( "resources" ) ... )
2014-08-20 03:54:20 +00:00
allErrs = append ( allErrs , cErrs . PrefixIndex ( i ) ... )
2014-07-01 22:14:25 +00:00
}
2014-07-08 04:32:56 +00:00
// Check for colliding ports across all containers.
// TODO(thockin): This really is dependent on the network config of the host (IP per pod?)
// and the config of the new manifest. But we have not specced that out yet, so we'll just
// make some assumptions for now. As of now, pods share a network namespace, which means that
// every Port.HostPort across the whole pod must be unique.
2014-08-16 20:34:06 +00:00
allErrs = append ( allErrs , checkHostPortConflicts ( containers ) ... )
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
}
2014-07-10 11:46:35 +00:00
var supportedManifestVersions = util . NewStringSet ( "v1beta1" , "v1beta2" )
2014-07-08 04:32:56 +00:00
2014-07-01 20:01:39 +00:00
// ValidateManifest tests that the specified ContainerManifest has valid data.
// This includes checking formatting and uniqueness. It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility
// tricks.
2014-11-07 02:08:46 +00:00
// TODO: replaced by ValidatePodSpec
2014-10-24 16:43:14 +00:00
func ValidateManifest ( manifest * api . ContainerManifest ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 06:20:30 +00:00
2014-07-01 20:01:39 +00:00
if len ( manifest . Version ) == 0 {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "version" ) )
2014-07-08 06:20:30 +00:00
} else if ! supportedManifestVersions . Has ( strings . ToLower ( manifest . Version ) ) {
2014-09-03 21:16:00 +00:00
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "version" , manifest . Version ) )
2014-07-01 20:01:39 +00:00
}
2014-09-09 23:08:21 +00:00
allVolumes , vErrs := validateVolumes ( manifest . Volumes )
allErrs = append ( allErrs , vErrs . Prefix ( "volumes" ) ... )
2014-08-20 03:54:20 +00:00
allErrs = append ( allErrs , validateContainers ( manifest . Containers , allVolumes ) . Prefix ( "containers" ) ... )
2014-08-26 18:25:17 +00:00
allErrs = append ( allErrs , validateRestartPolicy ( & manifest . RestartPolicy ) . Prefix ( "restartPolicy" ) ... )
2014-11-12 05:21:40 +00:00
allErrs = append ( allErrs , validateDNSPolicy ( & manifest . DNSPolicy ) . Prefix ( "dnsPolicy" ) ... )
2014-08-16 20:48:48 +00:00
return allErrs
2014-07-01 20:01:39 +00:00
}
2014-07-10 19:45:01 +00:00
2014-10-24 16:43:14 +00:00
func validateRestartPolicy ( restartPolicy * api . RestartPolicy ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
2015-03-14 01:38:07 +00:00
switch * restartPolicy {
case api . RestartPolicyAlways , api . RestartPolicyOnFailure , api . RestartPolicyNever :
break
case "" :
2015-03-11 14:57:19 +00:00
allErrors = append ( allErrors , errs . NewFieldRequired ( "" ) )
2015-03-14 01:38:07 +00:00
default :
allErrors = append ( allErrors , errs . NewFieldNotSupported ( "" , restartPolicy ) )
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
2014-11-12 05:21:40 +00:00
func validateDNSPolicy ( dnsPolicy * api . DNSPolicy ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
switch * dnsPolicy {
case api . DNSClusterFirst , api . DNSDefault :
break
2015-01-26 17:52:50 +00:00
case "" :
2015-03-11 14:57:19 +00:00
allErrors = append ( allErrors , errs . NewFieldRequired ( "" ) )
2014-11-12 05:21:40 +00:00
default :
allErrors = append ( allErrors , errs . NewFieldNotSupported ( "" , dnsPolicy ) )
}
return allErrors
}
2015-03-23 23:34:35 +00:00
func validateHostNetwork ( hostNetwork bool , containers [ ] api . Container ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
if hostNetwork {
for _ , container := range containers {
for _ , port := range container . Ports {
if port . HostPort != port . ContainerPort {
allErrors = append ( allErrors , errs . NewFieldInvalid ( "containerPort" , port . ContainerPort , "containerPort must match hostPort if hostNetwork is set to true" ) )
}
}
}
}
return allErrors
}
2014-09-02 10:00:28 +00:00
// ValidatePod tests if required fields in the pod are set.
2014-10-24 16:43:14 +00:00
func ValidatePod ( pod * api . Pod ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-01-28 04:50:01 +00:00
allErrs = append ( allErrs , ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName ) . Prefix ( "metadata" ) ... )
2014-11-13 15:52:13 +00:00
allErrs = append ( allErrs , ValidatePodSpec ( & pod . Spec ) . Prefix ( "spec" ) ... )
2015-01-27 23:55:54 +00:00
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.
func ValidatePodSpec ( spec * api . PodSpec ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allVolumes , vErrs := validateVolumes ( spec . Volumes )
allErrs = append ( allErrs , vErrs . Prefix ( "volumes" ) ... )
allErrs = append ( allErrs , validateContainers ( spec . Containers , allVolumes ) . Prefix ( "containers" ) ... )
allErrs = append ( allErrs , validateRestartPolicy ( & spec . RestartPolicy ) . Prefix ( "restartPolicy" ) ... )
2014-11-12 05:21:40 +00:00
allErrs = append ( allErrs , validateDNSPolicy ( & spec . DNSPolicy ) . Prefix ( "dnsPolicy" ) ... )
2015-01-20 03:32:39 +00:00
allErrs = append ( allErrs , ValidateLabels ( spec . NodeSelector , "nodeSelector" ) ... )
2015-03-23 23:34:35 +00:00
allErrs = append ( allErrs , validateHostNetwork ( spec . HostNetwork , spec . Containers ) . Prefix ( "hostNetwork" ) ... )
2014-11-07 02:08:46 +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.
2014-10-24 16:43:14 +00:00
func ValidatePodUpdate ( newPod , oldPod * api . Pod ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-10-10 03:30:34 +00:00
2015-01-27 23:55:54 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldPod . ObjectMeta , & newPod . ObjectMeta ) . Prefix ( "metadata" ) ... )
2014-10-14 19:28:45 +00:00
2014-11-13 15:52:13 +00:00
if len ( newPod . Spec . Containers ) != len ( oldPod . Spec . Containers ) {
2014-11-20 22:24:10 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.containers" , newPod . Spec . Containers , "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-03-24 16:11:16 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec" , newPod . Spec , "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.
func ValidatePodStatusUpdate ( newPod , oldPod * api . Pod ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldPod . ObjectMeta , & newPod . ObjectMeta ) . Prefix ( "metadata" ) ... )
// TODO: allow change when bindings are properly decoupled from pods
2015-04-02 12:52:03 +00:00
if newPod . Spec . Host != oldPod . Spec . Host {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "status.host" , newPod . Spec . Host , "pod host 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.
func ValidatePodTemplate ( pod * api . PodTemplate ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & pod . Spec , 0 ) . Prefix ( "spec" ) ... )
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.
func ValidatePodTemplateUpdate ( newPod , oldPod * api . PodTemplate ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldPod . ObjectMeta , & newPod . ObjectMeta ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , ValidatePodTemplateSpec ( & newPod . Spec , 0 ) . Prefix ( "spec" ) ... )
return allErrs
}
2014-12-17 12:52:11 +00:00
var supportedSessionAffinityType = util . NewStringSet ( string ( api . AffinityTypeClientIP ) , string ( api . AffinityTypeNone ) )
2014-07-15 13:53:39 +00:00
// ValidateService tests if required fields in the service are set.
2015-01-27 23:55:54 +00:00
func ValidateService ( service * api . Service ) errs . ValidationErrorList {
2014-10-24 16:43:14 +00:00
allErrs := errs . ValidationErrorList { }
2015-01-28 04:50:01 +00:00
allErrs = append ( allErrs , ValidateObjectMeta ( & service . ObjectMeta , true , ValidateServiceName ) . Prefix ( "metadata" ) ... )
2015-01-27 23:55:54 +00:00
2015-03-13 15:16:41 +00:00
if len ( service . Spec . Ports ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "spec.ports" ) )
2014-08-22 21:44:21 +00:00
}
2015-03-13 15:16:41 +00:00
allPortNames := util . StringSet { }
for i := range service . Spec . Ports {
allErrs = append ( allErrs , validateServicePort ( & service . Spec . Ports [ i ] , i , & allPortNames ) . PrefixIndex ( i ) . Prefix ( "spec.ports" ) ... )
2015-02-28 01:33:58 +00:00
}
2014-11-18 17:49:00 +00:00
if service . Spec . Selector != nil {
2015-01-20 03:32:39 +00:00
allErrs = append ( allErrs , ValidateLabels ( service . Spec . Selector , "spec.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-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "spec.sessionAffinity" ) )
2014-12-29 22:39:09 +00:00
} else if ! supportedSessionAffinityType . Has ( string ( service . Spec . SessionAffinity ) ) {
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "spec.sessionAffinity" , service . Spec . SessionAffinity ) )
2014-12-17 12:52:11 +00:00
}
2015-03-16 21:36:30 +00:00
if api . IsServiceIPSet ( service ) {
if ip := net . ParseIP ( service . Spec . PortalIP ) ; ip == nil {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.portalIP" , service . Spec . PortalIP , "portalIP should be empty, 'None', or a valid IP address" ) )
}
}
2015-03-16 14:03:05 +00:00
for _ , ip := range service . Spec . PublicIPs {
if ip == "0.0.0.0" {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.publicIPs" , ip , "is not an IP address" ) )
2015-03-20 14:40:20 +00:00
} else if util . IsValidIPv4 ( ip ) && net . ParseIP ( ip ) . IsLoopback ( ) {
2015-03-16 14:03:05 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.publicIPs" , ip , "publicIP cannot be a loopback" ) )
}
}
2015-04-03 19:06:25 +00:00
if service . Spec . CreateExternalLoadBalancer {
for i := range service . Spec . Ports {
if service . Spec . Ports [ i ] . Protocol != api . ProtocolTCP {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.ports" , service . Spec . Ports [ i ] , "cannot create an external load balancer with non-TCP ports" ) )
}
}
}
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-03-13 15:16:41 +00:00
func validateServicePort ( sp * api . ServicePort , index int , allNames * util . StringSet ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if len ( sp . Name ) == 0 {
// Allow empty names if they are the first port (mostly for compat).
if index != 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" ) )
}
} else if ! util . IsDNS1123Label ( sp . Name ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , sp . Name , dns1123LabelErrorMsg ) )
} else if allNames . Has ( sp . Name ) {
allErrs = append ( allErrs , errs . NewFieldDuplicate ( "name" , sp . Name ) )
}
if ! util . IsValidPortNum ( sp . Port ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "port" , sp . Port , portRangeErrorMsg ) )
}
if len ( sp . Protocol ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "protocol" ) )
} else if ! supportedPortProtocols . Has ( strings . ToUpper ( string ( sp . Protocol ) ) ) {
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "protocol" , sp . Protocol ) )
}
if sp . TargetPort != util . NewIntOrStringFromInt ( 0 ) && sp . TargetPort != util . NewIntOrStringFromString ( "" ) {
if sp . TargetPort . Kind == util . IntstrInt && ! util . IsValidPortNum ( sp . TargetPort . IntVal ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "targetPort" , sp . TargetPort , portRangeErrorMsg ) )
}
}
return allErrs
}
2015-01-27 23:55:54 +00:00
// ValidateServiceUpdate tests if required fields in the service are set during an update
func ValidateServiceUpdate ( oldService , service * api . Service ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldService . ObjectMeta , & service . ObjectMeta ) . Prefix ( "metadata" ) ... )
// TODO: PortalIP should be a Status field, since the system can set a value != to the user's value
2015-03-16 21:36:30 +00:00
// once PortalIP is set, it cannot be unset.
if api . IsServiceIPSet ( oldService ) && service . Spec . PortalIP != oldService . Spec . PortalIP {
2015-01-27 23:55:54 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.portalIP" , service . Spec . PortalIP , "field is immutable" ) )
}
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.
2014-10-24 16:43:14 +00:00
func ValidateReplicationController ( controller * api . ReplicationController ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-01-28 04:50:01 +00:00
allErrs = append ( allErrs , ValidateObjectMeta ( & controller . ObjectMeta , true , ValidateReplicationControllerName ) . Prefix ( "metadata" ) ... )
2014-11-07 02:08:46 +00:00
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec ) . Prefix ( "spec" ) ... )
2015-01-27 23:55:54 +00:00
return allErrs
}
// ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
func ValidateReplicationControllerUpdate ( oldController , controller * api . ReplicationController ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldController . ObjectMeta , & controller . ObjectMeta ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec ) . Prefix ( "spec" ) ... )
2014-09-16 16:54:38 +00:00
return allErrs
}
2014-11-07 02:08:46 +00:00
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
func ValidateReplicationControllerSpec ( spec * api . ReplicationControllerSpec ) errs . ValidationErrorList {
2014-10-24 16:43:14 +00:00
allErrs := errs . ValidationErrorList { }
2014-11-07 02:08:46 +00:00
selector := labels . Set ( spec . Selector ) . AsSelector ( )
if selector . Empty ( ) {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "selector" ) )
2014-07-25 16:15:17 +00:00
}
2014-11-07 02:08:46 +00:00
if spec . Replicas < 0 {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "replicas" , spec . Replicas , isNegativeErrorMsg ) )
2014-08-22 00:02:39 +00:00
}
2014-11-07 02:08:46 +00:00
if spec . Template == nil {
2015-03-11 14:57:19 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "template" ) )
2014-11-07 02:08:46 +00:00
} else {
labels := labels . Set ( spec . Template . Labels )
if ! selector . Matches ( labels ) {
2014-11-20 22:24:10 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "template.labels" , spec . Template . Labels , "selector does not match template" ) )
2014-11-07 02:08:46 +00:00
}
2015-02-05 04:55:16 +00:00
allErrs = append ( allErrs , ValidatePodTemplateSpec ( spec . Template , spec . Replicas ) . Prefix ( "template" ) ... )
2014-11-20 22:24:10 +00:00
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
2015-03-14 01:38:07 +00:00
if spec . Template . Spec . RestartPolicy != api . RestartPolicyAlways {
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "template.restartPolicy" , spec . Template . Spec . RestartPolicy ) )
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
// ValidatePodTemplateSpec validates the spec of a pod template
2015-02-05 04:55:16 +00:00
func ValidatePodTemplateSpec ( spec * api . PodTemplateSpec , replicas int ) errs . ValidationErrorList {
2014-11-07 02:08:46 +00:00
allErrs := errs . ValidationErrorList { }
2015-01-20 03:32:39 +00:00
allErrs = append ( allErrs , ValidateLabels ( spec . Labels , "labels" ) ... )
2015-02-02 00:03:04 +00:00
allErrs = append ( allErrs , ValidateAnnotations ( spec . Annotations , "annotations" ) ... )
2014-11-07 02:08:46 +00:00
allErrs = append ( allErrs , ValidatePodSpec ( & spec . Spec ) . Prefix ( "spec" ) ... )
2015-02-05 04:55:16 +00:00
if replicas > 1 {
allErrs = append ( allErrs , ValidateReadOnlyPersistentDisks ( spec . Spec . Volumes ) . Prefix ( "spec.volumes" ) ... )
}
2014-11-07 02:08:46 +00:00
return allErrs
}
2014-10-24 16:43:14 +00:00
func ValidateReadOnlyPersistentDisks ( volumes [ ] api . Volume ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-08-05 17:58:43 +00:00
for _ , vol := range volumes {
2015-03-03 22:48:55 +00:00
if vol . GCEPersistentDisk != nil {
if vol . GCEPersistentDisk . ReadOnly == false {
2015-02-05 04:55:16 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "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-01-27 23:55:54 +00:00
// ValidateMinion tests if required fields in the node are set.
func ValidateMinion ( node * api . Node ) errs . ValidationErrorList {
2014-11-12 17:38:15 +00:00
allErrs := errs . ValidationErrorList { }
2015-01-28 04:50:01 +00:00
allErrs = append ( allErrs , ValidateObjectMeta ( & node . ObjectMeta , false , ValidateNodeName ) . Prefix ( "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 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "spec.ExternalID" ) )
}
// 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
// ValidateMinionUpdate tests to make sure a minion update can be applied. Modifies oldMinion.
2014-12-08 03:44:27 +00:00
func ValidateMinionUpdate ( oldMinion * api . Node , minion * api . Node ) errs . ValidationErrorList {
2014-11-17 18:22:27 +00:00
allErrs := errs . ValidationErrorList { }
2015-01-27 23:55:54 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldMinion . ObjectMeta , & minion . ObjectMeta ) . Prefix ( "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.
// if !api.Semantic.DeepEqual(minion.Status, api.NodeStatus{}) {
// allErrs = append(allErrs, errs.NewFieldInvalid("status", minion.Status, "status must be empty"))
// }
2014-12-12 05:39:56 +00:00
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
oldMinion . ObjectMeta = minion . ObjectMeta
// Allow users to update capacity
2015-03-25 13:44:40 +00:00
oldMinion . Status . Capacity = minion . Status . Capacity
2015-02-17 20:03:14 +00:00
// Allow users to unschedule node
oldMinion . Spec . Unschedulable = minion . Spec . Unschedulable
2014-12-22 19:04:57 +00:00
// Clear status
oldMinion . Status = minion . Status
2014-12-12 05:39:56 +00:00
2015-02-05 00:36:27 +00:00
// TODO: Add a 'real' ValidationError type for this error and provide print actual diffs.
2015-01-05 21:38:39 +00:00
if ! api . Semantic . DeepEqual ( oldMinion , minion ) {
2014-12-12 05:39:56 +00:00
glog . V ( 4 ) . Infof ( "Update failed validation %#v vs %#v" , oldMinion , minion )
allErrs = append ( allErrs , fmt . Errorf ( "update contains more than labels or capacity changes" ) )
2014-11-17 18:22:27 +00:00
}
2015-01-27 23:55:54 +00:00
// TODO: validate Spec.Capacity
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-01-17 00:34:47 +00:00
// Refer to docs/resources.md for more details.
2015-02-05 00:36:27 +00:00
func validateResourceName ( value string , field string ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if ! util . IsQualifiedName ( value ) {
return append ( allErrs , errs . NewFieldInvalid ( field , 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-03-02 13:41:13 +00:00
return append ( allErrs , errs . NewFieldInvalid ( field , value , "is neither a standard resource type nor is fully qualified" ) )
2015-01-17 00:34:47 +00:00
}
}
return errs . ValidationErrorList { }
}
2015-01-22 21:52:40 +00:00
// ValidateLimitRange tests if required fields in the LimitRange are set.
func ValidateLimitRange ( limitRange * api . LimitRange ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-02-20 06:03:36 +00:00
allErrs = append ( allErrs , ValidateObjectMeta ( & limitRange . ObjectMeta , true , ValidateLimitRangeName ) . Prefix ( "metadata" ) ... )
2015-01-22 21:52:40 +00:00
// ensure resource names are properly qualified per docs/resources.md
for i := range limitRange . Spec . Limits {
limit := limitRange . Spec . Limits [ i ]
2015-01-24 15:47:07 +00:00
for k := range limit . Max {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , fmt . Sprintf ( "spec.limits[%d].max[%s]" , i , k ) ) ... )
2015-01-22 21:52:40 +00:00
}
2015-01-24 15:47:07 +00:00
for k := range limit . Min {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , fmt . Sprintf ( "spec.limits[%d].min[%s]" , i , k ) ) ... )
2015-01-25 04:19:36 +00:00
}
}
return allErrs
}
2015-02-18 01:24:50 +00:00
// ValidateSecret tests if required fields in the Secret are set.
func ValidateSecret ( secret * api . Secret ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-02-20 06:03:36 +00:00
allErrs = append ( allErrs , ValidateObjectMeta ( & secret . ObjectMeta , true , ValidateSecretName ) . Prefix ( "metadata" ) ... )
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-03-11 14:57:19 +00:00
if ! util . IsDNS1123Subdomain ( key ) {
2015-02-20 06:03:36 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( fmt . Sprintf ( "data[%s]" , key ) , key , cIdentifierErrorMsg ) )
2015-02-18 01:26:41 +00:00
}
2015-02-18 01:24:50 +00:00
totalSize += len ( value )
}
2015-02-18 01:26:41 +00:00
2015-02-18 01:24:50 +00:00
if totalSize > api . MaxSecretSize {
allErrs = append ( allErrs , errs . NewFieldForbidden ( "data" , "Maximum secret size exceeded" ) )
}
return allErrs
}
2015-01-25 04:19:36 +00:00
func validateBasicResource ( quantity resource . Quantity ) errs . ValidationErrorList {
if quantity . Value ( ) < 0 {
2015-04-21 19:20:06 +00:00
return errs . ValidationErrorList { errs . NewFieldInvalid ( "" , quantity . Value ( ) , "must be a valid resource quantity" ) }
2015-01-25 04:19:36 +00:00
}
return errs . ValidationErrorList { }
}
// Validates resource requirement spec.
2015-04-20 18:56:15 +00:00
func ValidateResourceRequirements ( requirements * api . ResourceRequirements ) errs . ValidationErrorList {
2015-01-25 04:19:36 +00:00
allErrs := errs . ValidationErrorList { }
2015-04-20 18:56:15 +00:00
for resourceName , quantity := range requirements . Limits {
2015-01-25 04:19:36 +00:00
// Validate resource name.
2015-02-05 00:36:27 +00:00
errs := validateResourceName ( resourceName . String ( ) , fmt . Sprintf ( "resources.limits[%s]" , resourceName ) )
2015-01-25 04:19:36 +00:00
if api . IsStandardResourceName ( resourceName . String ( ) ) {
errs = append ( errs , validateBasicResource ( quantity ) . Prefix ( fmt . Sprintf ( "Resource %s: " , resourceName ) ) ... )
2015-01-22 21:52:40 +00:00
}
2015-01-25 04:19:36 +00:00
allErrs = append ( allErrs , errs ... )
2015-01-22 21:52:40 +00:00
}
2015-04-20 18:56:15 +00:00
for resourceName , quantity := range requirements . Requests {
// Validate resource name.
errs := validateResourceName ( resourceName . String ( ) , fmt . Sprintf ( "resources.requests[%s]" , resourceName ) )
if api . IsStandardResourceName ( resourceName . String ( ) ) {
errs = append ( errs , validateBasicResource ( quantity ) . Prefix ( fmt . Sprintf ( "Resource %s: " , resourceName ) ) ... )
}
allErrs = append ( allErrs , errs ... )
}
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.
func ValidateResourceQuota ( resourceQuota * api . ResourceQuota ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-02-20 06:03:36 +00:00
allErrs = append ( allErrs , ValidateObjectMeta ( & resourceQuota . ObjectMeta , true , ValidateResourceQuotaName ) . Prefix ( "metadata" ) ... )
2015-01-23 17:38:30 +00:00
for k := range resourceQuota . Spec . Hard {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( resourceQuota . TypeMeta . Kind ) ) ... )
2015-01-23 17:38:30 +00:00
}
for k := range resourceQuota . Status . Hard {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( resourceQuota . TypeMeta . Kind ) ) ... )
2015-01-23 17:38:30 +00:00
}
for k := range resourceQuota . Status . Used {
2015-02-05 00:36:27 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( resourceQuota . TypeMeta . Kind ) ) ... )
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.
func ValidateResourceQuotaUpdate ( newResourceQuota , oldResourceQuota * api . ResourceQuota ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldResourceQuota . ObjectMeta , & newResourceQuota . ObjectMeta ) . Prefix ( "metadata" ) ... )
for k := range newResourceQuota . Spec . Hard {
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( newResourceQuota . TypeMeta . Kind ) ) ... )
}
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.
func ValidateResourceQuotaStatusUpdate ( newResourceQuota , oldResourceQuota * api . ResourceQuota ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldResourceQuota . ObjectMeta , & newResourceQuota . ObjectMeta ) . Prefix ( "metadata" ) ... )
if newResourceQuota . ResourceVersion == "" {
2015-04-21 19:20:06 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "resourceVersion" ) )
2015-03-13 19:15:04 +00:00
}
for k := range newResourceQuota . Status . Hard {
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( newResourceQuota . TypeMeta . Kind ) ) ... )
}
for k := range newResourceQuota . Status . Used {
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( newResourceQuota . TypeMeta . Kind ) ) ... )
}
newResourceQuota . Spec = oldResourceQuota . Spec
return allErrs
}
2015-01-19 21:50:00 +00:00
// ValidateNamespace tests if required fields are set.
func ValidateNamespace ( namespace * api . Namespace ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMeta ( & namespace . ObjectMeta , false , ValidateNamespaceName ) . Prefix ( "metadata" ) ... )
2015-03-20 16:48:12 +00:00
for i := range namespace . Spec . Finalizers {
allErrs = append ( allErrs , validateFinalizerName ( string ( namespace . Spec . Finalizers [ i ] ) ) ... )
}
2015-01-19 21:50:00 +00:00
return allErrs
}
2015-03-20 16:48:12 +00:00
// Validate finalizer names
func validateFinalizerName ( stringValue string ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if ! util . IsQualifiedName ( stringValue ) {
2015-04-21 19:20:06 +00:00
return append ( allErrs , errs . NewFieldInvalid ( "spec.finalizers" , stringValue , qualifiedNameErrorMsg ) )
2015-03-20 16:48:12 +00:00
}
if len ( strings . Split ( stringValue , "/" ) ) == 1 {
if ! api . IsStandardFinalizerName ( stringValue ) {
2015-04-21 19:20:06 +00:00
return append ( allErrs , errs . NewFieldInvalid ( "spec.finalizers" , stringValue , fmt . Sprintf ( "finalizer name is neither a standard finalizer name nor is it fully qualified" ) ) )
2015-03-20 16:48:12 +00:00
}
}
return errs . ValidationErrorList { }
}
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-04-09 03:15:49 +00:00
// TODO The syntax here is the reverse of the (old, new) pattern in most other validation. Fix this.
2015-03-31 21:00:04 +00:00
func ValidateNamespaceUpdate ( newNamespace * api . Namespace , oldNamespace * api . Namespace ) errs . ValidationErrorList {
2015-01-19 21:50:00 +00:00
allErrs := errs . ValidationErrorList { }
2015-03-31 21:00:04 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldNamespace . ObjectMeta , & newNamespace . ObjectMeta ) . Prefix ( "metadata" ) ... )
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.
func ValidateNamespaceStatusUpdate ( newNamespace , oldNamespace * api . Namespace ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldNamespace . ObjectMeta , & newNamespace . ObjectMeta ) . Prefix ( "metadata" ) ... )
newNamespace . Spec = oldNamespace . Spec
2015-04-10 15:41:18 +00:00
if newNamespace . DeletionTimestamp . IsZero ( ) {
if newNamespace . Status . Phase != api . NamespaceActive {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "Status.Phase" , newNamespace . Status . Phase , "A namespace may only be in active status if it does not have a deletion timestamp." ) )
}
} else {
if newNamespace . Status . Phase != api . NamespaceTerminating {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "Status.Phase" , newNamespace . Status . Phase , "A namespace may only be in terminating status if it has a deletion timestamp." ) )
}
}
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-03-20 16:48:12 +00:00
func ValidateNamespaceFinalizeUpdate ( newNamespace , oldNamespace * api . Namespace ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldNamespace . ObjectMeta , & newNamespace . ObjectMeta ) . Prefix ( "metadata" ) ... )
for i := range newNamespace . Spec . Finalizers {
allErrs = append ( allErrs , validateFinalizerName ( string ( newNamespace . Spec . Finalizers [ i ] ) ) ... )
}
newNamespace . Status = oldNamespace . Status
return allErrs
}
2015-03-15 06:03:46 +00:00
// ValidateEndpoints tests if required fields are set.
func ValidateEndpoints ( endpoints * api . Endpoints ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMeta ( & endpoints . ObjectMeta , true , ValidateEndpointsName ) . Prefix ( "metadata" ) ... )
2015-03-20 21:24:43 +00:00
allErrs = append ( allErrs , validateEndpointSubsets ( endpoints . Subsets ) . Prefix ( "subsets" ) ... )
return allErrs
}
func validateEndpointSubsets ( subsets [ ] api . EndpointSubset ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
for i := range subsets {
ss := & subsets [ i ]
ssErrs := errs . ValidationErrorList { }
if len ( ss . Addresses ) == 0 {
ssErrs = append ( ssErrs , errs . NewFieldRequired ( "addresses" ) )
}
if len ( ss . Ports ) == 0 {
ssErrs = append ( ssErrs , errs . NewFieldRequired ( "ports" ) )
}
2015-03-20 14:40:20 +00:00
for addr := range ss . Addresses {
ssErrs = append ( ssErrs , validateEndpointAddress ( & ss . Addresses [ addr ] ) . PrefixIndex ( addr ) . Prefix ( "addresses" ) ... )
}
for port := range ss . Ports {
ssErrs = append ( ssErrs , validateEndpointPort ( & ss . Ports [ port ] , len ( ss . Ports ) > 1 ) . PrefixIndex ( port ) . Prefix ( "ports" ) ... )
}
2015-03-20 21:24:43 +00:00
allErrs = append ( allErrs , ssErrs . PrefixIndex ( i ) ... )
}
2015-03-15 06:03:46 +00:00
return allErrs
}
2015-03-20 14:40:20 +00:00
func validateEndpointAddress ( address * api . EndpointAddress ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if ! util . IsValidIPv4 ( address . IP ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "ip" , address . IP , "invalid IPv4 address" ) )
}
return allErrs
}
func validateEndpointPort ( port * api . EndpointPort , requireName bool ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if requireName && port . Name == "" {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" ) )
} else if port . Name != "" {
if ! util . IsDNS1123Label ( port . Name ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , port . Name , dns1123LabelErrorMsg ) )
}
}
if ! util . IsValidPortNum ( port . Port ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "port" , port . Port , portRangeErrorMsg ) )
}
if len ( port . Protocol ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "protocol" ) )
} else if ! supportedPortProtocols . Has ( strings . ToUpper ( string ( port . Protocol ) ) ) {
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "protocol" , port . Protocol ) )
}
return allErrs
}
2015-03-15 06:03:46 +00:00
// ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
2015-03-20 14:40:20 +00:00
func ValidateEndpointsUpdate ( oldEndpoints , newEndpoints * api . Endpoints ) errs . ValidationErrorList {
2015-03-15 06:03:46 +00:00
allErrs := errs . ValidationErrorList { }
2015-03-20 14:40:20 +00:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldEndpoints . ObjectMeta , & newEndpoints . ObjectMeta ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , validateEndpointSubsets ( newEndpoints . Subsets ) . Prefix ( "subsets" ) ... )
2015-03-15 06:03:46 +00:00
return allErrs
}