Skip to content

Commit 5d1b59d

Browse files
author
Per Goncalves da Silva
committed
add regression testing
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent d3aec37 commit 5d1b59d

File tree

83 files changed

+134674
-69
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+134674
-69
lines changed

internal/operator-controller/rukpak/convert/registryv1_test.go

Lines changed: 179 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package convert_test
22

33
import (
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

55116
func 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-
250256
func 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+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## registry+v1 bundle generation regression tests
2+
3+
This directory includes test cases for the rukpak/convert package based on real bundle data.
4+
The manifests are generated and manually/visually verified for correctness.
5+
6+
The `generate-manifests.go` tool is used to generate the tests cases by calling convert.Convert on bundles
7+
in the `testdata` directory.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
creationTimestamp: null
5+
labels:
6+
control-plane: controller-manager
7+
name: argocd-operator-controller-manager-metrics-service
8+
spec:
9+
ports:
10+
- name: https
11+
port: 8443
12+
targetPort: https
13+
selector:
14+
control-plane: controller-manager
15+
status:
16+
loadBalancer: {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: v1
2+
data:
3+
controller_manager_config.yaml: |
4+
apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
5+
kind: ControllerManagerConfig
6+
health:
7+
healthProbeBindAddress: :8081
8+
metrics:
9+
bindAddress: 127.0.0.1:8080
10+
webhook:
11+
port: 9443
12+
leaderElection:
13+
leaderElect: true
14+
resourceName: b674928d.argoproj.io
15+
kind: ConfigMap
16+
metadata:
17+
name: argocd-operator-manager-config
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: ClusterRole
3+
metadata:
4+
creationTimestamp: null
5+
name: argocd-operator-metrics-reader
6+
rules:
7+
- nonResourceURLs:
8+
- /metrics
9+
verbs:
10+
- get

0 commit comments

Comments
 (0)