Merge pull request #28636 from derekwaynecarr/quota_pvc_storage

Automatic merge from submit-queue

Add support to quota pvc storage requests

Adds support to quota cumulative `PersistentVolumeClaim` storage requests in a namespace.

Per our chat today @markturansky @abhgupta - this is not done (lacks unit testing), but is functional.

This lets quota enforcement for `PersistentVolumeClaim` to occur at creation time.  Supporting bind time enforcement would require substantial more work.  It's possible this is sufficient for many, so I am opening it up for feedback.

In the future, I suspect we may want to treat local disk in a special manner, but that would have to be a different resource altogether (i.e. `requests.disk`) or something.

Example quota:

```
apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota
spec:
  hard:
    persistentvolumeclaims: "10"
    requests.storage: "40Gi"
```

/cc @kubernetes/rh-cluster-infra @deads2k
pull/6/head
k8s-merge-robot 2016-07-31 09:26:20 -07:00 committed by GitHub
commit 6ae6450a39
5 changed files with 26 additions and 2 deletions

View File

@ -134,6 +134,7 @@ var standardQuotaResources = sets.NewString(
string(ResourceMemory),
string(ResourceRequestsCPU),
string(ResourceRequestsMemory),
string(ResourceRequestsStorage),
string(ResourceLimitsCPU),
string(ResourceLimitsMemory),
string(ResourcePods),

View File

@ -2570,6 +2570,8 @@ const (
ResourceRequestsCPU ResourceName = "requests.cpu"
// Memory request, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
ResourceRequestsMemory ResourceName = "requests.memory"
// Storage request, in bytes
ResourceRequestsStorage ResourceName = "requests.storage"
// CPU limit, in cores. (500m = .5 cores)
ResourceLimitsCPU ResourceName = "limits.cpu"
// Memory limit, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)

View File

@ -3040,6 +3040,8 @@ const (
ResourceRequestsCPU ResourceName = "requests.cpu"
// Memory request, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
ResourceRequestsMemory ResourceName = "requests.memory"
// Storage request, in bytes
ResourceRequestsStorage ResourceName = "requests.storage"
// CPU limit, in cores. (500m = .5 cores)
ResourceLimitsCPU ResourceName = "limits.cpu"
// Memory limit, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)

View File

@ -19,6 +19,7 @@ package core
import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
@ -27,7 +28,7 @@ import (
// NewPersistentVolumeClaimEvaluator returns an evaluator that can evaluate persistent volume claims
func NewPersistentVolumeClaimEvaluator(kubeClient clientset.Interface) quota.Evaluator {
allResources := []api.ResourceName{api.ResourcePersistentVolumeClaims}
allResources := []api.ResourceName{api.ResourcePersistentVolumeClaims, api.ResourceRequestsStorage}
return &generic.GenericEvaluator{
Name: "Evaluator.PersistentVolumeClaim",
InternalGroupKind: api.Kind("PersistentVolumeClaim"),
@ -37,9 +38,23 @@ func NewPersistentVolumeClaimEvaluator(kubeClient clientset.Interface) quota.Eva
MatchedResourceNames: allResources,
MatchesScopeFunc: generic.MatchesNoScopeFunc,
ConstraintsFunc: generic.ObjectCountConstraintsFunc(api.ResourcePersistentVolumeClaims),
UsageFunc: generic.ObjectCountUsageFunc(api.ResourcePersistentVolumeClaims),
UsageFunc: PersistentVolumeClaimUsageFunc,
ListFuncByNamespace: func(namespace string, options api.ListOptions) (runtime.Object, error) {
return kubeClient.Core().PersistentVolumeClaims(namespace).List(options)
},
}
}
// PersistentVolumeClaimUsageFunc knows how to measure usage associated with persistent volume claims
func PersistentVolumeClaimUsageFunc(object runtime.Object) api.ResourceList {
pvc, ok := object.(*api.PersistentVolumeClaim)
if !ok {
return api.ResourceList{}
}
result := api.ResourceList{}
result[api.ResourcePersistentVolumeClaims] = resource.MustParse("1")
if request, found := pvc.Spec.Resources.Requests[api.ResourceStorage]; found {
result[api.ResourceRequestsStorage] = request
}
return result
}

View File

@ -278,6 +278,7 @@ var _ = framework.KubeDescribe("ResourceQuota", func() {
usedResources := api.ResourceList{}
usedResources[api.ResourceQuotas] = resource.MustParse("1")
usedResources[api.ResourcePersistentVolumeClaims] = resource.MustParse("0")
usedResources[api.ResourceRequestsStorage] = resource.MustParse("0")
err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources)
Expect(err).NotTo(HaveOccurred())
@ -289,6 +290,7 @@ var _ = framework.KubeDescribe("ResourceQuota", func() {
By("Ensuring resource quota status captures persistent volume claimcreation")
usedResources = api.ResourceList{}
usedResources[api.ResourcePersistentVolumeClaims] = resource.MustParse("1")
usedResources[api.ResourceRequestsStorage] = resource.MustParse("1Gi")
err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources)
Expect(err).NotTo(HaveOccurred())
@ -298,6 +300,7 @@ var _ = framework.KubeDescribe("ResourceQuota", func() {
By("Ensuring resource quota status released usage")
usedResources[api.ResourcePersistentVolumeClaims] = resource.MustParse("0")
usedResources[api.ResourceRequestsStorage] = resource.MustParse("0")
err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources)
Expect(err).NotTo(HaveOccurred())
})
@ -512,6 +515,7 @@ func newTestResourceQuota(name string) *api.ResourceQuota {
hard[api.ResourceConfigMaps] = resource.MustParse("2")
hard[api.ResourceSecrets] = resource.MustParse("10")
hard[api.ResourcePersistentVolumeClaims] = resource.MustParse("10")
hard[api.ResourceRequestsStorage] = resource.MustParse("10Gi")
return &api.ResourceQuota{
ObjectMeta: api.ObjectMeta{Name: name},
Spec: api.ResourceQuotaSpec{Hard: hard},