Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
19
vendor/k8s.io/apiextensions-apiserver/test/integration/apiserver.local.config/certificates/apiserver.crt
generated
vendored
Normal file
19
vendor/k8s.io/apiextensions-apiserver/test/integration/apiserver.local.config/certificates/apiserver.crt
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDFTCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTUxNTQ2MjIwNjAgFw0xODAxMDkwMTQzMjZaGA8yMTE4MDEwOTAxNDMy
|
||||
NlowHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE1MTU0NjIyMDYwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQC2hIORzonehlNadYyI30v1Jj8lhhABuiWiTSkl
|
||||
KCLqZjwBfWfSC4w02zxi2SAH9ju20XCJrUauwPq1qXCp/CqXC/rVgZrzluDlpJpe
|
||||
gF9AilQvGOxhrZhV4kqpOjGVE78uOmpfxiOyNermoJ0OVE8ugh3s/LLTNK/qmCAX
|
||||
uEYTQccAvNEiPX3XPBCiaFlSCkUNS0zp12mJNP43+KF9y0CbtYs1gXKHmmJVSpjR
|
||||
YmcuJJUfHxNrV2YR3ek6O4IIJFIlnLxgpjRBseBPkTenAT3S2YY9MyQkkBrRSPBa
|
||||
vLM24al3KDvXYikYe3WpxeYNHGNcHIgR+hKlRTQ5VrWlfx9dAgMBAAGjWjBYMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MCAGA1UdEQQZMBeCCWxvY2FsaG9zdIcEfwAAAYcEfwAAATANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAFhW8cVTraHPNsE+Jo0ZvcE2ic8lEzeOhWI2O/fpkrUJS5LptPKHS
|
||||
nTK+CPxA0zhIS/vlJznIabeddXwtq7Xb5SwlJMHYMnHD6f5qwpD22D2dxJJa5sma
|
||||
3yrK/4CutuEae08qqSeakfgCjcHLL9p7FZWxujkV9/5CEH5lFWYLGumyIoS46Svf
|
||||
nSfDFKTrOj8P60ncCoWcSpMbdVQBDuKlIZuBMmz9CguC1CtuQWPDUmOGJuPs/+So
|
||||
yusHbBfj+ATUWDYTg1lLjOIOSJpHGUQkvS+8Bo47SThD/b4w2i6VC72ldxtBuxGf
|
||||
L7+jALMhMhiQD+Q4qsNuyvvNQLoYcTTFTw==
|
||||
-----END CERTIFICATE-----
|
27
vendor/k8s.io/apiextensions-apiserver/test/integration/apiserver.local.config/certificates/apiserver.key
generated
vendored
Normal file
27
vendor/k8s.io/apiextensions-apiserver/test/integration/apiserver.local.config/certificates/apiserver.key
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAtoSDkc6J3oZTWnWMiN9L9SY/JYYQAbolok0pJSgi6mY8AX1n
|
||||
0guMNNs8YtkgB/Y7ttFwia1GrsD6talwqfwqlwv61YGa85bg5aSaXoBfQIpULxjs
|
||||
Ya2YVeJKqToxlRO/LjpqX8YjsjXq5qCdDlRPLoId7Pyy0zSv6pggF7hGE0HHALzR
|
||||
Ij191zwQomhZUgpFDUtM6ddpiTT+N/ihfctAm7WLNYFyh5piVUqY0WJnLiSVHx8T
|
||||
a1dmEd3pOjuCCCRSJZy8YKY0QbHgT5E3pwE90tmGPTMkJJAa0UjwWryzNuGpdyg7
|
||||
12IpGHt1qcXmDRxjXByIEfoSpUU0OVa1pX8fXQIDAQABAoIBAERy2ezaqnXbpnLs
|
||||
VrIWHCRqHZBzAJnFN8vwaBfZP47snGBqqX7qecBw3+qqRwr1W1uqnCvl4fYzxVJP
|
||||
o0L8oPRYt89OddAYq2s0GfiK6C4KMpwfGrdfJRxAa4OfoWypJS+vFKmqY0S4V8n6
|
||||
Pixbjf6BKbvw4Re4UKkIODDtGMqrZFVKcFe8LCnd3D+7jvt0M/WjEhrepWxscJh3
|
||||
aHgDzsLzCv1DNjgZfoRZubkK3bdndMaL6NhaKNBz6S7CT9XmZsJaWkmBXs9zOoyr
|
||||
0hKP0A11cm6a7LsmxX5h4uaQLh66KHUPbV4KjKgKiGkSS9cnZoXHFZLOplOfozje
|
||||
1DKitAECgYEA2eWiRNByNIqqRPvBtD8ydavOLk6iLlLt+LkCpGupgELs53WS5fTT
|
||||
TxbyVq+897qeW2Klir7jZFWG3Q+EaBATxMYON+jb7QnIz8gX9lh1PpUlo88BiQzO
|
||||
hAIx2uV19KM0ftXYVTSAUh1N2cgoOWGUWLaeMPdxPOlJwvM25hSfp90CgYEA1m8W
|
||||
vWBO8X5LXM9g+fO1TFSlTnUJW1gWrnOw4VmU2+DbqNmtefpVrqDa5Iw2+mU+EBgA
|
||||
d3wdAHARXpc2MGcIRnRbHn+gXJVHA+gA7H9LSZ4Yi0qJZbNVAgRySs2iBYUcunsR
|
||||
AXkS7sPGQinfnjKh6vhYVErh5jA+cvS8CXZtnYECgYBmh61hYAw9OPqB100AebRO
|
||||
tncgRxP9ZDxiCvx5TcfGeLds+mATIK7FynBh5fOvRfr52WM39DafobcCEiklplsG
|
||||
/oL2P/YshaweSXMtEdapihjaCbAZQxNx/m5jKBHm+VzcSdev0DKJcQyO66Yxyf65
|
||||
98RcGjMIjGWO/E7a2N1/aQKBgCPrY+HBGjg1saYQTuxPuJTasP4deL3GWbZLRtvY
|
||||
x6i1V9ZG8Fo4ZtXjuAcEvcjf4K+NdbaOIcWLAD3aEoe1GpvCrejD9DbOAqFS4aS8
|
||||
Bf6E7xOWHsHccmbuG78QBw3pqFBMgSLABz3bqYA3x2+Wh6z2gMVN7d1DQ5K6EC19
|
||||
mwsBAoGBAKZBgqRHRq1Ch3SWb5Q+SgUvNyQ+PAIwCve0vA4mMIK6EGqU/8wbU01B
|
||||
5/UkCfT+ovDeDuyeaZbTWzwUC4Mrg4C9rThrK5WLc43Dig6G1HhfjdLA+gdKFOjh
|
||||
FpocOI2FEwbmj5Mka6n3TSFI8c55ubYdyXQu92DoFt4dTOJStUn2
|
||||
-----END RSA PRIVATE KEY-----
|
944
vendor/k8s.io/apiextensions-apiserver/test/integration/basic_test.go
generated
vendored
Normal file
944
vendor/k8s.io/apiextensions-apiserver/test/integration/basic_test.go
generated
vendored
Normal file
@@ -0,0 +1,944 @@
|
||||
/*
|
||||
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"
|
||||
"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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
||||
noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, "foo")
|
||||
createdNoxuInstance, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
patch := []byte(`{"num": {"num2":999}}`)
|
||||
createdNoxuInstance, err = noxuNamespacedResourceClient.Patch("foo", types.MergePatchType, patch)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// a patch with no change
|
||||
createdNoxuInstance, err = noxuNamespacedResourceClient.Patch("foo", types.MergePatchType, patch)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// an empty patch
|
||||
createdNoxuInstance, err = noxuNamespacedResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", 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 != 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)
|
||||
}
|
||||
}
|
165
vendor/k8s.io/apiextensions-apiserver/test/integration/finalization_test.go
generated
vendored
Normal file
165
vendor/k8s.io/apiextensions-apiserver/test/integration/finalization_test.go
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
func TestFinalization(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
require.NoError(t, err)
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
ns := "not-the-default"
|
||||
name := "foo123"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
|
||||
instance := fixtures.NewNoxuInstance(ns, name)
|
||||
instance.SetFinalizers([]string{"noxu.example.com/finalizer"})
|
||||
createdNoxuInstance, err := instantiateCustomResource(t, instance, noxuResourceClient, noxuDefinition)
|
||||
require.NoError(t, err)
|
||||
|
||||
uid := createdNoxuInstance.GetUID()
|
||||
err = noxuResourceClient.Delete(name, &metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &uid,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Deleting something with a finalizer sets deletion timestamp to a not-nil value but does not
|
||||
// remove the object from the API server. Here we read it to confirm this.
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get(name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, gottenNoxuInstance.GetDeletionTimestamp())
|
||||
|
||||
// Trying to delete it again to confirm it will not remove the object because finalizer is still there.
|
||||
err = noxuResourceClient.Delete(name, &metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &uid,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Removing the finalizers to allow the following delete remove the object.
|
||||
// This step will fail if previous delete wrongly removed the object. The
|
||||
// object will be deleted as part of the finalizer update.
|
||||
for {
|
||||
gottenNoxuInstance.SetFinalizers(nil)
|
||||
_, err = noxuResourceClient.Update(gottenNoxuInstance)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if !errors.IsConflict(err) {
|
||||
require.NoError(t, err) // Fail on unexpected error
|
||||
}
|
||||
gottenNoxuInstance, err = noxuResourceClient.Get(name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Check that the object is actually gone.
|
||||
_, err = noxuResourceClient.Get(name, metav1.GetOptions{})
|
||||
require.Error(t, err)
|
||||
require.True(t, errors.IsNotFound(err), "%#v", err)
|
||||
}
|
||||
|
||||
func TestFinalizationAndDeletion(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
require.NoError(t, err)
|
||||
defer tearDown()
|
||||
|
||||
// Create a CRD.
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a CR with a finalizer.
|
||||
ns := "not-the-default"
|
||||
name := "foo123"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
|
||||
instance := fixtures.NewNoxuInstance(ns, name)
|
||||
instance.SetFinalizers([]string{"noxu.example.com/finalizer"})
|
||||
createdNoxuInstance, err := instantiateCustomResource(t, instance, noxuResourceClient, noxuDefinition)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete a CR. Because there's a finalizer, it will not get deleted now.
|
||||
uid := createdNoxuInstance.GetUID()
|
||||
err = noxuResourceClient.Delete(name, &metav1.DeleteOptions{
|
||||
Preconditions: &metav1.Preconditions{
|
||||
UID: &uid,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check is the CR scheduled for deletion.
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get(name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, gottenNoxuInstance.GetDeletionTimestamp())
|
||||
|
||||
// Delete the CRD.
|
||||
fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient)
|
||||
|
||||
// Check is CR still there after the CRD deletion.
|
||||
gottenNoxuInstance, err = noxuResourceClient.Get(name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update the CR to remove the finalizer.
|
||||
for {
|
||||
gottenNoxuInstance.SetFinalizers(nil)
|
||||
_, err = noxuResourceClient.Update(gottenNoxuInstance)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if !errors.IsConflict(err) {
|
||||
require.NoError(t, err) // Fail on unexpected error
|
||||
}
|
||||
gottenNoxuInstance, err = noxuResourceClient.Get(name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify the CR is gone.
|
||||
// It should return the NonFound error.
|
||||
_, err = noxuResourceClient.Get(name, metav1.GetOptions{})
|
||||
if !errors.IsNotFound(err) {
|
||||
t.Fatalf("unable to delete cr: %v", err)
|
||||
}
|
||||
|
||||
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{})
|
||||
return errors.IsNotFound(err), err
|
||||
})
|
||||
if !errors.IsNotFound(err) {
|
||||
t.Fatalf("unable to delete crd: %v", err)
|
||||
}
|
||||
}
|
403
vendor/k8s.io/apiextensions-apiserver/test/integration/fixtures/resources.go
generated
vendored
Normal file
403
vendor/k8s.io/apiextensions-apiserver/test/integration/fixtures/resources.go
generated
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
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 fixtures
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"k8s.io/client-go/scale"
|
||||
)
|
||||
|
||||
const (
|
||||
noxuInstanceNum int64 = 9223372036854775807
|
||||
)
|
||||
|
||||
// NewRandomNameCustomResourceDefinition generates a CRD with random name to avoid name conflict in e2e tests
|
||||
func NewRandomNameCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
// ensure the singular doesn't end in an s for now
|
||||
gName := names.SimpleNameGenerator.GenerateName("foo") + "a"
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: gName + "s.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: gName + "s",
|
||||
Singular: gName,
|
||||
Kind: gName,
|
||||
ListKind: gName + "List",
|
||||
},
|
||||
Scope: scope,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewNoxuCustomResourceDefinition returns a WishIHadChosenNoxu CRD.
|
||||
func NewNoxuCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus",
|
||||
Singular: "nonenglishnoxu",
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "NoxuItemList",
|
||||
Categories: []string{"all"},
|
||||
},
|
||||
Scope: scope,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewVersionedNoxuInstance returns a WishIHadChosenNoxu instance for a given version
|
||||
func NewVersionedNoxuInstance(namespace, name, version string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/" + version,
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"name": name,
|
||||
},
|
||||
"content": map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
"num": map[string]interface{}{
|
||||
"num1": noxuInstanceNum,
|
||||
"num2": 1000000,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewNoxuInstance returns a WishIHadChosenNoxu instance for v1beta1.
|
||||
func NewNoxuInstance(namespace, name string) *unstructured.Unstructured {
|
||||
return NewVersionedNoxuInstance(namespace, name, "v1beta1")
|
||||
}
|
||||
|
||||
// NewMultipleVersionNoxuCRD returns a WishIHadChosenNoxu with multiple versions.
|
||||
func NewMultipleVersionNoxuCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus",
|
||||
Singular: "nonenglishnoxu",
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "NoxuItemList",
|
||||
Categories: []string{"all"},
|
||||
},
|
||||
Scope: scope,
|
||||
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
{
|
||||
Name: "v1beta2",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
{
|
||||
Name: "v0",
|
||||
Served: false,
|
||||
Storage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewNoxu2CustomResourceDefinition returns a WishIHadChosenNoxu2 CRD.
|
||||
func NewNoxu2CustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus2.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1alpha1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus2",
|
||||
Singular: "nonenglishnoxu2",
|
||||
Kind: "WishIHadChosenNoxu2",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "Noxu2ItemList",
|
||||
},
|
||||
Scope: scope,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewCurletCustomResourceDefinition returns a Curlet CRD.
|
||||
func NewCurletCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "curlets.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "curlets",
|
||||
Singular: "curlet",
|
||||
Kind: "Curlet",
|
||||
ListKind: "CurletList",
|
||||
},
|
||||
Scope: scope,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewCurletInstance returns a Curlet instance.
|
||||
func NewCurletInstance(namespace, name string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "Curlet",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"name": name,
|
||||
},
|
||||
"content": map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func servedVersions(crd *apiextensionsv1beta1.CustomResourceDefinition) []string {
|
||||
if len(crd.Spec.Versions) == 0 {
|
||||
return []string{crd.Spec.Version}
|
||||
}
|
||||
var versions []string
|
||||
for _, v := range crd.Spec.Versions {
|
||||
if v.Served {
|
||||
versions = append(versions, v.Name)
|
||||
}
|
||||
}
|
||||
return versions
|
||||
}
|
||||
|
||||
func existsInDiscovery(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, version string) (bool, error) {
|
||||
groupResource, err := apiExtensionsClient.Discovery().ServerResourcesForGroupVersion(crd.Spec.Group + "/" + version)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
for _, g := range groupResource.APIResources {
|
||||
if g.Name == crd.Spec.Names.Plural {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// CreateNewCustomResourceDefinitionWatchUnsafe creates the CRD and makes sure
|
||||
// the apiextension apiserver has installed the CRD. But it's not safe to watch
|
||||
// the created CR. Please call CreateNewCustomResourceDefinition if you need to
|
||||
// watch the CR.
|
||||
func CreateNewCustomResourceDefinitionWatchUnsafe(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
crd, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wait until all resources appears in discovery
|
||||
for _, version := range servedVersions(crd) {
|
||||
err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
return existsInDiscovery(crd, apiExtensionsClient, version)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return crd, err
|
||||
}
|
||||
|
||||
// CreateNewCustomResourceDefinition creates the given CRD and makes sure its watch cache is primed on the server.
|
||||
func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, dynamicClientSet dynamic.Interface) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
crd, err := CreateNewCustomResourceDefinitionWatchUnsafe(crd, apiExtensionsClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This is only for a test. We need the watch cache to have a resource version that works for the test.
|
||||
// When new REST storage is created, the storage cacher for the CR starts asynchronously.
|
||||
// REST API operations return like list use the RV of etcd, but the storage cacher's reflector's list
|
||||
// can get a different RV because etcd can be touched in between the initial list operation (if that's what you're doing first)
|
||||
// and the storage cache reflector starting.
|
||||
// Later, you can issue a watch with the REST apis list.RV and end up earlier than the storage cacher.
|
||||
// The general working model is that if you get a "resourceVersion too old" message, you re-list and rewatch.
|
||||
// For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error.
|
||||
// This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen
|
||||
// before like the created RV could be too old to watch.
|
||||
err = wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
return isWatchCachePrimed(crd, dynamicClientSet)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return crd, nil
|
||||
}
|
||||
|
||||
func resourceClientForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.Interface, namespace, version string) dynamic.ResourceInterface {
|
||||
gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version, Resource: crd.Spec.Names.Plural}
|
||||
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
|
||||
return dynamicClientSet.Resource(gvr).Namespace(namespace)
|
||||
}
|
||||
return dynamicClientSet.Resource(gvr)
|
||||
}
|
||||
|
||||
// isWatchCachePrimed returns true if the watch is primed for an specified version of CRD watch
|
||||
func isWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.Interface) (bool, error) {
|
||||
ns := ""
|
||||
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
|
||||
ns = "aval"
|
||||
}
|
||||
|
||||
versions := servedVersions(crd)
|
||||
if len(versions) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
resourceClient := resourceClientForVersion(crd, dynamicClientSet, ns, versions[0])
|
||||
instanceName := "setup-instance"
|
||||
instance := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": crd.Spec.Group + "/" + versions[0],
|
||||
"kind": crd.Spec.Names.Kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": ns,
|
||||
"name": instanceName,
|
||||
},
|
||||
"alpha": "foo_123",
|
||||
"beta": 10,
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
"epsilon": "foobar",
|
||||
"spec": map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
createdInstance, err := resourceClient.Create(instance)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = resourceClient.Delete(createdInstance.GetName(), nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Wait for all versions of watch cache to be primed and also make sure we consumed the DELETE event for all
|
||||
// versions so that any new watch with ResourceVersion=0 does not get those events. This is source of some flaky tests.
|
||||
// When a client creates a watch with resourceVersion=0, it will get an ADD event for any existing objects
|
||||
// but because they specified resourceVersion=0, there is no starting point in the cache buffer to return existing events
|
||||
// from, thus the server will return anything from current head of the cache to the end. By accessing the delete
|
||||
// events for all versions here, we make sure that the head of the cache is passed those events and they will not being
|
||||
// delivered to any future watch with resourceVersion=0.
|
||||
for _, v := range versions {
|
||||
noxuWatch, err := resourceClientForVersion(crd, dynamicClientSet, ns, v).Watch(
|
||||
metav1.ListOptions{ResourceVersion: createdInstance.GetResourceVersion()})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer noxuWatch.Stop()
|
||||
|
||||
select {
|
||||
case watchEvent := <-noxuWatch.ResultChan():
|
||||
if watch.Error == watchEvent.Type {
|
||||
return false, nil
|
||||
}
|
||||
if watch.Deleted != watchEvent.Type {
|
||||
return false, fmt.Errorf("expected DELETE, but got %#v", watchEvent)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
return false, fmt.Errorf("gave up waiting for watch event")
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// DeleteCustomResourceDefinition deletes a CRD and waits until it disappears from discovery.
|
||||
func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) error {
|
||||
if err := apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Delete(crd.Name, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, version := range servedVersions(crd) {
|
||||
err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
exists, err := existsInDiscovery(crd, apiExtensionsClient, version)
|
||||
return !exists, err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateNewScaleClient returns a scale client.
|
||||
func CreateNewScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config) (scale.ScalesGetter, error) {
|
||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupResource, err := discoveryClient.ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resources := []*restmapper.APIGroupResources{
|
||||
{
|
||||
Group: metav1.APIGroup{
|
||||
Name: crd.Spec.Group,
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{Version: crd.Spec.Version},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{Version: crd.Spec.Version},
|
||||
},
|
||||
VersionedResources: map[string][]metav1.APIResource{
|
||||
crd.Spec.Version: groupResource.APIResources,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
restMapper := restmapper.NewDiscoveryRESTMapper(resources)
|
||||
resolver := scale.NewDiscoveryScaleKindResolver(discoveryClient)
|
||||
|
||||
return scale.NewForConfig(config, restMapper, dynamic.LegacyAPIPathResolverFunc, resolver)
|
||||
}
|
108
vendor/k8s.io/apiextensions-apiserver/test/integration/fixtures/server.go
generated
vendored
Normal file
108
vendor/k8s.io/apiextensions-apiserver/test/integration/fixtures/server.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright 2018 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 fixtures
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
servertesting "k8s.io/apiextensions-apiserver/pkg/cmd/server/testing"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// StartDefaultServer starts a test server.
|
||||
func StartDefaultServer(t servertesting.Logger) (func(), *rest.Config, *options.CustomResourceDefinitionsServerOptions, error) {
|
||||
// create kubeconfig which will not actually be used. But authz/authn needs it to startup.
|
||||
fakeKubeConfig, err := ioutil.TempFile("", "kubeconfig")
|
||||
fakeKubeConfig.WriteString(`
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
server: http://127.1.2.3:12345
|
||||
name: integration
|
||||
contexts:
|
||||
- context:
|
||||
cluster: integration
|
||||
user: test
|
||||
name: default-context
|
||||
current-context: default-context
|
||||
users:
|
||||
- name: test
|
||||
user:
|
||||
password: test
|
||||
username: test
|
||||
`)
|
||||
fakeKubeConfig.Close()
|
||||
|
||||
s, err := servertesting.StartTestServer(t, nil, []string{
|
||||
"--etcd-prefix", uuid.New(),
|
||||
"--etcd-servers", strings.Join(IntegrationEtcdServers(), ","),
|
||||
"--authentication-skip-lookup",
|
||||
"--authentication-kubeconfig", fakeKubeConfig.Name(),
|
||||
"--authorization-kubeconfig", fakeKubeConfig.Name(),
|
||||
"--kubeconfig", fakeKubeConfig.Name(),
|
||||
"--disable-admission-plugins", "NamespaceLifecycle,MutatingAdmissionWebhook,ValidatingAdmissionWebhook",
|
||||
}, nil)
|
||||
if err != nil {
|
||||
os.Remove(fakeKubeConfig.Name())
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
tearDownFn := func() {
|
||||
defer os.Remove(fakeKubeConfig.Name())
|
||||
s.TearDownFn()
|
||||
}
|
||||
|
||||
return tearDownFn, s.ClientConfig, s.ServerOpts, nil
|
||||
}
|
||||
|
||||
// StartDefaultServerWithClients starts a test server and returns clients for it.
|
||||
func StartDefaultServerWithClients(t servertesting.Logger) (func(), clientset.Interface, dynamic.Interface, error) {
|
||||
tearDown, config, _, err := StartDefaultServer(t)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
apiExtensionsClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
tearDown()
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
tearDown()
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return tearDown, apiExtensionsClient, dynamicClient, nil
|
||||
}
|
||||
|
||||
// IntegrationEtcdServers returns etcd server URLs.
|
||||
func IntegrationEtcdServers() []string {
|
||||
if etcdURL, ok := os.LookupEnv("KUBE_INTEGRATION_ETCD_URL"); ok {
|
||||
return []string{etcdURL}
|
||||
}
|
||||
return []string{"http://127.0.0.1:2379"}
|
||||
}
|
94
vendor/k8s.io/apiextensions-apiserver/test/integration/helpers.go
generated
vendored
Normal file
94
vendor/k8s.io/apiextensions-apiserver/test/integration/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
"testing"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"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/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) {
|
||||
return instantiateVersionedCustomResource(t, instanceToCreate, client, definition, definition.Spec.Versions[0].Name)
|
||||
}
|
||||
|
||||
func instantiateVersionedCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition, version string) (*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+"/"+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 newNamespacedCustomResourceVersionedClient(ns string, client dynamic.Interface, crd *apiextensionsv1beta1.CustomResourceDefinition, version string) dynamic.ResourceInterface {
|
||||
gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version, Resource: crd.Spec.Names.Plural}
|
||||
|
||||
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
|
||||
return client.Resource(gvr).Namespace(ns)
|
||||
}
|
||||
return client.Resource(gvr)
|
||||
}
|
||||
|
||||
func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.ResourceInterface {
|
||||
return newNamespacedCustomResourceVersionedClient(ns, client, crd, crd.Spec.Versions[0].Name)
|
||||
}
|
||||
|
||||
// updateCustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors.
|
||||
func updateCustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1beta1.CustomResourceDefinition)) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
for i := 0; i < 5; i++ {
|
||||
crd, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get CustomResourceDefinition %q: %v", name, err)
|
||||
}
|
||||
update(crd)
|
||||
crd, err = client.ApiextensionsV1beta1().CustomResourceDefinitions().Update(crd)
|
||||
if err == nil {
|
||||
return crd, nil
|
||||
}
|
||||
if !errors.IsConflict(err) {
|
||||
return nil, fmt.Errorf("failed to update CustomResourceDefinition %q: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("too many retries after conflicts updating CustomResourceDefinition %q", name)
|
||||
}
|
164
vendor/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go
generated
vendored
Normal file
164
vendor/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
func TestPostInvalidObjectMeta(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)
|
||||
}
|
||||
|
||||
noxuResourceClient := newNamespacedCustomResourceClient("default", dynamicClient, noxuDefinition)
|
||||
|
||||
obj := fixtures.NewNoxuInstance("default", "foo")
|
||||
unstructured.SetNestedField(obj.UnstructuredContent(), int64(42), "metadata", "unknown")
|
||||
unstructured.SetNestedField(obj.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "metadata", "labels")
|
||||
_, err = instantiateCustomResource(t, obj, noxuResourceClient, noxuDefinition)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error, expected invalid labels to be rejected: %v", err)
|
||||
}
|
||||
if status, ok := err.(errors.APIStatus); !ok {
|
||||
t.Fatalf("expected APIStatus error, but got: %#v", err)
|
||||
} else if !errors.IsBadRequest(err) {
|
||||
t.Fatalf("expected BadRequst error, but got: %v", errors.ReasonForError(err))
|
||||
} else if !strings.Contains(status.Status().Message, "cannot be handled") {
|
||||
t.Fatalf("expected 'cannot be handled' error message, got: %v", status.Status().Message)
|
||||
}
|
||||
|
||||
unstructured.SetNestedField(obj.UnstructuredContent(), map[string]interface{}{"bar": "abc"}, "metadata", "labels")
|
||||
obj, err = instantiateCustomResource(t, obj, noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if unknown, found, err := unstructured.NestedInt64(obj.UnstructuredContent(), "metadata", "unknown"); err != nil {
|
||||
t.Errorf("unexpected error getting metadata.unknown: %v", err)
|
||||
} else if found {
|
||||
t.Errorf("unexpected metadata.unknown=%#v: expected this to be pruned", unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidObjectMetaInStorage(t *testing.T) {
|
||||
tearDown, config, options, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
RESTOptionsGetter := serveroptions.NewCRDRESTOptionsGetter(*options.RecommendedOptions.Etcd)
|
||||
restOptions, err := RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: noxuDefinition.Spec.Group, Resource: noxuDefinition.Spec.Names.Plural})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlsInfo := transport.TLSInfo{
|
||||
CertFile: restOptions.StorageConfig.CertFile,
|
||||
KeyFile: restOptions.StorageConfig.KeyFile,
|
||||
CAFile: restOptions.StorageConfig.CAFile,
|
||||
}
|
||||
tlsConfig, err := tlsInfo.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
etcdConfig := clientv3.Config{
|
||||
Endpoints: restOptions.StorageConfig.ServerList,
|
||||
TLS: tlsConfig,
|
||||
}
|
||||
etcdclient, err := clientv3.New(etcdConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating object with invalid labels manually in etcd")
|
||||
|
||||
original := fixtures.NewNoxuInstance("default", "foo")
|
||||
unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "metadata", "unknown")
|
||||
unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "metadata", "labels")
|
||||
|
||||
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
||||
key := path.Join("/", restOptions.StorageConfig.Prefix, noxuDefinition.Spec.Group, "noxus/default/foo")
|
||||
val, _ := json.Marshal(original.UnstructuredContent())
|
||||
if _, err := etcdclient.Put(ctx, key, string(val)); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Checking that ObjectMeta is pruned from unknown fields")
|
||||
|
||||
noxuResourceClient := newNamespacedCustomResourceClient("default", dynamicClient, noxuDefinition)
|
||||
obj, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if unknown, found, err := unstructured.NestedFieldNoCopy(obj.UnstructuredContent(), "metadata", "unknown"); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if found {
|
||||
t.Errorf("unexpected to find metadata.unknown=%#v", unknown)
|
||||
}
|
||||
|
||||
t.Logf("Checking that ObjectMeta is pruned from invalid typed fields")
|
||||
|
||||
if labels, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "metadata", "labels"); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if found && !reflect.DeepEqual(labels, map[string]string{"bar": "abc"}) {
|
||||
t.Errorf("unexpected to find metadata.lables=%#v", labels)
|
||||
}
|
||||
}
|
456
vendor/k8s.io/apiextensions-apiserver/test/integration/registration_test.go
generated
vendored
Normal file
456
vendor/k8s.io/apiextensions-apiserver/test/integration/registration_test.go
generated
vendored
Normal file
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"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/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
func TestMultipleResourceInstances(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, 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, fixtures.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, metav1.GetOptions{})
|
||||
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.Items); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
for _, a := range listWithItem.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.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) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
ns := "not-the-default"
|
||||
sameInstanceName := "foo"
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
createdNoxuInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, sameInstanceName), noxuNamespacedResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu Instance:%v", err)
|
||||
}
|
||||
|
||||
gottenNoxuInstance, err := noxuNamespacedResourceClient.Get(sameInstanceName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := createdNoxuInstance, gottenNoxuInstance; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
curletDefinition := fixtures.NewCurletCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
||||
curletDefinition, err = fixtures.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
curletNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, curletDefinition)
|
||||
createdCurletInstance, err := instantiateCustomResource(t, fixtures.NewCurletInstance(ns, sameInstanceName), curletNamespacedResourceClient, curletDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu Instance:%v", err)
|
||||
}
|
||||
gottenCurletInstance, err := curletNamespacedResourceClient.Get(sameInstanceName, metav1.GetOptions{})
|
||||
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, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := createdNoxuInstance, gottenNoxuInstance2; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeRegistrationAndReRegistration(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
||||
ns := "not-the-default"
|
||||
sameInstanceName := "foo"
|
||||
func() {
|
||||
noxuDefinition, err := fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
if _, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, sameInstanceName), noxuNamespacedResourceClient, noxuDefinition); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected a NotFound error, got:%v", err)
|
||||
}
|
||||
if _, err = noxuNamespacedResourceClient.List(metav1.ListOptions{}); err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected a NotFound error, got:%v", err)
|
||||
}
|
||||
if _, err = noxuNamespacedResourceClient.Get("foo", metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected a NotFound error, got:%v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
func() {
|
||||
if _, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected a NotFound error, got:%v", err)
|
||||
}
|
||||
noxuDefinition, err := fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
initialList, err := noxuNamespacedResourceClient.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = noxuNamespacedResourceClient.Get(sameInstanceName, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected a NotFound error, got:%v", err)
|
||||
}
|
||||
if e, a := 0, len(initialList.Items); e != a {
|
||||
t.Fatalf("expected %v, got %v", e, a)
|
||||
}
|
||||
createdNoxuInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, sameInstanceName), noxuNamespacedResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gottenNoxuInstance, err := noxuNamespacedResourceClient.Get(sameInstanceName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := createdNoxuInstance, gottenNoxuInstance; !reflect.DeepEqual(e, a) {
|
||||
t.Fatalf("expected %v, got %v", e, a)
|
||||
}
|
||||
listWithItem, err := noxuNamespacedResourceClient.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := 1, len(listWithItem.Items); e != a {
|
||||
t.Fatalf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := *createdNoxuInstance, listWithItem.Items[0]; !reflect.DeepEqual(e, a) {
|
||||
t.Fatalf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
if err := noxuNamespacedResourceClient.Delete(sameInstanceName, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = noxuNamespacedResourceClient.Get(sameInstanceName, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected a NotFound error, got:%v", err)
|
||||
}
|
||||
listWithoutItem, err := noxuNamespacedResourceClient.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := 0, len(listWithoutItem.Items); e != a {
|
||||
t.Fatalf("expected %v, got %v", e, a)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func TestEtcdStorage(t *testing.T) {
|
||||
tearDown, clientConfig, s, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := apiextensionsclientset.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
etcdPrefix := s.RecommendedOptions.Etcd.StorageConfig.Prefix
|
||||
|
||||
ns1 := "another-default-is-possible"
|
||||
curletDefinition := fixtures.NewCurletCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
|
||||
curletDefinition, err = fixtures.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
curletNamespacedResourceClient := newNamespacedCustomResourceClient(ns1, dynamicClient, curletDefinition)
|
||||
if _, err := instantiateCustomResource(t, fixtures.NewCurletInstance(ns1, "bar"), curletNamespacedResourceClient, curletDefinition); err != nil {
|
||||
t.Fatalf("unable to create curlet cluster scoped Instance:%v", err)
|
||||
}
|
||||
|
||||
ns2 := "the-cruel-default"
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns2, dynamicClient, noxuDefinition)
|
||||
if _, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns2, "foo"), noxuNamespacedResourceClient, noxuDefinition); err != nil {
|
||||
t.Fatalf("unable to create noxu namespace scoped Instance:%v", err)
|
||||
}
|
||||
|
||||
testcases := map[string]struct {
|
||||
etcdPath string
|
||||
expectedObject *metaObject
|
||||
}{
|
||||
"namespacedNoxuDefinition": {
|
||||
etcdPath: "apiextensions.k8s.io/customresourcedefinitions/noxus.mygroup.example.com",
|
||||
expectedObject: &metaObject{
|
||||
Kind: "CustomResourceDefinition",
|
||||
APIVersion: "apiextensions.k8s.io/v1beta1",
|
||||
Metadata: Metadata{
|
||||
Name: "noxus.mygroup.example.com",
|
||||
Namespace: "",
|
||||
SelfLink: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
"namespacedNoxuInstance": {
|
||||
etcdPath: "mygroup.example.com/noxus/the-cruel-default/foo",
|
||||
expectedObject: &metaObject{
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
APIVersion: "mygroup.example.com/v1beta1",
|
||||
Metadata: Metadata{
|
||||
Name: "foo",
|
||||
Namespace: "the-cruel-default",
|
||||
SelfLink: "", // TODO double check: empty?
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"clusteredCurletDefinition": {
|
||||
etcdPath: "apiextensions.k8s.io/customresourcedefinitions/curlets.mygroup.example.com",
|
||||
expectedObject: &metaObject{
|
||||
Kind: "CustomResourceDefinition",
|
||||
APIVersion: "apiextensions.k8s.io/v1beta1",
|
||||
Metadata: Metadata{
|
||||
Name: "curlets.mygroup.example.com",
|
||||
Namespace: "",
|
||||
SelfLink: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"clusteredCurletInstance": {
|
||||
etcdPath: "mygroup.example.com/curlets/bar",
|
||||
expectedObject: &metaObject{
|
||||
Kind: "Curlet",
|
||||
APIVersion: "mygroup.example.com/v1beta1",
|
||||
Metadata: Metadata{
|
||||
Name: "bar",
|
||||
Namespace: "",
|
||||
SelfLink: "", // TODO double check: empty?
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
etcdURL, ok := os.LookupEnv("KUBE_INTEGRATION_ETCD_URL")
|
||||
if !ok {
|
||||
etcdURL = "http://127.0.0.1:2379"
|
||||
}
|
||||
cfg := clientv3.Config{
|
||||
Endpoints: []string{etcdURL},
|
||||
}
|
||||
c, err := clientv3.New(cfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
kv := clientv3.NewKV(c)
|
||||
for testName, tc := range testcases {
|
||||
output, err := getFromEtcd(kv, etcdPrefix, tc.etcdPath)
|
||||
if err != nil {
|
||||
t.Fatalf("%s - no path gotten from etcd:%v", testName, err)
|
||||
}
|
||||
if e, a := tc.expectedObject, output; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%s - expected %#v\n got %#v\n", testName, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFromEtcd(keys clientv3.KV, prefix, localPath string) (*metaObject, error) {
|
||||
internalPath := path.Join("/", prefix, localPath) // TODO: Double check, should we concatenate two prefixes?
|
||||
response, err := keys.Get(context.Background(), internalPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.More || response.Count != 1 || len(response.Kvs) != 1 {
|
||||
return nil, fmt.Errorf("Invalid etcd response (not found == %v): %#v", response.Count == 0, response)
|
||||
}
|
||||
obj := &metaObject{}
|
||||
if err := json.Unmarshal(response.Kvs[0].Value, obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
type metaObject struct {
|
||||
Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
|
||||
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
|
||||
Metadata `json:"metadata,omitempty" protobuf:"bytes,3,opt,name=metadata"`
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
|
||||
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"`
|
||||
SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,3,opt,name=selfLink"`
|
||||
}
|
744
vendor/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go
generated
vendored
Normal file
744
vendor/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go
generated
vendored
Normal file
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
autoscaling "k8s.io/api/autoscaling/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
var labelSelectorPath = ".status.labelSelector"
|
||||
|
||||
func NewNoxuSubresourcesCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus",
|
||||
Singular: "nonenglishnoxu",
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "NoxuItemList",
|
||||
},
|
||||
Scope: scope,
|
||||
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
||||
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
|
||||
SpecReplicasPath: ".spec.replicas",
|
||||
StatusReplicasPath: ".status.replicas",
|
||||
LabelSelectorPath: &labelSelectorPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewNoxuSubresourceInstance(namespace, name string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"name": name,
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"num": int64(10),
|
||||
"replicas": int64(3),
|
||||
},
|
||||
"status": map[string]interface{}{
|
||||
"replicas": int64(7),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusSubresource(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// status should not be set after creation
|
||||
if val, ok := gottenNoxuInstance.Object["status"]; ok {
|
||||
t.Fatalf("status should not be set after creation, got %v", val)
|
||||
}
|
||||
|
||||
// .status.num = 20
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, int64(20), "status", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// .spec.num = 20
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, int64(20), "spec", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// UpdateStatus should not update spec.
|
||||
// Check that .spec.num = 10 and .status.num = 20
|
||||
updatedStatusInstance, err := noxuResourceClient.UpdateStatus(gottenNoxuInstance)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to update status: %v", err)
|
||||
}
|
||||
|
||||
specNum, found, err := unstructured.NestedInt64(updatedStatusInstance.Object, "spec", "num")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .spec.num")
|
||||
}
|
||||
if specNum != int64(10) {
|
||||
t.Fatalf(".spec.num: expected: %v, got: %v", int64(10), specNum)
|
||||
}
|
||||
|
||||
statusNum, found, err := unstructured.NestedInt64(updatedStatusInstance.Object, "status", "num")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .status.num")
|
||||
}
|
||||
if statusNum != int64(20) {
|
||||
t.Fatalf(".status.num: expected: %v, got: %v", int64(20), statusNum)
|
||||
}
|
||||
|
||||
gottenNoxuInstance, err = noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// .status.num = 40
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, int64(40), "status", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// .spec.num = 40
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, int64(40), "spec", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Update should not update status.
|
||||
// Check that .spec.num = 40 and .status.num = 20
|
||||
updatedInstance, err := noxuResourceClient.Update(gottenNoxuInstance)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to update instance: %v", err)
|
||||
}
|
||||
|
||||
specNum, found, err = unstructured.NestedInt64(updatedInstance.Object, "spec", "num")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .spec.num")
|
||||
}
|
||||
if specNum != int64(40) {
|
||||
t.Fatalf(".spec.num: expected: %v, got: %v", int64(40), specNum)
|
||||
}
|
||||
|
||||
statusNum, found, err = unstructured.NestedInt64(updatedInstance.Object, "status", "num")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .status.num")
|
||||
}
|
||||
if statusNum != int64(20) {
|
||||
t.Fatalf(".status.num: expected: %v, got: %v", int64(20), statusNum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScaleSubresource(t *testing.T) {
|
||||
groupResource := schema.GroupResource{
|
||||
Group: "mygroup.example.com",
|
||||
Resource: "noxus",
|
||||
}
|
||||
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
|
||||
// set invalid json path for specReplicasPath
|
||||
noxuDefinition.Spec.Subresources.Scale.SpecReplicasPath = "foo,bar"
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: specReplicasPath should be a valid json path under .spec")
|
||||
}
|
||||
|
||||
noxuDefinition.Spec.Subresources.Scale.SpecReplicasPath = ".spec.replicas"
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
|
||||
scaleClient, err := fixtures.CreateNewScaleClient(noxuDefinition, config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// set .status.labelSelector = bar
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, "bar", "status", "labelSelector")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
_, err = noxuResourceClient.UpdateStatus(gottenNoxuInstance)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to update status: %v", err)
|
||||
}
|
||||
|
||||
// get the scale object
|
||||
gottenScale, err := scaleClient.Scales("not-the-default").Get(groupResource, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gottenScale.Spec.Replicas != 3 {
|
||||
t.Fatalf("Scale.Spec.Replicas: expected: %v, got: %v", 3, gottenScale.Spec.Replicas)
|
||||
}
|
||||
if gottenScale.Status.Selector != "bar" {
|
||||
t.Fatalf("Scale.Status.Selector: expected: %v, got: %v", "bar", gottenScale.Status.Selector)
|
||||
}
|
||||
|
||||
// check self link
|
||||
expectedSelfLink := "/apis/mygroup.example.com/v1beta1/namespaces/not-the-default/noxus/foo/scale"
|
||||
if gottenScale.GetSelfLink() != expectedSelfLink {
|
||||
t.Fatalf("Scale.Metadata.SelfLink: expected: %v, got: %v", expectedSelfLink, gottenScale.GetSelfLink())
|
||||
}
|
||||
|
||||
// update the scale object
|
||||
// check that spec is updated, but status is not
|
||||
gottenScale.Spec.Replicas = 5
|
||||
gottenScale.Status.Selector = "baz"
|
||||
updatedScale, err := scaleClient.Scales("not-the-default").Update(groupResource, gottenScale)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if updatedScale.Spec.Replicas != 5 {
|
||||
t.Fatalf("replicas: expected: %v, got: %v", 5, updatedScale.Spec.Replicas)
|
||||
}
|
||||
if updatedScale.Status.Selector != "bar" {
|
||||
t.Fatalf("scale should not update status: expected %v, got: %v", "bar", updatedScale.Status.Selector)
|
||||
}
|
||||
|
||||
// check that .spec.replicas = 5, but status is not updated
|
||||
updatedNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
specReplicas, found, err := unstructured.NestedInt64(updatedNoxuInstance.Object, "spec", "replicas")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .spec.replicas")
|
||||
}
|
||||
if specReplicas != 5 {
|
||||
t.Fatalf("replicas: expected: %v, got: %v", 5, specReplicas)
|
||||
}
|
||||
statusLabelSelector, found, err := unstructured.NestedString(updatedNoxuInstance.Object, "status", "labelSelector")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .status.labelSelector")
|
||||
}
|
||||
if statusLabelSelector != "bar" {
|
||||
t.Fatalf("scale should not update status: expected %v, got: %v", "bar", statusLabelSelector)
|
||||
}
|
||||
|
||||
// validate maximum value
|
||||
// set .spec.replicas = math.MaxInt64
|
||||
gottenNoxuInstance, err = noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, int64(math.MaxInt64), "spec", "replicas")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
_, err = noxuResourceClient.Update(gottenNoxuInstance)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: .spec.replicas should be less than 2147483647")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationSchemaWithStatus(t *testing.T) {
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// fields other than properties in root schema are not allowed
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition.Spec.Subresources = &apiextensionsv1beta1.CustomResourceSubresources{
|
||||
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||
}
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf(`unexpected non-error, expected: must not have "additionalProperties" at the root of the schema if the status subresource is enabled`)
|
||||
}
|
||||
|
||||
// make sure we are not restricting fields to properties even in subschemas
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema = &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"spec": {
|
||||
Description: "Validation for spec",
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"replicas": {
|
||||
Type: "integer",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"spec"},
|
||||
Description: "This is a description at the root of the schema",
|
||||
}
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to created crd %v: %v", noxuDefinition.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateOnlyStatus(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
// UpdateStatus should validate only status
|
||||
// 1. create a crd with max value of .spec.num = 10 and .status.num = 10
|
||||
// 2. create a cr with .spec.num = 10 and .status.num = 10 (valid)
|
||||
// 3. update the spec of the cr with .spec.num = 15 (spec is invalid), expect no error
|
||||
// 4. update the spec of the cr with .spec.num = 15 (spec is invalid), expect error
|
||||
|
||||
// max value of spec.num = 10 and status.num = 10
|
||||
schema := &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"spec": {
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"num": {
|
||||
Type: "integer",
|
||||
Maximum: float64Ptr(10),
|
||||
},
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"num": {
|
||||
Type: "integer",
|
||||
Maximum: float64Ptr(10),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: schema,
|
||||
}
|
||||
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
|
||||
// set .spec.num = 10 and .status.num = 10
|
||||
noxuInstance := NewNoxuSubresourceInstance(ns, "foo")
|
||||
err = unstructured.SetNestedField(noxuInstance.Object, int64(10), "status", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
createdNoxuInstance, err := instantiateCustomResource(t, noxuInstance, noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
|
||||
// update the spec with .spec.num = 15, expecting no error
|
||||
err = unstructured.SetNestedField(createdNoxuInstance.Object, int64(15), "spec", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error setting .spec.num: %v", err)
|
||||
}
|
||||
createdNoxuInstance, err = noxuResourceClient.UpdateStatus(createdNoxuInstance)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// update with .status.num = 15, expecting an error
|
||||
err = unstructured.SetNestedField(createdNoxuInstance.Object, int64(15), "status", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error setting .status.num: %v", err)
|
||||
}
|
||||
createdNoxuInstance, err = noxuResourceClient.UpdateStatus(createdNoxuInstance)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, but got none")
|
||||
}
|
||||
statusError, isStatus := err.(*apierrors.StatusError)
|
||||
if !isStatus || statusError == nil {
|
||||
t.Fatalf("expected status error, got %T: %v", err, err)
|
||||
}
|
||||
if !strings.Contains(statusError.Error(), "Invalid value") {
|
||||
t.Fatalf("expected 'Invalid value' in error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubresourcesDiscovery(t *testing.T) {
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
group := "mygroup.example.com"
|
||||
version := "v1beta1"
|
||||
|
||||
resources, err := apiExtensionClient.Discovery().ServerResourcesForGroupVersion(group + "/" + version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(resources.APIResources) != 3 {
|
||||
t.Fatalf("Expected exactly the resources \"noxus\", \"noxus/status\" and \"noxus/scale\" in group version %v/%v via discovery, got: %v", group, version, resources.APIResources)
|
||||
}
|
||||
|
||||
// check discovery info for status
|
||||
status := resources.APIResources[1]
|
||||
|
||||
if status.Name != "noxus/status" {
|
||||
t.Fatalf("incorrect status via discovery: expected name: %v, got: %v", "noxus/status", status.Name)
|
||||
}
|
||||
|
||||
if status.Namespaced != true {
|
||||
t.Fatalf("incorrect status via discovery: expected namespace: %v, got: %v", true, status.Namespaced)
|
||||
}
|
||||
|
||||
if status.Kind != "WishIHadChosenNoxu" {
|
||||
t.Fatalf("incorrect status via discovery: expected kind: %v, got: %v", "WishIHadChosenNoxu", status.Kind)
|
||||
}
|
||||
|
||||
expectedVerbs := []string{"get", "patch", "update"}
|
||||
sort.Strings(status.Verbs)
|
||||
if !reflect.DeepEqual([]string(status.Verbs), expectedVerbs) {
|
||||
t.Fatalf("incorrect status via discovery: expected: %v, got: %v", expectedVerbs, status.Verbs)
|
||||
}
|
||||
|
||||
// check discovery info for scale
|
||||
scale := resources.APIResources[2]
|
||||
|
||||
if scale.Group != autoscaling.GroupName {
|
||||
t.Fatalf("incorrect scale via discovery: expected group: %v, got: %v", autoscaling.GroupName, scale.Group)
|
||||
}
|
||||
|
||||
if scale.Version != "v1" {
|
||||
t.Fatalf("incorrect scale via discovery: expected version: %v, got %v", "v1", scale.Version)
|
||||
}
|
||||
|
||||
if scale.Name != "noxus/scale" {
|
||||
t.Fatalf("incorrect scale via discovery: expected name: %v, got: %v", "noxus/scale", scale.Name)
|
||||
}
|
||||
|
||||
if scale.Namespaced != true {
|
||||
t.Fatalf("incorrect scale via discovery: expected namespace: %v, got: %v", true, scale.Namespaced)
|
||||
}
|
||||
|
||||
if scale.Kind != "Scale" {
|
||||
t.Fatalf("incorrect scale via discovery: expected kind: %v, got: %v", "Scale", scale.Kind)
|
||||
}
|
||||
|
||||
sort.Strings(scale.Verbs)
|
||||
if !reflect.DeepEqual([]string(scale.Verbs), expectedVerbs) {
|
||||
t.Fatalf("incorrect scale via discovery: expected: %v, got: %v", expectedVerbs, scale.Verbs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneration(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
|
||||
// .metadata.generation = 1
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gottenNoxuInstance.GetGeneration() != 1 {
|
||||
t.Fatalf(".metadata.generation should be 1 after creation")
|
||||
}
|
||||
|
||||
// .status.num = 20
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, int64(20), "status", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// UpdateStatus does not increment generation
|
||||
updatedStatusInstance, err := noxuResourceClient.UpdateStatus(gottenNoxuInstance)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to update status: %v", err)
|
||||
}
|
||||
if updatedStatusInstance.GetGeneration() != 1 {
|
||||
t.Fatalf("updating status should not increment .metadata.generation: expected: %v, got: %v", 1, updatedStatusInstance.GetGeneration())
|
||||
}
|
||||
|
||||
gottenNoxuInstance, err = noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// .spec.num = 20
|
||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, int64(20), "spec", "num")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Update increments generation
|
||||
updatedInstance, err := noxuResourceClient.Update(gottenNoxuInstance)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to update instance: %v", err)
|
||||
}
|
||||
if updatedInstance.GetGeneration() != 2 {
|
||||
t.Fatalf("updating spec should increment .metadata.generation: expected: %v, got: %v", 2, updatedStatusInstance.GetGeneration())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubresourcePatch(t *testing.T) {
|
||||
groupResource := schema.GroupResource{
|
||||
Group: "mygroup.example.com",
|
||||
Resource: "noxus",
|
||||
}
|
||||
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
|
||||
scaleClient, err := fixtures.CreateNewScaleClient(noxuDefinition, config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
patch := []byte(`{"spec": {"num":999}, "status": {"num":999}}`)
|
||||
patchedNoxuInstance, err := noxuResourceClient.Patch("foo", types.MergePatchType, patch, "status")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// .spec.num should remain 10
|
||||
specNum, found, err := unstructured.NestedInt64(patchedNoxuInstance.Object, "spec", "num")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .spec.num")
|
||||
}
|
||||
if specNum != 10 {
|
||||
t.Fatalf(".spec.num: expected: %v, got: %v", 10, specNum)
|
||||
}
|
||||
|
||||
// .status.num should be 999
|
||||
statusNum, found, err := unstructured.NestedInt64(patchedNoxuInstance.Object, "status", "num")
|
||||
if !found || err != nil {
|
||||
t.Fatalf("unable to get .status.num")
|
||||
}
|
||||
if statusNum != 999 {
|
||||
t.Fatalf(".status.num: expected: %v, got: %v", 999, statusNum)
|
||||
}
|
||||
|
||||
// this call waits for the resourceVersion to be reached in the cache before returning.
|
||||
// We need to do this because the patch gets its initial object from the storage, and the cache serves that.
|
||||
// If it is out of date, then our initial patch is applied to an old resource version, which conflicts
|
||||
// and then the updated object shows a conflicting diff, which permanently fails the patch.
|
||||
// This gives expected stability in the patch without retrying on an known number of conflicts below in the test.
|
||||
// See https://issue.k8s.io/42644
|
||||
_, err = noxuResourceClient.Get("foo", metav1.GetOptions{ResourceVersion: patchedNoxuInstance.GetResourceVersion()})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// no-op patch
|
||||
_, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "status")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// empty patch
|
||||
_, err = noxuResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`), "status")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
patch = []byte(`{"spec": {"replicas":7}, "status": {"replicas":7}}`)
|
||||
patchedNoxuInstance, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "scale")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// this call waits for the resourceVersion to be reached in the cache before returning.
|
||||
// We need to do this because the patch gets its initial object from the storage, and the cache serves that.
|
||||
// If it is out of date, then our initial patch is applied to an old resource version, which conflicts
|
||||
// and then the updated object shows a conflicting diff, which permanently fails the patch.
|
||||
// This gives expected stability in the patch without retrying on an known number of conflicts below in the test.
|
||||
// See https://issue.k8s.io/42644
|
||||
_, err = noxuResourceClient.Get("foo", metav1.GetOptions{ResourceVersion: patchedNoxuInstance.GetResourceVersion()})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Scale.Spec.Replicas = 7 but Scale.Status.Replicas should remain 7
|
||||
gottenScale, err := scaleClient.Scales("not-the-default").Get(groupResource, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gottenScale.Spec.Replicas != 7 {
|
||||
t.Fatalf("Scale.Spec.Replicas: expected: %v, got: %v", 7, gottenScale.Spec.Replicas)
|
||||
}
|
||||
if gottenScale.Status.Replicas != 0 {
|
||||
t.Fatalf("Scale.Status.Replicas: expected: %v, got: %v", 0, gottenScale.Spec.Replicas)
|
||||
}
|
||||
|
||||
// no-op patch
|
||||
_, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "scale")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// empty patch
|
||||
_, err = noxuResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`), "scale")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// make sure strategic merge patch is not supported for both status and scale
|
||||
_, err = noxuResourceClient.Patch("foo", types.StrategicMergePatchType, patch, "status")
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources")
|
||||
}
|
||||
|
||||
_, err = noxuResourceClient.Patch("foo", types.StrategicMergePatchType, patch, "scale")
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources")
|
||||
}
|
||||
}
|
185
vendor/k8s.io/apiextensions-apiserver/test/integration/table_test.go
generated
vendored
Normal file
185
vendor/k8s.io/apiextensions-apiserver/test/integration/table_test.go
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tables.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "tables",
|
||||
Singular: "table",
|
||||
Kind: "Table",
|
||||
ListKind: "TablemList",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
|
||||
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
|
||||
{Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"},
|
||||
{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values", JSONPath: ".spec.gamma"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newTableInstance(name string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "Table",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"alpha": "foo_123",
|
||||
"beta": 10,
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableGet(t *testing.T) {
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd := newTableCRD()
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("table crd created: %#v", crd)
|
||||
|
||||
crClient := newNamespacedCustomResourceClient("", dynamicClient, crd)
|
||||
foo, err := crClient.Create(newTableInstance("foo"))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
t.Logf("foo created: %#v", foo.UnstructuredContent())
|
||||
|
||||
gv := schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Version}
|
||||
gvk := gv.WithKind(crd.Spec.Names.Kind)
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
parameterCodec := runtime.NewParameterCodec(scheme)
|
||||
metav1.AddToGroupVersion(scheme, gv)
|
||||
scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
|
||||
crConfig := *config
|
||||
crConfig.GroupVersion = &gv
|
||||
crConfig.APIPath = "/apis"
|
||||
crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: codecs}
|
||||
crRestClient, err := rest.RESTClientFor(&crConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ret, err := crRestClient.Get().
|
||||
Resource(crd.Spec.Names.Plural).
|
||||
SetHeader("Accept", fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)).
|
||||
VersionedParams(&metav1beta1.TableOptions{}, parameterCodec).
|
||||
Do().
|
||||
Get()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list %v resources: %v", gvk, err)
|
||||
}
|
||||
|
||||
tbl, ok := ret.(*metav1beta1.Table)
|
||||
if !ok {
|
||||
t.Fatalf("expected metav1beta1.Table, got %T", ret)
|
||||
}
|
||||
t.Logf("%v table list: %#v", gvk, tbl)
|
||||
|
||||
if got, expected := len(tbl.ColumnDefinitions), 5; got != expected {
|
||||
t.Errorf("expected %d headers, got %d", expected, got)
|
||||
} else {
|
||||
alpha := metav1beta1.TableColumnDefinition{Name: "Alpha", Type: "string", Format: "", Description: "Custom resource definition column (in JSONPath format): .spec.alpha", Priority: 0}
|
||||
if got, expected := tbl.ColumnDefinitions[2], alpha; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
beta := metav1beta1.TableColumnDefinition{Name: "Beta", Type: "integer", Format: "int64", Description: "the beta field", Priority: 42}
|
||||
if got, expected := tbl.ColumnDefinitions[3], beta; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
gamma := metav1beta1.TableColumnDefinition{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values"}
|
||||
if got, expected := tbl.ColumnDefinitions[4], gamma; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
}
|
||||
if got, expected := len(tbl.Rows), 1; got != expected {
|
||||
t.Errorf("expected %d rows, got %d", expected, got)
|
||||
} else if got, expected := len(tbl.Rows[0].Cells), 5; got != expected {
|
||||
t.Errorf("expected %d cells, got %d", expected, got)
|
||||
} else {
|
||||
if got, expected := tbl.Rows[0].Cells[0], "foo"; got != expected {
|
||||
t.Errorf("expected cell[0] to equal %q, got %q", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[2], "foo_123"; got != expected {
|
||||
t.Errorf("expected cell[2] to equal %q, got %q", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[3], int64(10); got != expected {
|
||||
t.Errorf("expected cell[3] to equal %#v, got %#v", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[4], interface{}(nil); got != expected {
|
||||
t.Errorf("expected cell[3] to equal %#v although the type does not match the column, got %#v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
431
vendor/k8s.io/apiextensions-apiserver/test/integration/validation_test.go
generated
vendored
Normal file
431
vendor/k8s.io/apiextensions-apiserver/test/integration/validation_test.go
generated
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
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 (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
func TestForProperValidationErrors(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"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
instanceFn func() *unstructured.Unstructured
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "bad version",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := fixtures.NewVersionedNoxuInstance(ns, "foo", "v2")
|
||||
return instance
|
||||
},
|
||||
expectedError: "the API version in the data (mygroup.example.com/v2) does not match the expected API version (mygroup.example.com/v1beta1)",
|
||||
},
|
||||
{
|
||||
name: "bad kind",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := fixtures.NewNoxuInstance(ns, "foo")
|
||||
instance.Object["kind"] = "SomethingElse"
|
||||
return instance
|
||||
},
|
||||
expectedError: `SomethingElse.mygroup.example.com "foo" is invalid: kind: Invalid value: "SomethingElse": must be WishIHadChosenNoxu`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
_, err := noxuResourceClient.Create(tc.instanceFn())
|
||||
if err == nil {
|
||||
t.Errorf("%v: expected %v", tc.name, tc.expectedError)
|
||||
continue
|
||||
}
|
||||
// this only works when status errors contain the expect kind and version, so this effectively tests serializations too
|
||||
if !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("%v: expected %v, got %v", tc.name, tc.expectedError, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newNoxuValidationCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus",
|
||||
Singular: "nonenglishnoxu",
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "NoxuItemList",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||
Validation: &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Required: []string{"alpha", "beta"},
|
||||
AdditionalProperties: &apiextensionsv1beta1.JSONSchemaPropsOrBool{
|
||||
Allows: true,
|
||||
},
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"alpha": {
|
||||
Description: "Alpha is an alphanumeric string with underscores",
|
||||
Type: "string",
|
||||
Pattern: "^[a-zA-Z0-9_]*$",
|
||||
},
|
||||
"beta": {
|
||||
Description: "Minimum value of beta is 10",
|
||||
Type: "number",
|
||||
Minimum: float64Ptr(10),
|
||||
},
|
||||
"gamma": {
|
||||
Description: "Gamma is restricted to foo, bar and baz",
|
||||
Type: "string",
|
||||
Enum: []apiextensionsv1beta1.JSON{
|
||||
{
|
||||
Raw: []byte(`"foo"`),
|
||||
},
|
||||
{
|
||||
Raw: []byte(`"bar"`),
|
||||
},
|
||||
{
|
||||
Raw: []byte(`"baz"`),
|
||||
},
|
||||
},
|
||||
},
|
||||
"delta": {
|
||||
Description: "Delta is a string with a maximum length of 5 or a number with a minimum value of 0",
|
||||
AnyOf: []apiextensionsv1beta1.JSONSchemaProps{
|
||||
{
|
||||
Type: "string",
|
||||
MaxLength: int64Ptr(5),
|
||||
},
|
||||
{
|
||||
Type: "number",
|
||||
Minimum: float64Ptr(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newNoxuValidationInstance(namespace, name string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"name": name,
|
||||
},
|
||||
"alpha": "foo_123",
|
||||
"beta": 10,
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomResourceValidation(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomResourceUpdateValidation(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// invalidate the instance
|
||||
gottenNoxuInstance.Object = map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "not-the-default",
|
||||
"name": "foo",
|
||||
},
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
}
|
||||
|
||||
_, err = noxuResourceClient.Update(gottenNoxuInstance)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: alpha and beta should be present while updating %v", gottenNoxuInstance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomResourceValidationErrors(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
instanceFn func() *unstructured.Unstructured
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "bad alpha",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["alpha"] = "foo_123!"
|
||||
return instance
|
||||
},
|
||||
expectedError: "alpha in body should match '^[a-zA-Z0-9_]*$'",
|
||||
},
|
||||
{
|
||||
name: "bad beta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["beta"] = 5
|
||||
return instance
|
||||
},
|
||||
expectedError: "beta in body should be greater than or equal to 10",
|
||||
},
|
||||
{
|
||||
name: "bad gamma",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["gamma"] = "qux"
|
||||
return instance
|
||||
},
|
||||
expectedError: "gamma in body should be one of [foo bar baz]",
|
||||
},
|
||||
{
|
||||
name: "bad delta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["delta"] = "foobarbaz"
|
||||
return instance
|
||||
},
|
||||
expectedError: "must validate at least one schema (anyOf)\ndelta in body should be at most 5 chars long",
|
||||
},
|
||||
{
|
||||
name: "absent alpha and beta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object = map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "not-the-default",
|
||||
"name": "foo",
|
||||
},
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
}
|
||||
return instance
|
||||
},
|
||||
expectedError: ".alpha in body is required\n.beta in body is required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
_, err := noxuResourceClient.Create(tc.instanceFn())
|
||||
if err == nil {
|
||||
t.Errorf("%v: expected %v", tc.name, tc.expectedError)
|
||||
continue
|
||||
}
|
||||
// this only works when status errors contain the expect kind and version, so this effectively tests serializations too
|
||||
if !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("%v: expected %v, got %v", tc.name, tc.expectedError, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCRValidationOnCRDUpdate(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
|
||||
// set stricter schema
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta", "epsilon"}
|
||||
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
|
||||
// CR is rejected
|
||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: CR should be rejected")
|
||||
}
|
||||
|
||||
// update the CRD to a less stricter schema
|
||||
_, err = updateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
crd.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta"}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// CR is now accepted
|
||||
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
_, err := noxuResourceClient.Create(newNoxuValidationInstance(ns, "foo"))
|
||||
if statusError, isStatus := err.(*apierrors.StatusError); isStatus {
|
||||
if strings.Contains(statusError.Error(), "is invalid") {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForbiddenFieldsInSchema(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.AdditionalProperties.Allows = false
|
||||
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: additionalProperties cannot be set to false")
|
||||
}
|
||||
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "array",
|
||||
UniqueItems: true,
|
||||
}
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.AdditionalProperties.Allows = true
|
||||
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: uniqueItems cannot be set to true")
|
||||
}
|
||||
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Ref = strPtr("#/definition/zeta")
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "array",
|
||||
UniqueItems: false,
|
||||
}
|
||||
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatal("unexpected non-error: $ref cannot be non-empty string")
|
||||
}
|
||||
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Ref = nil
|
||||
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func float64Ptr(f float64) *float64 {
|
||||
return &f
|
||||
}
|
||||
|
||||
func int64Ptr(f int64) *int64 {
|
||||
return &f
|
||||
}
|
||||
|
||||
func strPtr(str string) *string {
|
||||
return &str
|
||||
}
|
138
vendor/k8s.io/apiextensions-apiserver/test/integration/versioning_test.go
generated
vendored
Normal file
138
vendor/k8s.io/apiextensions-apiserver/test/integration/versioning_test.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestVersionedNamspacedScopedCRD(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
testSimpleCRUD(t, ns, noxuDefinition, dynamicClient)
|
||||
}
|
||||
|
||||
func TestVersionedClusterScopedCRD(t *testing.T) {
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.ClusterScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := ""
|
||||
testSimpleCRUD(t, ns, noxuDefinition, dynamicClient)
|
||||
}
|
||||
|
||||
func TestStoragedVersionInNamespacedCRDStatus(t *testing.T) {
|
||||
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
ns := "not-the-default"
|
||||
testStoragedVersionInCRDStatus(t, ns, noxuDefinition)
|
||||
}
|
||||
|
||||
func TestStoragedVersionInClusterScopedCRDStatus(t *testing.T) {
|
||||
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.ClusterScoped)
|
||||
ns := ""
|
||||
testStoragedVersionInCRDStatus(t, ns, noxuDefinition)
|
||||
}
|
||||
|
||||
func testStoragedVersionInCRDStatus(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
versionsV1Beta1Storage := []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
{
|
||||
Name: "v1beta2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
}
|
||||
versionsV1Beta2Storage := []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
{
|
||||
Name: "v1beta2",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
}
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition.Spec.Versions = versionsV1Beta1Storage
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The storage version list should be initilized to storage version
|
||||
crd, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := []string{"v1beta1"}, crd.Status.StoredVersions; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// Changing CRD storage version should be reflected immediately
|
||||
crd.Spec.Versions = versionsV1Beta2Storage
|
||||
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(crd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := []string{"v1beta1", "v1beta2"}, crd.Status.StoredVersions; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
err = fixtures.DeleteCustomResourceDefinition(crd, apiExtensionClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
541
vendor/k8s.io/apiextensions-apiserver/test/integration/yaml_test.go
generated
vendored
Normal file
541
vendor/k8s.io/apiextensions-apiserver/test/integration/yaml_test.go
generated
vendored
Normal file
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
func TestYAML(t *testing.T) {
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kind := noxuDefinition.Spec.Names.Kind
|
||||
listKind := noxuDefinition.Spec.Names.ListKind
|
||||
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version
|
||||
|
||||
rest := apiExtensionClient.Discovery().RESTClient()
|
||||
|
||||
// Discovery
|
||||
{
|
||||
result, err := rest.Get().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version).
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetAPIVersion() != "v1" || obj.GetKind() != "APIResourceList" {
|
||||
t.Fatalf("unexpected discovery kind: %s", string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "groupVersion"); v != apiVersion || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
|
||||
// Error
|
||||
{
|
||||
result, err := rest.Get().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "missingname").
|
||||
DoRaw()
|
||||
if !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected not found, got %v", err)
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
|
||||
t.Fatalf("unexpected discovery kind: %s", string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "reason"); v != "NotFound" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
|
||||
uid := types.UID("")
|
||||
resourceVersion := ""
|
||||
|
||||
// Create
|
||||
{
|
||||
yamlBody := []byte(fmt.Sprintf(`
|
||||
apiVersion: %s
|
||||
kind: %s
|
||||
metadata:
|
||||
name: mytest
|
||||
values:
|
||||
numVal: 1
|
||||
boolVal: true
|
||||
stringVal: "1"`, apiVersion, kind))
|
||||
|
||||
result, err := rest.Post().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
SetHeader("Content-Type", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
|
||||
Body(yamlBody).
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != kind {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 1 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != true || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "1" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
uid = obj.GetUID()
|
||||
resourceVersion = obj.GetResourceVersion()
|
||||
}
|
||||
|
||||
// Get
|
||||
{
|
||||
result, err := rest.Get().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest").
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != kind {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 1 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != true || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "1" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
|
||||
// List
|
||||
{
|
||||
result, err := rest.Get().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
listObj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if listObj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, listObj.GetAPIVersion())
|
||||
}
|
||||
if listObj.GetKind() != listKind {
|
||||
t.Fatalf("expected %s, got %s", kind, listObj.GetKind())
|
||||
}
|
||||
items, ok, err := unstructured.NestedSlice(listObj.Object, "items")
|
||||
if !ok || err != nil || len(items) != 1 {
|
||||
t.Fatalf("expected one item, got %v %v %v", items, ok, err)
|
||||
}
|
||||
obj := unstructured.Unstructured{Object: items[0].(map[string]interface{})}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != kind {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 1 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != true || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "1" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
|
||||
// Watch rejects yaml (no streaming support)
|
||||
{
|
||||
result, err := rest.Get().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
|
||||
Param("watch", "true").
|
||||
DoRaw()
|
||||
if !errors.IsNotAcceptable(err) {
|
||||
t.Fatalf("expected not acceptable error, got %v (%s)", err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
|
||||
t.Fatalf("unexpected result: %s", string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "reason"); v != "NotAcceptable" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "code"); v != http.StatusNotAcceptable || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
|
||||
// Update
|
||||
{
|
||||
yamlBody := []byte(fmt.Sprintf(`
|
||||
apiVersion: %s
|
||||
kind: %s
|
||||
metadata:
|
||||
name: mytest
|
||||
uid: %s
|
||||
resourceVersion: "%s"
|
||||
values:
|
||||
numVal: 2
|
||||
boolVal: false
|
||||
stringVal: "2"`, apiVersion, kind, uid, resourceVersion))
|
||||
result, err := rest.Put().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
SetHeader("Content-Type", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest").
|
||||
Body(yamlBody).
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != kind {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 2 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != false || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "2" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if obj.GetUID() != uid {
|
||||
t.Fatalf("uid changed: %v vs %v", uid, obj.GetUID())
|
||||
}
|
||||
}
|
||||
|
||||
// Patch rejects yaml requests (only JSON mime types are allowed)
|
||||
{
|
||||
yamlBody := []byte(fmt.Sprintf(`
|
||||
values:
|
||||
numVal: 3`))
|
||||
result, err := rest.Patch(types.MergePatchType).
|
||||
SetHeader("Accept", "application/yaml").
|
||||
SetHeader("Content-Type", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest").
|
||||
Body(yamlBody).
|
||||
DoRaw()
|
||||
if !errors.IsUnsupportedMediaType(err) {
|
||||
t.Fatalf("Expected bad request, got %v\n%s", err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
|
||||
t.Fatalf("expected %s %s, got %s %s", "v1", "Status", obj.GetAPIVersion(), obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "reason"); v != "UnsupportedMediaType" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
|
||||
// Delete
|
||||
{
|
||||
result, err := rest.Delete().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest").
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
|
||||
t.Fatalf("unexpected response: %s", string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedString(obj.Object, "status"); v != "Success" || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestYAMLSubresource(t *testing.T) {
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.ClusterScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kind := noxuDefinition.Spec.Names.Kind
|
||||
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version
|
||||
|
||||
rest := apiExtensionClient.Discovery().RESTClient()
|
||||
|
||||
uid := types.UID("")
|
||||
resourceVersion := ""
|
||||
|
||||
// Create
|
||||
{
|
||||
yamlBody := []byte(fmt.Sprintf(`
|
||||
apiVersion: %s
|
||||
kind: %s
|
||||
metadata:
|
||||
name: mytest
|
||||
spec:
|
||||
replicas: 3`, apiVersion, kind))
|
||||
|
||||
result, err := rest.Post().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
SetHeader("Content-Type", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
|
||||
Body(yamlBody).
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != kind {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
uid = obj.GetUID()
|
||||
resourceVersion = obj.GetResourceVersion()
|
||||
}
|
||||
|
||||
// Get at /status
|
||||
{
|
||||
result, err := rest.Get().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest", "status").
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != kind {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
|
||||
// Update at /status
|
||||
{
|
||||
yamlBody := []byte(fmt.Sprintf(`
|
||||
apiVersion: %s
|
||||
kind: %s
|
||||
metadata:
|
||||
name: mytest
|
||||
uid: %s
|
||||
resourceVersion: "%s"
|
||||
spec:
|
||||
replicas: 5
|
||||
status:
|
||||
replicas: 3`, apiVersion, kind, uid, resourceVersion))
|
||||
result, err := rest.Put().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
SetHeader("Content-Type", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest", "status").
|
||||
Body(yamlBody).
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != apiVersion {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != kind {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "status", "replicas"); v != 3 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if obj.GetUID() != uid {
|
||||
t.Fatalf("uid changed: %v vs %v", uid, obj.GetUID())
|
||||
}
|
||||
}
|
||||
|
||||
// Get at /scale
|
||||
{
|
||||
result, err := rest.Get().
|
||||
SetHeader("Accept", "application/yaml").
|
||||
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest", "scale").
|
||||
DoRaw()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
obj, err := decodeYAML(result)
|
||||
if err != nil {
|
||||
t.Fatal(err, string(result))
|
||||
}
|
||||
if obj.GetName() != "mytest" {
|
||||
t.Fatalf("expected mytest, got %s", obj.GetName())
|
||||
}
|
||||
if obj.GetAPIVersion() != "autoscaling/v1" {
|
||||
t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
|
||||
}
|
||||
if obj.GetKind() != "Scale" {
|
||||
t.Fatalf("expected %s, got %s", kind, obj.GetKind())
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
if v, ok, err := unstructured.NestedFloat64(obj.Object, "status", "replicas"); v != 3 || !ok || err != nil {
|
||||
t.Fatal(v, ok, err, string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeYAML(data []byte) (*unstructured.Unstructured, error) {
|
||||
retval := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
// ensure this isn't JSON
|
||||
if json.Unmarshal(data, &retval.Object) == nil {
|
||||
return nil, fmt.Errorf("data is JSON, not YAML: %s", string(data))
|
||||
}
|
||||
// ensure it is YAML
|
||||
retval.Object = map[string]interface{}{}
|
||||
if err := yaml.Unmarshal(data, &retval.Object); err != nil {
|
||||
return nil, fmt.Errorf("error decoding YAML: %v\noriginal YAML: %s", err, string(data))
|
||||
}
|
||||
return retval, nil
|
||||
}
|
Reference in New Issue
Block a user