admission.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. Copyright 2014 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 resourcequota
  14. import (
  15. "fmt"
  16. "io"
  17. "time"
  18. corev1 "k8s.io/api/core/v1"
  19. "k8s.io/apiserver/pkg/admission"
  20. genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
  21. "k8s.io/client-go/informers"
  22. "k8s.io/client-go/kubernetes"
  23. kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
  24. quota "k8s.io/kubernetes/pkg/quota/v1"
  25. "k8s.io/kubernetes/pkg/quota/v1/generic"
  26. resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
  27. "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation"
  28. )
  29. // PluginName is a string with the name of the plugin
  30. const PluginName = "ResourceQuota"
  31. // Register registers a plugin
  32. func Register(plugins *admission.Plugins) {
  33. plugins.Register(PluginName,
  34. func(config io.Reader) (admission.Interface, error) {
  35. // load the configuration provided (if any)
  36. configuration, err := LoadConfiguration(config)
  37. if err != nil {
  38. return nil, err
  39. }
  40. // validate the configuration (if any)
  41. if configuration != nil {
  42. if errs := validation.ValidateConfiguration(configuration); len(errs) != 0 {
  43. return nil, errs.ToAggregate()
  44. }
  45. }
  46. return NewResourceQuota(configuration, 5, make(chan struct{}))
  47. })
  48. }
  49. // QuotaAdmission implements an admission controller that can enforce quota constraints
  50. type QuotaAdmission struct {
  51. *admission.Handler
  52. config *resourcequotaapi.Configuration
  53. stopCh <-chan struct{}
  54. quotaConfiguration quota.Configuration
  55. numEvaluators int
  56. quotaAccessor *quotaAccessor
  57. evaluator Evaluator
  58. }
  59. var _ admission.ValidationInterface = &QuotaAdmission{}
  60. var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&QuotaAdmission{})
  61. var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&QuotaAdmission{})
  62. var _ = kubeapiserveradmission.WantsQuotaConfiguration(&QuotaAdmission{})
  63. type liveLookupEntry struct {
  64. expiry time.Time
  65. items []*corev1.ResourceQuota
  66. }
  67. // NewResourceQuota configures an admission controller that can enforce quota constraints
  68. // using the provided registry. The registry must have the capability to handle group/kinds that
  69. // are persisted by the server this admission controller is intercepting
  70. func NewResourceQuota(config *resourcequotaapi.Configuration, numEvaluators int, stopCh <-chan struct{}) (*QuotaAdmission, error) {
  71. quotaAccessor, err := newQuotaAccessor()
  72. if err != nil {
  73. return nil, err
  74. }
  75. return &QuotaAdmission{
  76. Handler: admission.NewHandler(admission.Create, admission.Update),
  77. stopCh: stopCh,
  78. numEvaluators: numEvaluators,
  79. config: config,
  80. quotaAccessor: quotaAccessor,
  81. }, nil
  82. }
  83. // SetExternalKubeClientSet registers the client into QuotaAdmission
  84. func (a *QuotaAdmission) SetExternalKubeClientSet(client kubernetes.Interface) {
  85. a.quotaAccessor.client = client
  86. }
  87. // SetExternalKubeInformerFactory registers an informer factory into QuotaAdmission
  88. func (a *QuotaAdmission) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
  89. a.quotaAccessor.lister = f.Core().V1().ResourceQuotas().Lister()
  90. }
  91. // SetQuotaConfiguration assigns and initializes configuration and evaluator for QuotaAdmission
  92. func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) {
  93. a.quotaConfiguration = c
  94. a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration.IgnoredResources(), generic.NewRegistry(a.quotaConfiguration.Evaluators()), nil, a.config, a.numEvaluators, a.stopCh)
  95. }
  96. // ValidateInitialization ensures an authorizer is set.
  97. func (a *QuotaAdmission) ValidateInitialization() error {
  98. if a.quotaAccessor == nil {
  99. return fmt.Errorf("missing quotaAccessor")
  100. }
  101. if a.quotaAccessor.client == nil {
  102. return fmt.Errorf("missing quotaAccessor.client")
  103. }
  104. if a.quotaAccessor.lister == nil {
  105. return fmt.Errorf("missing quotaAccessor.lister")
  106. }
  107. if a.quotaConfiguration == nil {
  108. return fmt.Errorf("missing quotaConfiguration")
  109. }
  110. if a.evaluator == nil {
  111. return fmt.Errorf("missing evaluator")
  112. }
  113. return nil
  114. }
  115. // Validate makes admission decisions while enforcing quota
  116. func (a *QuotaAdmission) Validate(attr admission.Attributes, o admission.ObjectInterfaces) (err error) {
  117. // ignore all operations that correspond to sub-resource actions
  118. if attr.GetSubresource() != "" {
  119. return nil
  120. }
  121. // ignore all operations that are not namespaced
  122. if attr.GetNamespace() == "" {
  123. return nil
  124. }
  125. return a.evaluator.Evaluate(attr)
  126. }