Skip to content

Commit 1989ffd

Browse files
committed
feat: watch and reconcile updates to the admin clusterrole
Signed-off-by: Chetan Banavikalmutt <chetanrns1997@gmail.com>
1 parent 2df7e8e commit 1989ffd

3 files changed

Lines changed: 167 additions & 2 deletions

File tree

cmd/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
argov1alpha1api "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
3636
argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1"
3737
argocdcommon "github.com/argoproj-labs/argocd-operator/common"
38+
"github.com/argoproj-labs/argocd-operator/controllers/argocd"
3839
argocdprovisioner "github.com/argoproj-labs/argocd-operator/controllers/argocd"
3940
notificationsprovisioner "github.com/argoproj-labs/argocd-operator/controllers/notificationsconfiguration"
4041
appsv1 "github.com/openshift/api/apps/v1"
@@ -221,6 +222,8 @@ func main() {
221222
os.Exit(1)
222223
}
223224

225+
argocd.Register(openshift.ReconcilerHook, openshift.BuilderHook)
226+
224227
if err = (&argocdprovisioner.ReconcileArgoCD{
225228
Client: mgr.GetClient(),
226229
Scheme: mgr.GetScheme(),
@@ -268,8 +271,6 @@ func main() {
268271
os.Exit(1)
269272
}
270273

271-
argocdprovisioner.Register(openshift.ReconcilerHook)
272-
273274
setupLog.Info("starting manager")
274275
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
275276
setupLog.Error(err, "problem running manager")

controllers/argocd/openshift/openshift.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@ import (
1414
appsv1 "k8s.io/api/apps/v1"
1515
corev1 "k8s.io/api/core/v1"
1616
rbacv1 "k8s.io/api/rbac/v1"
17+
v1 "k8s.io/api/rbac/v1"
1718
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1819
"k8s.io/client-go/kubernetes"
20+
"sigs.k8s.io/controller-runtime/pkg/builder"
21+
"sigs.k8s.io/controller-runtime/pkg/client"
1922
"sigs.k8s.io/controller-runtime/pkg/client/config"
23+
"sigs.k8s.io/controller-runtime/pkg/handler"
2024
logf "sigs.k8s.io/controller-runtime/pkg/log"
25+
"sigs.k8s.io/controller-runtime/pkg/predicate"
26+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2127
)
2228

2329
var log = logf.Log.WithName("openshift_controller_argocd")
@@ -101,6 +107,26 @@ func ReconcilerHook(cr *argoapp.ArgoCD, v interface{}, hint string) error {
101107
return nil
102108
}
103109

110+
// BuilderHook updates the Argo CD controller builder to watch for changes to the "admin" ClusterRole
111+
func BuilderHook(_ *argoapp.ArgoCD, v interface{}, _ string) error {
112+
logv := log.WithValues("module", "builder-hook")
113+
114+
bldr, ok := v.(*argocd.BuilderHook)
115+
if !ok {
116+
return nil
117+
}
118+
119+
logv.Info("updating the Argo CD controller to watch for changes to the admin ClusterRole")
120+
121+
clusterResourceHandler := handler.EnqueueRequestsFromMapFunc(adminClusterRoleMapper(bldr.Client))
122+
bldr.Builder.Watches(&v1.ClusterRole{}, clusterResourceHandler,
123+
builder.WithPredicates(predicate.NewPredicateFuncs(func(o client.Object) bool {
124+
return o.GetName() == "admin"
125+
})))
126+
127+
return nil
128+
}
129+
104130
func getPolicyRuleForApplicationController() []rbacv1.PolicyRule {
105131
return []rbacv1.PolicyRule{
106132
{
@@ -387,3 +413,33 @@ func initK8sClient() (*kubernetes.Clientset, error) {
387413

388414
return kClient, nil
389415
}
416+
417+
// adminClusterRoleMapper maps changes to the "admin" ClusterRole to all Argo CD instances in the cluster
418+
func adminClusterRoleMapper(k8sClient client.Client) handler.MapFunc {
419+
return func(ctx context.Context, o client.Object) []reconcile.Request {
420+
var result = []reconcile.Request{}
421+
422+
// Only process the "admin" ClusterRole
423+
if o.GetName() != "admin" {
424+
return result
425+
}
426+
427+
// Get all Argo CD instances in all namespaces
428+
argocds := &argoapp.ArgoCDList{}
429+
if err := k8sClient.List(ctx, argocds, &client.ListOptions{}); err != nil {
430+
log.Error(err, "failed to list Argo CD instances for admin ClusterRole mapping")
431+
return result
432+
}
433+
434+
// Create reconcile requests for all Argo CD instances
435+
for _, argocd := range argocds.Items {
436+
namespacedName := client.ObjectKey{
437+
Name: argocd.Name,
438+
Namespace: argocd.Namespace,
439+
}
440+
result = append(result, reconcile.Request{NamespacedName: namespacedName})
441+
}
442+
443+
return result
444+
}
445+
}

controllers/argocd/openshift/openshift_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
package openshift
22

33
import (
4+
"context"
5+
"sort"
46
"testing"
57

68
corev1 "k8s.io/api/core/v1"
79
rbacv1 "k8s.io/api/rbac/v1"
810
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/client-go/kubernetes/scheme"
912

13+
argoapp "github.com/argoproj-labs/argocd-operator/api/v1beta1"
1014
"github.com/stretchr/testify/assert"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
16+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
17+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1118
)
1219

1320
func TestReconcileArgoCD_reconcileApplicableClusterRole(t *testing.T) {
@@ -207,3 +214,104 @@ func TestReconcileArgoCD_reconcileSecrets(t *testing.T) {
207214
assert.NoError(t, ReconcilerHook(a, testSecret, ""))
208215
assert.Equal(t, string(testSecret.Data["namespaces"]), "someRandomNamespace")
209216
}
217+
218+
func TestAdminClusterRoleMapper(t *testing.T) {
219+
s := scheme.Scheme
220+
s.AddKnownTypes(argoapp.GroupVersion, &argoapp.ArgoCD{}, &argoapp.ArgoCDList{})
221+
222+
t.Run("non-admin object returns empty result", func(t *testing.T) {
223+
fakeClient := fake.NewClientBuilder().WithScheme(s).Build()
224+
225+
mapFunc := adminClusterRoleMapper(fakeClient)
226+
227+
nonAdminClusterRole := &rbacv1.ClusterRole{
228+
ObjectMeta: metav1.ObjectMeta{
229+
Name: "not-admin",
230+
},
231+
}
232+
233+
result := mapFunc(context.TODO(), nonAdminClusterRole)
234+
235+
assert.Empty(t, result)
236+
})
237+
238+
t.Run("admin object with no Argo CD instances returns empty result", func(t *testing.T) {
239+
fakeClient := fake.NewClientBuilder().WithScheme(s).Build()
240+
241+
mapFunc := adminClusterRoleMapper(fakeClient)
242+
243+
// Create admin cluster role
244+
adminClusterRole := &rbacv1.ClusterRole{
245+
ObjectMeta: metav1.ObjectMeta{
246+
Name: "admin",
247+
},
248+
}
249+
250+
result := mapFunc(context.TODO(), adminClusterRole)
251+
252+
assert.Empty(t, result)
253+
})
254+
255+
t.Run("admin object with Argo CD instances returns reconcile requests", func(t *testing.T) {
256+
// Create test Argo CD instances
257+
argocd1 := &argoapp.ArgoCD{
258+
ObjectMeta: metav1.ObjectMeta{
259+
Name: "argocd-1",
260+
Namespace: "namespace-1",
261+
},
262+
}
263+
264+
argocd2 := &argoapp.ArgoCD{
265+
ObjectMeta: metav1.ObjectMeta{
266+
Name: "argocd-2",
267+
Namespace: "namespace-2",
268+
},
269+
}
270+
271+
// Create fake client with Argo CD instances
272+
fakeClient := fake.NewClientBuilder().
273+
WithScheme(s).
274+
WithObjects(argocd1, argocd2).
275+
Build()
276+
277+
mapFunc := adminClusterRoleMapper(fakeClient)
278+
279+
// Create admin cluster role
280+
adminClusterRole := &rbacv1.ClusterRole{
281+
ObjectMeta: metav1.ObjectMeta{
282+
Name: "admin",
283+
},
284+
}
285+
286+
result := mapFunc(context.TODO(), adminClusterRole)
287+
288+
// Should return reconcile requests for both Argo CD instances
289+
assert.Len(t, result, 2)
290+
291+
// Check that the reconcile requests contain the correct namespaced names
292+
expectedRequests := []reconcile.Request{
293+
{
294+
NamespacedName: client.ObjectKey{
295+
Name: "argocd-1",
296+
Namespace: "namespace-1",
297+
},
298+
},
299+
{
300+
NamespacedName: client.ObjectKey{
301+
Name: "argocd-2",
302+
Namespace: "namespace-2",
303+
},
304+
},
305+
}
306+
307+
// Sort both slices to ensure consistent comparison
308+
sort.Slice(result, func(i, j int) bool {
309+
return result[i].NamespacedName.Name < result[j].NamespacedName.Name
310+
})
311+
sort.Slice(expectedRequests, func(i, j int) bool {
312+
return expectedRequests[i].NamespacedName.Name < expectedRequests[j].NamespacedName.Name
313+
})
314+
315+
assert.Equal(t, expectedRequests, result)
316+
})
317+
}

0 commit comments

Comments
 (0)