// ValidateStorageClass validates a StorageClass. func ValidateStorageClass(storageClass *storage.StorageClass) field.ErrorList { allErrs := apivalidation.ValidateObjectMeta(&storageClass.ObjectMeta, false, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata")) allErrs = append(allErrs, validateProvisioner(storageClass.Provisioner, field.NewPath("provisioner"))...) allErrs = append(allErrs, validateParameters(storageClass.Parameters, field.NewPath("parameters"))...) return allErrs }
// Validate ensures that the specified values fall within the range of the strategy. func (s *mustRunAs) Validate(pod *api.Pod, container *api.Container) field.ErrorList { allErrs := field.ErrorList{} if container.SecurityContext == nil { detail := fmt.Sprintf("unable to validate nil security context for %s", container.Name) allErrs = append(allErrs, field.Invalid(field.NewPath("securityContext"), container.SecurityContext, detail)) return allErrs } if container.SecurityContext.SELinuxOptions == nil { detail := fmt.Sprintf("unable to validate nil seLinuxOptions for %s", container.Name) allErrs = append(allErrs, field.Invalid(field.NewPath("seLinuxOptions"), container.SecurityContext.SELinuxOptions, detail)) return allErrs } seLinuxOptionsPath := field.NewPath("seLinuxOptions") seLinux := container.SecurityContext.SELinuxOptions if seLinux.Level != s.opts.SELinuxOptions.Level { detail := fmt.Sprintf("seLinuxOptions.level on %s does not match required level. Found %s, wanted %s", container.Name, seLinux.Level, s.opts.SELinuxOptions.Level) allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("level"), seLinux.Level, detail)) } if seLinux.Role != s.opts.SELinuxOptions.Role { detail := fmt.Sprintf("seLinuxOptions.role on %s does not match required role. Found %s, wanted %s", container.Name, seLinux.Role, s.opts.SELinuxOptions.Role) allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("role"), seLinux.Role, detail)) } if seLinux.Type != s.opts.SELinuxOptions.Type { detail := fmt.Sprintf("seLinuxOptions.type on %s does not match required type. Found %s, wanted %s", container.Name, seLinux.Type, s.opts.SELinuxOptions.Type) allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("type"), seLinux.Type, detail)) } if seLinux.User != s.opts.SELinuxOptions.User { detail := fmt.Sprintf("seLinuxOptions.user on %s does not match required user. Found %s, wanted %s", container.Name, seLinux.User, s.opts.SELinuxOptions.User) allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("user"), seLinux.User, detail)) } return allErrs }
// ValidateEvent makes sure that the event makes sense. func ValidateEvent(event *api.Event) field.ErrorList { allErrs := field.ErrorList{} // Make sure event.Namespace and the involvedObject.Namespace agree if len(event.InvolvedObject.Namespace) == 0 { // event.Namespace must also be empty (or "default", for compatibility with old clients) if event.Namespace != api.NamespaceNone && event.Namespace != api.NamespaceDefault { allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace")) } } else { // event namespace must match if event.Namespace != event.InvolvedObject.Namespace { allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace")) } } // For kinds we recognize, make sure involvedObject.Namespace is set for namespaced kinds if namespaced, err := isNamespacedKind(event.InvolvedObject.Kind, event.InvolvedObject.APIVersion); err == nil { if namespaced && len(event.InvolvedObject.Namespace) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("involvedObject", "namespace"), fmt.Sprintf("required for kind %s", event.InvolvedObject.Kind))) } if !namespaced && len(event.InvolvedObject.Namespace) > 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, fmt.Sprintf("not allowed for kind %s", event.InvolvedObject.Kind))) } } for _, msg := range validation.IsDNS1123Subdomain(event.Namespace) { allErrs = append(allErrs, field.Invalid(field.NewPath("namespace"), event.Namespace, msg)) } return allErrs }
func ValidateSelfSubjectAccessReview(sar *authorizationapi.SelfSubjectAccessReview) field.ErrorList { allErrs := ValidateSelfSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec")) if !api.Semantic.DeepEqual(metav1.ObjectMeta{}, sar.ObjectMeta) { allErrs = append(allErrs, field.Invalid(field.NewPath("metadata"), sar.ObjectMeta, `must be empty`)) } return allErrs }
// Ensure namespace names follow dns label format func TestValidateObjectMetaNamespaces(t *testing.T) { errs := ValidateObjectMeta( &metav1.ObjectMeta{Name: "test", Namespace: "foo.bar"}, true, func(s string, prefix bool) []string { return nil }, field.NewPath("field")) if len(errs) != 1 { t.Fatalf("unexpected errors: %v", errs) } if !strings.Contains(errs[0].Error(), `Invalid value: "foo.bar"`) { t.Errorf("unexpected error message: %v", errs) } maxLength := 63 letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") b := make([]rune, maxLength+1) for i := range b { b[i] = letters[rand.Intn(len(letters))] } errs = ValidateObjectMeta( &metav1.ObjectMeta{Name: "test", Namespace: string(b)}, true, func(s string, prefix bool) []string { return nil }, field.NewPath("field")) if len(errs) != 2 { t.Fatalf("unexpected errors: %v", errs) } if !strings.Contains(errs[0].Error(), "Invalid value") || !strings.Contains(errs[1].Error(), "Invalid value") { t.Errorf("unexpected error message: %v", errs) } }
// Ensure a pod's SecurityContext is in compliance with the given constraints. func (s *simpleProvider) ValidatePodSecurityContext(pod *api.Pod, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if pod.Spec.SecurityContext == nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("securityContext"), pod.Spec.SecurityContext, "No security context is set")) return allErrs } fsGroups := []int64{} if pod.Spec.SecurityContext.FSGroup != nil { fsGroups = append(fsGroups, *pod.Spec.SecurityContext.FSGroup) } allErrs = append(allErrs, s.strategies.FSGroupStrategy.Validate(pod, fsGroups)...) allErrs = append(allErrs, s.strategies.SupplementalGroupStrategy.Validate(pod, pod.Spec.SecurityContext.SupplementalGroups)...) allErrs = append(allErrs, s.strategies.SeccompStrategy.ValidatePod(pod)...) // make a dummy container context to reuse the selinux strategies container := &api.Container{ Name: pod.Name, SecurityContext: &api.SecurityContext{ SELinuxOptions: pod.Spec.SecurityContext.SELinuxOptions, }, } allErrs = append(allErrs, s.strategies.SELinuxStrategy.Validate(pod, container)...) if !s.psp.Spec.HostNetwork && pod.Spec.SecurityContext.HostNetwork { allErrs = append(allErrs, field.Invalid(fldPath.Child("hostNetwork"), pod.Spec.SecurityContext.HostNetwork, "Host network is not allowed to be used")) } if !s.psp.Spec.HostPID && pod.Spec.SecurityContext.HostPID { allErrs = append(allErrs, field.Invalid(fldPath.Child("hostPID"), pod.Spec.SecurityContext.HostPID, "Host PID is not allowed to be used")) } if !s.psp.Spec.HostIPC && pod.Spec.SecurityContext.HostIPC { allErrs = append(allErrs, field.Invalid(fldPath.Child("hostIPC"), pod.Spec.SecurityContext.HostIPC, "Host IPC is not allowed to be used")) } allErrs = append(allErrs, s.strategies.SysctlsStrategy.Validate(pod)...) // TODO(timstclair): ValidatePodSecurityContext should be renamed to ValidatePod since its scope // is not limited to the PodSecurityContext. if len(pod.Spec.Volumes) > 0 && !psputil.PSPAllowsAllVolumes(s.psp) { allowedVolumes := psputil.FSTypeToStringSet(s.psp.Spec.Volumes) for i, v := range pod.Spec.Volumes { fsType, err := psputil.GetVolumeFSType(v) if err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "volumes").Index(i), string(fsType), err.Error())) continue } if !allowedVolumes.Has(string(fsType)) { allErrs = append(allErrs, field.Invalid( field.NewPath("spec", "volumes").Index(i), string(fsType), fmt.Sprintf("%s volumes are not allowed to be used", string(fsType)))) } } } return allErrs }
func ValidateClusterRoleBinding(roleBinding *rbac.ClusterRoleBinding) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, false, minimalNameRequirements, field.NewPath("metadata"))...) // TODO allow multiple API groups. For now, restrict to one, but I can envision other experimental roles in other groups taking // advantage of the binding infrastructure if roleBinding.RoleRef.APIGroup != rbac.GroupName { allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName})) } switch roleBinding.RoleRef.Kind { case "ClusterRole": default: allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"ClusterRole"})) } if len(roleBinding.RoleRef.Name) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), "")) } else { for _, msg := range minimalNameRequirements(roleBinding.RoleRef.Name, false) { allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg)) } } subjectsPath := field.NewPath("subjects") for i, subject := range roleBinding.Subjects { allErrs = append(allErrs, validateRoleBindingSubject(subject, false, subjectsPath.Index(i))...) } return allErrs }
func ValidateDeploymentRollback(obj *extensions.DeploymentRollback) field.ErrorList { allErrs := apivalidation.ValidateAnnotations(obj.UpdatedAnnotations, field.NewPath("updatedAnnotations")) if len(obj.Name) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("name"), "name is required")) } allErrs = append(allErrs, ValidateRollback(&obj.RollbackTo, field.NewPath("rollback"))...) return allErrs }
// TestValidateAllowedVolumes will test that for every field of VolumeSource we can create // a pod with that type of volume and deny it, accept it explicitly, or accept it with // the FSTypeAll wildcard. func TestValidateAllowedVolumes(t *testing.T) { val := reflect.ValueOf(api.VolumeSource{}) for i := 0; i < val.NumField(); i++ { // reflectively create the volume source fieldVal := val.Type().Field(i) volumeSource := api.VolumeSource{} volumeSourceVolume := reflect.New(fieldVal.Type.Elem()) reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume) volume := api.Volume{VolumeSource: volumeSource} // sanity check before moving on fsType, err := psputil.GetVolumeFSType(volume) if err != nil { t.Errorf("error getting FSType for %s: %s", fieldVal.Name, err.Error()) continue } // add the volume to the pod pod := defaultPod() pod.Spec.Volumes = []api.Volume{volume} // create a PSP that allows no volumes psp := defaultPSP() provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory()) if err != nil { t.Errorf("error creating provider for %s: %s", fieldVal.Name, err.Error()) continue } // expect a denial for this PSP and test the error message to ensure it's related to the volumesource errs := provider.ValidatePodSecurityContext(pod, field.NewPath("")) if len(errs) != 1 { t.Errorf("expected exactly 1 error for %s but got %v", fieldVal.Name, errs) } else { if !strings.Contains(errs.ToAggregate().Error(), fmt.Sprintf("%s volumes are not allowed to be used", fsType)) { t.Errorf("did not find the expected error, received: %v", errs) } } // now add the fstype directly to the psp and it should validate psp.Spec.Volumes = []extensions.FSType{fsType} errs = provider.ValidatePodSecurityContext(pod, field.NewPath("")) if len(errs) != 0 { t.Errorf("directly allowing volume expected no errors for %s but got %v", fieldVal.Name, errs) } // now change the psp to allow any volumes and the pod should still validate psp.Spec.Volumes = []extensions.FSType{extensions.All} errs = provider.ValidatePodSecurityContext(pod, field.NewPath("")) if len(errs) != 0 { t.Errorf("wildcard volume expected no errors for %s but got %v", fieldVal.Name, errs) } } }
// ValidateStorageClassUpdate tests if an update to StorageClass is valid. func ValidateStorageClassUpdate(storageClass, oldStorageClass *storage.StorageClass) field.ErrorList { allErrs := apivalidation.ValidateObjectMetaUpdate(&storageClass.ObjectMeta, &oldStorageClass.ObjectMeta, field.NewPath("metadata")) if !reflect.DeepEqual(oldStorageClass.Parameters, storageClass.Parameters) { allErrs = append(allErrs, field.Forbidden(field.NewPath("parameters"), "updates to parameters are forbidden.")) } if strings.Compare(storageClass.Provisioner, oldStorageClass.Provisioner) != 0 { allErrs = append(allErrs, field.Forbidden(field.NewPath("provisioner"), "updates to provisioner are forbidden.")) } return allErrs }
func ValidatePodDisruptionBudgetUpdate(pdb, oldPdb *policy.PodDisruptionBudget) field.ErrorList { allErrs := field.ErrorList{} restoreGeneration := pdb.Generation pdb.Generation = oldPdb.Generation if !reflect.DeepEqual(pdb, oldPdb) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to poddisruptionbudget spec are forbidden.")) } allErrs = append(allErrs, ValidatePodDisruptionBudgetStatus(pdb.Status, field.NewPath("status"))...) pdb.Generation = restoreGeneration return allErrs }
// Validate ensures that the specified values fall within the range of the strategy. func (s *defaultCapabilities) Validate(pod *api.Pod, container *api.Container) field.ErrorList { allErrs := field.ErrorList{} // if the security context isn't set then we haven't generated correctly. Shouldn't get here // if using the provider correctly if container.SecurityContext == nil { allErrs = append(allErrs, field.Invalid(field.NewPath("securityContext"), container.SecurityContext, "no security context is set")) return allErrs } if container.SecurityContext.Capabilities == nil { // if container.SC.Caps is nil then nothing was defaulted by the strat or requested by the pod author // if there are no required caps on the strategy and nothing is requested on the pod // then we can safely return here without further validation. if len(s.defaultAddCapabilities) == 0 && len(s.requiredDropCapabilities) == 0 { return allErrs } // container has no requested caps but we have required caps. We should have something in // at least the drops on the container. allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities"), container.SecurityContext.Capabilities, "required capabilities are not set on the securityContext")) return allErrs } // validate that anything being added is in the default or allowed sets defaultAdd := makeCapSet(s.defaultAddCapabilities) allowedAdd := makeCapSet(s.allowedCaps) for _, cap := range container.SecurityContext.Capabilities.Add { sCap := string(cap) if !defaultAdd.Has(sCap) && !allowedAdd.Has(sCap) { allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities", "add"), sCap, "capability may not be added")) } } // validate that anything that is required to be dropped is in the drop set containerDrops := makeCapSet(container.SecurityContext.Capabilities.Drop) for _, requiredDrop := range s.requiredDropCapabilities { sDrop := string(requiredDrop) if !containerDrops.Has(sDrop) { allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities", "drop"), container.SecurityContext.Capabilities.Drop, fmt.Sprintf("%s is required to be dropped but was not found", sDrop))) } } return allErrs }
func ValidateJob(job *batch.Job) field.ErrorList { // Jobs and rcs have the same name validation allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata")) allErrs = append(allErrs, ValidateGeneratedSelector(job)...) allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"))...) return allErrs }
// ValidatePodSecurityPolicyUpdate validates a PSP for updates. func ValidatePodSecurityPolicyUpdate(old *extensions.PodSecurityPolicy, new *extensions.PodSecurityPolicy) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&new.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...) allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(new.Annotations, field.NewPath("metadata").Child("annotations"))...) allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, field.NewPath("spec"))...) return allErrs }
func ValidatePodSecurityPolicy(psp *extensions.PodSecurityPolicy) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...) allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(psp.Annotations, field.NewPath("metadata").Child("annotations"))...) allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...) return allErrs }
func (s *strategy) Validate(pod *api.Pod, container *api.Container) field.ErrorList { if s.allowedProfiles == nil { // Unrestricted: allow all. return nil } allErrs := field.ErrorList{} fieldPath := field.NewPath("pod", "metadata", "annotations").Key(apparmor.ContainerAnnotationKeyPrefix + container.Name) profile := apparmor.GetProfileNameFromPodAnnotations(pod.Annotations, container.Name) if profile == "" { if len(s.allowedProfiles) > 0 { allErrs = append(allErrs, field.Forbidden(fieldPath, "AppArmor profile must be set")) return allErrs } return nil } if !s.allowedProfiles[profile] { msg := fmt.Sprintf("%s is not an allowed profile. Allowed values: %q", profile, s.allowedProfilesString) allErrs = append(allErrs, field.Forbidden(fieldPath, msg)) } return allErrs }
func TestValidateSelfSAR(t *testing.T) { successCases := []authorizationapi.SelfSubjectAccessReviewSpec{ {ResourceAttributes: &authorizationapi.ResourceAttributes{}}, } for _, successCase := range successCases { if errs := ValidateSelfSubjectAccessReviewSpec(successCase, field.NewPath("spec")); len(errs) != 0 { t.Errorf("expected success: %v", errs) } } errorCases := []struct { name string obj authorizationapi.SelfSubjectAccessReviewSpec msg string }{ { name: "neither request", obj: authorizationapi.SelfSubjectAccessReviewSpec{}, msg: "exactly one of nonResourceAttributes or resourceAttributes must be specified", }, { name: "both requests", obj: authorizationapi.SelfSubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{}, NonResourceAttributes: &authorizationapi.NonResourceAttributes{}, }, msg: "cannot be specified in combination with resourceAttributes", }, } for _, c := range errorCases { errs := ValidateSelfSubjectAccessReviewSpec(c.obj, field.NewPath("spec")) if len(errs) == 0 { t.Errorf("%s: expected failure for %q", c.name, c.msg) } else if !strings.Contains(errs[0].Error(), c.msg) { t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg) } errs = ValidateSelfSubjectAccessReview(&authorizationapi.SelfSubjectAccessReview{Spec: c.obj}) if len(errs) == 0 { t.Errorf("%s: expected failure for %q", c.name, c.msg) } else if !strings.Contains(errs[0].Error(), c.msg) { t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg) } } }
func Convert_unversioned_LabelSelector_to_map(in *metav1.LabelSelector, out *map[string]string, s conversion.Scope) error { var err error *out, err = metav1.LabelSelectorAsMap(in) if err != nil { err = field.Invalid(field.NewPath("labelSelector"), *in, fmt.Sprintf("cannot convert to old selector: %v", err)) } return err }
// ValidateNetworkPolicyUpdate tests if an update to a NetworkPolicy is valid. func ValidateNetworkPolicyUpdate(update, old *extensions.NetworkPolicy) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...) if !reflect.DeepEqual(update.Spec, old.Spec) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to networkpolicy spec are forbidden.")) } return allErrs }
func ValidateClusterUpdate(cluster, oldCluster *federation.Cluster) field.ErrorList { allErrs := validation.ValidateObjectMetaUpdate(&cluster.ObjectMeta, &oldCluster.ObjectMeta, field.NewPath("metadata")) if cluster.Name != oldCluster.Name { allErrs = append(allErrs, field.Invalid(field.NewPath("meta", "name"), cluster.Name+" != "+oldCluster.Name, "cannot change cluster name")) } return allErrs }
func ValidatePodLogOptions(opts *v1.PodLogOptions) field.ErrorList { allErrs := field.ErrorList{} if opts.TailLines != nil && *opts.TailLines < 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("tailLines"), *opts.TailLines, isNegativeErrorMsg)) } if opts.LimitBytes != nil && *opts.LimitBytes < 1 { allErrs = append(allErrs, field.Invalid(field.NewPath("limitBytes"), *opts.LimitBytes, "must be greater than 0")) } switch { case opts.SinceSeconds != nil && opts.SinceTime != nil: allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "at most one of `sinceTime` or `sinceSeconds` may be specified")) case opts.SinceSeconds != nil: if *opts.SinceSeconds < 1 { allErrs = append(allErrs, field.Invalid(field.NewPath("sinceSeconds"), *opts.SinceSeconds, "must be greater than 0")) } } return allErrs }
func ValidateLocalSubjectAccessReview(sar *authorizationapi.LocalSubjectAccessReview) field.ErrorList { allErrs := ValidateSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec")) objectMetaShallowCopy := sar.ObjectMeta objectMetaShallowCopy.Namespace = "" if !api.Semantic.DeepEqual(metav1.ObjectMeta{}, objectMetaShallowCopy) { allErrs = append(allErrs, field.Invalid(field.NewPath("metadata"), sar.ObjectMeta, `must be empty except for namespace`)) } if sar.Spec.ResourceAttributes != nil && sar.Spec.ResourceAttributes.Namespace != sar.Namespace { allErrs = append(allErrs, field.Invalid(field.NewPath("spec.resourceAttributes.namespace"), sar.Spec.ResourceAttributes.Namespace, `must match metadata.namespace`)) } if sar.Spec.NonResourceAttributes != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec.nonResourceAttributes"), sar.Spec.NonResourceAttributes, `disallowed on this kind of request`)) } return allErrs }
// Ensure trailing slash is allowed in generate name func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) { errs := ValidateObjectMeta( &metav1.ObjectMeta{Name: "test", GenerateName: "foo-"}, false, apimachineryvalidation.NameIsDNSSubdomain, field.NewPath("field")) if len(errs) != 0 { t.Fatalf("unexpected errors: %v", errs) } }
func ValidateScale(scale *extensions.Scale) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&scale.ObjectMeta, true, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata"))...) if scale.Spec.Replicas < 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "replicas"), scale.Spec.Replicas, "must be greater than or equal to 0")) } return allErrs }
func ValidateClusterRoleBindingUpdate(roleBinding *rbac.ClusterRoleBinding, oldRoleBinding *rbac.ClusterRoleBinding) field.ErrorList { allErrs := ValidateClusterRoleBinding(roleBinding) allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...) if oldRoleBinding.RoleRef != roleBinding.RoleRef { allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef"), roleBinding.RoleRef, "cannot change roleRef")) } return allErrs }
// Validate ensures that the specified values fall within the range of the strategy. // Groups are passed in here to allow this strategy to support multiple group fields (fsgroup and // supplemental groups). func (s *mustRunAs) Validate(pod *api.Pod, groups []int64) field.ErrorList { allErrs := field.ErrorList{} if pod.Spec.SecurityContext == nil { allErrs = append(allErrs, field.Invalid(field.NewPath("securityContext"), pod.Spec.SecurityContext, "unable to validate nil security context")) return allErrs } if len(groups) == 0 && len(s.ranges) > 0 { allErrs = append(allErrs, field.Invalid(field.NewPath(s.field), groups, "unable to validate empty groups against required ranges")) } for _, group := range groups { if !s.isGroupValid(group) { detail := fmt.Sprintf("%d is not an allowed group", group) allErrs = append(allErrs, field.Invalid(field.NewPath(s.field), groups, detail)) } } return allErrs }
// TODO: generalize for other controller objects that will follow the same pattern, such as ReplicaSet and DaemonSet, and // move to new location. Replace batch.Job with an interface. // // ValidateGeneratedSelector validates that the generated selector on a controller object match the controller object // metadata, and the labels on the pod template are as generated. func ValidateGeneratedSelector(obj *batch.Job) field.ErrorList { allErrs := field.ErrorList{} if obj.Spec.ManualSelector != nil && *obj.Spec.ManualSelector { return allErrs } if obj.Spec.Selector == nil { return allErrs // This case should already have been checked in caller. No need for more errors. } // If somehow uid was unset then we would get "controller-uid=" as the selector // which is bad. if obj.ObjectMeta.UID == "" { allErrs = append(allErrs, field.Required(field.NewPath("metadata").Child("uid"), "")) } // If selector generation was requested, then expected labels must be // present on pod template, and must match job's uid and name. The // generated (not-manual) selectors/labels ensure no overlap with other // controllers. The manual mode allows orphaning, adoption, // backward-compatibility, and experimentation with new // labeling/selection schemes. Automatic selector generation should // have placed certain labels on the pod, but this could have failed if // the user added coflicting labels. Validate that the expected // generated ones are there. allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "controller-uid", string(obj.UID))...) allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "job-name", string(obj.Name))...) expectedLabels := make(map[string]string) expectedLabels["controller-uid"] = string(obj.UID) expectedLabels["job-name"] = string(obj.Name) // Whether manually or automatically generated, the selector of the job must match the pods it will produce. if selector, err := metav1.LabelSelectorAsSelector(obj.Spec.Selector); err == nil { if !selector.Matches(labels.Set(expectedLabels)) { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("selector"), obj.Spec.Selector, "`selector` not auto-generated")) } } return allErrs }
func ValidateAPIService(apiServer *discoveryapi.APIService) field.ErrorList { requiredName := apiServer.Spec.Version + "." + apiServer.Spec.Group allErrs := validation.ValidateObjectMeta(&apiServer.ObjectMeta, false, func(name string, prefix bool) []string { if minimalFailures := path.IsValidPathSegmentName(name); len(minimalFailures) > 0 { return minimalFailures } // the name *must* be version.group if name != requiredName { return []string{fmt.Sprintf("must be `spec.version+\".\"+spec.group`: %q", requiredName)} } return []string{} }, field.NewPath("metadata")) // in this case we allow empty group if len(apiServer.Spec.Group) == 0 && apiServer.Spec.Version != "v1" { allErrs = append(allErrs, field.Required(field.NewPath("spec", "group"), "only v1 may have an empty group and it better be legacy kube")) } if len(apiServer.Spec.Group) > 0 { for _, errString := range utilvalidation.IsDNS1123Subdomain(apiServer.Spec.Group) { allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "group"), apiServer.Spec.Group, errString)) } } for _, errString := range utilvalidation.IsDNS1035Label(apiServer.Spec.Version) { allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "version"), apiServer.Spec.Version, errString)) } if apiServer.Spec.Priority <= 0 || apiServer.Spec.Priority > 1000 { allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "priority"), apiServer.Spec.Priority, "priority must be positive and less than 1000")) } if len(apiServer.Spec.Service.Namespace) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("spec", "service", "namespace"), "")) } if len(apiServer.Spec.Service.Name) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("spec", "service", "name"), "")) } if apiServer.Spec.InsecureSkipTLSVerify && len(apiServer.Spec.CABundle) > 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "insecureSkipTLSVerify"), apiServer.Spec.InsecureSkipTLSVerify, "may not be true if caBundle is present")) } return allErrs }
// Constraints verifies that all required resources are present on the pod // In addition, it validates that the resources are valid (i.e. requests < limits) func (p *podEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error { pod, ok := item.(*api.Pod) if !ok { return fmt.Errorf("Unexpected input object %v", item) } // Pod level resources are often set during admission control // As a consequence, we want to verify that resources are valid prior // to ever charging quota prematurely in case they are not. allErrs := field.ErrorList{} fldPath := field.NewPath("spec").Child("containers") for i, ctr := range pod.Spec.Containers { allErrs = append(allErrs, validation.ValidateResourceRequirements(&ctr.Resources, fldPath.Index(i).Child("resources"))...) } fldPath = field.NewPath("spec").Child("initContainers") for i, ctr := range pod.Spec.InitContainers { allErrs = append(allErrs, validation.ValidateResourceRequirements(&ctr.Resources, fldPath.Index(i).Child("resources"))...) } if len(allErrs) > 0 { return allErrs.ToAggregate() } // TODO: fix this when we have pod level resource requirements // since we do not yet pod level requests/limits, we need to ensure each // container makes an explict request or limit for a quota tracked resource requiredSet := quota.ToSet(required) missingSet := sets.NewString() for i := range pod.Spec.Containers { enforcePodContainerConstraints(&pod.Spec.Containers[i], requiredSet, missingSet) } for i := range pod.Spec.InitContainers { enforcePodContainerConstraints(&pod.Spec.InitContainers[i], requiredSet, missingSet) } if len(missingSet) == 0 { return nil } return fmt.Errorf("must specify %s", strings.Join(missingSet.List(), ",")) }
// ParseWatchResourceVersion takes a resource version argument and converts it to // the etcd version we should pass to helper.Watch(). Because resourceVersion is // an opaque value, the default watch behavior for non-zero watch is to watch // the next value (if you pass "1", you will see updates from "2" onwards). func ParseWatchResourceVersion(resourceVersion string) (uint64, error) { if resourceVersion == "" || resourceVersion == "0" { return 0, nil } version, err := strconv.ParseUint(resourceVersion, 10, 64) if err != nil { return 0, NewInvalidError(field.ErrorList{ // Validation errors are supposed to return version-specific field // paths, but this is probably close enough. field.Invalid(field.NewPath("resourceVersion"), resourceVersion, err.Error()), }) } return version, nil }