mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 10:51:29 +00:00
Fix ResourceQuota admission shutdown
This commit is contained in:
parent
fbb5717279
commit
f8211d7e44
@ -115,7 +115,7 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
|
|||||||
whiteList: whiteList,
|
whiteList: whiteList,
|
||||||
}
|
}
|
||||||
|
|
||||||
genericPluginInitializer := initializer.New(nil, nil, fakeAuthorizer{}, nil)
|
genericPluginInitializer := initializer.New(nil, nil, fakeAuthorizer{}, nil, nil)
|
||||||
fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}}
|
fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}}
|
||||||
fakeDiscoveryClient.Resources = []*metav1.APIResourceList{
|
fakeDiscoveryClient.Resources = []*metav1.APIResourceList{
|
||||||
{
|
{
|
||||||
|
@ -790,7 +790,7 @@ func newHandlerForTest(c clientset.Interface) (*LimitRanger, informers.SharedInf
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, f, err
|
return nil, f, err
|
||||||
}
|
}
|
||||||
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil)
|
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil, nil)
|
||||||
pluginInitializer.Initialize(handler)
|
pluginInitializer.Initialize(handler)
|
||||||
err = admission.ValidateInitialization(handler)
|
err = admission.ValidateInitialization(handler)
|
||||||
return handler, f, err
|
return handler, f, err
|
||||||
|
@ -41,7 +41,7 @@ import (
|
|||||||
func newHandlerForTest(c clientset.Interface) (admission.MutationInterface, informers.SharedInformerFactory, error) {
|
func newHandlerForTest(c clientset.Interface) (admission.MutationInterface, informers.SharedInformerFactory, error) {
|
||||||
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
||||||
handler := NewProvision()
|
handler := NewProvision()
|
||||||
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil)
|
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil, nil)
|
||||||
pluginInitializer.Initialize(handler)
|
pluginInitializer.Initialize(handler)
|
||||||
err := admission.ValidateInitialization(handler)
|
err := admission.ValidateInitialization(handler)
|
||||||
return handler, f, err
|
return handler, f, err
|
||||||
|
@ -39,7 +39,7 @@ import (
|
|||||||
func newHandlerForTest(c kubernetes.Interface) (admission.ValidationInterface, informers.SharedInformerFactory, error) {
|
func newHandlerForTest(c kubernetes.Interface) (admission.ValidationInterface, informers.SharedInformerFactory, error) {
|
||||||
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
||||||
handler := NewExists()
|
handler := NewExists()
|
||||||
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil)
|
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil, nil)
|
||||||
pluginInitializer.Initialize(handler)
|
pluginInitializer.Initialize(handler)
|
||||||
err := admission.ValidateInitialization(handler)
|
err := admission.ValidateInitialization(handler)
|
||||||
return handler, f, err
|
return handler, f, err
|
||||||
|
@ -198,7 +198,7 @@ func TestHandles(t *testing.T) {
|
|||||||
func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
|
func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
|
||||||
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
||||||
handler := NewPodNodeSelector(nil)
|
handler := NewPodNodeSelector(nil)
|
||||||
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil)
|
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil, nil)
|
||||||
pluginInitializer.Initialize(handler)
|
pluginInitializer.Initialize(handler)
|
||||||
err := admission.ValidateInitialization(handler)
|
err := admission.ValidateInitialization(handler)
|
||||||
return handler, f, err
|
return handler, f, err
|
||||||
|
@ -356,7 +356,7 @@ func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInforme
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
handler := NewPodTolerationsPlugin(pluginConfig)
|
handler := NewPodTolerationsPlugin(pluginConfig)
|
||||||
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil)
|
pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil, nil)
|
||||||
pluginInitializer.Initialize(handler)
|
pluginInitializer.Initialize(handler)
|
||||||
err = admission.ValidateInitialization(handler)
|
err = admission.ValidateInitialization(handler)
|
||||||
return handler, f, err
|
return handler, f, err
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
||||||
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
@ -36,6 +37,7 @@ import (
|
|||||||
testcore "k8s.io/client-go/testing"
|
testcore "k8s.io/client-go/testing"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
|
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||||
"k8s.io/kubernetes/pkg/quota/v1/install"
|
"k8s.io/kubernetes/pkg/quota/v1/install"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -99,15 +101,18 @@ func createHandlerWithConfig(kubeClient kubernetes.Interface, informerFactory in
|
|||||||
}
|
}
|
||||||
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
|
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
|
||||||
|
|
||||||
handler, err := resourcequota.NewResourceQuota(config, 5, stopCh)
|
handler, err := resourcequota.NewResourceQuota(config, 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
handler.SetExternalKubeClientSet(kubeClient)
|
|
||||||
handler.SetExternalKubeInformerFactory(informerFactory)
|
|
||||||
handler.SetQuotaConfiguration(quotaConfiguration)
|
|
||||||
|
|
||||||
return handler, nil
|
initializers := admission.PluginInitializers{
|
||||||
|
genericadmissioninitializer.New(kubeClient, informerFactory, nil, nil, stopCh),
|
||||||
|
kubeapiserveradmission.NewPluginInitializer(nil, nil, quotaConfiguration),
|
||||||
|
}
|
||||||
|
initializers.Initialize(handler)
|
||||||
|
|
||||||
|
return handler, admission.ValidateInitialization(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAdmissionIgnoresDelete verifies that the admission controller ignores delete operations
|
// TestAdmissionIgnoresDelete verifies that the admission controller ignores delete operations
|
||||||
|
@ -29,6 +29,7 @@ type pluginInitializer struct {
|
|||||||
externalInformers informers.SharedInformerFactory
|
externalInformers informers.SharedInformerFactory
|
||||||
authorizer authorizer.Authorizer
|
authorizer authorizer.Authorizer
|
||||||
featureGates featuregate.FeatureGate
|
featureGates featuregate.FeatureGate
|
||||||
|
stopCh <-chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates an instance of admission plugins initializer.
|
// New creates an instance of admission plugins initializer.
|
||||||
@ -39,19 +40,26 @@ func New(
|
|||||||
extInformers informers.SharedInformerFactory,
|
extInformers informers.SharedInformerFactory,
|
||||||
authz authorizer.Authorizer,
|
authz authorizer.Authorizer,
|
||||||
featureGates featuregate.FeatureGate,
|
featureGates featuregate.FeatureGate,
|
||||||
|
stopCh <-chan struct{},
|
||||||
) pluginInitializer {
|
) pluginInitializer {
|
||||||
return pluginInitializer{
|
return pluginInitializer{
|
||||||
externalClient: extClientset,
|
externalClient: extClientset,
|
||||||
externalInformers: extInformers,
|
externalInformers: extInformers,
|
||||||
authorizer: authz,
|
authorizer: authz,
|
||||||
featureGates: featureGates,
|
featureGates: featureGates,
|
||||||
|
stopCh: stopCh,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize checks the initialization interfaces implemented by a plugin
|
// Initialize checks the initialization interfaces implemented by a plugin
|
||||||
// and provide the appropriate initialization data
|
// and provide the appropriate initialization data
|
||||||
func (i pluginInitializer) Initialize(plugin admission.Interface) {
|
func (i pluginInitializer) Initialize(plugin admission.Interface) {
|
||||||
// First tell the plugin about enabled features, so it can decide whether to start informers or not
|
// First tell the plugin about drained notification, so it can pass it to further initializations.
|
||||||
|
if wants, ok := plugin.(WantsDrainedNotification); ok {
|
||||||
|
wants.SetDrainedNotification(i.stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second tell the plugin about enabled features, so it can decide whether to start informers or not
|
||||||
if wants, ok := plugin.(WantsFeatures); ok {
|
if wants, ok := plugin.(WantsFeatures); ok {
|
||||||
wants.InspectFeatureGates(i.featureGates)
|
wants.InspectFeatureGates(i.featureGates)
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
// TestWantsAuthorizer ensures that the authorizer is injected
|
// TestWantsAuthorizer ensures that the authorizer is injected
|
||||||
// when the WantsAuthorizer interface is implemented by a plugin.
|
// when the WantsAuthorizer interface is implemented by a plugin.
|
||||||
func TestWantsAuthorizer(t *testing.T) {
|
func TestWantsAuthorizer(t *testing.T) {
|
||||||
target := initializer.New(nil, nil, &TestAuthorizer{}, nil)
|
target := initializer.New(nil, nil, &TestAuthorizer{}, nil, nil)
|
||||||
wantAuthorizerAdmission := &WantAuthorizerAdmission{}
|
wantAuthorizerAdmission := &WantAuthorizerAdmission{}
|
||||||
target.Initialize(wantAuthorizerAdmission)
|
target.Initialize(wantAuthorizerAdmission)
|
||||||
if wantAuthorizerAdmission.auth == nil {
|
if wantAuthorizerAdmission.auth == nil {
|
||||||
@ -44,7 +44,7 @@ func TestWantsAuthorizer(t *testing.T) {
|
|||||||
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
|
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
|
||||||
func TestWantsExternalKubeClientSet(t *testing.T) {
|
func TestWantsExternalKubeClientSet(t *testing.T) {
|
||||||
cs := &fake.Clientset{}
|
cs := &fake.Clientset{}
|
||||||
target := initializer.New(cs, nil, &TestAuthorizer{}, nil)
|
target := initializer.New(cs, nil, &TestAuthorizer{}, nil, nil)
|
||||||
wantExternalKubeClientSet := &WantExternalKubeClientSet{}
|
wantExternalKubeClientSet := &WantExternalKubeClientSet{}
|
||||||
target.Initialize(wantExternalKubeClientSet)
|
target.Initialize(wantExternalKubeClientSet)
|
||||||
if wantExternalKubeClientSet.cs != cs {
|
if wantExternalKubeClientSet.cs != cs {
|
||||||
@ -57,7 +57,7 @@ func TestWantsExternalKubeClientSet(t *testing.T) {
|
|||||||
func TestWantsExternalKubeInformerFactory(t *testing.T) {
|
func TestWantsExternalKubeInformerFactory(t *testing.T) {
|
||||||
cs := &fake.Clientset{}
|
cs := &fake.Clientset{}
|
||||||
sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second)
|
sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second)
|
||||||
target := initializer.New(cs, sf, &TestAuthorizer{}, nil)
|
target := initializer.New(cs, sf, &TestAuthorizer{}, nil, nil)
|
||||||
wantExternalKubeInformerFactory := &WantExternalKubeInformerFactory{}
|
wantExternalKubeInformerFactory := &WantExternalKubeInformerFactory{}
|
||||||
target.Initialize(wantExternalKubeInformerFactory)
|
target.Initialize(wantExternalKubeInformerFactory)
|
||||||
if wantExternalKubeInformerFactory.sf != sf {
|
if wantExternalKubeInformerFactory.sf != sf {
|
||||||
@ -65,6 +65,18 @@ func TestWantsExternalKubeInformerFactory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWantsShutdownSignal ensures that the shutdown signal is injected
|
||||||
|
// when the WantsShutdownSignal interface is implemented by a plugin.
|
||||||
|
func TestWantsShutdownNotification(t *testing.T) {
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
target := initializer.New(nil, nil, &TestAuthorizer{}, nil, stopCh)
|
||||||
|
wantDrainedNotification := &WantDrainedNotification{}
|
||||||
|
target.Initialize(wantDrainedNotification)
|
||||||
|
if wantDrainedNotification.stopCh == nil {
|
||||||
|
t.Errorf("expected stopCh to be initialized but found nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WantExternalKubeInformerFactory is a test stub that fulfills the WantsExternalKubeInformerFactory interface
|
// WantExternalKubeInformerFactory is a test stub that fulfills the WantsExternalKubeInformerFactory interface
|
||||||
type WantExternalKubeInformerFactory struct {
|
type WantExternalKubeInformerFactory struct {
|
||||||
sf informers.SharedInformerFactory
|
sf informers.SharedInformerFactory
|
||||||
@ -114,6 +126,23 @@ func (self *WantAuthorizerAdmission) ValidateInitialization() error { retur
|
|||||||
var _ admission.Interface = &WantAuthorizerAdmission{}
|
var _ admission.Interface = &WantAuthorizerAdmission{}
|
||||||
var _ initializer.WantsAuthorizer = &WantAuthorizerAdmission{}
|
var _ initializer.WantsAuthorizer = &WantAuthorizerAdmission{}
|
||||||
|
|
||||||
|
// WantDrainedNotification is a test stub that filfills the WantsDrainedNotification interface.
|
||||||
|
type WantDrainedNotification struct {
|
||||||
|
stopCh <-chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WantDrainedNotification) SetDrainedNotification(stopCh <-chan struct{}) {
|
||||||
|
self.stopCh = stopCh
|
||||||
|
}
|
||||||
|
func (self *WantDrainedNotification) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (self *WantDrainedNotification) Handles(o admission.Operation) bool { return false }
|
||||||
|
func (self *WantDrainedNotification) ValidateInitialization() error { return nil }
|
||||||
|
|
||||||
|
var _ admission.Interface = &WantDrainedNotification{}
|
||||||
|
var _ initializer.WantsDrainedNotification = &WantDrainedNotification{}
|
||||||
|
|
||||||
// TestAuthorizer is a test stub that fulfills the WantsAuthorizer interface.
|
// TestAuthorizer is a test stub that fulfills the WantsAuthorizer interface.
|
||||||
type TestAuthorizer struct{}
|
type TestAuthorizer struct{}
|
||||||
|
|
||||||
|
@ -49,6 +49,14 @@ type WantsQuotaConfiguration interface {
|
|||||||
admission.InitializationValidator
|
admission.InitializationValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WantsDrainedNotification defines a function which sets the notification of where the apiserver
|
||||||
|
// has already been drained for admission plugins that need it.
|
||||||
|
// After receiving that notification, Admit/Validate calls won't be called anymore.
|
||||||
|
type WantsDrainedNotification interface {
|
||||||
|
SetDrainedNotification(<-chan struct{})
|
||||||
|
admission.InitializationValidator
|
||||||
|
}
|
||||||
|
|
||||||
// WantsFeatureGate defines a function which passes the featureGates for inspection by an admission plugin.
|
// WantsFeatureGate defines a function which passes the featureGates for inspection by an admission plugin.
|
||||||
// Admission plugins should not hold a reference to the featureGates. Instead, they should query a particular one
|
// Admission plugins should not hold a reference to the featureGates. Instead, they should query a particular one
|
||||||
// and assign it to a simple bool in the admission plugin struct.
|
// and assign it to a simple bool in the admission plugin struct.
|
||||||
|
@ -53,7 +53,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, f, err
|
return nil, f, err
|
||||||
}
|
}
|
||||||
pluginInitializer := kubeadmission.New(c, f, nil, nil)
|
pluginInitializer := kubeadmission.New(c, f, nil, nil, nil)
|
||||||
pluginInitializer.Initialize(handler)
|
pluginInitializer.Initialize(handler)
|
||||||
err = admission.ValidateInitialization(handler)
|
err = admission.ValidateInitialization(handler)
|
||||||
return handler, f, err
|
return handler, f, err
|
||||||
|
@ -37,7 +37,10 @@ import (
|
|||||||
// PluginName is a string with the name of the plugin
|
// PluginName is a string with the name of the plugin
|
||||||
const PluginName = "ResourceQuota"
|
const PluginName = "ResourceQuota"
|
||||||
|
|
||||||
var namespaceGVK = v1.SchemeGroupVersion.WithKind("Namespace").GroupKind()
|
var (
|
||||||
|
namespaceGVK = v1.SchemeGroupVersion.WithKind("Namespace").GroupKind()
|
||||||
|
stopChUnconfiguredErr = fmt.Errorf("quota configuration configured between stop channel")
|
||||||
|
)
|
||||||
|
|
||||||
// Register registers a plugin
|
// Register registers a plugin
|
||||||
func Register(plugins *admission.Plugins) {
|
func Register(plugins *admission.Plugins) {
|
||||||
@ -54,7 +57,7 @@ func Register(plugins *admission.Plugins) {
|
|||||||
return nil, errs.ToAggregate()
|
return nil, errs.ToAggregate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NewResourceQuota(configuration, 5, make(chan struct{}))
|
return NewResourceQuota(configuration, 5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,12 +70,14 @@ type QuotaAdmission struct {
|
|||||||
numEvaluators int
|
numEvaluators int
|
||||||
quotaAccessor *quotaAccessor
|
quotaAccessor *quotaAccessor
|
||||||
evaluator Evaluator
|
evaluator Evaluator
|
||||||
|
initializationErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ admission.ValidationInterface = &QuotaAdmission{}
|
var _ admission.ValidationInterface = &QuotaAdmission{}
|
||||||
var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&QuotaAdmission{})
|
var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&QuotaAdmission{})
|
||||||
var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&QuotaAdmission{})
|
var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&QuotaAdmission{})
|
||||||
var _ = genericadmissioninitializer.WantsQuotaConfiguration(&QuotaAdmission{})
|
var _ = genericadmissioninitializer.WantsQuotaConfiguration(&QuotaAdmission{})
|
||||||
|
var _ = genericadmissioninitializer.WantsDrainedNotification(&QuotaAdmission{})
|
||||||
|
|
||||||
type liveLookupEntry struct {
|
type liveLookupEntry struct {
|
||||||
expiry time.Time
|
expiry time.Time
|
||||||
@ -82,7 +87,7 @@ type liveLookupEntry struct {
|
|||||||
// NewResourceQuota configures an admission controller that can enforce quota constraints
|
// NewResourceQuota configures an admission controller that can enforce quota constraints
|
||||||
// using the provided registry. The registry must have the capability to handle group/kinds that
|
// using the provided registry. The registry must have the capability to handle group/kinds that
|
||||||
// are persisted by the server this admission controller is intercepting
|
// are persisted by the server this admission controller is intercepting
|
||||||
func NewResourceQuota(config *resourcequotaapi.Configuration, numEvaluators int, stopCh <-chan struct{}) (*QuotaAdmission, error) {
|
func NewResourceQuota(config *resourcequotaapi.Configuration, numEvaluators int) (*QuotaAdmission, error) {
|
||||||
quotaAccessor, err := newQuotaAccessor()
|
quotaAccessor, err := newQuotaAccessor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -90,13 +95,18 @@ func NewResourceQuota(config *resourcequotaapi.Configuration, numEvaluators int,
|
|||||||
|
|
||||||
return &QuotaAdmission{
|
return &QuotaAdmission{
|
||||||
Handler: admission.NewHandler(admission.Create, admission.Update),
|
Handler: admission.NewHandler(admission.Create, admission.Update),
|
||||||
stopCh: stopCh,
|
stopCh: nil,
|
||||||
numEvaluators: numEvaluators,
|
numEvaluators: numEvaluators,
|
||||||
config: config,
|
config: config,
|
||||||
quotaAccessor: quotaAccessor,
|
quotaAccessor: quotaAccessor,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDrainedNotification sets the stop channel into QuotaAdmission.
|
||||||
|
func (a *QuotaAdmission) SetDrainedNotification(stopCh <-chan struct{}) {
|
||||||
|
a.stopCh = stopCh
|
||||||
|
}
|
||||||
|
|
||||||
// SetExternalKubeClientSet registers the client into QuotaAdmission
|
// SetExternalKubeClientSet registers the client into QuotaAdmission
|
||||||
func (a *QuotaAdmission) SetExternalKubeClientSet(client kubernetes.Interface) {
|
func (a *QuotaAdmission) SetExternalKubeClientSet(client kubernetes.Interface) {
|
||||||
a.quotaAccessor.client = client
|
a.quotaAccessor.client = client
|
||||||
@ -110,11 +120,21 @@ func (a *QuotaAdmission) SetExternalKubeInformerFactory(f informers.SharedInform
|
|||||||
// SetQuotaConfiguration assigns and initializes configuration and evaluator for QuotaAdmission
|
// SetQuotaConfiguration assigns and initializes configuration and evaluator for QuotaAdmission
|
||||||
func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) {
|
func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) {
|
||||||
a.quotaConfiguration = c
|
a.quotaConfiguration = c
|
||||||
|
if a.stopCh == nil {
|
||||||
|
a.initializationErr = stopChUnconfiguredErr
|
||||||
|
return
|
||||||
|
}
|
||||||
a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration.IgnoredResources(), generic.NewRegistry(a.quotaConfiguration.Evaluators()), nil, a.config, a.numEvaluators, a.stopCh)
|
a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration.IgnoredResources(), generic.NewRegistry(a.quotaConfiguration.Evaluators()), nil, a.config, a.numEvaluators, a.stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateInitialization ensures an authorizer is set.
|
// ValidateInitialization ensures an authorizer is set.
|
||||||
func (a *QuotaAdmission) ValidateInitialization() error {
|
func (a *QuotaAdmission) ValidateInitialization() error {
|
||||||
|
if a.initializationErr != nil {
|
||||||
|
return a.initializationErr
|
||||||
|
}
|
||||||
|
if a.stopCh == nil {
|
||||||
|
return fmt.Errorf("missing stopCh")
|
||||||
|
}
|
||||||
if a.quotaAccessor == nil {
|
if a.quotaAccessor == nil {
|
||||||
return fmt.Errorf("missing quotaAccessor")
|
return fmt.Errorf("missing quotaAccessor")
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
v1 "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1"
|
v1 "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1"
|
||||||
|
"k8s.io/apiserver/pkg/quota/v1/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPrettyPrint(t *testing.T) {
|
func TestPrettyPrint(t *testing.T) {
|
||||||
@ -161,3 +162,14 @@ func TestExcludedOperations(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInitializationOrder(t *testing.T) {
|
||||||
|
a := &QuotaAdmission{}
|
||||||
|
|
||||||
|
qca := generic.NewConfiguration(nil, nil)
|
||||||
|
a.SetQuotaConfiguration(qca)
|
||||||
|
|
||||||
|
if err := a.ValidateInitialization(); err != stopChUnconfiguredErr {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -143,9 +143,8 @@ func (a *AdmissionOptions) ApplyTo(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
genericInitializer := initializer.New(clientset, informers, c.Authorization.Authorizer, features)
|
genericInitializer := initializer.New(clientset, informers, c.Authorization.Authorizer, features, c.DrainedNotify())
|
||||||
initializersChain := admission.PluginInitializers{}
|
initializersChain := admission.PluginInitializers{genericInitializer}
|
||||||
pluginInitializers = append(pluginInitializers, genericInitializer)
|
|
||||||
initializersChain = append(initializersChain, pluginInitializers...)
|
initializersChain = append(initializersChain, pluginInitializers...)
|
||||||
|
|
||||||
admissionChain, err := a.Plugins.NewFromPlugins(pluginNames, pluginsConfigProvider, initializersChain, a.Decorators)
|
admissionChain, err := a.Plugins.NewFromPlugins(pluginNames, pluginsConfigProvider, initializersChain, a.Decorators)
|
||||||
|
@ -35,6 +35,8 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
"k8s.io/apiserver/pkg/admission/plugin/resourcequota"
|
||||||
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
|
||||||
"k8s.io/apiserver/pkg/quota/v1/generic"
|
"k8s.io/apiserver/pkg/quota/v1/generic"
|
||||||
@ -45,6 +47,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
|
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
|
||||||
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
|
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
|
||||||
|
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||||
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
|
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
)
|
)
|
||||||
@ -68,21 +71,27 @@ func TestQuota(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
admissionCh := make(chan struct{})
|
admissionCh := make(chan struct{})
|
||||||
|
defer close(admissionCh)
|
||||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||||
config := &resourcequotaapi.Configuration{}
|
config := &resourcequotaapi.Configuration{}
|
||||||
admission, err := resourcequota.NewResourceQuota(config, 5, admissionCh)
|
admissionControl, err := resourcequota.NewResourceQuota(config, 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
admission.SetExternalKubeClientSet(clientset)
|
|
||||||
internalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
internalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
||||||
admission.SetExternalKubeInformerFactory(internalInformers)
|
|
||||||
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
||||||
admission.SetQuotaConfiguration(qca)
|
|
||||||
defer close(admissionCh)
|
initializers := admission.PluginInitializers{
|
||||||
|
genericadmissioninitializer.New(clientset, internalInformers, nil, nil, admissionCh),
|
||||||
|
kubeapiserveradmission.NewPluginInitializer(nil, nil, qca),
|
||||||
|
}
|
||||||
|
initializers.Initialize(admissionControl)
|
||||||
|
if err := admission.ValidateInitialization(admissionControl); err != nil {
|
||||||
|
t.Fatalf("couldn't initialize resource quota: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig()
|
controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig()
|
||||||
controlPlaneConfig.GenericConfig.AdmissionControl = admission
|
controlPlaneConfig.GenericConfig.AdmissionControl = admissionControl
|
||||||
_, _, closeFn := framework.RunAnAPIServerUsingServer(controlPlaneConfig, s, h)
|
_, _, closeFn := framework.RunAnAPIServerUsingServer(controlPlaneConfig, s, h)
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
|
|
||||||
@ -291,6 +300,7 @@ func TestQuotaLimitedResourceDenial(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
admissionCh := make(chan struct{})
|
admissionCh := make(chan struct{})
|
||||||
|
defer close(admissionCh)
|
||||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||||
|
|
||||||
// stop creation of a pod resource unless there is a quota
|
// stop creation of a pod resource unless there is a quota
|
||||||
@ -303,18 +313,23 @@ func TestQuotaLimitedResourceDenial(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
||||||
admission, err := resourcequota.NewResourceQuota(config, 5, admissionCh)
|
admissionControl, err := resourcequota.NewResourceQuota(config, 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
admission.SetExternalKubeClientSet(clientset)
|
|
||||||
externalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
externalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
||||||
admission.SetExternalKubeInformerFactory(externalInformers)
|
|
||||||
admission.SetQuotaConfiguration(qca)
|
initializers := admission.PluginInitializers{
|
||||||
defer close(admissionCh)
|
genericadmissioninitializer.New(clientset, externalInformers, nil, nil, admissionCh),
|
||||||
|
kubeapiserveradmission.NewPluginInitializer(nil, nil, qca),
|
||||||
|
}
|
||||||
|
initializers.Initialize(admissionControl)
|
||||||
|
if err := admission.ValidateInitialization(admissionControl); err != nil {
|
||||||
|
t.Fatalf("couldn't initialize resource quota: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig()
|
controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig()
|
||||||
controlPlaneConfig.GenericConfig.AdmissionControl = admission
|
controlPlaneConfig.GenericConfig.AdmissionControl = admissionControl
|
||||||
_, _, closeFn := framework.RunAnAPIServerUsingServer(controlPlaneConfig, s, h)
|
_, _, closeFn := framework.RunAnAPIServerUsingServer(controlPlaneConfig, s, h)
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
|
|
||||||
@ -419,6 +434,7 @@ func TestQuotaLimitService(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
admissionCh := make(chan struct{})
|
admissionCh := make(chan struct{})
|
||||||
|
defer close(admissionCh)
|
||||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||||
|
|
||||||
// stop creation of a pod resource unless there is a quota
|
// stop creation of a pod resource unless there is a quota
|
||||||
@ -431,18 +447,23 @@ func TestQuotaLimitService(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
||||||
admission, err := resourcequota.NewResourceQuota(config, 5, admissionCh)
|
admissionControl, err := resourcequota.NewResourceQuota(config, 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
admission.SetExternalKubeClientSet(clientset)
|
|
||||||
externalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
externalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
||||||
admission.SetExternalKubeInformerFactory(externalInformers)
|
|
||||||
admission.SetQuotaConfiguration(qca)
|
initializers := admission.PluginInitializers{
|
||||||
defer close(admissionCh)
|
genericadmissioninitializer.New(clientset, externalInformers, nil, nil, admissionCh),
|
||||||
|
kubeapiserveradmission.NewPluginInitializer(nil, nil, qca),
|
||||||
|
}
|
||||||
|
initializers.Initialize(admissionControl)
|
||||||
|
if err := admission.ValidateInitialization(admissionControl); err != nil {
|
||||||
|
t.Fatalf("couldn't initialize resource quota: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig()
|
controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig()
|
||||||
controlPlaneConfig.GenericConfig.AdmissionControl = admission
|
controlPlaneConfig.GenericConfig.AdmissionControl = admissionControl
|
||||||
_, _, closeFn := framework.RunAnAPIServerUsingServer(controlPlaneConfig, s, h)
|
_, _, closeFn := framework.RunAnAPIServerUsingServer(controlPlaneConfig, s, h)
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user