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"
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"
2014-08-14 20:46:22 +00:00
errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
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"
2014-12-12 05:39:56 +00:00
"github.com/golang/glog"
2014-07-01 20:01:39 +00:00
)
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 { }
for k := range labels {
if ! util . IsQualifiedName ( k ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( field , k , "" ) )
}
}
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 ) ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( field , k , "" ) )
}
}
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-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-01-27 23:55:54 +00:00
if util . IsDNSSubdomain ( name ) {
return true , ""
}
return false , "name must be lowercase letters and numbers, with inline dashes or periods"
}
// 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 , ""
}
return false , "name must be lowercase letters, numbers, and dashes"
}
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 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" , meta . Name ) )
} 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 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , meta . Namespace ) )
} else if ! util . IsDNSSubdomain ( meta . Namespace ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , meta . Namespace , "" ) )
}
} 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
}
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-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-01-21 01:40:43 +00:00
el := validateSource ( & vol . Source ) . Prefix ( "source" )
2014-08-20 02:57:48 +00:00
if len ( vol . Name ) == 0 {
2014-09-03 21:16:00 +00:00
el = append ( el , errs . NewFieldRequired ( "name" , vol . Name ) )
2014-08-20 02:57:48 +00:00
} else if ! util . IsDNSLabel ( vol . Name ) {
2014-11-20 22:24:10 +00:00
el = append ( el , errs . NewFieldInvalid ( "name" , vol . Name , "" ) )
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-01-20 23:56:44 +00:00
allErrs = append ( allErrs , validateHostPath ( 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-01-20 23:56:44 +00:00
allErrs = append ( allErrs , validateGitRepo ( 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-01-20 23:56:44 +00:00
allErrs = append ( allErrs , validateGCEPersistentDisk ( source . GCEPersistentDisk ) . Prefix ( "persistentDisk" ) ... )
2014-08-05 17:58:43 +00:00
}
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-01-20 23:56:44 +00:00
func validateHostPath ( hostDir * api . HostPath ) 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-01-20 23:56:44 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "path" , hostDir . Path ) )
2014-07-15 01:39:30 +00:00
}
return allErrs
}
2014-11-24 04:03:11 +00:00
func validateGitRepo ( gitRepo * api . GitRepo ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if gitRepo . Repository == "" {
2015-01-20 23:56:44 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "repository" , gitRepo . Repository ) )
2014-11-24 04:03:11 +00:00
}
return allErrs
}
2014-07-08 04:32:56 +00:00
2014-10-24 16:43:14 +00:00
func validateGCEPersistentDisk ( PD * api . GCEPersistentDisk ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
2014-08-05 17:58:43 +00:00
if PD . PDName == "" {
2015-01-20 23:56:44 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "pdName" , PD . PDName ) )
2014-08-05 17:58:43 +00:00
}
if PD . FSType == "" {
2015-01-20 23:56:44 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "fsType" , PD . FSType ) )
2014-08-05 17:58:43 +00:00
}
if PD . Partition < 0 || PD . Partition > 255 {
2015-01-20 23:56:44 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "partition" , PD . Partition , "" ) )
2014-08-05 17:58:43 +00:00
}
return allErrs
}
2014-11-24 04:03:11 +00:00
var supportedPortProtocols = util . NewStringSet ( string ( api . ProtocolTCP ) , string ( api . ProtocolUDP ) )
2014-10-24 16:43:14 +00:00
func validatePorts ( ports [ ] api . Port ) errs . ValidationErrorList {
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 {
if len ( port . Name ) > 63 || ! util . IsDNSLabel ( port . Name ) {
2014-11-20 22:24:10 +00:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "name" , port . Name , "" ) )
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 {
2014-09-03 21:16:00 +00:00
pErrs = append ( pErrs , errs . NewFieldRequired ( "containerPort" , port . ContainerPort ) )
2014-08-20 02:57:48 +00:00
} else if ! util . IsValidPortNum ( port . ContainerPort ) {
2014-11-20 22:24:10 +00:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "containerPort" , port . ContainerPort , "" ) )
2014-07-08 04:32:56 +00:00
}
2014-08-19 22:18:49 +00:00
if port . HostPort != 0 && ! util . IsValidPortNum ( port . HostPort ) {
2014-11-20 22:24:10 +00:00
pErrs = append ( pErrs , errs . NewFieldInvalid ( "hostPort" , port . HostPort , "" ) )
2014-07-08 04:32:56 +00:00
}
if len ( port . Protocol ) == 0 {
2015-01-26 17:52:50 +00:00
pErrs = append ( pErrs , errs . NewFieldRequired ( "protocol" , port . 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 {
2014-09-03 21:16:00 +00:00
vErrs = append ( vErrs , errs . NewFieldRequired ( "name" , ev . Name ) )
2014-07-01 22:56:30 +00:00
}
if ! util . IsCIdentifier ( ev . Name ) {
2014-11-20 22:24:10 +00:00
vErrs = append ( vErrs , errs . NewFieldInvalid ( "name" , ev . Name , "" ) )
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 {
2014-09-03 21:16:00 +00:00
mErrs = append ( mErrs , errs . NewFieldRequired ( "name" , mnt . 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 {
2014-09-03 21:16:00 +00:00
mErrs = append ( mErrs , errs . NewFieldRequired ( "mountPath" , mnt . 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
}
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.
2014-10-24 16:43:14 +00:00
func AccumulateUniquePorts ( containers [ ] api . Container , accumulator map [ int ] bool , extract func ( * api . Port ) int ) errs . ValidationErrorList {
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 { }
2014-08-30 01:20:27 +00:00
return AccumulateUniquePorts ( containers , allPorts , func ( p * api . Port ) 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 {
allErrors = append ( allErrors , errs . NewFieldRequired ( "command" , exec . Command ) )
}
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 {
allErrors = append ( allErrors , errs . NewFieldRequired ( "path" , http . Path ) )
}
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
}
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 "" :
allErrors = append ( allErrors , errs . NewFieldRequired ( "" , ctr . ImagePullPolicy ) )
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
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 {
2014-09-03 21:16:00 +00:00
cErrs = append ( cErrs , errs . NewFieldRequired ( "name" , ctr . Name ) )
2014-08-20 02:57:48 +00:00
} else if ! util . IsDNSLabel ( ctr . Name ) {
2014-11-20 22:24:10 +00:00
cErrs = append ( cErrs , errs . NewFieldInvalid ( "name" , ctr . Name , "" ) )
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 {
2014-09-03 21:16:00 +00:00
cErrs = append ( cErrs , errs . NewFieldRequired ( "image" , ctr . 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" ) ... )
}
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" ) ... )
cErrs = append ( cErrs , validateResourceRequirements ( & ctr ) . 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 {
2014-09-03 21:16:00 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "version" , manifest . 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 {
2014-08-26 18:25:17 +00:00
numPolicies := 0
2014-10-24 16:43:14 +00:00
allErrors := errs . ValidationErrorList { }
2014-08-26 18:25:17 +00:00
if restartPolicy . Always != nil {
numPolicies ++
}
if restartPolicy . OnFailure != nil {
numPolicies ++
}
if restartPolicy . Never != nil {
numPolicies ++
}
2015-01-26 17:52:50 +00:00
if numPolicies != 1 {
2014-11-20 22:24:10 +00:00
allErrors = append ( allErrors , errs . NewFieldInvalid ( "" , restartPolicy , "only 1 policy is allowed" ) )
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 "" :
allErrors = append ( allErrors , errs . NewFieldRequired ( "" , * dnsPolicy ) )
2014-11-12 05:21:40 +00:00
default :
allErrors = append ( allErrors , errs . NewFieldNotSupported ( "" , dnsPolicy ) )
}
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" ) ... )
2014-11-07 02:08:46 +00:00
return allErrs
}
2014-10-10 03:30:34 +00:00
// ValidatePodUpdate tests to see if the update is legal
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 ) {
2014-11-20 22:24:10 +00: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-10 03:30:34 +00:00
}
2015-01-27 23:55:54 +00:00
2014-10-10 03:30:34 +00:00
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
2014-10-30 13:29:11 +00:00
if ! util . IsValidPortNum ( service . Spec . Port ) {
2014-11-20 22:24:10 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec.port" , service . Spec . Port , "" ) )
2014-08-22 21:44:21 +00:00
}
2014-10-30 13:29:11 +00:00
if len ( service . Spec . Protocol ) == 0 {
2015-01-26 17:52:50 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "spec.protocol" , service . Spec . Protocol ) )
2014-10-30 13:29:11 +00:00
} else if ! supportedPortProtocols . Has ( strings . ToUpper ( string ( service . Spec . Protocol ) ) ) {
allErrs = append ( allErrs , errs . NewFieldNotSupported ( "spec.protocol" , service . Spec . Protocol ) )
2014-09-10 16:53:40 +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-01-26 17:52:50 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "spec.sessionAffinity" , service . 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
}
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-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
// 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 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 ( ) {
allErrs = append ( allErrs , errs . NewFieldRequired ( "selector" , spec . Selector ) )
2014-07-25 16:15:17 +00:00
}
2014-11-07 02:08:46 +00:00
if spec . Replicas < 0 {
2014-11-20 22:24:10 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "replicas" , spec . Replicas , "" ) )
2014-08-22 00:02:39 +00:00
}
2014-11-07 02:08:46 +00: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 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().
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-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 {
if vol . Source . GCEPersistentDisk != nil {
if vol . Source . 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
}
}
}
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
// ValidateBoundPod tests if required fields on a bound pod are set.
2015-01-27 23:55:54 +00:00
// TODO: to be removed.
2015-01-06 05:42:28 +00: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 23:55:54 +00:00
} else {
2015-01-27 23:56:38 +00:00
if ok , qualifier := nameIsDNSSubdomain ( pod . Name , false ) ; ! ok {
2015-01-27 23:55:54 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , pod . Name , qualifier ) )
}
2014-10-08 19:56:02 +00:00
}
2015-01-10 04:30:50 +00:00
if len ( pod . Namespace ) == 0 {
2015-01-06 05:42:28 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , pod . Namespace ) )
} else if ! util . IsDNSSubdomain ( pod . Namespace ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , pod . Namespace , "" ) )
2014-10-08 19:56:02 +00:00
}
2015-01-06 05:42:28 +00:00
allErrs = append ( allErrs , ValidatePodSpec ( & pod . Spec ) . Prefix ( "spec" ) ... )
return allErrs
2014-10-08 19:56:02 +00:00
}
2014-11-12 17:38:15 +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" ) ... )
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
2014-12-12 05:39:56 +00:00
oldMinion . Spec . Capacity = minion . Spec . Capacity
2014-12-22 19:04:57 +00:00
// Clear status
oldMinion . Status = minion . Status
2014-12-12 05:39:56 +00:00
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-01-25 04:19:36 +00:00
func validateResourceName ( str string ) errs . ValidationErrorList {
2015-01-17 00:34:47 +00:00
if ! util . IsQualifiedName ( str ) {
return errs . ValidationErrorList { fmt . Errorf ( "invalid compute resource typename format %q" , str ) }
}
2015-01-25 04:19:36 +00:00
if len ( strings . Split ( str , "/" ) ) == 1 {
if ! api . IsStandardResourceName ( str ) {
2015-01-17 00:34:47 +00:00
return errs . ValidationErrorList { fmt . Errorf ( "invalid compute resource typename. %q is neither a standard resource type nor is fully qualified" , str ) }
}
}
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 { }
if len ( limitRange . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" , limitRange . Name ) )
} else if ! util . IsDNSSubdomain ( limitRange . Name ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , limitRange . Name , "" ) )
}
if len ( limitRange . Namespace ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , limitRange . Namespace ) )
} else if ! util . IsDNSSubdomain ( limitRange . Namespace ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , limitRange . Namespace , "" ) )
}
// 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-01-25 04:19:36 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) ) ... )
2015-01-22 21:52:40 +00:00
}
2015-01-24 15:47:07 +00:00
for k := range limit . Min {
2015-01-25 04:19:36 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) ) ... )
}
}
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.
errs := validateResourceName ( resourceName . String ( ) )
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-01-25 04:19:36 +00:00
2015-01-22 21:52:40 +00:00
return allErrs
}
2015-01-23 17:38:30 +00:00
// ValidateResourceQuota tests if required fields in the ResourceQuota are set.
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 ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , resourceQuota . Name , "" ) )
}
if len ( resourceQuota . Namespace ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "namespace" , resourceQuota . Namespace ) )
} else if ! util . IsDNSSubdomain ( resourceQuota . Namespace ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , resourceQuota . Namespace , "" ) )
}
for k := range resourceQuota . Spec . Hard {
2015-01-25 04:19:36 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) ) ... )
2015-01-23 17:38:30 +00:00
}
for k := range resourceQuota . Status . Hard {
2015-01-25 04:19:36 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) ) ... )
2015-01-23 17:38:30 +00:00
}
for k := range resourceQuota . Status . Used {
2015-01-25 04:19:36 +00:00
allErrs = append ( allErrs , validateResourceName ( string ( k ) ) ... )
2015-01-23 17:38:30 +00:00
}
return allErrs
}