func validateSource(input *buildapi.BuildSource) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} switch input.Type { case buildapi.BuildSourceGit: if input.Git == nil { allErrs = append(allErrs, fielderrors.NewFieldRequired("git")) } else { allErrs = append(allErrs, validateGitSource(input.Git).Prefix("git")...) } if input.Dockerfile != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("dockerfile", "", "may not be set when type is Git")) } case buildapi.BuildSourceDockerfile: if input.Dockerfile == nil { allErrs = append(allErrs, fielderrors.NewFieldRequired("dockerfile")) } else { if len(*input.Dockerfile) > maxDockerfileLengthBytes { allErrs = append(allErrs, fielderrors.NewFieldInvalid("dockerfile", "", fmt.Sprintf("must be smaller than %d bytes", maxDockerfileLengthBytes))) } } if input.Git != nil { allErrs = append(allErrs, validateGitSource(input.Git).Prefix("git")...) } case "": allErrs = append(allErrs, fielderrors.NewFieldRequired("type")) } allErrs = append(allErrs, validateSecretRef(input.SourceSecret).Prefix("sourceSecret")...) return allErrs }
// validateInsecureEdgeTerminationPolicy tests fields for different types of // insecure options. Called by validateTLS. func validateInsecureEdgeTerminationPolicy(tls *routeapi.TLSConfig) *fielderrors.ValidationError { // 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 fielderrors.NewFieldInvalid("InsecureEdgeTerminationPolicy", 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 fielderrors.NewFieldInvalid("InsecureEdgeTerminationPolicy", tls.InsecureEdgeTerminationPolicy, msg) } return nil }
// Ensure a container's SecurityContext is in compliance with the given constraints func (s *simpleProvider) ValidateContainerSecurityContext(pod *api.Pod, container *api.Container) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if container.SecurityContext == nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext", container.SecurityContext, "No security context is set")) return allErrs } sc := container.SecurityContext allErrs = append(allErrs, s.runAsUserStrategy.Validate(pod, container)...) allErrs = append(allErrs, s.seLinuxStrategy.Validate(pod, container)...) if !s.scc.AllowPrivilegedContainer && *sc.Privileged { allErrs = append(allErrs, fielderrors.NewFieldInvalid("privileged", *sc.Privileged, "Privileged containers are not allowed")) } allErrs = append(allErrs, s.capabilitiesStrategy.Validate(pod, container)...) if !s.scc.AllowHostDirVolumePlugin { for _, v := range pod.Spec.Volumes { if v.HostPath != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("VolumeMounts", v.Name, "Host Volumes are not allowed to be used")) } } } if !s.scc.AllowHostPorts { for idx, c := range pod.Spec.Containers { allErrs = append(allErrs, s.hasHostPort(&c).Prefix(fmt.Sprintf("containers.%d", idx))...) } } return allErrs }
// Process transforms Template object into List object. It generates // Parameter values using the defined set of generators first, and then it // substitutes all Parameter expression occurrences with their corresponding // values (currently in the containers' Environment variables only). func (p *Processor) Process(template *api.Template) fielderrors.ValidationErrorList { templateErrors := fielderrors.ValidationErrorList{} if err, badParam := p.GenerateParameterValues(template); err != nil { return append(templateErrors.Prefix("Template"), fielderrors.NewFieldInvalid("parameters", *badParam, err.Error())) } for i, item := range template.Objects { if obj, ok := item.(*runtime.Unknown); ok { // TODO: use runtime.DecodeList when it returns ValidationErrorList decodedObj, err := runtime.UnstructuredJSONScheme.Decode(obj.RawJSON) if err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("objects", obj, "unable to handle object")) continue } item = decodedObj } newItem, err := p.SubstituteParameters(template.Parameters, item) if err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("parameters", template.Parameters, err.Error())) } // If an object definition's metadata includes a namespace field, the field will be stripped out of // the definition during template instantiation. This is necessary because all objects created during // instantiation are placed into the target namespace, so it would be invalid for the object to declare //a different namespace. stripNamespace(newItem) if err := util.AddObjectLabels(newItem, template.ObjectLabels); err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("labels", err, "label could not be applied")) } template.Objects[i] = newItem } return templateErrors }
func ValidateAssetConfig(config *api.AssetConfig) ValidationResults { validationResults := ValidationResults{} validationResults.Append(ValidateHTTPServingInfo(config.ServingInfo).Prefix("servingInfo")) if len(config.LogoutURL) > 0 { _, urlErrs := ValidateURL(config.LogoutURL, "logoutURL") if len(urlErrs) > 0 { validationResults.AddErrors(urlErrs...) } } urlObj, urlErrs := ValidateURL(config.PublicURL, "publicURL") if len(urlErrs) > 0 { validationResults.AddErrors(urlErrs...) } if urlObj != nil { if !strings.HasSuffix(urlObj.Path, "/") { validationResults.AddErrors(fielderrors.NewFieldInvalid("publicURL", config.PublicURL, "must have a trailing slash in path")) } } if _, urlErrs := ValidateURL(config.MasterPublicURL, "masterPublicURL"); len(urlErrs) > 0 { validationResults.AddErrors(urlErrs...) } if len(config.LoggingPublicURL) > 0 { if _, loggingURLErrs := ValidateSecureURL(config.LoggingPublicURL, "loggingPublicURL"); len(loggingURLErrs) > 0 { validationResults.AddErrors(loggingURLErrs...) } } if len(config.MetricsPublicURL) > 0 { if _, metricsURLErrs := ValidateSecureURL(config.MetricsPublicURL, "metricsPublicURL"); len(metricsURLErrs) > 0 { validationResults.AddErrors(metricsURLErrs...) } } for i, scriptFile := range config.ExtensionScripts { validationResults.AddErrors(ValidateFile(scriptFile, fmt.Sprintf("extensionScripts[%d]", i))...) } for i, stylesheetFile := range config.ExtensionStylesheets { validationResults.AddErrors(ValidateFile(stylesheetFile, fmt.Sprintf("extensionStylesheets[%d]", i))...) } nameTaken := map[string]bool{} for i, extConfig := range config.Extensions { extConfigErrors := ValidateAssetExtensionsConfig(extConfig).Prefix(fmt.Sprintf("extensions[%d]", i)) validationResults.AddErrors(extConfigErrors...) if nameTaken[extConfig.Name] { dupError := fielderrors.NewFieldInvalid(fmt.Sprintf("extensions[%d].name", i), extConfig.Name, "duplicate extension name") validationResults.AddErrors(dupError) } else { nameTaken[extConfig.Name] = true } } return validationResults }
// Process transforms Template object into List object. It generates // Parameter values using the defined set of generators first, and then it // substitutes all Parameter expression occurrences with their corresponding // values (currently in the containers' Environment variables only). func (p *Processor) Process(template *api.Template) fielderrors.ValidationErrorList { templateErrors := fielderrors.ValidationErrorList{} if err := p.GenerateParameterValues(template); err != nil { return append(templateErrors.Prefix("Template"), fielderrors.NewFieldInvalid("parameters", err, "failure to generate parameter value")) } for i, item := range template.Objects { if obj, ok := item.(*runtime.Unknown); ok { // TODO: use runtime.DecodeList when it returns ValidationErrorList obj, err := runtime.UnstructuredJSONScheme.Decode(obj.RawJSON) if err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("objects", err, "unable to handle object")) continue } item = obj } newItem, err := p.SubstituteParameters(template.Parameters, item) if err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("parameters", template.Parameters, err.Error())) } stripNamespace(newItem) if err := util.AddObjectLabels(newItem, template.ObjectLabels); err != nil { util.ReportError(&templateErrors, i, *fielderrors.NewFieldInvalid("labels", err, "label could not be applied")) } template.Objects[i] = newItem } return templateErrors }
func ValidateIdentity(identity *api.Identity) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, kvalidation.ValidateObjectMeta(&identity.ObjectMeta, false, ValidateIdentityName).Prefix("metadata")...) if len(identity.ProviderName) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("providerName")) } else if ok, msg := ValidateIdentityProviderName(identity.ProviderName); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("providerName", identity.ProviderName, msg)) } if len(identity.ProviderUserName) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("providerUserName")) } else if ok, msg := ValidateIdentityProviderName(identity.ProviderUserName); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("providerUserName", identity.ProviderUserName, msg)) } if len(identity.ProviderName) > 0 && len(identity.ProviderUserName) > 0 { expectedIdentityName := identity.ProviderName + ":" + identity.ProviderUserName if identity.Name != expectedIdentityName { allErrs = append(allErrs, fielderrors.NewFieldInvalid("user.name", identity.User.Name, fmt.Sprintf("must be %s", expectedIdentityName))) } } if ok, msg := ValidateUserName(identity.User.Name, false); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("user.name", identity.User.Name, msg)) } if len(identity.User.Name) == 0 && len(identity.User.UID) != 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("user.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, fielderrors.NewFieldRequired("user.uid")) } return allErrs }
func validateDockerStrategy(strategy *buildapi.DockerBuildStrategy) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if strategy.From != nil { allErrs = append(allErrs, validateFromImageReference(strategy.From).Prefix("from")...) } allErrs = append(allErrs, validateSecretRef(strategy.PullSecret).Prefix("pullSecret")...) if len(strategy.DockerfilePath) != 0 { cleaned := path.Clean(strategy.DockerfilePath) switch { case strings.HasPrefix(cleaned, "/"): allErrs = append(allErrs, fielderrors.NewFieldInvalid("dockerfilePath", strategy.DockerfilePath, "dockerfilePath must not be an absolute path")) case strings.HasPrefix(cleaned, ".."): allErrs = append(allErrs, fielderrors.NewFieldInvalid("dockerfilePath", strategy.DockerfilePath, "dockerfilePath must not start with ..")) default: if cleaned == "." { cleaned = "" } strategy.DockerfilePath = cleaned } } return allErrs }
func validateHTTPIngressRuleValue(httpIngressRuleValue *extensions.HTTPIngressRuleValue) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} if len(httpIngressRuleValue.Paths) == 0 { allErrs = append(allErrs, errs.NewFieldRequired("paths")) } for _, rule := range httpIngressRuleValue.Paths { if len(rule.Path) > 0 { if !strings.HasPrefix(rule.Path, "/") { allErrs = append(allErrs, errs.NewFieldInvalid("path", rule.Path, "path must begin with /")) } // 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, errs.NewFieldInvalid("path", rule.Path, "httpIngressRuleValue.path must be a valid regex.")) } } allErrs = append(allErrs, validateIngressBackend(&rule.Backend).Prefix("backend")...) } return allErrs }
// Validate ensures that the specified values fall within the range of the strategy. func (s *mustRunAsRange) Validate(pod *api.Pod, container *api.Container) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if container.SecurityContext == nil { detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext", container.SecurityContext, detail)) return allErrs } if container.SecurityContext.RunAsUser == nil { detail := fmt.Sprintf("unable to validate nil RunAsUser for container %s", container.Name) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext.runAsUser", container.SecurityContext.RunAsUser, detail)) return allErrs } if *container.SecurityContext.RunAsUser < *s.opts.UIDRangeMin || *container.SecurityContext.RunAsUser > *s.opts.UIDRangeMax { detail := fmt.Sprintf("UID on container %s does not match required range. Found %d, required min: %d max: %d", container.Name, *container.SecurityContext.RunAsUser, *s.opts.UIDRangeMin, *s.opts.UIDRangeMax) allErrs = append(allErrs, fielderrors.NewFieldInvalid("securityContext.runAsUser", *container.SecurityContext.RunAsUser, detail)) } return allErrs }
func validateRollingParams(params *deployapi.RollingDeploymentStrategyParams) fielderrors.ValidationErrorList { errs := fielderrors.ValidationErrorList{} if params.IntervalSeconds != nil && *params.IntervalSeconds < 1 { errs = append(errs, fielderrors.NewFieldInvalid("intervalSeconds", *params.IntervalSeconds, "must be >0")) } if params.UpdatePeriodSeconds != nil && *params.UpdatePeriodSeconds < 1 { errs = append(errs, fielderrors.NewFieldInvalid("updatePeriodSeconds", *params.UpdatePeriodSeconds, "must be >0")) } if params.TimeoutSeconds != nil && *params.TimeoutSeconds < 1 { errs = append(errs, fielderrors.NewFieldInvalid("timeoutSeconds", *params.TimeoutSeconds, "must be >0")) } if params.UpdatePercent != nil { p := *params.UpdatePercent if p == 0 || p < -100 || p > 100 { errs = append(errs, fielderrors.NewFieldInvalid("updatePercent", *params.UpdatePercent, "must be between 1 and 100 or between -1 and -100 (inclusive)")) } } if params.Pre != nil { errs = append(errs, validateLifecycleHook(params.Pre).Prefix("pre")...) } if params.Post != nil { errs = append(errs, validateLifecycleHook(params.Post).Prefix("post")...) } return errs }
func ValidatePolicyBinding(policyBinding *authorizationapi.PolicyBinding, isNamespaced bool) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&policyBinding.ObjectMeta, isNamespaced, PolicyBindingNameValidator(policyBinding.PolicyRef.Namespace)).Prefix("metadata")...) if !isNamespaced { if len(policyBinding.PolicyRef.Namespace) > 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("policyRef.namespace", policyBinding.PolicyRef.Namespace, "may not reference another namespace")) } } for roleBindingKey, roleBinding := range policyBinding.RoleBindings { if roleBinding == nil { allErrs = append(allErrs, fielderrors.NewFieldRequired("roleBindings."+roleBindingKey)) } if roleBinding.RoleRef.Namespace != policyBinding.PolicyRef.Namespace { allErrs = append(allErrs, fielderrors.NewFieldInvalid("roleBindings."+roleBindingKey+".roleRef.namespace", policyBinding.PolicyRef.Namespace, "must be "+policyBinding.PolicyRef.Namespace)) } if roleBindingKey != roleBinding.Name { allErrs = append(allErrs, fielderrors.NewFieldInvalid("roleBindings."+roleBindingKey+".metadata.name", roleBinding.Name, "must be "+roleBindingKey)) } allErrs = append(allErrs, ValidateRoleBinding(roleBinding, isNamespaced).Prefix("roleBindings."+roleBindingKey)...) } return allErrs }
func ValidateServingInfo(info api.ServingInfo) ValidationResults { validationResults := ValidationResults{} validationResults.AddErrors(ValidateHostPort(info.BindAddress, "bindAddress")...) validationResults.AddErrors(ValidateCertInfo(info.ServerCert, false)...) if len(info.NamedCertificates) > 0 && len(info.ServerCert.CertFile) == 0 { validationResults.AddErrors(fielderrors.NewFieldInvalid("namedCertificates", "", "a default certificate and key is required in certFile/keyFile in order to use namedCertificates")) } validationResults.Append(ValidateNamedCertificates("namedCertificates", info.NamedCertificates)) switch info.BindNetwork { case "tcp", "tcp4", "tcp6": default: validationResults.AddErrors(fielderrors.NewFieldInvalid("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, "clientCA")...) } } else { if len(info.ClientCA) > 0 { validationResults.AddErrors(fielderrors.NewFieldInvalid("clientCA", info.ClientCA, "cannot specify a clientCA without a certFile")) } } return validationResults }
func TestCheckInvalidErr(t *testing.T) { tests := []struct { err error expected string }{ { errors.NewInvalid("Invalid1", "invalidation", fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("Cause", "single", "details")}), `Error from server: Invalid1 "invalidation" is invalid: Cause: invalid value 'single', Details: details`, }, { errors.NewInvalid("Invalid2", "invalidation", fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("Cause", "multi1", "details"), fielderrors.NewFieldInvalid("Cause", "multi2", "details")}), `Error from server: Invalid2 "invalidation" is invalid: [Cause: invalid value 'multi1', Details: details, Cause: invalid value 'multi2', Details: details]`, }, { errors.NewInvalid("Invalid3", "invalidation", fielderrors.ValidationErrorList{}), `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) } } }
func ValidateJobSpec(spec *experimental.JobSpec) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} if spec.Parallelism != nil && *spec.Parallelism < 0 { allErrs = append(allErrs, errs.NewFieldInvalid("parallelism", spec.Parallelism, isNegativeErrorMsg)) } if spec.Completions != nil && *spec.Completions < 0 { allErrs = append(allErrs, errs.NewFieldInvalid("completions", spec.Completions, isNegativeErrorMsg)) } selector := labels.Set(spec.Selector).AsSelector() if selector.Empty() { allErrs = append(allErrs, errs.NewFieldRequired("selector")) } if spec.Template == nil { allErrs = append(allErrs, errs.NewFieldRequired("template")) } else { labels := labels.Set(spec.Template.Labels) if !selector.Matches(labels) { allErrs = append(allErrs, errs.NewFieldInvalid("template.labels", spec.Template.Labels, "selector does not match template")) } allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(spec.Template).Prefix("template")...) if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure && spec.Template.Spec.RestartPolicy != api.RestartPolicyNever { allErrs = append(allErrs, errs.NewFieldValueNotSupported("template.spec.restartPolicy", spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyOnFailure), string(api.RestartPolicyNever)})) } } return allErrs }
func validateToImageReference(reference *kapi.ObjectReference) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} kind, name, namespace := reference.Kind, reference.Name, reference.Namespace switch kind { case "ImageStreamTag": if len(name) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("name")) } else if _, _, ok := imageapi.SplitImageStreamTag(name); !ok { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", name, "ImageStreamTag object references must be in the form <name>:<tag>")) } if len(namespace) != 0 && !kvalidation.IsDNS1123Subdomain(namespace) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("namespace", namespace, "namespace must be a valid subdomain")) } case "DockerImage": if len(namespace) != 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("namespace", namespace, "namespace is not valid when used with a 'DockerImage'")) } if _, err := imageapi.ParseDockerImageReference(name); err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", name, fmt.Sprintf("name is not a valid Docker pull specification: %v", err))) } case "": allErrs = append(allErrs, fielderrors.NewFieldRequired("kind")) default: allErrs = append(allErrs, fielderrors.NewFieldInvalid("kind", kind, "the target of build output must be an 'ImageStreamTag' or 'DockerImage'")) } return allErrs }
func ValidateEtcdConfig(config *api.EtcdConfig) ValidationResults { validationResults := ValidationResults{} validationResults.Append(ValidateServingInfo(config.ServingInfo).Prefix("servingInfo")) if config.ServingInfo.BindNetwork == "tcp6" { validationResults.AddErrors(fielderrors.NewFieldInvalid("servingInfo.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(fielderrors.NewFieldInvalid("servingInfo.namedCertificates", "<not shown>", "namedCertificates are not supported for etcd")) } validationResults.Append(ValidateServingInfo(config.PeerServingInfo).Prefix("peerServingInfo")) if config.ServingInfo.BindNetwork == "tcp6" { validationResults.AddErrors(fielderrors.NewFieldInvalid("peerServingInfo.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(fielderrors.NewFieldInvalid("peerServingInfo.namedCertificates", "<not shown>", "namedCertificates are not supported for etcd")) } validationResults.AddErrors(ValidateHostPort(config.Address, "address")...) validationResults.AddErrors(ValidateHostPort(config.PeerAddress, "peerAddress")...) if len(config.StorageDir) == 0 { validationResults.AddErrors(fielderrors.NewFieldRequired("storageDirectory")) } return validationResults }
// ValidateClusterNetwork tests if required fields in the ClusterNetwork are set. func ValidateClusterNetwork(clusterNet *sdnapi.ClusterNetwork) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&clusterNet.ObjectMeta, false, oapi.MinimalNameRequirements).Prefix("metadata")...) clusterIP, clusterIPNet, err := net.ParseCIDR(clusterNet.Network) if err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("network", clusterNet.Network, err.Error())) } else { ones, bitSize := clusterIPNet.Mask.Size() if (bitSize - ones) <= clusterNet.HostSubnetLength { allErrs = append(allErrs, fielderrors.NewFieldInvalid("hostSubnetLength", clusterNet.HostSubnetLength, "subnet length is greater than cluster Mask")) } } serviceIP, serviceIPNet, err := net.ParseCIDR(clusterNet.ServiceNetwork) if err != nil { allErrs = append(allErrs, fielderrors.NewFieldInvalid("serviceNetwork", clusterNet.ServiceNetwork, err.Error())) } if (clusterIPNet != nil) && (serviceIP != nil) && clusterIPNet.Contains(serviceIP) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("serviceNetwork", clusterNet.ServiceNetwork, "service network overlaps with cluster network")) } if (serviceIPNet != nil) && (clusterIP != nil) && serviceIPNet.Contains(clusterIP) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("network", clusterNet.Network, "cluster network overlaps with service network")) } return allErrs }
func validateBuildSpec(spec *buildapi.BuildSpec) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} hasSourceType := len(spec.Source.Type) != 0 switch t := spec.Strategy.Type; { // 'source' is optional for Custom builds case t == buildapi.CustomBuildStrategyType && hasSourceType: allErrs = append(allErrs, validateSource(&spec.Source).Prefix("source")...) case t == buildapi.SourceBuildStrategyType: allErrs = append(allErrs, validateSource(&spec.Source).Prefix("source")...) if spec.Source.Type == buildapi.BuildSourceDockerfile { allErrs = append(allErrs, fielderrors.NewFieldInvalid("source.type", nil, "may not be type Dockerfile for source builds")) } case t == buildapi.DockerBuildStrategyType: allErrs = append(allErrs, validateSource(&spec.Source).Prefix("source")...) } if spec.Revision != nil { allErrs = append(allErrs, validateRevision(spec.Revision).Prefix("revision")...) } if spec.CompletionDeadlineSeconds != nil { if *spec.CompletionDeadlineSeconds <= 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("completionDeadlineSeconds", spec.CompletionDeadlineSeconds, "completionDeadlineSeconds must be a positive integer greater than 0")) } } allErrs = append(allErrs, validateOutput(&spec.Output).Prefix("output")...) allErrs = append(allErrs, validateStrategy(&spec.Strategy).Prefix("strategy")...) // TODO: validate resource requirements (prereq: https://github.com/kubernetes/kubernetes/pull/7059) return allErrs }
func ValidateDeploymentConfig(config *deployapi.DeploymentConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&config.ObjectMeta, true, validation.NameIsDNSSubdomain).Prefix("metadata")...) // TODO: Refactor to validate spec and status separately for i := range config.Spec.Triggers { allErrs = append(allErrs, validateTrigger(&config.Spec.Triggers[i]).PrefixIndex(i).Prefix("spec.triggers")...) } allErrs = append(allErrs, validateDeploymentStrategy(&config.Spec.Strategy).Prefix("spec.strategy")...) if config.Spec.Template == nil { allErrs = append(allErrs, fielderrors.NewFieldRequired("spec.template")) } else { allErrs = append(allErrs, validation.ValidatePodTemplateSpec(config.Spec.Template).Prefix("spec.template")...) } if config.Status.LatestVersion < 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("status.latestVersion", config.Status.LatestVersion, "latestVersion cannot be negative")) } if config.Spec.Replicas < 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("spec.replicas", config.Spec.Replicas, "replicas cannot be negative")) } if len(config.Spec.Selector) == 0 { allErrs = append(allErrs, fielderrors.NewFieldInvalid("spec.selector", config.Spec.Selector, "selector cannot be empty")) } return allErrs }
func validateImageChangeParams(params *deployapi.DeploymentTriggerImageChangeParams) fielderrors.ValidationErrorList { errs := fielderrors.ValidationErrorList{} if len(params.From.Name) != 0 { if len(params.From.Kind) == 0 { params.From.Kind = "ImageStream" } kinds := util.NewStringSet("ImageRepository", "ImageStream", "ImageStreamTag") if !kinds.Has(params.From.Kind) { msg := fmt.Sprintf("kind must be one of: %s", strings.Join(kinds.List(), ", ")) errs = append(errs, fielderrors.NewFieldInvalid("from.kind", params.From.Kind, msg)) } if !util.IsDNS1123Subdomain(params.From.Name) { errs = append(errs, fielderrors.NewFieldInvalid("from.name", params.From.Name, "name must be a valid subdomain")) } if len(params.From.Namespace) != 0 && !util.IsDNS1123Subdomain(params.From.Namespace) { errs = append(errs, fielderrors.NewFieldInvalid("from.namespace", params.From.Namespace, "namespace must be a valid subdomain")) } if len(params.RepositoryName) != 0 { errs = append(errs, fielderrors.NewFieldInvalid("repositoryName", params.RepositoryName, "only one of 'from', 'repository' name may be specified")) } } else { if len(params.RepositoryName) == 0 { errs = append(errs, fielderrors.NewFieldRequired("from")) } } if len(params.ContainerNames) == 0 { errs = append(errs, fielderrors.NewFieldRequired("containerNames")) } return errs }
func ValidateLDAPQuery(query api.LDAPQuery) ValidationResults { validationResults := ValidationResults{} if _, err := ldap.ParseDN(query.BaseDN); err != nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("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(fielderrors.NewFieldInvalid("scope", query.Scope, "invalid LDAP search scope")) } } if len(query.DerefAliases) > 0 { if _, err := ldaputil.DetermineDerefAliasesBehavior(query.DerefAliases); err != nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("derefAliases", query.DerefAliases, "LDAP alias dereferencing instruction invalid")) } } if query.TimeLimit < 0 { validationResults.AddErrors(fielderrors.NewFieldInvalid("timeout", query.TimeLimit, "timeout must be equal to or greater than zero")) } if _, err := ldap.CompileFilter(query.Filter); err != nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("filter", query.Filter, fmt.Sprintf("invalid query filter: %v", err))) } return validationResults }
func ValidateLDAPSyncConfig(config *api.LDAPSyncConfig) ValidationResults { validationResults := ValidateLDAPClientConfig(config.URL, config.BindDN, config.BindPassword, config.CA, config.Insecure) 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(fielderrors.NewFieldInvalid("", config, fmt.Sprintf("only one schema-specific config is allowed; found %v", schemaConfigsFound))) } if len(schemaConfigsFound) == 0 { validationResults.AddErrors(fielderrors.NewFieldInvalid("", config, fmt.Sprintf("exactly one schema-specific config is required; one of %v", []string{"rfc2307", "activeDirectory", "augmentedActiveDirectory"}))) } return validationResults }
// ValidateRoute tests if required fields in the route are set. func ValidateRoute(route *routeapi.Route) fielderrors.ValidationErrorList { result := fielderrors.ValidationErrorList{} //ensure meta is set properly result = append(result, kval.ValidateObjectMeta(&route.ObjectMeta, true, oapi.GetNameValidationFunc(kval.ValidatePodName)).Prefix("metadata")...) //host is not required but if it is set ensure it meets DNS requirements if len(route.Host) > 0 { if !util.IsDNS1123Subdomain(route.Host) { result = append(result, fielderrors.NewFieldInvalid("host", route.Host, "host must conform to DNS 952 subdomain conventions")) } } if len(route.Path) > 0 && !strings.HasPrefix(route.Path, "/") { result = append(result, fielderrors.NewFieldInvalid("path", route.Path, "path must begin with /")) } if len(route.ServiceName) == 0 { result = append(result, fielderrors.NewFieldRequired("serviceName")) } if errs := validateTLS(route); len(errs) != 0 { result = append(result, errs.Prefix("tls")...) } return result }
func ValidateSessionConfig(config *api.SessionConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} // Validate session secrets file, if specified if len(config.SessionSecretsFile) > 0 { fileErrs := ValidateFile(config.SessionSecretsFile, "sessionSecretsFile") 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, fielderrors.NewFieldInvalid("sessionSecretsFile", config.SessionSecretsFile, fmt.Sprintf("error reading file: %v", err))) } else { for _, err := range ValidateSessionSecrets(secrets) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("sessionSecretsFile", config.SessionSecretsFile, err.Error())) } } } } if len(config.SessionName) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("sessionName")) } return allErrs }
// ValidateImageStreamMapping tests required fields for an ImageStreamMapping. func ValidateImageStreamMapping(mapping *api.ImageStreamMapping) fielderrors.ValidationErrorList { result := fielderrors.ValidationErrorList{} result = append(result, validation.ValidateObjectMeta(&mapping.ObjectMeta, true, oapi.MinimalNameRequirements).Prefix("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, fielderrors.NewFieldInvalid("dockerImageRepository", mapping.DockerImageRepository, err.Error())) } case hasName: default: result = append(result, fielderrors.NewFieldRequired("name")) result = append(result, fielderrors.NewFieldRequired("dockerImageRepository")) } if ok, msg := validation.ValidateNamespaceName(mapping.Namespace, false); !ok { result = append(result, fielderrors.NewFieldInvalid("namespace", mapping.Namespace, msg)) } if len(mapping.Tag) == 0 { result = append(result, fielderrors.NewFieldRequired("tag")) } if errs := ValidateImage(&mapping.Image).Prefix("image"); len(errs) != 0 { result = append(result, errs...) } return result }
func ValidateRoleBindingSubject(subject kapi.ObjectReference, isNamespaced bool) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} if len(subject.Name) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("name")) } if len(subject.UID) != 0 { allErrs = append(allErrs, fielderrors.NewFieldForbidden("uid", subject.UID)) } if len(subject.APIVersion) != 0 { allErrs = append(allErrs, fielderrors.NewFieldForbidden("apiVersion", subject.APIVersion)) } if len(subject.ResourceVersion) != 0 { allErrs = append(allErrs, fielderrors.NewFieldForbidden("resourceVersion", subject.ResourceVersion)) } if len(subject.FieldPath) != 0 { allErrs = append(allErrs, fielderrors.NewFieldForbidden("fieldPath", subject.FieldPath)) } switch subject.Kind { case authorizationapi.ServiceAccountKind: if valid, reason := validation.ValidateServiceAccountName(subject.Name, false); len(subject.Name) > 0 && !valid { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, reason)) } if !isNamespaced && len(subject.Namespace) == 0 { allErrs = append(allErrs, fielderrors.NewFieldRequired("namespace")) } case authorizationapi.UserKind: if valid, reason := uservalidation.ValidateUserName(subject.Name, false); len(subject.Name) > 0 && !valid { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, reason)) } case authorizationapi.GroupKind: if valid, reason := uservalidation.ValidateGroupName(subject.Name, false); len(subject.Name) > 0 && !valid { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, reason)) } case authorizationapi.SystemUserKind: isValidSAName, _ := validation.ValidateServiceAccountName(subject.Name, false) isValidUserName, _ := uservalidation.ValidateUserName(subject.Name, false) if isValidSAName || isValidUserName { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, "conforms to User.name or ServiceAccount.name restrictions")) } case authorizationapi.SystemGroupKind: if valid, _ := uservalidation.ValidateGroupName(subject.Name, false); len(subject.Name) > 0 && valid { allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, "conforms to Group.name restrictions")) } default: allErrs = append(allErrs, fielderrors.NewFieldValueNotSupported("kind", subject.Kind, []string{authorizationapi.ServiceAccountKind, authorizationapi.UserKind, authorizationapi.GroupKind, authorizationapi.SystemGroupKind, authorizationapi.SystemUserKind})) } return allErrs }
func ValidateClusterAutoscaler(autoscaler *extensions.ClusterAutoscaler) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} if autoscaler.Name != "ClusterAutoscaler" { allErrs = append(allErrs, errs.NewFieldInvalid("name", autoscaler.Name, `name must be ClusterAutoscaler`)) } if autoscaler.Namespace != api.NamespaceDefault { allErrs = append(allErrs, errs.NewFieldInvalid("namespace", autoscaler.Namespace, `namespace must be default`)) } allErrs = append(allErrs, validateClusterAutoscalerSpec(autoscaler.Spec)...) return allErrs }
func ValidateDeploymentConfigUpdate(newConfig *deployapi.DeploymentConfig, oldConfig *deployapi.DeploymentConfig) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&newConfig.ObjectMeta, &oldConfig.ObjectMeta).Prefix("metadata")...) allErrs = append(allErrs, ValidateDeploymentConfig(newConfig)...) if newConfig.LatestVersion < oldConfig.LatestVersion { allErrs = append(allErrs, fielderrors.NewFieldInvalid("latestVersion", newConfig.LatestVersion, "latestVersion cannot be decremented")) } else if newConfig.LatestVersion > (oldConfig.LatestVersion + 1) { allErrs = append(allErrs, fielderrors.NewFieldInvalid("latestVersion", newConfig.LatestVersion, "latestVersion can only be incremented by 1")) } return allErrs }
func ValidateRequestHeaderIdentityProvider(provider *api.RequestHeaderIdentityProvider, identityProvider api.IdentityProvider) ValidationResults { validationResults := ValidationResults{} if len(provider.ClientCA) > 0 { validationResults.AddErrors(ValidateFile(provider.ClientCA, "provider.clientCA")...) } if len(provider.Headers) == 0 { validationResults.AddErrors(fielderrors.NewFieldRequired("provider.headers")) } if identityProvider.UseAsChallenger && len(provider.ChallengeURL) == 0 { err := fielderrors.NewFieldRequired("provider.challengeURL") err.Detail = "challengeURL is required if challenge=true" validationResults.AddErrors(err) } if identityProvider.UseAsLogin && len(provider.LoginURL) == 0 { err := fielderrors.NewFieldRequired("provider.loginURL") err.Detail = "loginURL is required if login=true" validationResults.AddErrors(err) } if len(provider.ChallengeURL) > 0 { url, urlErrs := ValidateURL(provider.ChallengeURL, "provider.challengeURL") validationResults.AddErrors(urlErrs...) if len(urlErrs) == 0 && !strings.Contains(url.RawQuery, redirector.URLToken) && !strings.Contains(url.RawQuery, redirector.QueryToken) { validationResults.AddWarnings( fielderrors.NewFieldInvalid( "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, "provider.loginURL") validationResults.AddErrors(urlErrs...) if len(urlErrs) == 0 && !strings.Contains(url.RawQuery, redirector.URLToken) && !strings.Contains(url.RawQuery, redirector.QueryToken) { validationResults.AddWarnings( fielderrors.NewFieldInvalid( "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(fielderrors.NewFieldInvalid("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 }