func ValidateClusterRoleBinding(roleBinding *rbac.ClusterRoleBinding) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, false, minimalNameRequirements, field.NewPath("metadata"))...) // TODO allow multiple API groups. For now, restrict to one, but I can envision other experimental roles in other groups taking // advantage of the binding infrastructure if roleBinding.RoleRef.APIGroup != rbac.GroupName { allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName})) } switch roleBinding.RoleRef.Kind { case "ClusterRole": default: allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"ClusterRole"})) } if len(roleBinding.RoleRef.Name) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), "")) } else { for _, msg := range minimalNameRequirements(roleBinding.RoleRef.Name, false) { allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg)) } } subjectsPath := field.NewPath("subjects") for i, subject := range roleBinding.Subjects { allErrs = append(allErrs, validateRoleBindingSubject(subject, false, subjectsPath.Index(i))...) } return allErrs }
func validateGrantConfig(config api.GrantConfig, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if !api.ValidGrantHandlerTypes.Has(string(config.Method)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("method"), config.Method, api.ValidGrantHandlerTypes.List())) } if !api.ValidServiceAccountGrantHandlerTypes.Has(string(config.ServiceAccountMethod)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("serviceAccountMethod"), config.ServiceAccountMethod, api.ValidServiceAccountGrantHandlerTypes.List())) } 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{} allErrs = append(allErrs, ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) if spec.Template == nil { allErrs = append(allErrs, field.Required(fldPath.Child("template"))) return allErrs } 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")) } 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)})) } return allErrs }
// validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy. func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.FSType) field.ErrorList { allErrs := field.ErrorList{} allowed := sets.NewString(string(extensions.HostPath), string(extensions.EmptyDir), string(extensions.GCEPersistentDisk), string(extensions.AWSElasticBlockStore), string(extensions.GitRepo), string(extensions.Secret), string(extensions.NFS), string(extensions.ISCSI), string(extensions.Glusterfs), string(extensions.PersistentVolumeClaim), string(extensions.RBD), string(extensions.Cinder), string(extensions.CephFS), string(extensions.DownwardAPI), string(extensions.FC)) for _, v := range volumes { if !allowed.Has(string(v)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List())) } } 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 }
// 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 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 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 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 ValidateJobTemplateSpec(spec *batch.JobTemplateSpec, fldPath *field.Path) field.ErrorList { allErrs := validateJobSpec(&spec.Spec, fldPath.Child("spec")) // jobtemplate will always have the selector automatically generated if spec.Spec.Selector != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("spec", "selector"), spec.Spec.Selector, "`selector` will be auto-generated")) } if spec.Spec.ManualSelector != nil && *spec.Spec.ManualSelector { allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "manualSelector"), spec.Spec.ManualSelector, []string{"nil", "false"})) } return allErrs }
// validatePSPSELinuxContext validates the SELinuxContext fields of PodSecurityPolicy. func validatePSPSELinuxContext(fldPath *field.Path, seLinuxContext *extensions.SELinuxContextStrategyOptions) field.ErrorList { allErrs := field.ErrorList{} // ensure the selinux strategy has a valid type supportedSELinuxContextTypes := sets.NewString(string(extensions.SELinuxStrategyMustRunAs), string(extensions.SELinuxStrategyRunAsAny)) if !supportedSELinuxContextTypes.Has(string(seLinuxContext.Type)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), seLinuxContext.Type, supportedSELinuxContextTypes.List())) } 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 }
// validateWildcardPolicy tests that the wildcard policy is either empty or one of the supported types. func validateWildcardPolicy(policy routeapi.WildcardPolicyType, fldPath *field.Path) *field.Error { if len(policy) == 0 { return nil } // Check if policy is one of None or Subdomain. if !allowedWildcardPoliciesSet.Has(string(policy)) { return field.NotSupported(fldPath, policy, allowedWildcardPolicies) } return nil }
// validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy. func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.FSType) field.ErrorList { allErrs := field.ErrorList{} allowed := psputil.GetAllFSTypesAsSet() // add in the * value since that is a pseudo type that is not included by default allowed.Insert(string(extensions.All)) for _, v := range volumes { if !allowed.Has(string(v)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List())) } } return allErrs }
// validateTLS tests fields for different types of TLS combinations are set. Called // by ValidateRoute. func validateTLS(route *routeapi.Route, fldPath *field.Path) field.ErrorList { result := field.ErrorList{} tls := route.Spec.TLS // no tls config present, no need for validation if tls == nil { return nil } switch tls.Termination { // reencrypt must specify destination ca cert // cert, key, cacert may not be specified because the route may be a wildcard case routeapi.TLSTerminationReencrypt: if len(tls.DestinationCACertificate) == 0 { result = append(result, field.Required(fldPath.Child("destinationCACertificate"), "")) } //passthrough term should not specify any cert case routeapi.TLSTerminationPassthrough: if len(tls.Certificate) > 0 { result = append(result, field.Invalid(fldPath.Child("certificate"), tls.Certificate, "passthrough termination does not support certificates")) } if len(tls.Key) > 0 { result = append(result, field.Invalid(fldPath.Child("key"), tls.Key, "passthrough termination does not support certificates")) } if len(tls.CACertificate) > 0 { result = append(result, field.Invalid(fldPath.Child("caCertificate"), tls.CACertificate, "passthrough termination does not support certificates")) } if len(tls.DestinationCACertificate) > 0 { result = append(result, field.Invalid(fldPath.Child("destinationCACertificate"), tls.DestinationCACertificate, "passthrough termination does not support certificates")) } // edge cert should only specify cert, key, and cacert but those certs // may not be specified if the route is a wildcard route case routeapi.TLSTerminationEdge: if len(tls.DestinationCACertificate) > 0 { result = append(result, field.Invalid(fldPath.Child("destinationCACertificate"), tls.DestinationCACertificate, "edge termination does not support destination certificates")) } default: validValues := []string{string(routeapi.TLSTerminationEdge), string(routeapi.TLSTerminationPassthrough), string(routeapi.TLSTerminationReencrypt)} result = append(result, field.NotSupported(fldPath.Child("termination"), tls.Termination, validValues)) } if err := validateInsecureEdgeTerminationPolicy(tls); err != nil { result = append(result, err) } result = append(result, validateNoDoubleEscapes(tls)...) return result }
func validateConcurrencyPolicy(concurrencyPolicy *batch.ConcurrencyPolicy, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} switch *concurrencyPolicy { case batch.AllowConcurrent, batch.ForbidConcurrent, batch.ReplaceConcurrent: break case "": allErrs = append(allErrs, field.Required(fldPath, "")) default: validValues := []string{string(batch.AllowConcurrent), string(batch.ForbidConcurrent), string(batch.ReplaceConcurrent)} allErrs = append(allErrs, field.NotSupported(fldPath, *concurrencyPolicy, validValues)) } return allErrs }
func ValidateStorageVersionLevel(level string, knownAPILevels, deadAPILevels []string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(level) == 0 { allErrs = append(allErrs, field.Required(fldPath, "")) return allErrs } supportedLevels := sets.NewString(knownAPILevels...) supportedLevels.Delete(deadAPILevels...) if !supportedLevels.Has(level) { allErrs = append(allErrs, field.NotSupported(fldPath, level, supportedLevels.List())) } return allErrs }
// Create ensures a pod is bound to a specific host. func (r *BindingREST) Create(ctx api.Context, obj runtime.Object) (out runtime.Object, err error) { binding := obj.(*api.Binding) // TODO: move me to a binding strategy if len(binding.Target.Kind) != 0 && binding.Target.Kind != "Node" { // TODO: When validation becomes versioned, this gets more complicated. return nil, errors.NewInvalid("binding", binding.Name, field.ErrorList{field.NotSupported(field.NewPath("target", "kind"), binding.Target.Kind, []string{"Node", "<empty>"})}) } if len(binding.Target.Name) == 0 { // TODO: When validation becomes versioned, this gets more complicated. return nil, errors.NewInvalid("binding", binding.Name, field.ErrorList{field.Required(field.NewPath("target", "name"))}) } err = r.assignPod(ctx, binding.Name, binding.Target.Name, binding.Annotations) out = &unversioned.Status{Status: unversioned.StatusSuccess} return }
// validatePSPSupplementalGroup validates the SupplementalGroupsStrategyOptions fields of the PodSecurityPolicy. func validatePSPSupplementalGroup(fldPath *field.Path, groupOptions *extensions.SupplementalGroupsStrategyOptions) field.ErrorList { allErrs := field.ErrorList{} supportedRules := sets.NewString( string(extensions.SupplementalGroupsStrategyRunAsAny), string(extensions.SupplementalGroupsStrategyMustRunAs), ) if !supportedRules.Has(string(groupOptions.Rule)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List())) } for idx, rng := range groupOptions.Ranges { allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...) } return allErrs }
// validateWildcardPolicy tests that the wildcard policy is either empty or one of the supported types. func validateWildcardPolicy(host string, policy routeapi.WildcardPolicyType, fldPath *field.Path) *field.Error { if len(policy) == 0 { return nil } // Check if policy is one of None or Subdomain. if !allowedWildcardPoliciesSet.Has(string(policy)) { return field.NotSupported(fldPath, policy, allowedWildcardPolicies) } if policy == routeapi.WildcardPolicySubdomain && len(host) == 0 { return field.Invalid(fldPath, policy, "host name not specified for wildcard policy") } return nil }
// validatePSPRunAsUser validates the RunAsUser fields of PodSecurityPolicy. func validatePSPRunAsUser(fldPath *field.Path, runAsUser *extensions.RunAsUserStrategyOptions) field.ErrorList { allErrs := field.ErrorList{} // ensure the user strategy has a valid type supportedRunAsUserTypes := sets.NewString(string(extensions.RunAsUserStrategyMustRunAs), string(extensions.RunAsUserStrategyMustRunAsNonRoot), string(extensions.RunAsUserStrategyRunAsAny)) if !supportedRunAsUserTypes.Has(string(runAsUser.Type)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), runAsUser.Type, supportedRunAsUserTypes.List())) } // validate range settings for idx, rng := range runAsUser.Ranges { allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...) } return allErrs }
func ValidateDeploymentStrategy(strategy *extensions.DeploymentStrategy, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} switch strategy.Type { case extensions.RecreateDeploymentStrategyType: if strategy.RollingUpdate != nil { allErrs = append(allErrs, field.Forbidden(fldPath.Child("rollingUpdate"), "may not be specified when strategy `type` is '"+string(extensions.RecreateDeploymentStrategyType+"'"))) } case extensions.RollingUpdateDeploymentStrategyType: // This should never happen since it's set and checked in defaults.go if strategy.RollingUpdate == nil { allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), "this should be defaulted and never be nil")) } else { allErrs = append(allErrs, ValidateRollingUpdateDeployment(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...) } default: validValues := []string{string(extensions.RecreateDeploymentStrategyType), string(extensions.RollingUpdateDeploymentStrategyType)} allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues)) } return allErrs }
func validateHorizontalPodAutoscalerSpec(autoscaler extensions.HorizontalPodAutoscalerSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if autoscaler.MinReplicas != nil && *autoscaler.MinReplicas < 1 { allErrs = append(allErrs, field.Invalid(fldPath.Child("minReplicas"), *autoscaler.MinReplicas, "must be greater than 0")) } if autoscaler.MaxReplicas < 1 { allErrs = append(allErrs, field.Invalid(fldPath.Child("maxReplicas"), autoscaler.MaxReplicas, "must be greater than 0")) } if autoscaler.MinReplicas != nil && autoscaler.MaxReplicas < *autoscaler.MinReplicas { allErrs = append(allErrs, field.Invalid(fldPath.Child("maxReplicas"), autoscaler.MaxReplicas, "must be greater than or equal to `minReplicas`")) } if autoscaler.CPUUtilization != nil && autoscaler.CPUUtilization.TargetPercentage < 1 { allErrs = append(allErrs, field.Invalid(fldPath.Child("cpuUtilization", "targetPercentage"), autoscaler.CPUUtilization.TargetPercentage, "must be greater than 0")) } if refErrs := ValidateSubresourceReference(autoscaler.ScaleRef, fldPath.Child("scaleRef")); len(refErrs) > 0 { allErrs = append(allErrs, refErrs...) } else if autoscaler.ScaleRef.Subresource != "scale" { allErrs = append(allErrs, field.NotSupported(fldPath.Child("scaleRef", "subresource"), autoscaler.ScaleRef.Subresource, []string{"scale"})) } return allErrs }
func validateJobSpec(spec *batch.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"))...) } 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 validateRoleBindingSubject(subject rbac.Subject, 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.APIVersion) != 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("apiVersion"), subject.APIVersion)) } switch subject.Kind { case rbac.ServiceAccountKind: if len(subject.Name) > 0 { for _, msg := range validation.ValidateServiceAccountName(subject.Name, false) { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg)) } } if !isNamespaced && len(subject.Namespace) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "")) } case rbac.UserKind: // TODO(ericchiang): What other restrictions on user name are there? if len(subject.Name) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "user name cannot be empty")) } case rbac.GroupKind: // TODO(ericchiang): What other restrictions on group name are there? if len(subject.Name) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "group name cannot be empty")) } default: allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, []string{rbac.ServiceAccountKind, rbac.UserKind, rbac.GroupKind})) } return allErrs }
// Validates the given template and ensures that it is in accordance with the desired selector and replicas. func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selector labels.Selector, replicas int, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if template == nil { allErrs = append(allErrs, field.Required(fldPath, "")) } else { if !selector.Empty() { // Verify that the ReplicaSet selector matches the labels in template. labels := labels.Set(template.Labels) if !selector.Matches(labels) { allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`")) } } allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...) if replicas > 1 { allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...) } // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec(). if template.Spec.RestartPolicy != api.RestartPolicyAlways { allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)})) } } return allErrs }
func TestNewInvalid(t *testing.T) { testCases := []struct { Err *field.Error Details *unversioned.StatusDetails }{ { field.Duplicate(field.NewPath("field[0].name"), "bar"), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueDuplicate, Field: "field[0].name", }}, }, }, { field.Invalid(field.NewPath("field[0].name"), "bar", "detail"), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueInvalid, Field: "field[0].name", }}, }, }, { field.NotFound(field.NewPath("field[0].name"), "bar"), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueNotFound, Field: "field[0].name", }}, }, }, { field.NotSupported(field.NewPath("field[0].name"), "bar", nil), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueNotSupported, Field: "field[0].name", }}, }, }, { field.Required(field.NewPath("field[0].name")), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueRequired, Field: "field[0].name", }}, }, }, } for i, testCase := range testCases { vErr, expected := testCase.Err, testCase.Details expected.Causes[0].Message = vErr.ErrorBody() err := NewInvalid("kind", "name", field.ErrorList{vErr}) status := err.(*StatusError).ErrStatus if status.Code != 422 || status.Reason != unversioned.StatusReasonInvalid { t.Errorf("%d: unexpected status: %#v", i, status) } if !reflect.DeepEqual(expected, status.Details) { t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details) } } }
func TestValidate_ValidateEtcdStorageConfig(t *testing.T) { osField := "openShiftStorageVersion" kubeField := "kubernetesStorageVersion" tests := []struct { label string kubeStorageVersion string openshiftStorageVersion string name string expected field.ErrorList }{ { label: "valid levels", kubeStorageVersion: "v1", openshiftStorageVersion: "v1", expected: field.ErrorList{}, }, { label: "unknown openshift level", kubeStorageVersion: "v1", openshiftStorageVersion: "bogus", expected: field.ErrorList{ field.NotSupported(field.NewPath(osField), "bogus", []string{"v1"}), }, }, { label: "unsupported openshift level", kubeStorageVersion: "v1", openshiftStorageVersion: "v1beta3", expected: field.ErrorList{ field.NotSupported(field.NewPath(osField), "v1beta3", []string{"v1"}), }, }, { label: "missing openshift level", kubeStorageVersion: "v1", openshiftStorageVersion: "", expected: field.ErrorList{ field.Required(field.NewPath(osField), ""), }, }, { label: "unknown kube level", kubeStorageVersion: "bogus", openshiftStorageVersion: "v1", expected: field.ErrorList{ field.NotSupported(field.NewPath(kubeField), "bogus", []string{"v1"}), }, }, { label: "unsupported kube level", kubeStorageVersion: "v1beta3", openshiftStorageVersion: "v1", expected: field.ErrorList{ field.NotSupported(field.NewPath(kubeField), "v1beta3", []string{"v1"}), }, }, { label: "missing kube level", kubeStorageVersion: "", openshiftStorageVersion: "v1", expected: field.ErrorList{ field.Required(field.NewPath(kubeField), ""), }, }, } for _, test := range tests { t.Logf("evaluating test: %s", test.label) config := api.EtcdStorageConfig{ OpenShiftStorageVersion: test.openshiftStorageVersion, KubernetesStorageVersion: test.kubeStorageVersion, } results := ValidateEtcdStorageConfig(config, nil) if !kapi.Semantic.DeepEqual(test.expected, results) { t.Errorf("unexpected validation results; diff:\n%v", diff.ObjectDiff(test.expected, results)) return } } }
func ValidateKubernetesMasterConfig(config *api.KubernetesMasterConfig, fldPath *field.Path) ValidationResults { validationResults := ValidationResults{} if len(config.MasterIP) > 0 { validationResults.AddErrors(ValidateSpecifiedIP(config.MasterIP, fldPath.Child("masterIP"))...) } if config.MasterCount == 0 || config.MasterCount < -1 { validationResults.AddErrors(field.Invalid(fldPath.Child("masterCount"), config.MasterCount, "must be a positive integer or -1")) } validationResults.AddErrors(ValidateCertInfo(config.ProxyClientInfo, false, fldPath.Child("proxyClientInfo"))...) if len(config.ProxyClientInfo.CertFile) == 0 && len(config.ProxyClientInfo.KeyFile) == 0 { validationResults.AddWarnings(field.Invalid(fldPath.Child("proxyClientInfo"), "", "if no client certificate is specified, TLS pods and services cannot validate requests came from the proxy")) } if len(config.ServicesSubnet) > 0 { if _, _, err := net.ParseCIDR(strings.TrimSpace(config.ServicesSubnet)); err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("servicesSubnet"), config.ServicesSubnet, "must be a valid CIDR notation IP range (e.g. 172.30.0.0/16)")) } } if len(config.ServicesNodePortRange) > 0 { if _, err := knet.ParsePortRange(strings.TrimSpace(config.ServicesNodePortRange)); err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("servicesNodePortRange"), config.ServicesNodePortRange, "must be a valid port range (e.g. 30000-32000)")) } } if len(config.SchedulerConfigFile) > 0 { validationResults.AddErrors(ValidateFile(config.SchedulerConfigFile, fldPath.Child("schedulerConfigFile"))...) } for i, nodeName := range config.StaticNodeNames { if len(nodeName) == 0 { validationResults.AddErrors(field.Invalid(fldPath.Child("staticNodeName").Index(i), nodeName, "may not be empty")) } else { validationResults.AddWarnings(field.Invalid(fldPath.Child("staticNodeName").Index(i), nodeName, "static nodes are not supported")) } } if len(config.PodEvictionTimeout) > 0 { if _, err := time.ParseDuration(config.PodEvictionTimeout); err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("podEvictionTimeout"), config.PodEvictionTimeout, "must be a valid time duration string (e.g. '300ms' or '2m30s'). Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'")) } } for group, versions := range config.DisabledAPIGroupVersions { keyPath := fldPath.Child("disabledAPIGroupVersions").Key(group) if !api.KnownKubeAPIGroups.Has(group) { validationResults.AddWarnings(field.NotSupported(keyPath, group, api.KnownKubeAPIGroups.List())) continue } allowedVersions := sets.NewString(api.KubeAPIGroupsToAllowedVersions[group]...) for i, version := range versions { if version == "*" { continue } if !allowedVersions.Has(version) { validationResults.AddWarnings(field.NotSupported(keyPath.Index(i), version, allowedVersions.List())) } } } if config.AdmissionConfig.PluginConfig != nil { validationResults.AddErrors(ValidateAdmissionPluginConfig(config.AdmissionConfig.PluginConfig, fldPath.Child("admissionConfig", "pluginConfig"))...) } if len(config.AdmissionConfig.PluginOrderOverride) != 0 { validationResults.AddWarnings(field.Invalid(fldPath.Child("admissionConfig", "pluginOrderOverride"), config.AdmissionConfig.PluginOrderOverride, "specified admission ordering is being phased out. Convert to DefaultAdmissionConfig in admissionConfig.pluginConfig.")) } validationResults.Append(ValidateAPIServerExtendedArguments(config.APIServerArguments, fldPath.Child("apiServerArguments"))) validationResults.AddErrors(ValidateControllerExtendedArguments(config.ControllerArguments, fldPath.Child("controllerArguments"))...) return validationResults }