blob: b9f34b3cb4d5cecc6d9f0a87e314f4e444f3848e [file] [log] [blame]
/*
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 (
"fmt"
"reflect"
"sort"
"strings"
"testing"
"time"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors"
"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/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
)
func TestServerUp(t *testing.T) {
tearDown, _, _, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
}
func TestNamespaceScopedCRUD(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns := "not-the-default"
testSimpleCRUD(t, ns, noxuDefinition, dynamicClient)
testFieldSelector(t, ns, noxuDefinition, dynamicClient)
}
func TestClusterScopedCRUD(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns := ""
testSimpleCRUD(t, ns, noxuDefinition, dynamicClient)
testFieldSelector(t, ns, noxuDefinition, dynamicClient)
}
func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) {
noxuResourceClients := map[string]dynamic.ResourceInterface{}
noxuWatchs := map[string]watch.Interface{}
disabledVersions := map[string]bool{}
for _, v := range noxuDefinition.Spec.Versions {
disabledVersions[v.Name] = !v.Served
}
for _, v := range noxuDefinition.Spec.Versions {
noxuResourceClients[v.Name] = newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
noxuWatch, err := noxuResourceClients[v.Name].Watch(metav1.ListOptions{})
if disabledVersions[v.Name] {
if !errors.IsNotFound(err) {
t.Errorf("expected the watch operation fail with NotFound for disabled version %s, got error: %v", v.Name, err)
}
} else {
if err != nil {
t.Fatal(err)
}
noxuWatchs[v.Name] = noxuWatch
}
}
defer func() {
for _, w := range noxuWatchs {
w.Stop()
}
}()
for version, noxuResourceClient := range noxuResourceClients {
createdNoxuInstance, err := instantiateVersionedCustomResource(t, fixtures.NewVersionedNoxuInstance(ns, "foo", version), noxuResourceClient, noxuDefinition, version)
if disabledVersions[version] {
if !errors.IsNotFound(err) {
t.Errorf("expected the CR creation fail with NotFound for disabled version %s, got error: %v", version, err)
}
continue
}
if err != nil {
t.Fatalf("unable to create noxu Instance:%v", err)
}
if e, a := noxuDefinition.Spec.Group+"/"+version, createdNoxuInstance.GetAPIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
for watchVersion, noxuWatch := range noxuWatchs {
select {
case watchEvent := <-noxuWatch.ResultChan():
if e, a := watch.Added, watchEvent.Type; e != a {
t.Errorf("expected %v, got %v", e, a)
break
}
createdObjectMeta, err := meta.Accessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
// it should have a UUID
if len(createdObjectMeta.GetUID()) == 0 {
t.Errorf("missing uuid: %#v", watchEvent.Object)
}
if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
createdTypeMeta, err := meta.TypeAccessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
if e, a := noxuDefinition.Spec.Group+"/"+watchVersion, 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)
}
case <-time.After(5 * time.Second):
t.Errorf("missing watch event")
}
}
// Check get for all versions
for version2, noxuResourceClient2 := range noxuResourceClients {
// Get test
gottenNoxuInstance, err := noxuResourceClient2.Get("foo", metav1.GetOptions{})
if disabledVersions[version2] {
if !errors.IsNotFound(err) {
t.Errorf("expected the get operation fail with NotFound for disabled version %s, got error: %v", version2, err)
}
} else {
if err != nil {
t.Fatal(err)
}
if e, a := version2, gottenNoxuInstance.GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
}
// List test
listWithItem, err := noxuResourceClient2.List(metav1.ListOptions{})
if disabledVersions[version2] {
if !errors.IsNotFound(err) {
t.Errorf("expected the list operation fail with NotFound for disabled version %s, got error: %v", version2, err)
}
} else {
if err != nil {
t.Fatal(err)
}
if e, a := 1, len(listWithItem.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := version2, listWithItem.GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := version2, listWithItem.Items[0].GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
}
}
// Update test
for version2, noxuResourceClient2 := range noxuResourceClients {
var gottenNoxuInstance *unstructured.Unstructured
if disabledVersions[version2] {
gottenNoxuInstance = &unstructured.Unstructured{}
gottenNoxuInstance.SetName("foo")
} else {
gottenNoxuInstance, err = noxuResourceClient2.Get("foo", metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
}
gottenNoxuInstance.Object["updated"] = version2
updatedNoxuInstance, err := noxuResourceClient2.Update(gottenNoxuInstance, metav1.UpdateOptions{})
if disabledVersions[version2] {
if !errors.IsNotFound(err) {
t.Errorf("expected the update operation fail with NotFound for disabled version %s, got error: %v", version2, err)
}
} else {
if err != nil {
t.Fatal(err)
}
if updated, ok := updatedNoxuInstance.Object["updated"]; !ok {
t.Errorf("expected string 'updated' field")
} else if updated, ok := updated.(string); !ok || updated != version2 {
t.Errorf("expected string 'updated' field to equal %q, got %q of type %T", version2, updated, updated)
}
if e, a := version2, updatedNoxuInstance.GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
for _, noxuWatch := range noxuWatchs {
select {
case watchEvent := <-noxuWatch.ResultChan():
eventMetadata, err := meta.Accessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
if watchEvent.Type != watch.Modified {
t.Errorf("expected modified event, got %v", watchEvent.Type)
break
}
// it should have a UUID
createdMetadata, err := meta.Accessor(createdNoxuInstance)
if err != nil {
t.Fatal(err)
}
if e, a := createdMetadata.GetUID(), eventMetadata.GetUID(); e != a {
t.Errorf("expected equal UID for (expected) %v, and (actual) %v", createdNoxuInstance, watchEvent.Object)
}
case <-time.After(5 * time.Second):
t.Errorf("missing watch event")
}
}
}
}
// Delete test
if err := noxuResourceClient.Delete("foo", metav1.NewDeleteOptions(0)); err != nil {
t.Fatal(err)
}
listWithoutItem, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := 0, len(listWithoutItem.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
for _, noxuWatch := range noxuWatchs {
select {
case watchEvent := <-noxuWatch.ResultChan():
eventMetadata, err := meta.Accessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
if watchEvent.Type != watch.Deleted {
t.Errorf("expected delete event, got %v", watchEvent.Type)
break
}
// it should have a UUID
createdMetadata, err := meta.Accessor(createdNoxuInstance)
if err != nil {
t.Fatal(err)
}
if e, a := createdMetadata.GetUID(), eventMetadata.GetUID(); e != a {
t.Errorf("expected equal UID for (expected) %v, and (actual) %v", createdNoxuInstance, watchEvent.Object)
}
case <-time.After(5 * time.Second):
t.Errorf("missing watch event")
}
}
// Delete test
if err := noxuResourceClient.DeleteCollection(metav1.NewDeleteOptions(0), metav1.ListOptions{}); err != nil {
t.Fatal(err)
}
}
}
func testFieldSelector(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) {
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
initialList, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := 0, len(initialList.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
initialListTypeMeta, err := meta.TypeAccessor(initialList)
if err != nil {
t.Fatal(err)
}
if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, initialListTypeMeta.GetAPIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := noxuDefinition.Spec.Names.ListKind, initialListTypeMeta.GetKind(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
initialListListMeta, err := meta.ListAccessor(initialList)
if err != nil {
t.Fatal(err)
}
noxuWatch, err := noxuResourceClient.Watch(
metav1.ListOptions{
ResourceVersion: initialListListMeta.GetResourceVersion(),
FieldSelector: "metadata.name=foo",
},
)
if err != nil {
t.Fatal(err)
}
defer noxuWatch.Stop()
_, err = instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, "bar"), noxuResourceClient, noxuDefinition)
if err != nil {
t.Fatalf("unable to create noxu Instance:%v", err)
}
createdNoxuInstanceFoo, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
if err != nil {
t.Fatalf("unable to create noxu Instance:%v", err)
}
select {
case watchEvent := <-noxuWatch.ResultChan():
if e, a := watch.Added, watchEvent.Type; e != a {
t.Errorf("expected %v, got %v", e, a)
break
}
createdObjectMeta, err := meta.Accessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
// it should have a UUID
if len(createdObjectMeta.GetUID()) == 0 {
t.Errorf("missing uuid: %#v", watchEvent.Object)
}
if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "foo", createdObjectMeta.GetName(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
createdTypeMeta, err := meta.TypeAccessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, 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)
}
case <-time.After(5 * time.Second):
t.Errorf("missing watch event")
}
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := createdNoxuInstanceFoo, gottenNoxuInstance; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
listWithItem, err := noxuResourceClient.List(metav1.ListOptions{FieldSelector: "metadata.name=foo"})
if err != nil {
t.Fatal(err)
}
if e, a := 1, len(listWithItem.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := *createdNoxuInstanceFoo, listWithItem.Items[0]; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
if err := noxuResourceClient.Delete("bar", nil); err != nil {
t.Fatal(err)
}
if err := noxuResourceClient.Delete("foo", nil); err != nil {
t.Fatal(err)
}
listWithoutItem, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := 0, len(listWithoutItem.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
select {
case watchEvent := <-noxuWatch.ResultChan():
if e, a := watch.Deleted, watchEvent.Type; e != a {
t.Errorf("expected %v, got %v", e, a)
break
}
deletedObjectMeta, err := meta.Accessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
// it should have a UUID
createdObjectMeta, err := meta.Accessor(createdNoxuInstanceFoo)
if err != nil {
t.Fatal(err)
}
if e, a := createdObjectMeta.GetUID(), deletedObjectMeta.GetUID(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "foo", createdObjectMeta.GetName(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
case <-time.After(5 * time.Second):
t.Errorf("missing watch event")
}
}
func TestDiscovery(t *testing.T) {
group := "mygroup.example.com"
version := "v1beta1"
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
scope := apiextensionsv1beta1.NamespaceScoped
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(scope)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
// check whether it shows up in discovery properly
resources, err := apiExtensionClient.Discovery().ServerResourcesForGroupVersion(group + "/" + version)
if err != nil {
t.Fatal(err)
}
if len(resources.APIResources) != 1 {
t.Fatalf("Expected exactly the resource \"noxus\" in group version %v/%v via discovery, got: %v", group, version, resources.APIResources)
}
r := resources.APIResources[0]
if r.Name != "noxus" {
t.Fatalf("Expected exactly the resource \"noxus\" in group version %v/%v via discovery, got: %v", group, version, r.Name)
}
if r.Kind != "WishIHadChosenNoxu" {
t.Fatalf("Expected exactly the kind \"WishIHadChosenNoxu\" in group version %v/%v via discovery, got: %v", group, version, r.Kind)
}
s := []string{"foo", "bar", "abc", "def"}
if !reflect.DeepEqual(r.ShortNames, s) {
t.Fatalf("Expected exactly the shortnames `foo, bar, abc, def` in group version %v/%v via discovery, got: %v", group, version, r.ShortNames)
}
sort.Strings(r.Verbs)
expectedVerbs := []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"}
if !reflect.DeepEqual([]string(r.Verbs), expectedVerbs) {
t.Fatalf("Unexpected verbs for resource \"noxus\" in group version %v/%v via discovery: expected=%v got=%v", group, version, expectedVerbs, r.Verbs)
}
if !reflect.DeepEqual(r.Categories, []string{"all"}) {
t.Fatalf("Expected exactly the category \"all\" in group version %v/%v via discovery, got: %v", group, version, r.Categories)
}
}
func TestNoNamespaceReject(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns := ""
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
initialList, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := 0, len(initialList.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
initialListTypeMeta, err := meta.TypeAccessor(initialList)
if err != nil {
t.Fatal(err)
}
if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Version, initialListTypeMeta.GetAPIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := noxuDefinition.Spec.Names.ListKind, initialListTypeMeta.GetKind(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
createdNoxuInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
if err == nil {
t.Fatalf("unexpected non-error: an empty namespace may not be set during creation while creating noxu instance: %v ", createdNoxuInstance)
}
}
func TestSameNameDiffNamespace(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns1 := "namespace-1"
testSimpleCRUD(t, ns1, noxuDefinition, dynamicClient)
ns2 := "namespace-2"
testSimpleCRUD(t, ns2, noxuDefinition, dynamicClient)
}
func TestSelfLink(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
// namespace scoped
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns := "not-the-default"
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, "foo")
createdNoxuInstance, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate, metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := "/apis/mygroup.example.com/v1beta1/namespaces/not-the-default/noxus/foo", createdNoxuInstance.GetSelfLink(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
// cluster scoped
curletDefinition := fixtures.NewCurletCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
curletDefinition, err = fixtures.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
curletResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, curletDefinition)
curletInstanceToCreate := fixtures.NewCurletInstance(ns, "foo")
createdCurletInstance, err := curletResourceClient.Create(curletInstanceToCreate, metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := "/apis/mygroup.example.com/v1beta1/curlets/foo", createdCurletInstance.GetSelfLink(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
func TestPreserveInt(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns := "not-the-default"
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, "foo")
createdNoxuInstance, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate, metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
originalJSON, err := runtime.Encode(unstructured.UnstructuredJSONScheme, createdNoxuInstance)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
gottenNoxuInstance, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalJSON)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Check if int is preserved.
unstructuredObj := gottenNoxuInstance.(*unstructured.Unstructured).Object
num := unstructuredObj["num"].(map[string]interface{})
num1 := num["num1"].(int64)
num2 := num["num2"].(int64)
if num1 != 9223372036854775807 || num2 != 1000000 {
t.Errorf("Expected %v, got %v, %v", `9223372036854775807, 1000000`, num1, num2)
}
}
func TestPatch(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns := "not-the-default"
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
t.Logf("Creating foo")
noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, "foo")
_, err = noxuNamespacedResourceClient.Create(noxuInstanceToCreate, metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
t.Logf("Patching .num.num2 to 999")
patch := []byte(`{"num": {"num2":999}}`)
patchedNoxuInstance, err := noxuNamespacedResourceClient.Patch("foo", types.MergePatchType, patch, metav1.UpdateOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "num", "num2")
rv, found, err := unstructured.NestedString(patchedNoxuInstance.UnstructuredContent(), "metadata", "resourceVersion")
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatalf("metadata.resourceVersion not found")
}
// a patch with no change
t.Logf("Patching .num.num2 again to 999")
patchedNoxuInstance, err = noxuNamespacedResourceClient.Patch("foo", types.MergePatchType, patch, metav1.UpdateOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// make sure no-op patch does not increment resourceVersion
expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "num", "num2")
expectString(t, patchedNoxuInstance.UnstructuredContent(), rv, "metadata", "resourceVersion")
// an empty patch
t.Logf("Applying empty patch")
patchedNoxuInstance, err = noxuNamespacedResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`), metav1.UpdateOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// an empty patch is a no-op patch. make sure it does not increment resourceVersion
expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "num", "num2")
expectString(t, patchedNoxuInstance.UnstructuredContent(), rv, "metadata", "resourceVersion")
originalJSON, err := runtime.Encode(unstructured.UnstructuredJSONScheme, patchedNoxuInstance)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
gottenNoxuInstance, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalJSON)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Check if int is preserved.
unstructuredObj := gottenNoxuInstance.(*unstructured.Unstructured).Object
num := unstructuredObj["num"].(map[string]interface{})
num1 := num["num1"].(int64)
num2 := num["num2"].(int64)
if num1 != 9223372036854775807 || num2 != 999 {
t.Errorf("Expected %v, got %v, %v", `9223372036854775807, 999`, num1, num2)
}
}
func TestCrossNamespaceListWatch(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
ns := ""
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
initialList, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := 0, len(initialList.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
initialListListMeta, err := meta.ListAccessor(initialList)
if err != nil {
t.Fatal(err)
}
noxuWatch, err := noxuResourceClient.Watch(metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
if err != nil {
t.Fatal(err)
}
defer noxuWatch.Stop()
instances := make(map[string]*unstructured.Unstructured)
ns1 := "namespace-1"
noxuNamespacedResourceClient1 := newNamespacedCustomResourceClient(ns1, dynamicClient, noxuDefinition)
instances[ns1] = createInstanceWithNamespaceHelper(t, ns1, "foo1", noxuNamespacedResourceClient1, noxuDefinition)
noxuNamespacesWatch1, err := noxuNamespacedResourceClient1.Watch(metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
defer noxuNamespacesWatch1.Stop()
ns2 := "namespace-2"
noxuNamespacedResourceClient2 := newNamespacedCustomResourceClient(ns2, dynamicClient, noxuDefinition)
instances[ns2] = createInstanceWithNamespaceHelper(t, ns2, "foo2", noxuNamespacedResourceClient2, noxuDefinition)
noxuNamespacesWatch2, err := noxuNamespacedResourceClient2.Watch(metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
defer noxuNamespacesWatch2.Stop()
createdList, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
if e, a := 2, len(createdList.Items); e != a {
t.Errorf("expected %v, got %v", e, a)
}
for _, a := range createdList.Items {
if e := instances[a.GetNamespace()]; !reflect.DeepEqual(e, &a) {
t.Errorf("expected %v, got %v", e, a)
}
}
addEvents := 0
for addEvents < 2 {
select {
case watchEvent := <-noxuWatch.ResultChan():
if e, a := watch.Added, watchEvent.Type; e != a {
t.Fatalf("expected %v, got %v", e, a)
}
createdObjectMeta, err := meta.Accessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
if len(createdObjectMeta.GetUID()) == 0 {
t.Errorf("missing uuid: %#v", watchEvent.Object)
}
createdTypeMeta, err := meta.TypeAccessor(watchEvent.Object)
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)
}
delete(instances, createdObjectMeta.GetNamespace())
addEvents++
case <-time.After(5 * time.Second):
t.Fatalf("missing watch event")
}
}
if e, a := 0, len(instances); e != a {
t.Errorf("expected %v, got %v", e, a)
}
checkNamespacesWatchHelper(t, ns1, noxuNamespacesWatch1)
checkNamespacesWatchHelper(t, ns2, noxuNamespacesWatch2)
}
func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient dynamic.ResourceInterface, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) *unstructured.Unstructured {
createdInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, name), noxuNamespacedResourceClient, noxuDefinition)
if err != nil {
t.Fatalf("unable to create noxu Instance:%v", err)
}
return createdInstance
}
func checkNamespacesWatchHelper(t *testing.T, ns string, namespacedwatch watch.Interface) {
namespacedAddEvent := 0
for namespacedAddEvent < 2 {
select {
case watchEvent := <-namespacedwatch.ResultChan():
// Check that the namespaced watch only has one result
if namespacedAddEvent > 0 {
t.Fatalf("extra watch event")
}
if e, a := watch.Added, watchEvent.Type; e != a {
t.Fatalf("expected %v, got %v", e, a)
}
createdObjectMeta, err := meta.Accessor(watchEvent.Object)
if err != nil {
t.Fatal(err)
}
if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
case <-time.After(5 * time.Second):
if namespacedAddEvent != 1 {
t.Fatalf("missing watch event")
}
}
namespacedAddEvent++
}
}
func TestNameConflict(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
noxu2Definition := fixtures.NewNoxu2CustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(noxu2Definition)
if err != nil {
t.Fatal(err)
}
// A NameConflict occurs
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
crd, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxu2Definition.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
for _, condition := range crd.Status.Conditions {
if condition.Type == apiextensionsv1beta1.NamesAccepted && condition.Status == apiextensionsv1beta1.ConditionFalse {
return true, nil
}
}
return false, nil
})
if err != nil {
t.Fatal(err)
}
err = fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient)
if err != nil {
t.Fatal(err)
}
// Names are now accepted
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
crd, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxu2Definition.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
for _, condition := range crd.Status.Conditions {
if condition.Type == apiextensionsv1beta1.NamesAccepted && condition.Status == apiextensionsv1beta1.ConditionTrue {
return true, nil
}
}
return false, nil
})
if err != nil {
t.Fatal(err)
}
}
func TestStatusGetAndPatch(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
if err != nil {
t.Fatal(err)
}
defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
// make sure we don't get 405 Method Not Allowed from Getting CRD/status subresource
result := &apiextensionsv1beta1.CustomResourceDefinition{}
err = apiExtensionClient.ApiextensionsV1beta1().RESTClient().Get().
Resource("customresourcedefinitions").
Name(noxuDefinition.Name).
SubResource("status").
Do().
Into(result)
if err != nil {
t.Fatal(err)
}
// make sure we don't get 405 Method Not Allowed from Patching CRD/status subresource
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().
Patch(noxuDefinition.Name, types.StrategicMergePatchType,
[]byte(fmt.Sprintf(`{"labels":{"test-label":"dummy"}}`)),
"status")
if err != nil {
t.Fatal(err)
}
}
func expectInt64(t *testing.T, obj map[string]interface{}, value int64, pth ...string) {
if v, found, err := unstructured.NestedInt64(obj, pth...); err != nil {
t.Fatalf("failed to access .%s: %v", strings.Join(pth, "."), err)
} else if !found {
t.Fatalf("failed to find .%s", strings.Join(pth, "."))
} else if v != value {
t.Fatalf("wanted %d at .%s, got %d", value, strings.Join(pth, "."), v)
}
}
func expectString(t *testing.T, obj map[string]interface{}, value string, pth ...string) {
if v, found, err := unstructured.NestedString(obj, pth...); err != nil {
t.Fatalf("failed to access .%s: %v", strings.Join(pth, "."), err)
} else if !found {
t.Fatalf("failed to find .%s", strings.Join(pth, "."))
} else if v != value {
t.Fatalf("wanted %q at .%s, got %q", value, strings.Join(pth, "."), v)
}
}