Skip to content

Commit d695d11

Browse files
committed
fake reconciler for catalog sync tests
Signed-off-by: Ankita Thomas <[email protected]>
1 parent 48b3c3f commit d695d11

File tree

6 files changed

+232
-103
lines changed

6 files changed

+232
-103
lines changed

cmd/manager/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,14 @@ func main() {
9494
os.Exit(1)
9595
}
9696

97-
if err = (&controllers.OperatorReconciler{
97+
if err = controllers.SetupWithManager(&controllers.OperatorReconciler{
9898
Client: mgr.GetClient(),
9999
Scheme: mgr.GetScheme(),
100100
Resolver: solver.NewDeppySolver(
101101
entitysources.NewCatalogdEntitySource(mgr.GetClient()),
102102
olm.NewOLMVariableSource(mgr.GetClient()),
103103
),
104-
}).SetupWithManager(mgr); err != nil {
104+
}, mgr); err != nil {
105105
setupLog.Error(err, "unable to create controller", "controller", "Operator")
106106
os.Exit(1)
107107
}

internal/controllers/catalog_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package controllers_test
2+
3+
import (
4+
"context"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
9+
operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
10+
"github.com/operator-framework/operator-controller/internal/controllers"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
ctrl "sigs.k8s.io/controller-runtime"
13+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
14+
)
15+
16+
func collectRequests(reqChan <-chan string) func() []string {
17+
var reqNames []string
18+
return func() []string {
19+
select {
20+
case req := <-reqChan:
21+
reqNames = append(reqNames, req)
22+
default:
23+
}
24+
return reqNames
25+
}
26+
}
27+
28+
var _ = Describe("SetupWithManager", func() {
29+
When("catalog events occur", func() {
30+
var cancel context.CancelFunc
31+
var ctx context.Context
32+
var opNames []string
33+
var reqChan chan string
34+
BeforeEach(func() {
35+
ctx = context.Background()
36+
mgr, err := ctrl.NewManager(cfg, ctrl.Options{Scheme: sch})
37+
Expect(err).To(BeNil())
38+
39+
opNames = []string{"prometheus", "project-quay"}
40+
reqChan = make(chan string, 4*len(opNames))
41+
var fakeReconciler reconcile.Func = func(_ context.Context, request ctrl.Request) (ctrl.Result, error) {
42+
reqChan <- request.Name
43+
return ctrl.Result{}, nil
44+
}
45+
46+
err = controllers.SetupWithManager(fakeReconciler, mgr)
47+
Expect(err).To(BeNil())
48+
49+
var mgrCtx context.Context
50+
mgrCtx, cancel = context.WithCancel(ctx)
51+
52+
go func() {
53+
err := mgr.Start(mgrCtx)
54+
Expect(err).To(BeNil())
55+
}()
56+
57+
for _, p := range opNames {
58+
op := &operatorsv1alpha1.Operator{ObjectMeta: metav1.ObjectMeta{Name: p}, Spec: operatorsv1alpha1.OperatorSpec{PackageName: p}}
59+
err := cl.Create(ctx, op)
60+
Expect(err).To(BeNil())
61+
}
62+
By("verifying initial reconcile logs for operator creation")
63+
Eventually(collectRequests(reqChan)).Should(ConsistOf(opNames))
64+
})
65+
It("reconciles all affected operators on cluster", func() {
66+
By("creating a new catalog")
67+
catalog := &catalogd.Catalog{ObjectMeta: metav1.ObjectMeta{Name: "t"}, Spec: catalogd.CatalogSpec{Source: catalogd.CatalogSource{Type: catalogd.SourceTypeImage, Image: &catalogd.ImageSource{}}}}
68+
err := cl.Create(ctx, catalog)
69+
Expect(err).To(BeNil())
70+
By("verifying operator reconcile logs on catalog create")
71+
Eventually(collectRequests(reqChan)).Should(ConsistOf(opNames))
72+
73+
By("updating a catalog")
74+
catalog.Spec.Source.Image.Ref = "s"
75+
err = cl.Update(ctx, catalog)
76+
Expect(err).To(BeNil())
77+
By("verifying operator reconcile logs on catalog update")
78+
Eventually(collectRequests(reqChan)).Should(ConsistOf(opNames))
79+
80+
By("deleting a catalog")
81+
err = cl.Delete(ctx, catalog)
82+
Expect(err).To(BeNil())
83+
By("verifying operator reconcile logs on catalog delete")
84+
Eventually(collectRequests(reqChan)).Should(ConsistOf(opNames))
85+
})
86+
AfterEach(func() {
87+
cancel() // stop manager
88+
for _, p := range opNames {
89+
op := &operatorsv1alpha1.Operator{ObjectMeta: metav1.ObjectMeta{Name: p}, Spec: operatorsv1alpha1.OperatorSpec{PackageName: p}}
90+
Expect(cl.Delete(ctx, op)).To(Succeed())
91+
}
92+
})
93+
})
94+
})

internal/controllers/operator_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha
298298
}
299299

300300
// SetupWithManager sets up the controller with the Manager.
301-
func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error {
301+
func SetupWithManager(r reconcile.Reconciler, mgr ctrl.Manager) error {
302302
err := ctrl.NewControllerManagedBy(mgr).
303303
For(&operatorsv1alpha1.Operator{}).
304304
Watches(source.NewKindWithCache(&catalogd.Catalog{}, mgr.GetCache()),

internal/controllers/operator_controller_test.go

Lines changed: 0 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@ package controllers_test
33
import (
44
"context"
55
"fmt"
6-
"strings"
7-
"time"
86

9-
"github.com/go-logr/logr/funcr"
107
. "github.com/onsi/ginkgo/v2"
118
. "github.com/onsi/gomega"
12-
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
139
"github.com/operator-framework/deppy/pkg/deppy"
1410
"github.com/operator-framework/deppy/pkg/deppy/input"
1511
"github.com/operator-framework/deppy/pkg/deppy/solver"
@@ -22,7 +18,6 @@ import (
2218
ctrl "sigs.k8s.io/controller-runtime"
2319
"sigs.k8s.io/controller-runtime/pkg/client"
2420
"sigs.k8s.io/controller-runtime/pkg/client/fake"
25-
"sigs.k8s.io/controller-runtime/pkg/log"
2621

2722
operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
2823
"github.com/operator-framework/operator-controller/internal/conditionsets"
@@ -1062,94 +1057,6 @@ var _ = Describe("Operator Controller Test", func() {
10621057
})
10631058
})
10641059
})
1065-
When("a catalog changes on cluster", func() {
1066-
var testLogs, opNames []string
1067-
var cancel context.CancelFunc
1068-
var logCount int
1069-
BeforeEach(func() {
1070-
l := funcr.New(func(prefix, args string) {
1071-
if prefix == "operator-controller" &&
1072-
strings.Contains(args, `"controller"="operator"`) &&
1073-
strings.Contains(args, `"msg"="ending"`) {
1074-
// filter for only relevant logs
1075-
testLogs = append(testLogs, fmt.Sprintf("%s", args))
1076-
}
1077-
}, funcr.Options{Verbosity: 1})
1078-
mgr, err := ctrl.NewManager(cfg, ctrl.Options{Scheme: sch, Logger: l})
1079-
Expect(err).To(BeNil())
1080-
1081-
err = reconciler.SetupWithManager(mgr)
1082-
Expect(err).To(BeNil())
1083-
var mgrCtx context.Context
1084-
mgrCtx, cancel = context.WithCancel(log.IntoContext(ctx, l))
1085-
1086-
go func() {
1087-
err := mgr.Start(mgrCtx)
1088-
Expect(err).To(BeNil())
1089-
}()
1090-
1091-
opNames = []string{"prometheus", "project-quay"}
1092-
for _, p := range opNames {
1093-
op := &operatorsv1alpha1.Operator{ObjectMeta: metav1.ObjectMeta{Name: p}, Spec: operatorsv1alpha1.OperatorSpec{PackageName: p}}
1094-
err := cl.Create(ctx, op)
1095-
Expect(err).To(BeNil())
1096-
}
1097-
Eventually(func(g Gomega) {
1098-
By("verifying initial reconcile logs for operator creation")
1099-
g.Expect(len(testLogs) >= len(opNames)).To(BeTrue())
1100-
for _, p := range opNames {
1101-
g.Expect(testLogs[len(testLogs)-len(opNames):]).To(ContainElement(ContainSubstring(fmt.Sprintf("\"Operator\"={\"name\":\"%s\"}", p))))
1102-
}
1103-
logCount = len(testLogs)
1104-
}).WithTimeout(2 * time.Second).WithPolling(1 * time.Second).Should(Succeed())
1105-
})
1106-
1107-
It("reconciles all affected operators on cluster", func() {
1108-
By("creating a new catalog")
1109-
catalog := &catalogd.Catalog{ObjectMeta: metav1.ObjectMeta{Name: "t"}, Spec: catalogd.CatalogSpec{Source: catalogd.CatalogSource{Type: catalogd.SourceTypeImage, Image: &catalogd.ImageSource{}}}}
1110-
err := cl.Create(ctx, catalog)
1111-
Expect(err).To(BeNil())
1112-
Eventually(func(g Gomega) {
1113-
By("verifying operator reconcile logs on catalog create")
1114-
g.Expect(testLogs).To(HaveLen(logCount + len(opNames)))
1115-
for _, p := range opNames {
1116-
g.Expect(testLogs[len(testLogs)-len(opNames):]).To(ContainElement(ContainSubstring(fmt.Sprintf("\"Operator\"={\"name\":\"%s\"}", p))))
1117-
}
1118-
logCount = len(testLogs)
1119-
}).WithTimeout(2 * time.Second).WithPolling(1 * time.Second).Should(Succeed())
1120-
1121-
By("updating a catalog")
1122-
catalog.Spec.Source.Image.Ref = "s"
1123-
err = cl.Update(ctx, catalog)
1124-
Expect(err).To(BeNil())
1125-
Eventually(func(g Gomega) {
1126-
By("verifying operator reconcile logs on catalog update")
1127-
g.Expect(testLogs).To(HaveLen(logCount + len(opNames)))
1128-
for _, p := range opNames {
1129-
g.Expect(testLogs[len(testLogs)-len(opNames):]).To(ContainElement(ContainSubstring(fmt.Sprintf("\"Operator\"={\"name\":\"%s\"}", p))))
1130-
}
1131-
logCount = len(testLogs)
1132-
}).WithTimeout(2 * time.Second).WithPolling(1 * time.Second).Should(Succeed())
1133-
1134-
By("deleting a catalog")
1135-
err = cl.Delete(ctx, catalog)
1136-
Expect(err).To(BeNil())
1137-
Eventually(func(g Gomega) {
1138-
By("verifying operator reconcile logs on catalog delete")
1139-
g.Expect(testLogs).To(HaveLen(logCount + len(opNames)))
1140-
for _, p := range opNames {
1141-
g.Expect(testLogs[len(testLogs)-len(opNames):]).To(ContainElement(ContainSubstring(fmt.Sprintf("\"Operator\"={\"name\":\"%s\"}", p))))
1142-
}
1143-
}).WithTimeout(2 * time.Second).WithPolling(1 * time.Second).Should(Succeed())
1144-
})
1145-
AfterEach(func() {
1146-
for _, p := range opNames {
1147-
op := &operatorsv1alpha1.Operator{ObjectMeta: metav1.ObjectMeta{Name: p}, Spec: operatorsv1alpha1.OperatorSpec{PackageName: p}}
1148-
Expect(cl.Delete(ctx, op)).To(BeNil())
1149-
}
1150-
cancel() // stop manager
1151-
})
1152-
})
11531060
})
11541061

11551062
func verifyInvariants(ctx context.Context, c client.Client, op *operatorsv1alpha1.Operator) {
@@ -1188,12 +1095,6 @@ var testEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{
11881095
"olm.package": `{"packageName":"prometheus","version":"0.47.0"}`,
11891096
"olm.gvk": `[]`,
11901097
}),
1191-
"operatorhub/project-quay/3.8.3": *input.NewEntity("operatorhub/project-quay/3.8.3", map[string]string{
1192-
"olm.bundle.path": `"quay.io/openshift-community-operators/project-quay@sha256:4f5698b5fec2e5f9a4df78b5ef9609b3a697c81cdb137b98e82e79104f0eb3b5"`,
1193-
"olm.channel": `{"channelName":"stable","priority":0}`,
1194-
"olm.package": `{"packageName":"project-quay","version":"3.8.3"}`,
1195-
"olm.gvk": `[]`,
1196-
}),
11971098
"operatorhub/badimage/0.1.0": *input.NewEntity("operatorhub/badimage/0.1.0", map[string]string{
11981099
"olm.bundle.path": `{"name": "quay.io/operatorhubio/badimage:v0.1.0"}`,
11991100
"olm.package": `{"packageName":"badimage","version":"0.1.0"}`,

internal/controllers/suite_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323

2424
. "github.com/onsi/ginkgo/v2"
2525
. "github.com/onsi/gomega"
26-
catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1"
26+
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
2727
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
2828
"k8s.io/apimachinery/pkg/api/meta"
2929
"k8s.io/apimachinery/pkg/runtime"
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
apiVersion: apiextensions.k8s.io/v1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
annotations:
5+
controller-gen.kubebuilder.io/version: v0.11.4
6+
name: catalogs.catalogd.operatorframework.io
7+
spec:
8+
group: catalogd.operatorframework.io
9+
names:
10+
kind: Catalog
11+
listKind: CatalogList
12+
plural: catalogs
13+
singular: catalog
14+
scope: Cluster
15+
versions:
16+
- name: v1alpha1
17+
schema:
18+
openAPIV3Schema:
19+
description: Catalog is the Schema for the Catalogs API
20+
properties:
21+
apiVersion:
22+
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
23+
type: string
24+
kind:
25+
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
26+
type: string
27+
metadata:
28+
type: object
29+
spec:
30+
description: CatalogSpec defines the desired state of Catalog
31+
properties:
32+
source:
33+
description: Source is the source of a Catalog that contains Operators' metadata in the FBC format https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs
34+
oneOf:
35+
- required:
36+
- image
37+
properties:
38+
image:
39+
description: Image is the catalog image that backs the content of this catalog.
40+
properties:
41+
pullSecret:
42+
description: PullSecret contains the name of the image pull secret in the namespace that catalogd is deployed.
43+
type: string
44+
ref:
45+
description: Ref contains the reference to a container image containing Catalog contents.
46+
type: string
47+
required:
48+
- ref
49+
type: object
50+
type:
51+
description: Type defines the kind of Catalog content being sourced.
52+
type: string
53+
required:
54+
- type
55+
type: object
56+
required:
57+
- source
58+
type: object
59+
status:
60+
description: CatalogStatus defines the observed state of Catalog
61+
properties:
62+
conditions:
63+
description: Conditions store the status conditions of the Catalog instances
64+
items:
65+
description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
66+
properties:
67+
lastTransitionTime:
68+
description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
69+
format: date-time
70+
type: string
71+
message:
72+
description: message is a human readable message indicating details about the transition. This may be an empty string.
73+
maxLength: 32768
74+
type: string
75+
observedGeneration:
76+
description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.
77+
format: int64
78+
minimum: 0
79+
type: integer
80+
reason:
81+
description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.
82+
maxLength: 1024
83+
minLength: 1
84+
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
85+
type: string
86+
status:
87+
description: status of the condition, one of True, False, Unknown.
88+
enum:
89+
- "True"
90+
- "False"
91+
- Unknown
92+
type: string
93+
type:
94+
description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
95+
maxLength: 316
96+
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
97+
type: string
98+
required:
99+
- lastTransitionTime
100+
- message
101+
- reason
102+
- status
103+
- type
104+
type: object
105+
type: array
106+
phase:
107+
type: string
108+
resolvedSource:
109+
description: CatalogSource contains the sourcing information for a Catalog
110+
properties:
111+
image:
112+
description: Image is the catalog image that backs the content of this catalog.
113+
properties:
114+
pullSecret:
115+
description: PullSecret contains the name of the image pull secret in the namespace that catalogd is deployed.
116+
type: string
117+
ref:
118+
description: Ref contains the reference to a container image containing Catalog contents.
119+
type: string
120+
required:
121+
- ref
122+
type: object
123+
type:
124+
description: Type defines the kind of Catalog content being sourced.
125+
type: string
126+
required:
127+
- type
128+
type: object
129+
type: object
130+
type: object
131+
served: true
132+
storage: true
133+
subresources:
134+
status: {}

0 commit comments

Comments
 (0)