From f4ed4998ffd6b2b9836e626e8151d81e6161d80f Mon Sep 17 00:00:00 2001 From: Salvatore Dario Minonne Date: Thu, 14 Apr 2016 10:55:52 +0200 Subject: [PATCH] Adding loadbalancer services to quota --- pkg/api/helpers.go | 2 + pkg/api/types.go | 2 + pkg/api/v1/types.go | 2 + .../resourcequota/replenishment_controller.go | 2 +- pkg/quota/evaluator/core/services.go | 5 +- test/e2e/resource_quota.go | 91 +++++++++++++++++++ 6 files changed, 102 insertions(+), 2 deletions(-) diff --git a/pkg/api/helpers.go b/pkg/api/helpers.go index 8d4fadc536..ee75f67470 100644 --- a/pkg/api/helpers.go +++ b/pkg/api/helpers.go @@ -153,6 +153,7 @@ var standardQuotaResources = sets.NewString( string(ResourcePersistentVolumeClaims), string(ResourceConfigMaps), string(ResourceServicesNodePorts), + string(ResourceServicesLoadBalancers), ) // IsStandardQuotaResourceName returns true if the resource is known to @@ -192,6 +193,7 @@ var integerResources = sets.NewString( string(ResourceConfigMaps), string(ResourcePersistentVolumeClaims), string(ResourceServicesNodePorts), + string(ResourceServicesLoadBalancers), ) // IsIntegerResourceName returns true if the resource is measured in integer values diff --git a/pkg/api/types.go b/pkg/api/types.go index 69c8a876a6..f93e39680a 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -2212,6 +2212,8 @@ const ( ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims" // ResourceServicesNodePorts, number ResourceServicesNodePorts ResourceName = "services.nodeports" + // ResourceServicesLoadBalancers, number + ResourceServicesLoadBalancers ResourceName = "services.loadbalancers" // CPU request, in cores. (500m = .5 cores) ResourceRequestsCPU ResourceName = "requests.cpu" // Memory request, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 517d5e6413..4642c92c0c 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -2670,6 +2670,8 @@ const ( ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims" // ResourceServicesNodePorts, number ResourceServicesNodePorts ResourceName = "services.nodeports" + // ResourceServicesLoadBalancers, number + ResourceServicesLoadBalancers ResourceName = "services.loadbalancers" // CPU request, in cores. (500m = .5 cores) ResourceCPURequest ResourceName = "cpu.request" // CPU limit, in cores. (500m = .5 cores) diff --git a/pkg/controller/resourcequota/replenishment_controller.go b/pkg/controller/resourcequota/replenishment_controller.go index a75ba291dd..c6f0014625 100644 --- a/pkg/controller/resourcequota/replenishment_controller.go +++ b/pkg/controller/resourcequota/replenishment_controller.go @@ -215,7 +215,7 @@ func ServiceReplenishmentUpdateFunc(options *ReplenishmentControllerOptions) fun return func(oldObj, newObj interface{}) { oldService := oldObj.(*api.Service) newService := newObj.(*api.Service) - if core.QuotaServiceType(oldService) && !core.QuotaServiceType(newService) { + if core.QuotaServiceType(oldService) || core.QuotaServiceType(newService) { options.ReplenishmentFunc(options.GroupKind, newService.Namespace, newService) } } diff --git a/pkg/quota/evaluator/core/services.go b/pkg/quota/evaluator/core/services.go index 98812b6736..ec1a90282a 100644 --- a/pkg/quota/evaluator/core/services.go +++ b/pkg/quota/evaluator/core/services.go @@ -31,6 +31,7 @@ func NewServiceEvaluator(kubeClient clientset.Interface) quota.Evaluator { allResources := []api.ResourceName{ api.ResourceServices, api.ResourceServicesNodePorts, + api.ResourceServicesLoadBalancers, } return &generic.GenericEvaluator{ Name: "Evaluator.Service", @@ -56,6 +57,8 @@ func ServiceUsageFunc(object runtime.Object) api.ResourceList { switch service.Spec.Type { case api.ServiceTypeNodePort: result[api.ResourceServicesNodePorts] = resource.MustParse("1") + case api.ServiceTypeLoadBalancer: + result[api.ResourceServicesLoadBalancers] = resource.MustParse("1") } } return result @@ -64,7 +67,7 @@ func ServiceUsageFunc(object runtime.Object) api.ResourceList { // QuotaServiceType returns true if the service type is eligible to track against a quota func QuotaServiceType(service *api.Service) bool { switch service.Spec.Type { - case api.ServiceTypeNodePort: + case api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer: return true } return false diff --git a/test/e2e/resource_quota.go b/test/e2e/resource_quota.go index 6831a40b79..44740cd433 100644 --- a/test/e2e/resource_quota.go +++ b/test/e2e/resource_quota.go @@ -220,6 +220,96 @@ var _ = framework.KubeDescribe("ResourceQuota", func() { Expect(err).NotTo(HaveOccurred()) }) + It("should create a ResourceQuota and capture the life of a loadBalancer service.", func() { + By("Creating a ResourceQuota") + quotaName := "test-quota" + resourceQuota := newTestResourceQuota(quotaName) + resourceQuota, err := createResourceQuota(f.Client, f.Namespace.Name, resourceQuota) + Expect(err).NotTo(HaveOccurred()) + + By("Ensuring resource quota status is calculated") + usedResources := api.ResourceList{} + usedResources[api.ResourceQuotas] = resource.MustParse("1") + err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources) + Expect(err).NotTo(HaveOccurred()) + + By("Creating a loadBalancer type Service") + service := newTestServiceForQuota("test-service", api.ServiceTypeLoadBalancer) + service, err = f.Client.Services(f.Namespace.Name).Create(service) + Expect(err).NotTo(HaveOccurred()) + + By("Ensuring resource quota status captures service creation") + usedResources = api.ResourceList{} + usedResources[api.ResourceQuotas] = resource.MustParse("1") + usedResources[api.ResourceServices] = resource.MustParse("1") + usedResources[api.ResourceServicesLoadBalancers] = resource.MustParse("1") + err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources) + Expect(err).NotTo(HaveOccurred()) + + By("Deleting a Service") + err = f.Client.Services(f.Namespace.Name).Delete(service.Name) + Expect(err).NotTo(HaveOccurred()) + + By("Ensuring resource quota status released usage") + usedResources[api.ResourceServices] = resource.MustParse("0") + usedResources[api.ResourceServicesLoadBalancers] = resource.MustParse("0") + err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should create a ResourceQuota and capture the life of a nodePort service updated to loadBalancer.", func() { + By("Creating a ResourceQuota") + quotaName := "test-quota" + resourceQuota := newTestResourceQuota(quotaName) + resourceQuota, err := createResourceQuota(f.Client, f.Namespace.Name, resourceQuota) + Expect(err).NotTo(HaveOccurred()) + + By("Ensuring resource quota status is calculated") + usedResources := api.ResourceList{} + usedResources[api.ResourceQuotas] = resource.MustParse("1") + err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources) + Expect(err).NotTo(HaveOccurred()) + + By("Creating a nodePort type Service") + service := newTestServiceForQuota("test-service", api.ServiceTypeNodePort) + service, err = f.Client.Services(f.Namespace.Name).Create(service) + Expect(err).NotTo(HaveOccurred()) + + By("Ensuring resource quota status captures service creation") + usedResources = api.ResourceList{} + usedResources[api.ResourceQuotas] = resource.MustParse("1") + usedResources[api.ResourceServices] = resource.MustParse("1") + usedResources[api.ResourceServicesLoadBalancers] = resource.MustParse("0") + usedResources[api.ResourceServicesNodePorts] = resource.MustParse("1") + err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources) + Expect(err).NotTo(HaveOccurred()) + + By("Updating the service type to loadBalancer") + service.Spec.Type = api.ServiceTypeLoadBalancer + service.Spec.Ports[0].NodePort = 0 + _, err = f.Client.Services(f.Namespace.Name).Update(service) + Expect(err).NotTo(HaveOccurred()) + + By("Checking resource quota status capture service update") + usedResources = api.ResourceList{} + usedResources[api.ResourceQuotas] = resource.MustParse("1") + usedResources[api.ResourceServices] = resource.MustParse("1") + usedResources[api.ResourceServicesLoadBalancers] = resource.MustParse("1") + usedResources[api.ResourceServicesNodePorts] = resource.MustParse("0") + err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources) + Expect(err).NotTo(HaveOccurred()) + + By("Deleting a Service") + err = f.Client.Services(f.Namespace.Name).Delete(service.Name) + Expect(err).NotTo(HaveOccurred()) + + By("Ensuring resource quota status released usage") + usedResources[api.ResourceServices] = resource.MustParse("0") + usedResources[api.ResourceServicesLoadBalancers] = resource.MustParse("0") + err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources) + Expect(err).NotTo(HaveOccurred()) + }) + It("should create a ResourceQuota and capture the life of a pod.", func() { By("Creating a ResourceQuota") quotaName := "test-quota" @@ -578,6 +668,7 @@ func newTestResourceQuota(name string) *api.ResourceQuota { hard[api.ResourcePods] = resource.MustParse("5") hard[api.ResourceServices] = resource.MustParse("10") hard[api.ResourceServicesNodePorts] = resource.MustParse("1") + hard[api.ResourceServicesLoadBalancers] = resource.MustParse("1") hard[api.ResourceReplicationControllers] = resource.MustParse("10") hard[api.ResourceQuotas] = resource.MustParse("1") hard[api.ResourceCPU] = resource.MustParse("1")