@@ -27,11 +27,13 @@ import (
27
27
"k8s.io/apimachinery/pkg/api/errors"
28
28
"k8s.io/apimachinery/pkg/api/resource"
29
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30
31
"k8s.io/apimachinery/pkg/util/intstr"
31
32
"k8s.io/apimachinery/pkg/util/wait"
32
33
clientset "k8s.io/client-go/kubernetes"
33
34
"k8s.io/kubernetes/pkg/quota/v1/evaluator/core"
34
35
"k8s.io/kubernetes/test/e2e/framework"
36
+ "k8s.io/kubernetes/test/utils/crd"
35
37
imageutils "k8s.io/kubernetes/test/utils/image"
36
38
37
39
. "github.com/onsi/ginkgo"
@@ -487,6 +489,89 @@ var _ = SIGDescribe("ResourceQuota", func() {
487
489
Expect (err ).NotTo (HaveOccurred ())
488
490
})
489
491
492
+ It ("should create a ResourceQuota and capture the life of a custom resource." , func () {
493
+ By ("Creating a Custom Resource Definition" )
494
+ testcrd , err := crd .CreateTestCRD (f )
495
+ Expect (err ).NotTo (HaveOccurred ())
496
+ defer testcrd .CleanUp ()
497
+ countResourceName := "count/" + testcrd .Crd .Spec .Names .Plural + "." + testcrd .Crd .Spec .Group
498
+ // resourcequota controller needs to take 30 seconds at most to detect the new custom resource.
499
+ // in order to make sure the resourcequota controller knows this resource, we create one test
500
+ // resourcequota object, and triggering updates on it until the status is updated.
501
+ quotaName := "quota-for-" + testcrd .Crd .Spec .Names .Plural
502
+ resourceQuota , err := createResourceQuota (f .ClientSet , f .Namespace .Name , & v1.ResourceQuota {
503
+ ObjectMeta : metav1.ObjectMeta {Name : quotaName },
504
+ Spec : v1.ResourceQuotaSpec {
505
+ Hard : v1.ResourceList {
506
+ v1 .ResourceName (countResourceName ): resource .MustParse ("0" ),
507
+ },
508
+ },
509
+ })
510
+ err = updateResourceQuotaUntilUsageAppears (f .ClientSet , f .Namespace .Name , quotaName , v1 .ResourceName (countResourceName ))
511
+ Expect (err ).NotTo (HaveOccurred ())
512
+ err = f .ClientSet .CoreV1 ().ResourceQuotas (f .Namespace .Name ).Delete (quotaName , nil )
513
+ Expect (err ).NotTo (HaveOccurred ())
514
+
515
+ By ("Counting existing ResourceQuota" )
516
+ c , err := countResourceQuota (f .ClientSet , f .Namespace .Name )
517
+ Expect (err ).NotTo (HaveOccurred ())
518
+
519
+ By ("Creating a ResourceQuota" )
520
+ quotaName = "test-quota"
521
+ resourceQuota = newTestResourceQuota (quotaName )
522
+ resourceQuota .Spec .Hard [v1 .ResourceName (countResourceName )] = resource .MustParse ("1" )
523
+ resourceQuota , err = createResourceQuota (f .ClientSet , f .Namespace .Name , resourceQuota )
524
+ Expect (err ).NotTo (HaveOccurred ())
525
+
526
+ By ("Ensuring resource quota status is calculated" )
527
+ usedResources := v1.ResourceList {}
528
+ usedResources [v1 .ResourceQuotas ] = resource .MustParse (strconv .Itoa (c + 1 ))
529
+ usedResources [v1 .ResourceName (countResourceName )] = resource .MustParse ("0" )
530
+ err = waitForResourceQuota (f .ClientSet , f .Namespace .Name , quotaName , usedResources )
531
+ Expect (err ).NotTo (HaveOccurred ())
532
+
533
+ By ("Creating a custom resource" )
534
+ resourceClient := testcrd .GetV1DynamicClient ()
535
+ testcr , err := instantiateCustomResource (& unstructured.Unstructured {
536
+ Object : map [string ]interface {}{
537
+ "apiVersion" : testcrd .APIGroup + "/" + testcrd .GetAPIVersions ()[0 ],
538
+ "kind" : testcrd .Crd .Spec .Names .Kind ,
539
+ "metadata" : map [string ]interface {}{
540
+ "name" : "test-cr-1" ,
541
+ },
542
+ },
543
+ }, resourceClient , testcrd .Crd )
544
+ Expect (err ).NotTo (HaveOccurred ())
545
+
546
+ By ("Ensuring resource quota status captures custom resource creation" )
547
+ usedResources = v1.ResourceList {}
548
+ usedResources [v1 .ResourceName (countResourceName )] = resource .MustParse ("1" )
549
+ err = waitForResourceQuota (f .ClientSet , f .Namespace .Name , quotaName , usedResources )
550
+ Expect (err ).NotTo (HaveOccurred ())
551
+
552
+ By ("Creating a second custom resource" )
553
+ _ , err = instantiateCustomResource (& unstructured.Unstructured {
554
+ Object : map [string ]interface {}{
555
+ "apiVersion" : testcrd .APIGroup + "/" + testcrd .GetAPIVersions ()[0 ],
556
+ "kind" : testcrd .Crd .Spec .Names .Kind ,
557
+ "metadata" : map [string ]interface {}{
558
+ "name" : "test-cr-2" ,
559
+ },
560
+ },
561
+ }, resourceClient , testcrd .Crd )
562
+ // since we only give one quota, this creation should fail.
563
+ Expect (err ).To (HaveOccurred ())
564
+
565
+ By ("Deleting a custom resource" )
566
+ err = deleteCustomResource (resourceClient , testcr .GetName ())
567
+ Expect (err ).NotTo (HaveOccurred ())
568
+
569
+ By ("Ensuring resource quota status released usage" )
570
+ usedResources [v1 .ResourceName (countResourceName )] = resource .MustParse ("0" )
571
+ err = waitForResourceQuota (f .ClientSet , f .Namespace .Name , quotaName , usedResources )
572
+ Expect (err ).NotTo (HaveOccurred ())
573
+ })
574
+
490
575
It ("should verify ResourceQuota with terminating scopes." , func () {
491
576
By ("Creating a ResourceQuota with terminating scope" )
492
577
quotaTerminatingName := "quota-terminating"
@@ -1524,3 +1609,29 @@ func waitForResourceQuota(c clientset.Interface, ns, quotaName string, used v1.R
1524
1609
return true , nil
1525
1610
})
1526
1611
}
1612
+
1613
+ // updateResourceQuotaUntilUsageAppears updates the resource quota object until the usage is populated
1614
+ // for the specific resource name.
1615
+ func updateResourceQuotaUntilUsageAppears (c clientset.Interface , ns , quotaName string , resourceName v1.ResourceName ) error {
1616
+ return wait .Poll (framework .Poll , 1 * time .Minute , func () (bool , error ) {
1617
+ resourceQuota , err := c .CoreV1 ().ResourceQuotas (ns ).Get (quotaName , metav1.GetOptions {})
1618
+ if err != nil {
1619
+ return false , err
1620
+ }
1621
+ // verify that the quota shows the expected used resource values
1622
+ _ , ok := resourceQuota .Status .Used [resourceName ]
1623
+ if ok {
1624
+ return true , nil
1625
+ }
1626
+
1627
+ current := resourceQuota .Spec .Hard [resourceName ]
1628
+ current .Add (resource .MustParse ("1" ))
1629
+ resourceQuota .Spec .Hard [resourceName ] = current
1630
+ _ , err = c .CoreV1 ().ResourceQuotas (ns ).Update (resourceQuota )
1631
+ // ignoring conflicts since someone else may already updated it.
1632
+ if errors .IsConflict (err ) {
1633
+ return false , nil
1634
+ }
1635
+ return false , err
1636
+ })
1637
+ }
0 commit comments