Merge pull request #99101 from deads2k/crd-beta-prune

stop using CRD v1beta1 in tests
This commit is contained in:
Kubernetes Prow Robot 2021-03-03 08:39:21 -08:00 committed by GitHub
commit 2f263b24a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1127 additions and 949 deletions

View File

@ -21,7 +21,7 @@ import (
"testing" "testing"
"time" "time"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@ -34,43 +34,50 @@ func TestAPIApproval(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if noxuAPIApproved := findCRDCondition(noxuDefinition, apiextensionsv1beta1.KubernetesAPIApprovalPolicyConformant); noxuAPIApproved != nil { if noxuAPIApproved := findCRDCondition(noxuDefinition, apiextensionsv1.KubernetesAPIApprovalPolicyConformant); noxuAPIApproved != nil {
t.Fatal(noxuAPIApproved) t.Fatal(noxuAPIApproved)
} }
newSigKubeAPIFn := func(resource, approvalAnnotation string) *apiextensionsv1beta1.CustomResourceDefinition { newSigKubeAPIFn := func(resource, approvalAnnotation string) *apiextensionsv1.CustomResourceDefinition {
return &apiextensionsv1beta1.CustomResourceDefinition{ return &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: resource + ".sigs.k8s.io", Annotations: map[string]string{apiextensionsv1beta1.KubeAPIApprovedAnnotation: approvalAnnotation}}, ObjectMeta: metav1.ObjectMeta{Name: resource + ".sigs.k8s.io", Annotations: map[string]string{apiextensionsv1.KubeAPIApprovedAnnotation: approvalAnnotation}},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "sigs.k8s.io", Group: "sigs.k8s.io",
Version: "v1beta1", Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ {
Name: "v1beta1",
Served: true,
Storage: true,
Schema: fixtures.AllowAllSchema(),
},
},
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: resource, Plural: resource,
Singular: resource + "singular", Singular: resource + "singular",
Kind: resource + "Kind", Kind: resource + "Kind",
ListKind: resource + "List", ListKind: resource + "List",
}, },
Scope: apiextensionsv1beta1.NamespaceScoped, Scope: apiextensionsv1.NamespaceScoped,
}, },
} }
} }
// the unit tests cover all variations. We just need to be sure that we see the code being called // the unit tests cover all variations. We just need to be sure that we see the code being called
approvedKubeAPI := newSigKubeAPIFn("approved", "https://github.com/kubernetes/kubernetes/pull/79724") approvedKubeAPI := newSigKubeAPIFn("approved", "https://github.com/kubernetes/kubernetes/pull/79724")
approvedKubeAPI, err = fixtures.CreateNewCustomResourceDefinition(approvedKubeAPI, apiExtensionClient, dynamicClient) approvedKubeAPI, err = fixtures.CreateNewV1CustomResourceDefinition(approvedKubeAPI, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) {
approvedKubeAPI, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), approvedKubeAPI.Name, metav1.GetOptions{}) approvedKubeAPI, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), approvedKubeAPI.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
if approvedKubeAPIApproved := findCRDCondition(approvedKubeAPI, apiextensionsv1beta1.KubernetesAPIApprovalPolicyConformant); approvedKubeAPIApproved == nil || approvedKubeAPIApproved.Status != apiextensionsv1beta1.ConditionTrue { if approvedKubeAPIApproved := findCRDCondition(approvedKubeAPI, apiextensionsv1.KubernetesAPIApprovalPolicyConformant); approvedKubeAPIApproved == nil || approvedKubeAPIApproved.Status != apiextensionsv1.ConditionTrue {
t.Log(approvedKubeAPIApproved) t.Log(approvedKubeAPIApproved)
return false, nil return false, nil
} }

View File

@ -21,7 +21,7 @@ import (
"fmt" "fmt"
"testing" "testing"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
@ -50,14 +50,14 @@ func TestApplyBasic(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
kind := noxuDefinition.Spec.Names.Kind kind := noxuDefinition.Spec.Names.Kind
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
rest := apiExtensionClient.Discovery().RESTClient() rest := apiExtensionClient.Discovery().RESTClient()
yamlBody := []byte(fmt.Sprintf(` yamlBody := []byte(fmt.Sprintf(`
@ -70,7 +70,7 @@ values:
boolVal: true boolVal: true
stringVal: "1"`, apiVersion, kind)) stringVal: "1"`, apiVersion, kind))
result, err := rest.Patch(types.ApplyPatchType). result, err := rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name("mytest"). Name("mytest").
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
Body(yamlBody). Body(yamlBody).
@ -80,7 +80,7 @@ values:
} }
result, err = rest.Patch(types.MergePatchType). result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name("mytest"). Name("mytest").
Body([]byte(`{"values":{"numVal": 5}}`)). Body([]byte(`{"values":{"numVal": 5}}`)).
DoRaw(context.TODO()) DoRaw(context.TODO())
@ -90,7 +90,7 @@ values:
// Re-apply the same object, we should get conflicts now. // Re-apply the same object, we should get conflicts now.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name("mytest"). Name("mytest").
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
Body(yamlBody). Body(yamlBody).
@ -108,7 +108,7 @@ values:
// Re-apply with force, should work fine. // Re-apply with force, should work fine.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name("mytest"). Name("mytest").
Param("force", "true"). Param("force", "true").
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").

View File

@ -25,7 +25,7 @@ import (
"testing" "testing"
"time" "time"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -56,8 +56,8 @@ func TestNamespaceScopedCRUD(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -75,8 +75,8 @@ func TestClusterScopedCRUD(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -86,7 +86,7 @@ func TestClusterScopedCRUD(t *testing.T) {
testFieldSelector(t, ns, noxuDefinition, dynamicClient) testFieldSelector(t, ns, noxuDefinition, dynamicClient)
} }
func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) { func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1.CustomResourceDefinition, dynamicClient dynamic.Interface) {
noxuResourceClients := map[string]dynamic.ResourceInterface{} noxuResourceClients := map[string]dynamic.ResourceInterface{}
noxuWatchs := map[string]watch.Interface{} noxuWatchs := map[string]watch.Interface{}
disabledVersions := map[string]bool{} disabledVersions := map[string]bool{}
@ -321,8 +321,8 @@ func TestInvalidCRUD(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -373,7 +373,7 @@ func TestInvalidCRUD(t *testing.T) {
} }
} }
func testFieldSelector(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) { func testFieldSelector(t *testing.T, ns string, noxuDefinition *apiextensionsv1.CustomResourceDefinition, dynamicClient dynamic.Interface) {
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
initialList, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{}) initialList, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{})
if err != nil { if err != nil {
@ -527,9 +527,9 @@ func TestDiscovery(t *testing.T) {
} }
defer tearDown() defer tearDown()
scope := apiextensionsv1beta1.NamespaceScoped scope := apiextensionsv1.NamespaceScoped
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(scope) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(scope)
if _, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient); err != nil { if _, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -574,8 +574,8 @@ func TestNoNamespaceReject(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -593,7 +593,7 @@ func TestNoNamespaceReject(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Version, initialListTypeMeta.GetAPIVersion(); e != a { if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, initialListTypeMeta.GetAPIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a) t.Errorf("expected %v, got %v", e, a)
} }
if e, a := noxuDefinition.Spec.Names.ListKind, initialListTypeMeta.GetKind(); e != a { if e, a := noxuDefinition.Spec.Names.ListKind, initialListTypeMeta.GetKind(); e != a {
@ -613,8 +613,8 @@ func TestSameNameDiffNamespace(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -636,8 +636,8 @@ func TestSelfLink(t *testing.T) {
defer tearDown() defer tearDown()
// namespace scoped // namespace scoped
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -656,8 +656,8 @@ func TestSelfLink(t *testing.T) {
} }
// cluster scoped // cluster scoped
curletDefinition := fixtures.NewCurletCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) curletDefinition := fixtures.NewCurletV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
curletDefinition, err = fixtures.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient) curletDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -682,8 +682,8 @@ func TestPreserveInt(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -724,8 +724,8 @@ func TestPatch(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -802,8 +802,8 @@ func TestCrossNamespaceListWatch(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -881,7 +881,7 @@ func TestCrossNamespaceListWatch(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Version, createdTypeMeta.GetAPIVersion(); e != a { if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, createdTypeMeta.GetAPIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a) t.Errorf("expected %v, got %v", e, a)
} }
if e, a := noxuDefinition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a { if e, a := noxuDefinition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a {
@ -901,7 +901,7 @@ func TestCrossNamespaceListWatch(t *testing.T) {
checkNamespacesWatchHelper(t, ns2, noxuNamespacesWatch2) checkNamespacesWatchHelper(t, ns2, noxuNamespacesWatch2)
} }
func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient dynamic.ResourceInterface, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) *unstructured.Unstructured { func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient dynamic.ResourceInterface, noxuDefinition *apiextensionsv1.CustomResourceDefinition) *unstructured.Unstructured {
createdInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, name), noxuNamespacedResourceClient, noxuDefinition) createdInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, name), noxuNamespacedResourceClient, noxuDefinition)
if err != nil { if err != nil {
t.Fatalf("unable to create noxu Instance:%v", err) t.Fatalf("unable to create noxu Instance:%v", err)
@ -944,27 +944,27 @@ func TestNameConflict(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
noxu2Definition := fixtures.NewNoxu2CustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxu2Definition := fixtures.NewNoxu2CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(context.TODO(), noxu2Definition, metav1.CreateOptions{}) _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.TODO(), noxu2Definition, metav1.CreateOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// A NameConflict occurs // A NameConflict occurs
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
crd, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxu2Definition.Name, metav1.GetOptions{}) crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxu2Definition.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
for _, condition := range crd.Status.Conditions { for _, condition := range crd.Status.Conditions {
if condition.Type == apiextensionsv1beta1.NamesAccepted && condition.Status == apiextensionsv1beta1.ConditionFalse { if condition.Type == apiextensionsv1.NamesAccepted && condition.Status == apiextensionsv1.ConditionFalse {
return true, nil return true, nil
} }
} }
@ -974,20 +974,20 @@ func TestNameConflict(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient) err = fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Names are now accepted // Names are now accepted
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
crd, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxu2Definition.Name, metav1.GetOptions{}) crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxu2Definition.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
for _, condition := range crd.Status.Conditions { for _, condition := range crd.Status.Conditions {
if condition.Type == apiextensionsv1beta1.NamesAccepted && condition.Status == apiextensionsv1beta1.ConditionTrue { if condition.Type == apiextensionsv1.NamesAccepted && condition.Status == apiextensionsv1.ConditionTrue {
return true, nil return true, nil
} }
} }
@ -1005,15 +1005,15 @@ func TestStatusGetAndPatch(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// make sure we don't get 405 Method Not Allowed from Getting CRD/status subresource // make sure we don't get 405 Method Not Allowed from Getting CRD/status subresource
result := &apiextensionsv1beta1.CustomResourceDefinition{} result := &apiextensionsv1.CustomResourceDefinition{}
err = apiExtensionClient.ApiextensionsV1beta1().RESTClient().Get(). err = apiExtensionClient.ApiextensionsV1().RESTClient().Get().
Resource("customresourcedefinitions"). Resource("customresourcedefinitions").
Name(noxuDefinition.Name). Name(noxuDefinition.Name).
SubResource("status"). SubResource("status").
@ -1024,7 +1024,7 @@ func TestStatusGetAndPatch(t *testing.T) {
} }
// make sure we don't get 405 Method Not Allowed from Patching CRD/status subresource // make sure we don't get 405 Method Not Allowed from Patching CRD/status subresource
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions(). _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().
Patch(context.TODO(), noxuDefinition.Name, types.StrategicMergePatchType, Patch(context.TODO(), noxuDefinition.Name, types.StrategicMergePatchType,
[]byte(fmt.Sprintf(`{"labels":{"test-label":"dummy"}}`)), metav1.PatchOptions{}, []byte(fmt.Sprintf(`{"labels":{"test-label":"dummy"}}`)), metav1.PatchOptions{},
"status") "status")

View File

@ -23,7 +23,7 @@ import (
"testing" "testing"
"time" "time"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -50,8 +50,8 @@ func TestChangeCRD(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionsClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionsClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -76,7 +76,7 @@ func TestChangeCRD(t *testing.T) {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
noxuDefinitionToUpdate, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}) noxuDefinitionToUpdate, err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
continue continue
@ -90,7 +90,7 @@ func TestChangeCRD(t *testing.T) {
} else { } else {
noxuDefinitionToUpdate.Spec.Versions = noxuDefinitionToUpdate.Spec.Versions[0:1] noxuDefinitionToUpdate.Spec.Versions = noxuDefinitionToUpdate.Spec.Versions[0:1]
} }
if _, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), noxuDefinitionToUpdate, metav1.UpdateOptions{}); err != nil && !apierrors.IsConflict(err) { if _, err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), noxuDefinitionToUpdate, metav1.UpdateOptions{}); err != nil && !apierrors.IsConflict(err) {
t.Error(err) t.Error(err)
continue continue
} }

View File

@ -335,10 +335,8 @@ func validateNonTrivialConverted(t *testing.T, ctc *conversionTestContext) {
client := ctc.versionedClient(ns, createVersion.Name) client := ctc.versionedClient(ns, createVersion.Name)
fixture := newConversionMultiVersionFixture(ns, name, createVersion.Name) fixture := newConversionMultiVersionFixture(ns, name, createVersion.Name)
if !*ctc.crd.Spec.PreserveUnknownFields { if err := unstructured.SetNestedField(fixture.Object, "foo", "garbage"); err != nil {
if err := unstructured.SetNestedField(fixture.Object, "foo", "garbage"); err != nil { t.Fatal(err)
t.Fatal(err)
}
} }
if _, err := client.Create(context.TODO(), fixture, metav1.CreateOptions{}); err != nil { if _, err := client.Create(context.TODO(), fixture, metav1.CreateOptions{}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -393,10 +391,8 @@ func validateNonTrivialConvertedList(t *testing.T, ctc *conversionTestContext) {
name := "converted-" + createVersion.Name name := "converted-" + createVersion.Name
client := ctc.versionedClient(ns, createVersion.Name) client := ctc.versionedClient(ns, createVersion.Name)
fixture := newConversionMultiVersionFixture(ns, name, createVersion.Name) fixture := newConversionMultiVersionFixture(ns, name, createVersion.Name)
if !*ctc.crd.Spec.PreserveUnknownFields { if err := unstructured.SetNestedField(fixture.Object, "foo", "garbage"); err != nil {
if err := unstructured.SetNestedField(fixture.Object, "foo", "garbage"); err != nil { t.Fatal(err)
t.Fatal(err)
}
} }
_, err := client.Create(context.TODO(), fixture, metav1.CreateOptions{}) _, err := client.Create(context.TODO(), fixture, metav1.CreateOptions{})
if err != nil { if err != nil {
@ -428,10 +424,6 @@ func validateNonTrivialConvertedList(t *testing.T, ctc *conversionTestContext) {
} }
func validateStoragePruning(t *testing.T, ctc *conversionTestContext) { func validateStoragePruning(t *testing.T, ctc *conversionTestContext) {
if *ctc.crd.Spec.PreserveUnknownFields {
return
}
ns := ctc.namespace ns := ctc.namespace
for _, createVersion := range ctc.crd.Spec.Versions { for _, createVersion := range ctc.crd.Spec.Versions {
@ -905,13 +897,13 @@ func newConversionTestContext(t *testing.T, apiExtensionsClient clientset.Interf
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), v1CRD.Name, metav1.GetOptions{}) crd, err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), v1CRD.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
tearDown := func() { tearDown := func() {
if err := fixtures.DeleteCustomResourceDefinition(crd, apiExtensionsClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(crd, apiExtensionsClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -923,13 +915,13 @@ type conversionTestContext struct {
namespace string namespace string
apiExtensionsClient clientset.Interface apiExtensionsClient clientset.Interface
dynamicClient dynamic.Interface dynamicClient dynamic.Interface
crd *apiextensionsv1beta1.CustomResourceDefinition crd *apiextensionsv1.CustomResourceDefinition
etcdObjectReader *storage.EtcdObjectReader etcdObjectReader *storage.EtcdObjectReader
} }
func (c *conversionTestContext) versionedClient(ns string, version string) dynamic.ResourceInterface { func (c *conversionTestContext) versionedClient(ns string, version string) dynamic.ResourceInterface {
gvr := schema.GroupVersionResource{Group: c.crd.Spec.Group, Version: version, Resource: c.crd.Spec.Names.Plural} gvr := schema.GroupVersionResource{Group: c.crd.Spec.Group, Version: version, Resource: c.crd.Spec.Names.Plural}
if c.crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { if c.crd.Spec.Scope != apiextensionsv1.ClusterScoped {
return c.dynamicClient.Resource(gvr).Namespace(ns) return c.dynamicClient.Resource(gvr).Namespace(ns)
} }
return c.dynamicClient.Resource(gvr) return c.dynamicClient.Resource(gvr)
@ -943,17 +935,19 @@ func (c *conversionTestContext) versionedClients(ns string) map[string]dynamic.R
return ret return ret
} }
func (c *conversionTestContext) setConversionWebhook(t *testing.T, webhookClientConfig *apiextensionsv1beta1.WebhookClientConfig, reviewVersions []string) { func (c *conversionTestContext) setConversionWebhook(t *testing.T, webhookClientConfig *apiextensionsv1.WebhookClientConfig, reviewVersions []string) {
crd, err := c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{}) crd, err := c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd.Spec.Conversion = &apiextensionsv1beta1.CustomResourceConversion{ crd.Spec.Conversion = &apiextensionsv1.CustomResourceConversion{
Strategy: apiextensionsv1beta1.WebhookConverter, Strategy: apiextensionsv1.WebhookConverter,
WebhookClientConfig: webhookClientConfig, Webhook: &apiextensionsv1.WebhookConversion{
ConversionReviewVersions: reviewVersions, ClientConfig: webhookClientConfig,
ConversionReviewVersions: reviewVersions,
},
} }
crd, err = c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) crd, err = c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -962,15 +956,15 @@ func (c *conversionTestContext) setConversionWebhook(t *testing.T, webhookClient
} }
func (c *conversionTestContext) removeConversionWebhook(t *testing.T) { func (c *conversionTestContext) removeConversionWebhook(t *testing.T) {
crd, err := c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{}) crd, err := c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd.Spec.Conversion = &apiextensionsv1beta1.CustomResourceConversion{ crd.Spec.Conversion = &apiextensionsv1.CustomResourceConversion{
Strategy: apiextensionsv1beta1.NoneConverter, Strategy: apiextensionsv1.NoneConverter,
} }
crd, err = c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) crd, err = c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -998,14 +992,14 @@ func (c *conversionTestContext) setAndWaitStorageVersion(t *testing.T, version s
} }
func (c *conversionTestContext) setStorageVersion(t *testing.T, version string) { func (c *conversionTestContext) setStorageVersion(t *testing.T, version string) {
crd, err := c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{}) crd, err := c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for i, v := range crd.Spec.Versions { for i, v := range crd.Spec.Versions {
crd.Spec.Versions[i].Storage = v.Name == version crd.Spec.Versions[i].Storage = v.Name == version
} }
crd, err = c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) crd, err = c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1027,7 +1021,7 @@ func (c *conversionTestContext) waitForStorageVersion(t *testing.T, version stri
} }
func (c *conversionTestContext) setServed(t *testing.T, version string, served bool) { func (c *conversionTestContext) setServed(t *testing.T, version string, served bool) {
crd, err := c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{}) crd, err := c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), c.crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1036,7 +1030,7 @@ func (c *conversionTestContext) setServed(t *testing.T, version string, served b
crd.Spec.Versions[i].Served = served crd.Spec.Versions[i].Served = served
} }
} }
crd, err = c.apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) crd, err = c.apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -38,7 +38,7 @@ import (
// StartConversionWebhookServer starts an http server with the provided handler and returns the WebhookClientConfig // StartConversionWebhookServer starts an http server with the provided handler and returns the WebhookClientConfig
// needed to configure a CRD to use this conversion webhook as its converter. // needed to configure a CRD to use this conversion webhook as its converter.
func StartConversionWebhookServer(handler http.Handler) (func(), *apiextensionsv1beta1.WebhookClientConfig, error) { func StartConversionWebhookServer(handler http.Handler) (func(), *apiextensionsv1.WebhookClientConfig, error) {
roots := x509.NewCertPool() roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM(localhostCert) { if !roots.AppendCertsFromPEM(localhostCert) {
return nil, nil, fmt.Errorf("failed to append Cert from PEM") return nil, nil, fmt.Errorf("failed to append Cert from PEM")
@ -57,7 +57,7 @@ func StartConversionWebhookServer(handler http.Handler) (func(), *apiextensionsv
} }
webhookServer.StartTLS() webhookServer.StartTLS()
endpoint := webhookServer.URL + "/convert" endpoint := webhookServer.URL + "/convert"
webhookConfig := &apiextensionsv1beta1.WebhookClientConfig{ webhookConfig := &apiextensionsv1.WebhookClientConfig{
CABundle: localhostCert, CABundle: localhostCert,
URL: &endpoint, URL: &endpoint,
} }

View File

@ -23,8 +23,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -36,8 +35,8 @@ func TestFinalization(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
require.NoError(t, err) require.NoError(t, err)
ns := "not-the-default" ns := "not-the-default"
@ -100,8 +99,8 @@ func TestFinalizationAndDeletion(t *testing.T) {
defer tearDown() defer tearDown()
// Create a CRD. // Create a CRD.
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
require.NoError(t, err) require.NoError(t, err)
// Create a CR with a finalizer. // Create a CR with a finalizer.
@ -129,7 +128,7 @@ func TestFinalizationAndDeletion(t *testing.T) {
require.NotNil(t, gottenNoxuInstance.GetDeletionTimestamp()) require.NotNil(t, gottenNoxuInstance.GetDeletionTimestamp())
// Delete the CRD. // Delete the CRD.
fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient) fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient)
// Check is CR still there after the CRD deletion. // Check is CR still there after the CRD deletion.
gottenNoxuInstance, err = noxuResourceClient.Get(context.TODO(), name, metav1.GetOptions{}) gottenNoxuInstance, err = noxuResourceClient.Get(context.TODO(), name, metav1.GetOptions{})
@ -157,7 +156,7 @@ func TestFinalizationAndDeletion(t *testing.T) {
} }
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}) _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{})
return errors.IsNotFound(err), err return errors.IsNotFound(err), err
}) })
if !errors.IsNotFound(err) { if !errors.IsNotFound(err) {

View File

@ -44,6 +44,16 @@ const (
noxuInstanceNum int64 = 9223372036854775807 noxuInstanceNum int64 = 9223372036854775807
) )
// AllowAllSchema doesn't enforce any schema restrictions
func AllowAllSchema() *apiextensionsv1.CustomResourceValidation {
return &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
XPreserveUnknownFields: pointer.BoolPtr(true),
Type: "object",
},
}
}
// NewRandomNameV1CustomResourceDefinition generates a CRD with random name to avoid name conflict in e2e tests // NewRandomNameV1CustomResourceDefinition generates a CRD with random name to avoid name conflict in e2e tests
func NewRandomNameV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope) *apiextensionsv1.CustomResourceDefinition { func NewRandomNameV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope) *apiextensionsv1.CustomResourceDefinition {
// ensure the singular doesn't end in an s for now // ensure the singular doesn't end in an s for now
@ -57,12 +67,7 @@ func NewRandomNameV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: true, Storage: true,
Schema: &apiextensionsv1.CustomResourceValidation{ Schema: AllowAllSchema(),
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
XPreserveUnknownFields: pointer.BoolPtr(true),
Type: "object",
},
},
}, },
}, },
Names: apiextensionsv1.CustomResourceDefinitionNames{ Names: apiextensionsv1.CustomResourceDefinitionNames{
@ -76,46 +81,6 @@ func NewRandomNameV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope
} }
} }
// 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,
},
}
}
// NewNoxuV1CustomResourceDefinition returns a WishIHadChosenNoxu CRD. // NewNoxuV1CustomResourceDefinition returns a WishIHadChosenNoxu CRD.
func NewNoxuV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope) *apiextensionsv1.CustomResourceDefinition { func NewNoxuV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope) *apiextensionsv1.CustomResourceDefinition {
return &apiextensionsv1.CustomResourceDefinition{ return &apiextensionsv1.CustomResourceDefinition{
@ -126,12 +91,7 @@ func NewNoxuV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope) *api
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: true, Storage: true,
Schema: &apiextensionsv1.CustomResourceValidation{ Schema: AllowAllSchema(),
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
XPreserveUnknownFields: pointer.BoolPtr(true),
Type: "object",
},
},
}}, }},
Names: apiextensionsv1.CustomResourceDefinitionNames{ Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "noxus", Plural: "noxus",
@ -173,13 +133,12 @@ func NewNoxuInstance(namespace, name string) *unstructured.Unstructured {
} }
// NewMultipleVersionNoxuCRD returns a WishIHadChosenNoxu with multiple versions. // NewMultipleVersionNoxuCRD returns a WishIHadChosenNoxu with multiple versions.
func NewMultipleVersionNoxuCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition { func NewMultipleVersionNoxuCRD(scope apiextensionsv1.ResourceScope) *apiextensionsv1.CustomResourceDefinition {
return &apiextensionsv1beta1.CustomResourceDefinition{ return &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com", Group: "mygroup.example.com",
Version: "v1beta1", Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "noxus", Plural: "noxus",
Singular: "nonenglishnoxu", Singular: "nonenglishnoxu",
Kind: "WishIHadChosenNoxu", Kind: "WishIHadChosenNoxu",
@ -188,38 +147,52 @@ func NewMultipleVersionNoxuCRD(scope apiextensionsv1beta1.ResourceScope) *apiext
Categories: []string{"all"}, Categories: []string{"all"},
}, },
Scope: scope, Scope: scope,
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: false, Storage: false,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
Schema: AllowAllSchema(),
}, },
{ {
Name: "v1beta2", Name: "v1beta2",
Served: true, Served: true,
Storage: true, Storage: true,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
Schema: AllowAllSchema(),
}, },
{ {
Name: "v0", Name: "v0",
Served: false, Served: false,
Storage: false, Storage: false,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
Schema: AllowAllSchema(),
}, },
}, },
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
},
}, },
} }
} }
// NewNoxu2CustomResourceDefinition returns a WishIHadChosenNoxu2 CRD. // NewNoxu2CustomResourceDefinition returns a WishIHadChosenNoxu2 CRD.
func NewNoxu2CustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition { func NewNoxu2CustomResourceDefinition(scope apiextensionsv1.ResourceScope) *apiextensionsv1.CustomResourceDefinition {
return &apiextensionsv1beta1.CustomResourceDefinition{ return &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "noxus2.mygroup.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "noxus2.mygroup.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com", Group: "mygroup.example.com",
Version: "v1alpha1", Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ Name: "v1alpha1",
Served: true,
Storage: true,
Schema: AllowAllSchema(),
}},
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "noxus2", Plural: "noxus2",
Singular: "nonenglishnoxu2", Singular: "nonenglishnoxu2",
Kind: "WishIHadChosenNoxu2", Kind: "WishIHadChosenNoxu2",
@ -231,14 +204,21 @@ func NewNoxu2CustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope)
} }
} }
// NewCurletCustomResourceDefinition returns a Curlet CRD. // NewCurletV1CustomResourceDefinition returns a Curlet CRD.
func NewCurletCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition { func NewCurletV1CustomResourceDefinition(scope apiextensionsv1.ResourceScope) *apiextensionsv1.CustomResourceDefinition {
return &apiextensionsv1beta1.CustomResourceDefinition{ return &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "curlets.mygroup.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "curlets.mygroup.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com", Group: "mygroup.example.com",
Version: "v1beta1", Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ {
Name: "v1beta1",
Served: true,
Storage: true,
Schema: AllowAllSchema(),
},
},
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "curlets", Plural: "curlets",
Singular: "curlet", Singular: "curlet",
Kind: "Curlet", Kind: "Curlet",
@ -506,23 +486,6 @@ func isWatchCachePrimed(crd *apiextensionsv1.CustomResourceDefinition, dynamicCl
return true, nil 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.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(context.TODO(), crd.Name, metav1.DeleteOptions{}); 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
}
// DeleteV1CustomResourceDefinition deletes a CRD and waits until it disappears from discovery. // DeleteV1CustomResourceDefinition deletes a CRD and waits until it disappears from discovery.
func DeleteV1CustomResourceDefinition(crd *apiextensionsv1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) error { func DeleteV1CustomResourceDefinition(crd *apiextensionsv1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) error {
if err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), crd.Name, metav1.DeleteOptions{}); err != nil { if err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), crd.Name, metav1.DeleteOptions{}); err != nil {
@ -564,7 +527,7 @@ func DeleteV1CustomResourceDefinitions(deleteListOpts metav1.ListOptions, apiExt
} }
// CreateNewVersionedScaleClient returns a scale client. // CreateNewVersionedScaleClient returns a scale client.
func CreateNewVersionedScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config, version string) (scale.ScalesGetter, error) { func CreateNewVersionedScaleClient(crd *apiextensionsv1.CustomResourceDefinition, config *rest.Config, version string) (scale.ScalesGetter, error) {
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -20,9 +20,15 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"go.etcd.io/etcd/clientv3"
"go.etcd.io/etcd/pkg/transport"
"google.golang.org/grpc"
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options" "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
servertesting "k8s.io/apiextensions-apiserver/pkg/cmd/server/testing" servertesting "k8s.io/apiextensions-apiserver/pkg/cmd/server/testing"
@ -103,6 +109,55 @@ func StartDefaultServerWithClients(t servertesting.Logger, extraFlags ...string)
return tearDown, apiExtensionsClient, dynamicClient, nil return tearDown, apiExtensionsClient, dynamicClient, nil
} }
// StartDefaultServerWithClientsAndEtcd starts a test server and returns clients for it.
func StartDefaultServerWithClientsAndEtcd(t servertesting.Logger, extraFlags ...string) (func(), clientset.Interface, dynamic.Interface, *clientv3.Client, string, error) {
tearDown, config, options, err := StartDefaultServer(t, extraFlags...)
if err != nil {
return nil, nil, nil, nil, "", err
}
apiExtensionsClient, err := clientset.NewForConfig(config)
if err != nil {
tearDown()
return nil, nil, nil, nil, "", err
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
tearDown()
return nil, nil, nil, nil, "", err
}
RESTOptionsGetter := serveroptions.NewCRDRESTOptionsGetter(*options.RecommendedOptions.Etcd)
restOptions, err := RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: "hopefully-ignored-group", Resource: "hopefully-ignored-resources"})
if err != nil {
return nil, nil, nil, nil, "", err
}
tlsInfo := transport.TLSInfo{
CertFile: restOptions.StorageConfig.Transport.CertFile,
KeyFile: restOptions.StorageConfig.Transport.KeyFile,
TrustedCAFile: restOptions.StorageConfig.Transport.TrustedCAFile,
}
tlsConfig, err := tlsInfo.ClientConfig()
if err != nil {
return nil, nil, nil, nil, "", err
}
etcdConfig := clientv3.Config{
Endpoints: restOptions.StorageConfig.Transport.ServerList,
DialTimeout: 20 * time.Second,
DialOptions: []grpc.DialOption{
grpc.WithBlock(), // block until the underlying connection is up
},
TLS: tlsConfig,
}
etcdclient, err := clientv3.New(etcdConfig)
if err != nil {
return nil, nil, nil, nil, "", err
}
return tearDown, apiExtensionsClient, dynamicClient, etcdclient, restOptions.StorageConfig.Prefix, nil
}
// IntegrationEtcdServers returns etcd server URLs. // IntegrationEtcdServers returns etcd server URLs.
func IntegrationEtcdServers() []string { func IntegrationEtcdServers() []string {
if etcdURL, ok := os.LookupEnv("KUBE_INTEGRATION_ETCD_URL"); ok { if etcdURL, ok := os.LookupEnv("KUBE_INTEGRATION_ETCD_URL"); ok {

View File

@ -22,7 +22,6 @@ import (
"testing" "testing"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -34,11 +33,11 @@ import (
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) { func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1.CustomResourceDefinition) (*unstructured.Unstructured, error) {
return instantiateVersionedCustomResource(t, instanceToCreate, client, definition, definition.Spec.Versions[0].Name) 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) { func instantiateVersionedCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1.CustomResourceDefinition, version string) (*unstructured.Unstructured, error) {
createdInstance, err := client.Create(context.TODO(), instanceToCreate, metav1.CreateOptions{}) createdInstance, err := client.Create(context.TODO(), instanceToCreate, metav1.CreateOptions{})
if err != nil { if err != nil {
t.Logf("%#v", createdInstance) t.Logf("%#v", createdInstance)
@ -65,28 +64,28 @@ func instantiateVersionedCustomResource(t *testing.T, instanceToCreate *unstruct
return createdInstance, nil return createdInstance, nil
} }
func newNamespacedCustomResourceVersionedClient(ns string, client dynamic.Interface, crd *apiextensionsv1beta1.CustomResourceDefinition, version string) dynamic.ResourceInterface { func newNamespacedCustomResourceVersionedClient(ns string, client dynamic.Interface, crd *apiextensionsv1.CustomResourceDefinition, version string) dynamic.ResourceInterface {
gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version, Resource: crd.Spec.Names.Plural} gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version, Resource: crd.Spec.Names.Plural}
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { if crd.Spec.Scope != apiextensionsv1.ClusterScoped {
return client.Resource(gvr).Namespace(ns) return client.Resource(gvr).Namespace(ns)
} }
return client.Resource(gvr) return client.Resource(gvr)
} }
func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.ResourceInterface { func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd *apiextensionsv1.CustomResourceDefinition) dynamic.ResourceInterface {
return newNamespacedCustomResourceVersionedClient(ns, client, crd, crd.Spec.Versions[0].Name) return newNamespacedCustomResourceVersionedClient(ns, client, crd, crd.Spec.Versions[0].Name)
} }
// UpdateCustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors. // 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) { func UpdateCustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1.CustomResourceDefinition)) (*apiextensionsv1.CustomResourceDefinition, error) {
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
crd, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{}) crd, err := client.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get CustomResourceDefinition %q: %v", name, err) return nil, fmt.Errorf("failed to get CustomResourceDefinition %q: %v", name, err)
} }
update(crd) update(crd)
crd, err = client.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) crd, err = client.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
if err == nil { if err == nil {
return crd, nil return crd, nil
} }
@ -117,13 +116,7 @@ func UpdateV1CustomResourceDefinitionWithRetry(client clientset.Interface, name
} }
// getSchemaForVersion returns the validation schema for given version in given CRD. // getSchemaForVersion returns the validation schema for given version in given CRD.
func getSchemaForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) (*apiextensionsv1beta1.CustomResourceValidation, error) { func getSchemaForVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) (*apiextensionsv1.CustomResourceValidation, error) {
if !hasPerVersionSchema(crd.Spec.Versions) {
return crd.Spec.Validation, nil
}
if crd.Spec.Validation != nil {
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version schemas must be mutual exclusive", crd.Name, version)
}
for _, v := range crd.Spec.Versions { for _, v := range crd.Spec.Versions {
if version == v.Name { if version == v.Name {
return v.Schema, nil return v.Schema, nil
@ -133,13 +126,7 @@ func getSchemaForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, ver
} }
// getSubresourcesForVersion returns the subresources for given version in given CRD. // getSubresourcesForVersion returns the subresources for given version in given CRD.
func getSubresourcesForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) (*apiextensionsv1beta1.CustomResourceSubresources, error) { func getSubresourcesForVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) (*apiextensionsv1.CustomResourceSubresources, error) {
if !hasPerVersionSubresources(crd.Spec.Versions) {
return crd.Spec.Subresources, nil
}
if crd.Spec.Subresources != nil {
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version subresources must be mutual exclusive", crd.Name, version)
}
for _, v := range crd.Spec.Versions { for _, v := range crd.Spec.Versions {
if version == v.Name { if version == v.Name {
return v.Subresources, nil return v.Subresources, nil
@ -152,13 +139,7 @@ func getSubresourcesForVersion(crd *apiextensionsv1beta1.CustomResourceDefinitio
// NOTE: the newly logically-defaulted columns is not pointing to the original CRD object. // NOTE: the newly logically-defaulted columns is not pointing to the original CRD object.
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through // One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
// the original CRD object instead. // the original CRD object instead.
func getColumnsForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) ([]apiextensionsv1beta1.CustomResourceColumnDefinition, error) { func getColumnsForVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) ([]apiextensionsv1.CustomResourceColumnDefinition, error) {
if !hasPerVersionColumns(crd.Spec.Versions) {
return serveDefaultColumnsIfEmpty(crd.Spec.AdditionalPrinterColumns), nil
}
if len(crd.Spec.AdditionalPrinterColumns) > 0 {
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version additionalPrinterColumns must be mutual exclusive", crd.Name, version)
}
for _, v := range crd.Spec.Versions { for _, v := range crd.Spec.Versions {
if version == v.Name { if version == v.Name {
return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil
@ -171,41 +152,11 @@ func getColumnsForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, ve
// NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object. // NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object.
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through // One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
// the original CRD object instead. // the original CRD object instead.
func serveDefaultColumnsIfEmpty(columns []apiextensionsv1beta1.CustomResourceColumnDefinition) []apiextensionsv1beta1.CustomResourceColumnDefinition { func serveDefaultColumnsIfEmpty(columns []apiextensionsv1.CustomResourceColumnDefinition) []apiextensionsv1.CustomResourceColumnDefinition {
if len(columns) > 0 { if len(columns) > 0 {
return columns return columns
} }
return []apiextensionsv1beta1.CustomResourceColumnDefinition{ return []apiextensionsv1.CustomResourceColumnDefinition{
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"}, {Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
} }
} }
// hasPerVersionSchema returns true if a CRD uses per-version schema.
func hasPerVersionSchema(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
for _, v := range versions {
if v.Schema != nil {
return true
}
}
return false
}
// hasPerVersionSubresources returns true if a CRD uses per-version subresources.
func hasPerVersionSubresources(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
for _, v := range versions {
if v.Subresources != nil {
return true
}
}
return false
}
// hasPerVersionColumns returns true if a CRD uses per-version columns.
func hasPerVersionColumns(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
for _, v := range versions {
if len(v.AdditionalPrinterColumns) > 0 {
return true
}
}
return false
}

View File

@ -22,13 +22,12 @@ import (
"strings" "strings"
"testing" "testing"
"k8s.io/client-go/dynamic" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
) )
func TestLimits(t *testing.T) { func TestLimits(t *testing.T) {
@ -47,14 +46,14 @@ func TestLimits(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
kind := noxuDefinition.Spec.Names.Kind kind := noxuDefinition.Spec.Names.Kind
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
rest := apiExtensionClient.Discovery().RESTClient() rest := apiExtensionClient.Discovery().RESTClient()
@ -70,7 +69,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
_, err := rest.Post(). _, err := rest.Post().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if !apierrors.IsRequestEntityTooLargeError(err) { if !apierrors.IsRequestEntityTooLargeError(err) {
@ -93,7 +92,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
_, err := rest.Post(). _, err := rest.Post().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if !apierrors.IsBadRequest(err) { if !apierrors.IsBadRequest(err) {
@ -116,7 +115,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
_, err := rest.Post(). _, err := rest.Post().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if !apierrors.IsBadRequest(err) { if !apierrors.IsBadRequest(err) {
@ -137,7 +136,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
_, err := rest.Post(). _, err := rest.Post().
SetHeader("Accept", "application/json"). SetHeader("Accept", "application/json").
SetHeader("Content-Type", "application/json"). SetHeader("Content-Type", "application/json").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(jsonBody). Body(jsonBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if !apierrors.IsRequestEntityTooLargeError(err) { if !apierrors.IsRequestEntityTooLargeError(err) {
@ -161,7 +160,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
_, err := rest.Post(). _, err := rest.Post().
SetHeader("Accept", "application/json"). SetHeader("Accept", "application/json").
SetHeader("Content-Type", "application/json"). SetHeader("Content-Type", "application/json").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(jsonBody). Body(jsonBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if !apierrors.IsBadRequest(err) { if !apierrors.IsBadRequest(err) {
@ -185,7 +184,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
_, err := rest.Post(). _, err := rest.Post().
SetHeader("Accept", "application/json"). SetHeader("Accept", "application/json").
SetHeader("Content-Type", "application/json"). SetHeader("Content-Type", "application/json").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(jsonBody). Body(jsonBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if !apierrors.IsBadRequest(err) { if !apierrors.IsBadRequest(err) {
@ -196,7 +195,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
// Create instance to allow patching // Create instance to allow patching
{ {
jsonBody := []byte(fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "metadata": {"name": "test"}}`, apiVersion, kind)) jsonBody := []byte(fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "metadata": {"name": "test"}}`, apiVersion, kind))
_, err := rest.Post().AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).Body(jsonBody).DoRaw(context.TODO()) _, err := rest.Post().AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).Body(jsonBody).DoRaw(context.TODO())
if err != nil { if err != nil {
t.Fatalf("error creating object: %v", err) t.Fatalf("error creating object: %v", err)
} }
@ -207,7 +206,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
t.Skip("skipping expensive test") t.Skip("skipping expensive test")
} }
patchBody := []byte(`[{"op":"add","path":"/foo","value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}]`) patchBody := []byte(`[{"op":"add","path":"/foo","value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}]`)
err = rest.Patch(types.JSONPatchType).AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "test"). err = rest.Patch(types.JSONPatchType).AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "test").
Body(patchBody).Do(context.TODO()).Error() Body(patchBody).Do(context.TODO()).Error()
if !apierrors.IsBadRequest(err) { if !apierrors.IsBadRequest(err) {
t.Errorf("expected success or bad request err, got %v", err) t.Errorf("expected success or bad request err, got %v", err)
@ -218,7 +217,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
t.Skip("skipping expensive test") t.Skip("skipping expensive test")
} }
patchBody := []byte(`{"value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}`) patchBody := []byte(`{"value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}`)
err = rest.Patch(types.MergePatchType).AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "test"). err = rest.Patch(types.MergePatchType).AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "test").
Body(patchBody).Do(context.TODO()).Error() Body(patchBody).Do(context.TODO()).Error()
if !apierrors.IsBadRequest(err) { if !apierrors.IsBadRequest(err) {
t.Errorf("expected success or bad request err, got %v", err) t.Errorf("expected success or bad request err, got %v", err)
@ -229,7 +228,7 @@ values: `+strings.Repeat("[", 3*1024*1024), apiVersion, kind))
t.Skip("skipping expensive test") t.Skip("skipping expensive test")
} }
patchBody := []byte(`{"value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}`) patchBody := []byte(`{"value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}`)
err = rest.Patch(types.ApplyPatchType).Param("fieldManager", "test").AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "test"). err = rest.Patch(types.ApplyPatchType).Param("fieldManager", "test").AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "test").
Body(patchBody).Do(context.TODO()).Error() Body(patchBody).Do(context.TODO()).Error()
if !apierrors.IsBadRequest(err) { if !apierrors.IsBadRequest(err) {
t.Errorf("expected bad request err, got %#v", err) t.Errorf("expected bad request err, got %#v", err)

View File

@ -22,37 +22,36 @@ import (
"testing" "testing"
"time" "time"
"k8s.io/apimachinery/pkg/api/errors" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/yaml"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/yaml"
) )
var listTypeResourceFixture = &apiextensionsv1beta1.CustomResourceDefinition{ var listTypeResourceFixture = &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "tests.example.com", Group: "tests.example.com",
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Storage: true, Storage: true,
Served: true, Served: true,
Schema: fixtures.AllowAllSchema(),
}, },
}, },
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
Singular: "foo", Singular: "foo",
Kind: "Foo", Kind: "Foo",
ListKind: "FooList", ListKind: "FooList",
}, },
Scope: apiextensionsv1beta1.ClusterScoped, Scope: apiextensionsv1.ClusterScoped,
Validation: &apiextensionsv1beta1.CustomResourceValidation{},
}, },
} }
@ -128,11 +127,11 @@ func TestListTypes(t *testing.T) {
defer tearDownFn() defer tearDownFn()
crd := listTypeResourceFixture.DeepCopy() crd := listTypeResourceFixture.DeepCopy()
if err := yaml.Unmarshal([]byte(listTypeResourceSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(listTypeResourceSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -195,14 +194,14 @@ func TestListTypes(t *testing.T) {
t.Logf("Remove \"b\" from the keys in the schema which renders the valid instance invalid") t.Logf("Remove \"b\" from the keys in the schema which renders the valid instance invalid")
err = retry.RetryOnConflict(retry.DefaultBackoff, func() error { err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
crd, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{}) crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return err return err
} }
s := crd.Spec.Validation.OpenAPIV3Schema.Properties["correct-map"] s := crd.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["correct-map"]
s.XListMapKeys = []string{"a"} s.XListMapKeys = []string{"a"}
crd.Spec.Validation.OpenAPIV3Schema.Properties["correct-map"] = s crd.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["correct-map"] = s
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
return err return err
}) })
if err != nil { if err != nil {

View File

@ -30,7 +30,6 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options" serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
@ -52,8 +51,8 @@ func TestPostInvalidObjectMeta(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -114,11 +113,11 @@ func TestInvalidObjectMetaInStorage(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{ noxuDefinition.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"embedded": { "embedded": {
Type: "object", Type: "object",
XEmbeddedResource: true, XEmbeddedResource: true,
@ -127,7 +126,7 @@ func TestInvalidObjectMetaInStorage(t *testing.T) {
}, },
}, },
} }
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -24,6 +24,8 @@ import (
"testing" "testing"
"time" "time"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/clientv3"
"go.etcd.io/etcd/pkg/transport" "go.etcd.io/etcd/pkg/transport"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -39,29 +41,37 @@ import (
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/utils/pointer"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
) )
var pruningFixture = &apiextensionsv1beta1.CustomResourceDefinition{ var pruningFixture = &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "tests.example.com", Group: "tests.example.com",
Version: "v1beta1", Scope: apiextensionsv1.ClusterScoped,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1beta1",
Served: true,
Storage: true,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Type: "object",
},
},
},
},
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
Singular: "foo", Singular: "foo",
Kind: "Foo", Kind: "Foo",
ListKind: "FooList", ListKind: "FooList",
}, },
Scope: apiextensionsv1beta1.ClusterScoped,
PreserveUnknownFields: pointer.BoolPtr(false),
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
},
}, },
} }
@ -189,18 +199,18 @@ func TestPruningCreate(t *testing.T) {
defer tearDownFn() defer tearDownFn()
crd := pruningFixture.DeepCopy() crd := pruningFixture.DeepCopy()
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("Creating CR and expect 'unspecified' fields to be pruned") t.Logf("Creating CR and expect 'unspecified' fields to be pruned")
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo := &unstructured.Unstructured{} foo := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil { if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil {
t.Fatal(err) t.Fatal(err)
@ -241,18 +251,18 @@ func TestPruningStatus(t *testing.T) {
defer tearDownFn() defer tearDownFn()
crd := pruningFixture.DeepCopy() crd := pruningFixture.DeepCopy()
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(fooStatusSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(fooStatusSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("Creating CR and expect 'unspecified' fields to be pruned") t.Logf("Creating CR and expect 'unspecified' fields to be pruned")
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo := &unstructured.Unstructured{} foo := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil { if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil {
t.Fatal(err) t.Fatal(err)
@ -310,12 +320,12 @@ func TestPruningFromStorage(t *testing.T) {
} }
crd := pruningFixture.DeepCopy() crd := pruningFixture.DeepCopy()
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -368,7 +378,7 @@ func TestPruningFromStorage(t *testing.T) {
} }
t.Logf("Checking that CustomResource is pruned from unknown fields") t.Logf("Checking that CustomResource is pruned from unknown fields")
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo, err := fooClient.Get(context.TODO(), "foo", metav1.GetOptions{}) foo, err := fooClient.Get(context.TODO(), "foo", metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@ -400,16 +410,16 @@ func TestPruningPatch(t *testing.T) {
defer tearDownFn() defer tearDownFn()
crd := pruningFixture.DeepCopy() crd := pruningFixture.DeepCopy()
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo := &unstructured.Unstructured{} foo := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil { if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil {
t.Fatal(err) t.Fatal(err)
@ -451,18 +461,18 @@ func TestPruningCreatePreservingUnknownFields(t *testing.T) {
defer tearDownFn() defer tearDownFn()
crd := pruningFixture.DeepCopy() crd := pruningFixture.DeepCopy()
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(fooSchemaPreservingUnknownFields), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(fooSchemaPreservingUnknownFields), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("Creating CR and expect 'unspecified' field to be pruned") t.Logf("Creating CR and expect 'unspecified' field to be pruned")
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo := &unstructured.Unstructured{} foo := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil { if err := yaml.Unmarshal([]byte(pruningFooInstance), &foo.Object); err != nil {
t.Fatal(err) t.Fatal(err)
@ -536,18 +546,18 @@ func TestPruningEmbeddedResources(t *testing.T) {
defer tearDownFn() defer tearDownFn()
crd := pruningFixture.DeepCopy() crd := pruningFixture.DeepCopy()
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(fooSchemaEmbeddedResource), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(fooSchemaEmbeddedResource), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("Creating CR and expect 'unspecified' field to be pruned") t.Logf("Creating CR and expect 'unspecified' field to be pruned")
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo := &unstructured.Unstructured{} foo := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(fooSchemaEmbeddedResourceInstance), &foo.Object); err != nil { if err := yaml.Unmarshal([]byte(fooSchemaEmbeddedResourceInstance), &foo.Object); err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -28,7 +28,7 @@ import (
"go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/clientv3"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
@ -47,8 +47,8 @@ func TestMultipleResourceInstances(t *testing.T) {
defer tearDown() defer tearDown()
ns := "not-the-default" ns := "not-the-default"
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -172,8 +172,8 @@ func TestMultipleRegistration(t *testing.T) {
ns := "not-the-default" ns := "not-the-default"
sameInstanceName := "foo" sameInstanceName := "foo"
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -191,8 +191,8 @@ func TestMultipleRegistration(t *testing.T) {
t.Errorf("expected %v, got %v", e, a) t.Errorf("expected %v, got %v", e, a)
} }
curletDefinition := fixtures.NewCurletCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) curletDefinition := fixtures.NewCurletV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
curletDefinition, err = fixtures.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient) curletDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -225,11 +225,11 @@ func TestDeRegistrationAndReRegistration(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
ns := "not-the-default" ns := "not-the-default"
sameInstanceName := "foo" sameInstanceName := "foo"
func() { func() {
noxuDefinition, err := fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err := fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -237,10 +237,10 @@ func TestDeRegistrationAndReRegistration(t *testing.T) {
if _, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, sameInstanceName), noxuNamespacedResourceClient, noxuDefinition); err != nil { if _, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, sameInstanceName), noxuNamespacedResourceClient, noxuDefinition); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) { if _, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) {
t.Fatalf("expected a NotFound error, got:%v", err) t.Fatalf("expected a NotFound error, got:%v", err)
} }
if _, err = noxuNamespacedResourceClient.List(context.TODO(), metav1.ListOptions{}); err == nil || !errors.IsNotFound(err) { if _, err = noxuNamespacedResourceClient.List(context.TODO(), metav1.ListOptions{}); err == nil || !errors.IsNotFound(err) {
@ -252,10 +252,10 @@ func TestDeRegistrationAndReRegistration(t *testing.T) {
}() }()
func() { func() {
if _, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) { if _, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}); err == nil || !errors.IsNotFound(err) {
t.Fatalf("expected a NotFound error, got:%v", err) t.Fatalf("expected a NotFound error, got:%v", err)
} }
noxuDefinition, err := fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err := fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -327,8 +327,8 @@ func TestEtcdStorage(t *testing.T) {
etcdPrefix := s.RecommendedOptions.Etcd.StorageConfig.Prefix etcdPrefix := s.RecommendedOptions.Etcd.StorageConfig.Prefix
ns1 := "another-default-is-possible" ns1 := "another-default-is-possible"
curletDefinition := fixtures.NewCurletCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) curletDefinition := fixtures.NewCurletV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
curletDefinition, err = fixtures.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient) curletDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(curletDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -338,8 +338,8 @@ func TestEtcdStorage(t *testing.T) {
} }
ns2 := "the-cruel-default" ns2 := "the-cruel-default"
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -23,15 +23,14 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
) )
func TestHandlerScope(t *testing.T) { func TestHandlerScope(t *testing.T) {
@ -41,15 +40,32 @@ func TestHandlerScope(t *testing.T) {
} }
defer tearDown() defer tearDown()
for _, scope := range []apiextensionsv1beta1.ResourceScope{apiextensionsv1beta1.ClusterScoped, apiextensionsv1beta1.NamespaceScoped} { for _, scope := range []apiextensionsv1.ResourceScope{apiextensionsv1.ClusterScoped, apiextensionsv1.NamespaceScoped} {
t.Run(string(scope), func(t *testing.T) { t.Run(string(scope), func(t *testing.T) {
crd := &apiextensionsv1beta1.CustomResourceDefinition{ crd := &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: strings.ToLower(string(scope)) + "s.test.apiextensions-apiserver.k8s.io"}, ObjectMeta: metav1.ObjectMeta{
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Name: strings.ToLower(string(scope)) + "s.test.apiextensions-apiserver.k8s.io",
Group: "test.apiextensions-apiserver.k8s.io", Annotations: map[string]string{"api-approved.kubernetes.io": "unapproved, test-only"},
Version: "v1beta1", },
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "test.apiextensions-apiserver.k8s.io",
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1beta1",
Served: true,
Storage: true,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
},
},
Schema: fixtures.AllowAllSchema(),
},
},
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: strings.ToLower(string(scope)) + "s", Plural: strings.ToLower(string(scope)) + "s",
Singular: strings.ToLower(string(scope)), Singular: strings.ToLower(string(scope)),
Kind: string(scope), Kind: string(scope),
@ -58,14 +74,7 @@ func TestHandlerScope(t *testing.T) {
Scope: scope, Scope: scope,
}, },
} }
crd.Spec.Subresources = &apiextensionsv1beta1.CustomResourceSubresources{ crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
},
}
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -75,7 +84,7 @@ func TestHandlerScope(t *testing.T) {
ns := "test" ns := "test"
var client dynamic.ResourceInterface = dynamicClient.Resource(gvr) var client dynamic.ResourceInterface = dynamicClient.Resource(gvr)
var otherScopeClient dynamic.ResourceInterface = dynamicClient.Resource(gvr).Namespace(ns) var otherScopeClient dynamic.ResourceInterface = dynamicClient.Resource(gvr).Namespace(ns)
if crd.Spec.Scope == apiextensionsv1beta1.NamespaceScoped { if crd.Spec.Scope == apiextensionsv1.NamespaceScoped {
client, otherScopeClient = otherScopeClient, client client, otherScopeClient = otherScopeClient, client
} }
@ -141,7 +150,7 @@ func TestHandlerScope(t *testing.T) {
err = otherScopeClient.DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{}) err = otherScopeClient.DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})
assert.True(t, apierrors.IsNotFound(err)) assert.True(t, apierrors.IsNotFound(err))
if scope == apiextensionsv1beta1.ClusterScoped { if scope == apiextensionsv1.ClusterScoped {
_, err = otherScopeClient.List(context.TODO(), metav1.ListOptions{}) _, err = otherScopeClient.List(context.TODO(), metav1.ListOptions{})
assert.True(t, apierrors.IsNotFound(err)) assert.True(t, apierrors.IsNotFound(err))

View File

@ -35,7 +35,7 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
) )
@ -43,51 +43,14 @@ import (
var labelSelectorPath = ".status.labelSelector" var labelSelectorPath = ".status.labelSelector"
var anotherLabelSelectorPath = ".status.anotherLabelSelector" var anotherLabelSelectorPath = ".status.anotherLabelSelector"
func NewNoxuSubresourcesCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiextensionsv1beta1.CustomResourceDefinition { func NewNoxuSubresourcesCRDs(scope apiextensionsv1.ResourceScope) []*apiextensionsv1.CustomResourceDefinition {
return []*apiextensionsv1beta1.CustomResourceDefinition{ return []*apiextensionsv1.CustomResourceDefinition{
// CRD that uses top-level subresources
{
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,
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
{
Name: "v1beta1",
Served: true,
Storage: true,
},
{
Name: "v1",
Served: true,
Storage: false,
},
},
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
LabelSelectorPath: &labelSelectorPath,
},
},
},
},
// CRD that uses per-version subresources // CRD that uses per-version subresources
{ {
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com", Group: "mygroup.example.com",
Version: "v1beta1", Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "noxus", Plural: "noxus",
Singular: "nonenglishnoxu", Singular: "nonenglishnoxu",
Kind: "WishIHadChosenNoxu", Kind: "WishIHadChosenNoxu",
@ -95,32 +58,34 @@ func NewNoxuSubresourcesCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiext
ListKind: "NoxuItemList", ListKind: "NoxuItemList",
}, },
Scope: scope, Scope: scope,
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: true, Storage: true,
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{ Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}, Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{ Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas", SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas", StatusReplicasPath: ".status.replicas",
LabelSelectorPath: &labelSelectorPath, LabelSelectorPath: &labelSelectorPath,
}, },
}, },
Schema: fixtures.AllowAllSchema(),
}, },
{ {
Name: "v1", Name: "v1",
Served: true, Served: true,
Storage: false, Storage: false,
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{ Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}, Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{ Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas", SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas", StatusReplicasPath: ".status.replicas",
LabelSelectorPath: &anotherLabelSelectorPath, LabelSelectorPath: &anotherLabelSelectorPath,
}, },
}, },
Schema: fixtures.AllowAllSchema(),
}, },
}, },
}, },
@ -155,9 +120,9 @@ func TestStatusSubresource(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -254,7 +219,7 @@ func TestStatusSubresource(t *testing.T) {
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -281,7 +246,7 @@ func TestScaleSubresource(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
for _, v := range noxuDefinition.Spec.Versions { for _, v := range noxuDefinition.Spec.Versions {
// Start with a new CRD, so that the object doesn't have resourceVersion // Start with a new CRD, so that the object doesn't have resourceVersion
@ -293,13 +258,13 @@ func TestScaleSubresource(t *testing.T) {
} }
// set invalid json path for specReplicasPath // set invalid json path for specReplicasPath
subresources.Scale.SpecReplicasPath = "foo,bar" subresources.Scale.SpecReplicasPath = "foo,bar"
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) _, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err == nil { if err == nil {
t.Fatalf("unexpected non-error: specReplicasPath should be a valid json path under .spec") t.Fatalf("unexpected non-error: specReplicasPath should be a valid json path under .spec")
} }
subresources.Scale.SpecReplicasPath = ".spec.replicas" subresources.Scale.SpecReplicasPath = ".spec.replicas"
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -400,7 +365,7 @@ func TestScaleSubresource(t *testing.T) {
t.Fatalf("unexpected non-error: .spec.replicas should be less than 2147483647") t.Fatalf("unexpected non-error: .spec.replicas should be less than 2147483647")
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -423,22 +388,16 @@ func TestValidationSchemaWithStatus(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// fields other than properties in root schema are not allowed noxuDefinition := newNoxuValidationCRDs()[0]
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[0]
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 // make sure we are not restricting fields to properties even in subschemas
noxuDefinition.Spec.Validation.OpenAPIV3Schema = &apiextensionsv1beta1.JSONSchemaProps{ noxuDefinition.Spec.Versions[0].Schema.OpenAPIV3Schema = &apiextensionsv1.JSONSchemaProps{
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Type: "object",
Properties: map[string]apiextensionsv1.JSONSchemaProps{
"spec": { "spec": {
Type: "object",
Description: "Validation for spec", Description: "Validation for spec",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"replicas": { "replicas": {
Type: "integer", Type: "integer",
}, },
@ -448,7 +407,9 @@ func TestValidationSchemaWithStatus(t *testing.T) {
Required: []string{"spec"}, Required: []string{"spec"},
Description: "This is a description at the root of the schema", Description: "This is a description at the root of the schema",
} }
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition.Spec.Versions[1].Schema.OpenAPIV3Schema = noxuDefinition.Spec.Versions[0].Schema.OpenAPIV3Schema
_, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatalf("unable to created crd %v: %v", noxuDefinition.Name, err) t.Fatalf("unable to created crd %v: %v", noxuDefinition.Name, err)
} }
@ -468,10 +429,12 @@ func TestValidateOnlyStatus(t *testing.T) {
// 4. update the spec of the cr with .spec.num = 15 (spec is invalid), expect 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 // max value of spec.num = 10 and status.num = 10
schema := &apiextensionsv1beta1.JSONSchemaProps{ schema := &apiextensionsv1.JSONSchemaProps{
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Type: "object",
Properties: map[string]apiextensionsv1.JSONSchemaProps{
"spec": { "spec": {
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Type: "object",
Properties: map[string]apiextensionsv1.JSONSchemaProps{
"num": { "num": {
Type: "integer", Type: "integer",
Maximum: float64Ptr(10), Maximum: float64Ptr(10),
@ -479,7 +442,8 @@ func TestValidateOnlyStatus(t *testing.T) {
}, },
}, },
"status": { "status": {
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Type: "object",
Properties: map[string]apiextensionsv1.JSONSchemaProps{
"num": { "num": {
Type: "integer", Type: "integer",
Maximum: float64Ptr(10), Maximum: float64Ptr(10),
@ -489,24 +453,16 @@ func TestValidateOnlyStatus(t *testing.T) {
}, },
} }
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)
for i, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
if i == 0 { noxuDefinition.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{
noxuDefinition.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{ OpenAPIV3Schema: schema.DeepCopy(),
OpenAPIV3Schema: schema, }
} noxuDefinition.Spec.Versions[1].Schema = &apiextensionsv1.CustomResourceValidation{
} else { OpenAPIV3Schema: schema.DeepCopy(),
noxuDefinition.Spec.Versions[0].Schema = &apiextensionsv1beta1.CustomResourceValidation{
OpenAPIV3Schema: schema,
}
schemaWithDescription := schema.DeepCopy()
schemaWithDescription.Description = "test"
noxuDefinition.Spec.Versions[1].Schema = &apiextensionsv1beta1.CustomResourceValidation{
OpenAPIV3Schema: schemaWithDescription,
}
} }
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -554,7 +510,7 @@ func TestValidateOnlyStatus(t *testing.T) {
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -576,9 +532,9 @@ func TestSubresourcesDiscovery(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -645,7 +601,7 @@ func TestSubresourcesDiscovery(t *testing.T) {
t.Fatalf("incorrect scale via discovery: expected: %v, got: %v", expectedVerbs, scale.Verbs) t.Fatalf("incorrect scale via discovery: expected: %v, got: %v", expectedVerbs, scale.Verbs)
} }
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -658,9 +614,9 @@ func TestGeneration(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -718,7 +674,7 @@ func TestGeneration(t *testing.T) {
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -745,9 +701,9 @@ func TestSubresourcePatch(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -882,7 +838,7 @@ func TestSubresourcePatch(t *testing.T) {
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }

View File

@ -32,42 +32,42 @@ import (
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
) )
func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition { func newTableCRD() *apiextensionsv1.CustomResourceDefinition {
return &apiextensionsv1beta1.CustomResourceDefinition{ return &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "tables.mygroup.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "tables.mygroup.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com", Group: "mygroup.example.com",
Version: "v1beta1", Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "tables", Plural: "tables",
Singular: "table", Singular: "table",
Kind: "Table", Kind: "Table",
ListKind: "TablemList", ListKind: "TablemList",
}, },
Scope: apiextensionsv1beta1.ClusterScoped, Scope: apiextensionsv1.ClusterScoped,
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: false, Storage: false,
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{ AdditionalPrinterColumns: []apiextensionsv1.CustomResourceColumnDefinition{
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"}, {Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"}, {Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
{Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"}, {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"}, {Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values", JSONPath: ".spec.gamma"},
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"}, {Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
}, },
Schema: fixtures.AllowAllSchema(),
}, },
{ {
Name: "v1", Name: "v1",
Served: true, Served: true,
Storage: true, Storage: true,
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{ AdditionalPrinterColumns: []apiextensionsv1.CustomResourceColumnDefinition{
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"}, {Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"}, {Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
{Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"}, {Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"},
@ -75,6 +75,7 @@ func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition {
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"}, {Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
{Name: "Zeta", Type: "integer", Description: "the zeta field", Format: "int64", Priority: 42, JSONPath: ".spec.zeta"}, {Name: "Zeta", Type: "integer", Description: "the zeta field", Format: "int64", Priority: 42, JSONPath: ".spec.zeta"},
}, },
Schema: fixtures.AllowAllSchema(),
}, },
}, },
}, },
@ -119,12 +120,12 @@ func TestTableGet(t *testing.T) {
} }
crd := newTableCRD() crd := newTableCRD()
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{}) crd, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -377,8 +378,8 @@ func TestColumnsPatch(t *testing.T) {
} }
// CRD with no top-level and per-version columns should be created successfully // CRD with no top-level and per-version columns should be created successfully
crd := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)[0] crd := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)[0]
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -386,14 +387,50 @@ func TestColumnsPatch(t *testing.T) {
// One should be able to patch the CRD to use per-version columns. The top-level columns // One should be able to patch the CRD to use per-version columns. The top-level columns
// should not be defaulted during creation, and apiserver should not return validation // should not be defaulted during creation, and apiserver should not return validation
// error about top-level and per-version columns being mutual exclusive. // error about top-level and per-version columns being mutual exclusive.
patch := []byte(`{"spec":{"versions":[{"name":"v1beta1","served":true,"storage":true,"additionalPrinterColumns":[{"name":"Age","type":"date","JSONPath":".metadata.creationTimestamp"}]},{"name":"v1","served":true,"storage":false,"additionalPrinterColumns":[{"name":"Age2","type":"date","JSONPath":".metadata.creationTimestamp"}]}]}}`) patch := []byte(`{
"spec": {
"versions": [
{
"name": "v1beta1",
"served": true,
"storage": true,
"additionalPrinterColumns": [
{
"name": "Age",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
],
"schema": {
"openAPIV3Schema": {"x-kubernetes-preserve-unknown-fields": true, "type": "object"}
}
},
{
"name": "v1",
"served": true,
"storage": false,
"additionalPrinterColumns": [
{
"name": "Age2",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
],
"schema": {
"openAPIV3Schema": {"x-kubernetes-preserve-unknown-fields": true, "type": "object"}
}
}
]
}
}
`)
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{}) _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Patch(context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{}) crd, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -420,11 +457,11 @@ func TestPatchCleanTopLevelColumns(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
crd := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)[0] crd := NewNoxuSubresourcesCRDs(apiextensionsv1.NamespaceScoped)[0]
crd.Spec.AdditionalPrinterColumns = []apiextensionsv1beta1.CustomResourceColumnDefinition{ crd.Spec.Versions[0].AdditionalPrinterColumns = []apiextensionsv1.CustomResourceColumnDefinition{
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"}, {Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -432,14 +469,51 @@ func TestPatchCleanTopLevelColumns(t *testing.T) {
// One should be able to patch the CRD to use per-version columns by cleaning // One should be able to patch the CRD to use per-version columns by cleaning
// the top-level columns. // the top-level columns.
patch := []byte(`{"spec":{"additionalPrinterColumns":null,"versions":[{"name":"v1beta1","served":true,"storage":true,"additionalPrinterColumns":[{"name":"Age","type":"date","JSONPath":".metadata.creationTimestamp"}]},{"name":"v1","served":true,"storage":false,"additionalPrinterColumns":[{"name":"Age2","type":"date","JSONPath":".metadata.creationTimestamp"}]}]}}`) patch := []byte(`{
"spec": {
"additionalPrinterColumns": null,
"versions": [
{
"name": "v1beta1",
"served": true,
"storage": true,
"additionalPrinterColumns": [
{
"name": "Age",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
],
"schema": {
"openAPIV3Schema": {"x-kubernetes-preserve-unknown-fields": true, "type": "object"}
}
},
{
"name": "v1",
"served": true,
"storage": false,
"additionalPrinterColumns": [
{
"name": "Age2",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
],
"schema": {
"openAPIV3Schema": {"x-kubernetes-preserve-unknown-fields": true, "type": "object"}
}
}
]
}
}
`)
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{}) _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Patch(context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{}) crd, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -19,6 +19,7 @@ package integration
import ( import (
"context" "context"
"fmt" "fmt"
"path"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -27,9 +28,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/util/yaml" "k8s.io/apimachinery/pkg/util/yaml"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
@ -44,8 +46,8 @@ func TestForProperValidationErrors(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -91,13 +93,11 @@ func TestForProperValidationErrors(t *testing.T) {
} }
} }
func newNoxuValidationCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiextensionsv1beta1.CustomResourceDefinition { func newNoxuValidationCRDs() []*apiextensionsv1.CustomResourceDefinition {
validationSchema := &apiextensionsv1beta1.JSONSchemaProps{ validationSchema := &apiextensionsv1.JSONSchemaProps{
Type: "object",
Required: []string{"alpha", "beta"}, Required: []string{"alpha", "beta"},
AdditionalProperties: &apiextensionsv1beta1.JSONSchemaPropsOrBool{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
Allows: true,
},
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
"alpha": { "alpha": {
Description: "Alpha is an alphanumeric string with underscores", Description: "Alpha is an alphanumeric string with underscores",
Type: "string", Type: "string",
@ -111,7 +111,7 @@ func newNoxuValidationCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiexten
"gamma": { "gamma": {
Description: "Gamma is restricted to foo, bar and baz", Description: "Gamma is restricted to foo, bar and baz",
Type: "string", Type: "string",
Enum: []apiextensionsv1beta1.JSON{ Enum: []apiextensionsv1.JSON{
{ {
Raw: []byte(`"foo"`), Raw: []byte(`"foo"`),
}, },
@ -123,73 +123,29 @@ func newNoxuValidationCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiexten
}, },
}, },
}, },
"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),
},
},
},
}, },
} }
validationSchemaWithDescription := validationSchema.DeepCopy() validationSchemaWithDescription := validationSchema.DeepCopy()
validationSchemaWithDescription.Description = "test" validationSchemaWithDescription.Description = "test"
return []*apiextensionsv1beta1.CustomResourceDefinition{ return []*apiextensionsv1.CustomResourceDefinition{
{ {
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com", Group: "mygroup.example.com",
Version: "v1beta1", Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "noxus", Plural: "noxus",
Singular: "nonenglishnoxu", Singular: "nonenglishnoxu",
Kind: "WishIHadChosenNoxu", Kind: "WishIHadChosenNoxu",
ShortNames: []string{"foo", "bar", "abc", "def"}, ShortNames: []string{"foo", "bar", "abc", "def"},
ListKind: "NoxuItemList", ListKind: "NoxuItemList",
}, },
Scope: apiextensionsv1beta1.NamespaceScoped, Scope: apiextensionsv1.NamespaceScoped,
Validation: &apiextensionsv1beta1.CustomResourceValidation{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
OpenAPIV3Schema: validationSchema,
},
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: true, Storage: true,
}, Schema: &apiextensionsv1.CustomResourceValidation{
{
Name: "v1",
Served: true,
Storage: false,
},
},
},
},
{
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,
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
{
Name: "v1beta1",
Served: true,
Storage: true,
Schema: &apiextensionsv1beta1.CustomResourceValidation{
OpenAPIV3Schema: validationSchema, OpenAPIV3Schema: validationSchema,
}, },
}, },
@ -197,7 +153,39 @@ func newNoxuValidationCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiexten
Name: "v1", Name: "v1",
Served: true, Served: true,
Storage: false, Storage: false,
Schema: &apiextensionsv1beta1.CustomResourceValidation{ Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: validationSchema,
},
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com",
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "noxus",
Singular: "nonenglishnoxu",
Kind: "WishIHadChosenNoxu",
ShortNames: []string{"foo", "bar", "abc", "def"},
ListKind: "NoxuItemList",
},
Scope: apiextensionsv1.NamespaceScoped,
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1beta1",
Served: true,
Storage: true,
Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: validationSchema,
},
},
{
Name: "v1",
Served: true,
Storage: false,
Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: validationSchemaWithDescription, OpenAPIV3Schema: validationSchemaWithDescription,
}, },
}, },
@ -231,9 +219,9 @@ func TestCustomResourceValidation(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := newNoxuValidationCRDs()
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -249,7 +237,7 @@ func TestCustomResourceValidation(t *testing.T) {
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -436,9 +424,9 @@ func TestCustomResourceUpdateValidation(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := newNoxuValidationCRDs()
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -476,7 +464,7 @@ func TestCustomResourceUpdateValidation(t *testing.T) {
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -489,9 +477,9 @@ func TestCustomResourceValidationErrors(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := newNoxuValidationCRDs()
for _, noxuDefinition := range noxuDefinitions { for _, noxuDefinition := range noxuDefinitions {
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -530,18 +518,6 @@ func TestCustomResourceValidationErrors(t *testing.T) {
}, },
expectedErrors: []string{`gamma: Unsupported value: "qux": supported values: "foo", "bar", "baz"`}, expectedErrors: []string{`gamma: Unsupported value: "qux": supported values: "foo", "bar", "baz"`},
}, },
{
name: "bad delta",
instanceFn: func() *unstructured.Unstructured {
instance := newNoxuValidationInstance(ns, "foo")
instance.Object["delta"] = "foobarbaz"
return instance
},
expectedErrors: []string{
"must validate at least one schema (anyOf)",
"delta in body should be at most 5 chars long",
},
},
{ {
name: "absent alpha and beta", name: "absent alpha and beta",
instanceFn: func() *unstructured.Unstructured { instanceFn: func() *unstructured.Unstructured {
@ -580,7 +556,7 @@ func TestCustomResourceValidationErrors(t *testing.T) {
} }
} }
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -593,26 +569,27 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := newNoxuValidationCRDs()
for i, noxuDefinition := range noxuDefinitions { for i, noxuDefinition := range noxuDefinitions {
for _, v := range noxuDefinition.Spec.Versions { for _, v := range noxuDefinition.Spec.Versions {
// Re-define the CRD to make sure we start with a clean CRD // Re-define the CRD to make sure we start with a clean CRD
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[i] noxuDefinition := newNoxuValidationCRDs()[i]
validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name) validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// set stricter schema // set stricter schema
validationSchema.OpenAPIV3Schema.Required = []string{"alpha", "beta", "epsilon"} validationSchema.OpenAPIV3Schema.Required = []string{"alpha", "beta", "gamma"}
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
ns := "not-the-default" ns := "not-the-default"
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name) noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
instanceToCreate := newNoxuValidationInstance(ns, "foo") instanceToCreate := newNoxuValidationInstance(ns, "foo")
unstructured.RemoveNestedField(instanceToCreate.Object, "gamma")
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name) instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
// CR is rejected // CR is rejected
@ -622,7 +599,7 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
} }
// update the CRD to a less stricter schema // update the CRD to a less stricter schema
_, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) { _, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1.CustomResourceDefinition) {
validationSchema, err := getSchemaForVersion(crd, v.Name) validationSchema, err := getSchemaForVersion(crd, v.Name)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -650,7 +627,7 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}) noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{})
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -664,51 +641,59 @@ func TestForbiddenFieldsInSchema(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped) noxuDefinitions := newNoxuValidationCRDs()
for i, noxuDefinition := range noxuDefinitions { for i, noxuDefinition := range noxuDefinitions {
for _, v := range noxuDefinition.Spec.Versions { for _, v := range noxuDefinition.Spec.Versions {
// Re-define the CRD to make sure we start with a clean CRD // Re-define the CRD to make sure we start with a clean CRD
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[i] noxuDefinition := newNoxuValidationCRDs()[i]
validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name) validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
validationSchema.OpenAPIV3Schema.AdditionalProperties.Allows = false existingProperties := validationSchema.OpenAPIV3Schema.Properties
validationSchema.OpenAPIV3Schema.Properties = nil
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) validationSchema.OpenAPIV3Schema.AdditionalProperties = &apiextensionsv1.JSONSchemaPropsOrBool{Allows: false}
_, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err == nil { if err == nil {
t.Fatalf("unexpected non-error: additionalProperties cannot be set to false") t.Fatalf("unexpected non-error: additionalProperties cannot be set to false")
} }
// reset
validationSchema.OpenAPIV3Schema.Properties = existingProperties
validationSchema.OpenAPIV3Schema.AdditionalProperties = nil
validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{ validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1.JSONSchemaProps{
Type: "array", Type: "array",
UniqueItems: true, UniqueItems: true,
AdditionalProperties: &apiextensionsv1.JSONSchemaPropsOrBool{
Allows: true,
},
} }
validationSchema.OpenAPIV3Schema.AdditionalProperties.Allows = true _, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err == nil { if err == nil {
t.Fatalf("unexpected non-error: uniqueItems cannot be set to true") t.Fatalf("unexpected non-error: uniqueItems cannot be set to true")
} }
validationSchema.OpenAPIV3Schema.Ref = strPtr("#/definition/zeta") validationSchema.OpenAPIV3Schema.Ref = strPtr("#/definition/zeta")
validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{ validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1.JSONSchemaProps{
Type: "array", Type: "array",
UniqueItems: false, UniqueItems: false,
Items: &apiextensionsv1.JSONSchemaPropsOrArray{
Schema: &apiextensionsv1.JSONSchemaProps{Type: "object"},
},
} }
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) _, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err == nil { if err == nil {
t.Fatal("unexpected non-error: $ref cannot be non-empty string") t.Fatal("unexpected non-error: $ref cannot be non-empty string")
} }
validationSchema.OpenAPIV3Schema.Ref = nil validationSchema.OpenAPIV3Schema.Ref = nil
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil { if err := fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -716,7 +701,7 @@ func TestForbiddenFieldsInSchema(t *testing.T) {
} }
func TestNonStructuralSchemaConditionUpdate(t *testing.T) { func TestNonStructuralSchemaConditionUpdate(t *testing.T) {
tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t) tearDown, apiExtensionClient, _, etcdclient, etcdStoragePrefix, err := fixtures.StartDefaultServerWithClientsAndEtcd(t)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -752,27 +737,37 @@ spec:
if err != nil { if err != nil {
t.Fatalf("failed decoding of: %v\n\n%s", err, manifest) t.Fatalf("failed decoding of: %v\n\n%s", err, manifest)
} }
crd := obj.(*apiextensionsv1beta1.CustomResourceDefinition) betaCRD := obj.(*apiextensionsv1beta1.CustomResourceDefinition)
name := crd.Name name := betaCRD.Name
// save schema for later // save schema for later
origSchema := crd.Spec.Validation.OpenAPIV3Schema origSchema := &apiextensionsv1.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensionsv1.JSONSchemaProps{
"a": {
Type: "object",
},
},
}
// create CRDs // create CRDs. We cannot create these in v1, but they can exist in upgraded clusters
t.Logf("Creating CRD %s", crd.Name) t.Logf("Creating CRD %s", betaCRD.Name)
if _, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(context.TODO(), crd, metav1.CreateOptions{}); err != nil { ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceNone)
t.Fatalf("unexpected create error: %v", err) key := path.Join("/", etcdStoragePrefix, "apiextensions.k8s.io", "customresourcedefinitions/foos.tests.example.com")
val, _ := json.Marshal(betaCRD)
if _, err := etcdclient.Put(ctx, key, string(val)); err != nil {
t.Fatalf("unexpected error: %v", err)
} }
// wait for condition with violations // wait for condition with violations
t.Log("Waiting for NonStructuralSchema condition") t.Log("Waiting for NonStructuralSchema condition")
var cond *apiextensionsv1beta1.CustomResourceDefinitionCondition var cond *apiextensionsv1.CustomResourceDefinitionCondition
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) { err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema) cond = findCRDCondition(obj, apiextensionsv1.NonStructuralSchema)
return cond != nil, nil return cond != nil, nil
}) })
if err != nil { if err != nil {
@ -785,34 +780,32 @@ spec:
t.Fatalf("expected violation %q, but got: %v", v, cond.Message) t.Fatalf("expected violation %q, but got: %v", v, cond.Message)
} }
// remove schema t.Log("fix schema")
t.Log("Remove schema")
for retry := 0; retry < 5; retry++ { for retry := 0; retry < 5; retry++ {
// This patch fixes two fields to resolve crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
// 1. property type validation error if err != nil {
// 2. preserveUnknownFields validation error t.Fatal(err)
patch := []byte("[{\"op\":\"add\",\"path\":\"/spec/validation/openAPIV3Schema/properties/a/type\",\"value\":\"int\"}," + }
"{\"op\":\"replace\",\"path\":\"/spec/preserveUnknownFields\",\"value\":false}]") crd.Spec.Versions[0].Schema = fixtures.AllowAllSchema()
if _, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(context.TODO(), name, types.JSONPatchType, patch, metav1.PatchOptions{}); apierrors.IsConflict(err) { crd.Spec.PreserveUnknownFields = false
_, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
if apierrors.IsConflict(err) {
continue continue
} }
if err != nil { if err != nil {
t.Fatalf("unexpected update error: %v", err) t.Fatal(err)
} }
break break
} }
if err != nil {
t.Fatalf("unexpected update error: %v", err)
}
// wait for condition to go away // wait for condition to go away
t.Log("Wait for condition to disappear") t.Log("Wait for condition to disappear")
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) { err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema) cond = findCRDCondition(obj, apiextensionsv1.NonStructuralSchema)
return cond == nil, nil return cond == nil, nil
}) })
if err != nil { if err != nil {
@ -822,43 +815,23 @@ spec:
// re-add schema // re-add schema
t.Log("Re-add schema") t.Log("Re-add schema")
for retry := 0; retry < 5; retry++ { for retry := 0; retry < 5; retry++ {
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{}) crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatalf("unexpected get error: %v", err) t.Fatalf("unexpected get error: %v", err)
} }
crd.Spec.PreserveUnknownFields = nil crd.Spec.PreserveUnknownFields = true
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{OpenAPIV3Schema: origSchema} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{OpenAPIV3Schema: origSchema}
if _, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}); apierrors.IsConflict(err) { if _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}); apierrors.IsConflict(err) {
continue continue
} }
if err != nil { if err == nil {
t.Fatalf("unexpected update error: %v", err) t.Fatalf("missing error")
}
if !strings.Contains(err.Error(), "spec.preserveUnknownFields") {
t.Fatal(err)
} }
break break
} }
if err != nil {
t.Fatalf("unexpected update error: %v", err)
}
// wait for condition with violations
t.Log("Wait for condition to reappear")
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, err
}
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema)
return cond != nil, nil
})
if err != nil {
t.Fatalf("unexpected error waiting for NonStructuralSchema condition: %v", cond)
}
if v := "spec.versions[0].schema.openAPIV3Schema.properties[a].type: Required value: must not be empty for specified object fields"; !strings.Contains(cond.Message, v) {
t.Fatalf("expected violation %q, but got: %v", v, cond.Message)
}
if v := "spec.preserveUnknownFields: Invalid value: true: must be false"; !strings.Contains(cond.Message, v) {
t.Fatalf("expected violation %q, but got: %v", v, cond.Message)
}
} }
func TestNonStructuralSchemaCondition(t *testing.T) { func TestNonStructuralSchemaCondition(t *testing.T) {
@ -1657,13 +1630,13 @@ properties:
if len(tst.expectedViolations) == 0 { if len(tst.expectedViolations) == 0 {
// wait for condition to not appear // wait for condition to not appear
var cond *apiextensionsv1beta1.CustomResourceDefinitionCondition var cond *apiextensionsv1.CustomResourceDefinitionCondition
err := wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) { err := wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema) cond = findCRDCondition(obj, apiextensionsv1.NonStructuralSchema)
if cond == nil { if cond == nil {
return false, nil return false, nil
} }
@ -1676,13 +1649,13 @@ properties:
} }
// wait for condition to appear with the given violations // wait for condition to appear with the given violations
var cond *apiextensionsv1beta1.CustomResourceDefinitionCondition var cond *apiextensionsv1.CustomResourceDefinitionCondition
err = wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { err = wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema) cond = findCRDCondition(obj, apiextensionsv1.NonStructuralSchema)
if cond != nil { if cond != nil {
return true, nil return true, nil
} }
@ -1696,7 +1669,7 @@ properties:
if cond.Reason != "Violations" { if cond.Reason != "Violations" {
t.Errorf("expected reason Violations, got: %v", cond.Reason) t.Errorf("expected reason Violations, got: %v", cond.Reason)
} }
if cond.Status != apiextensionsv1beta1.ConditionTrue { if cond.Status != apiextensionsv1.ConditionTrue {
t.Errorf("expected reason True, got: %v", cond.Status) t.Errorf("expected reason True, got: %v", cond.Status)
} }
@ -1717,7 +1690,7 @@ properties:
} }
// findCRDCondition returns the condition you're looking for or nil. // findCRDCondition returns the condition you're looking for or nil.
func findCRDCondition(crd *apiextensionsv1beta1.CustomResourceDefinition, conditionType apiextensionsv1beta1.CustomResourceDefinitionConditionType) *apiextensionsv1beta1.CustomResourceDefinitionCondition { func findCRDCondition(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType) *apiextensionsv1.CustomResourceDefinitionCondition {
for i := range crd.Status.Conditions { for i := range crd.Status.Conditions {
if crd.Status.Conditions[i].Type == conditionType { if crd.Status.Conditions[i].Type == conditionType {
return &crd.Status.Conditions[i] return &crd.Status.Conditions[i]
@ -1742,10 +1715,6 @@ func float64Ptr(f float64) *float64 {
return &f return &f
} }
func int64Ptr(f int64) *int64 {
return &f
}
func strPtr(str string) *string { func strPtr(str string) *string {
return &str return &str
} }

View File

@ -25,7 +25,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -40,13 +40,13 @@ func TestInternalVersionIsHandlerVersion(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1.NamespaceScoped)
assert.Equal(t, "v1beta1", noxuDefinition.Spec.Versions[0].Name) assert.Equal(t, "v1beta1", noxuDefinition.Spec.Versions[0].Name)
assert.Equal(t, "v1beta2", noxuDefinition.Spec.Versions[1].Name) assert.Equal(t, "v1beta2", noxuDefinition.Spec.Versions[1].Name)
assert.True(t, noxuDefinition.Spec.Versions[1].Storage) assert.True(t, noxuDefinition.Spec.Versions[1].Storage)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -64,16 +64,20 @@ func TestInternalVersionIsHandlerVersion(t *testing.T) {
// update validation via update because the cache priming in CreateNewCustomResourceDefinition will fail otherwise // update validation via update because the cache priming in CreateNewCustomResourceDefinition will fail otherwise
t.Logf("Updating CRD to validate apiVersion") t.Logf("Updating CRD to validate apiVersion")
noxuDefinition, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, noxuDefinition.Name, func(crd *apiextensionsv1beta1.CustomResourceDefinition) { noxuDefinition, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, noxuDefinition.Name, func(crd *apiextensionsv1.CustomResourceDefinition) {
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{ for i := range crd.Spec.Versions {
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ crd.Spec.Versions[i].Schema = &apiextensionsv1.CustomResourceValidation{
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
"apiVersion": { Type: "object",
Pattern: "^mygroup.example.com/v1beta1$", // this means we can only patch via the v1beta1 handler version Properties: map[string]apiextensionsv1.JSONSchemaProps{
"apiVersion": {
Pattern: "^mygroup.example.com/v1beta1$", // this means we can only patch via the v1beta1 handler version
Type: "string",
},
}, },
Required: []string{"apiVersion"},
}, },
Required: []string{"apiVersion"}, }
},
} }
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -134,8 +138,8 @@ func TestVersionedNamespacedScopedCRD(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1.NamespaceScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -151,8 +155,8 @@ func TestVersionedClusterScopedCRD(t *testing.T) {
} }
defer tearDown() defer tearDown()
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -162,40 +166,44 @@ func TestVersionedClusterScopedCRD(t *testing.T) {
} }
func TestStoragedVersionInNamespacedCRDStatus(t *testing.T) { func TestStoragedVersionInNamespacedCRDStatus(t *testing.T) {
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.NamespaceScoped) noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1.NamespaceScoped)
ns := "not-the-default" ns := "not-the-default"
testStoragedVersionInCRDStatus(t, ns, noxuDefinition) testStoragedVersionInCRDStatus(t, ns, noxuDefinition)
} }
func TestStoragedVersionInClusterScopedCRDStatus(t *testing.T) { func TestStoragedVersionInClusterScopedCRDStatus(t *testing.T) {
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1.ClusterScoped)
ns := "" ns := ""
testStoragedVersionInCRDStatus(t, ns, noxuDefinition) testStoragedVersionInCRDStatus(t, ns, noxuDefinition)
} }
func testStoragedVersionInCRDStatus(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) { func testStoragedVersionInCRDStatus(t *testing.T, ns string, noxuDefinition *apiextensionsv1.CustomResourceDefinition) {
versionsV1Beta1Storage := []apiextensionsv1beta1.CustomResourceDefinitionVersion{ versionsV1Beta1Storage := []apiextensionsv1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: true, Storage: true,
Schema: fixtures.AllowAllSchema(),
}, },
{ {
Name: "v1beta2", Name: "v1beta2",
Served: true, Served: true,
Storage: false, Storage: false,
Schema: fixtures.AllowAllSchema(),
}, },
} }
versionsV1Beta2Storage := []apiextensionsv1beta1.CustomResourceDefinitionVersion{ versionsV1Beta2Storage := []apiextensionsv1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: false, Storage: false,
Schema: fixtures.AllowAllSchema(),
}, },
{ {
Name: "v1beta2", Name: "v1beta2",
Served: true, Served: true,
Storage: true, Storage: true,
Schema: fixtures.AllowAllSchema(),
}, },
} }
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t) tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
@ -205,13 +213,13 @@ func testStoragedVersionInCRDStatus(t *testing.T, ns string, noxuDefinition *api
defer tearDown() defer tearDown()
noxuDefinition.Spec.Versions = versionsV1Beta1Storage noxuDefinition.Spec.Versions = versionsV1Beta1Storage
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// The storage version list should be initilized to storage version // The storage version list should be initilized to storage version
crd, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}) crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -221,11 +229,11 @@ func testStoragedVersionInCRDStatus(t *testing.T, ns string, noxuDefinition *api
// Changing CRD storage version should be reflected immediately // Changing CRD storage version should be reflected immediately
crd.Spec.Versions = versionsV1Beta2Storage crd.Spec.Versions = versionsV1Beta2Storage
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{}) crd, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuDefinition.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -233,7 +241,7 @@ func testStoragedVersionInCRDStatus(t *testing.T, ns string, noxuDefinition *api
t.Errorf("expected %v, got %v", e, a) t.Errorf("expected %v, got %v", e, a)
} }
err = fixtures.DeleteCustomResourceDefinition(crd, apiExtensionClient) err = fixtures.DeleteV1CustomResourceDefinition(crd, apiExtensionClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -30,7 +30,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
) )
@ -51,15 +51,15 @@ func TestYAML(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
kind := noxuDefinition.Spec.Names.Kind kind := noxuDefinition.Spec.Names.Kind
listKind := noxuDefinition.Spec.Names.ListKind listKind := noxuDefinition.Spec.Names.ListKind
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
rest := apiExtensionClient.Discovery().RESTClient() rest := apiExtensionClient.Discovery().RESTClient()
@ -67,7 +67,7 @@ func TestYAML(t *testing.T) {
{ {
result, err := rest.Get(). result, err := rest.Get().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name).
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
t.Fatal(err, string(result)) t.Fatal(err, string(result))
@ -88,7 +88,7 @@ func TestYAML(t *testing.T) {
{ {
result, err := rest.Get(). result, err := rest.Get().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "missingname"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "missingname").
DoRaw(context.TODO()) DoRaw(context.TODO())
if !errors.IsNotFound(err) { if !errors.IsNotFound(err) {
t.Fatalf("expected not found, got %v", err) t.Fatalf("expected not found, got %v", err)
@ -123,7 +123,7 @@ values:
result, err := rest.Post(). result, err := rest.Post().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
@ -159,7 +159,7 @@ values:
{ {
result, err := rest.Get(). result, err := rest.Get().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -192,7 +192,7 @@ values:
{ {
result, err := rest.Get(). result, err := rest.Get().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
t.Fatal(err, string(result)) t.Fatal(err, string(result))
@ -236,7 +236,7 @@ values:
{ {
result, err := rest.Get(). result, err := rest.Get().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Param("watch", "true"). Param("watch", "true").
DoRaw(context.TODO()) DoRaw(context.TODO())
if !errors.IsNotAcceptable(err) { if !errors.IsNotAcceptable(err) {
@ -273,7 +273,7 @@ values:
result, err := rest.Put(). result, err := rest.Put().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
@ -314,7 +314,7 @@ values:
result, err := rest.Patch(types.MergePatchType). result, err := rest.Patch(types.MergePatchType).
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if !errors.IsUnsupportedMediaType(err) { if !errors.IsUnsupportedMediaType(err) {
@ -336,7 +336,7 @@ values:
{ {
result, err := rest.Delete(). result, err := rest.Delete().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
t.Fatal(err, string(result)) t.Fatal(err, string(result))
@ -370,14 +370,14 @@ func TestYAMLSubresource(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.ClusterScoped)[0] noxuDefinition := NewNoxuSubresourcesCRDs(apiextensionsv1.ClusterScoped)[0]
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
kind := noxuDefinition.Spec.Names.Kind kind := noxuDefinition.Spec.Names.Kind
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
rest := apiExtensionClient.Discovery().RESTClient() rest := apiExtensionClient.Discovery().RESTClient()
@ -397,7 +397,7 @@ spec:
result, err := rest.Post(). result, err := rest.Post().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
@ -427,7 +427,7 @@ spec:
{ {
result, err := rest.Get(). result, err := rest.Get().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest", "status"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest", "status").
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -466,7 +466,7 @@ status:
result, err := rest.Put(). result, err := rest.Put().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
SetHeader("Content-Type", "application/yaml"). SetHeader("Content-Type", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest", "status"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest", "status").
Body(yamlBody). Body(yamlBody).
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
@ -500,7 +500,7 @@ status:
{ {
result, err := rest.Get(). result, err := rest.Get().
SetHeader("Accept", "application/yaml"). SetHeader("Accept", "application/yaml").
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural, "mytest", "scale"). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest", "scale").
DoRaw(context.TODO()) DoRaw(context.TODO())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -34,7 +34,7 @@ import (
apps "k8s.io/api/apps/v1" apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -798,28 +798,35 @@ func TestMetadataClient(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
fooCRD := &apiextensionsv1beta1.CustomResourceDefinition{ fooCRD := &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "foos.cr.bar.com", Name: "foos.cr.bar.com",
}, },
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "cr.bar.com", Group: "cr.bar.com",
Version: "v1", Scope: apiextensionsv1.NamespaceScoped,
Scope: apiextensionsv1beta1.NamespaceScoped, Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
Kind: "Foo", Kind: "Foo",
}, },
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}, {
Name: "v1",
Served: true,
Storage: true,
Schema: fixtures.AllowAllSchema(),
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
},
}, },
}, },
} }
fooCRD, err = fixtures.CreateNewCustomResourceDefinition(fooCRD, apiExtensionClient, dynamicClient) fooCRD, err = fixtures.CreateNewV1CustomResourceDefinition(fooCRD, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crdGVR := schema.GroupVersionResource{Group: fooCRD.Spec.Group, Version: fooCRD.Spec.Version, Resource: "foos"} crdGVR := schema.GroupVersionResource{Group: fooCRD.Spec.Group, Version: fooCRD.Spec.Versions[0].Name, Resource: "foos"}
testcases := []struct { testcases := []struct {
name string name string
@ -1119,26 +1126,33 @@ func TestAPICRDProtobuf(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
fooCRD := &apiextensionsv1beta1.CustomResourceDefinition{ fooCRD := &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "foos.cr.bar.com", Name: "foos.cr.bar.com",
}, },
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "cr.bar.com", Group: "cr.bar.com",
Version: "v1", Scope: apiextensionsv1.NamespaceScoped,
Scope: apiextensionsv1beta1.NamespaceScoped, Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
Kind: "Foo", Kind: "Foo",
}, },
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}}, Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: fixtures.AllowAllSchema(),
Subresources: &apiextensionsv1.CustomResourceSubresources{Status: &apiextensionsv1.CustomResourceSubresourceStatus{}},
},
},
}, },
} }
fooCRD, err = fixtures.CreateNewCustomResourceDefinition(fooCRD, apiExtensionClient, dynamicClient) fooCRD, err = fixtures.CreateNewV1CustomResourceDefinition(fooCRD, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crdGVR := schema.GroupVersionResource{Group: fooCRD.Spec.Group, Version: fooCRD.Spec.Version, Resource: "foos"} crdGVR := schema.GroupVersionResource{Group: fooCRD.Spec.Group, Version: fooCRD.Spec.Versions[0].Name, Resource: "foos"}
crclient := dynamicClient.Resource(crdGVR).Namespace(testNamespace) crclient := dynamicClient.Resource(crdGVR).Namespace(testNamespace)
testcases := []struct { testcases := []struct {
@ -1331,25 +1345,32 @@ func TestTransform(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
fooCRD := &apiextensionsv1beta1.CustomResourceDefinition{ fooCRD := &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "foos.cr.bar.com", Name: "foos.cr.bar.com",
}, },
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "cr.bar.com", Group: "cr.bar.com",
Version: "v1", Scope: apiextensionsv1.NamespaceScoped,
Scope: apiextensionsv1beta1.NamespaceScoped, Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
Kind: "Foo", Kind: "Foo",
}, },
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: fixtures.AllowAllSchema(),
},
},
}, },
} }
fooCRD, err = fixtures.CreateNewCustomResourceDefinition(fooCRD, apiExtensionClient, dynamicClient) fooCRD, err = fixtures.CreateNewV1CustomResourceDefinition(fooCRD, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
crdGVR := schema.GroupVersionResource{Group: fooCRD.Spec.Group, Version: fooCRD.Spec.Version, Resource: "foos"} crdGVR := schema.GroupVersionResource{Group: fooCRD.Spec.Group, Version: fooCRD.Spec.Versions[0].Name, Resource: "foos"}
crclient := dynamicClient.Resource(crdGVR).Namespace(testNamespace) crclient := dynamicClient.Resource(crdGVR).Namespace(testNamespace)
testcases := []struct { testcases := []struct {

View File

@ -0,0 +1,234 @@
/*
Copyright 2021 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 apiserver
import (
"context"
"fmt"
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
genericfeatures "k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/dynamic"
featuregatetesting "k8s.io/component-base/featuregate/testing"
apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/test/integration/framework"
)
// TestApplyCRDNoSchema tests that CRDs and CRs can both be applied to with a PATCH request with the apply content type
// when there is no validation field provided.
func TestApplyCRDNoSchema(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd())
if err != nil {
t.Fatal(err)
}
defer server.TearDownFn()
config := server.ClientConfig
apiExtensionClient, err := clientset.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
noxuDefinition := nearlyRemovedBetaMultipleVersionNoxuCRD(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
name := "mytest"
rest := apiExtensionClient.Discovery().RESTClient()
yamlBody := []byte(fmt.Sprintf(`
apiVersion: %s
kind: %s
metadata:
name: %s
spec:
replicas: 1`, apiVersion, kind, name))
result, err := rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("fieldManager", "apply_test").
Body(yamlBody).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
}
verifyReplicas(t, result, 1)
// Patch object to change the number of replicas
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Body([]byte(`{"spec":{"replicas": 5}}`)).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
}
verifyReplicas(t, result, 5)
// Re-apply, we should get conflicts now, since the number of replicas was changed.
result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("fieldManager", "apply_test").
Body(yamlBody).
DoRaw(context.TODO())
if err == nil {
t.Fatalf("Expecting to get conflicts when applying object after updating replicas, got no error: %s", result)
}
status, ok := err.(*apierrors.StatusError)
if !ok {
t.Fatalf("Expecting to get conflicts as API error")
}
if len(status.Status().Details.Causes) != 1 {
t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
}
// Re-apply with force, should work fine.
result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("force", "true").
Param("fieldManager", "apply_test").
Body(yamlBody).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
}
verifyReplicas(t, result, 1)
// Try to set managed fields using a subresource and verify that it has no effect
existingManagedFields, err := getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
updateBytes := []byte(`{
"metadata": {
"managedFields": [{
"manager":"testing",
"operation":"Update",
"apiVersion":"v1",
"fieldsType":"FieldsV1",
"fieldsV1":{
"f:spec":{
"f:containers":{
"k:{\"name\":\"testing\"}":{
".":{},
"f:image":{},
"f:name":{}
}
}
}
}
}]
}
}`)
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
SubResource("status").
Name(name).
Param("fieldManager", "subresource_test").
Body(updateBytes).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("Error updating subresource: %v ", err)
}
newManagedFields, err := getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
if !reflect.DeepEqual(existingManagedFields, newManagedFields) {
t.Fatalf("Expected managed fields to not have changed when trying manually settting them via subresoures.\n\nExpected: %#v\n\nGot: %#v", existingManagedFields, newManagedFields)
}
// However, it is possible to modify managed fields using the main resource
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("fieldManager", "subresource_test").
Body([]byte(`{"metadata":{"managedFields":[{}]}}`)).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("Error updating managed fields of the main resource: %v ", err)
}
newManagedFields, err = getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
if len(newManagedFields) != 0 {
t.Fatalf("Expected managed fields to have been reset, but got: %v", newManagedFields)
}
}
func nearlyRemovedBetaMultipleVersionNoxuCRD(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,
},
},
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
},
},
}
}

View File

@ -20,8 +20,18 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"path"
"reflect" "reflect"
"testing" "testing"
"time"
"k8s.io/apimachinery/pkg/util/wait"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"go.etcd.io/etcd/clientv3"
"go.etcd.io/etcd/pkg/transport"
"google.golang.org/grpc"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
@ -39,163 +49,6 @@ import (
"k8s.io/kubernetes/test/integration/framework" "k8s.io/kubernetes/test/integration/framework"
) )
// TestApplyCRDNoSchema tests that CRDs and CRs can both be applied to with a PATCH request with the apply content type
// when there is no validation field provided.
func TestApplyCRDNoSchema(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd())
if err != nil {
t.Fatal(err)
}
defer server.TearDownFn()
config := server.ClientConfig
apiExtensionClient, err := clientset.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(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
name := "mytest"
rest := apiExtensionClient.Discovery().RESTClient()
yamlBody := []byte(fmt.Sprintf(`
apiVersion: %s
kind: %s
metadata:
name: %s
spec:
replicas: 1`, apiVersion, kind, name))
result, err := rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("fieldManager", "apply_test").
Body(yamlBody).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
}
verifyReplicas(t, result, 1)
// Patch object to change the number of replicas
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Body([]byte(`{"spec":{"replicas": 5}}`)).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
}
verifyReplicas(t, result, 5)
// Re-apply, we should get conflicts now, since the number of replicas was changed.
result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("fieldManager", "apply_test").
Body(yamlBody).
DoRaw(context.TODO())
if err == nil {
t.Fatalf("Expecting to get conflicts when applying object after updating replicas, got no error: %s", result)
}
status, ok := err.(*apierrors.StatusError)
if !ok {
t.Fatalf("Expecting to get conflicts as API error")
}
if len(status.Status().Details.Causes) != 1 {
t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
}
// Re-apply with force, should work fine.
result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("force", "true").
Param("fieldManager", "apply_test").
Body(yamlBody).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
}
verifyReplicas(t, result, 1)
// Try to set managed fields using a subresource and verify that it has no effect
existingManagedFields, err := getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
updateBytes := []byte(`{
"metadata": {
"managedFields": [{
"manager":"testing",
"operation":"Update",
"apiVersion":"v1",
"fieldsType":"FieldsV1",
"fieldsV1":{
"f:spec":{
"f:containers":{
"k:{\"name\":\"testing\"}":{
".":{},
"f:image":{},
"f:name":{}
}
}
}
}
}]
}
}`)
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
SubResource("status").
Name(name).
Param("fieldManager", "subresource_test").
Body(updateBytes).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("Error updating subresource: %v ", err)
}
newManagedFields, err := getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
if !reflect.DeepEqual(existingManagedFields, newManagedFields) {
t.Fatalf("Expected managed fields to not have changed when trying manually settting them via subresoures.\n\nExpected: %#v\n\nGot: %#v", existingManagedFields, newManagedFields)
}
// However, it is possible to modify managed fields using the main resource
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("fieldManager", "subresource_test").
Body([]byte(`{"metadata":{"managedFields":[{}]}}`)).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("Error updating managed fields of the main resource: %v ", err)
}
newManagedFields, err = getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
if len(newManagedFields) != 0 {
t.Fatalf("Expected managed fields to have been reset, but got: %v", newManagedFields)
}
}
// TestApplyCRDStructuralSchema tests that when a CRD has a structural schema in its validation field, // TestApplyCRDStructuralSchema tests that when a CRD has a structural schema in its validation field,
// it will be used to construct the CR schema used by apply. // it will be used to construct the CR schema used by apply.
func TestApplyCRDStructuralSchema(t *testing.T) { func TestApplyCRDStructuralSchema(t *testing.T) {
@ -217,9 +70,9 @@ func TestApplyCRDStructuralSchema(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1beta1.ClusterScoped) noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1.ClusterScoped)
var c apiextensionsv1beta1.CustomResourceValidation var c apiextensionsv1.CustomResourceValidation
err = json.Unmarshal([]byte(`{ err = json.Unmarshal([]byte(`{
"openAPIV3Schema": { "openAPIV3Schema": {
"type": "object", "type": "object",
@ -274,17 +127,17 @@ func TestApplyCRDStructuralSchema(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition.Spec.Validation = &c for i := range noxuDefinition.Spec.Versions {
falseBool := false noxuDefinition.Spec.Versions[i].Schema = &c
noxuDefinition.Spec.PreserveUnknownFields = &falseBool }
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
kind := noxuDefinition.Spec.Names.Kind kind := noxuDefinition.Spec.Names.Kind
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
name := "mytest" name := "mytest"
rest := apiExtensionClient.Discovery().RESTClient() rest := apiExtensionClient.Discovery().RESTClient()
@ -303,7 +156,7 @@ spec:
containerPort: 80 containerPort: 80
protocol: TCP`, apiVersion, kind, name)) protocol: TCP`, apiVersion, kind, name))
result, err := rest.Patch(types.ApplyPatchType). result, err := rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
Body(yamlBody). Body(yamlBody).
@ -318,7 +171,7 @@ spec:
// Patch object to add another finalizer to the finalizers list // Patch object to add another finalizer to the finalizers list
result, err = rest.Patch(types.MergePatchType). result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Body([]byte(`{"metadata":{"finalizers":["test-finalizer","another-one"]}}`)). Body([]byte(`{"metadata":{"finalizers":["test-finalizer","another-one"]}}`)).
DoRaw(context.TODO()) DoRaw(context.TODO())
@ -331,7 +184,7 @@ spec:
// Re-apply the same config, should work fine, since finalizers should have the list-type extension 'set'. // Re-apply the same config, should work fine, since finalizers should have the list-type extension 'set'.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
SetHeader("Accept", "application/json"). SetHeader("Accept", "application/json").
@ -346,7 +199,7 @@ spec:
// Patch object to change the number of replicas // Patch object to change the number of replicas
result, err = rest.Patch(types.MergePatchType). result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Body([]byte(`{"spec":{"replicas": 5}}`)). Body([]byte(`{"spec":{"replicas": 5}}`)).
DoRaw(context.TODO()) DoRaw(context.TODO())
@ -357,7 +210,7 @@ spec:
// Re-apply, we should get conflicts now, since the number of replicas was changed. // Re-apply, we should get conflicts now, since the number of replicas was changed.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
Body(yamlBody). Body(yamlBody).
@ -375,7 +228,7 @@ spec:
// Re-apply with force, should work fine. // Re-apply with force, should work fine.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("force", "true"). Param("force", "true").
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
@ -388,7 +241,7 @@ spec:
// New applier tries to edit an existing list item, we should get conflicts. // New applier tries to edit an existing list item, we should get conflicts.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("fieldManager", "apply_test_2"). Param("fieldManager", "apply_test_2").
Body([]byte(fmt.Sprintf(` Body([]byte(fmt.Sprintf(`
@ -415,7 +268,7 @@ spec:
// New applier tries to add a new list item, should work fine. // New applier tries to add a new list item, should work fine.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("fieldManager", "apply_test_2"). Param("fieldManager", "apply_test_2").
Body([]byte(fmt.Sprintf(` Body([]byte(fmt.Sprintf(`
@ -459,7 +312,7 @@ spec:
"protocol": "TCP" "protocol": "TCP"
}`, apiVersion, kind, "should-not-exist")) }`, apiVersion, kind, "should-not-exist"))
_, err = rest.Put(). _, err = rest.Put().
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name("should-not-exist"). Name("should-not-exist").
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
Body(notExistingYAMLBody). Body(notExistingYAMLBody).
@ -552,12 +405,45 @@ func verifyNumPorts(t *testing.T, b []byte, n int) {
} }
} }
func findCRDCondition(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType) *apiextensionsv1.CustomResourceDefinitionCondition {
for i := range crd.Status.Conditions {
if crd.Status.Conditions[i].Type == conditionType {
return &crd.Status.Conditions[i]
}
}
return nil
}
// TestApplyCRDUnhandledSchema tests that when a CRD has a schema that kube-openapi ToProtoModels cannot handle correctly, // TestApplyCRDUnhandledSchema tests that when a CRD has a schema that kube-openapi ToProtoModels cannot handle correctly,
// apply falls back to non-schema behavior // apply falls back to non-schema behavior
func TestApplyCRDUnhandledSchema(t *testing.T) { func TestApplyCRDUnhandledSchema(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd()) storageConfig := framework.SharedEtcd()
tlsInfo := transport.TLSInfo{
CertFile: storageConfig.Transport.CertFile,
KeyFile: storageConfig.Transport.KeyFile,
TrustedCAFile: storageConfig.Transport.TrustedCAFile,
}
tlsConfig, err := tlsInfo.ClientConfig()
if err != nil {
t.Fatal(err)
}
etcdConfig := clientv3.Config{
Endpoints: storageConfig.Transport.ServerList,
DialTimeout: 20 * time.Second,
DialOptions: []grpc.DialOption{
grpc.WithBlock(), // block until the underlying connection is up
},
TLS: tlsConfig,
}
etcdclient, err := clientv3.New(etcdConfig)
if err != nil {
t.Fatal(err)
}
server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, storageConfig)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -568,12 +454,33 @@ func TestApplyCRDUnhandledSchema(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped) // this has to be v1beta1, so we can have an item with validation that does not match. v1 validation prevents this.
noxuBetaDefinition := &apiextensionsv1beta1.CustomResourceDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "CustomResourceDefinition",
APIVersion: "apiextensions.k8s.io/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: "mygroup.example.com",
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{{
Name: "v1beta1",
Served: true,
Storage: true,
}},
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "noxus",
Singular: "nonenglishnoxu",
Kind: "WishIHadChosenNoxu",
ShortNames: []string{"foo", "bar", "abc", "def"},
ListKind: "NoxuItemList",
Categories: []string{"all"},
},
Scope: apiextensionsv1beta1.ClusterScoped,
},
}
// This is a schema that kube-openapi ToProtoModels does not handle correctly. // This is a schema that kube-openapi ToProtoModels does not handle correctly.
// https://github.com/kubernetes/kubernetes/blob/38752f7f99869ed65fb44378360a517649dc2f83/vendor/k8s.io/kube-openapi/pkg/util/proto/document.go#L184 // https://github.com/kubernetes/kubernetes/blob/38752f7f99869ed65fb44378360a517649dc2f83/vendor/k8s.io/kube-openapi/pkg/util/proto/document.go#L184
@ -590,15 +497,44 @@ func TestApplyCRDUnhandledSchema(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
noxuDefinition.Spec.Validation = &c noxuBetaDefinition.Spec.Validation = &c
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient) betaBytes, err := json.Marshal(noxuBetaDefinition)
if err != nil {
t.Fatal(err)
}
t.Log(string(betaBytes))
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceNone)
key := path.Join("/", storageConfig.Prefix, "apiextensions.k8s.io", "customresourcedefinitions", noxuBetaDefinition.Name)
if _, err := etcdclient.Put(ctx, key, string(betaBytes)); err != nil {
t.Fatalf("unexpected error: %v", err)
}
noxuDefinition, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuBetaDefinition.Name, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
// wait until the CRD is established
err = wait.Poll(100*time.Millisecond, 10*time.Second, func() (bool, error) {
localCrd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuBetaDefinition.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
condition := findCRDCondition(localCrd, apiextensionsv1.Established)
if condition == nil {
return false, nil
}
if condition.Status == apiextensionsv1.ConditionTrue {
return true, nil
}
return false, nil
})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
kind := noxuDefinition.Spec.Names.Kind kind := noxuDefinition.Spec.Names.Kind
apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Version apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
name := "mytest" name := "mytest"
rest := apiExtensionClient.Discovery().RESTClient() rest := apiExtensionClient.Discovery().RESTClient()
@ -610,7 +546,7 @@ metadata:
spec: spec:
replicas: 1`, apiVersion, kind, name)) replicas: 1`, apiVersion, kind, name))
result, err := rest.Patch(types.ApplyPatchType). result, err := rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
Body(yamlBody). Body(yamlBody).
@ -622,7 +558,7 @@ spec:
// Patch object to change the number of replicas // Patch object to change the number of replicas
result, err = rest.Patch(types.MergePatchType). result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Body([]byte(`{"spec":{"replicas": 5}}`)). Body([]byte(`{"spec":{"replicas": 5}}`)).
DoRaw(context.TODO()) DoRaw(context.TODO())
@ -633,7 +569,7 @@ spec:
// Re-apply, we should get conflicts now, since the number of replicas was changed. // Re-apply, we should get conflicts now, since the number of replicas was changed.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").
Body(yamlBody). Body(yamlBody).
@ -651,7 +587,7 @@ spec:
// Re-apply with force, should work fine. // Re-apply with force, should work fine.
result, err = rest.Patch(types.ApplyPatchType). result, err = rest.Patch(types.ApplyPatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural). AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
Name(name). Name(name).
Param("force", "true"). Param("force", "true").
Param("fieldManager", "apply_test"). Param("fieldManager", "apply_test").

View File

@ -26,7 +26,7 @@ import (
"time" "time"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apiextensionstestserver "k8s.io/apiextensions-apiserver/test/integration/fixtures" apiextensionstestserver "k8s.io/apiextensions-apiserver/test/integration/fixtures"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -136,11 +136,11 @@ func newOwnerRC(name, namespace string) *v1.ReplicationController {
} }
} }
func newCRDInstance(definition *apiextensionsv1beta1.CustomResourceDefinition, namespace, name string) *unstructured.Unstructured { func newCRDInstance(definition *apiextensionsv1.CustomResourceDefinition, namespace, name string) *unstructured.Unstructured {
return &unstructured.Unstructured{ return &unstructured.Unstructured{
Object: map[string]interface{}{ Object: map[string]interface{}{
"kind": definition.Spec.Names.Kind, "kind": definition.Spec.Names.Kind,
"apiVersion": definition.Spec.Group + "/" + definition.Spec.Version, "apiVersion": definition.Spec.Group + "/" + definition.Spec.Versions[0].Name,
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": name, "name": name,
"namespace": namespace, "namespace": namespace,
@ -180,18 +180,18 @@ func createRandomCustomResourceDefinition(
t *testing.T, apiExtensionClient apiextensionsclientset.Interface, t *testing.T, apiExtensionClient apiextensionsclientset.Interface,
dynamicClient dynamic.Interface, dynamicClient dynamic.Interface,
namespace string, namespace string,
) (*apiextensionsv1beta1.CustomResourceDefinition, dynamic.ResourceInterface) { ) (*apiextensionsv1.CustomResourceDefinition, dynamic.ResourceInterface) {
// Create a random custom resource definition and ensure it's available for // Create a random custom resource definition and ensure it's available for
// use. // use.
definition := apiextensionstestserver.NewRandomNameCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) definition := apiextensionstestserver.NewRandomNameV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
definition, err := apiextensionstestserver.CreateNewCustomResourceDefinition(definition, apiExtensionClient, dynamicClient) definition, err := apiextensionstestserver.CreateNewV1CustomResourceDefinition(definition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatalf("failed to create CustomResourceDefinition: %v", err) t.Fatalf("failed to create CustomResourceDefinition: %v", err)
} }
// Get a client for the custom resource. // Get a client for the custom resource.
gvr := schema.GroupVersionResource{Group: definition.Spec.Group, Version: definition.Spec.Version, Resource: definition.Spec.Names.Plural} gvr := schema.GroupVersionResource{Group: definition.Spec.Group, Version: definition.Spec.Versions[0].Name, Resource: definition.Spec.Names.Plural}
resourceClient := dynamicClient.Resource(gvr).Namespace(namespace) resourceClient := dynamicClient.Resource(gvr).Namespace(namespace)
@ -1230,14 +1230,14 @@ func TestCRDDeletionCascading(t *testing.T) {
t.Logf("Second pass CRD cascading deletion") t.Logf("Second pass CRD cascading deletion")
accessor := meta.NewAccessor() accessor := meta.NewAccessor()
accessor.SetResourceVersion(definition, "") accessor.SetResourceVersion(definition, "")
_, err := apiextensionstestserver.CreateNewCustomResourceDefinition(definition, apiExtensionClient, dynamicClient) _, err := apiextensionstestserver.CreateNewV1CustomResourceDefinition(definition, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatalf("failed to create CustomResourceDefinition: %v", err) t.Fatalf("failed to create CustomResourceDefinition: %v", err)
} }
testCRDDeletion(t, ctx, ns, definition, resourceClient) testCRDDeletion(t, ctx, ns, definition, resourceClient)
} }
func testCRDDeletion(t *testing.T, ctx *testContext, ns *v1.Namespace, definition *apiextensionsv1beta1.CustomResourceDefinition, resourceClient dynamic.ResourceInterface) { func testCRDDeletion(t *testing.T, ctx *testContext, ns *v1.Namespace, definition *apiextensionsv1.CustomResourceDefinition, resourceClient dynamic.ResourceInterface) {
clientSet, apiExtensionClient := ctx.clientSet, ctx.apiExtensionClient clientSet, apiExtensionClient := ctx.clientSet, ctx.apiExtensionClient
configMapClient := clientSet.CoreV1().ConfigMaps(ns.Name) configMapClient := clientSet.CoreV1().ConfigMaps(ns.Name)
@ -1261,7 +1261,7 @@ func testCRDDeletion(t *testing.T, ctx *testContext, ns *v1.Namespace, definitio
time.Sleep(ctx.syncPeriod + 5*time.Second) time.Sleep(ctx.syncPeriod + 5*time.Second)
// Delete the definition, which should cascade to the owner and ultimately its dependents. // Delete the definition, which should cascade to the owner and ultimately its dependents.
if err := apiextensionstestserver.DeleteCustomResourceDefinition(definition, apiExtensionClient); err != nil { if err := apiextensionstestserver.DeleteV1CustomResourceDefinition(definition, apiExtensionClient); err != nil {
t.Fatalf("failed to delete %q: %v", definition.Name, err) t.Fatalf("failed to delete %q: %v", definition.Name, err)
} }

View File

@ -89,12 +89,7 @@ func CreateMultiVersionTestCRD(f *framework.Framework, group string, opts ...Opt
Served: true, Served: true,
Storage: true, Storage: true,
Name: "v1", Name: "v1",
Schema: &apiextensionsv1.CustomResourceValidation{ Schema: fixtures.AllowAllSchema(),
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
XPreserveUnknownFields: pointer.BoolPtr(true),
Type: "object",
},
},
}} }}
} }