diff --git a/go.mod b/go.mod index 746b0626c..6059433f5 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,6 @@ require ( github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 - github.com/onsi/ginkgo/v2 v2.22.0 - github.com/onsi/gomega v1.36.0 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 github.com/operator-framework/catalogd v1.0.0 @@ -107,7 +105,6 @@ require ( github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -116,7 +113,6 @@ require ( github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -170,6 +166,8 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 // indirect @@ -230,7 +228,6 @@ require ( golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/internal/rukpak/convert/registryv1_test.go b/internal/rukpak/convert/registryv1_test.go index 0be7e9e84..09890d67f 100644 --- a/internal/rukpak/convert/registryv1_test.go +++ b/internal/rukpak/convert/registryv1_test.go @@ -7,9 +7,7 @@ import ( "strings" "testing" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - + "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -24,194 +22,192 @@ import ( "github.com/operator-framework/operator-registry/alpha/property" ) -func TestRegistryV1Converter(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "RegistryV1 suite") +const ( + olmNamespaces = "olm.targetNamespaces" + olmProperties = "olm.properties" + installNamespace = "testInstallNamespace" +) + +func getCsvAndService() (v1alpha1.ClusterServiceVersion, corev1.Service) { + csv := v1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testCSV", + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}}, + }, + } + svc := corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testService", + }, + } + svc.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}) + return csv, svc } -var _ = Describe("RegistryV1 Suite", func() { - var _ = Describe("Convert", func() { - var ( - registryv1Bundle RegistryV1 - installNamespace string - targetNamespaces []string - ) - Context("Should set the namespaces of object correctly", func() { - var ( - svc corev1.Service - csv v1alpha1.ClusterServiceVersion - ) - BeforeEach(func() { - csv = v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}}, - }, - } - svc = corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testService", - }, - } - svc.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}) - installNamespace = "testInstallNamespace" - }) - - It("should set the namespace to installnamespace if not available", func() { - By("creating a registry v1 bundle") - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) - Expect(err).NotTo(HaveOccurred()) - - By("verifying if plain bundle has required objects") - Expect(plainBundle).NotTo(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(1)) - - By("verifying if ns has been set correctly") - resObj := findObjectByName(svc.Name, plainBundle.Objects) - Expect(resObj).NotTo(BeNil()) - Expect(resObj.GetNamespace()).To(BeEquivalentTo(installNamespace)) - }) - - It("should override namespace if already available", func() { - By("creating a registry v1 bundle") - svc.SetNamespace("otherNs") - unstructuredSvc := convertToUnstructured(svc) - unstructuredSvc.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}) - - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) - Expect(err).NotTo(HaveOccurred()) - - By("verifying if plain bundle has required objects") - Expect(plainBundle).NotTo(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(1)) - - By("verifying if ns has been set correctly") - resObj := findObjectByName(svc.Name, plainBundle.Objects) - Expect(resObj).NotTo(BeNil()) - Expect(resObj.GetNamespace()).To(BeEquivalentTo(installNamespace)) - }) - - Context("Should error when object is not supported", func() { - It("should error when unsupported GVK is passed", func() { - By("creating an unsupported kind") - event := corev1.Event{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testEvent", - }, - } - - unstructuredEvt := convertToUnstructured(event) - unstructuredEvt.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Event"}) - - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: csv, - Others: []unstructured.Unstructured{unstructuredEvt}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("bundle contains unsupported resource")) - Expect(plainBundle).To(BeNil()) - }) - }) - - Context("Should not set ns cluster scoped object is passed", func() { - It("should not error when cluster scoped obj is passed and not set its namespace", func() { - By("creating an unsupported kind") - pc := schedulingv1.PriorityClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testPriorityClass", - }, - } - - unstructuredpriorityclass := convertToUnstructured(pc) - unstructuredpriorityclass.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "PriorityClass"}) - - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: csv, - Others: []unstructured.Unstructured{unstructuredpriorityclass}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) - Expect(err).NotTo(HaveOccurred()) - - By("verifying if plain bundle has required objects") - Expect(plainBundle).NotTo(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(1)) - - By("verifying if ns has been set correctly") - resObj := findObjectByName(pc.Name, plainBundle.Objects) - Expect(resObj).NotTo(BeNil()) - Expect(resObj.GetNamespace()).To(BeEmpty()) - }) - }) - }) - - Context("Should generate objects successfully based on target namespaces", func() { - var ( - svc corev1.Service - baseCSV v1alpha1.ClusterServiceVersion - watchNamespaces []string - ) - - BeforeEach(func() { - // base CSV definition that each test case will deep copy and modify - baseCSV = v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - Annotations: map[string]string{ - "olm.properties": fmt.Sprintf("[{\"type\": %s, \"value\": \"%s\"}]", property.TypeConstraint, "value"), - }, - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategySpec: v1alpha1.StrategyDetailsDeployment{ - DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ - { - Name: "testDeployment", - Spec: appsv1.DeploymentSpec{ - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "testContainer", - Image: "testImage", - }, - }, - }, - }, - }, - }, - }, - Permissions: []v1alpha1.StrategyDeploymentPermissions{ - { - ServiceAccountName: "testServiceAccount", - Rules: []rbacv1.PolicyRule{ +func TestRegistryV1SuiteNamespaceNotAvailable(t *testing.T) { + var targetNamespaces []string + + t.Log("RegistryV1 Suite Convert") + t.Log("It should set the namespaces of the object correctly") + t.Log("It should set the namespace to installnamespace if not available") + + t.Log("By creating a registry v1 bundle") + csv, svc := getCsvAndService() + + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) + require.NoError(t, err) + + t.Log("By verifying if plain bundle has required objects") + require.NotNil(t, plainBundle) + require.Len(t, plainBundle.Objects, 1) + + t.Log("By verifying if ns has been set correctly") + resObj := findObjectByName(svc.Name, plainBundle.Objects) + require.NotNil(t, resObj) + require.Equal(t, installNamespace, resObj.GetNamespace()) +} + +func TestRegistryV1SuiteNamespaceAvailable(t *testing.T) { + var targetNamespaces []string + + t.Log("RegistryV1 Suite Convert") + t.Log("It should set the namespaces of the object correctly") + t.Log("It should override namespace if already available") + + t.Log("By creating a registry v1 bundle") + csv, svc := getCsvAndService() + + svc.SetNamespace("otherNs") + unstructuredSvc := convertToUnstructured(t, svc) + unstructuredSvc.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}) + + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) + require.NoError(t, err) + + t.Log("By verifying if plain bundle has required objects") + require.NotNil(t, plainBundle) + require.Len(t, plainBundle.Objects, 1) + + t.Log("By verifying if ns has been set correctly") + resObj := findObjectByName(svc.Name, plainBundle.Objects) + require.NotNil(t, plainBundle) + require.Equal(t, installNamespace, resObj.GetNamespace()) +} + +func TestRegistryV1SuiteNamespaceUnsupportedKind(t *testing.T) { + var targetNamespaces []string + + t.Log("RegistryV1 Suite Convert") + t.Log("It should set the namespaces of the object correctly") + t.Log("It should error when object is not supported") + t.Log("It should error when unsupported GVK is passed") + + t.Log("By creating a registry v1 bundle") + csv, _ := getCsvAndService() + + t.Log("By creating an unsupported kind") + event := corev1.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testEvent", + }, + } + + unstructuredEvt := convertToUnstructured(t, event) + unstructuredEvt.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Event"}) + + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: csv, + Others: []unstructured.Unstructured{unstructuredEvt}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) + require.Error(t, err) + require.ErrorContains(t, err, "bundle contains unsupported resource") + require.Nil(t, plainBundle) +} + +func TestRegistryV1SuiteNamespaceClusterScoped(t *testing.T) { + var targetNamespaces []string + + t.Log("RegistryV1 Suite Convert") + t.Log("It should set the namespaces of the object correctly") + t.Log("It should not set ns cluster scoped object is passed") + t.Log("It should not error when cluster scoped obj is passed and not set its namespace") + + t.Log("By creating a registry v1 bundle") + csv, _ := getCsvAndService() + + t.Log("By creating an unsupported kind") + pc := schedulingv1.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPriorityClass", + }, + } + + unstructuredpriorityclass := convertToUnstructured(t, pc) + unstructuredpriorityclass.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "PriorityClass"}) + + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: csv, + Others: []unstructured.Unstructured{unstructuredpriorityclass}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, targetNamespaces) + require.NoError(t, err) + + t.Log("By verifying if plain bundle has required objects") + require.NotNil(t, plainBundle) + require.Len(t, plainBundle.Objects, 1) + + t.Log("By verifying if ns has been set correctly") + resObj := findObjectByName(pc.Name, plainBundle.Objects) + require.NotNil(t, resObj) + require.Empty(t, resObj.GetNamespace()) +} + +func getBaseCsvAndService() (v1alpha1.ClusterServiceVersion, corev1.Service) { + // base CSV definition that each test case will deep copy and modify + baseCSV := v1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testCSV", + Annotations: map[string]string{ + olmProperties: fmt.Sprintf("[{\"type\": %s, \"value\": \"%s\"}]", property.TypeConstraint, "value"), + }, + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + InstallStrategy: v1alpha1.NamedInstallStrategy{ + StrategySpec: v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ + { + Name: "testDeployment", + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { - APIGroups: []string{"test"}, - Resources: []string{"pods"}, - Verbs: []string{"*"}, + Name: "testContainer", + Image: "testImage", }, }, }, @@ -219,274 +215,344 @@ var _ = Describe("RegistryV1 Suite", func() { }, }, }, - } - - svc = corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testService", - }, - } - svc.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}) - installNamespace = "testInstallNamespace" - }) - - It("should convert into plain manifests successfully with AllNamespaces", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}} - - By("creating a registry v1 bundle") - watchNamespaces = []string{""} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).NotTo(HaveOccurred()) - - By("verifying if plain bundle has required objects") - Expect(plainBundle).ShouldNot(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(5)) - - By("verifying olm.targetNamespaces annotation in the deployment's pod template") - dep := findObjectByName("testDeployment", plainBundle.Objects) - Expect(dep).NotTo(BeNil()) - Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) - }) - - It("should convert into plain manifests successfully with MultiNamespace", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} - - By("creating a registry v1 bundle") - watchNamespaces = []string{"testWatchNs1", "testWatchNs2"} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).NotTo(HaveOccurred()) - - By("verifying if plain bundle has required objects") - Expect(plainBundle).ShouldNot(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(7)) - - By("verifying olm.targetNamespaces annotation in the deployment's pod template") - dep := findObjectByName("testDeployment", plainBundle.Objects) - Expect(dep).NotTo(BeNil()) - Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) - }) - - It("should convert into plain manifests successfully with SingleNamespace", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}} - - By("creating a registry v1 bundle") - watchNamespaces = []string{"testWatchNs1"} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).NotTo(HaveOccurred()) - - By("verifying if plain bundle has required objects") - Expect(plainBundle).ShouldNot(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(5)) - - By("verifying olm.targetNamespaces annotation in the deployment's pod template") - dep := findObjectByName("testDeployment", plainBundle.Objects) - Expect(dep).NotTo(BeNil()) - Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) - }) - - It("should convert into plain manifests successfully with own namespace", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}} - - By("creating a registry v1 bundle") - watchNamespaces = []string{installNamespace} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).NotTo(HaveOccurred()) - - By("verifying if plain bundle has required objects") - Expect(plainBundle).ShouldNot(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(5)) - - By("verifying olm.targetNamespaces annotation in the deployment's pod template") - dep := findObjectByName("testDeployment", plainBundle.Objects) - Expect(dep).NotTo(BeNil()) - Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) - }) - - It("should error when multinamespace mode is supported with an empty string in target namespaces", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} - - By("creating a registry v1 bundle") - watchNamespaces = []string{"testWatchNs1", ""} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).To(HaveOccurred()) - Expect(plainBundle).To(BeNil()) - }) - - It("should error when single namespace mode is disabled with more than one target namespaces", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: false}} - - By("creating a registry v1 bundle") - watchNamespaces = []string{"testWatchNs1", "testWatchNs2"} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).To(HaveOccurred()) - Expect(plainBundle).To(BeNil()) - }) - - It("should error when all namespace mode is disabled with target namespace containing an empty string", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{ - {Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: false}, - {Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}, - {Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}, - {Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}, - } - - By("creating a registry v1 bundle") - watchNamespaces = []string{""} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).To(HaveOccurred()) - Expect(plainBundle).To(BeNil()) - }) - - It("should propagate csv annotations to chart metadata annotation", func() { - csv := baseCSV.DeepCopy() - csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} - - By("creating a registry v1 bundle") - watchNamespaces = []string{"testWatchNs1", "testWatchNs2"} - unstructuredSvc := convertToUnstructured(svc) - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: *csv, - Others: []unstructured.Unstructured{unstructuredSvc}, - } - - By("converting to helm") - chrt, err := toChart(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).NotTo(HaveOccurred()) - Expect(chrt.Metadata.Annotations["olm.properties"]).NotTo(BeNil()) - }) - }) - - Context("Should read the registry+v1 bundle filesystem correctly", func() { - It("should include metadata/properties.yaml and csv.metadata.annotations['olm.properties'] in chart metadata", func() { - fsys := os.DirFS("testdata/combine-properties-bundle") - chrt, err := RegistryV1ToHelmChart(context.Background(), fsys, "", nil) - Expect(err).NotTo(HaveOccurred()) - Expect(chrt).NotTo(BeNil()) - Expect(chrt.Metadata).NotTo(BeNil()) - Expect(chrt.Metadata.Annotations).To(HaveKeyWithValue("olm.properties", `[{"type":"from-csv-annotations-key","value":"from-csv-annotations-value"},{"type":"from-file-key","value":"from-file-value"}]`)) - }) - }) - - Context("Should enforce limitations", func() { - It("should not allow bundles with webhooks", func() { - By("creating a registry v1 bundle") - csv := v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}}, - WebhookDefinitions: []v1alpha1.WebhookDescription{{ConversionCRDs: []string{"fake-webhook.package-with-webhooks.io"}}}, - }, - } - watchNamespaces := []string{metav1.NamespaceAll} - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: csv, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).To(MatchError(ContainSubstring("webhookDefinitions are not supported"))) - Expect(plainBundle).To(BeNil()) - }) - - It("should not allow bundles with API service definitions", func() { - By("creating a registry v1 bundle") - csv := v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}}, - APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ - Owned: []v1alpha1.APIServiceDescription{{Name: "fake-owned-api-definition"}}, + Permissions: []v1alpha1.StrategyDeploymentPermissions{ + { + ServiceAccountName: "testServiceAccount", + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"test"}, + Resources: []string{"pods"}, + Verbs: []string{"*"}, + }, + }, }, }, - } - watchNamespaces := []string{metav1.NamespaceAll} - registryv1Bundle = RegistryV1{ - PackageName: "testPkg", - CSV: csv, - } - - By("converting to plain") - plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) - Expect(err).To(MatchError(ContainSubstring("apiServiceDefintions are not supported"))) - Expect(plainBundle).To(BeNil()) - }) - }) - }) -}) - -func convertToUnstructured(obj interface{}) unstructured.Unstructured { + }, + }, + }, + } + + svc := corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testService", + }, + } + svc.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}) + return baseCSV, svc +} + +func TestRegistryV1SuiteGenerateAllNamespace(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should convert into plain manifests successfully with AllNamespaces") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}} + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{""} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.NoError(t, err) + + t.Log("By verifying if plain bundle has required objects") + require.NotNil(t, plainBundle) + require.Len(t, plainBundle.Objects, 5) + + t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + require.NotNil(t, dep) + require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces) + require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces]) +} + +func TestRegistryV1SuiteGenerateMultiNamespace(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should convert into plain manifests successfully with MultiNamespace") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{"testWatchNs1", "testWatchNs2"} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.NoError(t, err) + + t.Log("By verifying if plain bundle has required objects") + require.NotNil(t, plainBundle) + require.Len(t, plainBundle.Objects, 7) + + t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + require.NotNil(t, dep) + require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces) + require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces]) +} + +func TestRegistryV1SuiteGenerateSingleNamespace(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should convert into plain manifests successfully with SingleNamespace") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}} + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{"testWatchNs1"} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.NoError(t, err) + + t.Log("By verifying if plain bundle has required objects") + require.NotNil(t, plainBundle) + require.Len(t, plainBundle.Objects, 5) + + t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + require.NotNil(t, dep) + require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces) + require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces]) +} + +func TestRegistryV1SuiteGenerateOwnNamespace(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should convert into plain manifests successfully with own namespace") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}} + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{installNamespace} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.NoError(t, err) + + t.Log("By verifying if plain bundle has required objects") + require.NotNil(t, plainBundle) + require.Len(t, plainBundle.Objects, 5) + + t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + require.NotNil(t, dep) + require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces) + require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces]) +} + +func TestRegistryV1SuiteGenerateErrorMultiNamespaceEmpty(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should error when multinamespace mode is supported with an empty string in target namespaces") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{"testWatchNs1", ""} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.Error(t, err) + require.Nil(t, plainBundle) +} + +func TestRegistryV1SuiteGenerateErrorSingleNamespaceDisabled(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should error when single namespace mode is disabled with more than one target namespaces") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: false}} + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{"testWatchNs1", "testWatchNs2"} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.Error(t, err) + require.Nil(t, plainBundle) +} + +func TestRegistryV1SuiteGenerateErrorAllNamespaceDisabled(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should error when all namespace mode is disabled with target namespace containing an empty string") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{ + {Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: false}, + {Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}, + {Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}, + {Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}, + } + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{""} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.Error(t, err) + require.Nil(t, plainBundle) +} + +func TestRegistryV1SuiteGeneratePropagateCsvAnnotations(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should propagate csv annotations to chart metadata annotation") + baseCSV, svc := getBaseCsvAndService() + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} + + t.Log("By creating a registry v1 bundle") + watchNamespaces := []string{"testWatchNs1", "testWatchNs2"} + unstructuredSvc := convertToUnstructured(t, svc) + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, + } + + t.Log("By converting to helm") + chrt, err := toChart(registryv1Bundle, installNamespace, watchNamespaces) + require.NoError(t, err) + require.Contains(t, chrt.Metadata.Annotations, olmProperties) +} + +func TestRegistryV1SuiteReadBundleFileSystem(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should read the registry+v1 bundle filesystem correctly") + t.Log("It should include metadata/properties.yaml and csv.metadata.annotations['olm.properties'] in chart metadata") + fsys := os.DirFS("testdata/combine-properties-bundle") + chrt, err := RegistryV1ToHelmChart(context.Background(), fsys, "", nil) + require.NoError(t, err) + require.NotNil(t, chrt) + require.NotNil(t, chrt.Metadata) + require.Contains(t, chrt.Metadata.Annotations, olmProperties) + require.Equal(t, `[{"type":"from-csv-annotations-key","value":"from-csv-annotations-value"},{"type":"from-file-key","value":"from-file-value"}]`, chrt.Metadata.Annotations[olmProperties]) +} + +func TestRegistryV1SuiteGenerateNoWebhooks(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should enforce limitations") + t.Log("It should not allow bundles with webhooks") + t.Log("By creating a registry v1 bundle") + csv := v1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testCSV", + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}}, + WebhookDefinitions: []v1alpha1.WebhookDescription{{ConversionCRDs: []string{"fake-webhook.package-with-webhooks.io"}}}, + }, + } + watchNamespaces := []string{metav1.NamespaceAll} + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: csv, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.Error(t, err) + require.ErrorContains(t, err, "webhookDefinitions are not supported") + require.Nil(t, plainBundle) +} + +func TestRegistryV1SuiteGenerateNoAPISerciceDefinitions(t *testing.T) { + t.Log("RegistryV1 Suite Convert") + t.Log("It should generate objects successfully based on target namespaces") + + t.Log("It should enforce limitations") + t.Log("It should not allow bundles with API service definitions") + t.Log("By creating a registry v1 bundle") + csv := v1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testCSV", + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}}, + APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ + Owned: []v1alpha1.APIServiceDescription{{Name: "fake-owned-api-definition"}}, + }, + }, + } + watchNamespaces := []string{metav1.NamespaceAll} + registryv1Bundle := RegistryV1{ + PackageName: "testPkg", + CSV: csv, + } + + t.Log("By converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + require.Error(t, err) + require.ErrorContains(t, err, "apiServiceDefintions are not supported") + require.Nil(t, plainBundle) +} + +func convertToUnstructured(t *testing.T, obj interface{}) unstructured.Unstructured { unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj) - Expect(err).NotTo(HaveOccurred()) - Expect(unstructuredObj).NotTo(BeNil()) + require.NoError(t, err) + require.NotNil(t, unstructuredObj) return unstructured.Unstructured{Object: unstructuredObj} }