diff --git a/commitchecker.yaml b/commitchecker.yaml index 828ad4925..db447ae2c 100644 --- a/commitchecker.yaml +++ b/commitchecker.yaml @@ -1,4 +1,4 @@ -expectedMergeBase: dfd25de92909f8a72869cfd0b1377b4fd524df8c +expectedMergeBase: 25704ad1e1d3dd9029d01b918829850f7139a63a upstreamBranch: main upstreamOrg: operator-framework upstreamRepo: operator-controller diff --git a/go.mod b/go.mod index d604830a5..ca711474a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/operator-framework/operator-controller -go 1.25.3 +go 1.25.7 require ( github.com/BurntSushi/toml v1.6.0 diff --git a/hack/kind-config/containerd/certs.d/go.mod b/hack/kind-config/containerd/certs.d/go.mod index fa9ef1076..96849082f 100644 --- a/hack/kind-config/containerd/certs.d/go.mod +++ b/hack/kind-config/containerd/certs.d/go.mod @@ -1,6 +1,6 @@ module hack-cert.d -go 1.25.3 +go 1.25.7 // This file is present in the certs.d directory to ensure that // certs.d/host:port directories are not included in the main go diff --git a/internal/operator-controller/rukpak/render/registryv1/registryv1.go b/internal/operator-controller/rukpak/render/registryv1/registryv1.go index 1cfefbb8b..87ab11ba4 100644 --- a/internal/operator-controller/rukpak/render/registryv1/registryv1.go +++ b/internal/operator-controller/rukpak/render/registryv1/registryv1.go @@ -29,6 +29,7 @@ var BundleValidator = render.BundleValidator{ validators.CheckConversionWebhookCRDReferenceUniqueness, validators.CheckConversionWebhooksReferenceOwnedCRDs, validators.CheckWebhookRules, + validators.CheckObjectSupport, } // ResourceGenerators a slice of ResourceGenerators required to generate plain resource manifests for diff --git a/internal/operator-controller/rukpak/render/registryv1/registryv1_test.go b/internal/operator-controller/rukpak/render/registryv1/registryv1_test.go index b092cc8e1..009d47c13 100644 --- a/internal/operator-controller/rukpak/render/registryv1/registryv1_test.go +++ b/internal/operator-controller/rukpak/render/registryv1/registryv1_test.go @@ -34,6 +34,7 @@ func Test_BundleValidatorHasAllValidationFns(t *testing.T) { validators.CheckConversionWebhookCRDReferenceUniqueness, validators.CheckConversionWebhooksReferenceOwnedCRDs, validators.CheckWebhookRules, + validators.CheckObjectSupport, } actualValidationFns := registryv1.BundleValidator diff --git a/internal/operator-controller/rukpak/render/registryv1/validators/validator.go b/internal/operator-controller/rukpak/render/registryv1/validators/validator.go index 65a483728..726024559 100644 --- a/internal/operator-controller/rukpak/render/registryv1/validators/validator.go +++ b/internal/operator-controller/rukpak/render/registryv1/validators/validator.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation" "github.com/operator-framework/api/pkg/operators/v1alpha1" + regv1bundle "github.com/operator-framework/operator-registry/pkg/lib/bundle" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle" ) @@ -348,3 +349,16 @@ func CheckWebhookRules(rv1 *bundle.RegistryV1) []error { } return errs } + +// CheckObjectSupport checks that the non-CRD and non-CSV bundle objects are supported by the +// registry+v1 standard +func CheckObjectSupport(rv1 *bundle.RegistryV1) []error { + var errs []error + for _, obj := range rv1.Others { + kind := obj.GetObjectKind().GroupVersionKind().Kind + if ok, _ := regv1bundle.IsSupported(kind); !ok { + errs = append(errs, fmt.Errorf("unsupported resource %q with kind %q", obj.GetName(), kind)) + } + } + return errs +} diff --git a/internal/operator-controller/rukpak/render/registryv1/validators/validator_test.go b/internal/operator-controller/rukpak/render/registryv1/validators/validator_test.go index b9377c81a..cf0e0728e 100644 --- a/internal/operator-controller/rukpak/render/registryv1/validators/validator_test.go +++ b/internal/operator-controller/rukpak/render/registryv1/validators/validator_test.go @@ -8,6 +8,8 @@ import ( admissionregistrationv1 "k8s.io/api/admissionregistration/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" "github.com/operator-framework/api/pkg/operators/v1alpha1" @@ -1171,3 +1173,82 @@ func Test_CheckWebhookNameIsDNS1123SubDomain(t *testing.T) { }) } } + +func newUnstructuredObject(gvk schema.GroupVersionKind, name string) unstructured.Unstructured { + obj := unstructured.Unstructured{} + obj.SetGroupVersionKind(gvk) + obj.SetName(name) + return obj +} + +func Test_CheckObjectSupport(t *testing.T) { + for _, tc := range []struct { + name string + bundle *bundle.RegistryV1 + expectedErrs []error + }{ + { + name: "accepts bundles with no other objects", + bundle: &bundle.RegistryV1{}, + expectedErrs: nil, + }, + { + name: "accepts bundles with supported object kinds", + bundle: &bundle.RegistryV1{ + Others: []unstructured.Unstructured{ + newUnstructuredObject(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Secret"}, "my-secret"), + newUnstructuredObject(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"}, "my-configmap"), + newUnstructuredObject(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ServiceAccount"}, "my-sa"), + newUnstructuredObject(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}, "my-service"), + newUnstructuredObject(schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole"}, "my-clusterrole"), + newUnstructuredObject(schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}, "my-clusterrolebinding"), + newUnstructuredObject(schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"}, "my-role"), + newUnstructuredObject(schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}, "my-rolebinding"), + }, + }, + expectedErrs: nil, + }, + { + name: "rejects bundles with unsupported object kinds", + bundle: &bundle.RegistryV1{ + Others: []unstructured.Unstructured{ + newUnstructuredObject(schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, "my-deployment"), + }, + }, + expectedErrs: []error{ + errors.New(`unsupported resource "my-deployment" with kind "Deployment"`), + }, + }, + { + name: "reports errors for each unsupported object", + bundle: &bundle.RegistryV1{ + Others: []unstructured.Unstructured{ + newUnstructuredObject(schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, "my-deployment"), + newUnstructuredObject(schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"}, "my-statefulset"), + }, + }, + expectedErrs: []error{ + errors.New(`unsupported resource "my-deployment" with kind "Deployment"`), + errors.New(`unsupported resource "my-statefulset" with kind "StatefulSet"`), + }, + }, + { + name: "accepts supported objects and rejects unsupported objects in the same bundle", + bundle: &bundle.RegistryV1{ + Others: []unstructured.Unstructured{ + newUnstructuredObject(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"}, "my-configmap"), + newUnstructuredObject(schema.GroupVersionKind{Group: "batch", Version: "v1", Kind: "Job"}, "my-job"), + newUnstructuredObject(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Secret"}, "my-secret"), + }, + }, + expectedErrs: []error{ + errors.New(`unsupported resource "my-job" with kind "Job"`), + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + errs := validators.CheckObjectSupport(tc.bundle) + require.Equal(t, tc.expectedErrs, errs) + }) + } +} diff --git a/openshift/tests-extension/go.mod b/openshift/tests-extension/go.mod index 1d7fb4ad3..0f8e409e1 100644 --- a/openshift/tests-extension/go.mod +++ b/openshift/tests-extension/go.mod @@ -1,6 +1,6 @@ module github.com/openshift/operator-framework-operator-controller/openshift/tests-extension -go 1.25.3 +go 1.25.7 require ( github.com/blang/semver/v4 v4.0.0 diff --git a/openshift/tests-extension/test/olmv1-catalog.go b/openshift/tests-extension/test/olmv1-catalog.go index dbae3f535..5e1bfab1b 100644 --- a/openshift/tests-extension/test/olmv1-catalog.go +++ b/openshift/tests-extension/test/olmv1-catalog.go @@ -11,7 +11,6 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -77,38 +76,12 @@ func verifyCatalogEndpoint(ctx SpecContext, catalog, endpoint, query string) { strings.ReplaceAll(endpoint, "?", ""), strings.ReplaceAll(catalog, "-", "")) - // create service account object - serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: jobNamePrefix, - Namespace: "default", - }, - } - - err = k8sClient.Create(ctx, serviceAccount) - - Expect(err).To(Or(BeNil(), Satisfy(apierrors.IsAlreadyExists)), - "Failed to ensure ServiceAccount %s", jobNamePrefix) - - DeferCleanup(func(ctx SpecContext) { - _ = k8sClient.Delete(ctx, serviceAccount) - }) - - job := buildCurlJob(jobNamePrefix, "default", serviceURL, serviceAccount) - + job := buildCurlJob(jobNamePrefix, "default", serviceURL) err = k8sClient.Create(ctx, job) Expect(err).NotTo(HaveOccurred(), "failed to create Job") DeferCleanup(func(ctx SpecContext) { _ = k8sClient.Delete(ctx, job) - // We poll for deletion success so that the cleanup succeeds only when - // the job is deleted. Then, we can move onto deleting the service - // account without issue. - Eventually(func(g Gomega) { - recheck := &batchv1.Job{} - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(job), recheck) - g.Expect(apierrors.IsNotFound(err)).To(BeTrue(), fmt.Sprintf("Job %v should be deleted", job.Name)) - }).WithTimeout(helpers.DefaultTimeout).WithPolling(helpers.DefaultPolling).Should(Succeed()) }) By("Waiting for Job to succeed") @@ -230,7 +203,7 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 }) }) -func buildCurlJob(prefix, namespace, url string, serviceAccount *corev1.ServiceAccount) *batchv1.Job { +func buildCurlJob(prefix, namespace, url string) *batchv1.Job { backoff := int32(1) // This means the k8s garbage collector will automatically delete the job 5 minutes // after it has completed or failed. @@ -259,8 +232,7 @@ func buildCurlJob(prefix, namespace, url string, serviceAccount *corev1.ServiceA BackoffLimit: &backoff, Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccount.Name, - RestartPolicy: corev1.RestartPolicyNever, + RestartPolicy: corev1.RestartPolicyNever, Containers: []corev1.Container{{ Name: "api-tester", Image: "registry.redhat.io/rhel8/httpd-24:latest", diff --git a/openshift/tests-extension/vendor/modules.txt b/openshift/tests-extension/vendor/modules.txt index f19511500..ba4d6cf35 100644 --- a/openshift/tests-extension/vendor/modules.txt +++ b/openshift/tests-extension/vendor/modules.txt @@ -282,7 +282,7 @@ github.com/openshift/client-go/user/clientset/versioned/typed/user/v1 ## explicit; go 1.24.0 github.com/openshift/origin/test/extended/util/image # github.com/operator-framework/operator-controller v1.5.1 => ../../ -## explicit; go 1.25.3 +## explicit; go 1.25.7 github.com/operator-framework/operator-controller/api/v1 # github.com/pborman/uuid v1.2.1 ## explicit diff --git a/requirements.txt b/requirements.txt index c40ac8146..b8191c0f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ mkdocs-material-extensions==1.3.1 packaging==26.0 paginate==0.5.7 pathspec==1.0.4 -platformdirs==4.5.1 +platformdirs==4.6.0 Pygments==2.19.2 pymdown-extensions==10.20.1 pyquery==2.0.1