github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/rolebinding_test.go (about) 1 package argocd 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 corev1 "k8s.io/api/core/v1" 10 rbacv1 "k8s.io/api/rbac/v1" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 "k8s.io/apimachinery/pkg/runtime" 13 "k8s.io/apimachinery/pkg/types" 14 "sigs.k8s.io/controller-runtime/pkg/client" 15 logf "sigs.k8s.io/controller-runtime/pkg/log" 16 17 argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" 18 "github.com/argoproj-labs/argocd-operator/common" 19 ) 20 21 func TestReconcileArgoCD_reconcileRoleBinding(t *testing.T) { 22 logf.SetLogger(ZapLogger(true)) 23 a := makeTestArgoCD() 24 25 resObjs := []client.Object{a} 26 subresObjs := []client.Object{a} 27 runtimeObjs := []runtime.Object{} 28 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 29 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 30 r := makeTestReconciler(cl, sch) 31 32 p := policyRuleForApplicationController() 33 34 assert.NoError(t, createNamespace(r, a.Namespace, "")) 35 assert.NoError(t, createNamespace(r, "newTestNamespace", a.Namespace)) 36 37 workloadIdentifier := common.ArgoCDApplicationControllerComponent 38 39 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a)) 40 41 roleBinding := &rbacv1.RoleBinding{} 42 expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier) 43 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, roleBinding)) 44 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding)) 45 46 // update role reference and subject of the rolebinding 47 roleBinding.RoleRef.Name = "not-xrb" 48 roleBinding.Subjects[0].Name = "not-xrb" 49 assert.NoError(t, r.Client.Update(context.TODO(), roleBinding)) 50 51 // try reconciling it again and verify if the changes are overwritten 52 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a)) 53 54 roleBinding = &rbacv1.RoleBinding{} 55 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, roleBinding)) 56 } 57 58 func TestReconcileArgoCD_reconcileRoleBinding_for_new_namespace(t *testing.T) { 59 logf.SetLogger(ZapLogger(true)) 60 a := makeTestArgoCD() 61 62 resObjs := []client.Object{a} 63 subresObjs := []client.Object{a} 64 runtimeObjs := []runtime.Object{} 65 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 66 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 67 r := makeTestReconciler(cl, sch) 68 69 assert.NoError(t, createNamespace(r, a.Namespace, "")) 70 assert.NoError(t, createNamespace(r, "newTestNamespace", a.Namespace)) 71 72 // check no dexServer rolebinding is created for the new namespace with managed-by label 73 roleBinding := &rbacv1.RoleBinding{} 74 workloadIdentifier := common.ArgoCDDexServerComponent 75 expectedDexServerRules := policyRuleForDexServer() 76 expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier) 77 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedDexServerRules, a)) 78 assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding)) 79 80 // check no redisHa rolebinding is created for the new namespace with managed-by label 81 workloadIdentifier = common.ArgoCDRedisHAComponent 82 expectedRedisHaRules := policyRuleForRedisHa(r.Client) 83 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedRedisHaRules, a)) 84 assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding)) 85 86 // check no redis rolebinding is created for the new namespace with managed-by label 87 workloadIdentifier = common.ArgoCDRedisComponent 88 expectedRedisRules := policyRuleForRedis(r.Client) 89 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedRedisRules, a)) 90 assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding)) 91 } 92 93 // This test validates the behavior of the operator reconciliation when a managed namespace is not properly terminated 94 // or remains terminating may be because of some resources in the namespace not getting deleted. 95 func TestReconcileRoleBinding_for_Managed_Teminating_Namespace(t *testing.T) { 96 a := makeTestArgoCD() 97 98 resObjs := []client.Object{a} 99 subresObjs := []client.Object{a} 100 runtimeObjs := []runtime.Object{} 101 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 102 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 103 r := makeTestReconciler(cl, sch) 104 105 assert.NoError(t, createNamespace(r, a.Namespace, "")) 106 assert.NoError(t, createNamespace(r, "managedNS", a.Namespace)) 107 108 // Verify role bindings are created for the new namespace with managed-by label 109 roleBinding := &rbacv1.RoleBinding{} 110 workloadIdentifier := common.ArgoCDApplicationControllerComponent 111 expectedRules := policyRuleForApplicationController() 112 expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier) 113 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedRules, a)) 114 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS"}, roleBinding)) 115 116 // Create a configmap with an invalid finalizer in the "managedNS". 117 configMap := &corev1.ConfigMap{ 118 ObjectMeta: metav1.ObjectMeta{ 119 Name: "dummy", 120 Namespace: "managedNS", 121 Finalizers: []string{ 122 "nonexistent.finalizer/dummy", 123 }, 124 }, 125 } 126 assert.NoError(t, r.Client.Create( 127 context.TODO(), configMap)) 128 129 // Delete the newNamespaceTest ns. 130 // Verify that operator should not reconcile back to create the roleBindings in terminating ns. 131 newNS := &corev1.Namespace{} 132 r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS) 133 r.Client.Delete(context.TODO(), newNS) 134 135 // Verify that the namespace exists and is in terminating state. 136 r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS) 137 assert.NotEqual(t, newNS.DeletionTimestamp, nil) 138 139 err := r.reconcileRoleBinding(workloadIdentifier, expectedRules, a) 140 assert.NoError(t, err) 141 142 // Verify that the role bindings are deleted 143 err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, roleBinding) 144 assert.ErrorContains(t, err, "not found") 145 146 // Create another managed namespace 147 assert.NoError(t, createNamespace(r, "managedNS2", a.Namespace)) 148 149 // Check if roleBindings are created for the new namespace as well 150 err = r.reconcileRoleBinding(workloadIdentifier, expectedRules, a) 151 assert.NoError(t, err) 152 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, roleBinding)) 153 } 154 155 func TestReconcileArgoCD_reconcileClusterRoleBinding(t *testing.T) { 156 logf.SetLogger(ZapLogger(true)) 157 a := makeTestArgoCD() 158 159 resObjs := []client.Object{a} 160 subresObjs := []client.Object{a} 161 runtimeObjs := []runtime.Object{} 162 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 163 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 164 r := makeTestReconciler(cl, sch) 165 166 workloadIdentifier := "x" 167 expectedClusterRole := &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: workloadIdentifier}} 168 169 assert.NoError(t, r.reconcileClusterRoleBinding(workloadIdentifier, expectedClusterRole, a)) 170 171 clusterRoleBinding := &rbacv1.ClusterRoleBinding{} 172 expectedName := fmt.Sprintf("%s-%s-%s", a.Name, a.Namespace, workloadIdentifier) 173 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName}, clusterRoleBinding)) 174 175 // update role reference and subject of the clusterrolebinding 176 clusterRoleBinding.RoleRef.Name = "not-x" 177 clusterRoleBinding.Subjects[0].Name = "not-x" 178 assert.NoError(t, r.Client.Update(context.TODO(), clusterRoleBinding)) 179 180 // try reconciling it again and verify if the changes are overwritten 181 assert.NoError(t, r.reconcileClusterRoleBinding(workloadIdentifier, expectedClusterRole, a)) 182 183 clusterRoleBinding = &rbacv1.ClusterRoleBinding{} 184 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName}, clusterRoleBinding)) 185 } 186 187 func TestReconcileArgoCD_reconcileRoleBinding_custom_role(t *testing.T) { 188 logf.SetLogger(ZapLogger(true)) 189 a := makeTestArgoCD() 190 191 resObjs := []client.Object{a} 192 subresObjs := []client.Object{a} 193 runtimeObjs := []runtime.Object{} 194 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 195 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 196 r := makeTestReconciler(cl, sch) 197 198 p := policyRuleForApplicationController() 199 200 assert.NoError(t, createNamespace(r, a.Namespace, "")) 201 202 workloadIdentifier := "argocd-application-controller" 203 expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier) 204 205 namespaceWithCustomRole := "namespace-with-custom-role" 206 assert.NoError(t, createNamespace(r, namespaceWithCustomRole, a.Namespace)) 207 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a)) 208 209 // check if the default rolebindings are created 210 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, &rbacv1.RoleBinding{})) 211 212 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: namespaceWithCustomRole}, &rbacv1.RoleBinding{})) 213 214 checkForUpdatedRoleRef := func(t *testing.T, roleName, expectedName string) { 215 t.Helper() 216 expectedRoleRef := rbacv1.RoleRef{ 217 APIGroup: rbacv1.GroupName, 218 Kind: "ClusterRole", 219 Name: roleName, 220 } 221 roleBinding := &rbacv1.RoleBinding{} 222 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, roleBinding)) 223 assert.Equal(t, roleBinding.RoleRef, expectedRoleRef) 224 225 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: namespaceWithCustomRole}, roleBinding)) 226 assert.Equal(t, roleBinding.RoleRef, expectedRoleRef) 227 } 228 229 t.Setenv(common.ArgoCDControllerClusterRoleEnvName, "custom-controller-role") 230 assert.NoError(t, r.reconcileRoleBinding(common.ArgoCDApplicationControllerComponent, p, a)) 231 232 expectedName = fmt.Sprintf("%s-%s", a.Name, "argocd-application-controller") 233 checkForUpdatedRoleRef(t, "custom-controller-role", expectedName) 234 235 t.Setenv(common.ArgoCDServerClusterRoleEnvName, "custom-server-role") 236 assert.NoError(t, r.reconcileRoleBinding("argocd-server", p, a)) 237 238 expectedName = fmt.Sprintf("%s-%s", a.Name, "argocd-server") 239 checkForUpdatedRoleRef(t, "custom-server-role", expectedName) 240 } 241 242 func TestReconcileArgoCD_reconcileRoleBinding_forSourceNamespaces(t *testing.T) { 243 logf.SetLogger(ZapLogger(true)) 244 sourceNamespace := "newNamespaceTest" 245 a := makeTestArgoCD() 246 a.Spec = argoproj.ArgoCDSpec{ 247 SourceNamespaces: []string{ 248 sourceNamespace, 249 }, 250 } 251 252 resObjs := []client.Object{a} 253 subresObjs := []client.Object{a} 254 runtimeObjs := []runtime.Object{} 255 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 256 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 257 r := makeTestReconciler(cl, sch) 258 259 p := policyRuleForApplicationController() 260 261 assert.NoError(t, createNamespace(r, a.Namespace, "")) 262 assert.NoError(t, createNamespaceManagedByClusterArgoCDLabel(r, sourceNamespace, a.Namespace)) 263 264 workloadIdentifier := common.ArgoCDServerComponent 265 266 assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a)) 267 268 roleBinding := &rbacv1.RoleBinding{} 269 expectedName := getRoleBindingNameForSourceNamespaces(a.Name, sourceNamespace) 270 271 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, roleBinding)) 272 273 }