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 }
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 reasons := ValidateIdentityProviderName(identity.ProviderName); len(reasons) != 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("providerName"), identity.ProviderName, strings.Join(reasons, ", "))) } if len(identity.ProviderUserName) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("providerUserName"), "")) } else if reasons := ValidateIdentityProviderName(identity.ProviderUserName); len(reasons) != 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("providerUserName"), identity.ProviderUserName, strings.Join(reasons, ", "))) } 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 reasons := ValidateUserName(identity.User.Name, false); len(reasons) != 0 { allErrs = append(allErrs, field.Invalid(userPath.Child("name"), identity.User.Name, strings.Join(reasons, ", "))) } if len(identity.User.Name) == 0 && len(identity.User.UID) != 0 { allErrs = append(allErrs, field.Required(userPath.Child("username"), "username is required when uid is provided")) } if len(identity.User.Name) != 0 && len(identity.User.UID) == 0 { allErrs = append(allErrs, field.Required(userPath.Child("uid"), "uid is required when username is provided")) } 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 ValidateRFC2307Config(config *api.RFC2307Config) ValidationResults { validationResults := ValidationResults{} validationResults.Append(ValidateLDAPQuery(config.AllGroupsQuery, field.NewPath("groupsQuery"))) if len(config.GroupUIDAttribute) == 0 { validationResults.AddErrors(field.Required(field.NewPath("groupUIDAttribute"), "")) } if len(config.GroupNameAttributes) == 0 { validationResults.AddErrors(field.Required(field.NewPath("groupNameAttributes"), "")) } if len(config.GroupMembershipAttributes) == 0 { validationResults.AddErrors(field.Required(field.NewPath("groupMembershipAttributes"), "")) } isUserDNQuery := strings.TrimSpace(strings.ToLower(config.UserUIDAttribute)) == "dn" validationResults.Append(validateLDAPQuery(config.AllUsersQuery, field.NewPath("usersQuery"), isUserDNQuery)) if len(config.UserUIDAttribute) == 0 { validationResults.AddErrors(field.Required(field.NewPath("userUIDAttribute"), "")) } if len(config.UserNameAttributes) == 0 { validationResults.AddErrors(field.Required(field.NewPath("userNameAttributes"), "")) } return validationResults }
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 ValidateCertInfo(certInfo api.CertInfo, required bool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if required { if len(certInfo.CertFile) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("certFile"), "The certificate file must be provided")) } if len(certInfo.KeyFile) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("keyFile"), "The certificate key must be provided")) } } if (len(certInfo.CertFile) == 0) != (len(certInfo.KeyFile) == 0) { allErrs = append(allErrs, field.Required(fldPath.Child("certFile"), "Both the certificate file and the certificate key must be provided together or not at all")) allErrs = append(allErrs, field.Required(fldPath.Child("keyFile"), "Both the certificate file and the certificate key must be provided together or not at all")) } if len(certInfo.CertFile) > 0 { allErrs = append(allErrs, ValidateFile(certInfo.CertFile, fldPath.Child("certFile"))...) } if len(certInfo.KeyFile) > 0 { allErrs = append(allErrs, ValidateFile(certInfo.KeyFile, fldPath.Child("keyFile"))...) } // validate certfile/keyfile load/parse? 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 }
func ValidateNodeAuthConfig(config api.NodeAuthConfig, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} authenticationCacheTTLPath := fldPath.Child("authenticationCacheTTL") if len(config.AuthenticationCacheTTL) == 0 { allErrs = append(allErrs, field.Required(authenticationCacheTTLPath, "")) } else if ttl, err := time.ParseDuration(config.AuthenticationCacheTTL); err != nil { allErrs = append(allErrs, field.Invalid(authenticationCacheTTLPath, config.AuthenticationCacheTTL, fmt.Sprintf("%v", err))) } else if ttl < 0 { allErrs = append(allErrs, field.Invalid(authenticationCacheTTLPath, config.AuthenticationCacheTTL, fmt.Sprintf("cannot be less than zero"))) } if config.AuthenticationCacheSize <= 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("authenticationCacheSize"), config.AuthenticationCacheSize, fmt.Sprintf("must be greater than zero"))) } authorizationCacheTTLPath := fldPath.Child("authorizationCacheTTL") if len(config.AuthorizationCacheTTL) == 0 { allErrs = append(allErrs, field.Required(authorizationCacheTTLPath, "")) } else if ttl, err := time.ParseDuration(config.AuthorizationCacheTTL); err != nil { allErrs = append(allErrs, field.Invalid(authorizationCacheTTLPath, config.AuthorizationCacheTTL, fmt.Sprintf("%v", err))) } else if ttl < 0 { allErrs = append(allErrs, field.Invalid(authorizationCacheTTLPath, config.AuthorizationCacheTTL, fmt.Sprintf("cannot be less than zero"))) } if config.AuthorizationCacheSize <= 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("authorizationCacheSize"), config.AuthorizationCacheSize, fmt.Sprintf("must be greater than zero"))) } return allErrs }
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 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 ValidateRequestHeaderIdentityProvider(provider *api.RequestHeaderIdentityProvider, identityProvider api.IdentityProvider, fieldPath *field.Path) ValidationResults { validationResults := ValidationResults{} if len(provider.ClientCA) > 0 { validationResults.AddErrors(ValidateFile(provider.ClientCA, fieldPath.Child("provider", "clientCA"))...) } else if len(provider.ClientCommonNames) > 0 { validationResults.AddErrors(field.Invalid(fieldPath.Child("provider", "clientCommonNames"), provider.ClientCommonNames, "clientCA must be specified in order to use clientCommonNames")) } if len(provider.Headers) == 0 { validationResults.AddErrors(field.Required(fieldPath.Child("provider", "headers"), "")) } if identityProvider.UseAsChallenger && len(provider.ChallengeURL) == 0 { err := field.Required(fieldPath.Child("provider", "challengeURL"), "") err.Detail = "challengeURL is required if challenge=true" validationResults.AddErrors(err) } if identityProvider.UseAsLogin && len(provider.LoginURL) == 0 { err := field.Required(fieldPath.Child("provider", "loginURL"), "") err.Detail = "loginURL is required if login=true" validationResults.AddErrors(err) } if len(provider.ChallengeURL) > 0 { url, urlErrs := ValidateURL(provider.ChallengeURL, fieldPath.Child("provider", "challengeURL")) validationResults.AddErrors(urlErrs...) if len(urlErrs) == 0 && !strings.Contains(url.RawQuery, redirector.URLToken) && !strings.Contains(url.RawQuery, redirector.QueryToken) { validationResults.AddWarnings( field.Invalid( field.NewPath("provider", "challengeURL"), provider.ChallengeURL, fmt.Sprintf("query does not include %q or %q, redirect will not preserve original authorize parameters", redirector.URLToken, redirector.QueryToken), ), ) } } if len(provider.LoginURL) > 0 { url, urlErrs := ValidateURL(provider.LoginURL, fieldPath.Child("provider", "loginURL")) validationResults.AddErrors(urlErrs...) if len(urlErrs) == 0 && !strings.Contains(url.RawQuery, redirector.URLToken) && !strings.Contains(url.RawQuery, redirector.QueryToken) { validationResults.AddWarnings( field.Invalid( fieldPath.Child("provider", "loginURL"), provider.LoginURL, fmt.Sprintf("query does not include %q or %q, redirect will not preserve original authorize parameters", redirector.URLToken, redirector.QueryToken), ), ) } } // Warn if it looks like they expect direct requests to the OAuth endpoints, and have not secured the header checking with a client certificate check if len(provider.ClientCA) == 0 && (len(provider.ChallengeURL) > 0 || len(provider.LoginURL) > 0) { validationResults.AddWarnings(field.Invalid(fieldPath.Child("provider", "clientCA"), "", "if no clientCA is set, no request verification is done, and any request directly against the OAuth server can impersonate any identity from this provider")) } return validationResults }
func validateRoleBindingSubject(subject kapi.ObjectReference, isNamespaced bool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(subject.Name) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("name"), "")) } if len(subject.UID) != 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("uid"), fmt.Sprintf("%v", subject.UID))) } if len(subject.APIVersion) != 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("apiVersion"), subject.APIVersion)) } if len(subject.ResourceVersion) != 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("resourceVersion"), subject.ResourceVersion)) } if len(subject.FieldPath) != 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("fieldPath"), subject.FieldPath)) } switch subject.Kind { case authorizationapi.ServiceAccountKind: if reasons := validation.ValidateServiceAccountName(subject.Name, false); len(subject.Name) > 0 && len(reasons) != 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, strings.Join(reasons, ", "))) } if !isNamespaced && len(subject.Namespace) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "Service account subjects for ClusterRoleBindings must have a namespace")) } case authorizationapi.UserKind: if reasons := uservalidation.ValidateUserName(subject.Name, false); len(subject.Name) > 0 && len(reasons) != 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, strings.Join(reasons, ", "))) } case authorizationapi.GroupKind: if reasons := uservalidation.ValidateGroupName(subject.Name, false); len(subject.Name) > 0 && len(reasons) != 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, strings.Join(reasons, ", "))) } case authorizationapi.SystemUserKind: isValidSAName := len(validation.ValidateServiceAccountName(subject.Name, false)) == 0 isValidUserName := len(uservalidation.ValidateUserName(subject.Name, false)) == 0 if isValidSAName || isValidUserName { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "conforms to User.name or ServiceAccount.name restrictions")) } case authorizationapi.SystemGroupKind: if reasons := uservalidation.ValidateGroupName(subject.Name, false); len(subject.Name) > 0 && len(reasons) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "conforms to Group.name restrictions")) } default: allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, []string{authorizationapi.ServiceAccountKind, authorizationapi.UserKind, authorizationapi.GroupKind, authorizationapi.SystemGroupKind, authorizationapi.SystemUserKind})) } return allErrs }
func ValidateImageStreamImport(isi *api.ImageStreamImport) field.ErrorList { specPath := field.NewPath("spec") imagesPath := specPath.Child("images") repoPath := specPath.Child("repository") errs := field.ErrorList{} for i, spec := range isi.Spec.Images { from := spec.From switch from.Kind { case "DockerImage": if spec.To != nil && len(spec.To.Name) == 0 { errs = append(errs, field.Invalid(imagesPath.Index(i).Child("to", "name"), spec.To.Name, "the name of the target tag must be specified")) } if len(spec.From.Name) == 0 { errs = append(errs, field.Required(imagesPath.Index(i).Child("from", "name"), "")) } else { if ref, err := api.ParseDockerImageReference(spec.From.Name); err != nil { errs = append(errs, field.Invalid(imagesPath.Index(i).Child("from", "name"), spec.From.Name, err.Error())) } else { if len(ref.ID) > 0 && spec.ImportPolicy.Scheduled { errs = append(errs, field.Invalid(imagesPath.Index(i).Child("from", "name"), spec.From.Name, "only tags can be scheduled for import")) } } } default: errs = append(errs, field.Invalid(imagesPath.Index(i).Child("from", "kind"), from.Kind, "only DockerImage is supported")) } } if spec := isi.Spec.Repository; spec != nil { from := spec.From switch from.Kind { case "DockerImage": if len(spec.From.Name) == 0 { errs = append(errs, field.Required(repoPath.Child("from", "name"), "")) } else { if ref, err := api.ParseDockerImageReference(from.Name); err != nil { errs = append(errs, field.Invalid(repoPath.Child("from", "name"), from.Name, err.Error())) } else { if len(ref.ID) > 0 || len(ref.Tag) > 0 { errs = append(errs, field.Invalid(repoPath.Child("from", "name"), from.Name, "you must specify an image repository, not a tag or ID")) } } } default: errs = append(errs, field.Invalid(repoPath.Child("from", "kind"), from.Kind, "only DockerImage is supported")) } } if len(isi.Spec.Images) == 0 && isi.Spec.Repository == nil { errs = append(errs, field.Invalid(imagesPath, nil, "you must specify at least one image or a repository import")) } errs = append(errs, validation.ValidateObjectMeta(&isi.ObjectMeta, true, ValidateImageStreamName, field.NewPath("metadata"))...) return errs }
func validateTrigger(trigger *buildapi.BuildTriggerPolicy, buildFrom *kapi.ObjectReference, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(trigger.Type) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("type"), "")) return allErrs } // Validate each trigger type switch trigger.Type { case buildapi.GitHubWebHookBuildTriggerType: if trigger.GitHubWebHook == nil { allErrs = append(allErrs, field.Required(fldPath.Child("github"), "")) } else { allErrs = append(allErrs, validateWebHook(trigger.GitHubWebHook, fldPath.Child("github"), false)...) } case buildapi.GenericWebHookBuildTriggerType: if trigger.GenericWebHook == nil { allErrs = append(allErrs, field.Required(fldPath.Child("generic"), "")) } else { allErrs = append(allErrs, validateWebHook(trigger.GenericWebHook, fldPath.Child("generic"), true)...) } case buildapi.ImageChangeBuildTriggerType: if trigger.ImageChange == nil { allErrs = append(allErrs, field.Required(fldPath.Child("imageChange"), "")) break } if trigger.ImageChange.From == nil { if buildFrom == nil || buildFrom.Kind != "ImageStreamTag" { invalidKindErr := field.Invalid( fldPath.Child("imageChange"), fmt.Sprintf("build from: %v", buildFrom), "a default ImageChange trigger can only be used when the build strategy includes an ImageStreamTag reference.") allErrs = append(allErrs, invalidKindErr) break } break } if kind := trigger.ImageChange.From.Kind; kind != "ImageStreamTag" { invalidKindErr := field.Invalid( fldPath.Child("imageChange").Child("from").Child("kind"), kind, "only an ImageStreamTag type of reference is allowed in an ImageChange trigger.") allErrs = append(allErrs, invalidKindErr) break } allErrs = append(allErrs, validateFromImageReference(trigger.ImageChange.From, fldPath.Child("from"))...) case buildapi.ConfigChangeBuildTriggerType: // doesn't require additional validation default: allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), trigger.Type, "invalid trigger type")) } return allErrs }
func validateImageSignature(signature *api.ImageSignature, fldPath *field.Path) field.ErrorList { allErrs := validation.ValidateObjectMeta(&signature.ObjectMeta, false, oapi.MinimalNameRequirements, fldPath.Child("metadata")) if len(signature.Labels) > 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("metadata").Child("labels"), "signature labels cannot be set")) } if len(signature.Annotations) > 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("metadata").Child("annotations"), "signature annotations cannot be set")) } if _, _, err := api.SplitImageSignatureName(signature.Name); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata").Child("name"), signature.Name, "name must be of format <imageName>@<signatureName>")) } if len(signature.Type) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("type"), "")) } if len(signature.Content) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("content"), "")) } var trustedCondition, forImageCondition *api.SignatureCondition for i := range signature.Conditions { cond := &signature.Conditions[i] if cond.Type == api.SignatureTrusted && (trustedCondition == nil || !cond.LastProbeTime.Before(trustedCondition.LastProbeTime)) { trustedCondition = cond } else if cond.Type == api.SignatureForImage && forImageCondition == nil || !cond.LastProbeTime.Before(forImageCondition.LastProbeTime) { forImageCondition = cond } } if trustedCondition != nil && forImageCondition == nil { msg := fmt.Sprintf("missing %q condition type", api.SignatureForImage) allErrs = append(allErrs, field.Invalid(fldPath.Child("conditions"), signature.Conditions, msg)) } else if forImageCondition != nil && trustedCondition == nil { msg := fmt.Sprintf("missing %q condition type", api.SignatureTrusted) allErrs = append(allErrs, field.Invalid(fldPath.Child("conditions"), signature.Conditions, msg)) } if trustedCondition == nil || trustedCondition.Status == kapi.ConditionUnknown { if len(signature.ImageIdentity) != 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("imageIdentity"), signature.ImageIdentity, "must be unset for unknown signature state")) } if len(signature.SignedClaims) != 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("signedClaims"), signature.SignedClaims, "must be unset for unknown signature state")) } if signature.IssuedBy != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("issuedBy"), signature.IssuedBy, "must be unset for unknown signature state")) } if signature.IssuedTo != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("issuedTo"), signature.IssuedTo, "must be unset for unknown signature state")) } } return allErrs }
func ValidateLocalResourceAccessReview(review *authorizationapi.LocalResourceAccessReview) field.ErrorList { allErrs := field.ErrorList{} if len(review.Action.Verb) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("verb"), "")) } if len(review.Action.Resource) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("resource"), "")) } return allErrs }
func ValidateIdentityProvider(identityProvider api.IdentityProvider, fldPath *field.Path) ValidationResults { validationResults := ValidationResults{} if len(identityProvider.Name) == 0 { validationResults.AddErrors(field.Required(fldPath.Child("name"), "")) } if reasons := validation.ValidateIdentityProviderName(identityProvider.Name); len(reasons) != 0 { validationResults.AddErrors(field.Invalid(fldPath.Child("name"), identityProvider.Name, strings.Join(reasons, ", "))) } if len(identityProvider.MappingMethod) == 0 { validationResults.AddErrors(field.Required(fldPath.Child("mappingMethod"), "")) } else if !validMappingMethods.Has(identityProvider.MappingMethod) { validationResults.AddErrors(field.NotSupported(fldPath.Child("mappingMethod"), identityProvider.MappingMethod, validMappingMethods.List())) } providerPath := fldPath.Child("provider") if !api.IsIdentityProviderType(identityProvider.Provider) { validationResults.AddErrors(field.Invalid(fldPath.Child("provider"), identityProvider.Provider, fmt.Sprintf("%v is invalid in this context", identityProvider.Provider))) } else { switch provider := identityProvider.Provider.(type) { case (*api.RequestHeaderIdentityProvider): validationResults.Append(ValidateRequestHeaderIdentityProvider(provider, identityProvider, fldPath)) case (*api.BasicAuthPasswordIdentityProvider): validationResults.AddErrors(ValidateRemoteConnectionInfo(provider.RemoteConnectionInfo, providerPath)...) case (*api.HTPasswdPasswordIdentityProvider): validationResults.AddErrors(ValidateFile(provider.File, providerPath.Child("file"))...) case (*api.LDAPPasswordIdentityProvider): validationResults.Append(ValidateLDAPIdentityProvider(provider, providerPath)) case (*api.KeystonePasswordIdentityProvider): validationResults.Append(ValidateKeystoneIdentityProvider(provider, identityProvider, providerPath)) case (*api.GitHubIdentityProvider): validationResults.AddErrors(ValidateGitHubIdentityProvider(provider, identityProvider.UseAsChallenger, fldPath)...) case (*api.GitLabIdentityProvider): validationResults.AddErrors(ValidateGitLabIdentityProvider(provider, fldPath)...) case (*api.GoogleIdentityProvider): validationResults.AddErrors(ValidateGoogleIdentityProvider(provider, identityProvider.UseAsChallenger, fldPath)...) case (*api.OpenIDIdentityProvider): validationResults.AddErrors(ValidateOpenIDIdentityProvider(provider, identityProvider, fldPath)...) } } return validationResults }
func ValidateActiveDirectoryConfig(config *api.ActiveDirectoryConfig) ValidationResults { validationResults := ValidationResults{} validationResults.Append(ValidateLDAPQuery(config.AllUsersQuery, field.NewPath("usersQuery"))) if len(config.UserNameAttributes) == 0 { validationResults.AddErrors(field.Required(field.NewPath("userNameAttributes"))) } if len(config.GroupMembershipAttributes) == 0 { validationResults.AddErrors(field.Required(field.NewPath("groupMembershipAttributes"))) } return validationResults }
func validateDeploymentStrategy(strategy *deployapi.DeploymentStrategy, pod *kapi.PodSpec, fldPath *field.Path) field.ErrorList { errs := field.ErrorList{} if len(strategy.Type) == 0 { errs = append(errs, field.Required(fldPath.Child("type"), "")) } if strategy.CustomParams != nil { errs = append(errs, validateCustomParams(strategy.CustomParams, fldPath.Child("customParams"))...) } switch strategy.Type { case deployapi.DeploymentStrategyTypeRecreate: if strategy.RecreateParams != nil { errs = append(errs, validateRecreateParams(strategy.RecreateParams, pod, fldPath.Child("recreateParams"))...) } case deployapi.DeploymentStrategyTypeRolling: if strategy.RollingParams == nil { errs = append(errs, field.Required(fldPath.Child("rollingParams"), "")) } else { errs = append(errs, validateRollingParams(strategy.RollingParams, pod, fldPath.Child("rollingParams"))...) } case deployapi.DeploymentStrategyTypeCustom: if strategy.CustomParams == nil { errs = append(errs, field.Required(fldPath.Child("customParams"), "")) } if strategy.RollingParams != nil { errs = append(errs, validateRollingParams(strategy.RollingParams, pod, fldPath.Child("rollingParams"))...) } if strategy.RecreateParams != nil { errs = append(errs, validateRecreateParams(strategy.RecreateParams, pod, fldPath.Child("recreateParams"))...) } case "": errs = append(errs, field.Required(fldPath.Child("type"), "strategy type is required")) default: errs = append(errs, field.Invalid(fldPath.Child("type"), strategy.Type, "unsupported strategy type, use \"Custom\" instead and specify your own strategy")) } if strategy.Labels != nil { errs = append(errs, unversionedvalidation.ValidateLabels(strategy.Labels, fldPath.Child("labels"))...) } if strategy.Annotations != nil { errs = append(errs, validation.ValidateAnnotations(strategy.Annotations, fldPath.Child("annotations"))...) } errs = append(errs, validation.ValidateResourceRequirements(&strategy.Resources, fldPath.Child("resources"))...) return errs }
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 }
func ValidateUserIdentityMapping(mapping *api.UserIdentityMapping) field.ErrorList { allErrs := kvalidation.ValidateObjectMeta(&mapping.ObjectMeta, false, ValidateIdentityName, field.NewPath("metadata")) identityPath := field.NewPath("identity") if len(mapping.Identity.Name) == 0 { allErrs = append(allErrs, field.Required(identityPath.Child("name"), "")) } if mapping.Identity.Name != mapping.Name { allErrs = append(allErrs, field.Invalid(identityPath.Child("name"), mapping.Identity.Name, "must match metadata.name")) } if len(mapping.User.Name) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("user", "name"), "")) } return allErrs }
func validateLifecycleHook(hook *deployapi.LifecycleHook, fldPath *field.Path) field.ErrorList { errs := field.ErrorList{} if len(hook.FailurePolicy) == 0 { errs = append(errs, field.Required(fldPath.Child("failurePolicy"), "")) } if hook.ExecNewPod == nil { errs = append(errs, field.Required(fldPath.Child("execNewPod"), "")) } else { errs = append(errs, validateExecNewPod(hook.ExecNewPod, fldPath.Child("execNewPod"))...) } return errs }
func ValidateOAuthIdentityProvider(clientID, clientSecret string, challenge bool) field.ErrorList { allErrs := field.ErrorList{} if len(clientID) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("provider", "clientID"))) } if len(clientSecret) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("provider", "clientSecret"))) } if challenge { allErrs = append(allErrs, field.Invalid(field.NewPath("challenge"), challenge, "oauth providers cannot be used for challenges")) } return allErrs }
// 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 }
// 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 }
// ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set. func ValidateDaemonSetSpec(spec *extensions.DaemonSetSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} // The order of these checks is important because spec.Template is tested for nil value here // before accessing its labels in the following check. if spec.Template == nil { allErrs = append(allErrs, field.Required(fldPath.Child("template"), "")) return allErrs } allErrs = append(allErrs, ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) selector, err := extensions.LabelSelectorAsSelector(spec.Selector) if err == nil && !selector.Matches(labels.Set(spec.Template.Labels)) { allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`")) } allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(spec.Template, fldPath.Child("template"))...) // Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid. allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes, fldPath.Child("template", "spec", "volumes"))...) // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec(). 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)})) } allErrs = append(allErrs, ValidateDaemonSetUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...) 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 }
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 ValidateJobSpec(spec *extensions.JobSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if spec.Parallelism != nil { allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Parallelism), fldPath.Child("parallelism"))...) } if spec.Completions != nil { allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Completions), fldPath.Child("completions"))...) } if spec.ActiveDeadlineSeconds != nil { allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.ActiveDeadlineSeconds), fldPath.Child("activeDeadlineSeconds"))...) } if spec.Selector == nil { allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) } else { allErrs = append(allErrs, ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) } if selector, err := extensions.LabelSelectorAsSelector(spec.Selector); err == nil { labels := labels.Set(spec.Template.Labels) if !selector.Matches(labels) { allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`")) } } allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...) if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure && spec.Template.Spec.RestartPolicy != api.RestartPolicyNever { allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyOnFailure), string(api.RestartPolicyNever)})) } return allErrs }
func ValidateThirdPartyResourceData(obj *extensions.ThirdPartyResourceData) field.ErrorList { allErrs := field.ErrorList{} if len(obj.Name) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("name"), "")) } return allErrs }