github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package validation 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "net" 23 "os" 24 "path" 25 "reflect" 26 "regexp" 27 "strings" 28 29 "k8s.io/kubernetes/pkg/api" 30 "k8s.io/kubernetes/pkg/api/resource" 31 "k8s.io/kubernetes/pkg/capabilities" 32 "k8s.io/kubernetes/pkg/labels" 33 "k8s.io/kubernetes/pkg/util/intstr" 34 "k8s.io/kubernetes/pkg/util/sets" 35 "k8s.io/kubernetes/pkg/util/validation" 36 37 "github.com/golang/glog" 38 ) 39 40 // TODO: delete this global variable when we enable the validation of common 41 // fields by default. 42 var RepairMalformedUpdates bool = true 43 44 const isNegativeErrorMsg string = `must be non-negative` 45 const fieldImmutableErrorMsg string = `field is immutable` 46 const cIdentifierErrorMsg string = `must be a C identifier (matching regex ` + validation.CIdentifierFmt + `): e.g. "my_name" or "MyName"` 47 const isNotIntegerErrorMsg string = `must be an integer` 48 49 func IntervalErrorMsg(lo, hi int) string { 50 return fmt.Sprintf(`must be greater than %d and less than %d`, lo, hi) 51 } 52 53 var labelValueErrorMsg string = fmt.Sprintf(`must have at most %d characters, matching regex %s: e.g. "MyValue" or ""`, validation.LabelValueMaxLength, validation.LabelValueFmt) 54 var qualifiedNameErrorMsg string = fmt.Sprintf(`must be a qualified name (at most %d characters, matching regex %s), with an optional DNS subdomain prefix (at most %d characters, matching regex %s) and slash (/): e.g. "MyName" or "example.com/MyName"`, validation.QualifiedNameMaxLength, validation.QualifiedNameFmt, validation.DNS1123SubdomainMaxLength, validation.DNS1123SubdomainFmt) 55 var DNSSubdomainErrorMsg string = fmt.Sprintf(`must be a DNS subdomain (at most %d characters, matching regex %s): e.g. "example.com"`, validation.DNS1123SubdomainMaxLength, validation.DNS1123SubdomainFmt) 56 var DNS1123LabelErrorMsg string = fmt.Sprintf(`must be a DNS label (at most %d characters, matching regex %s): e.g. "my-name"`, validation.DNS1123LabelMaxLength, validation.DNS1123LabelFmt) 57 var DNS952LabelErrorMsg string = fmt.Sprintf(`must be a DNS 952 label (at most %d characters, matching regex %s): e.g. "my-name"`, validation.DNS952LabelMaxLength, validation.DNS952LabelFmt) 58 var pdPartitionErrorMsg string = IntervalErrorMsg(0, 255) 59 var PortRangeErrorMsg string = IntervalErrorMsg(0, 65536) 60 var PortNameErrorMsg string = fmt.Sprintf(`must be an IANA_SVC_NAME (at most 15 characters, matching regex %s, it must contain at least one letter [a-z], and hyphens cannot be adjacent to other hyphens): e.g. "http"`, validation.IdentifierNoHyphensBeginEndFmt) 61 62 const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB 63 64 func ValidateLabelName(labelName, fieldName string) validation.ErrorList { 65 allErrs := validation.ErrorList{} 66 if !validation.IsQualifiedName(labelName) { 67 allErrs = append(allErrs, validation.NewInvalidError(fieldName, labelName, qualifiedNameErrorMsg)) 68 } 69 return allErrs 70 } 71 72 // ValidateLabels validates that a set of labels are correctly defined. 73 func ValidateLabels(labels map[string]string, field string) validation.ErrorList { 74 allErrs := validation.ErrorList{} 75 for k, v := range labels { 76 allErrs = append(allErrs, ValidateLabelName(k, field)...) 77 if !validation.IsValidLabelValue(v) { 78 allErrs = append(allErrs, validation.NewInvalidError(field, v, labelValueErrorMsg)) 79 } 80 } 81 return allErrs 82 } 83 84 // ValidateAnnotations validates that a set of annotations are correctly defined. 85 func ValidateAnnotations(annotations map[string]string, field string) validation.ErrorList { 86 allErrs := validation.ErrorList{} 87 var totalSize int64 88 for k, v := range annotations { 89 if !validation.IsQualifiedName(strings.ToLower(k)) { 90 allErrs = append(allErrs, validation.NewInvalidError(field, k, qualifiedNameErrorMsg)) 91 } 92 totalSize += (int64)(len(k)) + (int64)(len(v)) 93 } 94 if totalSize > (int64)(totalAnnotationSizeLimitB) { 95 allErrs = append(allErrs, validation.NewTooLongError(field, "", totalAnnotationSizeLimitB)) 96 } 97 return allErrs 98 } 99 100 // ValidateNameFunc validates that the provided name is valid for a given resource type. 101 // Not all resources have the same validation rules for names. Prefix is true if the 102 // name will have a value appended to it. 103 type ValidateNameFunc func(name string, prefix bool) (bool, string) 104 105 // maskTrailingDash replaces the final character of a string with a subdomain safe 106 // value if is a dash. 107 func maskTrailingDash(name string) string { 108 if strings.HasSuffix(name, "-") { 109 return name[:len(name)-2] + "a" 110 } 111 return name 112 } 113 114 // ValidatePodName can be used to check whether the given pod name is valid. 115 // Prefix indicates this name will be used as part of generation, in which case 116 // trailing dashes are allowed. 117 func ValidatePodName(name string, prefix bool) (bool, string) { 118 return NameIsDNSSubdomain(name, prefix) 119 } 120 121 // ValidateReplicationControllerName can be used to check whether the given replication 122 // controller name is valid. 123 // Prefix indicates this name will be used as part of generation, in which case 124 // trailing dashes are allowed. 125 func ValidateReplicationControllerName(name string, prefix bool) (bool, string) { 126 return NameIsDNSSubdomain(name, prefix) 127 } 128 129 // ValidateServiceName can be used to check whether the given service name is valid. 130 // Prefix indicates this name will be used as part of generation, in which case 131 // trailing dashes are allowed. 132 func ValidateServiceName(name string, prefix bool) (bool, string) { 133 return NameIsDNS952Label(name, prefix) 134 } 135 136 // ValidateNodeName can be used to check whether the given node name is valid. 137 // Prefix indicates this name will be used as part of generation, in which case 138 // trailing dashes are allowed. 139 func ValidateNodeName(name string, prefix bool) (bool, string) { 140 return NameIsDNSSubdomain(name, prefix) 141 } 142 143 // ValidateNamespaceName can be used to check whether the given namespace name is valid. 144 // Prefix indicates this name will be used as part of generation, in which case 145 // trailing dashes are allowed. 146 func ValidateNamespaceName(name string, prefix bool) (bool, string) { 147 return NameIsDNSLabel(name, prefix) 148 } 149 150 // ValidateLimitRangeName can be used to check whether the given limit range name is valid. 151 // Prefix indicates this name will be used as part of generation, in which case 152 // trailing dashes are allowed. 153 func ValidateLimitRangeName(name string, prefix bool) (bool, string) { 154 return NameIsDNSSubdomain(name, prefix) 155 } 156 157 // ValidateResourceQuotaName can be used to check whether the given 158 // resource quota name is valid. 159 // Prefix indicates this name will be used as part of generation, in which case 160 // trailing dashes are allowed. 161 func ValidateResourceQuotaName(name string, prefix bool) (bool, string) { 162 return NameIsDNSSubdomain(name, prefix) 163 } 164 165 // ValidateSecretName can be used to check whether the given secret name is valid. 166 // Prefix indicates this name will be used as part of generation, in which case 167 // trailing dashes are allowed. 168 func ValidateSecretName(name string, prefix bool) (bool, string) { 169 return NameIsDNSSubdomain(name, prefix) 170 } 171 172 // ValidateServiceAccountName can be used to check whether the given service account name is valid. 173 // Prefix indicates this name will be used as part of generation, in which case 174 // trailing dashes are allowed. 175 func ValidateServiceAccountName(name string, prefix bool) (bool, string) { 176 return NameIsDNSSubdomain(name, prefix) 177 } 178 179 // ValidateEndpointsName can be used to check whether the given endpoints name is valid. 180 // Prefix indicates this name will be used as part of generation, in which case 181 // trailing dashes are allowed. 182 func ValidateEndpointsName(name string, prefix bool) (bool, string) { 183 return NameIsDNSSubdomain(name, prefix) 184 } 185 186 // NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain. 187 func NameIsDNSSubdomain(name string, prefix bool) (bool, string) { 188 if prefix { 189 name = maskTrailingDash(name) 190 } 191 if validation.IsDNS1123Subdomain(name) { 192 return true, "" 193 } 194 return false, DNSSubdomainErrorMsg 195 } 196 197 // NameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label. 198 func NameIsDNSLabel(name string, prefix bool) (bool, string) { 199 if prefix { 200 name = maskTrailingDash(name) 201 } 202 if validation.IsDNS1123Label(name) { 203 return true, "" 204 } 205 return false, DNS1123LabelErrorMsg 206 } 207 208 // NameIsDNS952Label is a ValidateNameFunc for names that must be a DNS 952 label. 209 func NameIsDNS952Label(name string, prefix bool) (bool, string) { 210 if prefix { 211 name = maskTrailingDash(name) 212 } 213 if validation.IsDNS952Label(name) { 214 return true, "" 215 } 216 return false, DNS952LabelErrorMsg 217 } 218 219 // Validates that given value is not negative. 220 func ValidatePositiveField(value int64, fieldName string) validation.ErrorList { 221 allErrs := validation.ErrorList{} 222 if value < 0 { 223 allErrs = append(allErrs, validation.NewInvalidError(fieldName, value, isNegativeErrorMsg)) 224 } 225 return allErrs 226 } 227 228 // Validates that a Quantity is not negative 229 func ValidatePositiveQuantity(value resource.Quantity, fieldName string) validation.ErrorList { 230 allErrs := validation.ErrorList{} 231 if value.Cmp(resource.Quantity{}) < 0 { 232 allErrs = append(allErrs, validation.NewInvalidError(fieldName, value.String(), isNegativeErrorMsg)) 233 } 234 return allErrs 235 } 236 237 func ValidateImmutableField(new, old interface{}, fieldName string) validation.ErrorList { 238 allErrs := validation.ErrorList{} 239 if !api.Semantic.DeepEqual(old, new) { 240 allErrs = append(allErrs, validation.NewInvalidError(fieldName, new, fieldImmutableErrorMsg)) 241 } 242 return allErrs 243 } 244 245 // ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already 246 // been performed. 247 // It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before. 248 // TODO: Remove calls to this method scattered in validations of specific resources, e.g., ValidatePodUpdate. 249 func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc) validation.ErrorList { 250 allErrs := validation.ErrorList{} 251 252 if len(meta.GenerateName) != 0 { 253 if ok, qualifier := nameFn(meta.GenerateName, true); !ok { 254 allErrs = append(allErrs, validation.NewInvalidError("generateName", meta.GenerateName, qualifier)) 255 } 256 } 257 // If the generated name validates, but the calculated value does not, it's a problem with generation, and we 258 // report it here. This may confuse users, but indicates a programming bug and still must be validated. 259 // If there are multiple fields out of which one is required then add a or as a separator 260 if len(meta.Name) == 0 { 261 requiredErr := validation.NewRequiredError("name") 262 requiredErr.Detail = "name or generateName is required" 263 allErrs = append(allErrs, requiredErr) 264 } else { 265 if ok, qualifier := nameFn(meta.Name, false); !ok { 266 allErrs = append(allErrs, validation.NewInvalidError("name", meta.Name, qualifier)) 267 } 268 } 269 allErrs = append(allErrs, ValidatePositiveField(meta.Generation, "generation")...) 270 if requiresNamespace { 271 if len(meta.Namespace) == 0 { 272 allErrs = append(allErrs, validation.NewRequiredError("namespace")) 273 } else if ok, _ := ValidateNamespaceName(meta.Namespace, false); !ok { 274 allErrs = append(allErrs, validation.NewInvalidError("namespace", meta.Namespace, DNS1123LabelErrorMsg)) 275 } 276 } else { 277 if len(meta.Namespace) != 0 { 278 allErrs = append(allErrs, validation.NewInvalidError("namespace", meta.Namespace, "namespace is not allowed on this type")) 279 } 280 } 281 allErrs = append(allErrs, ValidateLabels(meta.Labels, "labels")...) 282 allErrs = append(allErrs, ValidateAnnotations(meta.Annotations, "annotations")...) 283 284 return allErrs 285 } 286 287 // ValidateObjectMetaUpdate validates an object's metadata when updated 288 func ValidateObjectMetaUpdate(new, old *api.ObjectMeta) validation.ErrorList { 289 allErrs := validation.ErrorList{} 290 291 if !RepairMalformedUpdates && new.UID != old.UID { 292 allErrs = append(allErrs, validation.NewInvalidError("uid", new.UID, "field is immutable")) 293 } 294 // in the event it is left empty, set it, to allow clients more flexibility 295 // TODO: remove the following code that repairs the update request when we retire the clients that modify the immutable fields. 296 // Please do not copy this pattern elsewhere; validation functions should not be modifying the objects they are passed! 297 if RepairMalformedUpdates { 298 if len(new.UID) == 0 { 299 new.UID = old.UID 300 } 301 // ignore changes to timestamp 302 if old.CreationTimestamp.IsZero() { 303 old.CreationTimestamp = new.CreationTimestamp 304 } else { 305 new.CreationTimestamp = old.CreationTimestamp 306 } 307 // an object can never remove a deletion timestamp or clear/change grace period seconds 308 if !old.DeletionTimestamp.IsZero() { 309 new.DeletionTimestamp = old.DeletionTimestamp 310 } 311 if old.DeletionGracePeriodSeconds != nil && new.DeletionGracePeriodSeconds == nil { 312 new.DeletionGracePeriodSeconds = old.DeletionGracePeriodSeconds 313 } 314 } 315 316 // TODO: needs to check if new==nil && old !=nil after the repair logic is removed. 317 if new.DeletionGracePeriodSeconds != nil && old.DeletionGracePeriodSeconds != nil && *new.DeletionGracePeriodSeconds != *old.DeletionGracePeriodSeconds { 318 allErrs = append(allErrs, validation.NewInvalidError("deletionGracePeriodSeconds", new.DeletionGracePeriodSeconds, "field is immutable; may only be changed via deletion")) 319 } 320 321 // Reject updates that don't specify a resource version 322 if new.ResourceVersion == "" { 323 allErrs = append(allErrs, validation.NewInvalidError("resourceVersion", new.ResourceVersion, "resourceVersion must be specified for an update")) 324 } 325 326 allErrs = append(allErrs, ValidateImmutableField(new.Name, old.Name, "name")...) 327 allErrs = append(allErrs, ValidateImmutableField(new.Namespace, old.Namespace, "namespace")...) 328 allErrs = append(allErrs, ValidateImmutableField(new.UID, old.UID, "uid")...) 329 allErrs = append(allErrs, ValidateImmutableField(new.CreationTimestamp, old.CreationTimestamp, "creationTimestamp")...) 330 331 allErrs = append(allErrs, ValidateLabels(new.Labels, "labels")...) 332 allErrs = append(allErrs, ValidateAnnotations(new.Annotations, "annotations")...) 333 334 return allErrs 335 } 336 337 func validateVolumes(volumes []api.Volume) (sets.String, validation.ErrorList) { 338 allErrs := validation.ErrorList{} 339 340 allNames := sets.String{} 341 for i, vol := range volumes { 342 el := validateSource(&vol.VolumeSource).Prefix("source") 343 if len(vol.Name) == 0 { 344 el = append(el, validation.NewRequiredError("name")) 345 } else if !validation.IsDNS1123Label(vol.Name) { 346 el = append(el, validation.NewInvalidError("name", vol.Name, DNS1123LabelErrorMsg)) 347 } else if allNames.Has(vol.Name) { 348 el = append(el, validation.NewDuplicateError("name", vol.Name)) 349 } 350 if len(el) == 0 { 351 allNames.Insert(vol.Name) 352 } else { 353 allErrs = append(allErrs, el.PrefixIndex(i)...) 354 } 355 } 356 return allNames, allErrs 357 } 358 359 func validateSource(source *api.VolumeSource) validation.ErrorList { 360 numVolumes := 0 361 allErrs := validation.ErrorList{} 362 if source.HostPath != nil { 363 numVolumes++ 364 allErrs = append(allErrs, validateHostPathVolumeSource(source.HostPath).Prefix("hostPath")...) 365 } 366 if source.EmptyDir != nil { 367 numVolumes++ 368 // EmptyDirs have nothing to validate 369 } 370 if source.GitRepo != nil { 371 numVolumes++ 372 allErrs = append(allErrs, validateGitRepoVolumeSource(source.GitRepo).Prefix("gitRepo")...) 373 } 374 if source.GCEPersistentDisk != nil { 375 numVolumes++ 376 allErrs = append(allErrs, validateGCEPersistentDiskVolumeSource(source.GCEPersistentDisk).Prefix("persistentDisk")...) 377 } 378 if source.AWSElasticBlockStore != nil { 379 numVolumes++ 380 allErrs = append(allErrs, validateAWSElasticBlockStoreVolumeSource(source.AWSElasticBlockStore).Prefix("awsElasticBlockStore")...) 381 } 382 if source.Secret != nil { 383 numVolumes++ 384 allErrs = append(allErrs, validateSecretVolumeSource(source.Secret).Prefix("secret")...) 385 } 386 if source.NFS != nil { 387 numVolumes++ 388 allErrs = append(allErrs, validateNFS(source.NFS).Prefix("nfs")...) 389 } 390 if source.ISCSI != nil { 391 numVolumes++ 392 allErrs = append(allErrs, validateISCSIVolumeSource(source.ISCSI).Prefix("iscsi")...) 393 } 394 if source.Glusterfs != nil { 395 numVolumes++ 396 allErrs = append(allErrs, validateGlusterfs(source.Glusterfs).Prefix("glusterfs")...) 397 } 398 if source.Flocker != nil { 399 numVolumes++ 400 allErrs = append(allErrs, validateFlocker(source.Flocker).Prefix("flocker")...) 401 } 402 if source.PersistentVolumeClaim != nil { 403 numVolumes++ 404 allErrs = append(allErrs, validatePersistentClaimVolumeSource(source.PersistentVolumeClaim).Prefix("persistentVolumeClaim")...) 405 } 406 if source.RBD != nil { 407 numVolumes++ 408 allErrs = append(allErrs, validateRBD(source.RBD).Prefix("rbd")...) 409 } 410 if source.Cinder != nil { 411 numVolumes++ 412 allErrs = append(allErrs, validateCinderVolumeSource(source.Cinder).Prefix("cinder")...) 413 } 414 if source.CephFS != nil { 415 numVolumes++ 416 allErrs = append(allErrs, validateCephFS(source.CephFS).Prefix("cephfs")...) 417 } 418 if source.DownwardAPI != nil { 419 numVolumes++ 420 allErrs = append(allErrs, validateDownwardAPIVolumeSource(source.DownwardAPI).Prefix("downwardApi")...) 421 } 422 if source.FC != nil { 423 numVolumes++ 424 allErrs = append(allErrs, validateFCVolumeSource(source.FC).Prefix("fc")...) 425 } 426 if numVolumes != 1 { 427 allErrs = append(allErrs, validation.NewInvalidError("", source, "exactly 1 volume type is required")) 428 } 429 430 return allErrs 431 } 432 433 func validateHostPathVolumeSource(hostPath *api.HostPathVolumeSource) validation.ErrorList { 434 allErrs := validation.ErrorList{} 435 if hostPath.Path == "" { 436 allErrs = append(allErrs, validation.NewRequiredError("path")) 437 } 438 return allErrs 439 } 440 441 func validateGitRepoVolumeSource(gitRepo *api.GitRepoVolumeSource) validation.ErrorList { 442 allErrs := validation.ErrorList{} 443 if gitRepo.Repository == "" { 444 allErrs = append(allErrs, validation.NewRequiredError("repository")) 445 } 446 return allErrs 447 } 448 449 func validateISCSIVolumeSource(iscsi *api.ISCSIVolumeSource) validation.ErrorList { 450 allErrs := validation.ErrorList{} 451 if iscsi.TargetPortal == "" { 452 allErrs = append(allErrs, validation.NewRequiredError("targetPortal")) 453 } 454 if iscsi.IQN == "" { 455 allErrs = append(allErrs, validation.NewRequiredError("iqn")) 456 } 457 if iscsi.FSType == "" { 458 allErrs = append(allErrs, validation.NewRequiredError("fsType")) 459 } 460 if iscsi.Lun < 0 || iscsi.Lun > 255 { 461 allErrs = append(allErrs, validation.NewInvalidError("lun", iscsi.Lun, "")) 462 } 463 return allErrs 464 } 465 466 func validateFCVolumeSource(fc *api.FCVolumeSource) validation.ErrorList { 467 allErrs := validation.ErrorList{} 468 if len(fc.TargetWWNs) < 1 { 469 allErrs = append(allErrs, validation.NewRequiredError("targetWWNs")) 470 } 471 if fc.FSType == "" { 472 allErrs = append(allErrs, validation.NewRequiredError("fsType")) 473 } 474 475 if fc.Lun == nil { 476 allErrs = append(allErrs, validation.NewRequiredError("lun")) 477 } else { 478 if *fc.Lun < 0 || *fc.Lun > 255 { 479 allErrs = append(allErrs, validation.NewInvalidError("lun", fc.Lun, "")) 480 } 481 } 482 return allErrs 483 } 484 485 func validateGCEPersistentDiskVolumeSource(PD *api.GCEPersistentDiskVolumeSource) validation.ErrorList { 486 allErrs := validation.ErrorList{} 487 if PD.PDName == "" { 488 allErrs = append(allErrs, validation.NewRequiredError("pdName")) 489 } 490 if PD.FSType == "" { 491 allErrs = append(allErrs, validation.NewRequiredError("fsType")) 492 } 493 if PD.Partition < 0 || PD.Partition > 255 { 494 allErrs = append(allErrs, validation.NewInvalidError("partition", PD.Partition, pdPartitionErrorMsg)) 495 } 496 return allErrs 497 } 498 499 func validateAWSElasticBlockStoreVolumeSource(PD *api.AWSElasticBlockStoreVolumeSource) validation.ErrorList { 500 allErrs := validation.ErrorList{} 501 if PD.VolumeID == "" { 502 allErrs = append(allErrs, validation.NewRequiredError("volumeID")) 503 } 504 if PD.FSType == "" { 505 allErrs = append(allErrs, validation.NewRequiredError("fsType")) 506 } 507 if PD.Partition < 0 || PD.Partition > 255 { 508 allErrs = append(allErrs, validation.NewInvalidError("partition", PD.Partition, pdPartitionErrorMsg)) 509 } 510 return allErrs 511 } 512 513 func validateSecretVolumeSource(secretSource *api.SecretVolumeSource) validation.ErrorList { 514 allErrs := validation.ErrorList{} 515 if secretSource.SecretName == "" { 516 allErrs = append(allErrs, validation.NewRequiredError("secretName")) 517 } 518 return allErrs 519 } 520 521 func validatePersistentClaimVolumeSource(claim *api.PersistentVolumeClaimVolumeSource) validation.ErrorList { 522 allErrs := validation.ErrorList{} 523 if claim.ClaimName == "" { 524 allErrs = append(allErrs, validation.NewRequiredError("claimName")) 525 } 526 return allErrs 527 } 528 529 func validateNFS(nfs *api.NFSVolumeSource) validation.ErrorList { 530 allErrs := validation.ErrorList{} 531 if nfs.Server == "" { 532 allErrs = append(allErrs, validation.NewRequiredError("server")) 533 } 534 if nfs.Path == "" { 535 allErrs = append(allErrs, validation.NewRequiredError("path")) 536 } 537 if !path.IsAbs(nfs.Path) { 538 allErrs = append(allErrs, validation.NewInvalidError("path", nfs.Path, "must be an absolute path")) 539 } 540 return allErrs 541 } 542 543 func validateGlusterfs(glusterfs *api.GlusterfsVolumeSource) validation.ErrorList { 544 allErrs := validation.ErrorList{} 545 if glusterfs.EndpointsName == "" { 546 allErrs = append(allErrs, validation.NewRequiredError("endpoints")) 547 } 548 if glusterfs.Path == "" { 549 allErrs = append(allErrs, validation.NewRequiredError("path")) 550 } 551 return allErrs 552 } 553 554 func validateFlocker(flocker *api.FlockerVolumeSource) validation.ErrorList { 555 allErrs := validation.ErrorList{} 556 if flocker.DatasetName == "" { 557 allErrs = append(allErrs, validation.NewRequiredError("datasetName")) 558 } 559 if strings.Contains(flocker.DatasetName, "/") { 560 allErrs = append(allErrs, validation.NewInvalidError("datasetName", flocker.DatasetName, "must not contain '/'")) 561 } 562 return allErrs 563 } 564 565 var validDownwardAPIFieldPathExpressions = sets.NewString("metadata.name", "metadata.namespace", "metadata.labels", "metadata.annotations") 566 567 func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSource) validation.ErrorList { 568 allErrs := validation.ErrorList{} 569 for _, downwardAPIVolumeFile := range downwardAPIVolume.Items { 570 if len(downwardAPIVolumeFile.Path) == 0 { 571 allErrs = append(allErrs, validation.NewRequiredError("path")) 572 } 573 if path.IsAbs(downwardAPIVolumeFile.Path) { 574 allErrs = append(allErrs, validation.NewForbiddenError("path", "must not be an absolute path")) 575 } 576 items := strings.Split(downwardAPIVolumeFile.Path, string(os.PathSeparator)) 577 for _, item := range items { 578 if item == ".." { 579 allErrs = append(allErrs, validation.NewInvalidError("path", downwardAPIVolumeFile.Path, "must not contain \"..\".")) 580 } 581 } 582 if strings.HasPrefix(items[0], "..") && len(items[0]) > 2 { 583 allErrs = append(allErrs, validation.NewInvalidError("path", downwardAPIVolumeFile.Path, "must not start with \"..\".")) 584 } 585 allErrs = append(allErrs, validateObjectFieldSelector(&downwardAPIVolumeFile.FieldRef, &validDownwardAPIFieldPathExpressions).Prefix("FieldRef")...) 586 } 587 return allErrs 588 } 589 590 func validateRBD(rbd *api.RBDVolumeSource) validation.ErrorList { 591 allErrs := validation.ErrorList{} 592 if len(rbd.CephMonitors) == 0 { 593 allErrs = append(allErrs, validation.NewRequiredError("monitors")) 594 } 595 if rbd.RBDImage == "" { 596 allErrs = append(allErrs, validation.NewRequiredError("image")) 597 } 598 if rbd.FSType == "" { 599 allErrs = append(allErrs, validation.NewRequiredError("fsType")) 600 } 601 return allErrs 602 } 603 604 func validateCinderVolumeSource(cd *api.CinderVolumeSource) validation.ErrorList { 605 allErrs := validation.ErrorList{} 606 if cd.VolumeID == "" { 607 allErrs = append(allErrs, validation.NewRequiredError("volumeID")) 608 } 609 if cd.FSType == "" || (cd.FSType != "ext3" && cd.FSType != "ext4") { 610 allErrs = append(allErrs, validation.NewRequiredError("fsType required and should be of type ext3 or ext4")) 611 } 612 return allErrs 613 } 614 615 func validateCephFS(cephfs *api.CephFSVolumeSource) validation.ErrorList { 616 allErrs := validation.ErrorList{} 617 if len(cephfs.Monitors) == 0 { 618 allErrs = append(allErrs, validation.NewRequiredError("monitors")) 619 } 620 return allErrs 621 } 622 623 func ValidatePersistentVolumeName(name string, prefix bool) (bool, string) { 624 return NameIsDNSSubdomain(name, prefix) 625 } 626 627 func ValidatePersistentVolume(pv *api.PersistentVolume) validation.ErrorList { 628 allErrs := validation.ErrorList{} 629 allErrs = append(allErrs, ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName).Prefix("metadata")...) 630 631 if len(pv.Spec.AccessModes) == 0 { 632 allErrs = append(allErrs, validation.NewRequiredError("persistentVolume.AccessModes")) 633 } 634 635 for _, mode := range pv.Spec.AccessModes { 636 if mode != api.ReadWriteOnce && mode != api.ReadOnlyMany && mode != api.ReadWriteMany { 637 allErrs = append(allErrs, validation.NewInvalidError("persistentVolume.Spec.AccessModes", mode, fmt.Sprintf("only %s, %s, and %s are valid", api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany))) 638 } 639 } 640 641 if len(pv.Spec.Capacity) == 0 { 642 allErrs = append(allErrs, validation.NewRequiredError("persistentVolume.Capacity")) 643 } 644 645 if _, ok := pv.Spec.Capacity[api.ResourceStorage]; !ok || len(pv.Spec.Capacity) > 1 { 646 allErrs = append(allErrs, validation.NewInvalidError("", pv.Spec.Capacity, fmt.Sprintf("only %s is expected", api.ResourceStorage))) 647 } 648 649 for _, qty := range pv.Spec.Capacity { 650 allErrs = append(allErrs, validateBasicResource(qty)...) 651 } 652 653 numVolumes := 0 654 if pv.Spec.HostPath != nil { 655 numVolumes++ 656 allErrs = append(allErrs, validateHostPathVolumeSource(pv.Spec.HostPath).Prefix("hostPath")...) 657 } 658 if pv.Spec.GCEPersistentDisk != nil { 659 numVolumes++ 660 allErrs = append(allErrs, validateGCEPersistentDiskVolumeSource(pv.Spec.GCEPersistentDisk).Prefix("persistentDisk")...) 661 } 662 if pv.Spec.AWSElasticBlockStore != nil { 663 numVolumes++ 664 allErrs = append(allErrs, validateAWSElasticBlockStoreVolumeSource(pv.Spec.AWSElasticBlockStore).Prefix("awsElasticBlockStore")...) 665 } 666 if pv.Spec.Glusterfs != nil { 667 numVolumes++ 668 allErrs = append(allErrs, validateGlusterfs(pv.Spec.Glusterfs).Prefix("glusterfs")...) 669 } 670 if pv.Spec.Flocker != nil { 671 numVolumes++ 672 allErrs = append(allErrs, validateFlocker(pv.Spec.Flocker).Prefix("flocker")...) 673 } 674 if pv.Spec.NFS != nil { 675 numVolumes++ 676 allErrs = append(allErrs, validateNFS(pv.Spec.NFS).Prefix("nfs")...) 677 } 678 if pv.Spec.RBD != nil { 679 numVolumes++ 680 allErrs = append(allErrs, validateRBD(pv.Spec.RBD).Prefix("rbd")...) 681 } 682 if pv.Spec.CephFS != nil { 683 numVolumes++ 684 allErrs = append(allErrs, validateCephFS(pv.Spec.CephFS).Prefix("cephfs")...) 685 } 686 if pv.Spec.ISCSI != nil { 687 numVolumes++ 688 allErrs = append(allErrs, validateISCSIVolumeSource(pv.Spec.ISCSI).Prefix("iscsi")...) 689 } 690 if pv.Spec.Cinder != nil { 691 numVolumes++ 692 allErrs = append(allErrs, validateCinderVolumeSource(pv.Spec.Cinder).Prefix("cinder")...) 693 } 694 if pv.Spec.FC != nil { 695 numVolumes++ 696 allErrs = append(allErrs, validateFCVolumeSource(pv.Spec.FC).Prefix("fc")...) 697 } 698 if numVolumes != 1 { 699 allErrs = append(allErrs, validation.NewInvalidError("", pv.Spec.PersistentVolumeSource, "exactly 1 volume type is required")) 700 } 701 return allErrs 702 } 703 704 // ValidatePersistentVolumeUpdate tests to see if the update is legal for an end user to make. 705 // newPv is updated with fields that cannot be changed. 706 func ValidatePersistentVolumeUpdate(newPv, oldPv *api.PersistentVolume) validation.ErrorList { 707 allErrs := validation.ErrorList{} 708 allErrs = ValidatePersistentVolume(newPv) 709 newPv.Status = oldPv.Status 710 return allErrs 711 } 712 713 // ValidatePersistentVolumeStatusUpdate tests to see if the status update is legal for an end user to make. 714 // newPv is updated with fields that cannot be changed. 715 func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *api.PersistentVolume) validation.ErrorList { 716 allErrs := validation.ErrorList{} 717 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newPv.ObjectMeta, &oldPv.ObjectMeta).Prefix("metadata")...) 718 if newPv.ResourceVersion == "" { 719 allErrs = append(allErrs, validation.NewRequiredError("resourceVersion")) 720 } 721 newPv.Spec = oldPv.Spec 722 return allErrs 723 } 724 725 func ValidatePersistentVolumeClaim(pvc *api.PersistentVolumeClaim) validation.ErrorList { 726 allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName) 727 if len(pvc.Spec.AccessModes) == 0 { 728 allErrs = append(allErrs, validation.NewInvalidError("persistentVolumeClaim.Spec.AccessModes", pvc.Spec.AccessModes, "at least 1 PersistentVolumeAccessMode is required")) 729 } 730 for _, mode := range pvc.Spec.AccessModes { 731 if mode != api.ReadWriteOnce && mode != api.ReadOnlyMany && mode != api.ReadWriteMany { 732 allErrs = append(allErrs, validation.NewInvalidError("persistentVolumeClaim.Spec.AccessModes", mode, fmt.Sprintf("only %s, %s, and %s are valid", api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany))) 733 } 734 } 735 if _, ok := pvc.Spec.Resources.Requests[api.ResourceStorage]; !ok { 736 allErrs = append(allErrs, validation.NewInvalidError("persistentVolumeClaim.Spec.Resources.Requests", pvc.Spec.Resources.Requests, "No Storage size specified")) 737 } 738 return allErrs 739 } 740 741 func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *api.PersistentVolumeClaim) validation.ErrorList { 742 allErrs := validation.ErrorList{} 743 allErrs = ValidatePersistentVolumeClaim(newPvc) 744 newPvc.Status = oldPvc.Status 745 return allErrs 746 } 747 748 func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *api.PersistentVolumeClaim) validation.ErrorList { 749 allErrs := validation.ErrorList{} 750 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta).Prefix("metadata")...) 751 if newPvc.ResourceVersion == "" { 752 allErrs = append(allErrs, validation.NewRequiredError("resourceVersion")) 753 } 754 if len(newPvc.Spec.AccessModes) == 0 { 755 allErrs = append(allErrs, validation.NewRequiredError("persistentVolume.AccessModes")) 756 } 757 for _, qty := range newPvc.Status.Capacity { 758 allErrs = append(allErrs, validateBasicResource(qty)...) 759 } 760 newPvc.Spec = oldPvc.Spec 761 return allErrs 762 } 763 764 var supportedPortProtocols = sets.NewString(string(api.ProtocolTCP), string(api.ProtocolUDP)) 765 766 func validatePorts(ports []api.ContainerPort) validation.ErrorList { 767 allErrs := validation.ErrorList{} 768 769 allNames := sets.String{} 770 for i, port := range ports { 771 pErrs := validation.ErrorList{} 772 if len(port.Name) > 0 { 773 if !validation.IsValidPortName(port.Name) { 774 pErrs = append(pErrs, validation.NewInvalidError("name", port.Name, PortNameErrorMsg)) 775 } else if allNames.Has(port.Name) { 776 pErrs = append(pErrs, validation.NewDuplicateError("name", port.Name)) 777 } else { 778 allNames.Insert(port.Name) 779 } 780 } 781 if port.ContainerPort == 0 { 782 pErrs = append(pErrs, validation.NewInvalidError("containerPort", port.ContainerPort, PortRangeErrorMsg)) 783 } else if !validation.IsValidPortNum(port.ContainerPort) { 784 pErrs = append(pErrs, validation.NewInvalidError("containerPort", port.ContainerPort, PortRangeErrorMsg)) 785 } 786 if port.HostPort != 0 && !validation.IsValidPortNum(port.HostPort) { 787 pErrs = append(pErrs, validation.NewInvalidError("hostPort", port.HostPort, PortRangeErrorMsg)) 788 } 789 if len(port.Protocol) == 0 { 790 pErrs = append(pErrs, validation.NewRequiredError("protocol")) 791 } else if !supportedPortProtocols.Has(string(port.Protocol)) { 792 pErrs = append(pErrs, validation.NewNotSupportedError("protocol", port.Protocol, supportedPortProtocols.List())) 793 } 794 allErrs = append(allErrs, pErrs.PrefixIndex(i)...) 795 } 796 return allErrs 797 } 798 799 func validateEnv(vars []api.EnvVar) validation.ErrorList { 800 allErrs := validation.ErrorList{} 801 802 for i, ev := range vars { 803 vErrs := validation.ErrorList{} 804 if len(ev.Name) == 0 { 805 vErrs = append(vErrs, validation.NewRequiredError("name")) 806 } else if !validation.IsCIdentifier(ev.Name) { 807 vErrs = append(vErrs, validation.NewInvalidError("name", ev.Name, cIdentifierErrorMsg)) 808 } 809 vErrs = append(vErrs, validateEnvVarValueFrom(ev).Prefix("valueFrom")...) 810 allErrs = append(allErrs, vErrs.PrefixIndex(i)...) 811 } 812 return allErrs 813 } 814 815 var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "status.podIP") 816 817 func validateEnvVarValueFrom(ev api.EnvVar) validation.ErrorList { 818 allErrs := validation.ErrorList{} 819 820 if ev.ValueFrom == nil { 821 return allErrs 822 } 823 824 numSources := 0 825 826 switch { 827 case ev.ValueFrom.FieldRef != nil: 828 numSources++ 829 allErrs = append(allErrs, validateObjectFieldSelector(ev.ValueFrom.FieldRef, &validFieldPathExpressionsEnv).Prefix("fieldRef")...) 830 } 831 832 if ev.Value != "" && numSources != 0 { 833 allErrs = append(allErrs, validation.NewInvalidError("", "", "sources cannot be specified when value is not empty")) 834 } 835 836 return allErrs 837 } 838 839 func validateObjectFieldSelector(fs *api.ObjectFieldSelector, expressions *sets.String) validation.ErrorList { 840 allErrs := validation.ErrorList{} 841 842 if fs.APIVersion == "" { 843 allErrs = append(allErrs, validation.NewRequiredError("apiVersion")) 844 } else if fs.FieldPath == "" { 845 allErrs = append(allErrs, validation.NewRequiredError("fieldPath")) 846 } else { 847 internalFieldPath, _, err := api.Scheme.ConvertFieldLabel(fs.APIVersion, "Pod", fs.FieldPath, "") 848 if err != nil { 849 allErrs = append(allErrs, validation.NewInvalidError("fieldPath", fs.FieldPath, "error converting fieldPath")) 850 } else if !expressions.Has(internalFieldPath) { 851 allErrs = append(allErrs, validation.NewNotSupportedError("fieldPath", internalFieldPath, expressions.List())) 852 } 853 } 854 855 return allErrs 856 } 857 858 func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String) validation.ErrorList { 859 allErrs := validation.ErrorList{} 860 861 for i, mnt := range mounts { 862 mErrs := validation.ErrorList{} 863 if len(mnt.Name) == 0 { 864 mErrs = append(mErrs, validation.NewRequiredError("name")) 865 } else if !volumes.Has(mnt.Name) { 866 mErrs = append(mErrs, validation.NewNotFoundError("name", mnt.Name)) 867 } 868 if len(mnt.MountPath) == 0 { 869 mErrs = append(mErrs, validation.NewRequiredError("mountPath")) 870 } 871 allErrs = append(allErrs, mErrs.PrefixIndex(i)...) 872 } 873 return allErrs 874 } 875 876 func validateProbe(probe *api.Probe) validation.ErrorList { 877 allErrs := validation.ErrorList{} 878 879 if probe == nil { 880 return allErrs 881 } 882 allErrs = append(allErrs, validateHandler(&probe.Handler)...) 883 allErrs = append(allErrs, ValidatePositiveField(probe.InitialDelaySeconds, "initialDelaySeconds")...) 884 allErrs = append(allErrs, ValidatePositiveField(probe.TimeoutSeconds, "timeoutSeconds")...) 885 allErrs = append(allErrs, ValidatePositiveField(int64(probe.PeriodSeconds), "periodSeconds")...) 886 allErrs = append(allErrs, ValidatePositiveField(int64(probe.SuccessThreshold), "successThreshold")...) 887 allErrs = append(allErrs, ValidatePositiveField(int64(probe.FailureThreshold), "failureThreshold")...) 888 return allErrs 889 } 890 891 // AccumulateUniqueHostPorts extracts each HostPort of each Container, 892 // accumulating the results and returning an error if any ports conflict. 893 func AccumulateUniqueHostPorts(containers []api.Container, accumulator *sets.String) validation.ErrorList { 894 allErrs := validation.ErrorList{} 895 896 for ci, ctr := range containers { 897 cErrs := validation.ErrorList{} 898 for pi := range ctr.Ports { 899 port := ctr.Ports[pi].HostPort 900 if port == 0 { 901 continue 902 } 903 str := fmt.Sprintf("%d/%s", port, ctr.Ports[pi].Protocol) 904 if accumulator.Has(str) { 905 cErrs = append(cErrs, validation.NewDuplicateError("port", str)) 906 } else { 907 accumulator.Insert(str) 908 } 909 } 910 allErrs = append(allErrs, cErrs.PrefixIndex(ci)...) 911 } 912 return allErrs 913 } 914 915 // checkHostPortConflicts checks for colliding Port.HostPort values across 916 // a slice of containers. 917 func checkHostPortConflicts(containers []api.Container) validation.ErrorList { 918 allPorts := sets.String{} 919 return AccumulateUniqueHostPorts(containers, &allPorts) 920 } 921 922 func validateExecAction(exec *api.ExecAction) validation.ErrorList { 923 allErrors := validation.ErrorList{} 924 if len(exec.Command) == 0 { 925 allErrors = append(allErrors, validation.NewRequiredError("command")) 926 } 927 return allErrors 928 } 929 930 func validateHTTPGetAction(http *api.HTTPGetAction) validation.ErrorList { 931 allErrors := validation.ErrorList{} 932 if len(http.Path) == 0 { 933 allErrors = append(allErrors, validation.NewRequiredError("path")) 934 } 935 if http.Port.Type == intstr.Int && !validation.IsValidPortNum(http.Port.IntVal) { 936 allErrors = append(allErrors, validation.NewInvalidError("port", http.Port, PortRangeErrorMsg)) 937 } else if http.Port.Type == intstr.String && !validation.IsValidPortName(http.Port.StrVal) { 938 allErrors = append(allErrors, validation.NewInvalidError("port", http.Port.StrVal, PortNameErrorMsg)) 939 } 940 supportedSchemes := sets.NewString(string(api.URISchemeHTTP), string(api.URISchemeHTTPS)) 941 if !supportedSchemes.Has(string(http.Scheme)) { 942 allErrors = append(allErrors, validation.NewInvalidError("scheme", http.Scheme, fmt.Sprintf("must be one of %v", supportedSchemes.List()))) 943 } 944 return allErrors 945 } 946 947 func validateTCPSocketAction(tcp *api.TCPSocketAction) validation.ErrorList { 948 allErrors := validation.ErrorList{} 949 if tcp.Port.Type == intstr.Int && !validation.IsValidPortNum(tcp.Port.IntVal) { 950 allErrors = append(allErrors, validation.NewInvalidError("port", tcp.Port, PortRangeErrorMsg)) 951 } else if tcp.Port.Type == intstr.String && !validation.IsValidPortName(tcp.Port.StrVal) { 952 allErrors = append(allErrors, validation.NewInvalidError("port", tcp.Port.StrVal, PortNameErrorMsg)) 953 } 954 return allErrors 955 } 956 957 func validateHandler(handler *api.Handler) validation.ErrorList { 958 numHandlers := 0 959 allErrors := validation.ErrorList{} 960 if handler.Exec != nil { 961 numHandlers++ 962 allErrors = append(allErrors, validateExecAction(handler.Exec).Prefix("exec")...) 963 } 964 if handler.HTTPGet != nil { 965 numHandlers++ 966 allErrors = append(allErrors, validateHTTPGetAction(handler.HTTPGet).Prefix("httpGet")...) 967 } 968 if handler.TCPSocket != nil { 969 numHandlers++ 970 allErrors = append(allErrors, validateTCPSocketAction(handler.TCPSocket).Prefix("tcpSocket")...) 971 } 972 if numHandlers != 1 { 973 allErrors = append(allErrors, validation.NewInvalidError("", handler, "exactly 1 handler type is required")) 974 } 975 return allErrors 976 } 977 978 func validateLifecycle(lifecycle *api.Lifecycle) validation.ErrorList { 979 allErrs := validation.ErrorList{} 980 if lifecycle.PostStart != nil { 981 allErrs = append(allErrs, validateHandler(lifecycle.PostStart).Prefix("postStart")...) 982 } 983 if lifecycle.PreStop != nil { 984 allErrs = append(allErrs, validateHandler(lifecycle.PreStop).Prefix("preStop")...) 985 } 986 return allErrs 987 } 988 989 func validatePullPolicy(ctr *api.Container) validation.ErrorList { 990 allErrors := validation.ErrorList{} 991 992 switch ctr.ImagePullPolicy { 993 case api.PullAlways, api.PullIfNotPresent, api.PullNever: 994 break 995 case "": 996 allErrors = append(allErrors, validation.NewRequiredError("")) 997 default: 998 validValues := []string{string(api.PullAlways), string(api.PullIfNotPresent), string(api.PullNever)} 999 allErrors = append(allErrors, validation.NewNotSupportedError("", ctr.ImagePullPolicy, validValues)) 1000 } 1001 1002 return allErrors 1003 } 1004 1005 func validateContainers(containers []api.Container, volumes sets.String) validation.ErrorList { 1006 allErrs := validation.ErrorList{} 1007 1008 if len(containers) == 0 { 1009 return append(allErrs, validation.NewRequiredError("")) 1010 } 1011 1012 allNames := sets.String{} 1013 for i, ctr := range containers { 1014 cErrs := validation.ErrorList{} 1015 if len(ctr.Name) == 0 { 1016 cErrs = append(cErrs, validation.NewRequiredError("name")) 1017 } else if !validation.IsDNS1123Label(ctr.Name) { 1018 cErrs = append(cErrs, validation.NewInvalidError("name", ctr.Name, DNS1123LabelErrorMsg)) 1019 } else if allNames.Has(ctr.Name) { 1020 cErrs = append(cErrs, validation.NewDuplicateError("name", ctr.Name)) 1021 } else { 1022 allNames.Insert(ctr.Name) 1023 } 1024 if len(ctr.Image) == 0 { 1025 cErrs = append(cErrs, validation.NewRequiredError("image")) 1026 } 1027 if ctr.Lifecycle != nil { 1028 cErrs = append(cErrs, validateLifecycle(ctr.Lifecycle).Prefix("lifecycle")...) 1029 } 1030 cErrs = append(cErrs, validateProbe(ctr.LivenessProbe).Prefix("livenessProbe")...) 1031 // Liveness-specific validation 1032 if ctr.LivenessProbe != nil && ctr.LivenessProbe.SuccessThreshold != 1 { 1033 allErrs = append(allErrs, validation.NewForbiddenError("livenessProbe.successThreshold", "must be 1")) 1034 } 1035 1036 cErrs = append(cErrs, validateProbe(ctr.ReadinessProbe).Prefix("readinessProbe")...) 1037 cErrs = append(cErrs, validatePorts(ctr.Ports).Prefix("ports")...) 1038 cErrs = append(cErrs, validateEnv(ctr.Env).Prefix("env")...) 1039 cErrs = append(cErrs, validateVolumeMounts(ctr.VolumeMounts, volumes).Prefix("volumeMounts")...) 1040 cErrs = append(cErrs, validatePullPolicy(&ctr).Prefix("imagePullPolicy")...) 1041 cErrs = append(cErrs, ValidateResourceRequirements(&ctr.Resources).Prefix("resources")...) 1042 cErrs = append(cErrs, ValidateSecurityContext(ctr.SecurityContext).Prefix("securityContext")...) 1043 allErrs = append(allErrs, cErrs.PrefixIndex(i)...) 1044 } 1045 // Check for colliding ports across all containers. 1046 allErrs = append(allErrs, checkHostPortConflicts(containers)...) 1047 1048 return allErrs 1049 } 1050 1051 func validateRestartPolicy(restartPolicy *api.RestartPolicy) validation.ErrorList { 1052 allErrors := validation.ErrorList{} 1053 switch *restartPolicy { 1054 case api.RestartPolicyAlways, api.RestartPolicyOnFailure, api.RestartPolicyNever: 1055 break 1056 case "": 1057 allErrors = append(allErrors, validation.NewRequiredError("")) 1058 default: 1059 validValues := []string{string(api.RestartPolicyAlways), string(api.RestartPolicyOnFailure), string(api.RestartPolicyNever)} 1060 allErrors = append(allErrors, validation.NewNotSupportedError("", *restartPolicy, validValues)) 1061 } 1062 1063 return allErrors 1064 } 1065 1066 func validateDNSPolicy(dnsPolicy *api.DNSPolicy) validation.ErrorList { 1067 allErrors := validation.ErrorList{} 1068 switch *dnsPolicy { 1069 case api.DNSClusterFirst, api.DNSDefault: 1070 break 1071 case "": 1072 allErrors = append(allErrors, validation.NewRequiredError("")) 1073 default: 1074 validValues := []string{string(api.DNSClusterFirst), string(api.DNSDefault)} 1075 allErrors = append(allErrors, validation.NewNotSupportedError("", dnsPolicy, validValues)) 1076 } 1077 return allErrors 1078 } 1079 1080 func validateHostNetwork(hostNetwork bool, containers []api.Container) validation.ErrorList { 1081 allErrors := validation.ErrorList{} 1082 if hostNetwork { 1083 for _, container := range containers { 1084 for _, port := range container.Ports { 1085 if port.HostPort != port.ContainerPort { 1086 allErrors = append(allErrors, validation.NewInvalidError("containerPort", port.ContainerPort, "containerPort must match hostPort if hostNetwork is set to true")) 1087 } 1088 } 1089 } 1090 } 1091 return allErrors 1092 } 1093 1094 // validateImagePullSecrets checks to make sure the pull secrets are well formed. Right now, we only expect name to be set (it's the only field). If this ever changes 1095 // and someone decides to set those fields, we'd like to know. 1096 func validateImagePullSecrets(imagePullSecrets []api.LocalObjectReference) validation.ErrorList { 1097 allErrors := validation.ErrorList{} 1098 for i, currPullSecret := range imagePullSecrets { 1099 strippedRef := api.LocalObjectReference{Name: currPullSecret.Name} 1100 1101 if !reflect.DeepEqual(strippedRef, currPullSecret) { 1102 allErrors = append(allErrors, validation.NewInvalidError(fmt.Sprintf("[%d]", i), currPullSecret, "only name may be set")) 1103 } 1104 } 1105 return allErrors 1106 } 1107 1108 // ValidatePod tests if required fields in the pod are set. 1109 func ValidatePod(pod *api.Pod) validation.ErrorList { 1110 allErrs := validation.ErrorList{} 1111 allErrs = append(allErrs, ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName).Prefix("metadata")...) 1112 allErrs = append(allErrs, ValidatePodSpec(&pod.Spec).Prefix("spec")...) 1113 1114 return allErrs 1115 } 1116 1117 // ValidatePodSpec tests that the specified PodSpec has valid data. 1118 // This includes checking formatting and uniqueness. It also canonicalizes the 1119 // structure by setting default values and implementing any backwards-compatibility 1120 // tricks. 1121 func ValidatePodSpec(spec *api.PodSpec) validation.ErrorList { 1122 allErrs := validation.ErrorList{} 1123 1124 allVolumes, vErrs := validateVolumes(spec.Volumes) 1125 allErrs = append(allErrs, vErrs.Prefix("volumes")...) 1126 allErrs = append(allErrs, validateContainers(spec.Containers, allVolumes).Prefix("containers")...) 1127 allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy).Prefix("restartPolicy")...) 1128 allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy).Prefix("dnsPolicy")...) 1129 allErrs = append(allErrs, ValidateLabels(spec.NodeSelector, "nodeSelector")...) 1130 allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec).Prefix("securityContext")...) 1131 allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets).Prefix("imagePullSecrets")...) 1132 if len(spec.ServiceAccountName) > 0 { 1133 if ok, msg := ValidateServiceAccountName(spec.ServiceAccountName, false); !ok { 1134 allErrs = append(allErrs, validation.NewInvalidError("serviceAccountName", spec.ServiceAccountName, msg)) 1135 } 1136 } 1137 1138 if spec.ActiveDeadlineSeconds != nil { 1139 if *spec.ActiveDeadlineSeconds <= 0 { 1140 allErrs = append(allErrs, validation.NewInvalidError("activeDeadlineSeconds", spec.ActiveDeadlineSeconds, "activeDeadlineSeconds must be a positive integer greater than 0")) 1141 } 1142 } 1143 return allErrs 1144 } 1145 1146 // ValidatePodSecurityContext test that the specified PodSecurityContext has valid data. 1147 func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *api.PodSpec) validation.ErrorList { 1148 allErrs := validation.ErrorList{} 1149 1150 if securityContext != nil { 1151 allErrs = append(allErrs, validateHostNetwork(securityContext.HostNetwork, spec.Containers).Prefix("hostNetwork")...) 1152 } 1153 1154 return allErrs 1155 } 1156 1157 // ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields 1158 // that cannot be changed. 1159 func ValidatePodUpdate(newPod, oldPod *api.Pod) validation.ErrorList { 1160 allErrs := validation.ErrorList{} 1161 1162 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta).Prefix("metadata")...) 1163 1164 if len(newPod.Spec.Containers) != len(oldPod.Spec.Containers) { 1165 //TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff 1166 allErrs = append(allErrs, validation.NewInvalidError("spec.containers", "content of spec.containers is not printed out, please refer to the \"details\"", "may not add or remove containers")) 1167 return allErrs 1168 } 1169 pod := *newPod 1170 // Tricky, we need to copy the container list so that we don't overwrite the update 1171 var newContainers []api.Container 1172 for ix, container := range pod.Spec.Containers { 1173 container.Image = oldPod.Spec.Containers[ix].Image 1174 newContainers = append(newContainers, container) 1175 } 1176 pod.Spec.Containers = newContainers 1177 if !api.Semantic.DeepEqual(pod.Spec, oldPod.Spec) { 1178 //TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff 1179 allErrs = append(allErrs, validation.NewInvalidError("spec", "content of spec is not printed out, please refer to the \"details\"", "may not update fields other than container.image")) 1180 } 1181 1182 newPod.Status = oldPod.Status 1183 return allErrs 1184 } 1185 1186 // ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields 1187 // that cannot be changed. 1188 func ValidatePodStatusUpdate(newPod, oldPod *api.Pod) validation.ErrorList { 1189 allErrs := validation.ErrorList{} 1190 1191 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta).Prefix("metadata")...) 1192 1193 // TODO: allow change when bindings are properly decoupled from pods 1194 if newPod.Spec.NodeName != oldPod.Spec.NodeName { 1195 allErrs = append(allErrs, validation.NewInvalidError("status.nodeName", newPod.Spec.NodeName, "pod nodename cannot be changed directly")) 1196 } 1197 1198 // For status update we ignore changes to pod spec. 1199 newPod.Spec = oldPod.Spec 1200 1201 return allErrs 1202 } 1203 1204 // ValidatePodTemplate tests if required fields in the pod template are set. 1205 func ValidatePodTemplate(pod *api.PodTemplate) validation.ErrorList { 1206 allErrs := validation.ErrorList{} 1207 allErrs = append(allErrs, ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName).Prefix("metadata")...) 1208 allErrs = append(allErrs, ValidatePodTemplateSpec(&pod.Template).Prefix("template")...) 1209 return allErrs 1210 } 1211 1212 // ValidatePodTemplateUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields 1213 // that cannot be changed. 1214 func ValidatePodTemplateUpdate(newPod, oldPod *api.PodTemplate) validation.ErrorList { 1215 allErrs := validation.ErrorList{} 1216 allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldPod.ObjectMeta, &newPod.ObjectMeta).Prefix("metadata")...) 1217 allErrs = append(allErrs, ValidatePodTemplateSpec(&newPod.Template).Prefix("template")...) 1218 return allErrs 1219 } 1220 1221 var supportedSessionAffinityType = sets.NewString(string(api.ServiceAffinityClientIP), string(api.ServiceAffinityNone)) 1222 var supportedServiceType = sets.NewString(string(api.ServiceTypeClusterIP), string(api.ServiceTypeNodePort), 1223 string(api.ServiceTypeLoadBalancer)) 1224 1225 // ValidateService tests if required fields in the service are set. 1226 func ValidateService(service *api.Service) validation.ErrorList { 1227 allErrs := validation.ErrorList{} 1228 allErrs = append(allErrs, ValidateObjectMeta(&service.ObjectMeta, true, ValidateServiceName).Prefix("metadata")...) 1229 1230 if len(service.Spec.Ports) == 0 && service.Spec.ClusterIP != api.ClusterIPNone { 1231 allErrs = append(allErrs, validation.NewRequiredError("spec.ports")) 1232 } 1233 if service.Spec.Type == api.ServiceTypeLoadBalancer { 1234 for ix := range service.Spec.Ports { 1235 port := &service.Spec.Ports[ix] 1236 if port.Port == 10250 { 1237 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.ports[%d].port", ix), port.Port, "can not expose port 10250 externally since it is used by kubelet")) 1238 } 1239 } 1240 } 1241 allPortNames := sets.String{} 1242 for i := range service.Spec.Ports { 1243 allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, &allPortNames).PrefixIndex(i).Prefix("spec.ports")...) 1244 } 1245 1246 if service.Spec.Selector != nil { 1247 allErrs = append(allErrs, ValidateLabels(service.Spec.Selector, "spec.selector")...) 1248 } 1249 1250 if service.Spec.SessionAffinity == "" { 1251 allErrs = append(allErrs, validation.NewRequiredError("spec.sessionAffinity")) 1252 } else if !supportedSessionAffinityType.Has(string(service.Spec.SessionAffinity)) { 1253 allErrs = append(allErrs, validation.NewNotSupportedError("spec.sessionAffinity", service.Spec.SessionAffinity, supportedSessionAffinityType.List())) 1254 } 1255 1256 if api.IsServiceIPSet(service) { 1257 if ip := net.ParseIP(service.Spec.ClusterIP); ip == nil { 1258 allErrs = append(allErrs, validation.NewInvalidError("spec.clusterIP", service.Spec.ClusterIP, "clusterIP should be empty, 'None', or a valid IP address")) 1259 } 1260 } 1261 1262 for _, ip := range service.Spec.ExternalIPs { 1263 if ip == "0.0.0.0" { 1264 allErrs = append(allErrs, validation.NewInvalidError("spec.externalIPs", ip, "is not an IP address")) 1265 } 1266 allErrs = append(allErrs, validateIpIsNotLinkLocalOrLoopback(ip, "spec.externalIPs")...) 1267 } 1268 1269 if service.Spec.Type == "" { 1270 allErrs = append(allErrs, validation.NewRequiredError("spec.type")) 1271 } else if !supportedServiceType.Has(string(service.Spec.Type)) { 1272 allErrs = append(allErrs, validation.NewNotSupportedError("spec.type", service.Spec.Type, supportedServiceType.List())) 1273 } 1274 1275 if service.Spec.Type == api.ServiceTypeLoadBalancer { 1276 for i := range service.Spec.Ports { 1277 if service.Spec.Ports[i].Protocol != api.ProtocolTCP { 1278 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.ports[%d].protocol", i), service.Spec.Ports[i].Protocol, "cannot create an external load balancer with non-TCP ports")) 1279 } 1280 } 1281 } 1282 1283 if service.Spec.Type == api.ServiceTypeClusterIP { 1284 for i := range service.Spec.Ports { 1285 if service.Spec.Ports[i].NodePort != 0 { 1286 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.ports[%d].nodePort", i), service.Spec.Ports[i].NodePort, "cannot specify a node port with services of type ClusterIP")) 1287 } 1288 } 1289 } 1290 1291 // Check for duplicate NodePorts, considering (protocol,port) pairs 1292 nodePorts := make(map[api.ServicePort]bool) 1293 for i := range service.Spec.Ports { 1294 port := &service.Spec.Ports[i] 1295 if port.NodePort == 0 { 1296 continue 1297 } 1298 var key api.ServicePort 1299 key.Protocol = port.Protocol 1300 key.NodePort = port.NodePort 1301 _, found := nodePorts[key] 1302 if found { 1303 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.ports[%d].nodePort", i), port.NodePort, "duplicate nodePort specified")) 1304 } 1305 nodePorts[key] = true 1306 } 1307 1308 return allErrs 1309 } 1310 1311 func validateServicePort(sp *api.ServicePort, requireName bool, allNames *sets.String) validation.ErrorList { 1312 allErrs := validation.ErrorList{} 1313 1314 if requireName && sp.Name == "" { 1315 allErrs = append(allErrs, validation.NewRequiredError("name")) 1316 } else if sp.Name != "" { 1317 if !validation.IsDNS1123Label(sp.Name) { 1318 allErrs = append(allErrs, validation.NewInvalidError("name", sp.Name, DNS1123LabelErrorMsg)) 1319 } else if allNames.Has(sp.Name) { 1320 allErrs = append(allErrs, validation.NewDuplicateError("name", sp.Name)) 1321 } else { 1322 allNames.Insert(sp.Name) 1323 } 1324 } 1325 1326 if !validation.IsValidPortNum(sp.Port) { 1327 allErrs = append(allErrs, validation.NewInvalidError("port", sp.Port, PortRangeErrorMsg)) 1328 } 1329 1330 if len(sp.Protocol) == 0 { 1331 allErrs = append(allErrs, validation.NewRequiredError("protocol")) 1332 } else if !supportedPortProtocols.Has(string(sp.Protocol)) { 1333 allErrs = append(allErrs, validation.NewNotSupportedError("protocol", sp.Protocol, supportedPortProtocols.List())) 1334 } 1335 1336 if sp.TargetPort.Type == intstr.Int && !validation.IsValidPortNum(sp.TargetPort.IntVal) { 1337 allErrs = append(allErrs, validation.NewInvalidError("targetPort", sp.TargetPort, PortRangeErrorMsg)) 1338 } 1339 if sp.TargetPort.Type == intstr.String && !validation.IsValidPortName(sp.TargetPort.StrVal) { 1340 allErrs = append(allErrs, validation.NewInvalidError("targetPort", sp.TargetPort, PortNameErrorMsg)) 1341 } 1342 1343 return allErrs 1344 } 1345 1346 // ValidateServiceUpdate tests if required fields in the service are set during an update 1347 func ValidateServiceUpdate(service, oldService *api.Service) validation.ErrorList { 1348 allErrs := validation.ErrorList{} 1349 allErrs = append(allErrs, ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta).Prefix("metadata")...) 1350 1351 if api.IsServiceIPSet(oldService) { 1352 allErrs = append(allErrs, ValidateImmutableField(service.Spec.ClusterIP, oldService.Spec.ClusterIP, "spec.clusterIP")...) 1353 } 1354 1355 allErrs = append(allErrs, ValidateService(service)...) 1356 return allErrs 1357 } 1358 1359 // ValidateReplicationController tests if required fields in the replication controller are set. 1360 func ValidateReplicationController(controller *api.ReplicationController) validation.ErrorList { 1361 allErrs := validation.ErrorList{} 1362 allErrs = append(allErrs, ValidateObjectMeta(&controller.ObjectMeta, true, ValidateReplicationControllerName).Prefix("metadata")...) 1363 allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec).Prefix("spec")...) 1364 return allErrs 1365 } 1366 1367 // ValidateReplicationControllerUpdate tests if required fields in the replication controller are set. 1368 func ValidateReplicationControllerUpdate(controller, oldController *api.ReplicationController) validation.ErrorList { 1369 allErrs := validation.ErrorList{} 1370 allErrs = append(allErrs, ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta).Prefix("metadata")...) 1371 allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec).Prefix("spec")...) 1372 return allErrs 1373 } 1374 1375 // ValidateReplicationControllerStatusUpdate tests if required fields in the replication controller are set. 1376 func ValidateReplicationControllerStatusUpdate(controller, oldController *api.ReplicationController) validation.ErrorList { 1377 allErrs := validation.ErrorList{} 1378 allErrs = append(allErrs, ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta).Prefix("metadata")...) 1379 allErrs = append(allErrs, ValidatePositiveField(int64(controller.Status.Replicas), "status.replicas")...) 1380 allErrs = append(allErrs, ValidatePositiveField(int64(controller.Status.ObservedGeneration), "status.observedGeneration")...) 1381 return allErrs 1382 } 1383 1384 // Validates that the given selector is non-empty. 1385 func ValidateNonEmptySelector(selectorMap map[string]string, fieldName string) validation.ErrorList { 1386 allErrs := validation.ErrorList{} 1387 selector := labels.Set(selectorMap).AsSelector() 1388 if selector.Empty() { 1389 allErrs = append(allErrs, validation.NewRequiredError(fieldName)) 1390 } 1391 return allErrs 1392 } 1393 1394 // Validates the given template and ensures that it is in accordance with the desrired selector and replicas. 1395 func ValidatePodTemplateSpecForRC(template *api.PodTemplateSpec, selectorMap map[string]string, replicas int, fieldName string) validation.ErrorList { 1396 allErrs := validation.ErrorList{} 1397 if template == nil { 1398 allErrs = append(allErrs, validation.NewRequiredError(fieldName)) 1399 } else { 1400 selector := labels.Set(selectorMap).AsSelector() 1401 if !selector.Empty() { 1402 // Verify that the RC selector matches the labels in template. 1403 labels := labels.Set(template.Labels) 1404 if !selector.Matches(labels) { 1405 allErrs = append(allErrs, validation.NewInvalidError(fieldName+".metadata.labels", template.Labels, "selector does not match labels in "+fieldName)) 1406 } 1407 } 1408 allErrs = append(allErrs, ValidatePodTemplateSpec(template).Prefix(fieldName)...) 1409 if replicas > 1 { 1410 allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(template.Spec.Volumes).Prefix(fieldName+".spec.volumes")...) 1411 } 1412 // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec(). 1413 if template.Spec.RestartPolicy != api.RestartPolicyAlways { 1414 allErrs = append(allErrs, validation.NewNotSupportedError(fieldName+".spec.restartPolicy", template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)})) 1415 } 1416 } 1417 return allErrs 1418 } 1419 1420 // ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set. 1421 func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec) validation.ErrorList { 1422 allErrs := validation.ErrorList{} 1423 1424 allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, "selector")...) 1425 allErrs = append(allErrs, ValidatePositiveField(int64(spec.Replicas), "replicas")...) 1426 allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, "template")...) 1427 return allErrs 1428 } 1429 1430 // ValidatePodTemplateSpec validates the spec of a pod template 1431 func ValidatePodTemplateSpec(spec *api.PodTemplateSpec) validation.ErrorList { 1432 allErrs := validation.ErrorList{} 1433 allErrs = append(allErrs, ValidateLabels(spec.Labels, "labels")...) 1434 allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, "annotations")...) 1435 allErrs = append(allErrs, ValidatePodSpec(&spec.Spec).Prefix("spec")...) 1436 return allErrs 1437 } 1438 1439 func ValidateReadOnlyPersistentDisks(volumes []api.Volume) validation.ErrorList { 1440 allErrs := validation.ErrorList{} 1441 for _, vol := range volumes { 1442 if vol.GCEPersistentDisk != nil { 1443 if vol.GCEPersistentDisk.ReadOnly == false { 1444 allErrs = append(allErrs, validation.NewInvalidError("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.")) 1445 } 1446 } 1447 // TODO: What to do for AWS? It doesn't support replicas 1448 } 1449 return allErrs 1450 } 1451 1452 // ValidateNode tests if required fields in the node are set. 1453 func ValidateNode(node *api.Node) validation.ErrorList { 1454 allErrs := validation.ErrorList{} 1455 allErrs = append(allErrs, ValidateObjectMeta(&node.ObjectMeta, false, ValidateNodeName).Prefix("metadata")...) 1456 1457 // Only validate spec. All status fields are optional and can be updated later. 1458 1459 // external ID is required. 1460 if len(node.Spec.ExternalID) == 0 { 1461 allErrs = append(allErrs, validation.NewRequiredError("spec.ExternalID")) 1462 } 1463 1464 // TODO(rjnagal): Ignore PodCIDR till its completely implemented. 1465 return allErrs 1466 } 1467 1468 // ValidateNodeUpdate tests to make sure a node update can be applied. Modifies oldNode. 1469 func ValidateNodeUpdate(node, oldNode *api.Node) validation.ErrorList { 1470 allErrs := validation.ErrorList{} 1471 allErrs = append(allErrs, ValidateObjectMetaUpdate(&node.ObjectMeta, &oldNode.ObjectMeta).Prefix("metadata")...) 1472 1473 // TODO: Enable the code once we have better api object.status update model. Currently, 1474 // anyone can update node status. 1475 // if !api.Semantic.DeepEqual(node.Status, api.NodeStatus{}) { 1476 // allErrs = append(allErrs, validation.NewInvalidError("status", node.Status, "status must be empty")) 1477 // } 1478 1479 // Validte no duplicate addresses in node status. 1480 addresses := make(map[api.NodeAddress]bool) 1481 for _, address := range node.Status.Addresses { 1482 if _, ok := addresses[address]; ok { 1483 allErrs = append(allErrs, validation.NewDuplicateError("addresses", address)) 1484 } 1485 addresses[address] = true 1486 } 1487 1488 // TODO: move reset function to its own location 1489 // Ignore metadata changes now that they have been tested 1490 oldNode.ObjectMeta = node.ObjectMeta 1491 // Allow users to update capacity 1492 oldNode.Status.Capacity = node.Status.Capacity 1493 // Allow the controller manager to assign a CIDR to a node. 1494 oldNode.Spec.PodCIDR = node.Spec.PodCIDR 1495 // Allow users to unschedule node 1496 oldNode.Spec.Unschedulable = node.Spec.Unschedulable 1497 // Clear status 1498 oldNode.Status = node.Status 1499 1500 // TODO: Add a 'real' error type for this error and provide print actual diffs. 1501 if !api.Semantic.DeepEqual(oldNode, node) { 1502 glog.V(4).Infof("Update failed validation %#v vs %#v", oldNode, node) 1503 allErrs = append(allErrs, validation.NewForbiddenError("", "update contains more than labels or capacity changes")) 1504 } 1505 1506 return allErrs 1507 } 1508 1509 // Validate compute resource typename. 1510 // Refer to docs/design/resources.md for more details. 1511 func validateResourceName(value string, field string) validation.ErrorList { 1512 allErrs := validation.ErrorList{} 1513 if !validation.IsQualifiedName(value) { 1514 return append(allErrs, validation.NewInvalidError(field, value, "resource typename: "+qualifiedNameErrorMsg)) 1515 } 1516 1517 if len(strings.Split(value, "/")) == 1 { 1518 if !api.IsStandardResourceName(value) { 1519 return append(allErrs, validation.NewInvalidError(field, value, "is neither a standard resource type nor is fully qualified")) 1520 } 1521 } 1522 1523 return validation.ErrorList{} 1524 } 1525 1526 // ValidateLimitRange tests if required fields in the LimitRange are set. 1527 func ValidateLimitRange(limitRange *api.LimitRange) validation.ErrorList { 1528 allErrs := validation.ErrorList{} 1529 allErrs = append(allErrs, ValidateObjectMeta(&limitRange.ObjectMeta, true, ValidateLimitRangeName).Prefix("metadata")...) 1530 1531 // ensure resource names are properly qualified per docs/design/resources.md 1532 limitTypeSet := map[api.LimitType]bool{} 1533 for i := range limitRange.Spec.Limits { 1534 limit := limitRange.Spec.Limits[i] 1535 _, found := limitTypeSet[limit.Type] 1536 if found { 1537 allErrs = append(allErrs, validation.NewDuplicateError(fmt.Sprintf("spec.limits[%d].type", i), limit.Type)) 1538 } 1539 limitTypeSet[limit.Type] = true 1540 1541 keys := sets.String{} 1542 min := map[string]resource.Quantity{} 1543 max := map[string]resource.Quantity{} 1544 defaults := map[string]resource.Quantity{} 1545 defaultRequests := map[string]resource.Quantity{} 1546 maxLimitRequestRatios := map[string]resource.Quantity{} 1547 1548 for k, q := range limit.Max { 1549 allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].max[%s]", i, k))...) 1550 keys.Insert(string(k)) 1551 max[string(k)] = q 1552 } 1553 for k, q := range limit.Min { 1554 allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].min[%s]", i, k))...) 1555 keys.Insert(string(k)) 1556 min[string(k)] = q 1557 } 1558 1559 if limit.Type == api.LimitTypePod { 1560 if len(limit.Default) > 0 { 1561 allErrs = append(allErrs, validation.NewInvalidError("spec.limits[%d].default", limit.Default, "Default is not supported when limit type is Pod")) 1562 } 1563 if len(limit.DefaultRequest) > 0 { 1564 allErrs = append(allErrs, validation.NewInvalidError("spec.limits[%d].defaultRequest", limit.DefaultRequest, "DefaultRequest is not supported when limit type is Pod")) 1565 } 1566 } else { 1567 for k, q := range limit.Default { 1568 allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].default[%s]", i, k))...) 1569 keys.Insert(string(k)) 1570 defaults[string(k)] = q 1571 } 1572 for k, q := range limit.DefaultRequest { 1573 allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k))...) 1574 keys.Insert(string(k)) 1575 defaultRequests[string(k)] = q 1576 } 1577 } 1578 1579 for k, q := range limit.MaxLimitRequestRatio { 1580 allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].maxLimitRequestRatio[%s]", i, k))...) 1581 keys.Insert(string(k)) 1582 maxLimitRequestRatios[string(k)] = q 1583 } 1584 1585 for k := range keys { 1586 minQuantity, minQuantityFound := min[k] 1587 maxQuantity, maxQuantityFound := max[k] 1588 defaultQuantity, defaultQuantityFound := defaults[k] 1589 defaultRequestQuantity, defaultRequestQuantityFound := defaultRequests[k] 1590 maxRatio, maxRatioFound := maxLimitRequestRatios[k] 1591 1592 if minQuantityFound && maxQuantityFound && minQuantity.Cmp(maxQuantity) > 0 { 1593 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].min[%s]", i, k), minQuantity, fmt.Sprintf("min value %s is greater than max value %s", minQuantity.String(), maxQuantity.String()))) 1594 } 1595 1596 if defaultRequestQuantityFound && minQuantityFound && minQuantity.Cmp(defaultRequestQuantity) > 0 { 1597 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k), defaultRequestQuantity, fmt.Sprintf("min value %s is greater than default request value %s", minQuantity.String(), defaultRequestQuantity.String()))) 1598 } 1599 1600 if defaultRequestQuantityFound && maxQuantityFound && defaultRequestQuantity.Cmp(maxQuantity) > 0 { 1601 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k), defaultRequestQuantity, fmt.Sprintf("default request value %s is greater than max value %s", defaultRequestQuantity.String(), maxQuantity.String()))) 1602 } 1603 1604 if defaultRequestQuantityFound && defaultQuantityFound && defaultRequestQuantity.Cmp(defaultQuantity) > 0 { 1605 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k), defaultRequestQuantity, fmt.Sprintf("default request value %s is greater than default limit value %s", defaultRequestQuantity.String(), defaultQuantity.String()))) 1606 } 1607 1608 if defaultQuantityFound && minQuantityFound && minQuantity.Cmp(defaultQuantity) > 0 { 1609 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].default[%s]", i, k), minQuantity, fmt.Sprintf("min value %s is greater than default value %s", minQuantity.String(), defaultQuantity.String()))) 1610 } 1611 1612 if defaultQuantityFound && maxQuantityFound && defaultQuantity.Cmp(maxQuantity) > 0 { 1613 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].default[%s]", i, k), maxQuantity, fmt.Sprintf("default value %s is greater than max value %s", defaultQuantity.String(), maxQuantity.String()))) 1614 } 1615 if maxRatioFound && maxRatio.Cmp(*resource.NewQuantity(1, resource.DecimalSI)) < 0 { 1616 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].maxLimitRequestRatio[%s]", i, k), maxRatio, fmt.Sprintf("maxLimitRequestRatio %s is less than 1", maxRatio.String()))) 1617 } 1618 if maxRatioFound && minQuantityFound && maxQuantityFound { 1619 maxRatioValue := float64(maxRatio.Value()) 1620 minQuantityValue := minQuantity.Value() 1621 maxQuantityValue := maxQuantity.Value() 1622 if maxRatio.Value() < resource.MaxMilliValue && minQuantityValue < resource.MaxMilliValue && maxQuantityValue < resource.MaxMilliValue { 1623 maxRatioValue = float64(maxRatio.MilliValue()) / 1000 1624 minQuantityValue = minQuantity.MilliValue() 1625 maxQuantityValue = maxQuantity.MilliValue() 1626 } 1627 maxRatioLimit := float64(maxQuantityValue) / float64(minQuantityValue) 1628 if maxRatioValue > maxRatioLimit { 1629 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("spec.limits[%d].maxLimitRequestRatio[%s]", i, k), maxRatio, fmt.Sprintf("maxLimitRequestRatio %s is greater than max/min = %f", maxRatio.String(), maxRatioLimit))) 1630 } 1631 } 1632 } 1633 } 1634 1635 return allErrs 1636 } 1637 1638 // ValidateServiceAccount tests if required fields in the ServiceAccount are set. 1639 func ValidateServiceAccount(serviceAccount *api.ServiceAccount) validation.ErrorList { 1640 allErrs := validation.ErrorList{} 1641 allErrs = append(allErrs, ValidateObjectMeta(&serviceAccount.ObjectMeta, true, ValidateServiceAccountName).Prefix("metadata")...) 1642 return allErrs 1643 } 1644 1645 // ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set. 1646 func ValidateServiceAccountUpdate(newServiceAccount, oldServiceAccount *api.ServiceAccount) validation.ErrorList { 1647 allErrs := validation.ErrorList{} 1648 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newServiceAccount.ObjectMeta, &oldServiceAccount.ObjectMeta).Prefix("metadata")...) 1649 allErrs = append(allErrs, ValidateServiceAccount(newServiceAccount)...) 1650 return allErrs 1651 } 1652 1653 const SecretKeyFmt string = "\\.?" + validation.DNS1123LabelFmt + "(\\." + validation.DNS1123LabelFmt + ")*" 1654 1655 var secretKeyRegexp = regexp.MustCompile("^" + SecretKeyFmt + "$") 1656 1657 // IsSecretKey tests for a string that conforms to the definition of a 1658 // subdomain in DNS (RFC 1123), except that a leading dot is allowed 1659 func IsSecretKey(value string) bool { 1660 return len(value) <= validation.DNS1123SubdomainMaxLength && secretKeyRegexp.MatchString(value) 1661 } 1662 1663 // ValidateSecret tests if required fields in the Secret are set. 1664 func ValidateSecret(secret *api.Secret) validation.ErrorList { 1665 allErrs := validation.ErrorList{} 1666 allErrs = append(allErrs, ValidateObjectMeta(&secret.ObjectMeta, true, ValidateSecretName).Prefix("metadata")...) 1667 1668 totalSize := 0 1669 for key, value := range secret.Data { 1670 if !IsSecretKey(key) { 1671 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("data[%s]", key), key, fmt.Sprintf("must have at most %d characters and match regex %s", validation.DNS1123SubdomainMaxLength, SecretKeyFmt))) 1672 } 1673 1674 totalSize += len(value) 1675 } 1676 1677 if totalSize > api.MaxSecretSize { 1678 allErrs = append(allErrs, validation.NewForbiddenError("data", "Maximum secret size exceeded")) 1679 } 1680 1681 switch secret.Type { 1682 case api.SecretTypeServiceAccountToken: 1683 // Only require Annotations[kubernetes.io/service-account.name] 1684 // Additional fields (like Annotations[kubernetes.io/service-account.uid] and Data[token]) might be contributed later by a controller loop 1685 if value := secret.Annotations[api.ServiceAccountNameKey]; len(value) == 0 { 1686 allErrs = append(allErrs, validation.NewRequiredError(fmt.Sprintf("metadata.annotations[%s]", api.ServiceAccountNameKey))) 1687 } 1688 case api.SecretTypeOpaque, "": 1689 // no-op 1690 case api.SecretTypeDockercfg: 1691 dockercfgBytes, exists := secret.Data[api.DockerConfigKey] 1692 if !exists { 1693 allErrs = append(allErrs, validation.NewRequiredError(fmt.Sprintf("data[%s]", api.DockerConfigKey))) 1694 break 1695 } 1696 1697 // make sure that the content is well-formed json. 1698 if err := json.Unmarshal(dockercfgBytes, &map[string]interface{}{}); err != nil { 1699 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("data[%s]", api.DockerConfigKey), "<secret contents redacted>", err.Error())) 1700 } 1701 1702 default: 1703 // no-op 1704 } 1705 1706 return allErrs 1707 } 1708 1709 // ValidateSecretUpdate tests if required fields in the Secret are set. 1710 func ValidateSecretUpdate(newSecret, oldSecret *api.Secret) validation.ErrorList { 1711 allErrs := validation.ErrorList{} 1712 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newSecret.ObjectMeta, &oldSecret.ObjectMeta).Prefix("metadata")...) 1713 1714 if len(newSecret.Type) == 0 { 1715 newSecret.Type = oldSecret.Type 1716 } 1717 1718 allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, "type")...) 1719 1720 allErrs = append(allErrs, ValidateSecret(newSecret)...) 1721 return allErrs 1722 } 1723 1724 func validateBasicResource(quantity resource.Quantity) validation.ErrorList { 1725 if quantity.Value() < 0 { 1726 return validation.ErrorList{validation.NewInvalidError("", quantity.Value(), "must be a valid resource quantity")} 1727 } 1728 return validation.ErrorList{} 1729 } 1730 1731 // Validates resource requirement spec. 1732 func ValidateResourceRequirements(requirements *api.ResourceRequirements) validation.ErrorList { 1733 allErrs := validation.ErrorList{} 1734 for resourceName, quantity := range requirements.Limits { 1735 // Validate resource name. 1736 allErrs = append(allErrs, validateResourceName(resourceName.String(), fmt.Sprintf("resources.limits[%s]", resourceName))...) 1737 if api.IsStandardResourceName(resourceName.String()) { 1738 allErrs = append(allErrs, validateBasicResource(quantity).Prefix(fmt.Sprintf("Resource %s: ", resourceName))...) 1739 } 1740 // Check that request <= limit. 1741 requestQuantity, exists := requirements.Requests[resourceName] 1742 if exists { 1743 var requestValue, limitValue int64 1744 requestValue = requestQuantity.Value() 1745 limitValue = quantity.Value() 1746 // Do a more precise comparison if possible (if the value won't overflow). 1747 if requestValue <= resource.MaxMilliValue && limitValue <= resource.MaxMilliValue { 1748 requestValue = requestQuantity.MilliValue() 1749 limitValue = quantity.MilliValue() 1750 } 1751 if limitValue < requestValue { 1752 allErrs = append(allErrs, validation.NewInvalidError(fmt.Sprintf("resources.limits[%s]", resourceName), quantity.String(), "limit cannot be smaller than request")) 1753 } 1754 } 1755 } 1756 for resourceName, quantity := range requirements.Requests { 1757 // Validate resource name. 1758 allErrs = append(allErrs, validateResourceName(resourceName.String(), fmt.Sprintf("resources.requests[%s]", resourceName))...) 1759 if api.IsStandardResourceName(resourceName.String()) { 1760 allErrs = append(allErrs, validateBasicResource(quantity).Prefix(fmt.Sprintf("Resource %s: ", resourceName))...) 1761 } 1762 } 1763 return allErrs 1764 } 1765 1766 // ValidateResourceQuota tests if required fields in the ResourceQuota are set. 1767 func ValidateResourceQuota(resourceQuota *api.ResourceQuota) validation.ErrorList { 1768 allErrs := validation.ErrorList{} 1769 allErrs = append(allErrs, ValidateObjectMeta(&resourceQuota.ObjectMeta, true, ValidateResourceQuotaName).Prefix("metadata")...) 1770 1771 for k, v := range resourceQuota.Spec.Hard { 1772 allErrs = append(allErrs, validateResourceName(string(k), string(resourceQuota.TypeMeta.Kind))...) 1773 allErrs = append(allErrs, validateResourceQuantityValue(string(k), v)...) 1774 } 1775 for k, v := range resourceQuota.Status.Hard { 1776 allErrs = append(allErrs, validateResourceName(string(k), string(resourceQuota.TypeMeta.Kind))...) 1777 allErrs = append(allErrs, validateResourceQuantityValue(string(k), v)...) 1778 } 1779 for k, v := range resourceQuota.Status.Used { 1780 allErrs = append(allErrs, validateResourceName(string(k), string(resourceQuota.TypeMeta.Kind))...) 1781 allErrs = append(allErrs, validateResourceQuantityValue(string(k), v)...) 1782 } 1783 return allErrs 1784 } 1785 1786 // validateResourceQuantityValue enforces that specified quantity is valid for specified resource 1787 func validateResourceQuantityValue(resource string, value resource.Quantity) validation.ErrorList { 1788 allErrs := validation.ErrorList{} 1789 allErrs = append(allErrs, ValidatePositiveQuantity(value, resource)...) 1790 if api.IsIntegerResourceName(resource) { 1791 if value.MilliValue()%int64(1000) != int64(0) { 1792 allErrs = append(allErrs, validation.NewInvalidError(resource, value, isNotIntegerErrorMsg)) 1793 } 1794 } 1795 return allErrs 1796 } 1797 1798 // ValidateResourceQuotaUpdate tests to see if the update is legal for an end user to make. 1799 // newResourceQuota is updated with fields that cannot be changed. 1800 func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *api.ResourceQuota) validation.ErrorList { 1801 allErrs := validation.ErrorList{} 1802 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta).Prefix("metadata")...) 1803 for k, v := range newResourceQuota.Spec.Hard { 1804 allErrs = append(allErrs, validateResourceName(string(k), string(newResourceQuota.TypeMeta.Kind))...) 1805 allErrs = append(allErrs, validateResourceQuantityValue(string(k), v)...) 1806 } 1807 newResourceQuota.Status = oldResourceQuota.Status 1808 return allErrs 1809 } 1810 1811 // ValidateResourceQuotaStatusUpdate tests to see if the status update is legal for an end user to make. 1812 // newResourceQuota is updated with fields that cannot be changed. 1813 func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *api.ResourceQuota) validation.ErrorList { 1814 allErrs := validation.ErrorList{} 1815 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta).Prefix("metadata")...) 1816 if newResourceQuota.ResourceVersion == "" { 1817 allErrs = append(allErrs, validation.NewRequiredError("resourceVersion")) 1818 } 1819 for k, v := range newResourceQuota.Status.Hard { 1820 allErrs = append(allErrs, validateResourceName(string(k), string(newResourceQuota.TypeMeta.Kind))...) 1821 allErrs = append(allErrs, validateResourceQuantityValue(string(k), v)...) 1822 } 1823 for k, v := range newResourceQuota.Status.Used { 1824 allErrs = append(allErrs, validateResourceName(string(k), string(newResourceQuota.TypeMeta.Kind))...) 1825 allErrs = append(allErrs, validateResourceQuantityValue(string(k), v)...) 1826 } 1827 newResourceQuota.Spec = oldResourceQuota.Spec 1828 return allErrs 1829 } 1830 1831 // ValidateNamespace tests if required fields are set. 1832 func ValidateNamespace(namespace *api.Namespace) validation.ErrorList { 1833 allErrs := validation.ErrorList{} 1834 allErrs = append(allErrs, ValidateObjectMeta(&namespace.ObjectMeta, false, ValidateNamespaceName).Prefix("metadata")...) 1835 for i := range namespace.Spec.Finalizers { 1836 allErrs = append(allErrs, validateFinalizerName(string(namespace.Spec.Finalizers[i]))...) 1837 } 1838 return allErrs 1839 } 1840 1841 // Validate finalizer names 1842 func validateFinalizerName(stringValue string) validation.ErrorList { 1843 allErrs := validation.ErrorList{} 1844 if !validation.IsQualifiedName(stringValue) { 1845 return append(allErrs, validation.NewInvalidError("spec.finalizers", stringValue, qualifiedNameErrorMsg)) 1846 } 1847 1848 if len(strings.Split(stringValue, "/")) == 1 { 1849 if !api.IsStandardFinalizerName(stringValue) { 1850 return append(allErrs, validation.NewInvalidError("spec.finalizers", stringValue, fmt.Sprintf("finalizer name is neither a standard finalizer name nor is it fully qualified"))) 1851 } 1852 } 1853 1854 return validation.ErrorList{} 1855 } 1856 1857 // ValidateNamespaceUpdate tests to make sure a namespace update can be applied. 1858 // newNamespace is updated with fields that cannot be changed 1859 func ValidateNamespaceUpdate(newNamespace *api.Namespace, oldNamespace *api.Namespace) validation.ErrorList { 1860 allErrs := validation.ErrorList{} 1861 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta).Prefix("metadata")...) 1862 newNamespace.Spec.Finalizers = oldNamespace.Spec.Finalizers 1863 newNamespace.Status = oldNamespace.Status 1864 return allErrs 1865 } 1866 1867 // ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make. newNamespace is updated with fields 1868 // that cannot be changed. 1869 func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *api.Namespace) validation.ErrorList { 1870 allErrs := validation.ErrorList{} 1871 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta).Prefix("metadata")...) 1872 newNamespace.Spec = oldNamespace.Spec 1873 if newNamespace.DeletionTimestamp.IsZero() { 1874 if newNamespace.Status.Phase != api.NamespaceActive { 1875 allErrs = append(allErrs, validation.NewInvalidError("Status.Phase", newNamespace.Status.Phase, "A namespace may only be in active status if it does not have a deletion timestamp.")) 1876 } 1877 } else { 1878 if newNamespace.Status.Phase != api.NamespaceTerminating { 1879 allErrs = append(allErrs, validation.NewInvalidError("Status.Phase", newNamespace.Status.Phase, "A namespace may only be in terminating status if it has a deletion timestamp.")) 1880 } 1881 } 1882 return allErrs 1883 } 1884 1885 // ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make. 1886 // newNamespace is updated with fields that cannot be changed. 1887 func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *api.Namespace) validation.ErrorList { 1888 allErrs := validation.ErrorList{} 1889 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta).Prefix("metadata")...) 1890 for i := range newNamespace.Spec.Finalizers { 1891 allErrs = append(allErrs, validateFinalizerName(string(newNamespace.Spec.Finalizers[i]))...) 1892 } 1893 newNamespace.Status = oldNamespace.Status 1894 return allErrs 1895 } 1896 1897 // ValidateEndpoints tests if required fields are set. 1898 func ValidateEndpoints(endpoints *api.Endpoints) validation.ErrorList { 1899 allErrs := validation.ErrorList{} 1900 allErrs = append(allErrs, ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName).Prefix("metadata")...) 1901 allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets).Prefix("subsets")...) 1902 return allErrs 1903 } 1904 1905 func validateEndpointSubsets(subsets []api.EndpointSubset) validation.ErrorList { 1906 allErrs := validation.ErrorList{} 1907 1908 for i := range subsets { 1909 ss := &subsets[i] 1910 1911 ssErrs := validation.ErrorList{} 1912 1913 if len(ss.Addresses) == 0 && len(ss.NotReadyAddresses) == 0 { 1914 ssErrs = append(ssErrs, validation.NewRequiredError("addresses or notReadyAddresses")) 1915 } 1916 if len(ss.Ports) == 0 { 1917 ssErrs = append(ssErrs, validation.NewRequiredError("ports")) 1918 } 1919 for addr := range ss.Addresses { 1920 ssErrs = append(ssErrs, validateEndpointAddress(&ss.Addresses[addr]).PrefixIndex(addr).Prefix("addresses")...) 1921 } 1922 for port := range ss.Ports { 1923 ssErrs = append(ssErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1).PrefixIndex(port).Prefix("ports")...) 1924 } 1925 1926 allErrs = append(allErrs, ssErrs.PrefixIndex(i)...) 1927 } 1928 1929 return allErrs 1930 } 1931 1932 func validateEndpointAddress(address *api.EndpointAddress) validation.ErrorList { 1933 allErrs := validation.ErrorList{} 1934 if !validation.IsValidIPv4(address.IP) { 1935 allErrs = append(allErrs, validation.NewInvalidError("ip", address.IP, "invalid IPv4 address")) 1936 return allErrs 1937 } 1938 return validateIpIsNotLinkLocalOrLoopback(address.IP, "ip") 1939 } 1940 1941 func validateIpIsNotLinkLocalOrLoopback(ipAddress, fieldName string) validation.ErrorList { 1942 // We disallow some IPs as endpoints or external-ips. Specifically, loopback addresses are 1943 // nonsensical and link-local addresses tend to be used for node-centric purposes (e.g. metadata service). 1944 allErrs := validation.ErrorList{} 1945 ip := net.ParseIP(ipAddress) 1946 if ip == nil { 1947 allErrs = append(allErrs, validation.NewInvalidError(fieldName, ipAddress, "not a valid IP address")) 1948 return allErrs 1949 } 1950 if ip.IsLoopback() { 1951 allErrs = append(allErrs, validation.NewInvalidError(fieldName, ipAddress, "may not be in the loopback range (127.0.0.0/8)")) 1952 } 1953 if ip.IsLinkLocalUnicast() { 1954 allErrs = append(allErrs, validation.NewInvalidError(fieldName, ipAddress, "may not be in the link-local range (169.254.0.0/16)")) 1955 } 1956 if ip.IsLinkLocalMulticast() { 1957 allErrs = append(allErrs, validation.NewInvalidError(fieldName, ipAddress, "may not be in the link-local multicast range (224.0.0.0/24)")) 1958 } 1959 return allErrs 1960 } 1961 1962 func validateEndpointPort(port *api.EndpointPort, requireName bool) validation.ErrorList { 1963 allErrs := validation.ErrorList{} 1964 if requireName && port.Name == "" { 1965 allErrs = append(allErrs, validation.NewRequiredError("name")) 1966 } else if port.Name != "" { 1967 if !validation.IsDNS1123Label(port.Name) { 1968 allErrs = append(allErrs, validation.NewInvalidError("name", port.Name, DNS1123LabelErrorMsg)) 1969 } 1970 } 1971 if !validation.IsValidPortNum(port.Port) { 1972 allErrs = append(allErrs, validation.NewInvalidError("port", port.Port, PortRangeErrorMsg)) 1973 } 1974 if len(port.Protocol) == 0 { 1975 allErrs = append(allErrs, validation.NewRequiredError("protocol")) 1976 } else if !supportedPortProtocols.Has(string(port.Protocol)) { 1977 allErrs = append(allErrs, validation.NewNotSupportedError("protocol", port.Protocol, supportedPortProtocols.List())) 1978 } 1979 return allErrs 1980 } 1981 1982 // ValidateEndpointsUpdate tests to make sure an endpoints update can be applied. 1983 func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *api.Endpoints) validation.ErrorList { 1984 allErrs := validation.ErrorList{} 1985 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta).Prefix("metadata")...) 1986 allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets).Prefix("subsets")...) 1987 return allErrs 1988 } 1989 1990 // ValidateSecurityContext ensure the security context contains valid settings 1991 func ValidateSecurityContext(sc *api.SecurityContext) validation.ErrorList { 1992 allErrs := validation.ErrorList{} 1993 //this should only be true for testing since SecurityContext is defaulted by the api 1994 if sc == nil { 1995 return allErrs 1996 } 1997 1998 if sc.Privileged != nil { 1999 if *sc.Privileged && !capabilities.Get().AllowPrivileged { 2000 allErrs = append(allErrs, validation.NewForbiddenError("privileged", sc.Privileged)) 2001 } 2002 } 2003 2004 if sc.RunAsUser != nil { 2005 if *sc.RunAsUser < 0 { 2006 allErrs = append(allErrs, validation.NewInvalidError("runAsUser", *sc.RunAsUser, "runAsUser cannot be negative")) 2007 } 2008 } 2009 return allErrs 2010 } 2011 2012 func ValidatePodLogOptions(opts *api.PodLogOptions) validation.ErrorList { 2013 allErrs := validation.ErrorList{} 2014 if opts.TailLines != nil && *opts.TailLines < 0 { 2015 allErrs = append(allErrs, validation.NewInvalidError("tailLines", *opts.TailLines, "tailLines must be a non-negative integer or nil")) 2016 } 2017 if opts.LimitBytes != nil && *opts.LimitBytes < 1 { 2018 allErrs = append(allErrs, validation.NewInvalidError("limitBytes", *opts.LimitBytes, "limitBytes must be a positive integer or nil")) 2019 } 2020 switch { 2021 case opts.SinceSeconds != nil && opts.SinceTime != nil: 2022 allErrs = append(allErrs, validation.NewInvalidError("sinceSeconds", *opts.SinceSeconds, "only one of sinceTime or sinceSeconds can be provided")) 2023 allErrs = append(allErrs, validation.NewInvalidError("sinceTime", *opts.SinceTime, "only one of sinceTime or sinceSeconds can be provided")) 2024 case opts.SinceSeconds != nil: 2025 if *opts.SinceSeconds < 1 { 2026 allErrs = append(allErrs, validation.NewInvalidError("sinceSeconds", *opts.SinceSeconds, "sinceSeconds must be a positive integer")) 2027 } 2028 } 2029 return allErrs 2030 } 2031 2032 // ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus 2033 func ValidateLoadBalancerStatus(status *api.LoadBalancerStatus) validation.ErrorList { 2034 allErrs := validation.ErrorList{} 2035 for _, ingress := range status.Ingress { 2036 if len(ingress.IP) > 0 { 2037 if isIP := (net.ParseIP(ingress.IP) != nil); !isIP { 2038 allErrs = append(allErrs, validation.NewInvalidError("ingress.ip", ingress.IP, "must be an IP address")) 2039 } 2040 } 2041 if len(ingress.Hostname) > 0 { 2042 if valid, errMsg := NameIsDNSSubdomain(ingress.Hostname, false); !valid { 2043 allErrs = append(allErrs, validation.NewInvalidError("ingress.hostname", ingress.Hostname, errMsg)) 2044 } 2045 if isIP := (net.ParseIP(ingress.Hostname) != nil); isIP { 2046 allErrs = append(allErrs, validation.NewInvalidError("ingress.hostname", ingress.Hostname, "must be a DNS name, not an IP address")) 2047 } 2048 } 2049 } 2050 return allErrs 2051 }