Skip to content
Permalink
Browse files

Add CRD e2e tests for list, delete collection, and status sub-resourc…

…e operations
  • Loading branch information...
jpbetz committed Aug 9, 2019
1 parent f4e39af commit 472555152c4b3e1068ac5499dcb7a10d68f5198b
@@ -370,6 +370,29 @@ func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefi
return nil
}

// DeleteCustomResourceDefinitions deletes all CRD matching the provided deleteListOpts and waits until all the CRDs disappear from discovery.
func DeleteCustomResourceDefinitions(deleteListOpts metav1.ListOptions, apiExtensionsClient clientset.Interface) error {
list, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(deleteListOpts)
if err != nil {
return err
}
if err = apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().DeleteCollection(nil, deleteListOpts); err != nil {
return err
}
for _, crd := range list.Items {
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
}

// CreateNewVersionedScaleClient returns a scale client.
func CreateNewVersionedScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config, version string) (scale.ScalesGetter, error) {
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
@@ -57,6 +57,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
@@ -74,6 +75,7 @@ go_library(
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1:go_default_library",
"//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library",
@@ -17,14 +17,23 @@ limitations under the License.
package apimachinery

import (
"github.com/onsi/ginkgo"

"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/uuid"
utilversion "k8s.io/apimachinery/pkg/util/version"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/util/retry"
"k8s.io/kubernetes/test/e2e/framework"
e2elog "k8s.io/kubernetes/test/e2e/framework/log"

"github.com/onsi/ginkgo"
)

var crdVersion = utilversion.MustParseSemantic("v1.7.0")
@@ -42,29 +51,147 @@ var _ = SIGDescribe("CustomResourceDefinition resources", func() {
framework.ConformanceIt("creating/deleting custom resource definition objects works ", func() {

config, err := framework.LoadConfig()
if err != nil {
e2elog.Failf("failed to load config: %v", err)
}

framework.ExpectNoError(err, "loading config")
apiExtensionClient, err := clientset.NewForConfig(config)
if err != nil {
e2elog.Failf("failed to initialize apiExtensionClient: %v", err)
}
framework.ExpectNoError(err, "initializing apiExtensionClient")

randomDefinition := fixtures.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped)

//create CRD and waits for the resource to be recognized and available.
// Create CRD and waits for the resource to be recognized and available.
randomDefinition, err = fixtures.CreateNewCustomResourceDefinition(randomDefinition, apiExtensionClient, f.DynamicClient)
if err != nil {
e2elog.Failf("failed to create CustomResourceDefinition: %v", err)
}
framework.ExpectNoError(err, "creating CustomResourceDefinition")

defer func() {
err = fixtures.DeleteCustomResourceDefinition(randomDefinition, apiExtensionClient)
if err != nil {
e2elog.Failf("failed to delete CustomResourceDefinition: %v", err)
framework.ExpectNoError(err, "deleting CustomResourceDefinition")
}()
})

/*
Release : v1.16
Testname: Custom Resource Definition, list
Description: Create a API extension client, define 10 random custom resource definitions and list them using a label selector. API server MUST be able to list the custom resource definitions and delete them via delete collection.
*/
ginkgo.It("listing custom resource definition objects works ", func() {
testListSize := 10
config, err := framework.LoadConfig()
framework.ExpectNoError(err, "loading config")
apiExtensionClient, err := clientset.NewForConfig(config)
framework.ExpectNoError(err, "initializing apiExtensionClient")

// Label the CRDs we create so we can list only them even though they are cluster scoped
testUUID := string(uuid.NewUUID())

// Create CRD and wait for the resource to be recognized and available.
crds := make([]*v1beta1.CustomResourceDefinition, testListSize)
for i := 0; i < testListSize; i++ {
crd := fixtures.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped)
crd.Labels = map[string]string{"e2e-list-test-uuid": testUUID}
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, f.DynamicClient)
framework.ExpectNoError(err, "creating CustomResourceDefinition")
crds[i] = crd
}

// Create a crd w/o the label to ensure the label selector matching works correctly
crd := fixtures.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped)
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, f.DynamicClient)
framework.ExpectNoError(err, "creating CustomResourceDefinition")
defer func() {
err = fixtures.DeleteCustomResourceDefinition(crd, apiExtensionClient)
framework.ExpectNoError(err, "deleting CustomResourceDefinition")
}()

selectorListOpts := metav1.ListOptions{LabelSelector: "e2e-list-test-uuid=" + testUUID}
list, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(selectorListOpts)
framework.ExpectNoError(err, "listing CustomResourceDefinitions")
framework.ExpectEqual(len(list.Items), testListSize)
for _, actual := range list.Items {
var expected *v1beta1.CustomResourceDefinition
for _, e := range crds {
if e.Name == actual.Name && e.Namespace == actual.Namespace {
expected = e
}
}
framework.ExpectNotEqual(expected, nil)
if !equality.Semantic.DeepEqual(actual.Spec, expected.Spec) {
e2elog.Failf("Expected CustomResourceDefinition in list with name %s to match crd created with same name, but got different specs:\n%s",
actual.Name, diff.ObjectReflectDiff(expected.Spec, actual.Spec))
}
}

// Use delete collection to remove the CRDs
err = fixtures.DeleteCustomResourceDefinitions(selectorListOpts, apiExtensionClient)
framework.ExpectNoError(err, "deleting CustomResourceDefinitions")
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
framework.ExpectNoError(err, "getting remaining CustomResourceDefinition")
})

/*
Release : v1.16
Testname: Custom Resource Definition, status sub-resource
Description: Create a API extension client, create a custom resource definition and then read, update and patch its status sub-resource. API server MUST be able to perform the operations against the status sub-resource.
*/
ginkgo.It("getting/updating/patching custom resource definition status sub-resource works ", func() {
config, err := framework.LoadConfig()
framework.ExpectNoError(err, "loading config")
apiExtensionClient, err := clientset.NewForConfig(config)
framework.ExpectNoError(err, "initializing apiExtensionClient")
dynamicClient, err := dynamic.NewForConfig(config)
framework.ExpectNoError(err, "initializing dynamic client")
gvr := v1beta1.SchemeGroupVersion.WithResource("customresourcedefinitions")
resourceClient := dynamicClient.Resource(gvr)

// Create CRD and waits for the resource to be recognized and available.
crd := fixtures.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped)
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, f.DynamicClient)
framework.ExpectNoError(err, "creating CustomResourceDefinition")
defer func() {
err = fixtures.DeleteCustomResourceDefinition(crd, apiExtensionClient)
framework.ExpectNoError(err, "deleting CustomResourceDefinition")
}()

var updated *v1beta1.CustomResourceDefinition
updateCondition := v1beta1.CustomResourceDefinitionCondition{Message: "updated"}
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
// Use dynamic client to read the status sub-resource since typed client does not expose it.
u, err := resourceClient.Get(crd.GetName(), metav1.GetOptions{}, "status")
framework.ExpectNoError(err, "getting CustomResourceDefinition status")
status := unstructuredToCRD(u)
if !equality.Semantic.DeepEqual(status.Spec, crd.Spec) {
e2elog.Failf("Expected CustomResourceDefinition Spec to match status sub-resource Spec, but got:\n%s", diff.ObjectReflectDiff(status.Spec, crd.Spec))
}
status.Status.Conditions = append(status.Status.Conditions, updateCondition)
updated, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().UpdateStatus(status)
return err
})
framework.ExpectNoError(err, "updating CustomResourceDefinition status")
expectCondition(updated.Status.Conditions, updateCondition)

patchCondition := v1beta1.CustomResourceDefinitionCondition{Message: "patched"}
patched, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(
crd.GetName(),
types.JSONPatchType,
[]byte(`[{"op": "add", "path": "/status/conditions", "value": [{"message": "patched"}]}]`),
"status")
framework.ExpectNoError(err, "patching CustomResourceDefinition status")
expectCondition(updated.Status.Conditions, updateCondition)
expectCondition(patched.Status.Conditions, patchCondition)
})
})
})

func unstructuredToCRD(obj *unstructured.Unstructured) *v1beta1.CustomResourceDefinition {
crd := new(v1beta1.CustomResourceDefinition)
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, crd)
framework.ExpectNoError(err, "converting unstructured to CustomResourceDefinition")
return crd
}

func expectCondition(conditions []v1beta1.CustomResourceDefinitionCondition, expected v1beta1.CustomResourceDefinitionCondition) {
for _, c := range conditions {
if equality.Semantic.DeepEqual(c, expected) {
return
}
}
e2elog.Failf("Condition %#v not found in conditions %#v", expected, conditions)
}

0 comments on commit 4725551

Please sign in to comment.
You can’t perform that action at this time.