diff --git a/pkg/quota/evaluator/core/configmap.go b/pkg/quota/evaluator/core/configmap.go index 1cc9668c52f9f..bde20fc72478c 100644 --- a/pkg/quota/evaluator/core/configmap.go +++ b/pkg/quota/evaluator/core/configmap.go @@ -17,30 +17,45 @@ limitations under the License. package core import ( + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota/generic" ) +// listConfigMapsByNamespaceFuncUsingClient returns a configMap listing function based on the provided client. +func listConfigMapsByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace { + // TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this. + // 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().ConfigMaps(namespace).List(options) + if err != nil { + return nil, err + } + results := make([]runtime.Object, 0, len(itemList.Items)) + for i := range itemList.Items { + results = append(results, &itemList.Items[i]) + } + return results, nil + } +} + // NewConfigMapEvaluator returns an evaluator that can evaluate configMaps -func NewConfigMapEvaluator(kubeClient clientset.Interface) quota.Evaluator { +// 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: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) { - itemList, err := kubeClient.Core().ConfigMaps(namespace).List(options) - if err != nil { - return nil, err - } - results := make([]runtime.Object, 0, len(itemList.Items)) - for i := range itemList.Items { - results = append(results, &itemList.Items[i]) - } - return results, nil - }, + ListFuncByNamespace: listFuncByNamespace, } } diff --git a/pkg/quota/evaluator/core/registry.go b/pkg/quota/evaluator/core/registry.go index 1c524f832f74a..0ada06e405187 100644 --- a/pkg/quota/evaluator/core/registry.go +++ b/pkg/quota/evaluator/core/registry.go @@ -28,11 +28,11 @@ import ( // If an informer factory is provided, evaluators will use them. func NewRegistry(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Registry { pod := NewPodEvaluator(kubeClient, f) - service := NewServiceEvaluator(kubeClient) - replicationController := NewReplicationControllerEvaluator(kubeClient) - resourceQuota := NewResourceQuotaEvaluator(kubeClient) - secret := NewSecretEvaluator(kubeClient) - configMap := NewConfigMapEvaluator(kubeClient) + service := NewServiceEvaluator(kubeClient, f) + replicationController := NewReplicationControllerEvaluator(kubeClient, f) + resourceQuota := NewResourceQuotaEvaluator(kubeClient, f) + secret := NewSecretEvaluator(kubeClient, f) + configMap := NewConfigMapEvaluator(kubeClient, f) persistentVolumeClaim := NewPersistentVolumeClaimEvaluator(kubeClient, f) return &generic.GenericRegistry{ InternalEvaluators: map[schema.GroupKind]quota.Evaluator{ diff --git a/pkg/quota/evaluator/core/replication_controllers.go b/pkg/quota/evaluator/core/replication_controllers.go index 57e83017f5d6c..0626146083473 100644 --- a/pkg/quota/evaluator/core/replication_controllers.go +++ b/pkg/quota/evaluator/core/replication_controllers.go @@ -17,30 +17,45 @@ limitations under the License. package core import ( + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota/generic" ) -// NewReplicationControllerEvaluator returns an evaluator that can evaluate replication controllers -func NewReplicationControllerEvaluator(kubeClient clientset.Interface) quota.Evaluator { +// listReplicationControllersByNamespaceFuncUsingClient returns a replicationController listing function based on the provided client. +func listReplicationControllersByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace { + // TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this. + // 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().ReplicationControllers(namespace).List(options) + if err != nil { + return nil, err + } + results := make([]runtime.Object, 0, len(itemList.Items)) + for i := range itemList.Items { + results = append(results, &itemList.Items[i]) + } + 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: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) { - itemList, err := kubeClient.Core().ReplicationControllers(namespace).List(options) - if err != nil { - return nil, err - } - results := make([]runtime.Object, 0, len(itemList.Items)) - for i := range itemList.Items { - results = append(results, &itemList.Items[i]) - } - return results, nil - }, + ListFuncByNamespace: listFuncByNamespace, } } diff --git a/pkg/quota/evaluator/core/resource_quotas.go b/pkg/quota/evaluator/core/resource_quotas.go index 8ab48a9710cc2..50033e068c2e6 100644 --- a/pkg/quota/evaluator/core/resource_quotas.go +++ b/pkg/quota/evaluator/core/resource_quotas.go @@ -17,30 +17,45 @@ limitations under the License. package core import ( + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota/generic" ) -// NewResourceQuotaEvaluator returns an evaluator that can evaluate resource quotas -func NewResourceQuotaEvaluator(kubeClient clientset.Interface) quota.Evaluator { +// listResourceQuotasByNamespaceFuncUsingClient returns a resourceQuota listing function based on the provided client. +func listResourceQuotasByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace { + // TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this. + // 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().ResourceQuotas(namespace).List(options) + if err != nil { + return nil, err + } + results := make([]runtime.Object, 0, len(itemList.Items)) + for i := range itemList.Items { + results = append(results, &itemList.Items[i]) + } + 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: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) { - itemList, err := kubeClient.Core().ResourceQuotas(namespace).List(options) - if err != nil { - return nil, err - } - results := make([]runtime.Object, 0, len(itemList.Items)) - for i := range itemList.Items { - results = append(results, &itemList.Items[i]) - } - return results, nil - }, + ListFuncByNamespace: listFuncByNamespace, } } diff --git a/pkg/quota/evaluator/core/secrets.go b/pkg/quota/evaluator/core/secrets.go index d72de70aab1ea..bfd678c95aa79 100644 --- a/pkg/quota/evaluator/core/secrets.go +++ b/pkg/quota/evaluator/core/secrets.go @@ -17,30 +17,45 @@ limitations under the License. package core import ( + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota/generic" ) +// listSecretsByNamespaceFuncUsingClient returns a secret listing function based on the provided client. +func listSecretsByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace { + // TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this. + // 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().Secrets(namespace).List(options) + if err != nil { + return nil, err + } + results := make([]runtime.Object, 0, len(itemList.Items)) + for i := range itemList.Items { + results = append(results, &itemList.Items[i]) + } + return results, nil + } +} + // NewSecretEvaluator returns an evaluator that can evaluate secrets -func NewSecretEvaluator(kubeClient clientset.Interface) quota.Evaluator { +// 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: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) { - itemList, err := kubeClient.Core().Secrets(namespace).List(options) - if err != nil { - return nil, err - } - results := make([]runtime.Object, 0, len(itemList.Items)) - for i := range itemList.Items { - results = append(results, &itemList.Items[i]) - } - return results, nil - }, + ListFuncByNamespace: listFuncByNamespace, } } diff --git a/pkg/quota/evaluator/core/services.go b/pkg/quota/evaluator/core/services.go index edba4a4eb5c61..457d8b4173543 100644 --- a/pkg/quota/evaluator/core/services.go +++ b/pkg/quota/evaluator/core/services.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/admission" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/api" k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1" @@ -41,20 +42,33 @@ var serviceResources = []api.ResourceName{ api.ResourceServicesLoadBalancers, } -// NewServiceEvaluator returns an evaluator that can evaluate service quotas -func NewServiceEvaluator(kubeClient clientset.Interface) quota.Evaluator { +// listServicesByNamespaceFuncUsingClient returns a service listing function based on the provided client. +func listServicesByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace { + // TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this. + // 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) + if err != nil { + return nil, err + } + results := make([]runtime.Object, 0, len(itemList.Items)) + for i := range itemList.Items { + results = append(results, &itemList.Items[i]) + } + 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: func(namespace string, options metav1.ListOptions) ([]runtime.Object, error) { - itemList, err := kubeClient.Core().Services(namespace).List(options) - if err != nil { - return nil, err - } - results := make([]runtime.Object, 0, len(itemList.Items)) - for i := range itemList.Items { - results = append(results, &itemList.Items[i]) - } - return results, nil - }, + listFuncByNamespace: listFuncByNamespace, } } @@ -125,7 +139,7 @@ func toInternalServiceOrError(obj runtime.Object) (*api.Service, error) { 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) { result := api.ResourceList{} svc, err := toInternalServiceOrError(item) diff --git a/pkg/quota/evaluator/core/services_test.go b/pkg/quota/evaluator/core/services_test.go index 1e05672fe2f47..3f42290824fac 100644 --- a/pkg/quota/evaluator/core/services_test.go +++ b/pkg/quota/evaluator/core/services_test.go @@ -27,7 +27,7 @@ import ( func TestServiceEvaluatorMatchesResources(t *testing.T) { kubeClient := fake.NewSimpleClientset() - evaluator := NewServiceEvaluator(kubeClient) + evaluator := NewServiceEvaluator(kubeClient, nil) // we give a lot of resources input := []api.ResourceName{ api.ResourceConfigMaps, @@ -50,7 +50,7 @@ func TestServiceEvaluatorMatchesResources(t *testing.T) { func TestServiceEvaluatorUsage(t *testing.T) { kubeClient := fake.NewSimpleClientset() - evaluator := NewServiceEvaluator(kubeClient) + evaluator := NewServiceEvaluator(kubeClient, nil) testCases := map[string]struct { service *api.Service usage api.ResourceList @@ -199,7 +199,7 @@ func TestServiceConstraintsFunc(t *testing.T) { } kubeClient := fake.NewSimpleClientset() - evaluator := NewServiceEvaluator(kubeClient) + evaluator := NewServiceEvaluator(kubeClient, nil) for testName, test := range testCases { err := evaluator.Constraints(test.required, test.service) switch {