admission.go 5.0 KB

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