Add CRD e2e tests for list, delete collection, and status sub-resource operations
This commit is contained in:
		| @@ -370,6 +370,29 @@ func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefi | |||||||
| 	return nil | 	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. | // CreateNewVersionedScaleClient returns a scale client. | ||||||
| func CreateNewVersionedScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config, version string) (scale.ScalesGetter, error) { | func CreateNewVersionedScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config, version string) (scale.ScalesGetter, error) { | ||||||
| 	discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) | 	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:go_default_library", | ||||||
|         "//staging/src/k8s.io/apimachinery/pkg/runtime/schema: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/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/errors:go_default_library", | ||||||
|         "//staging/src/k8s.io/apimachinery/pkg/util/intstr: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", |         "//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/rest:go_default_library", | ||||||
|         "//staging/src/k8s.io/client-go/util/cert: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/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/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/apis/apiregistration/v1:go_default_library", | ||||||
|         "//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset: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 | package apimachinery | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"github.com/onsi/ginkgo" | ||||||
|  |  | ||||||
| 	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" | 	"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" | ||||||
|  | 	"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" | 	utilversion "k8s.io/apimachinery/pkg/util/version" | ||||||
|  | 	"k8s.io/client-go/dynamic" | ||||||
|  | 	"k8s.io/client-go/util/retry" | ||||||
| 	"k8s.io/kubernetes/test/e2e/framework" | 	"k8s.io/kubernetes/test/e2e/framework" | ||||||
| 	e2elog "k8s.io/kubernetes/test/e2e/framework/log" | 	e2elog "k8s.io/kubernetes/test/e2e/framework/log" | ||||||
|  |  | ||||||
| 	"github.com/onsi/ginkgo" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var crdVersion = utilversion.MustParseSemantic("v1.7.0") | 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() { | 		framework.ConformanceIt("creating/deleting custom resource definition objects works ", func() { | ||||||
|  |  | ||||||
| 			config, err := framework.LoadConfig() | 			config, err := framework.LoadConfig() | ||||||
| 			if err != nil { | 			framework.ExpectNoError(err, "loading config") | ||||||
| 				e2elog.Failf("failed to load config: %v", err) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			apiExtensionClient, err := clientset.NewForConfig(config) | 			apiExtensionClient, err := clientset.NewForConfig(config) | ||||||
| 			if err != nil { | 			framework.ExpectNoError(err, "initializing apiExtensionClient") | ||||||
| 				e2elog.Failf("failed to initialize apiExtensionClient: %v", err) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			randomDefinition := fixtures.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped) | 			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) | 			randomDefinition, err = fixtures.CreateNewCustomResourceDefinition(randomDefinition, apiExtensionClient, f.DynamicClient) | ||||||
| 			if err != nil { | 			framework.ExpectNoError(err, "creating CustomResourceDefinition") | ||||||
| 				e2elog.Failf("failed to create CustomResourceDefinition: %v", err) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			defer func() { | 			defer func() { | ||||||
| 				err = fixtures.DeleteCustomResourceDefinition(randomDefinition, apiExtensionClient) | 				err = fixtures.DeleteCustomResourceDefinition(randomDefinition, apiExtensionClient) | ||||||
| 				if err != nil { | 				framework.ExpectNoError(err, "deleting CustomResourceDefinition") | ||||||
| 					e2elog.Failf("failed to delete CustomResourceDefinition: %v", err) |  | ||||||
| 				} |  | ||||||
| 			}() | 			}() | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 			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) | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Joe Betz
					Joe Betz