diff --git a/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD b/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD index 59a4b1bccc1..78bfb9a6d16 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD +++ b/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD @@ -9,7 +9,10 @@ load( go_test( name = "go_default_test", - srcs = ["basic_test.go"], + srcs = [ + "basic_test.go", + "registration_test.go", + ], tags = [ "automanaged", "integration", @@ -19,6 +22,8 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1:go_default_library", "//vendor/k8s.io/kube-apiextensions-server/test/integration/testserver:go_default_library", ], ) diff --git a/staging/src/k8s.io/kube-apiextensions-server/test/integration/basic_test.go b/staging/src/k8s.io/kube-apiextensions-server/test/integration/basic_test.go index eb7915af095..6b19c459e0d 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/test/integration/basic_test.go +++ b/staging/src/k8s.io/kube-apiextensions-server/test/integration/basic_test.go @@ -50,10 +50,7 @@ func TestSimpleCRUD(t *testing.T) { } ns := "not-the-default" - noxuNamespacedResourceClient := noxuVersionClient.Resource(&metav1.APIResource{ - Name: noxuDefinition.Spec.Names.Plural, - Namespaced: true, - }, ns) + noxuNamespacedResourceClient := NewNamespacedCustomResourceClient(ns, noxuVersionClient, noxuDefinition) initialList, err := noxuNamespacedResourceClient.List(metav1.ListOptions{}) if err != nil { t.Fatal(err) @@ -82,29 +79,9 @@ func TestSimpleCRUD(t *testing.T) { } defer noxuNamespacedWatch.Stop() - noxuInstanceToCreate := testserver.NewNoxuInstance(ns, "foo") - createdNoxuInstance, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate) + createdNoxuInstance, err := instantiateCustomResource(t, testserver.NewNoxuInstance(ns, "foo"), noxuNamespacedResourceClient, noxuDefinition) if err != nil { - t.Logf("%#v", createdNoxuInstance) - t.Fatal(err) - } - createdObjectMeta, err := meta.Accessor(createdNoxuInstance) - if err != nil { - t.Fatal(err) - } - // it should have a UUID - if len(createdObjectMeta.GetUID()) == 0 { - t.Errorf("missing uuid: %#v", createdNoxuInstance) - } - createdTypeMeta, err := meta.TypeAccessor(createdNoxuInstance) - if err != nil { - t.Fatal(err) - } - if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Version, createdTypeMeta.GetAPIVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := noxuDefinition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a { - t.Errorf("expected %v, got %v", e, a) + t.Fatalf("unable to create noxu Instance:%v", err) } select { @@ -178,6 +155,10 @@ func TestSimpleCRUD(t *testing.T) { t.Fatal(err) } // it should have a UUID + createdObjectMeta, err := meta.Accessor(createdNoxuInstance) + if err != nil { + t.Fatal(err) + } if e, a := createdObjectMeta.GetUID(), deletedObjectMeta.GetUID(); e != a { t.Errorf("expected %v, got %v", e, a) } diff --git a/staging/src/k8s.io/kube-apiextensions-server/test/integration/registration_test.go b/staging/src/k8s.io/kube-apiextensions-server/test/integration/registration_test.go new file mode 100644 index 00000000000..2c56ee59335 --- /dev/null +++ b/staging/src/k8s.io/kube-apiextensions-server/test/integration/registration_test.go @@ -0,0 +1,246 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "reflect" + "testing" + "time" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + apiextensionsv1alpha1 "k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1" + "k8s.io/kube-apiextensions-server/test/integration/testserver" +) + +func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client *dynamic.ResourceClient, definition *apiextensionsv1alpha1.CustomResource) (*unstructured.Unstructured, error) { + createdInstance, err := client.Create(instanceToCreate) + if err != nil { + t.Logf("%#v", createdInstance) + return nil, err + } + createdObjectMeta, err := meta.Accessor(createdInstance) + if err != nil { + t.Fatal(err) + } + // it should have a UUID + if len(createdObjectMeta.GetUID()) == 0 { + t.Errorf("missing uuid: %#v", createdInstance) + } + createdTypeMeta, err := meta.TypeAccessor(createdInstance) + if err != nil { + t.Fatal(err) + } + if e, a := definition.Spec.Group+"/"+definition.Spec.Version, createdTypeMeta.GetAPIVersion(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := definition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + return createdInstance, nil +} + +func NewNamespacedCustomResourceClient(ns string, client *dynamic.Client, definition *apiextensionsv1alpha1.CustomResource) *dynamic.ResourceClient { + return client.Resource(&metav1.APIResource{ + Name: definition.Spec.Names.Plural, + Namespaced: definition.Spec.Scope == apiextensionsv1alpha1.NamespaceScoped, + }, ns) +} + +func TestMultipleResourceInstances(t *testing.T) { + stopCh, apiExtensionClient, clientPool, err := testserver.StartDefaultServer() + if err != nil { + t.Fatal(err) + } + defer close(stopCh) + + ns := "not-the-default" + noxuDefinition := testserver.NewNoxuCustomResourceDefinition() + noxuVersionClient, err := testserver.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, clientPool) + if err != nil { + t.Fatal(err) + } + noxuNamespacedResourceClient := NewNamespacedCustomResourceClient(ns, noxuVersionClient, noxuDefinition) + noxuList, err := noxuNamespacedResourceClient.List(metav1.ListOptions{}) + if err != nil { + t.Fatal(err) + } + + noxuListListMeta, err := meta.ListAccessor(noxuList) + if err != nil { + t.Fatal(err) + } + + noxuNamespacedWatch, err := noxuNamespacedResourceClient.Watch(metav1.ListOptions{ResourceVersion: noxuListListMeta.GetResourceVersion()}) + if err != nil { + t.Fatal(err) + } + defer noxuNamespacedWatch.Stop() + + instances := map[string]*struct { + Added bool + Deleted bool + Instance *unstructured.Unstructured + }{ + "foo": {}, + "bar": {}, + } + + for key, val := range instances { + val.Instance, err = instantiateCustomResource(t, testserver.NewNoxuInstance(ns, key), noxuNamespacedResourceClient, noxuDefinition) + if err != nil { + t.Fatalf("unable to create Noxu Instance %q:%v", key, err) + } + } + + addEvents := 0 + for addEvents < len(instances) { + select { + case watchEvent := <-noxuNamespacedWatch.ResultChan(): + if e, a := watch.Added, watchEvent.Type; e != a { + t.Fatalf("expected %v, got %v", e, a) + } + name, err := meta.NewAccessor().Name(watchEvent.Object) + if err != nil { + t.Fatalf("unable to retrieve object name:%v", err) + } + if instances[name].Added { + t.Fatalf("Add event already registered for %q", name) + } + instances[name].Added = true + addEvents++ + case <-time.After(5 * time.Second): + t.Fatalf("missing watch event") + } + } + + for key, val := range instances { + gottenNoxuInstace, err := noxuNamespacedResourceClient.Get(key) + if err != nil { + t.Fatal(err) + } + if e, a := val.Instance, gottenNoxuInstace; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } + } + listWithItem, err := noxuNamespacedResourceClient.List(metav1.ListOptions{}) + if err != nil { + t.Fatal(err) + } + if e, a := len(instances), len(listWithItem.(*unstructured.UnstructuredList).Items); e != a { + t.Errorf("expected %v, got %v", e, a) + } + for _, a := range listWithItem.(*unstructured.UnstructuredList).Items { + if e := instances[a.GetName()].Instance; !reflect.DeepEqual(e, &a) { + t.Errorf("expected %v, got %v", e, a) + } + } + for key := range instances { + if err := noxuNamespacedResourceClient.Delete(key, nil); err != nil { + t.Fatalf("unable to delete %s:%v", key, err) + } + } + listWithoutItem, err := noxuNamespacedResourceClient.List(metav1.ListOptions{}) + if err != nil { + t.Fatal(err) + } + if e, a := 0, len(listWithoutItem.(*unstructured.UnstructuredList).Items); e != a { + t.Errorf("expected %v, got %v", e, a) + } + + deleteEvents := 0 + for deleteEvents < len(instances) { + select { + case watchEvent := <-noxuNamespacedWatch.ResultChan(): + if e, a := watch.Deleted, watchEvent.Type; e != a { + t.Errorf("expected %v, got %v", e, a) + break + } + name, err := meta.NewAccessor().Name(watchEvent.Object) + if err != nil { + t.Errorf("unable to retrieve object name:%v", err) + } + if instances[name].Deleted { + t.Errorf("Delete event already registered for %q", name) + } + instances[name].Deleted = true + deleteEvents++ + case <-time.After(5 * time.Second): + t.Errorf("missing watch event") + } + } +} + +func TestMultipleRegistration(t *testing.T) { + stopCh, apiExtensionClient, clientPool, err := testserver.StartDefaultServer() + if err != nil { + t.Fatal(err) + } + defer close(stopCh) + + ns := "not-the-default" + sameInstanceName := "foo" + noxuDefinition := testserver.NewNoxuCustomResourceDefinition() + noxuVersionClient, err := testserver.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, clientPool) + if err != nil { + t.Fatal(err) + } + noxuNamespacedResourceClient := NewNamespacedCustomResourceClient(ns, noxuVersionClient, noxuDefinition) + createdNoxuInstance, err := instantiateCustomResource(t, testserver.NewNoxuInstance(ns, sameInstanceName), noxuNamespacedResourceClient, noxuDefinition) + if err != nil { + t.Fatalf("unable to create noxu Instance:%v", err) + } + + gottenNoxuInstance, err := noxuNamespacedResourceClient.Get(sameInstanceName) + if err != nil { + t.Fatal(err) + } + if e, a := createdNoxuInstance, gottenNoxuInstance; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } + + curletDefinition := testserver.NewCurletCustomResourceDefinition() + curletVersionClient, err := testserver.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, clientPool) + if err != nil { + t.Fatal(err) + } + curletNamespacedResourceClient := NewNamespacedCustomResourceClient(ns, curletVersionClient, curletDefinition) + createdCurletInstance, err := instantiateCustomResource(t, testserver.NewCurletInstance(ns, sameInstanceName), curletNamespacedResourceClient, curletDefinition) + if err != nil { + t.Fatalf("unable to create noxu Instance:%v", err) + } + gottenCurletInstance, err := curletNamespacedResourceClient.Get(sameInstanceName) + if err != nil { + t.Fatal(err) + } + if e, a := createdCurletInstance, gottenCurletInstance; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } + + // now re-GET noxu + gottenNoxuInstance2, err := noxuNamespacedResourceClient.Get(sameInstanceName) + if err != nil { + t.Fatal(err) + } + if e, a := createdNoxuInstance, gottenNoxuInstance2; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } + +} diff --git a/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go b/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go index 28082b77fbf..9a7e0e800ec 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go +++ b/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go @@ -61,6 +61,39 @@ func NewNoxuInstance(namespace, name string) *unstructured.Unstructured { } } +func NewCurletCustomResourceDefinition() *apiextensionsv1alpha1.CustomResource { + return &apiextensionsv1alpha1.CustomResource{ + ObjectMeta: metav1.ObjectMeta{Name: "curlets.mygroup.example.com"}, + Spec: apiextensionsv1alpha1.CustomResourceSpec{ + Group: "mygroup.example.com", + Version: "v1alpha1", + Names: apiextensionsv1alpha1.CustomResourceNames{ + Plural: "curlets", + Singular: "curlet", + Kind: "Curlet", + ListKind: "CurletList", + }, + Scope: apiextensionsv1alpha1.NamespaceScoped, + }, + } +} + +func NewCurletInstance(namespace, name string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "mygroup.example.com/v1alpha1", + "kind": "Curlet", + "metadata": map[string]interface{}{ + "namespace": namespace, + "name": name, + }, + "content": map[string]interface{}{ + "key": "value", + }, + }, + } +} + func CreateNewCustomResourceDefinition(customResource *apiextensionsv1alpha1.CustomResource, apiExtensionsClient clientset.Interface, clientPool dynamic.ClientPool) (*dynamic.Client, error) { _, err := apiExtensionsClient.Apiextensions().CustomResources().Create(customResource) if err != nil {