func filterInvalidPods(pods []*api.Pod, source string, recorder record.EventRecorder) (filtered []*api.Pod) { names := sets.String{} for i, pod := range pods { var errlist field.ErrorList if errs := validation.ValidatePod(pod); len(errs) != 0 { errlist = append(errlist, errs...) // If validation fails, don't trust it any further - // even Name could be bad. } else { name := kubecontainer.GetPodFullName(pod) if names.Has(name) { // TODO: when validation becomes versioned, this gets a bit // more complicated. errlist = append(errlist, field.Duplicate(field.NewPath("metadata", "name"), pod.Name)) } else { names.Insert(name) } } if len(errlist) > 0 { name := bestPodIdentString(pod) err := errlist.ToAggregate() glog.Warningf("Pod[%d] (%s) from %s failed validation, ignoring: %v", i+1, name, source, err) recorder.Eventf(pod, api.EventTypeWarning, kubecontainer.FailedValidation, "Error validating pod %s from %s, ignoring: %v", name, source, err) continue } filtered = append(filtered, pod) } return }
// NewInvalid returns an error indicating the item is invalid and cannot be processed. func NewInvalid(kind, name string, errs field.ErrorList) error { causes := make([]unversioned.StatusCause, 0, len(errs)) for i := range errs { err := errs[i] causes = append(causes, unversioned.StatusCause{ Type: unversioned.CauseType(err.Type), Message: err.ErrorBody(), Field: err.Field, }) } return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: StatusUnprocessableEntity, // RFC 4918: StatusUnprocessableEntity Reason: unversioned.StatusReasonInvalid, Details: &unversioned.StatusDetails{ Kind: kind, Name: name, Causes: causes, }, Message: fmt.Sprintf("%s %q is invalid: %v", kind, name, errs.ToAggregate()), }} }
// PodConstraintsFunc verifies that all required resources are present on the pod // In addition, it validates that the resources are valid (i.e. requests < limits) func PodConstraintsFunc(required []api.ResourceName, object runtime.Object) error { pod, ok := object.(*api.Pod) if !ok { return fmt.Errorf("Unexpected input object %v", object) } // 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 { idxPath := fldPath.Index(i) allErrs = append(allErrs, validation.ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"))...) } if len(allErrs) > 0 { return allErrs.ToAggregate() } // TODO: fix this when we have pod level cgroups // 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 { requests := pod.Spec.Containers[i].Resources.Requests limits := pod.Spec.Containers[i].Resources.Limits containerUsage := podUsageHelper(requests, limits) containerSet := quota.ToSet(quota.ResourceNames(containerUsage)) if !containerSet.Equal(requiredSet) { difference := requiredSet.Difference(containerSet) missingSet.Insert(difference.List()...) } } if len(missingSet) == 0 { return nil } return fmt.Errorf("must specify %s", strings.Join(missingSet.List(), ",")) }
// 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(), ",")) }
// NewInvalid returns an error indicating the item is invalid and cannot be processed. func NewInvalid(qualifiedKind schema.GroupKind, name string, errs field.ErrorList) *StatusError { causes := make([]metav1.StatusCause, 0, len(errs)) for i := range errs { err := errs[i] causes = append(causes, metav1.StatusCause{ Type: metav1.CauseType(err.Type), Message: err.ErrorBody(), Field: err.Field, }) } return &StatusError{metav1.Status{ Status: metav1.StatusFailure, Code: StatusUnprocessableEntity, // RFC 4918: StatusUnprocessableEntity Reason: metav1.StatusReasonInvalid, Details: &metav1.StatusDetails{ Group: qualifiedKind.Group, Kind: qualifiedKind.Kind, Name: name, Causes: causes, }, Message: fmt.Sprintf("%s %q is invalid: %v", qualifiedKind.String(), name, errs.ToAggregate()), }} }