diff --git a/test/extended/operators/check_crd_status.go b/test/extended/operators/check_crd_status.go new file mode 100644 index 000000000000..64c04caa4ffa --- /dev/null +++ b/test/extended/operators/check_crd_status.go @@ -0,0 +1,198 @@ +package operators + +import ( + "context" + "fmt" + "strings" + + g "github.com/onsi/ginkgo" + o "github.com/onsi/gomega" + "k8s.io/kube-openapi/pkg/util/sets" + e2e "k8s.io/kubernetes/test/e2e/framework" + + exutil "github.com/openshift/origin/test/extended/util" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// checkSubresourceStatus returns a list of names of CRDs that have a "status" in the CRD schema +// but no subresource.status defined. +// For now, it ignores the ones that are currently failing. +func checkSubresourceStatus(crdItemList []apiextensionsv1.CustomResourceDefinition) []string { + + // These CRDs, at the time this test was written, do not have a "status" in the CRD schema + // and subresource.status. + // These can be skipped for now but we don't want the number to increase. + // These CRDs should be tidied up over time. + // + exceptionsList := sets.NewString( + "networks.config.openshift.io", + "networks.operator.openshift.io", + "operatorpkis.network.operator.openshift.io", + "profiles.tuned.openshift.io", + "tuneds.tuned.openshift.io", + ) + + failures := []string{} + for _, crdItem := range crdItemList { + + // This test is interested only in CRDs that end with "openshift.io". + if !strings.HasSuffix(crdItem.ObjectMeta.Name, "openshift.io") { + continue + } + + crdName := crdItem.ObjectMeta.Name + + // Skip CRDs in the exceptions list for now. + if exceptionsList.Has(crdName) { + continue + } + + // Iterate through all versions of the CustomResourceDefinition Spec looking for one with + // a schema status element, + foundStatusInSchema := false + var i int + for i = 0; i < len(crdItem.Spec.Versions); i++ { + if _, ok := crdItem.Spec.Versions[i].Schema.OpenAPIV3Schema.Properties["status"]; ok { + foundStatusInSchema = true + break + } + } + + if foundStatusInSchema { + if !(crdItem.Spec.Versions[i].Subresources != nil && crdItem.Spec.Versions[i].Subresources.Status != nil) { + failures = append(failures, fmt.Sprintf("CRD %s has a status in the schema but no subresource.status", crdName)) + } + } + } + + return failures +} + +// checkStatusInSchema returns a list of names of CRDs that don't have a "status" in the CRD schema. +// For now, it ignores the ones that are currently failing. +func checkStatusInSchema(crdItemList []apiextensionsv1.CustomResourceDefinition) []string { + + // These CRDs, at the time this test was written, do not have a "status" in the CRD schema + // and subresource.status. + // These can be skipped for now but we don't want the number to increase. + // These CRDs should be tidied up over time. + // + exceptionsList := sets.NewString( + "builds.config.openshift.io", + "clusternetworks.network.openshift.io", + "consoleclidownloads.console.openshift.io", + "consoleexternalloglinks.console.openshift.io", + "consolelinks.console.openshift.io", + "consolenotifications.console.openshift.io", + "consoleplugins.console.openshift.io", + "consolequickstarts.console.openshift.io", + "consoleyamlsamples.console.openshift.io", + "egressnetworkpolicies.network.openshift.io", + "hostsubnets.network.openshift.io", + "imagecontentpolicies.config.openshift.io", + "imagecontentsourcepolicies.operator.openshift.io", + "machineconfigs.machineconfiguration.openshift.io", + "netnamespaces.network.openshift.io", + "rangeallocations.security.internal.openshift.io", + "rolebindingrestrictions.authorization.openshift.io", + "securitycontextconstraints.security.openshift.io", + ) + + failures := []string{} + for _, crdItem := range crdItemList { + + // This test is interested only in CRDs that end with "openshift.io". + if !strings.HasSuffix(crdItem.ObjectMeta.Name, "openshift.io") { + continue + } + + crdName := crdItem.ObjectMeta.Name + + // Skip CRDs in the exceptions list for now. + if exceptionsList.Has(crdName) { + continue + } + + // Iterate through all versions of the CustomResourceDefinition Spec looking for one with + // a schema status element, + foundStatusInSchema := false + var i int + for i = 0; i < len(crdItem.Spec.Versions); i++ { + if _, ok := crdItem.Spec.Versions[i].Schema.OpenAPIV3Schema.Properties["status"]; ok { + foundStatusInSchema = true + break + } + } + + if !foundStatusInSchema { + failures = append(failures, fmt.Sprintf("CRD %s has no 'status' element in its schema", crdName)) + } + } + + return failures +} + +var _ = g.Describe("[sig-arch][Early]", func() { + + defer g.GinkgoRecover() + + var ( + crdItemList []apiextensionsv1.CustomResourceDefinition + ) + + oc := exutil.NewCLI("subresource-status-check") + + g.BeforeEach(func() { + var err error + crdClient := apiextensionsclientset.NewForConfigOrDie(oc.AdminConfig()) + crdItemList, err = getCRDItemList(*crdClient) + o.Expect(err).NotTo(o.HaveOccurred()) + }) + + g.Describe("CRDs for openshift.io", func() { + g.It("should have subresource.status", func() { + failures := checkSubresourceStatus(crdItemList) + if len(failures) > 0 { + e2e.Fail(strings.Join(failures, "\n")) + } + }) + }) +}) + +var _ = g.Describe("[sig-arch][Early]", func() { + + defer g.GinkgoRecover() + + var ( + crdItemList []apiextensionsv1.CustomResourceDefinition + ) + + oc := exutil.NewCLI("schema-status-check") + + g.BeforeEach(func() { + var err error + crdClient := apiextensionsclientset.NewForConfigOrDie(oc.AdminConfig()) + crdItemList, err = getCRDItemList(*crdClient) + o.Expect(err).NotTo(o.HaveOccurred()) + }) + + g.Describe("CRDs for openshift.io", func() { + g.It("should have a status in the CRD schema", func() { + failures := checkStatusInSchema(crdItemList) + if len(failures) > 0 { + e2e.Fail(strings.Join(failures, "\n")) + } + }) + }) +}) + +func getCRDItemList(crdClient apiextensionsclientset.Clientset) ([]apiextensionsv1.CustomResourceDefinition, error) { + + crdList, err := crdClient.ApiextensionsV1().CustomResourceDefinitions().List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + return crdList.Items, err +} diff --git a/test/extended/operators/check_crd_status_test.go b/test/extended/operators/check_crd_status_test.go new file mode 100644 index 000000000000..de8b4ef720e0 --- /dev/null +++ b/test/extended/operators/check_crd_status_test.go @@ -0,0 +1,60 @@ +package operators + +import ( + "fmt" + "os" + "testing" + + exutil "github.com/openshift/origin/test/extended/util" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" +) + +func Test_checkSubresourceStatus(t *testing.T) { + + crdClient := setupLocalAPIClientset() + t.Run("Test_checkSubresourceStatus test", func(t *testing.T) { + crdList, err := getCRDItemList(*crdClient) + if err != nil { + t.Errorf("Fail: %s", err) + } + failures := checkSubresourceStatus(crdList) + if len(failures) > 0 { + t.Error("There should be no failures") + for _, i := range failures { + fmt.Println(i) + } + } + }) +} + +func Test_checkStatusInSchema(t *testing.T) { + + crdClient := setupLocalAPIClientset() + t.Run("Test_checkStatusInSchema test", func(t *testing.T) { + crdList, err := getCRDItemList(*crdClient) + if err != nil { + t.Errorf("Fail: %s", err) + } + failures := checkStatusInSchema(crdList) + if len(failures) > 0 { + t.Error("There should be no failures") + for _, i := range failures { + fmt.Println(i) + } + } + }) +} +func setupLocalAPIClientset() *apiextensionsclientset.Clientset { + // Get the kubeconfig by creating an Openshift cluster with cluster-bot, downloading it, + // and using the filename for KUBECONFIG. + home_dir := os.Getenv("HOME") + err := os.Setenv("KUBECONFIG", fmt.Sprintf("%s/Downloads/cluster-bot-2022-05-10-100029.kubeconfig.txt", home_dir)) + kube_dir := os.Getenv("KUBECONFIG") + fmt.Println(kube_dir) + if err != nil { + fmt.Printf("Error setting KUBECONFIG: %s", err) + } + oc := exutil.NewCLI("default") + local_client := apiextensionsclientset.NewForConfigOrDie(oc.AdminConfig()) + return local_client +} diff --git a/test/extended/util/annotate/generated/zz_generated.annotations.go b/test/extended/util/annotate/generated/zz_generated.annotations.go index 0d97d8009cf4..cb57ea43ad04 100644 --- a/test/extended/util/annotate/generated/zz_generated.annotations.go +++ b/test/extended/util/annotate/generated/zz_generated.annotations.go @@ -619,6 +619,10 @@ var annotations = map[string]string{ "[Top Level] [sig-arch] ocp payload should be based on existing source OLM version should contain the source commit id": "OLM version should contain the source commit id [Suite:openshift/conformance/parallel]", + "[Top Level] [sig-arch][Early] CRDs for openshift.io should have a status in the CRD schema": "should have a status in the CRD schema [Suite:openshift/conformance/parallel]", + + "[Top Level] [sig-arch][Early] CRDs for openshift.io should have subresource.status": "should have subresource.status [Suite:openshift/conformance/parallel]", + "[Top Level] [sig-arch][Early] Managed cluster should start all core operators": "start all core operators [Skipped:Disconnected] [Suite:openshift/conformance/parallel]", "[Top Level] [sig-arch][Feature:ClusterUpgrade] Cluster should remain functional during upgrade [Disruptive]": "Cluster should remain functional during upgrade [Disruptive] [Serial]",