DRA quota: add ResourceClaim v1.ResourceQuota limits
Dynamic resource allocation is similar to storage in the sense that users create ResourceClaim objects to request resources, same as with persistent volume claims. The actual resource usage is only known when allocating claims, but some limits can already be enforced at admission time: - "count/resourceclaims.resource.k8s.io" limits the number of ResourceClaim objects in a namespace; this is a generic feature that is already supported also without this commit. - "resourceclaims" is *not* an alias - use "count/resourceclaims.resource.k8s.io" instead. - <device-class-name>.deviceclass.resource.k8s.io/devices limits the number of ResourceClaim objects in a namespace such that the number of devices requested through those objects with that class does not exceed the limit. A single request may cause the allocation of multiple devices. For exact counts, the quota limit is based on the sum of those exact counts. For requests asking for "all" matching devices, the maximum number of allocated devices per claim is used as a worst-case upper bound. Requests asking for "admin access" contribute to the quota. DRA quota: remove admin mode exception
This commit is contained in:
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
resourceapi "k8s.io/api/resource/v1alpha3"
|
||||
schedulingv1 "k8s.io/api/scheduling/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -489,6 +490,56 @@ var _ = SIGDescribe("ResourceQuota", func() {
|
||||
framework.ExpectNoError(err)
|
||||
})
|
||||
|
||||
/*
|
||||
Release: v1.31
|
||||
Testname: ResourceQuota, object count quota, ResourceClaim
|
||||
Description: Create a ResourceQuota. Creation MUST be successful and its ResourceQuotaStatus MUST match to expected used and total allowed resource quota count within namespace.
|
||||
Create ResourceClaim. Creation MUST be successful and resource usage count against the ResourceClaim object MUST be captured in ResourceQuotaStatus of the ResourceQuota.
|
||||
Delete the ResourceClaim. Deletion MUST succeed and resource usage count against the ResourceClaim object MUST be released from ResourceQuotaStatus of the ResourceQuota.
|
||||
[NotConformancePromotable] alpha feature
|
||||
*/
|
||||
f.It("should create a ResourceQuota and capture the life of a ResourceClaim", feature.DynamicResourceAllocation, func(ctx context.Context) {
|
||||
ginkgo.By("Counting existing ResourceQuota")
|
||||
c, err := countResourceQuota(ctx, f.ClientSet, f.Namespace.Name)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("Creating a ResourceQuota")
|
||||
quotaName := "test-quota"
|
||||
resourceQuota := newTestResourceQuotaDRA(quotaName)
|
||||
_, err = createResourceQuota(ctx, f.ClientSet, f.Namespace.Name, resourceQuota)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("Ensuring resource quota status is calculated")
|
||||
usedResources := v1.ResourceList{}
|
||||
usedResources[v1.ResourceQuotas] = resource.MustParse(strconv.Itoa(c + 1))
|
||||
usedResources[core.ClaimObjectCountName] = resource.MustParse("0")
|
||||
usedResources[core.V1ResourceByDeviceClass(classGold)] = resource.MustParse("0")
|
||||
err = waitForResourceQuota(ctx, f.ClientSet, f.Namespace.Name, quotaName, usedResources)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("Creating a ResourceClaim")
|
||||
claim := newTestResourceClaimForQuota("test-claim")
|
||||
claim, err = f.ClientSet.ResourceV1alpha3().ResourceClaims(f.Namespace.Name).Create(ctx, claim, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("Ensuring resource quota status captures resource claim creation")
|
||||
usedResources = v1.ResourceList{}
|
||||
usedResources[core.ClaimObjectCountName] = resource.MustParse("1")
|
||||
usedResources[core.V1ResourceByDeviceClass(classGold)] = resource.MustParse("1")
|
||||
err = waitForResourceQuota(ctx, f.ClientSet, f.Namespace.Name, quotaName, usedResources)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("Deleting a ResourceClaim")
|
||||
err = f.ClientSet.ResourceV1alpha3().ResourceClaims(f.Namespace.Name).Delete(ctx, claim.Name, metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("Ensuring resource quota status released usage")
|
||||
usedResources[core.ClaimObjectCountName] = resource.MustParse("0")
|
||||
usedResources[core.V1ResourceByDeviceClass(classGold)] = resource.MustParse("0")
|
||||
err = waitForResourceQuota(ctx, f.ClientSet, f.Namespace.Name, quotaName, usedResources)
|
||||
framework.ExpectNoError(err)
|
||||
})
|
||||
|
||||
/*
|
||||
Release: v1.16
|
||||
Testname: ResourceQuota, object count quota, pvc
|
||||
@@ -1906,6 +1957,14 @@ func newTestResourceQuota(name string) *v1.ResourceQuota {
|
||||
}
|
||||
}
|
||||
|
||||
// newTestResourceQuotaDRA returns a quota that includes hard limits for ResourceClaim objects.
|
||||
func newTestResourceQuotaDRA(name string) *v1.ResourceQuota {
|
||||
quota := newTestResourceQuota(name)
|
||||
quota.Spec.Hard[core.ClaimObjectCountName] = resource.MustParse("1")
|
||||
quota.Spec.Hard[core.V1ResourceByDeviceClass(classGold)] = resource.MustParse("1")
|
||||
return quota
|
||||
}
|
||||
|
||||
// newTestPodForQuota returns a pod that has the specified requests and limits
|
||||
func newTestPodForQuota(f *framework.Framework, name string, requests v1.ResourceList, limits v1.ResourceList) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
@@ -2003,6 +2062,23 @@ func newTestPersistentVolumeClaimForQuota(name string) *v1.PersistentVolumeClaim
|
||||
}
|
||||
}
|
||||
|
||||
// newTestResourceClaimForQuota returns a simple resource claim
|
||||
func newTestResourceClaimForQuota(name string) *resourceapi.ResourceClaim {
|
||||
return &resourceapi.ResourceClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: resourceapi.ResourceClaimSpec{
|
||||
Devices: resourceapi.DeviceClaim{
|
||||
Requests: []resourceapi.DeviceRequest{{
|
||||
Name: "req-0",
|
||||
DeviceClassName: classGold,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// newTestReplicationControllerForQuota returns a simple replication controller
|
||||
func newTestReplicationControllerForQuota(name, image string, replicas int32) *v1.ReplicationController {
|
||||
return &v1.ReplicationController{
|
||||
|
||||
Reference in New Issue
Block a user