Skip to content

Commit 006168a

Browse files
committed
Extract shared watch map helpers into internal/common
Replace duplicated controller watch map functions with generic helpers in internal/common/watch.go.
1 parent decb6bf commit 006168a

10 files changed

Lines changed: 285 additions & 530 deletions

internal/common/watch.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
Copyright 2026.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package common //nolint:revive // common is the established package name for multi-group shared code
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
"github.com/go-logr/logr"
24+
"github.com/openstack-k8s-operators/lib-common/modules/common"
25+
util "github.com/openstack-k8s-operators/lib-common/modules/common/util"
26+
"k8s.io/apimachinery/pkg/fields"
27+
"k8s.io/apimachinery/pkg/types"
28+
"sigs.k8s.io/controller-runtime/pkg/client"
29+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
30+
)
31+
32+
// FindObjectsForSrcByField returns reconcile requests for CRs in src's namespace
33+
// that reference src via one of the indexed watchFields.
34+
func FindObjectsForSrcByField[L client.ObjectList, E any, S ~[]E](
35+
ctx context.Context,
36+
log logr.Logger,
37+
reader client.Reader,
38+
src client.Object,
39+
watchFields []string,
40+
newList func() L,
41+
getItems func(L) S,
42+
) []reconcile.Request {
43+
requests := []reconcile.Request{}
44+
45+
for _, field := range watchFields {
46+
crList := newList()
47+
listOps := &client.ListOptions{
48+
FieldSelector: fields.OneTermEqualSelector(field, src.GetName()),
49+
Namespace: src.GetNamespace(),
50+
}
51+
err := reader.List(ctx, crList, listOps)
52+
if err != nil {
53+
log.Error(err, fmt.Sprintf("listing %s for field: %s - %s", crList.GetObjectKind().GroupVersionKind().Kind, field, src.GetNamespace()))
54+
return requests
55+
}
56+
57+
requests = appendRequestsForObjects(log, src, requests, itemsAsObjects(getItems(crList)))
58+
}
59+
60+
return requests
61+
}
62+
63+
// FindObjectsForSrcInNamespace returns reconcile requests for all CRs in src's namespace.
64+
func FindObjectsForSrcInNamespace[L client.ObjectList, E any, S ~[]E](
65+
ctx context.Context,
66+
log logr.Logger,
67+
reader client.Reader,
68+
src client.Object,
69+
newList func() L,
70+
getItems func(L) S,
71+
) []reconcile.Request {
72+
requests := []reconcile.Request{}
73+
74+
crList := newList()
75+
listOps := &client.ListOptions{
76+
Namespace: src.GetNamespace(),
77+
}
78+
err := reader.List(ctx, crList, listOps)
79+
if err != nil {
80+
log.Error(err, fmt.Sprintf("listing %s for namespace: %s", crList.GetObjectKind().GroupVersionKind().Kind, src.GetNamespace()))
81+
return requests
82+
}
83+
84+
return appendRequestsForObjects(log, src, requests, itemsAsObjects(getItems(crList)))
85+
}
86+
87+
// FindObjectsWithAppSelectorLabelInNamespace returns reconcile requests for all CRs
88+
// in src's namespace when src carries an AppSelector label matching allowedServices.
89+
func FindObjectsWithAppSelectorLabelInNamespace[L client.ObjectList, E any, S ~[]E](
90+
ctx context.Context,
91+
log logr.Logger,
92+
reader client.Reader,
93+
src client.Object,
94+
allowedServices []string,
95+
newList func() L,
96+
getItems func(L) S,
97+
) []reconcile.Request {
98+
// if the endpoint has the service label and its in our endpointList, reconcile the CR in the namespace
99+
if svc, ok := src.GetLabels()[common.AppSelector]; ok && util.StringInSlice(svc, allowedServices) {
100+
return FindObjectsForSrcInNamespace(ctx, log, reader, src, newList, getItems)
101+
}
102+
103+
return []reconcile.Request{}
104+
}
105+
106+
func itemsAsObjects[E any, S ~[]E](items S) []client.Object {
107+
objs := make([]client.Object, len(items))
108+
for i := range items {
109+
objs[i] = any(&items[i]).(client.Object)
110+
}
111+
return objs
112+
}
113+
114+
func appendRequestsForObjects(
115+
log logr.Logger,
116+
src client.Object,
117+
requests []reconcile.Request,
118+
items []client.Object,
119+
) []reconcile.Request {
120+
for _, item := range items {
121+
log.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace()))
122+
123+
requests = append(requests,
124+
reconcile.Request{
125+
NamespacedName: types.NamespacedName{
126+
Name: item.GetName(),
127+
Namespace: item.GetNamespace(),
128+
},
129+
},
130+
)
131+
}
132+
133+
return requests
134+
}

internal/controller/nova/nova_controller.go

Lines changed: 17 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
rbacv1 "k8s.io/api/rbac/v1"
2929
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3030
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31-
"k8s.io/apimachinery/pkg/fields"
3231
"k8s.io/apimachinery/pkg/types"
3332
ctrl "sigs.k8s.io/controller-runtime"
3433
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -2269,68 +2268,26 @@ func (r *NovaReconciler) ensureTopLevelSecret(
22692268
}
22702269

22712270
func (r *NovaReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request {
2272-
requests := []reconcile.Request{}
2273-
2274-
Log := r.GetLogger(ctx)
2275-
2276-
for _, field := range novaWatchFields {
2277-
crList := &novav1.NovaList{}
2278-
listOps := &client.ListOptions{
2279-
FieldSelector: fields.OneTermEqualSelector(field, src.GetName()),
2280-
Namespace: src.GetNamespace(),
2281-
}
2282-
err := r.Client.List(ctx, crList, listOps)
2283-
if err != nil {
2284-
Log.Error(err, fmt.Sprintf("listing %s for field: %s - %s", crList.GroupVersionKind().Kind, field, src.GetNamespace()))
2285-
return requests
2286-
}
2287-
2288-
for _, item := range crList.Items {
2289-
Log.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace()))
2290-
2291-
requests = append(requests,
2292-
reconcile.Request{
2293-
NamespacedName: types.NamespacedName{
2294-
Name: item.GetName(),
2295-
Namespace: item.GetNamespace(),
2296-
},
2297-
},
2298-
)
2299-
}
2300-
}
2301-
2302-
return requests
2271+
return internalcommon.FindObjectsForSrcByField(
2272+
ctx,
2273+
r.GetLogger(ctx),
2274+
r.Client,
2275+
src,
2276+
novaWatchFields,
2277+
func() *novav1.NovaList { return &novav1.NovaList{} },
2278+
func(l *novav1.NovaList) []novav1.Nova { return l.Items },
2279+
)
23032280
}
23042281

23052282
func (r *NovaReconciler) findObjectForSrc(ctx context.Context, src client.Object) []reconcile.Request {
2306-
requests := []reconcile.Request{}
2307-
2308-
Log := r.GetLogger(ctx)
2309-
2310-
crList := &novav1.NovaList{}
2311-
listOps := &client.ListOptions{
2312-
Namespace: src.GetNamespace(),
2313-
}
2314-
err := r.Client.List(ctx, crList, listOps)
2315-
if err != nil {
2316-
Log.Error(err, fmt.Sprintf("listing %s for namespace: %s", crList.GroupVersionKind().Kind, src.GetNamespace()))
2317-
return requests
2318-
}
2319-
2320-
for _, item := range crList.Items {
2321-
Log.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace()))
2322-
2323-
requests = append(requests,
2324-
reconcile.Request{
2325-
NamespacedName: types.NamespacedName{
2326-
Name: item.GetName(),
2327-
Namespace: item.GetNamespace(),
2328-
},
2329-
},
2330-
)
2331-
}
2332-
2333-
return requests
2283+
return internalcommon.FindObjectsForSrcInNamespace(
2284+
ctx,
2285+
r.GetLogger(ctx),
2286+
r.Client,
2287+
src,
2288+
func() *novav1.NovaList { return &novav1.NovaList{} },
2289+
func(l *novav1.NovaList) []novav1.Nova { return l.Items },
2290+
)
23342291
}
23352292

23362293
func (r *NovaReconciler) memcachedNamespaceMapFunc(ctx context.Context, src client.Object) []reconcile.Request {

internal/controller/nova/novaapi_controller.go

Lines changed: 18 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323

2424
v1 "k8s.io/api/apps/v1"
2525
corev1 "k8s.io/api/core/v1"
26-
"k8s.io/apimachinery/pkg/fields"
2726
"k8s.io/apimachinery/pkg/types"
2827
"k8s.io/utils/ptr"
2928
ctrl "sigs.k8s.io/controller-runtime"
@@ -928,71 +927,27 @@ func getAPIServiceLabels() map[string]string {
928927
}
929928

930929
func (r *NovaAPIReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request {
931-
requests := []reconcile.Request{}
932-
933-
Log := r.GetLogger(ctx)
934-
935-
for _, field := range apiWatchFields {
936-
crList := &novav1.NovaAPIList{}
937-
listOps := &client.ListOptions{
938-
FieldSelector: fields.OneTermEqualSelector(field, src.GetName()),
939-
Namespace: src.GetNamespace(),
940-
}
941-
err := r.Client.List(ctx, crList, listOps)
942-
if err != nil {
943-
Log.Error(err, fmt.Sprintf("listing %s for field: %s - %s", crList.GroupVersionKind().Kind, field, src.GetNamespace()))
944-
return requests
945-
}
946-
947-
for _, item := range crList.Items {
948-
Log.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace()))
949-
950-
requests = append(requests,
951-
reconcile.Request{
952-
NamespacedName: types.NamespacedName{
953-
Name: item.GetName(),
954-
Namespace: item.GetNamespace(),
955-
},
956-
},
957-
)
958-
}
959-
}
960-
961-
return requests
930+
return internalcommon.FindObjectsForSrcByField(
931+
ctx,
932+
r.GetLogger(ctx),
933+
r.Client,
934+
src,
935+
apiWatchFields,
936+
func() *novav1.NovaAPIList { return &novav1.NovaAPIList{} },
937+
func(l *novav1.NovaAPIList) []novav1.NovaAPI { return l.Items },
938+
)
962939
}
963940

964941
func (r *NovaAPIReconciler) findObjectsWithAppSelectorLabelInNamespace(ctx context.Context, src client.Object) []reconcile.Request {
965-
requests := []reconcile.Request{}
966-
967-
Log := r.GetLogger(ctx)
968-
969-
// if the endpoint has the service label and its in our endpointList, reconcile the CR in the namespace
970-
if svc, ok := src.GetLabels()[common.AppSelector]; ok && util.StringInSlice(svc, endpointList) {
971-
crList := &novav1.NovaAPIList{}
972-
listOps := &client.ListOptions{
973-
Namespace: src.GetNamespace(),
974-
}
975-
err := r.Client.List(ctx, crList, listOps)
976-
if err != nil {
977-
Log.Error(err, fmt.Sprintf("listing %s for namespace: %s", crList.GroupVersionKind().Kind, src.GetNamespace()))
978-
return requests
979-
}
980-
981-
for _, item := range crList.Items {
982-
Log.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace()))
983-
984-
requests = append(requests,
985-
reconcile.Request{
986-
NamespacedName: types.NamespacedName{
987-
Name: item.GetName(),
988-
Namespace: item.GetNamespace(),
989-
},
990-
},
991-
)
992-
}
993-
}
994-
995-
return requests
942+
return internalcommon.FindObjectsWithAppSelectorLabelInNamespace(
943+
ctx,
944+
r.GetLogger(ctx),
945+
r.Client,
946+
src,
947+
endpointList,
948+
func() *novav1.NovaAPIList { return &novav1.NovaAPIList{} },
949+
func(l *novav1.NovaAPIList) []novav1.NovaAPI { return l.Items },
950+
)
996951
}
997952

998953
// fields to index to reconcile when change

internal/controller/nova/novacell_controller.go

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
corev1 "k8s.io/api/core/v1"
2525
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27-
"k8s.io/apimachinery/pkg/fields"
2827
"k8s.io/apimachinery/pkg/types"
2928
"k8s.io/utils/ptr"
3029
ctrl "sigs.k8s.io/controller-runtime"
@@ -867,37 +866,15 @@ func (r *NovaCellReconciler) getVNCProxyURL(
867866
}
868867

869868
func (r *NovaCellReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request {
870-
requests := []reconcile.Request{}
871-
872-
Log := r.GetLogger(ctx)
873-
874-
for _, field := range cellWatchFields {
875-
crList := &novav1.NovaCellList{}
876-
listOps := &client.ListOptions{
877-
FieldSelector: fields.OneTermEqualSelector(field, src.GetName()),
878-
Namespace: src.GetNamespace(),
879-
}
880-
err := r.Client.List(ctx, crList, listOps)
881-
if err != nil {
882-
Log.Error(err, fmt.Sprintf("listing %s for field: %s - %s", crList.GroupVersionKind().Kind, field, src.GetNamespace()))
883-
return requests
884-
}
885-
886-
for _, item := range crList.Items {
887-
Log.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace()))
888-
889-
requests = append(requests,
890-
reconcile.Request{
891-
NamespacedName: types.NamespacedName{
892-
Name: item.GetName(),
893-
Namespace: item.GetNamespace(),
894-
},
895-
},
896-
)
897-
}
898-
}
899-
900-
return requests
869+
return internalcommon.FindObjectsForSrcByField(
870+
ctx,
871+
r.GetLogger(ctx),
872+
r.Client,
873+
src,
874+
cellWatchFields,
875+
func() *novav1.NovaCellList { return &novav1.NovaCellList{} },
876+
func(l *novav1.NovaCellList) []novav1.NovaCell { return l.Items },
877+
)
901878
}
902879

903880
// fields to index to reconcile when change

0 commit comments

Comments
 (0)