@@ -17,6 +17,8 @@ limitations under the License.
17
17
package customresource_test
18
18
19
19
import (
20
+ "context"
21
+ "fmt"
20
22
"reflect"
21
23
"strings"
22
24
"testing"
@@ -526,6 +528,184 @@ func TestScaleUpdateWithoutSpecReplicas(t *testing.T) {
526
528
}
527
529
}
528
530
531
+ func TestScaleUpdateWithoutResourceVersion (t * testing.T ) {
532
+ storage , server := newStorage (t )
533
+ defer server .Terminate (t )
534
+ defer storage .CustomResource .Store .DestroyFunc ()
535
+
536
+ name := "foo"
537
+
538
+ var cr unstructured.Unstructured
539
+ ctx := genericapirequest .WithNamespace (genericapirequest .NewContext (), metav1 .NamespaceDefault )
540
+ key := "/noxus/" + metav1 .NamespaceDefault + "/" + name
541
+ if err := storage .CustomResource .Storage .Create (ctx , key , & validCustomResource , & cr , 0 , false ); err != nil {
542
+ t .Fatalf ("error setting new custom resource (key: %s) %v: %v" , key , validCustomResource , err )
543
+ }
544
+
545
+ replicas := int32 (8 )
546
+ update := autoscalingv1.Scale {
547
+ ObjectMeta : metav1.ObjectMeta {
548
+ Name : name ,
549
+ },
550
+ Spec : autoscalingv1.ScaleSpec {
551
+ Replicas : replicas ,
552
+ },
553
+ }
554
+
555
+ if _ , _ , err := storage .Scale .Update (ctx , update .Name , rest .DefaultUpdatedObjectInfo (& update ), rest .ValidateAllObjectFunc , rest .ValidateAllObjectUpdateFunc , false , & metav1.UpdateOptions {}); err != nil {
556
+ t .Fatalf ("error updating scale %v: %v" , update , err )
557
+ }
558
+
559
+ obj , err := storage .Scale .Get (ctx , name , & metav1.GetOptions {})
560
+ if err != nil {
561
+ t .Fatalf ("error fetching scale for %s: %v" , name , err )
562
+ }
563
+ scale := obj .(* autoscalingv1.Scale )
564
+ if scale .Spec .Replicas != replicas {
565
+ t .Errorf ("wrong replicas count: expected: %d got: %d" , replicas , scale .Spec .Replicas )
566
+ }
567
+ }
568
+
569
+ func TestScaleUpdateWithoutResourceVersionWithConflicts (t * testing.T ) {
570
+ storage , server := newStorage (t )
571
+ defer server .Terminate (t )
572
+ defer storage .CustomResource .Store .DestroyFunc ()
573
+
574
+ name := "foo"
575
+
576
+ var cr unstructured.Unstructured
577
+ ctx := genericapirequest .WithNamespace (genericapirequest .NewContext (), metav1 .NamespaceDefault )
578
+ key := "/noxus/" + metav1 .NamespaceDefault + "/" + name
579
+ if err := storage .CustomResource .Storage .Create (ctx , key , & validCustomResource , & cr , 0 , false ); err != nil {
580
+ t .Fatalf ("error setting new custom resource (key: %s) %v: %v" , key , validCustomResource , err )
581
+ }
582
+
583
+ fetchObject := func (name string ) (* unstructured.Unstructured , error ) {
584
+ gotObj , err := storage .CustomResource .Get (ctx , name , & metav1.GetOptions {})
585
+ if err != nil {
586
+ return nil , fmt .Errorf ("error fetching custom resource %s: %v" , name , err )
587
+ }
588
+ return gotObj .(* unstructured.Unstructured ), nil
589
+ }
590
+
591
+ applyPatch := func (labelName , labelValue string ) rest.TransformFunc {
592
+ return func (_ context.Context , _ , currentObject runtime.Object ) (objToUpdate runtime.Object , patchErr error ) {
593
+ o := currentObject .(metav1.Object )
594
+ o .SetLabels (map [string ]string {
595
+ labelName : labelValue ,
596
+ })
597
+ return currentObject , nil
598
+ }
599
+ }
600
+
601
+ errs := make (chan error , 1 )
602
+ rounds := 100
603
+ go func () {
604
+ // continuously submits a patch that updates a label and verifies the label update was effective
605
+ labelName := "timestamp"
606
+ for i := 0 ; i < rounds ; i ++ {
607
+ expectedLabelValue := fmt .Sprint (i )
608
+ update , err := fetchObject (name )
609
+ if err != nil {
610
+ errs <- err
611
+ return
612
+ }
613
+ setNestedField (update , expectedLabelValue , "metadata" , "labels" , labelName )
614
+ if _ , _ , err := storage .CustomResource .Update (ctx , name , rest .DefaultUpdatedObjectInfo (nil , applyPatch (labelName , fmt .Sprint (i ))), rest .ValidateAllObjectFunc , rest .ValidateAllObjectUpdateFunc , false , & metav1.UpdateOptions {}); err != nil {
615
+
616
+ errs <- fmt .Errorf ("error updating custom resource label: %v" , err )
617
+ return
618
+ }
619
+
620
+ gotObj , err := fetchObject (name )
621
+ if err != nil {
622
+ errs <- err
623
+ return
624
+ }
625
+ gotLabelValue , _ , err := unstructured .NestedString (gotObj .Object , "metadata" , "labels" , labelName )
626
+ if err != nil {
627
+ errs <- fmt .Errorf ("error getting label %s of custom resource %s: %v" , labelName , name , err )
628
+ return
629
+ }
630
+ if gotLabelValue != expectedLabelValue {
631
+ errs <- fmt .Errorf ("wrong label value: expected: %s, got: %s" , expectedLabelValue , gotLabelValue )
632
+ return
633
+ }
634
+ }
635
+ }()
636
+
637
+ replicas := int32 (0 )
638
+ update := autoscalingv1.Scale {
639
+ ObjectMeta : metav1.ObjectMeta {
640
+ Name : name ,
641
+ },
642
+ }
643
+ // continuously submits a scale update without a resourceVersion for a monotonically increasing replica value
644
+ // and verifies the scale update was effective
645
+ for i := 0 ; i < rounds ; i ++ {
646
+ select {
647
+ case err := <- errs :
648
+ t .Fatal (err )
649
+ default :
650
+ replicas ++
651
+ update .Spec .Replicas = replicas
652
+ if _ , _ , err := storage .Scale .Update (ctx , update .Name , rest .DefaultUpdatedObjectInfo (& update ), rest .ValidateAllObjectFunc , rest .ValidateAllObjectUpdateFunc , false , & metav1.UpdateOptions {}); err != nil {
653
+ t .Fatalf ("error updating scale %v: %v" , update , err )
654
+ }
655
+
656
+ obj , err := storage .Scale .Get (ctx , name , & metav1.GetOptions {})
657
+ if err != nil {
658
+ t .Fatalf ("error fetching scale for %s: %v" , name , err )
659
+ }
660
+ scale := obj .(* autoscalingv1.Scale )
661
+ if scale .Spec .Replicas != replicas {
662
+ t .Errorf ("wrong replicas count: expected: %d got: %d" , replicas , scale .Spec .Replicas )
663
+ }
664
+ }
665
+ }
666
+ }
667
+
668
+ func TestScaleUpdateWithResourceVersionWithConflicts (t * testing.T ) {
669
+ storage , server := newStorage (t )
670
+ defer server .Terminate (t )
671
+ defer storage .CustomResource .Store .DestroyFunc ()
672
+
673
+ name := "foo"
674
+
675
+ var cr unstructured.Unstructured
676
+ ctx := genericapirequest .WithNamespace (genericapirequest .NewContext (), metav1 .NamespaceDefault )
677
+ key := "/noxus/" + metav1 .NamespaceDefault + "/" + name
678
+ if err := storage .CustomResource .Storage .Create (ctx , key , & validCustomResource , & cr , 0 , false ); err != nil {
679
+ t .Fatalf ("error setting new custom resource (key: %s) %v: %v" , key , validCustomResource , err )
680
+ }
681
+
682
+ obj , err := storage .Scale .Get (ctx , name , & metav1.GetOptions {})
683
+ if err != nil {
684
+ t .Fatalf ("error fetching scale for %s: %v" , name , err )
685
+ }
686
+ scale , ok := obj .(* autoscalingv1.Scale )
687
+ if ! ok {
688
+ t .Fatalf ("%v is not of the type autoscalingv1.Scale" , scale )
689
+ }
690
+
691
+ replicas := int32 (12 )
692
+ update := autoscalingv1.Scale {
693
+ ObjectMeta : scale .ObjectMeta ,
694
+ Spec : autoscalingv1.ScaleSpec {
695
+ Replicas : replicas ,
696
+ },
697
+ }
698
+ update .ResourceVersion = "1"
699
+
700
+ _ , _ , err = storage .Scale .Update (ctx , update .Name , rest .DefaultUpdatedObjectInfo (& update ), rest .ValidateAllObjectFunc , rest .ValidateAllObjectUpdateFunc , false , & metav1.UpdateOptions {})
701
+ if err == nil {
702
+ t .Fatal ("expecting an update conflict error" )
703
+ }
704
+ if ! errors .IsConflict (err ) {
705
+ t .Fatalf ("unexpected error, expecting an update conflict but got %v" , err )
706
+ }
707
+ }
708
+
529
709
func setSpecReplicas (u * unstructured.Unstructured , replicas int64 ) {
530
710
setNestedField (u , replicas , "spec" , "replicas" )
531
711
}
0 commit comments