validation.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package validation
  14. import (
  15. "errors"
  16. "fmt"
  17. "github.com/google/go-cmp/cmp"
  18. v1 "k8s.io/api/core/v1"
  19. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  20. "k8s.io/apimachinery/pkg/util/sets"
  21. "k8s.io/apimachinery/pkg/util/validation"
  22. "k8s.io/apimachinery/pkg/util/validation/field"
  23. componentbasevalidation "k8s.io/component-base/config/validation"
  24. v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
  25. "k8s.io/kubernetes/pkg/scheduler/apis/config"
  26. )
  27. // ValidateKubeSchedulerConfiguration ensures validation of the KubeSchedulerConfiguration struct
  28. func ValidateKubeSchedulerConfiguration(cc *config.KubeSchedulerConfiguration) field.ErrorList {
  29. allErrs := field.ErrorList{}
  30. allErrs = append(allErrs, componentbasevalidation.ValidateClientConnectionConfiguration(&cc.ClientConnection, field.NewPath("clientConnection"))...)
  31. allErrs = append(allErrs, validateKubeSchedulerLeaderElectionConfiguration(field.NewPath("leaderElection"), &cc.LeaderElection)...)
  32. profilesPath := field.NewPath("profiles")
  33. if len(cc.Profiles) == 0 {
  34. allErrs = append(allErrs, field.Required(profilesPath, ""))
  35. } else {
  36. existingProfiles := make(map[string]int, len(cc.Profiles))
  37. for i := range cc.Profiles {
  38. profile := &cc.Profiles[i]
  39. path := profilesPath.Index(i)
  40. allErrs = append(allErrs, validateKubeSchedulerProfile(path, profile)...)
  41. if idx, ok := existingProfiles[profile.SchedulerName]; ok {
  42. allErrs = append(allErrs, field.Duplicate(path.Child("schedulerName"), profilesPath.Index(idx).Child("schedulerName")))
  43. }
  44. existingProfiles[profile.SchedulerName] = i
  45. }
  46. allErrs = append(allErrs, validateCommonQueueSort(profilesPath, cc.Profiles)...)
  47. }
  48. for _, msg := range validation.IsValidSocketAddr(cc.HealthzBindAddress) {
  49. allErrs = append(allErrs, field.Invalid(field.NewPath("healthzBindAddress"), cc.HealthzBindAddress, msg))
  50. }
  51. for _, msg := range validation.IsValidSocketAddr(cc.MetricsBindAddress) {
  52. allErrs = append(allErrs, field.Invalid(field.NewPath("metricsBindAddress"), cc.MetricsBindAddress, msg))
  53. }
  54. if cc.PercentageOfNodesToScore < 0 || cc.PercentageOfNodesToScore > 100 {
  55. allErrs = append(allErrs, field.Invalid(field.NewPath("percentageOfNodesToScore"),
  56. cc.PercentageOfNodesToScore, "not in valid range [0-100]"))
  57. }
  58. if cc.PodInitialBackoffSeconds <= 0 {
  59. allErrs = append(allErrs, field.Invalid(field.NewPath("podInitialBackoffSeconds"),
  60. cc.PodInitialBackoffSeconds, "must be greater than 0"))
  61. }
  62. if cc.PodMaxBackoffSeconds < cc.PodInitialBackoffSeconds {
  63. allErrs = append(allErrs, field.Invalid(field.NewPath("podMaxBackoffSeconds"),
  64. cc.PodMaxBackoffSeconds, "must be greater than or equal to PodInitialBackoffSeconds"))
  65. }
  66. return allErrs
  67. }
  68. func validateKubeSchedulerProfile(path *field.Path, profile *config.KubeSchedulerProfile) field.ErrorList {
  69. allErrs := field.ErrorList{}
  70. if len(profile.SchedulerName) == 0 {
  71. allErrs = append(allErrs, field.Required(path.Child("schedulerName"), ""))
  72. }
  73. return allErrs
  74. }
  75. func validateCommonQueueSort(path *field.Path, profiles []config.KubeSchedulerProfile) field.ErrorList {
  76. allErrs := field.ErrorList{}
  77. var canon *config.PluginSet
  78. if profiles[0].Plugins != nil {
  79. canon = profiles[0].Plugins.QueueSort
  80. }
  81. for i := 1; i < len(profiles); i++ {
  82. var curr *config.PluginSet
  83. if profiles[i].Plugins != nil {
  84. curr = profiles[i].Plugins.QueueSort
  85. }
  86. if !cmp.Equal(canon, curr) {
  87. allErrs = append(allErrs, field.Invalid(path.Index(i).Child("plugins", "queueSort"), curr, "has to match for all profiles"))
  88. }
  89. }
  90. // TODO(#88093): Validate that all plugin configs for the queue sort extension match.
  91. return allErrs
  92. }
  93. func validateKubeSchedulerLeaderElectionConfiguration(fldPath *field.Path, cc *config.KubeSchedulerLeaderElectionConfiguration) field.ErrorList {
  94. allErrs := field.ErrorList{}
  95. if !cc.LeaderElectionConfiguration.LeaderElect {
  96. return allErrs
  97. }
  98. allErrs = append(allErrs, componentbasevalidation.ValidateLeaderElectionConfiguration(&cc.LeaderElectionConfiguration, fldPath)...)
  99. return allErrs
  100. }
  101. // ValidatePolicy checks for errors in the Config
  102. // It does not return early so that it can find as many errors as possible
  103. func ValidatePolicy(policy config.Policy) error {
  104. var validationErrors []error
  105. priorities := make(map[string]config.PriorityPolicy, len(policy.Priorities))
  106. for _, priority := range policy.Priorities {
  107. if priority.Weight <= 0 || priority.Weight >= config.MaxWeight {
  108. validationErrors = append(validationErrors, fmt.Errorf("Priority %s should have a positive weight applied to it or it has overflown", priority.Name))
  109. }
  110. validationErrors = append(validationErrors, validateCustomPriorities(priorities, priority))
  111. }
  112. binders := 0
  113. extenderManagedResources := sets.NewString()
  114. for _, extender := range policy.Extenders {
  115. if len(extender.PrioritizeVerb) > 0 && extender.Weight <= 0 {
  116. validationErrors = append(validationErrors, fmt.Errorf("Priority for extender %s should have a positive weight applied to it", extender.URLPrefix))
  117. }
  118. if extender.BindVerb != "" {
  119. binders++
  120. }
  121. for _, resource := range extender.ManagedResources {
  122. errs := validateExtendedResourceName(v1.ResourceName(resource.Name))
  123. if len(errs) != 0 {
  124. validationErrors = append(validationErrors, errs...)
  125. }
  126. if extenderManagedResources.Has(resource.Name) {
  127. validationErrors = append(validationErrors, fmt.Errorf("Duplicate extender managed resource name %s", string(resource.Name)))
  128. }
  129. extenderManagedResources.Insert(resource.Name)
  130. }
  131. }
  132. if binders > 1 {
  133. validationErrors = append(validationErrors, fmt.Errorf("Only one extender can implement bind, found %v", binders))
  134. }
  135. if policy.HardPodAffinitySymmetricWeight < 0 || policy.HardPodAffinitySymmetricWeight > 100 {
  136. validationErrors = append(validationErrors, field.Invalid(field.NewPath("hardPodAffinitySymmetricWeight"), policy.HardPodAffinitySymmetricWeight, "not in valid range [0-100]"))
  137. }
  138. return utilerrors.NewAggregate(validationErrors)
  139. }
  140. // validateCustomPriorities validates that:
  141. // 1. RequestedToCapacityRatioRedeclared custom priority cannot be declared multiple times,
  142. // 2. LabelPreference/ServiceAntiAffinity custom priorities can be declared multiple times,
  143. // however the weights for each custom priority type should be the same.
  144. func validateCustomPriorities(priorities map[string]config.PriorityPolicy, priority config.PriorityPolicy) error {
  145. verifyRedeclaration := func(priorityType string) error {
  146. if existing, alreadyDeclared := priorities[priorityType]; alreadyDeclared {
  147. return fmt.Errorf("Priority %q redeclares custom priority %q, from:%q", priority.Name, priorityType, existing.Name)
  148. }
  149. priorities[priorityType] = priority
  150. return nil
  151. }
  152. verifyDifferentWeights := func(priorityType string) error {
  153. if existing, alreadyDeclared := priorities[priorityType]; alreadyDeclared {
  154. if existing.Weight != priority.Weight {
  155. return fmt.Errorf("%s priority %q has a different weight with %q", priorityType, priority.Name, existing.Name)
  156. }
  157. }
  158. priorities[priorityType] = priority
  159. return nil
  160. }
  161. if priority.Argument != nil {
  162. if priority.Argument.LabelPreference != nil {
  163. if err := verifyDifferentWeights("LabelPreference"); err != nil {
  164. return err
  165. }
  166. } else if priority.Argument.ServiceAntiAffinity != nil {
  167. if err := verifyDifferentWeights("ServiceAntiAffinity"); err != nil {
  168. return err
  169. }
  170. } else if priority.Argument.RequestedToCapacityRatioArguments != nil {
  171. if err := verifyRedeclaration("RequestedToCapacityRatio"); err != nil {
  172. return err
  173. }
  174. } else {
  175. return fmt.Errorf("No priority arguments set for priority %s", priority.Name)
  176. }
  177. }
  178. return nil
  179. }
  180. // validateExtendedResourceName checks whether the specified name is a valid
  181. // extended resource name.
  182. func validateExtendedResourceName(name v1.ResourceName) []error {
  183. var validationErrors []error
  184. for _, msg := range validation.IsQualifiedName(string(name)) {
  185. validationErrors = append(validationErrors, errors.New(msg))
  186. }
  187. if len(validationErrors) != 0 {
  188. return validationErrors
  189. }
  190. if !v1helper.IsExtendedResourceName(name) {
  191. validationErrors = append(validationErrors, fmt.Errorf("%s is an invalid extended resource name", name))
  192. }
  193. return validationErrors
  194. }