mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 09:49:50 +00:00
Merge pull request #49230 from deads2k/quota-02-informer
Automatic merge from submit-queue (batch tested with PRs 49218, 48253, 48967, 48460, 49230) use informers for quota evaluation of core resources where possible Not all quota evaluators are using shared informers. This updates them all to have the option of doing it. Fixes https://github.com/kubernetes/kubernetes/issues/49233 ``` resource quota uses shared informers for core types ```
This commit is contained in:
commit
e48ad7782e
@ -17,21 +17,22 @@ limitations under the License.
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/quota"
|
"k8s.io/kubernetes/pkg/quota"
|
||||||
"k8s.io/kubernetes/pkg/quota/generic"
|
"k8s.io/kubernetes/pkg/quota/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewConfigMapEvaluator returns an evaluator that can evaluate configMaps
|
// listConfigMapsByNamespaceFuncUsingClient returns a configMap listing function based on the provided client.
|
||||||
func NewConfigMapEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
func listConfigMapsByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace {
|
||||||
return &generic.ObjectCountEvaluator{
|
// TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this.
|
||||||
AllowCreateOnUpdate: false,
|
// unfortunately, dynamic client works with Unstructured objects, and when we calculate Usage, we require
|
||||||
InternalGroupKind: api.Kind("ConfigMap"),
|
// structured objects.
|
||||||
ResourceName: api.ResourceConfigMaps,
|
return func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
||||||
ListFuncByNamespace: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
|
||||||
itemList, err := kubeClient.Core().ConfigMaps(namespace).List(options)
|
itemList, err := kubeClient.Core().ConfigMaps(namespace).List(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -41,6 +42,20 @@ func NewConfigMapEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
|||||||
results = append(results, &itemList.Items[i])
|
results = append(results, &itemList.Items[i])
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfigMapEvaluator returns an evaluator that can evaluate configMaps
|
||||||
|
// if the specified shared informer factory is not nil, evaluator may use it to support listing functions.
|
||||||
|
func NewConfigMapEvaluator(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Evaluator {
|
||||||
|
listFuncByNamespace := listConfigMapsByNamespaceFuncUsingClient(kubeClient)
|
||||||
|
if f != nil {
|
||||||
|
listFuncByNamespace = generic.ListResourceUsingInformerFunc(f, v1.SchemeGroupVersion.WithResource("configmaps"))
|
||||||
|
}
|
||||||
|
return &generic.ObjectCountEvaluator{
|
||||||
|
AllowCreateOnUpdate: false,
|
||||||
|
InternalGroupKind: api.Kind("ConfigMap"),
|
||||||
|
ResourceName: api.ResourceConfigMaps,
|
||||||
|
ListFuncByNamespace: listFuncByNamespace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,11 @@ import (
|
|||||||
// If an informer factory is provided, evaluators will use them.
|
// If an informer factory is provided, evaluators will use them.
|
||||||
func NewRegistry(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Registry {
|
func NewRegistry(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Registry {
|
||||||
pod := NewPodEvaluator(kubeClient, f)
|
pod := NewPodEvaluator(kubeClient, f)
|
||||||
service := NewServiceEvaluator(kubeClient)
|
service := NewServiceEvaluator(kubeClient, f)
|
||||||
replicationController := NewReplicationControllerEvaluator(kubeClient)
|
replicationController := NewReplicationControllerEvaluator(kubeClient, f)
|
||||||
resourceQuota := NewResourceQuotaEvaluator(kubeClient)
|
resourceQuota := NewResourceQuotaEvaluator(kubeClient, f)
|
||||||
secret := NewSecretEvaluator(kubeClient)
|
secret := NewSecretEvaluator(kubeClient, f)
|
||||||
configMap := NewConfigMapEvaluator(kubeClient)
|
configMap := NewConfigMapEvaluator(kubeClient, f)
|
||||||
persistentVolumeClaim := NewPersistentVolumeClaimEvaluator(kubeClient, f)
|
persistentVolumeClaim := NewPersistentVolumeClaimEvaluator(kubeClient, f)
|
||||||
return &generic.GenericRegistry{
|
return &generic.GenericRegistry{
|
||||||
InternalEvaluators: map[schema.GroupKind]quota.Evaluator{
|
InternalEvaluators: map[schema.GroupKind]quota.Evaluator{
|
||||||
|
@ -17,21 +17,22 @@ limitations under the License.
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/quota"
|
"k8s.io/kubernetes/pkg/quota"
|
||||||
"k8s.io/kubernetes/pkg/quota/generic"
|
"k8s.io/kubernetes/pkg/quota/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewReplicationControllerEvaluator returns an evaluator that can evaluate replication controllers
|
// listReplicationControllersByNamespaceFuncUsingClient returns a replicationController listing function based on the provided client.
|
||||||
func NewReplicationControllerEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
func listReplicationControllersByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace {
|
||||||
return &generic.ObjectCountEvaluator{
|
// TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this.
|
||||||
AllowCreateOnUpdate: false,
|
// unfortunately, dynamic client works with Unstructured objects, and when we calculate Usage, we require
|
||||||
InternalGroupKind: api.Kind("ReplicationController"),
|
// structured objects.
|
||||||
ResourceName: api.ResourceReplicationControllers,
|
return func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
||||||
ListFuncByNamespace: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
|
||||||
itemList, err := kubeClient.Core().ReplicationControllers(namespace).List(options)
|
itemList, err := kubeClient.Core().ReplicationControllers(namespace).List(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -41,6 +42,20 @@ func NewReplicationControllerEvaluator(kubeClient clientset.Interface) quota.Eva
|
|||||||
results = append(results, &itemList.Items[i])
|
results = append(results, &itemList.Items[i])
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReplicationControllerEvaluator returns an evaluator that can evaluate replicationControllers
|
||||||
|
// if the specified shared informer factory is not nil, evaluator may use it to support listing functions.
|
||||||
|
func NewReplicationControllerEvaluator(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Evaluator {
|
||||||
|
listFuncByNamespace := listReplicationControllersByNamespaceFuncUsingClient(kubeClient)
|
||||||
|
if f != nil {
|
||||||
|
listFuncByNamespace = generic.ListResourceUsingInformerFunc(f, v1.SchemeGroupVersion.WithResource("replicationcontrollers"))
|
||||||
|
}
|
||||||
|
return &generic.ObjectCountEvaluator{
|
||||||
|
AllowCreateOnUpdate: false,
|
||||||
|
InternalGroupKind: api.Kind("ReplicationController"),
|
||||||
|
ResourceName: api.ResourceReplicationControllers,
|
||||||
|
ListFuncByNamespace: listFuncByNamespace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,22 @@ limitations under the License.
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/quota"
|
"k8s.io/kubernetes/pkg/quota"
|
||||||
"k8s.io/kubernetes/pkg/quota/generic"
|
"k8s.io/kubernetes/pkg/quota/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewResourceQuotaEvaluator returns an evaluator that can evaluate resource quotas
|
// listResourceQuotasByNamespaceFuncUsingClient returns a resourceQuota listing function based on the provided client.
|
||||||
func NewResourceQuotaEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
func listResourceQuotasByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace {
|
||||||
return &generic.ObjectCountEvaluator{
|
// TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this.
|
||||||
AllowCreateOnUpdate: false,
|
// unfortunately, dynamic client works with Unstructured objects, and when we calculate Usage, we require
|
||||||
InternalGroupKind: api.Kind("ResourceQuota"),
|
// structured objects.
|
||||||
ResourceName: api.ResourceQuotas,
|
return func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
||||||
ListFuncByNamespace: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
|
||||||
itemList, err := kubeClient.Core().ResourceQuotas(namespace).List(options)
|
itemList, err := kubeClient.Core().ResourceQuotas(namespace).List(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -41,6 +42,20 @@ func NewResourceQuotaEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
|||||||
results = append(results, &itemList.Items[i])
|
results = append(results, &itemList.Items[i])
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResourceQuotaEvaluator returns an evaluator that can evaluate resourceQuotas
|
||||||
|
// if the specified shared informer factory is not nil, evaluator may use it to support listing functions.
|
||||||
|
func NewResourceQuotaEvaluator(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Evaluator {
|
||||||
|
listFuncByNamespace := listResourceQuotasByNamespaceFuncUsingClient(kubeClient)
|
||||||
|
if f != nil {
|
||||||
|
listFuncByNamespace = generic.ListResourceUsingInformerFunc(f, v1.SchemeGroupVersion.WithResource("resourcequotas"))
|
||||||
|
}
|
||||||
|
return &generic.ObjectCountEvaluator{
|
||||||
|
AllowCreateOnUpdate: false,
|
||||||
|
InternalGroupKind: api.Kind("ResourceQuota"),
|
||||||
|
ResourceName: api.ResourceQuotas,
|
||||||
|
ListFuncByNamespace: listFuncByNamespace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,22 @@ limitations under the License.
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/quota"
|
"k8s.io/kubernetes/pkg/quota"
|
||||||
"k8s.io/kubernetes/pkg/quota/generic"
|
"k8s.io/kubernetes/pkg/quota/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewSecretEvaluator returns an evaluator that can evaluate secrets
|
// listSecretsByNamespaceFuncUsingClient returns a secret listing function based on the provided client.
|
||||||
func NewSecretEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
func listSecretsByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace {
|
||||||
return &generic.ObjectCountEvaluator{
|
// TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this.
|
||||||
AllowCreateOnUpdate: false,
|
// unfortunately, dynamic client works with Unstructured objects, and when we calculate Usage, we require
|
||||||
InternalGroupKind: api.Kind("Secret"),
|
// structured objects.
|
||||||
ResourceName: api.ResourceSecrets,
|
return func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
||||||
ListFuncByNamespace: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
|
||||||
itemList, err := kubeClient.Core().Secrets(namespace).List(options)
|
itemList, err := kubeClient.Core().Secrets(namespace).List(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -41,6 +42,20 @@ func NewSecretEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
|||||||
results = append(results, &itemList.Items[i])
|
results = append(results, &itemList.Items[i])
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSecretEvaluator returns an evaluator that can evaluate secrets
|
||||||
|
// if the specified shared informer factory is not nil, evaluator may use it to support listing functions.
|
||||||
|
func NewSecretEvaluator(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Evaluator {
|
||||||
|
listFuncByNamespace := listSecretsByNamespaceFuncUsingClient(kubeClient)
|
||||||
|
if f != nil {
|
||||||
|
listFuncByNamespace = generic.ListResourceUsingInformerFunc(f, v1.SchemeGroupVersion.WithResource("secrets"))
|
||||||
|
}
|
||||||
|
return &generic.ObjectCountEvaluator{
|
||||||
|
AllowCreateOnUpdate: false,
|
||||||
|
InternalGroupKind: api.Kind("Secret"),
|
||||||
|
ResourceName: api.ResourceSecrets,
|
||||||
|
ListFuncByNamespace: listFuncByNamespace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
@ -41,10 +42,12 @@ var serviceResources = []api.ResourceName{
|
|||||||
api.ResourceServicesLoadBalancers,
|
api.ResourceServicesLoadBalancers,
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceEvaluator returns an evaluator that can evaluate service quotas
|
// listServicesByNamespaceFuncUsingClient returns a service listing function based on the provided client.
|
||||||
func NewServiceEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
func listServicesByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace {
|
||||||
return &serviceEvaluator{
|
// TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this.
|
||||||
listFuncByNamespace: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
// unfortunately, dynamic client works with Unstructured objects, and when we calculate Usage, we require
|
||||||
|
// structured objects.
|
||||||
|
return func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) {
|
||||||
itemList, err := kubeClient.Core().Services(namespace).List(options)
|
itemList, err := kubeClient.Core().Services(namespace).List(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -54,7 +57,18 @@ func NewServiceEvaluator(kubeClient clientset.Interface) quota.Evaluator {
|
|||||||
results = append(results, &itemList.Items[i])
|
results = append(results, &itemList.Items[i])
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServiceEvaluator returns an evaluator that can evaluate services
|
||||||
|
// if the specified shared informer factory is not nil, evaluator may use it to support listing functions.
|
||||||
|
func NewServiceEvaluator(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Evaluator {
|
||||||
|
listFuncByNamespace := listServicesByNamespaceFuncUsingClient(kubeClient)
|
||||||
|
if f != nil {
|
||||||
|
listFuncByNamespace = generic.ListResourceUsingInformerFunc(f, v1.SchemeGroupVersion.WithResource("services"))
|
||||||
|
}
|
||||||
|
return &serviceEvaluator{
|
||||||
|
listFuncByNamespace: listFuncByNamespace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +139,7 @@ func toInternalServiceOrError(obj runtime.Object) (*api.Service, error) {
|
|||||||
return svc, nil
|
return svc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage knows how to measure usage associated with pods
|
// Usage knows how to measure usage associated with services
|
||||||
func (p *serviceEvaluator) Usage(item runtime.Object) (api.ResourceList, error) {
|
func (p *serviceEvaluator) Usage(item runtime.Object) (api.ResourceList, error) {
|
||||||
result := api.ResourceList{}
|
result := api.ResourceList{}
|
||||||
svc, err := toInternalServiceOrError(item)
|
svc, err := toInternalServiceOrError(item)
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func TestServiceEvaluatorMatchesResources(t *testing.T) {
|
func TestServiceEvaluatorMatchesResources(t *testing.T) {
|
||||||
kubeClient := fake.NewSimpleClientset()
|
kubeClient := fake.NewSimpleClientset()
|
||||||
evaluator := NewServiceEvaluator(kubeClient)
|
evaluator := NewServiceEvaluator(kubeClient, nil)
|
||||||
// we give a lot of resources
|
// we give a lot of resources
|
||||||
input := []api.ResourceName{
|
input := []api.ResourceName{
|
||||||
api.ResourceConfigMaps,
|
api.ResourceConfigMaps,
|
||||||
@ -50,7 +50,7 @@ func TestServiceEvaluatorMatchesResources(t *testing.T) {
|
|||||||
|
|
||||||
func TestServiceEvaluatorUsage(t *testing.T) {
|
func TestServiceEvaluatorUsage(t *testing.T) {
|
||||||
kubeClient := fake.NewSimpleClientset()
|
kubeClient := fake.NewSimpleClientset()
|
||||||
evaluator := NewServiceEvaluator(kubeClient)
|
evaluator := NewServiceEvaluator(kubeClient, nil)
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
service *api.Service
|
service *api.Service
|
||||||
usage api.ResourceList
|
usage api.ResourceList
|
||||||
@ -199,7 +199,7 @@ func TestServiceConstraintsFunc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kubeClient := fake.NewSimpleClientset()
|
kubeClient := fake.NewSimpleClientset()
|
||||||
evaluator := NewServiceEvaluator(kubeClient)
|
evaluator := NewServiceEvaluator(kubeClient, nil)
|
||||||
for testName, test := range testCases {
|
for testName, test := range testCases {
|
||||||
err := evaluator.Constraints(test.required, test.service)
|
err := evaluator.Constraints(test.required, test.service)
|
||||||
switch {
|
switch {
|
||||||
|
Loading…
Reference in New Issue
Block a user