11package convert_test
22
33import (
4+ "cmp"
45 "fmt"
56 "io/fs"
67 "os"
8+ "path/filepath"
9+ "reflect"
10+ "slices"
711 "strings"
812 "testing"
913 "testing/fstest"
@@ -17,6 +21,7 @@ import (
1721 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1822 "k8s.io/apimachinery/pkg/runtime"
1923 "k8s.io/apimachinery/pkg/runtime/schema"
24+ "k8s.io/cli-runtime/pkg/resource"
2025 "sigs.k8s.io/controller-runtime/pkg/client"
2126
2227 "github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -32,24 +37,80 @@ const (
3237
3338 bundlePathAnnotations = "metadata/annotations.yaml"
3439 bundlePathCSV = "manifests/csv.yaml"
40+
41+ regressionTestRoot = "testdata/regression-tests"
3542)
3643
37- func getCsvAndService () (v1alpha1.ClusterServiceVersion , corev1.Service ) {
38- csv := v1alpha1.ClusterServiceVersion {
39- ObjectMeta : metav1.ObjectMeta {
40- Name : "testCSV" ,
41- },
42- Spec : v1alpha1.ClusterServiceVersionSpec {
43- InstallModes : []v1alpha1.InstallMode {{Type : v1alpha1 .InstallModeTypeAllNamespaces , Supported : true }},
44- },
45- }
46- svc := corev1.Service {
47- ObjectMeta : metav1.ObjectMeta {
48- Name : "testService" ,
44+ // Test_ConvertRegressions checks that the registry+v1 to plain manifest
45+ // conversion code against bundles in testdata. The expected output is pre-generated using the rukpak/convert
46+ // package with the `regression-tests/generate-manifests.go` tool. New test cases must also be added to
47+ // generate manifests, which will generate the expected manifests. These _must_ be manually inspected/tested
48+ // for correctness before changes are committed to the test-cases. If the expected manifests for existing test-cases
49+ // change, this is likely a regression. Inspect it carefully.
50+ func Test_ConvertRegressions (t * testing.T ) {
51+ for _ , tc := range []struct {
52+ name string
53+ installNamespace string
54+ watchNamespace string
55+ bundle string
56+ testCaseName string
57+ }{
58+ {
59+ name : "AllNamespaces" ,
60+ installNamespace : "argocd-system" ,
61+ bundle : "argocd-operator.v0.6.0" ,
62+ testCaseName : "all-namespaces" ,
63+ }, {
64+ name : "SingleNamespaces" ,
65+ installNamespace : "argocd-system" ,
66+ watchNamespace : "argocd-watch" ,
67+ bundle : "argocd-operator.v0.6.0" ,
68+ testCaseName : "single-namespaces" ,
69+ }, {
70+ name : "OwnNamespaces" ,
71+ installNamespace : "argocd-system" ,
72+ watchNamespace : "argocd-system" ,
73+ bundle : "argocd-operator.v0.6.0" ,
74+ testCaseName : "own-namespaces" ,
4975 },
76+ } {
77+ t .Run (tc .name , func (t * testing.T ) {
78+ var (
79+ bundleRoot = filepath .Join (regressionTestRoot , tc .bundle )
80+ testCaseRoot = filepath .Join (bundleRoot , "test-cases" )
81+ )
82+
83+ t .Log ("Build BundleFS" )
84+ bundleFs := os .DirFS (filepath .Join (bundleRoot , "bundle" ))
85+
86+ t .Log ("Parse BundleFS into registry+v1" )
87+ regv1 , err := convert .ParseFS (bundleFs )
88+ require .NoError (t , err )
89+
90+ t .Log ("Convert registry+v1 to plain manifests" )
91+ plain , err := convert .Convert (regv1 , tc .installNamespace , []string {tc .watchNamespace })
92+ require .NoError (t , err )
93+
94+ t .Log ("Compare to pre-generated manifests" )
95+ var actualObjs []client.Object
96+ for _ , obj := range plain .Objects {
97+ uObj := convertToUnstructured (t , obj )
98+ actualObjs = append (actualObjs , & uObj )
99+ }
100+
101+ expectedObjs , err := loadGeneratedManifests (filepath .Join (testCaseRoot , tc .testCaseName ))
102+ require .NoError (t , err )
103+
104+ slices .SortFunc (actualObjs , sortByKindNamespaceName )
105+ slices .SortFunc (expectedObjs , sortByKindNamespaceName )
106+
107+ for i := range actualObjs {
108+ require .True (t , reflect .DeepEqual (actualObjs [i ], expectedObjs [i ]), "failed comparing manifest %d" , i )
109+ //require.Equal(t, expectedObjs[i], actualObjs[i])
110+ }
111+ require .Equal (t , len (expectedObjs ), len (actualObjs ))
112+ })
50113 }
51- svc .SetGroupVersionKind (schema.GroupVersionKind {Group : "" , Version : "v1" , Kind : "Service" })
52- return csv , svc
53114}
54115
55116func TestRegistryV1SuiteNamespaceNotAvailable (t * testing.T ) {
@@ -192,61 +253,6 @@ func TestRegistryV1SuiteNamespaceClusterScoped(t *testing.T) {
192253 require .Empty (t , resObj .GetNamespace ())
193254}
194255
195- func getBaseCsvAndService () (v1alpha1.ClusterServiceVersion , corev1.Service ) {
196- // base CSV definition that each test case will deep copy and modify
197- baseCSV := v1alpha1.ClusterServiceVersion {
198- ObjectMeta : metav1.ObjectMeta {
199- Name : "testCSV" ,
200- Annotations : map [string ]string {
201- olmProperties : fmt .Sprintf ("[{\" type\" : %s, \" value\" : \" %s\" }]" , property .TypeConstraint , "value" ),
202- },
203- },
204- Spec : v1alpha1.ClusterServiceVersionSpec {
205- InstallStrategy : v1alpha1.NamedInstallStrategy {
206- StrategySpec : v1alpha1.StrategyDetailsDeployment {
207- DeploymentSpecs : []v1alpha1.StrategyDeploymentSpec {
208- {
209- Name : "testDeployment" ,
210- Spec : appsv1.DeploymentSpec {
211- Template : corev1.PodTemplateSpec {
212- Spec : corev1.PodSpec {
213- Containers : []corev1.Container {
214- {
215- Name : "testContainer" ,
216- Image : "testImage" ,
217- },
218- },
219- },
220- },
221- },
222- },
223- },
224- Permissions : []v1alpha1.StrategyDeploymentPermissions {
225- {
226- ServiceAccountName : "testServiceAccount" ,
227- Rules : []rbacv1.PolicyRule {
228- {
229- APIGroups : []string {"test" },
230- Resources : []string {"pods" },
231- Verbs : []string {"*" },
232- },
233- },
234- },
235- },
236- },
237- },
238- },
239- }
240-
241- svc := corev1.Service {
242- ObjectMeta : metav1.ObjectMeta {
243- Name : "testService" ,
244- },
245- }
246- svc .SetGroupVersionKind (schema.GroupVersionKind {Group : "" , Version : "v1" , Kind : "Service" })
247- return baseCSV , svc
248- }
249-
250256func TestRegistryV1SuiteGenerateAllNamespace (t * testing.T ) {
251257 t .Log ("RegistryV1 Suite Convert" )
252258 t .Log ("It should generate objects successfully based on target namespaces" )
@@ -650,3 +656,107 @@ func removePaths(mapFs fstest.MapFS, paths ...string) fstest.MapFS {
650656 }
651657 return mapFs
652658}
659+
660+ func getCsvAndService () (v1alpha1.ClusterServiceVersion , corev1.Service ) {
661+ csv := v1alpha1.ClusterServiceVersion {
662+ ObjectMeta : metav1.ObjectMeta {
663+ Name : "testCSV" ,
664+ },
665+ Spec : v1alpha1.ClusterServiceVersionSpec {
666+ InstallModes : []v1alpha1.InstallMode {{Type : v1alpha1 .InstallModeTypeAllNamespaces , Supported : true }},
667+ },
668+ }
669+ svc := corev1.Service {
670+ ObjectMeta : metav1.ObjectMeta {
671+ Name : "testService" ,
672+ },
673+ }
674+ svc .SetGroupVersionKind (schema.GroupVersionKind {Group : "" , Version : "v1" , Kind : "Service" })
675+ return csv , svc
676+ }
677+
678+ func getBaseCsvAndService () (v1alpha1.ClusterServiceVersion , corev1.Service ) {
679+ // base CSV definition that each test case will deep copy and modify
680+ baseCSV := v1alpha1.ClusterServiceVersion {
681+ ObjectMeta : metav1.ObjectMeta {
682+ Name : "testCSV" ,
683+ Annotations : map [string ]string {
684+ olmProperties : fmt .Sprintf ("[{\" type\" : %s, \" value\" : \" %s\" }]" , property .TypeConstraint , "value" ),
685+ },
686+ },
687+ Spec : v1alpha1.ClusterServiceVersionSpec {
688+ InstallStrategy : v1alpha1.NamedInstallStrategy {
689+ StrategySpec : v1alpha1.StrategyDetailsDeployment {
690+ DeploymentSpecs : []v1alpha1.StrategyDeploymentSpec {
691+ {
692+ Name : "testDeployment" ,
693+ Spec : appsv1.DeploymentSpec {
694+ Template : corev1.PodTemplateSpec {
695+ Spec : corev1.PodSpec {
696+ Containers : []corev1.Container {
697+ {
698+ Name : "testContainer" ,
699+ Image : "testImage" ,
700+ },
701+ },
702+ },
703+ },
704+ },
705+ },
706+ },
707+ Permissions : []v1alpha1.StrategyDeploymentPermissions {
708+ {
709+ ServiceAccountName : "testServiceAccount" ,
710+ Rules : []rbacv1.PolicyRule {
711+ {
712+ APIGroups : []string {"test" },
713+ Resources : []string {"pods" },
714+ Verbs : []string {"*" },
715+ },
716+ },
717+ },
718+ },
719+ },
720+ },
721+ },
722+ }
723+
724+ svc := corev1.Service {
725+ ObjectMeta : metav1.ObjectMeta {
726+ Name : "testService" ,
727+ },
728+ }
729+ svc .SetGroupVersionKind (schema.GroupVersionKind {Group : "" , Version : "v1" , Kind : "Service" })
730+ return baseCSV , svc
731+ }
732+
733+ func loadGeneratedManifests (generatedPath string ) ([]client.Object , error ) {
734+ var expectedObjs []client.Object
735+ err := fs .WalkDir (os .DirFS (generatedPath ), "." , func (path string , info fs.DirEntry , err error ) error {
736+ if err != nil || info .IsDir () || filepath .Ext (path ) != ".yaml" {
737+ return err
738+ }
739+ file , err := os .Open (filepath .Join (generatedPath , path ))
740+ if err != nil {
741+ return err
742+ }
743+ defer file .Close ()
744+ result := resource .NewLocalBuilder ().Unstructured ().Stream (file , path ).Do ()
745+ if result .Err () != nil {
746+ return err
747+ }
748+ return result .Visit (func (info * resource.Info , err error ) error {
749+ expectedObjs = append (expectedObjs , info .Object .(* unstructured.Unstructured ))
750+ return nil
751+ })
752+ })
753+ return expectedObjs , err
754+ }
755+
756+ func sortByKindNamespaceName (a client.Object , b client.Object ) int {
757+ return cmp .Or (
758+ cmp .Compare (a .GetObjectKind ().GroupVersionKind ().Kind , b .GetObjectKind ().GroupVersionKind ().Kind ),
759+ cmp .Compare (a .GetNamespace (), b .GetNamespace ()),
760+ cmp .Compare (a .GetName (), b .GetName ()),
761+ )
762+ }
0 commit comments