func validateRoleBinding(roleBinding *rbac.RoleBinding, isNamespaced bool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, isNamespaced, minimalNameRequirements, fldPath.Child("metadata"))...) // roleRef namespace is empty when referring to global policy. if len(roleBinding.RoleRef.Namespace) > 0 { if ok, reason := validation.ValidateNamespaceName(roleBinding.RoleRef.Namespace, false); !ok { allErrs = append(allErrs, field.Invalid(fldPath.Child("roleRef", "namespace"), roleBinding.RoleRef.Namespace, reason)) } } if len(roleBinding.RoleRef.Name) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("roleRef", "name"), "")) } else { if valid, err := minimalNameRequirements(roleBinding.RoleRef.Name, false); !valid { allErrs = append(allErrs, field.Invalid(fldPath.Child("roleRef", "name"), roleBinding.RoleRef.Name, err)) } } subjectsPath := field.NewPath("subjects") for i, subject := range roleBinding.Subjects { allErrs = append(allErrs, validateRoleBindingSubject(subject, isNamespaced, subjectsPath.Index(i))...) } return allErrs }
// GenerateParameterValues generates Value for each Parameter of the given // Template that has Generate field specified where Value is not already // supplied. // // Examples: // // from | value // ----------------------------- // "test[0-9]{1}x" | "test7x" // "[0-1]{8}" | "01001100" // "0x[A-F0-9]{4}" | "0xB3AF" // "[a-zA-Z0-9]{8}" | "hW4yQU5i" // If an error occurs, the parameter that caused the error is returned along with the error message. func (p *Processor) GenerateParameterValues(t *api.Template) *field.Error { for i := range t.Parameters { param := &t.Parameters[i] if len(param.Value) > 0 { continue } templatePath := field.NewPath("template").Child("parameters").Index(i) if param.Generate != "" { generator, ok := p.Generators[param.Generate] if !ok { return field.NotFound(templatePath, param) } if generator == nil { err := fmt.Errorf("template.parameters[%v]: Invalid '%v' generator for parameter %s", i, param.Generate, param.Name) return field.Invalid(templatePath, param, err.Error()) } value, err := generator.GenerateValue(param.From) if err != nil { return field.Invalid(templatePath, param, err.Error()) } param.Value, ok = value.(string) if !ok { err := fmt.Errorf("template.parameters[%v]: Unable to convert the generated value '%#v' to string for parameter %s", i, value, param.Name) return field.Invalid(templatePath, param, err.Error()) } } if len(param.Value) == 0 && param.Required { err := fmt.Errorf("template.parameters[%v]: parameter %s is required and must be specified", i, param.Name) return field.Required(templatePath, err.Error()) } } return nil }
// ValidateImageStream tests required fields for an ImageStream. func ValidateImageStream(stream *api.ImageStream) field.ErrorList { result := validation.ValidateObjectMeta(&stream.ObjectMeta, true, ValidateImageStreamName, field.NewPath("metadata")) // Ensure we can generate a valid docker image repository from namespace/name if len(stream.Namespace+"/"+stream.Name) > reference.NameTotalLengthMax { result = append(result, field.Invalid(field.NewPath("metadata", "name"), stream.Name, fmt.Sprintf("'namespace/name' cannot be longer than %d characters", reference.NameTotalLengthMax))) } if len(stream.Spec.DockerImageRepository) != 0 { dockerImageRepositoryPath := field.NewPath("spec", "dockerImageRepository") if ref, err := api.ParseDockerImageReference(stream.Spec.DockerImageRepository); err != nil { result = append(result, field.Invalid(dockerImageRepositoryPath, stream.Spec.DockerImageRepository, err.Error())) } else { if len(ref.Tag) > 0 { result = append(result, field.Invalid(dockerImageRepositoryPath, stream.Spec.DockerImageRepository, "the repository name may not contain a tag")) } if len(ref.ID) > 0 { result = append(result, field.Invalid(dockerImageRepositoryPath, stream.Spec.DockerImageRepository, "the repository name may not contain an ID")) } } } for tag, tagRef := range stream.Spec.Tags { path := field.NewPath("spec", "tags").Key(tag) result = append(result, ValidateImageStreamTagReference(tagRef, path)...) } for tag, history := range stream.Status.Tags { for i, tagEvent := range history.Items { if len(tagEvent.DockerImageReference) == 0 { result = append(result, field.Required(field.NewPath("status", "tags").Key(tag).Child("items").Index(i).Child("dockerImageReference"), "")) } } } return result }
// validateInsecureEdgeTerminationPolicy tests fields for different types of // insecure options. Called by validateTLS. func validateInsecureEdgeTerminationPolicy(tls *routeapi.TLSConfig, fldPath *field.Path) *field.Error { // Check insecure option value if specified (empty is ok). if len(tls.InsecureEdgeTerminationPolicy) == 0 { return nil } // Ensure insecure is set only for edge terminated routes. if routeapi.TLSTerminationEdge != tls.Termination { // tls.InsecureEdgeTerminationPolicy option is not supported for a non edge-terminated routes. return field.Invalid(fldPath, tls.InsecureEdgeTerminationPolicy, "InsecureEdgeTerminationPolicy is only allowed for edge-terminated routes") } // It is an edge-terminated route, check insecure option value is // one of None(for disable), Allow or Redirect. allowedValues := map[routeapi.InsecureEdgeTerminationPolicyType]struct{}{ routeapi.InsecureEdgeTerminationPolicyNone: {}, routeapi.InsecureEdgeTerminationPolicyAllow: {}, routeapi.InsecureEdgeTerminationPolicyRedirect: {}, } if _, ok := allowedValues[tls.InsecureEdgeTerminationPolicy]; !ok { msg := fmt.Sprintf("invalid value for InsecureEdgeTerminationPolicy option, acceptable values are %s, %s, %s, or empty", routeapi.InsecureEdgeTerminationPolicyNone, routeapi.InsecureEdgeTerminationPolicyAllow, routeapi.InsecureEdgeTerminationPolicyRedirect) return field.Invalid(fldPath, tls.InsecureEdgeTerminationPolicy, msg) } return nil }
// ValidateClusterNetwork tests if required fields in the ClusterNetwork are set. func ValidateClusterNetwork(clusterNet *sdnapi.ClusterNetwork) field.ErrorList { allErrs := validation.ValidateObjectMeta(&clusterNet.ObjectMeta, false, oapi.MinimalNameRequirements, field.NewPath("metadata")) clusterIP, clusterIPNet, err := net.ParseCIDR(clusterNet.Network) if err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("network"), clusterNet.Network, err.Error())) } else { ones, bitSize := clusterIPNet.Mask.Size() if uint32(bitSize-ones) <= clusterNet.HostSubnetLength { allErrs = append(allErrs, field.Invalid(field.NewPath("hostSubnetLength"), clusterNet.HostSubnetLength, "subnet length is greater than cluster Mask")) } } serviceIP, serviceIPNet, err := net.ParseCIDR(clusterNet.ServiceNetwork) if err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, err.Error())) } if (clusterIPNet != nil) && (serviceIP != nil) && clusterIPNet.Contains(serviceIP) { allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, "service network overlaps with cluster network")) } if (serviceIPNet != nil) && (clusterIP != nil) && serviceIPNet.Contains(clusterIP) { allErrs = append(allErrs, field.Invalid(field.NewPath("network"), clusterNet.Network, "cluster network overlaps with service network")) } return allErrs }
func validateIngressRules(ingressRules []extensions.IngressRule, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(ingressRules) == 0 { return append(allErrs, field.Required(fldPath, "")) } for i, ih := range ingressRules { if len(ih.Host) > 0 { if isIP := (net.ParseIP(ih.Host) != nil); isIP { allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address")) } // TODO: Ports and ips are allowed in the host part of a url // according to RFC 3986, consider allowing them. if strings.Contains(ih.Host, "*") { for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) { allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg)) } continue } for _, msg := range validation.IsDNS1123Subdomain(ih.Host) { allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg)) } } allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(0))...) } return allErrs }
func ValidateScopeRestriction(restriction api.ScopeRestriction, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} specifiers := 0 if len(restriction.ExactValues) > 0 { specifiers = specifiers + 1 } if restriction.ClusterRole != nil { specifiers = specifiers + 1 } if specifiers != 1 { allErrs = append(allErrs, field.Invalid(fldPath, restriction, "exactly one of literals, clusterRole is required")) return allErrs } switch { case len(restriction.ExactValues) > 0: for i, literal := range restriction.ExactValues { if len(literal) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("literals").Index(i), literal, "may not be empty")) } } case restriction.ClusterRole != nil: if len(restriction.ClusterRole.RoleNames) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("clusterRole", "roleNames"), "won't match anything")) } if len(restriction.ClusterRole.Namespaces) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("clusterRole", "namespaces"), "won't match anything")) } } return allErrs }
// ValidateEvent makes sure that the event makes sense. func ValidateEvent(event *api.Event) field.ErrorList { allErrs := field.ErrorList{} // There is no namespace required for root-scoped kind, for example, node. // However, older client code accidentally sets event.Namespace // to api.NamespaceDefault, so we accept that too, but "" is preferred. // Todo: Events may reference 3rd party object, and we can't check whether the object is namespaced. // Suppose them are namespaced. Do check if we can get the piece of information. // This should apply to all groups served by this apiserver. namespacedKindFlag, err := isNamespacedKind(event.InvolvedObject.Kind, event.InvolvedObject.APIVersion) if err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "kind"), event.InvolvedObject.Kind, fmt.Sprintf("couldn't check whether namespace is allowed: %s", err))) } else { if !namespacedKindFlag && event.Namespace != api.NamespaceDefault && event.Namespace != "" { allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, fmt.Sprintf("not allowed for %s", event.InvolvedObject.Kind))) } if namespacedKindFlag && event.Namespace != event.InvolvedObject.Namespace { allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match involvedObject")) } } if !validation.IsDNS1123Subdomain(event.Namespace) { allErrs = append(allErrs, field.Invalid(field.NewPath("namespace"), event.Namespace, "")) } return allErrs }
func ValidateLDAPSyncConfig(config *api.LDAPSyncConfig) ValidationResults { validationResults := ValidateLDAPClientConfig(config.URL, config.BindDN, config.BindPassword, config.CA, config.Insecure, nil) schemaConfigsFound := []string{} if config.RFC2307Config != nil { configResults := ValidateRFC2307Config(config.RFC2307Config) validationResults.AddErrors(configResults.Errors...) validationResults.AddWarnings(configResults.Warnings...) schemaConfigsFound = append(schemaConfigsFound, "rfc2307") } if config.ActiveDirectoryConfig != nil { configResults := ValidateActiveDirectoryConfig(config.ActiveDirectoryConfig) validationResults.AddErrors(configResults.Errors...) validationResults.AddWarnings(configResults.Warnings...) schemaConfigsFound = append(schemaConfigsFound, "activeDirectory") } if config.AugmentedActiveDirectoryConfig != nil { configResults := ValidateAugmentedActiveDirectoryConfig(config.AugmentedActiveDirectoryConfig) validationResults.AddErrors(configResults.Errors...) validationResults.AddWarnings(configResults.Warnings...) schemaConfigsFound = append(schemaConfigsFound, "augmentedActiveDirectory") } if len(schemaConfigsFound) > 1 { validationResults.AddErrors(field.Invalid(nil, config, fmt.Sprintf("only one schema-specific config is allowed; found %v", schemaConfigsFound))) } if len(schemaConfigsFound) == 0 { validationResults.AddErrors(field.Invalid(nil, config, fmt.Sprintf("exactly one schema-specific config is required; one of %v", []string{"rfc2307", "activeDirectory", "augmentedActiveDirectory"}))) } return validationResults }
func ValidateDeploymentConfig(config *deployapi.DeploymentConfig) field.ErrorList { allErrs := validation.ValidateObjectMeta(&config.ObjectMeta, true, validation.NameIsDNSSubdomain, field.NewPath("metadata")) // TODO: Refactor to validate spec and status separately for i := range config.Spec.Triggers { allErrs = append(allErrs, validateTrigger(&config.Spec.Triggers[i], field.NewPath("spec", "triggers").Index(i))...) } specPath := field.NewPath("spec") allErrs = append(allErrs, validateDeploymentStrategy(&config.Spec.Strategy, field.NewPath("spec", "strategy"))...) if config.Spec.Template == nil { allErrs = append(allErrs, field.Required(specPath.Child("template"), "")) } else { allErrs = append(allErrs, validation.ValidatePodTemplateSpec(config.Spec.Template, specPath.Child("template"))...) } if config.Status.LatestVersion < 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("status", "latestVersion"), config.Status.LatestVersion, "latestVersion cannot be negative")) } if config.Spec.Replicas < 0 { allErrs = append(allErrs, field.Invalid(specPath.Child("replicas"), config.Spec.Replicas, "replicas cannot be negative")) } if len(config.Spec.Selector) == 0 { allErrs = append(allErrs, field.Invalid(specPath.Child("selector"), config.Spec.Selector, "selector cannot be empty")) } return allErrs }
func ValidatePolicyBinding(policyBinding *authorizationapi.PolicyBinding, isNamespaced bool) field.ErrorList { allErrs := validation.ValidateObjectMeta(&policyBinding.ObjectMeta, isNamespaced, PolicyBindingNameValidator(policyBinding.PolicyRef.Namespace), field.NewPath("metadata")) if !isNamespaced { if len(policyBinding.PolicyRef.Namespace) > 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("policyRef", "namespace"), policyBinding.PolicyRef.Namespace, "may not reference another namespace")) } } roleBindingsPath := field.NewPath("roleBindings") for roleBindingKey, roleBinding := range policyBinding.RoleBindings { keyPath := roleBindingsPath.Key(roleBindingKey) if roleBinding == nil { allErrs = append(allErrs, field.Required(keyPath, "")) } if roleBinding.RoleRef.Namespace != policyBinding.PolicyRef.Namespace { allErrs = append(allErrs, field.Invalid(keyPath.Child("roleRef", "namespace"), policyBinding.PolicyRef.Namespace, "must be "+policyBinding.PolicyRef.Namespace)) } if roleBindingKey != roleBinding.Name { allErrs = append(allErrs, field.Invalid(keyPath.Child("metadata", "name"), roleBinding.Name, "must be "+roleBindingKey)) } allErrs = append(allErrs, validateRoleBinding(roleBinding, isNamespaced, keyPath)...) } return allErrs }
func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} limPath := fldPath.Child("limits") reqPath := fldPath.Child("requests") for resourceName, quantity := range requirements.Limits { fldPath := limPath.Key(string(resourceName)) // Validate resource name. allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) // Validate resource quantity. allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...) // Check that request <= limit. requestQuantity, exists := requirements.Requests[resourceName] if exists { // For GPUs, not only requests can't exceed limits, they also can't be lower, i.e. must be equal. if resourceName == v1.ResourceNvidiaGPU && quantity.Cmp(requestQuantity) != 0 { allErrs = append(allErrs, field.Invalid(reqPath, requestQuantity.String(), fmt.Sprintf("must be equal to %s limit", v1.ResourceNvidiaGPU))) } else if quantity.Cmp(requestQuantity) < 0 { allErrs = append(allErrs, field.Invalid(limPath, quantity.String(), fmt.Sprintf("must be greater than or equal to %s request", resourceName))) } } } for resourceName, quantity := range requirements.Requests { fldPath := reqPath.Key(string(resourceName)) // Validate resource name. allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) // Validate resource quantity. allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...) } return allErrs }
func ValidateDeploymentConfigSpec(spec deployapi.DeploymentConfigSpec) field.ErrorList { allErrs := field.ErrorList{} specPath := field.NewPath("spec") for i := range spec.Triggers { allErrs = append(allErrs, validateTrigger(&spec.Triggers[i], specPath.Child("triggers").Index(i))...) } var podSpec *kapi.PodSpec if spec.Template != nil { podSpec = &spec.Template.Spec } allErrs = append(allErrs, validateDeploymentStrategy(&spec.Strategy, podSpec, specPath.Child("strategy"))...) if spec.Template == nil { allErrs = append(allErrs, field.Required(specPath.Child("template"), "")) } else { originalContainerImageNames := getContainerImageNames(spec.Template) defer setContainerImageNames(spec.Template, originalContainerImageNames) handleEmptyImageReferences(spec.Template, spec.Triggers) allErrs = append(allErrs, validation.ValidatePodTemplateSpec(spec.Template, specPath.Child("template"))...) } if spec.Replicas < 0 { allErrs = append(allErrs, field.Invalid(specPath.Child("replicas"), spec.Replicas, "replicas cannot be negative")) } if len(spec.Selector) == 0 { allErrs = append(allErrs, field.Invalid(specPath.Child("selector"), spec.Selector, "selector cannot be empty")) } return allErrs }
func validateLifecycleHook(hook *deployapi.LifecycleHook, pod *kapi.PodSpec, fldPath *field.Path) field.ErrorList { errs := field.ErrorList{} if len(hook.FailurePolicy) == 0 { errs = append(errs, field.Required(fldPath.Child("failurePolicy"), "")) } switch { case hook.ExecNewPod != nil && len(hook.TagImages) > 0: errs = append(errs, field.Invalid(fldPath, "<hook>", "only one of 'execNewPod' of 'tagImages' may be specified")) case hook.ExecNewPod != nil: errs = append(errs, validateExecNewPod(hook.ExecNewPod, fldPath.Child("execNewPod"))...) case len(hook.TagImages) > 0: for i, image := range hook.TagImages { if len(image.ContainerName) == 0 { errs = append(errs, field.Required(fldPath.Child("tagImages").Index(i).Child("containerName"), "a containerName is required")) } else { if _, err := deployapi.TemplateImageForContainer(pod, deployapi.IgnoreTriggers, image.ContainerName); err != nil { errs = append(errs, field.Invalid(fldPath.Child("tagImages").Index(i).Child("containerName"), image.ContainerName, err.Error())) } } if image.To.Kind != "ImageStreamTag" { errs = append(errs, field.Invalid(fldPath.Child("tagImages").Index(i).Child("to", "kind"), image.To.Kind, "Must be 'ImageStreamTag'")) } if len(image.To.Name) == 0 { errs = append(errs, field.Required(fldPath.Child("tagImages").Index(i).Child("to", "name"), "a destination tag name is required")) } } default: errs = append(errs, field.Invalid(fldPath, "<empty>", "One of execNewPod or tagImages must be specified")) } return errs }
func validateDockerStrategy(strategy *buildapi.DockerBuildStrategy, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if strategy.From != nil { allErrs = append(allErrs, validateFromImageReference(strategy.From, fldPath.Child("from"))...) } allErrs = append(allErrs, validateSecretRef(strategy.PullSecret, fldPath.Child("pullSecret"))...) if len(strategy.DockerfilePath) != 0 { cleaned := path.Clean(strategy.DockerfilePath) switch { case strings.HasPrefix(cleaned, "/"): allErrs = append(allErrs, field.Invalid(fldPath.Child("dockerfilePath"), strategy.DockerfilePath, "dockerfilePath must not be an absolute path")) case strings.HasPrefix(cleaned, ".."): allErrs = append(allErrs, field.Invalid(fldPath.Child("dockerfilePath"), strategy.DockerfilePath, "dockerfilePath must not start with ..")) default: if cleaned == "." { cleaned = "" } strategy.DockerfilePath = cleaned } } return allErrs }
func ValidateLDAPQuery(query api.LDAPQuery, fldPath *field.Path) ValidationResults { validationResults := ValidationResults{} if _, err := ldap.ParseDN(query.BaseDN); err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("baseDN"), query.BaseDN, fmt.Sprintf("invalid base DN for search: %v", err))) } if len(query.Scope) > 0 { if _, err := ldaputil.DetermineLDAPScope(query.Scope); err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("scope"), query.Scope, "invalid LDAP search scope")) } } if len(query.DerefAliases) > 0 { if _, err := ldaputil.DetermineDerefAliasesBehavior(query.DerefAliases); err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("derefAliases"), query.DerefAliases, "LDAP alias dereferencing instruction invalid")) } } if query.TimeLimit < 0 { validationResults.AddErrors(field.Invalid(fldPath.Child("timeout"), query.TimeLimit, "timeout must be equal to or greater than zero")) } if _, err := ldap.CompileFilter(query.Filter); err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("filter"), query.Filter, fmt.Sprintf("invalid query filter: %v", err))) } return validationResults }
func ValidateEtcdConfig(config *api.EtcdConfig, fldPath *field.Path) ValidationResults { validationResults := ValidationResults{} servingInfoPath := fldPath.Child("servingInfo") validationResults.Append(ValidateServingInfo(config.ServingInfo, servingInfoPath)) if config.ServingInfo.BindNetwork == "tcp6" { validationResults.AddErrors(field.Invalid(servingInfoPath.Child("bindNetwork"), config.ServingInfo.BindNetwork, "tcp6 is not a valid bindNetwork for etcd, must be tcp or tcp4")) } if len(config.ServingInfo.NamedCertificates) > 0 { validationResults.AddErrors(field.Invalid(servingInfoPath.Child("namedCertificates"), "<not shown>", "namedCertificates are not supported for etcd")) } peerServingInfoPath := fldPath.Child("peerServingInfo") validationResults.Append(ValidateServingInfo(config.PeerServingInfo, peerServingInfoPath)) if config.ServingInfo.BindNetwork == "tcp6" { validationResults.AddErrors(field.Invalid(peerServingInfoPath.Child("bindNetwork"), config.ServingInfo.BindNetwork, "tcp6 is not a valid bindNetwork for etcd peers, must be tcp or tcp4")) } if len(config.ServingInfo.NamedCertificates) > 0 { validationResults.AddErrors(field.Invalid(peerServingInfoPath.Child("namedCertificates"), "<not shown>", "namedCertificates are not supported for etcd")) } validationResults.AddErrors(ValidateHostPort(config.Address, fldPath.Child("address"))...) validationResults.AddErrors(ValidateHostPort(config.PeerAddress, fldPath.Child("peerAddress"))...) if len(config.StorageDir) == 0 { validationResults.AddErrors(field.Required(fldPath.Child("storageDirectory"), "")) } return validationResults }
// ValidateStatefulSetSpec tests if required fields in the StatefulSet spec are set. func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) if spec.Selector == nil { allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) } else { allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for statefulset.")) } } selector, err := unversioned.LabelSelectorAsSelector(spec.Selector) if err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "")) } else { allErrs = append(allErrs, ValidatePodTemplateSpecForStatefulSet(&spec.Template, selector, fldPath.Child("template"))...) } if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways { allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)})) } return allErrs }
func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if p := annotations[apparmor.DefaultProfileAnnotationKey]; p != "" { if err := apparmor.ValidateProfileFormat(p); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.DefaultProfileAnnotationKey), p, err.Error())) } } if allowed := annotations[apparmor.AllowedProfilesAnnotationKey]; allowed != "" { for _, p := range strings.Split(allowed, ",") { if err := apparmor.ValidateProfileFormat(p); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.AllowedProfilesAnnotationKey), allowed, err.Error())) } } } sysctlAnnotation := annotations[extensions.SysctlsPodSecurityPolicyAnnotationKey] sysctlFldPath := fldPath.Key(extensions.SysctlsPodSecurityPolicyAnnotationKey) sysctls, err := extensions.SysctlsFromPodSecurityPolicyAnnotation(sysctlAnnotation) if err != nil { allErrs = append(allErrs, field.Invalid(sysctlFldPath, sysctlAnnotation, err.Error())) } else { allErrs = append(allErrs, validatePodSecurityPolicySysctls(sysctlFldPath, sysctls)...) } if p := annotations[seccomp.DefaultProfileAnnotationKey]; p != "" { allErrs = append(allErrs, apivalidation.ValidateSeccompProfile(p, fldPath.Key(seccomp.DefaultProfileAnnotationKey))...) } if allowed := annotations[seccomp.AllowedProfilesAnnotationKey]; allowed != "" { for _, p := range strings.Split(allowed, ",") { allErrs = append(allErrs, apivalidation.ValidateSeccompProfile(p, fldPath.Key(seccomp.AllowedProfilesAnnotationKey))...) } } return allErrs }
func validateCommonSpec(spec *buildapi.CommonSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} s := spec.Strategy if s.DockerStrategy != nil && s.JenkinsPipelineStrategy == nil && spec.Source.Git == nil && spec.Source.Binary == nil && spec.Source.Dockerfile == nil && spec.Source.Images == nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("source"), "", "must provide a value for at least one source input(git, binary, dockerfile, images).")) } allErrs = append(allErrs, validateSource( &spec.Source, s.CustomStrategy != nil, s.DockerStrategy != nil, s.JenkinsPipelineStrategy != nil && len(s.JenkinsPipelineStrategy.Jenkinsfile) == 0, fldPath.Child("source"))..., ) if spec.CompletionDeadlineSeconds != nil { if *spec.CompletionDeadlineSeconds <= 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("completionDeadlineSeconds"), spec.CompletionDeadlineSeconds, "completionDeadlineSeconds must be a positive integer greater than 0")) } } allErrs = append(allErrs, validateOutput(&spec.Output, fldPath.Child("output"))...) allErrs = append(allErrs, validateStrategy(&spec.Strategy, fldPath.Child("strategy"))...) allErrs = append(allErrs, validatePostCommit(spec.PostCommit, fldPath.Child("postCommit"))...) // TODO: validate resource requirements (prereq: https://github.com/kubernetes/kubernetes/pull/7059) return allErrs }
func ValidateAuthorizeToken(authorizeToken *api.OAuthAuthorizeToken) field.ErrorList { allErrs := validation.ValidateObjectMeta(&authorizeToken.ObjectMeta, false, ValidateTokenName, field.NewPath("metadata")) allErrs = append(allErrs, ValidateClientNameField(authorizeToken.ClientName, field.NewPath("clientName"))...) allErrs = append(allErrs, ValidateUserNameField(authorizeToken.UserName, field.NewPath("userName"))...) allErrs = append(allErrs, ValidateScopes(authorizeToken.Scopes, field.NewPath("scopes"))...) if len(authorizeToken.UserUID) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("userUID"), "")) } if ok, msg := ValidateRedirectURI(authorizeToken.RedirectURI); !ok { allErrs = append(allErrs, field.Invalid(field.NewPath("redirectURI"), authorizeToken.RedirectURI, msg)) } if len(authorizeToken.CodeChallenge) > 0 || len(authorizeToken.CodeChallengeMethod) > 0 { switch { case len(authorizeToken.CodeChallenge) == 0: allErrs = append(allErrs, field.Required(field.NewPath("codeChallenge"), "required if codeChallengeMethod is specified")) case !codeChallengeRegex.MatchString(authorizeToken.CodeChallenge): allErrs = append(allErrs, field.Invalid(field.NewPath("codeChallenge"), authorizeToken.CodeChallenge, "must be 43-128 characters [a-zA-Z0-9.~_-]")) } switch authorizeToken.CodeChallengeMethod { case "": allErrs = append(allErrs, field.Required(field.NewPath("codeChallengeMethod"), "required if codeChallenge is specified")) case codeChallengeMethodPlain, codeChallengeMethodSHA256: // no-op, good default: allErrs = append(allErrs, field.NotSupported(field.NewPath("codeChallengeMethod"), authorizeToken.CodeChallengeMethod, CodeChallengeMethodsSupported)) } } return allErrs }
func TestCheckInvalidErr(t *testing.T) { tests := []struct { err error expected string }{ { errors.NewInvalid(api.Kind("Invalid1"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field"), "single", "details")}), `Error from server: Invalid1 "invalidation" is invalid: field: Invalid value: "single": details`, }, { errors.NewInvalid(api.Kind("Invalid2"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field1"), "multi1", "details"), field.Invalid(field.NewPath("field2"), "multi2", "details")}), `Error from server: Invalid2 "invalidation" is invalid: [field1: Invalid value: "multi1": details, field2: Invalid value: "multi2": details]`, }, { errors.NewInvalid(api.Kind("Invalid3"), "invalidation", field.ErrorList{}), `Error from server: Invalid3 "invalidation" is invalid: <nil>`, }, } var errReturned string errHandle := func(err string) { errReturned = err } for _, test := range tests { checkErr(test.err, errHandle) if errReturned != test.expected { t.Fatalf("Got: %s, expected: %s", errReturned, test.expected) } } }
// Validates given deployment spec. func ValidateDeploymentSpec(spec *extensions.DeploymentSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) if spec.Selector == nil { allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) } else { allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for deployment.")) } } selector, err := unversioned.LabelSelectorAsSelector(spec.Selector) if err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "failed to convert LabelSelector to Selector.")) } else { allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"))...) } allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...) if spec.RevisionHistoryLimit != nil { // zero is a valid RevisionHistoryLimit allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...) } if spec.RollbackTo != nil { allErrs = append(allErrs, ValidateRollback(spec.RollbackTo, fldPath.Child("rollback"))...) } return allErrs }
func ValidateServingInfo(info api.ServingInfo, fldPath *field.Path) ValidationResults { validationResults := ValidationResults{} validationResults.AddErrors(ValidateHostPort(info.BindAddress, fldPath.Child("bindAddress"))...) validationResults.AddErrors(ValidateCertInfo(info.ServerCert, false, fldPath)...) if len(info.NamedCertificates) > 0 && len(info.ServerCert.CertFile) == 0 { validationResults.AddErrors(field.Invalid(fldPath.Child("namedCertificates"), "", "a default certificate and key is required in certFile/keyFile in order to use namedCertificates")) } validationResults.Append(ValidateNamedCertificates(fldPath.Child("namedCertificates"), info.NamedCertificates)) switch info.BindNetwork { case "tcp", "tcp4", "tcp6": default: validationResults.AddErrors(field.Invalid(fldPath.Child("bindNetwork"), info.BindNetwork, "must be 'tcp', 'tcp4', or 'tcp6'")) } if len(info.ServerCert.CertFile) > 0 { if len(info.ClientCA) > 0 { validationResults.AddErrors(ValidateFile(info.ClientCA, fldPath.Child("clientCA"))...) } } else { if len(info.ClientCA) > 0 { validationResults.AddErrors(field.Invalid(fldPath.Child("clientCA"), info.ClientCA, "cannot specify a clientCA without a certFile")) } } return validationResults }
func validateHTTPIngressRuleValue(httpIngressRuleValue *extensions.HTTPIngressRuleValue, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(httpIngressRuleValue.Paths) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("paths"), "")) } for i, rule := range httpIngressRuleValue.Paths { if len(rule.Path) > 0 { if !strings.HasPrefix(rule.Path, "/") { allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be an absolute path")) } // TODO: More draconian path regex validation. // Path must be a valid regex. This is the basic requirement. // In addition to this any characters not allowed in a path per // RFC 3986 section-3.3 cannot appear as a literal in the regex. // Consider the example: http://host/valid?#bar, everything after // the last '/' is a valid regex that matches valid#bar, which // isn't a valid path, because the path terminates at the first ? // or #. A more sophisticated form of validation would detect that // the user is confusing url regexes with path regexes. _, err := regexp.CompilePOSIX(rule.Path) if err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be a valid regex")) } } allErrs = append(allErrs, validateIngressBackend(&rule.Backend, fldPath.Child("backend"))...) } return allErrs }
func validateSessionConfig(config *api.SessionConfig, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} // Validate session secrets file, if specified sessionSecretsFilePath := fldPath.Child("sessionSecretsFile") if len(config.SessionSecretsFile) > 0 { fileErrs := ValidateFile(config.SessionSecretsFile, sessionSecretsFilePath) if len(fileErrs) != 0 { // Missing file allErrs = append(allErrs, fileErrs...) } else { // Validate file contents secrets, err := latest.ReadSessionSecrets(config.SessionSecretsFile) if err != nil { allErrs = append(allErrs, field.Invalid(sessionSecretsFilePath, config.SessionSecretsFile, fmt.Sprintf("error reading file: %v", err))) } else { for _, err := range ValidateSessionSecrets(secrets) { allErrs = append(allErrs, field.Invalid(sessionSecretsFilePath, config.SessionSecretsFile, err.Error())) } } } } if len(config.SessionName) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("sessionName"), "")) } return allErrs }
// ValidateImageStreamMapping tests required fields for an ImageStreamMapping. func ValidateImageStreamMapping(mapping *api.ImageStreamMapping) field.ErrorList { result := validation.ValidateObjectMeta(&mapping.ObjectMeta, true, oapi.MinimalNameRequirements, field.NewPath("metadata")) hasRepository := len(mapping.DockerImageRepository) != 0 hasName := len(mapping.Name) != 0 switch { case hasRepository: if _, err := api.ParseDockerImageReference(mapping.DockerImageRepository); err != nil { result = append(result, field.Invalid(field.NewPath("dockerImageRepository"), mapping.DockerImageRepository, err.Error())) } case hasName: default: result = append(result, field.Required(field.NewPath("name"), "")) result = append(result, field.Required(field.NewPath("dockerImageRepository"), "")) } if ok, msg := validation.ValidateNamespaceName(mapping.Namespace, false); !ok { result = append(result, field.Invalid(field.NewPath("metadata", "namespace"), mapping.Namespace, msg)) } if len(mapping.Tag) == 0 { result = append(result, field.Required(field.NewPath("tag"), "")) } if errs := validateImage(&mapping.Image, field.NewPath("image")); len(errs) != 0 { result = append(result, errs...) } return result }
func validateToImageReference(reference *kapi.ObjectReference, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} kind, name, namespace := reference.Kind, reference.Name, reference.Namespace switch kind { case "ImageStreamTag": if len(name) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("name"))) } else if _, _, ok := imageapi.SplitImageStreamTag(name); !ok { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), name, "ImageStreamTag object references must be in the form <name>:<tag>")) } if len(namespace) != 0 && !kvalidation.IsDNS1123Subdomain(namespace) { allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), namespace, "namespace must be a valid subdomain")) } case "DockerImage": if len(namespace) != 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), namespace, "namespace is not valid when used with a 'DockerImage'")) } if _, err := imageapi.ParseDockerImageReference(name); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), name, fmt.Sprintf("name is not a valid Docker pull specification: %v", err))) } case "": allErrs = append(allErrs, field.Required(fldPath.Child("kind"))) default: allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), kind, "the target of build output must be an 'ImageStreamTag' or 'DockerImage'")) } return allErrs }
func ValidateIdentity(identity *api.Identity) field.ErrorList { allErrs := kvalidation.ValidateObjectMeta(&identity.ObjectMeta, false, ValidateIdentityName, field.NewPath("metadata")) if len(identity.ProviderName) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("providerName"), "")) } else if ok, msg := ValidateIdentityProviderName(identity.ProviderName); !ok { allErrs = append(allErrs, field.Invalid(field.NewPath("providerName"), identity.ProviderName, msg)) } if len(identity.ProviderUserName) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("providerUserName"), "")) } else if ok, msg := ValidateIdentityProviderName(identity.ProviderUserName); !ok { allErrs = append(allErrs, field.Invalid(field.NewPath("providerUserName"), identity.ProviderUserName, msg)) } userPath := field.NewPath("user") if len(identity.ProviderName) > 0 && len(identity.ProviderUserName) > 0 { expectedIdentityName := identity.ProviderName + ":" + identity.ProviderUserName if identity.Name != expectedIdentityName { allErrs = append(allErrs, field.Invalid(userPath.Child("name"), identity.User.Name, fmt.Sprintf("must be %s", expectedIdentityName))) } } if ok, msg := ValidateUserName(identity.User.Name, false); !ok { allErrs = append(allErrs, field.Invalid(userPath.Child("name"), identity.User.Name, msg)) } if len(identity.User.Name) == 0 && len(identity.User.UID) != 0 { allErrs = append(allErrs, field.Invalid(userPath.Child("uid"), identity.User.UID, "may not be set if user.name is empty")) } if len(identity.User.Name) != 0 && len(identity.User.UID) == 0 { allErrs = append(allErrs, field.Required(userPath.Child("uid"), "")) } 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 }