diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD index 73ac1009ee..1650919915 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD @@ -68,6 +68,7 @@ go_test( "//vendor/k8s.io/api/autoscaling/v1:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/apiserver:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/features:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresource:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", @@ -82,6 +83,8 @@ go_test( "//vendor/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library", + "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", + "//vendor/k8s.io/apiserver/pkg/util/feature/testing:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go index 8bee3396c9..79fc7e83d0 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go @@ -35,10 +35,13 @@ import ( registrytest "k8s.io/apiserver/pkg/registry/generic/testing" "k8s.io/apiserver/pkg/registry/rest" etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing" + utilfeature "k8s.io/apiserver/pkg/util/feature" + utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" "k8s.io/client-go/discovery" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apiserver" + "k8s.io/apiextensions-apiserver/pkg/features" "k8s.io/apiextensions-apiserver/pkg/registry/customresource" "k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor" ) @@ -160,6 +163,61 @@ func TestDelete(t *testing.T) { test.TestDelete(validNewCustomResource()) } +func TestGenerationNumber(t *testing.T) { + // enable alpha feature CustomResourceSubresources + defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CustomResourceSubresources, true)() + + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.CustomResource.Store.DestroyFunc() + modifiedRno := *validNewCustomResource() + modifiedRno.SetGeneration(10) + ctx := genericapirequest.NewDefaultContext() + cr, err := createCustomResource(storage.CustomResource, modifiedRno, t) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + etcdCR, err := storage.CustomResource.Get(ctx, cr.GetName(), &metav1.GetOptions{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + storedCR, _ := etcdCR.(*unstructured.Unstructured) + + // Generation initialization + if storedCR.GetGeneration() != 1 { + t.Fatalf("Unexpected generation number %v", storedCR.GetGeneration()) + } + + // Updates to spec should increment the generation number + setSpecReplicas(storedCR, getSpecReplicas(storedCR)+1) + if _, _, err := storage.CustomResource.Update(ctx, storedCR.GetName(), rest.DefaultUpdatedObjectInfo(storedCR), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { + t.Errorf("unexpected error: %v", err) + } + etcdCR, err = storage.CustomResource.Get(ctx, cr.GetName(), &metav1.GetOptions{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + storedCR, _ = etcdCR.(*unstructured.Unstructured) + if storedCR.GetGeneration() != 2 { + t.Fatalf("Unexpected generation, spec: %v", storedCR.GetGeneration()) + } + + // Updates to status should not increment the generation number + setStatusReplicas(storedCR, getStatusReplicas(storedCR)+1) + if _, _, err := storage.CustomResource.Update(ctx, storedCR.GetName(), rest.DefaultUpdatedObjectInfo(storedCR), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc); err != nil { + t.Errorf("unexpected error: %v", err) + } + etcdCR, err = storage.CustomResource.Get(ctx, cr.GetName(), &metav1.GetOptions{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + storedCR, _ = etcdCR.(*unstructured.Unstructured) + if storedCR.GetGeneration() != 2 { + t.Fatalf("Unexpected generation, spec: %v", storedCR.GetGeneration()) + } + +} + func TestCategories(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) @@ -398,3 +456,34 @@ func (c unstructuredJsonCodec) Encode(obj runtime.Object, w io.Writer) error { w.Write(bs) return nil } + +func setSpecReplicas(u *unstructured.Unstructured, replicas int64) { + setNestedField(u, replicas, "spec", "replicas") +} + +func getSpecReplicas(u *unstructured.Unstructured) int64 { + val, found, err := unstructured.NestedInt64(u.Object, "spec", "replicas") + if !found || err != nil { + return 0 + } + return val +} + +func setStatusReplicas(u *unstructured.Unstructured, replicas int64) { + setNestedField(u, replicas, "status", "replicas") +} + +func getStatusReplicas(u *unstructured.Unstructured) int64 { + val, found, err := unstructured.NestedInt64(u.Object, "status", "replicas") + if !found || err != nil { + return 0 + } + return val +} + +func setNestedField(u *unstructured.Unstructured, value interface{}, fields ...string) { + if u.Object == nil { + u.Object = make(map[string]interface{}) + } + unstructured.SetNestedField(u.Object, value, fields...) +}