2014-07-01 16:01:39 -04: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 18:48:41 -04:00
package validation
2014-07-01 16:01:39 -04:00
import (
2014-10-31 02:03:52 -04:00
"fmt"
2014-07-08 00:32:56 -04:00
"strings"
2014-07-01 16:01:39 -04:00
2014-08-29 21:20:27 -04:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
2014-08-14 16:46:22 -04:00
errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
2015-01-24 23:19:36 -05:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
2014-09-16 10:04:12 -04:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
2014-07-25 12:15:17 -04:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
2014-07-01 16:01:39 -04:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
2014-12-12 00:39:56 -05:00
"github.com/golang/glog"
2014-07-01 16:01:39 -04:00
)
2015-02-04 19:36:27 -05:00
const qualifiedNameErrorMsg string = "must match regex [" + util . DNS1123SubdomainFmt + " / ] " + util . DNS1123LabelFmt
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 )
}
var dnsSubdomainErrorMsg string = fmt . Sprintf ( "must have at most %d characters and match regex %s" , util . DNS1123SubdomainMaxLength , util . DNS1123SubdomainFmt )
var dnsLabelErrorMsg string = fmt . Sprintf ( "must have at most %d characters and match regex %s" , util . DNS1123LabelMaxLength , util . DNS1123LabelFmt )
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-02-01 19:03:04 -05:00
// ValidateLabels validates that a set of labels are correctly defined.
func ValidateLabels ( labels map [ string ] string , field string ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
for k := range labels {
if ! util . IsQualifiedName ( k ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( field , k , qualifiedNameErrorMsg ) )
2015-02-01 19:03:04 -05: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 { }
for k := range annotations {
if ! util . IsQualifiedName ( strings . ToLower ( k ) ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( field , k , qualifiedNameErrorMsg ) )
2015-02-01 19:03:04 -05:00
}
}
return allErrs
}
2015-01-27 18:55:54 -05:00
// ValidateNameFunc validates that the provided name is valid for a given resource type.
2015-01-27 18:56:38 -05: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 18:55:54 -05:00
2015-01-27 23:50:01 -05:00
// ValidatePodName can be used to check whether the given pod name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
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 16:50:00 -05:00
// ValidateNamespaceName can be used to check whether the given namespace name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateNamespaceName ( name string , prefix bool ) ( bool , string ) {
return nameIsDNSSubdomain ( name , prefix )
}
2015-01-27 18:55:54 -05:00
// nameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
2015-01-27 18:56:38 -05:00
func nameIsDNSSubdomain ( name string , prefix bool ) ( bool , string ) {
if prefix {
name = maskTrailingDash ( name )
}
2015-01-27 18:55:54 -05:00
if util . IsDNSSubdomain ( name ) {
return true , ""
}
2015-02-04 19:36:27 -05:00
return false , dnsSubdomainErrorMsg
2015-01-27 18:55:54 -05:00
}
// nameIsDNS952Label is a ValidateNameFunc for names that must be a DNS 952 label.
2015-01-27 18:56:38 -05:00
func nameIsDNS952Label ( name string , prefix bool ) ( bool , string ) {
if prefix {
name = maskTrailingDash ( name )
}
2015-01-27 18:55:54 -05:00
if util . IsDNS952Label ( name ) {
return true , ""
}
2015-02-04 19:36:27 -05:00
return false , dns952LabelErrorMsg
2015-01-27 18:55:54 -05:00
}
2015-01-27 18:56:38 -05:00
// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
// been performed.
2015-01-27 18:55:54 -05:00
func ValidateObjectMeta ( meta * api . ObjectMeta , requiresNamespace bool , nameFn ValidateNameFunc ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-01-27 18:56:38 -05: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 18:55:54 -05:00
if len ( meta . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" , meta . Name ) )
} else {
2015-01-27 18:56:38 -05:00
if ok , qualifier := nameFn ( meta . Name , false ) ; ! ok {
2015-01-27 18:55:54 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , meta . Name , qualifier ) )
}
}
2015-01-27 18:56:38 -05:00
2015-01-27 18:55:54 -05:00
if requiresNamespace {
if len ( meta . Namespace ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , meta . Namespace ) )
} else if ! util . IsDNSSubdomain ( meta . Namespace ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , meta . Namespace , dnsSubdomainErrorMsg ) )
2015-01-27 18:55:54 -05: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-01 19:03:04 -05:00
allErrs = append ( allErrs , ValidateAnnotations ( meta . Annotations , "annotations" ) ... )
2015-01-27 18:55:54 -05: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
}
if meta . CreationTimestamp . IsZero ( ) {
meta . CreationTimestamp = old . CreationTimestamp
}
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-01 19:03:04 -05:00
allErrs = append ( allErrs , ValidateAnnotations ( meta . Annotations , "annotations" ) ... )
2015-01-27 18:55:54 -05:00
return allErrs
2014-10-31 02:03:52 -04:00
}
2014-10-24 12:43:14 -04:00
func validateVolumes ( volumes [ ] api . Volume ) ( util . StringSet , errs . ValidationErrorList ) {
allErrs := errs . ValidationErrorList { }
2014-07-08 02:20:30 -04:00
2014-07-01 17:40:36 -04:00
allNames := util . StringSet { }
2015-01-26 12:52:50 -05:00
for i , vol := range volumes {
2015-01-20 20:40:43 -05:00
el := validateSource ( & vol . Source ) . Prefix ( "source" )
2014-08-19 22:57:48 -04:00
if len ( vol . Name ) == 0 {
2014-09-03 17:16:00 -04:00
el = append ( el , errs . NewFieldRequired ( "name" , vol . Name ) )
2014-08-19 22:57:48 -04:00
} else if ! util . IsDNSLabel ( vol . Name ) {
2015-02-04 19:36:27 -05:00
el = append ( el , errs . NewFieldInvalid ( "name" , vol . Name , dnsLabelErrorMsg ) )
2014-07-08 02:20:30 -04:00
} else if allNames . Has ( vol . Name ) {
2014-09-03 17:16:00 -04:00
el = append ( el , errs . NewFieldDuplicate ( "name" , vol . Name ) )
2014-07-16 15:32:59 -04:00
}
2014-08-14 16:46:22 -04:00
if len ( el ) == 0 {
2014-07-08 02:20:30 -04:00
allNames . Insert ( vol . Name )
2014-07-16 15:32:59 -04:00
} else {
2014-08-19 23:54:20 -04:00
allErrs = append ( allErrs , el . PrefixIndex ( i ) ... )
2014-07-01 17:40:36 -04:00
}
}
2014-07-08 02:20:30 -04:00
return allNames , allErrs
2014-07-01 17:40:36 -04:00
}
2014-10-24 12:43:14 -04:00
func validateSource ( source * api . VolumeSource ) errs . ValidationErrorList {
2014-07-16 15:32:59 -04:00
numVolumes := 0
2014-10-24 12:43:14 -04:00
allErrs := errs . ValidationErrorList { }
2015-01-20 18:56:44 -05:00
if source . HostPath != nil {
2014-07-16 15:32:59 -04:00
numVolumes ++
2015-01-20 18:56:44 -05:00
allErrs = append ( allErrs , validateHostPath ( source . HostPath ) . Prefix ( "hostPath" ) ... )
2014-07-16 15:32:59 -04:00
}
2014-10-01 16:35:21 -04:00
if source . EmptyDir != nil {
2014-07-16 15:32:59 -04:00
numVolumes ++
2014-11-23 23:03:11 -05:00
// EmptyDirs have nothing to validate
}
if source . GitRepo != nil {
numVolumes ++
2015-01-20 18:56:44 -05:00
allErrs = append ( allErrs , validateGitRepo ( source . GitRepo ) . Prefix ( "gitRepo" ) ... )
2014-07-16 15:32:59 -04:00
}
2014-08-05 13:58:43 -04:00
if source . GCEPersistentDisk != nil {
numVolumes ++
2015-01-20 18:56:44 -05:00
allErrs = append ( allErrs , validateGCEPersistentDisk ( source . GCEPersistentDisk ) . Prefix ( "persistentDisk" ) ... )
2014-08-05 13:58:43 -04:00
}
2015-01-26 12:52:50 -05:00
if numVolumes != 1 {
2014-11-20 17:24:10 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "" , source , "exactly 1 volume type is required" ) )
2014-07-16 15:32:59 -04:00
}
return allErrs
}
2015-01-20 18:56:44 -05:00
func validateHostPath ( hostDir * api . HostPath ) errs . ValidationErrorList {
2014-10-24 12:43:14 -04:00
allErrs := errs . ValidationErrorList { }
2014-07-14 21:39:30 -04:00
if hostDir . Path == "" {
2015-01-20 18:56:44 -05:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "path" , hostDir . Path ) )
2014-07-14 21:39:30 -04:00
}
return allErrs
}
2014-11-23 23:03:11 -05:00
func validateGitRepo ( gitRepo * api . GitRepo ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if gitRepo . Repository == "" {
2015-01-20 18:56:44 -05:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "repository" , gitRepo . Repository ) )
2014-11-23 23:03:11 -05:00
}
return allErrs
}
2014-07-08 00:32:56 -04:00
2014-10-24 12:43:14 -04:00
func validateGCEPersistentDisk ( PD * api . GCEPersistentDisk ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-08-05 13:58:43 -04:00
if PD . PDName == "" {
2015-01-20 18:56:44 -05:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "pdName" , PD . PDName ) )
2014-08-05 13:58:43 -04:00
}
if PD . FSType == "" {
2015-01-20 18:56:44 -05:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "fsType" , PD . FSType ) )
2014-08-05 13:58:43 -04:00
}
if PD . Partition < 0 || PD . Partition > 255 {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "partition" , PD . Partition , pdPartitionErrorMsg ) )
2014-08-05 13:58:43 -04:00
}
return allErrs
}
2014-11-23 23:03:11 -05:00
var supportedPortProtocols = util . NewStringSet ( string ( api . ProtocolTCP ) , string ( api . ProtocolUDP ) )
2014-10-24 12:43:14 -04:00
func validatePorts ( ports [ ] api . Port ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 02:20:30 -04:00
2014-07-08 00:32:56 -04:00
allNames := util . StringSet { }
2015-01-26 12:52:50 -05:00
for i , port := range ports {
2014-10-24 12:43:14 -04:00
pErrs := errs . ValidationErrorList { }
2014-07-08 00:32:56 -04:00
if len ( port . Name ) > 0 {
2015-02-04 19:36:27 -05:00
if len ( port . Name ) > util . DNS1123LabelMaxLength || ! util . IsDNSLabel ( port . Name ) {
pErrs = append ( pErrs , errs . NewFieldInvalid ( "name" , port . Name , dnsLabelErrorMsg ) )
2014-07-08 02:20:30 -04:00
} else if allNames . Has ( port . Name ) {
2014-09-03 17:16:00 -04:00
pErrs = append ( pErrs , errs . NewFieldDuplicate ( "name" , port . Name ) )
2014-07-08 02:20:30 -04:00
} else {
allNames . Insert ( port . Name )
2014-07-08 00:32:56 -04:00
}
}
2014-08-19 22:57:48 -04:00
if port . ContainerPort == 0 {
2015-02-04 19:36:27 -05:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "containerPort" , port . ContainerPort , portRangeErrorMsg ) )
2014-08-19 22:57:48 -04:00
} else if ! util . IsValidPortNum ( port . ContainerPort ) {
2015-02-04 19:36:27 -05:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "containerPort" , port . ContainerPort , portRangeErrorMsg ) )
2014-07-08 00:32:56 -04:00
}
2014-08-19 18:18:49 -04:00
if port . HostPort != 0 && ! util . IsValidPortNum ( port . HostPort ) {
2015-02-04 19:36:27 -05:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "hostPort" , port . HostPort , portRangeErrorMsg ) )
2014-07-08 00:32:56 -04:00
}
if len ( port . Protocol ) == 0 {
2015-01-26 12:52:50 -05:00
pErrs = append ( pErrs , errs . NewFieldRequired ( "protocol" , port . Protocol ) )
2014-09-27 23:31:37 -04:00
} else if ! supportedPortProtocols . Has ( strings . ToUpper ( string ( port . Protocol ) ) ) {
2014-09-03 17:16:00 -04:00
pErrs = append ( pErrs , errs . NewFieldNotSupported ( "protocol" , port . Protocol ) )
2014-07-08 00:32:56 -04:00
}
2014-08-19 23:54:20 -04:00
allErrs = append ( allErrs , pErrs . PrefixIndex ( i ) ... )
2014-07-08 00:32:56 -04:00
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-08 00:32:56 -04:00
}
2014-10-24 12:43:14 -04:00
func validateEnv ( vars [ ] api . EnvVar ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 02:20:30 -04:00
2015-01-26 12:52:50 -05:00
for i , ev := range vars {
2014-10-24 12:43:14 -04:00
vErrs := errs . ValidationErrorList { }
2014-07-01 18:56:30 -04:00
if len ( ev . Name ) == 0 {
2014-09-03 17:16:00 -04:00
vErrs = append ( vErrs , errs . NewFieldRequired ( "name" , ev . Name ) )
2014-07-01 18:56:30 -04:00
}
if ! util . IsCIdentifier ( ev . Name ) {
2015-02-04 19:36:27 -05:00
vErrs = append ( vErrs , errs . NewFieldInvalid ( "name" , ev . Name , cIdentifierErrorMsg ) )
2014-07-01 18:56:30 -04:00
}
2014-08-19 23:54:20 -04:00
allErrs = append ( allErrs , vErrs . PrefixIndex ( i ) ... )
2014-07-01 18:56:30 -04:00
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-01 18:56:30 -04:00
}
2014-10-24 12:43:14 -04:00
func validateVolumeMounts ( mounts [ ] api . VolumeMount , volumes util . StringSet ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 02:20:30 -04:00
2015-01-26 12:52:50 -05:00
for i , mnt := range mounts {
2014-10-24 12:43:14 -04:00
mErrs := errs . ValidationErrorList { }
2014-07-04 22:46:56 -04:00
if len ( mnt . Name ) == 0 {
2014-09-03 17:16:00 -04:00
mErrs = append ( mErrs , errs . NewFieldRequired ( "name" , mnt . Name ) )
2014-07-08 02:20:30 -04:00
} else if ! volumes . Has ( mnt . Name ) {
2014-11-06 21:08:46 -05:00
mErrs = append ( mErrs , errs . NewFieldNotFound ( "name" , mnt . Name ) )
2014-07-04 22:46:56 -04:00
}
if len ( mnt . MountPath ) == 0 {
2014-09-03 17:16:00 -04:00
mErrs = append ( mErrs , errs . NewFieldRequired ( "mountPath" , mnt . MountPath ) )
2014-07-14 21:39:30 -04:00
}
2014-08-19 23:54:20 -04:00
allErrs = append ( allErrs , mErrs . PrefixIndex ( i ) ... )
2014-07-04 22:46:56 -04:00
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-04 22:46:56 -04:00
}
2014-07-08 00:32:56 -04:00
// AccumulateUniquePorts runs an extraction function on each Port of each Container,
// accumulating the results and returning an error if any ports conflict.
2014-10-24 12:43:14 -04:00
func AccumulateUniquePorts ( containers [ ] api . Container , accumulator map [ int ] bool , extract func ( * api . Port ) int ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 02:20:30 -04:00
2015-01-26 12:52:50 -05:00
for ci , ctr := range containers {
2014-10-24 12:43:14 -04:00
cErrs := errs . ValidationErrorList { }
2014-07-08 00:32:56 -04:00
for pi := range ctr . Ports {
port := extract ( & ctr . Ports [ pi ] )
2014-08-19 18:18:49 -04:00
if port == 0 {
continue
}
2014-07-08 00:32:56 -04:00
if accumulator [ port ] {
2014-10-02 23:41:02 -04:00
cErrs = append ( cErrs , errs . NewFieldDuplicate ( "port" , port ) )
2014-07-08 02:20:30 -04:00
} else {
accumulator [ port ] = true
2014-07-08 00:32:56 -04:00
}
}
2014-08-19 23:54:20 -04:00
allErrs = append ( allErrs , cErrs . PrefixIndex ( ci ) ... )
2014-07-08 00:32:56 -04:00
}
2014-07-08 02:20:30 -04:00
return allErrs
2014-07-08 00:32:56 -04:00
}
2014-09-02 06:00:28 -04:00
// checkHostPortConflicts checks for colliding Port.HostPort values across
// a slice of containers.
2014-10-24 12:43:14 -04:00
func checkHostPortConflicts ( containers [ ] api . Container ) errs . ValidationErrorList {
2014-07-08 00:32:56 -04:00
allPorts := map [ int ] bool { }
2014-08-29 21:20:27 -04:00
return AccumulateUniquePorts ( containers , allPorts , func ( p * api . Port ) int { return p . HostPort } )
2014-07-08 00:32:56 -04:00
}
2014-10-24 12:43:14 -04:00
func validateExecAction ( exec * api . ExecAction ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
2014-09-12 19:04:10 -04:00
if len ( exec . Command ) == 0 {
allErrors = append ( allErrors , errs . NewFieldRequired ( "command" , exec . Command ) )
}
return allErrors
}
2014-10-24 12:43:14 -04:00
func validateHTTPGetAction ( http * api . HTTPGetAction ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
2014-09-12 19:04:10 -04:00
if len ( http . Path ) == 0 {
allErrors = append ( allErrors , errs . NewFieldRequired ( "path" , http . Path ) )
}
return allErrors
}
2014-10-24 12:43:14 -04:00
func validateHandler ( handler * api . Handler ) errs . ValidationErrorList {
2014-11-20 17:24:10 -05:00
numHandlers := 0
2014-10-24 12:43:14 -04:00
allErrors := errs . ValidationErrorList { }
2014-09-12 19:04:10 -04:00
if handler . Exec != nil {
2014-11-20 17:24:10 -05:00
numHandlers ++
2014-09-12 19:04:10 -04:00
allErrors = append ( allErrors , validateExecAction ( handler . Exec ) . Prefix ( "exec" ) ... )
2014-11-20 17:24:10 -05:00
}
if handler . HTTPGet != nil {
numHandlers ++
2014-09-12 19:04:10 -04:00
allErrors = append ( allErrors , validateHTTPGetAction ( handler . HTTPGet ) . Prefix ( "httpGet" ) ... )
2014-11-20 17:24:10 -05:00
}
if numHandlers != 1 {
allErrors = append ( allErrors , errs . NewFieldInvalid ( "" , handler , "exactly 1 handler type is required" ) )
2014-09-12 19:04:10 -04:00
}
return allErrors
}
2014-10-24 12:43:14 -04:00
func validateLifecycle ( lifecycle * api . Lifecycle ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-09-12 19:04:10 -04: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 12:52:50 -05:00
func validatePullPolicy ( ctr * api . Container ) errs . ValidationErrorList {
2015-01-16 18:02:36 -05:00
allErrors := errs . ValidationErrorList { }
2015-01-16 18:02:36 -05:00
switch ctr . ImagePullPolicy {
case api . PullAlways , api . PullIfNotPresent , api . PullNever :
break
2015-01-26 12:52:50 -05:00
case "" :
allErrors = append ( allErrors , errs . NewFieldRequired ( "" , ctr . ImagePullPolicy ) )
2015-01-16 18:02:36 -05:00
default :
2015-01-16 18:02:36 -05:00
allErrors = append ( allErrors , errs . NewFieldNotSupported ( "" , ctr . ImagePullPolicy ) )
}
return allErrors
}
2014-10-24 12:43:14 -04:00
func validateContainers ( containers [ ] api . Container , volumes util . StringSet ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 02:20:30 -04:00
2014-07-01 18:14:25 -04:00
allNames := util . StringSet { }
2015-01-26 12:52:50 -05:00
for i , ctr := range containers {
2014-10-24 12:43:14 -04:00
cErrs := errs . ValidationErrorList { }
2014-09-16 18:18:33 -04:00
capabilities := capabilities . Get ( )
2014-08-19 22:57:48 -04:00
if len ( ctr . Name ) == 0 {
2014-09-03 17:16:00 -04:00
cErrs = append ( cErrs , errs . NewFieldRequired ( "name" , ctr . Name ) )
2014-08-19 22:57:48 -04:00
} else if ! util . IsDNSLabel ( ctr . Name ) {
2015-02-04 19:36:27 -05:00
cErrs = append ( cErrs , errs . NewFieldInvalid ( "name" , ctr . Name , dnsLabelErrorMsg ) )
2014-07-08 02:20:30 -04:00
} else if allNames . Has ( ctr . Name ) {
2014-09-03 17:16:00 -04:00
cErrs = append ( cErrs , errs . NewFieldDuplicate ( "name" , ctr . Name ) )
2014-09-16 10:04:12 -04:00
} else if ctr . Privileged && ! capabilities . AllowPrivileged {
2014-10-14 19:14:28 -04:00
cErrs = append ( cErrs , errs . NewFieldForbidden ( "privileged" , ctr . Privileged ) )
2014-07-08 02:20:30 -04:00
} else {
allNames . Insert ( ctr . Name )
2014-07-01 18:14:25 -04:00
}
if len ( ctr . Image ) == 0 {
2014-09-03 17:16:00 -04:00
cErrs = append ( cErrs , errs . NewFieldRequired ( "image" , ctr . Image ) )
2014-07-04 22:46:56 -04:00
}
2014-09-12 19:04:10 -04:00
if ctr . Lifecycle != nil {
cErrs = append ( cErrs , validateLifecycle ( ctr . Lifecycle ) . Prefix ( "lifecycle" ) ... )
}
2014-08-19 23:54:20 -04: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 12:52:50 -05:00
cErrs = append ( cErrs , validatePullPolicy ( & ctr ) . Prefix ( "pullPolicy" ) ... )
cErrs = append ( cErrs , validateResourceRequirements ( & ctr ) . Prefix ( "resources" ) ... )
2014-08-19 23:54:20 -04:00
allErrs = append ( allErrs , cErrs . PrefixIndex ( i ) ... )
2014-07-01 18:14:25 -04:00
}
2014-07-08 00:32:56 -04: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 16:34:06 -04:00
allErrs = append ( allErrs , checkHostPortConflicts ( containers ) ... )
2014-07-08 20:33:15 -04:00
2014-07-10 07:46:35 -04:00
return allErrs
2014-07-01 18:14:25 -04:00
}
2014-07-10 07:46:35 -04:00
var supportedManifestVersions = util . NewStringSet ( "v1beta1" , "v1beta2" )
2014-07-08 00:32:56 -04:00
2014-07-01 16:01:39 -04: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-06 21:08:46 -05:00
// TODO: replaced by ValidatePodSpec
2014-10-24 12:43:14 -04:00
func ValidateManifest ( manifest * api . ContainerManifest ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-07-08 02:20:30 -04:00
2014-07-01 16:01:39 -04:00
if len ( manifest . Version ) == 0 {
2014-09-03 17:16:00 -04:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "version" , manifest . Version ) )
2014-07-08 02:20:30 -04:00
} else if ! supportedManifestVersions . Has ( strings . ToLower ( manifest . Version ) ) {
2014-09-03 17:16:00 -04:00
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "version" , manifest . Version ) )
2014-07-01 16:01:39 -04:00
}
2014-09-09 19:08:21 -04:00
allVolumes , vErrs := validateVolumes ( manifest . Volumes )
allErrs = append ( allErrs , vErrs . Prefix ( "volumes" ) ... )
2014-08-19 23:54:20 -04:00
allErrs = append ( allErrs , validateContainers ( manifest . Containers , allVolumes ) . Prefix ( "containers" ) ... )
2014-08-26 14:25:17 -04:00
allErrs = append ( allErrs , validateRestartPolicy ( & manifest . RestartPolicy ) . Prefix ( "restartPolicy" ) ... )
2014-11-12 00:21:40 -05:00
allErrs = append ( allErrs , validateDNSPolicy ( & manifest . DNSPolicy ) . Prefix ( "dnsPolicy" ) ... )
2014-08-16 16:48:48 -04:00
return allErrs
2014-07-01 16:01:39 -04:00
}
2014-07-10 15:45:01 -04:00
2014-10-24 12:43:14 -04:00
func validateRestartPolicy ( restartPolicy * api . RestartPolicy ) errs . ValidationErrorList {
2014-08-26 14:25:17 -04:00
numPolicies := 0
2014-10-24 12:43:14 -04:00
allErrors := errs . ValidationErrorList { }
2014-08-26 14:25:17 -04:00
if restartPolicy . Always != nil {
numPolicies ++
}
if restartPolicy . OnFailure != nil {
numPolicies ++
}
if restartPolicy . Never != nil {
numPolicies ++
}
2015-01-26 12:52:50 -05:00
if numPolicies != 1 {
2014-11-20 17:24:10 -05:00
allErrors = append ( allErrors , errs . NewFieldInvalid ( "" , restartPolicy , "only 1 policy is allowed" ) )
2014-08-26 14:25:17 -04:00
}
return allErrors
}
2014-07-22 14:45:12 -04:00
2014-11-12 00:21:40 -05:00
func validateDNSPolicy ( dnsPolicy * api . DNSPolicy ) errs . ValidationErrorList {
allErrors := errs . ValidationErrorList { }
switch * dnsPolicy {
case api . DNSClusterFirst , api . DNSDefault :
break
2015-01-26 12:52:50 -05:00
case "" :
allErrors = append ( allErrors , errs . NewFieldRequired ( "" , * dnsPolicy ) )
2014-11-12 00:21:40 -05:00
default :
allErrors = append ( allErrors , errs . NewFieldNotSupported ( "" , dnsPolicy ) )
}
return allErrors
}
2014-09-02 06:00:28 -04:00
// ValidatePod tests if required fields in the pod are set.
2014-10-24 12:43:14 -04:00
func ValidatePod ( pod * api . Pod ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-01-27 23:50:01 -05:00
allErrs = append ( allErrs , ValidateObjectMeta ( & pod . ObjectMeta , true , ValidatePodName ) . Prefix ( "metadata" ) ... )
2014-11-13 10:52:13 -05:00
allErrs = append ( allErrs , ValidatePodSpec ( & pod . Spec ) . Prefix ( "spec" ) ... )
2015-01-27 18:55:54 -05:00
2014-10-23 16:14:13 -04:00
return allErrs
}
2014-11-06 21:08:46 -05:00
// ValidatePodSpec tests that the specified PodSpec has valid data.
// This includes checking formatting and uniqueness. It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility
// tricks.
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 00:21:40 -05:00
allErrs = append ( allErrs , validateDNSPolicy ( & spec . DNSPolicy ) . Prefix ( "dnsPolicy" ) ... )
2015-01-19 22:32:39 -05:00
allErrs = append ( allErrs , ValidateLabels ( spec . NodeSelector , "nodeSelector" ) ... )
2014-11-06 21:08:46 -05:00
return allErrs
}
2014-10-09 23:30:34 -04:00
// ValidatePodUpdate tests to see if the update is legal
2014-10-24 12:43:14 -04:00
func ValidatePodUpdate ( newPod , oldPod * api . Pod ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-10-09 23:30:34 -04:00
2015-01-27 18:55:54 -05:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldPod . ObjectMeta , & newPod . ObjectMeta ) . Prefix ( "metadata" ) ... )
2014-10-14 15:28:45 -04:00
2014-11-13 10:52:13 -05:00
if len ( newPod . Spec . Containers ) != len ( oldPod . Spec . Containers ) {
2014-11-20 17:24:10 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.containers" , newPod . Spec . Containers , "may not add or remove containers" ) )
2014-10-09 23:30:34 -04: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 10:52:13 -05:00
for ix , container := range pod . Spec . Containers {
container . Image = oldPod . Spec . Containers [ ix ] . Image
2014-10-09 23:30:34 -04:00
newContainers = append ( newContainers , container )
}
2014-11-13 10:52:13 -05:00
pod . Spec . Containers = newContainers
2015-01-05 16:38:39 -05:00
if ! api . Semantic . DeepEqual ( pod . Spec , oldPod . Spec ) {
2014-11-20 17:24:10 -05:00
// TODO: a better error would include all immutable fields explicitly.
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.containers" , newPod . Spec . Containers , "some fields are immutable" ) )
2014-10-09 23:30:34 -04:00
}
2015-01-27 18:55:54 -05:00
2014-10-09 23:30:34 -04:00
return allErrs
}
2014-12-17 07:52:11 -05:00
var supportedSessionAffinityType = util . NewStringSet ( string ( api . AffinityTypeClientIP ) , string ( api . AffinityTypeNone ) )
2014-07-15 09:53:39 -04:00
// ValidateService tests if required fields in the service are set.
2015-01-27 18:55:54 -05:00
func ValidateService ( service * api . Service ) errs . ValidationErrorList {
2014-10-24 12:43:14 -04:00
allErrs := errs . ValidationErrorList { }
2015-01-27 23:50:01 -05:00
allErrs = append ( allErrs , ValidateObjectMeta ( & service . ObjectMeta , true , ValidateServiceName ) . Prefix ( "metadata" ) ... )
2015-01-27 18:55:54 -05:00
2014-10-30 09:29:11 -04:00
if ! util . IsValidPortNum ( service . Spec . Port ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.port" , service . Spec . Port , portRangeErrorMsg ) )
2014-08-22 17:44:21 -04:00
}
2014-10-30 09:29:11 -04:00
if len ( service . Spec . Protocol ) == 0 {
2015-01-26 12:52:50 -05:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "spec.protocol" , service . Spec . Protocol ) )
2014-10-30 09:29:11 -04:00
} else if ! supportedPortProtocols . Has ( strings . ToUpper ( string ( service . Spec . Protocol ) ) ) {
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "spec.protocol" , service . Spec . Protocol ) )
2014-09-10 12:53:40 -04:00
}
2014-11-18 12:49:00 -05:00
if service . Spec . Selector != nil {
2015-01-19 22:32:39 -05:00
allErrs = append ( allErrs , ValidateLabels ( service . Spec . Selector , "spec.selector" ) ... )
2014-07-10 15:45:01 -04:00
}
2014-11-18 12:49:00 -05:00
2014-12-29 17:39:09 -05:00
if service . Spec . SessionAffinity == "" {
2015-01-26 12:52:50 -05:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "spec.sessionAffinity" , service . Spec . SessionAffinity ) )
2014-12-29 17:39:09 -05:00
} else if ! supportedSessionAffinityType . Has ( string ( service . Spec . SessionAffinity ) ) {
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "spec.sessionAffinity" , service . Spec . SessionAffinity ) )
2014-12-17 07:52:11 -05:00
}
2014-08-16 16:48:48 -04:00
return allErrs
2014-07-10 15:45:01 -04:00
}
2014-07-25 12:15:17 -04:00
2015-01-27 18:55:54 -05: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
// PortalIP can only be set, not unset.
if oldService . Spec . PortalIP != "" && service . Spec . PortalIP != oldService . Spec . PortalIP {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.portalIP" , service . Spec . PortalIP , "field is immutable" ) )
}
return allErrs
}
2014-07-25 12:15:17 -04:00
// ValidateReplicationController tests if required fields in the replication controller are set.
2014-10-24 12:43:14 -04:00
func ValidateReplicationController ( controller * api . ReplicationController ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2015-01-27 23:50:01 -05:00
allErrs = append ( allErrs , ValidateObjectMeta ( & controller . ObjectMeta , true , ValidateReplicationControllerName ) . Prefix ( "metadata" ) ... )
2014-11-06 21:08:46 -05:00
allErrs = append ( allErrs , ValidateReplicationControllerSpec ( & controller . Spec ) . Prefix ( "spec" ) ... )
2015-01-27 18:55:54 -05: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 12:54:38 -04:00
return allErrs
}
2014-11-06 21:08:46 -05:00
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
func ValidateReplicationControllerSpec ( spec * api . ReplicationControllerSpec ) errs . ValidationErrorList {
2014-10-24 12:43:14 -04:00
allErrs := errs . ValidationErrorList { }
2014-11-06 21:08:46 -05:00
selector := labels . Set ( spec . Selector ) . AsSelector ( )
if selector . Empty ( ) {
allErrs = append ( allErrs , errs . NewFieldRequired ( "selector" , spec . Selector ) )
2014-07-25 12:15:17 -04:00
}
2014-11-06 21:08:46 -05:00
if spec . Replicas < 0 {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "replicas" , spec . Replicas , isNegativeErrorMsg ) )
2014-08-21 20:02:39 -04:00
}
2014-11-06 21:08:46 -05:00
if spec . Template == nil {
allErrs = append ( allErrs , errs . NewFieldRequired ( "template" , spec . Template ) )
} else {
labels := labels . Set ( spec . Template . Labels )
if ! selector . Matches ( labels ) {
2014-11-20 17:24:10 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "template.labels" , spec . Template . Labels , "selector does not match template" ) )
2014-11-06 21:08:46 -05:00
}
2015-02-04 23:55:16 -05:00
allErrs = append ( allErrs , ValidatePodTemplateSpec ( spec . Template , spec . Replicas ) . Prefix ( "template" ) ... )
2014-11-20 17:24:10 -05:00
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
if spec . Template . Spec . RestartPolicy . Always == nil {
// TODO: should probably be Unsupported
// TODO: api.RestartPolicy should have a String() method for nicer printing
allErrs = append ( allErrs , errs . NewFieldInvalid ( "template.restartPolicy" , spec . Template . Spec . RestartPolicy , "must be Always" ) )
2014-11-17 23:08:23 -05:00
}
2014-08-04 15:02:51 -04:00
}
2014-08-05 13:58:43 -04:00
return allErrs
}
2014-11-06 21:08:46 -05:00
// ValidatePodTemplateSpec validates the spec of a pod template
2015-02-04 23:55:16 -05:00
func ValidatePodTemplateSpec ( spec * api . PodTemplateSpec , replicas int ) errs . ValidationErrorList {
2014-11-06 21:08:46 -05:00
allErrs := errs . ValidationErrorList { }
2015-01-19 22:32:39 -05:00
allErrs = append ( allErrs , ValidateLabels ( spec . Labels , "labels" ) ... )
2015-02-01 19:03:04 -05:00
allErrs = append ( allErrs , ValidateAnnotations ( spec . Annotations , "annotations" ) ... )
2014-11-06 21:08:46 -05:00
allErrs = append ( allErrs , ValidatePodSpec ( & spec . Spec ) . Prefix ( "spec" ) ... )
2015-02-04 23:55:16 -05:00
if replicas > 1 {
allErrs = append ( allErrs , ValidateReadOnlyPersistentDisks ( spec . Spec . Volumes ) . Prefix ( "spec.volumes" ) ... )
}
2014-11-06 21:08:46 -05:00
return allErrs
}
2014-10-24 12:43:14 -04:00
func ValidateReadOnlyPersistentDisks ( volumes [ ] api . Volume ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-08-05 13:58:43 -04:00
for _ , vol := range volumes {
if vol . Source . GCEPersistentDisk != nil {
if vol . Source . GCEPersistentDisk . ReadOnly == false {
2015-02-04 23:55:16 -05: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 13:58:43 -04:00
}
}
}
2014-08-16 16:48:48 -04:00
return allErrs
2014-07-25 12:15:17 -04:00
}
2014-10-08 15:56:02 -04:00
// ValidateBoundPod tests if required fields on a bound pod are set.
2015-01-27 18:55:54 -05:00
// TODO: to be removed.
2015-01-06 00:42:28 -05:00
func ValidateBoundPod ( pod * api . BoundPod ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if len ( pod . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" , pod . Name ) )
2015-01-27 18:55:54 -05:00
} else {
2015-01-27 18:56:38 -05:00
if ok , qualifier := nameIsDNSSubdomain ( pod . Name , false ) ; ! ok {
2015-01-27 18:55:54 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , pod . Name , qualifier ) )
}
2014-10-08 15:56:02 -04:00
}
2015-01-09 23:30:50 -05:00
if len ( pod . Namespace ) == 0 {
2015-01-06 00:42:28 -05:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , pod . Namespace ) )
} else if ! util . IsDNSSubdomain ( pod . Namespace ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , pod . Namespace , dnsSubdomainErrorMsg ) )
2014-10-08 15:56:02 -04:00
}
2015-01-06 00:42:28 -05:00
allErrs = append ( allErrs , ValidatePodSpec ( & pod . Spec ) . Prefix ( "spec" ) ... )
return allErrs
2014-10-08 15:56:02 -04:00
}
2014-11-12 12:38:15 -05:00
2015-01-27 18:55:54 -05:00
// ValidateMinion tests if required fields in the node are set.
func ValidateMinion ( node * api . Node ) errs . ValidationErrorList {
2014-11-12 12:38:15 -05:00
allErrs := errs . ValidationErrorList { }
2015-01-27 23:50:01 -05:00
allErrs = append ( allErrs , ValidateObjectMeta ( & node . ObjectMeta , false , ValidateNodeName ) . Prefix ( "metadata" ) ... )
2014-11-12 12:38:15 -05:00
return allErrs
}
2014-11-17 13:22:27 -05:00
// ValidateMinionUpdate tests to make sure a minion update can be applied. Modifies oldMinion.
2014-12-07 22:44:27 -05:00
func ValidateMinionUpdate ( oldMinion * api . Node , minion * api . Node ) errs . ValidationErrorList {
2014-11-17 13:22:27 -05:00
allErrs := errs . ValidationErrorList { }
2015-01-27 18:55:54 -05:00
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldMinion . ObjectMeta , & minion . ObjectMeta ) . Prefix ( "metadata" ) ... )
2014-12-12 00:39:56 -05:00
2015-01-16 17:28:20 -05: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 00:39:56 -05:00
2015-01-27 18:55:54 -05:00
// TODO: move reset function to its own location
// Ignore metadata changes now that they have been tested
oldMinion . ObjectMeta = minion . ObjectMeta
// Allow users to update capacity
2014-12-12 00:39:56 -05:00
oldMinion . Spec . Capacity = minion . Spec . Capacity
2014-12-22 14:04:57 -05:00
// Clear status
oldMinion . Status = minion . Status
2014-12-12 00:39:56 -05:00
2015-02-04 19:36:27 -05:00
// TODO: Add a 'real' ValidationError type for this error and provide print actual diffs.
2015-01-05 16:38:39 -05:00
if ! api . Semantic . DeepEqual ( oldMinion , minion ) {
2014-12-12 00:39:56 -05: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 13:22:27 -05:00
}
2015-01-27 18:55:54 -05:00
// TODO: validate Spec.Capacity
2014-11-17 13:22:27 -05:00
return allErrs
}
2015-01-16 19:34:47 -05:00
2015-01-24 23:19:36 -05:00
// Validate compute resource typename.
2015-01-16 19:34:47 -05:00
// Refer to docs/resources.md for more details.
2015-02-04 19:36:27 -05: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-16 19:34:47 -05:00
}
2015-02-04 19:36:27 -05:00
if len ( strings . Split ( value , "/" ) ) == 1 {
if ! api . IsStandardResourceName ( value ) {
return append ( allErrs , errs . NewFieldInvalid ( field , value , "is neither a standard resource type nor is fully qualified" ) )
2015-01-16 19:34:47 -05:00
}
}
return errs . ValidationErrorList { }
}
2015-01-22 16:52:40 -05:00
// ValidateLimitRange tests if required fields in the LimitRange are set.
func ValidateLimitRange ( limitRange * api . LimitRange ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if len ( limitRange . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" , limitRange . Name ) )
} else if ! util . IsDNSSubdomain ( limitRange . Name ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , limitRange . Name , dnsSubdomainErrorMsg ) )
2015-01-22 16:52:40 -05:00
}
if len ( limitRange . Namespace ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , limitRange . Namespace ) )
} else if ! util . IsDNSSubdomain ( limitRange . Namespace ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , limitRange . Namespace , dnsSubdomainErrorMsg ) )
2015-01-22 16:52:40 -05: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 10:47:07 -05:00
for k := range limit . Max {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , fmt . Sprintf ( "spec.limits[%d].max[%s]" , i , k ) ) ... )
2015-01-22 16:52:40 -05:00
}
2015-01-24 10:47:07 -05:00
for k := range limit . Min {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , fmt . Sprintf ( "spec.limits[%d].min[%s]" , i , k ) ) ... )
2015-01-24 23:19:36 -05:00
}
}
return allErrs
}
func validateBasicResource ( quantity resource . Quantity ) errs . ValidationErrorList {
if quantity . Value ( ) < 0 {
return errs . ValidationErrorList { fmt . Errorf ( "%v is not a valid resource quantity" , quantity . Value ( ) ) }
}
return errs . ValidationErrorList { }
}
// Validates resource requirement spec.
func validateResourceRequirements ( container * api . Container ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
for resourceName , quantity := range container . Resources . Limits {
// Validate resource name.
2015-02-04 19:36:27 -05:00
errs := validateResourceName ( resourceName . String ( ) , fmt . Sprintf ( "resources.limits[%s]" , resourceName ) )
2015-01-24 23:19:36 -05:00
if api . IsStandardResourceName ( resourceName . String ( ) ) {
errs = append ( errs , validateBasicResource ( quantity ) . Prefix ( fmt . Sprintf ( "Resource %s: " , resourceName ) ) ... )
2015-01-22 16:52:40 -05:00
}
2015-01-24 23:19:36 -05:00
allErrs = append ( allErrs , errs ... )
2015-01-22 16:52:40 -05:00
}
2015-01-24 23:19:36 -05:00
2015-01-22 16:52:40 -05:00
return allErrs
}
2015-01-23 12:38:30 -05:00
// ValidateResourceQuota tests if required fields in the ResourceQuota are set.
func ValidateResourceQuota ( resourceQuota * api . ResourceQuota ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if len ( resourceQuota . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" , resourceQuota . Name ) )
} else if ! util . IsDNSSubdomain ( resourceQuota . Name ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , resourceQuota . Name , dnsSubdomainErrorMsg ) )
2015-01-23 12:38:30 -05:00
}
if len ( resourceQuota . Namespace ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , resourceQuota . Namespace ) )
} else if ! util . IsDNSSubdomain ( resourceQuota . Namespace ) {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , resourceQuota . Namespace , dnsSubdomainErrorMsg ) )
2015-01-23 12:38:30 -05:00
}
for k := range resourceQuota . Spec . Hard {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( resourceQuota . TypeMeta . Kind ) ) ... )
2015-01-23 12:38:30 -05:00
}
for k := range resourceQuota . Status . Hard {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( resourceQuota . TypeMeta . Kind ) ) ... )
2015-01-23 12:38:30 -05:00
}
for k := range resourceQuota . Status . Used {
2015-02-04 19:36:27 -05:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) , string ( resourceQuota . TypeMeta . Kind ) ) ... )
2015-01-23 12:38:30 -05:00
}
return allErrs
}
2015-01-19 16:50:00 -05: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" ) ... )
return allErrs
}
// ValidateNamespaceUpdate tests to make sure a mamespace update can be applied. Modifies oldNamespace.
func ValidateNamespaceUpdate ( oldNamespace * api . Namespace , namespace * api . Namespace ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateObjectMetaUpdate ( & oldNamespace . ObjectMeta , & namespace . ObjectMeta ) . Prefix ( "metadata" ) ... )
// TODO: move reset function to its own location
// Ignore metadata changes now that they have been tested
oldNamespace . ObjectMeta = namespace . ObjectMeta
// TODO: Add a 'real' ValidationError type for this error and provide print actual diffs.
if ! api . Semantic . DeepEqual ( oldNamespace , namespace ) {
glog . V ( 4 ) . Infof ( "Update failed validation %#v vs %#v" , oldNamespace , namespace )
allErrs = append ( allErrs , fmt . Errorf ( "update contains more than labels or annotation changes" ) )
}
return allErrs
}