evaluator.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. Copyright 2016 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 generic
  14. import (
  15. "fmt"
  16. "k8s.io/kubernetes/pkg/admission"
  17. "k8s.io/kubernetes/pkg/api"
  18. "k8s.io/kubernetes/pkg/api/meta"
  19. "k8s.io/kubernetes/pkg/api/resource"
  20. "k8s.io/kubernetes/pkg/api/unversioned"
  21. "k8s.io/kubernetes/pkg/quota"
  22. "k8s.io/kubernetes/pkg/runtime"
  23. )
  24. // ConstraintsFunc takes a list of required resources that must match on the input item
  25. type ConstraintsFunc func(required []api.ResourceName, item runtime.Object) error
  26. // GetFuncByNamespace knows how to get a resource with specified namespace and name
  27. type GetFuncByNamespace func(namespace, name string) (runtime.Object, error)
  28. // ListFuncByNamespace knows how to list resources in a namespace
  29. type ListFuncByNamespace func(namespace string, options api.ListOptions) (runtime.Object, error)
  30. // MatchesScopeFunc knows how to evaluate if an object matches a scope
  31. type MatchesScopeFunc func(scope api.ResourceQuotaScope, object runtime.Object) bool
  32. // UsageFunc knows how to measure usage associated with an object
  33. type UsageFunc func(object runtime.Object) api.ResourceList
  34. // MatchesNoScopeFunc returns false on all match checks
  35. func MatchesNoScopeFunc(scope api.ResourceQuotaScope, object runtime.Object) bool {
  36. return false
  37. }
  38. // ObjectCountConstraintsFunc returns ConstraintsFunc that returns nil if the
  39. // specified resource name is in the required set of resource names
  40. func ObjectCountConstraintsFunc(resourceName api.ResourceName) ConstraintsFunc {
  41. return func(required []api.ResourceName, item runtime.Object) error {
  42. if !quota.Contains(required, resourceName) {
  43. return fmt.Errorf("missing %s", resourceName)
  44. }
  45. return nil
  46. }
  47. }
  48. // ObjectCountUsageFunc is useful if you are only counting your object
  49. // It always returns 1 as the usage for the named resource
  50. func ObjectCountUsageFunc(resourceName api.ResourceName) UsageFunc {
  51. return func(object runtime.Object) api.ResourceList {
  52. return api.ResourceList{
  53. resourceName: resource.MustParse("1"),
  54. }
  55. }
  56. }
  57. // GenericEvaluator provides an implementation for quota.Evaluator
  58. type GenericEvaluator struct {
  59. // Name used for logging
  60. Name string
  61. // The GroupKind that this evaluator tracks
  62. InternalGroupKind unversioned.GroupKind
  63. // The set of resources that are pertinent to the mapped operation
  64. InternalOperationResources map[admission.Operation][]api.ResourceName
  65. // The set of resource names this evaluator matches
  66. MatchedResourceNames []api.ResourceName
  67. // A function that knows how to evaluate a matches scope request
  68. MatchesScopeFunc MatchesScopeFunc
  69. // A function that knows how to return usage for an object
  70. UsageFunc UsageFunc
  71. // A function that knows how to list resources by namespace
  72. ListFuncByNamespace ListFuncByNamespace
  73. // A function that knows how to get resource in a namespace
  74. // This function must be specified if the evaluator needs to handle UPDATE
  75. GetFuncByNamespace GetFuncByNamespace
  76. // A function that checks required constraints are satisfied
  77. ConstraintsFunc ConstraintsFunc
  78. }
  79. // Ensure that GenericEvaluator implements quota.Evaluator
  80. var _ quota.Evaluator = &GenericEvaluator{}
  81. // Constraints checks required constraints are satisfied on the input object
  82. func (g *GenericEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error {
  83. return g.ConstraintsFunc(required, item)
  84. }
  85. // Get returns the object by namespace and name
  86. func (g *GenericEvaluator) Get(namespace, name string) (runtime.Object, error) {
  87. return g.GetFuncByNamespace(namespace, name)
  88. }
  89. // OperationResources returns the set of resources that could be updated for the
  90. // specified operation for this kind. If empty, admission control will ignore
  91. // quota processing for the operation.
  92. func (g *GenericEvaluator) OperationResources(operation admission.Operation) []api.ResourceName {
  93. return g.InternalOperationResources[operation]
  94. }
  95. // GroupKind that this evaluator tracks
  96. func (g *GenericEvaluator) GroupKind() unversioned.GroupKind {
  97. return g.InternalGroupKind
  98. }
  99. // MatchesResources is the list of resources that this evaluator matches
  100. func (g *GenericEvaluator) MatchesResources() []api.ResourceName {
  101. return g.MatchedResourceNames
  102. }
  103. // Matches returns true if the evaluator matches the specified quota with the provided input item
  104. func (g *GenericEvaluator) Matches(resourceQuota *api.ResourceQuota, item runtime.Object) bool {
  105. if resourceQuota == nil {
  106. return false
  107. }
  108. // verify the quota matches on resource, by default its false
  109. matchResource := false
  110. for resourceName := range resourceQuota.Status.Hard {
  111. if g.MatchesResource(resourceName) {
  112. matchResource = true
  113. break
  114. }
  115. }
  116. // by default, no scopes matches all
  117. matchScope := true
  118. for _, scope := range resourceQuota.Spec.Scopes {
  119. matchScope = matchScope && g.MatchesScope(scope, item)
  120. }
  121. return matchResource && matchScope
  122. }
  123. // MatchesResource returns true if this evaluator can match on the specified resource
  124. func (g *GenericEvaluator) MatchesResource(resourceName api.ResourceName) bool {
  125. for _, matchedResourceName := range g.MatchedResourceNames {
  126. if resourceName == matchedResourceName {
  127. return true
  128. }
  129. }
  130. return false
  131. }
  132. // MatchesScope returns true if the input object matches the specified scope
  133. func (g *GenericEvaluator) MatchesScope(scope api.ResourceQuotaScope, object runtime.Object) bool {
  134. return g.MatchesScopeFunc(scope, object)
  135. }
  136. // Usage returns the resource usage for the specified object
  137. func (g *GenericEvaluator) Usage(object runtime.Object) api.ResourceList {
  138. return g.UsageFunc(object)
  139. }
  140. // UsageStats calculates latest observed usage stats for all objects
  141. func (g *GenericEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.UsageStats, error) {
  142. // default each tracked resource to zero
  143. result := quota.UsageStats{Used: api.ResourceList{}}
  144. for _, resourceName := range g.MatchedResourceNames {
  145. result.Used[resourceName] = resource.MustParse("0")
  146. }
  147. list, err := g.ListFuncByNamespace(options.Namespace, api.ListOptions{})
  148. if err != nil {
  149. return result, fmt.Errorf("%s: Failed to list %v: %v", g.Name, g.GroupKind(), err)
  150. }
  151. _, err = meta.ListAccessor(list)
  152. if err != nil {
  153. return result, fmt.Errorf("%s: Unable to understand list result, does not appear to be a list %#v", g.Name, list)
  154. }
  155. items, err := meta.ExtractList(list)
  156. if err != nil {
  157. return result, fmt.Errorf("%s: Unable to understand list result %#v (%v)", g.Name, list, err)
  158. }
  159. for _, item := range items {
  160. // need to verify that the item matches the set of scopes
  161. matchesScopes := true
  162. for _, scope := range options.Scopes {
  163. if !g.MatchesScope(scope, item) {
  164. matchesScopes = false
  165. }
  166. }
  167. // only count usage if there was a match
  168. if matchesScopes {
  169. result.Used = quota.Add(result.Used, g.Usage(item))
  170. }
  171. }
  172. return result, nil
  173. }