github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/role_test.go (about) 1 package argocd 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 corev1 "k8s.io/api/core/v1" 11 v1 "k8s.io/api/rbac/v1" 12 "k8s.io/apimachinery/pkg/api/errors" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/runtime" 15 "k8s.io/apimachinery/pkg/types" 16 "sigs.k8s.io/controller-runtime/pkg/client" 17 logf "sigs.k8s.io/controller-runtime/pkg/log" 18 19 argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" 20 "github.com/argoproj-labs/argocd-operator/common" 21 ) 22 23 func TestReconcileArgoCD_reconcileRole(t *testing.T) { 24 logf.SetLogger(ZapLogger(true)) 25 a := makeTestArgoCD() 26 27 resObjs := []client.Object{a} 28 subresObjs := []client.Object{a} 29 runtimeObjs := []runtime.Object{} 30 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 31 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 32 r := makeTestReconciler(cl, sch) 33 34 assert.NoError(t, createNamespace(r, a.Namespace, "")) 35 assert.NoError(t, createNamespace(r, "newNamespaceTest", a.Namespace)) 36 37 workloadIdentifier := common.ArgoCDApplicationControllerComponent 38 expectedRules := policyRuleForApplicationController() 39 _, err := r.reconcileRole(workloadIdentifier, expectedRules, a) 40 assert.NoError(t, err) 41 42 expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier) 43 reconciledRole := &v1.Role{} 44 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole)) 45 assert.Equal(t, expectedRules, reconciledRole.Rules) 46 47 // check if roles are created for the new namespace as well 48 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newNamespaceTest"}, reconciledRole)) 49 assert.Equal(t, expectedRules, reconciledRole.Rules) 50 51 // update reconciledRole policy rules to RedisHa policy rules 52 reconciledRole.Rules = policyRuleForRedisHa(r.Client) 53 assert.NoError(t, r.Client.Update(context.TODO(), reconciledRole)) 54 55 // Check if the RedisHa policy rules are overwritten to Application Controller 56 // policy rules by the reconciler 57 _, err = r.reconcileRole(workloadIdentifier, expectedRules, a) 58 assert.NoError(t, err) 59 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole)) 60 assert.Equal(t, expectedRules, reconciledRole.Rules) 61 } 62 func TestReconcileArgoCD_reconcileRole_for_new_namespace(t *testing.T) { 63 logf.SetLogger(ZapLogger(true)) 64 a := makeTestArgoCD() 65 66 resObjs := []client.Object{a} 67 subresObjs := []client.Object{a} 68 runtimeObjs := []runtime.Object{} 69 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 70 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 71 r := makeTestReconciler(cl, sch) 72 73 assert.NoError(t, createNamespace(r, a.Namespace, "")) 74 assert.NoError(t, createNamespace(r, "newNamespaceTest", a.Namespace)) 75 76 // only 1 role for the Argo CD instance namespace will be created 77 expectedNumberOfRoles := 1 78 // check no dexServer role is created for the new namespace with managed-by label 79 workloadIdentifier := common.ArgoCDDexServerComponent 80 expectedRoleNamespace := a.Namespace 81 expectedDexServerRules := policyRuleForDexServer() 82 dexRoles, err := r.reconcileRole(workloadIdentifier, expectedDexServerRules, a) 83 assert.NoError(t, err) 84 assert.Equal(t, expectedNumberOfRoles, len(dexRoles)) 85 assert.Equal(t, expectedRoleNamespace, dexRoles[0].ObjectMeta.Namespace) 86 // check no redisHa role is created for the new namespace with managed-by label 87 workloadIdentifier = common.ArgoCDRedisHAComponent 88 expectedRedisHaRules := policyRuleForRedisHa(r.Client) 89 redisHaRoles, err := r.reconcileRole(workloadIdentifier, expectedRedisHaRules, a) 90 assert.NoError(t, err) 91 assert.Equal(t, expectedNumberOfRoles, len(redisHaRoles)) 92 assert.Equal(t, expectedRoleNamespace, redisHaRoles[0].ObjectMeta.Namespace) 93 // check no redis role is created for the new namespace with managed-by label 94 workloadIdentifier = common.ArgoCDRedisComponent 95 expectedRedisRules := policyRuleForRedis(r.Client) 96 redisRoles, err := r.reconcileRole(workloadIdentifier, expectedRedisRules, a) 97 assert.NoError(t, err) 98 assert.Equal(t, expectedNumberOfRoles, len(redisRoles)) 99 assert.Equal(t, expectedRoleNamespace, redisRoles[0].ObjectMeta.Namespace) 100 } 101 102 func TestReconcileArgoCD_reconcileClusterRole(t *testing.T) { 103 logf.SetLogger(ZapLogger(true)) 104 a := makeTestArgoCD() 105 106 resObjs := []client.Object{a} 107 subresObjs := []client.Object{a} 108 runtimeObjs := []runtime.Object{} 109 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 110 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 111 r := makeTestReconciler(cl, sch) 112 113 workloadIdentifier := common.ArgoCDApplicationControllerComponent 114 clusterRoleName := GenerateUniqueResourceName(workloadIdentifier, a) 115 expectedRules := policyRuleForApplicationController() 116 _, err := r.reconcileClusterRole(workloadIdentifier, expectedRules, a) 117 assert.NoError(t, err) 118 119 // cluster role should not be created 120 //assert.ErrorContains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, &v1.ClusterRole{}), "not found") 121 //TODO: https://github.com/stretchr/testify/pull/1022 introduced ErrorContains, but is not yet available in a tagged release. Revert to ErrorContains once this becomes available 122 assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, &v1.ClusterRole{})) 123 assert.Contains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, &v1.ClusterRole{}).Error(), "not found") 124 125 t.Setenv("ARGOCD_CLUSTER_CONFIG_NAMESPACES", a.Namespace) 126 _, err = r.reconcileClusterRole(workloadIdentifier, expectedRules, a) 127 assert.NoError(t, err) 128 129 reconciledClusterRole := &v1.ClusterRole{} 130 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole)) 131 assert.Equal(t, expectedRules, reconciledClusterRole.Rules) 132 133 // update reconciledRole policy rules to RedisHa policy rules 134 reconciledClusterRole.Rules = policyRuleForRedisHa(r.Client) 135 assert.NoError(t, r.Client.Update(context.TODO(), reconciledClusterRole)) 136 137 // Check if the RedisHa policy rules are overwritten to Application Controller 138 // policy rules for cluster role by the reconciler 139 _, err = r.reconcileClusterRole(workloadIdentifier, expectedRules, a) 140 assert.NoError(t, err) 141 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole)) 142 assert.Equal(t, expectedRules, reconciledClusterRole.Rules) 143 144 // Check if the CLuster Role gets deleted 145 os.Unsetenv("ARGOCD_CLUSTER_CONFIG_NAMESPACES") 146 _, err = r.reconcileClusterRole(workloadIdentifier, expectedRules, a) 147 assert.NoError(t, err) 148 //assert.ErrorContains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole), "not found") 149 //TODO: https://github.com/stretchr/testify/pull/1022 introduced ErrorContains, but is not yet available in a tagged release. Revert to ErrorContains once this becomes available 150 assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole)) 151 assert.Contains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole).Error(), "not found") 152 } 153 154 func TestReconcileArgoCD_reconcileRoleForApplicationSourceNamespaces(t *testing.T) { 155 logf.SetLogger(ZapLogger(true)) 156 sourceNamespace := "newNamespaceTest" 157 a := makeTestArgoCD() 158 a.Spec = argoproj.ArgoCDSpec{ 159 SourceNamespaces: []string{ 160 sourceNamespace, 161 }, 162 ApplicationSet: &argoproj.ArgoCDApplicationSet{ 163 SourceNamespaces: []string{"tmp"}, 164 }, 165 } 166 167 resObjs := []client.Object{a} 168 subresObjs := []client.Object{a} 169 runtimeObjs := []runtime.Object{} 170 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 171 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 172 r := makeTestReconciler(cl, sch) 173 174 assert.NoError(t, createNamespace(r, a.Namespace, "")) 175 assert.NoError(t, createNamespaceManagedByClusterArgoCDLabel(r, sourceNamespace, a.Namespace)) 176 177 workloadIdentifier := common.ArgoCDServerComponent 178 expectedRules := policyRuleForServerApplicationSourceNamespaces() 179 err := r.reconcileRoleForApplicationSourceNamespaces(workloadIdentifier, expectedRules, a) 180 assert.NoError(t, err) 181 182 expectedName := getRoleNameForApplicationSourceNamespaces(sourceNamespace, a) 183 reconciledRole := &v1.Role{} 184 185 // check if roles are created for the new namespace 186 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, reconciledRole)) 187 assert.Equal(t, expectedRules, reconciledRole.Rules) 188 189 // check if appset rules are added for server-role when new appset namespace is added 190 a.Spec = argoproj.ArgoCDSpec{ 191 SourceNamespaces: []string{ 192 sourceNamespace, 193 }, 194 ApplicationSet: &argoproj.ArgoCDApplicationSet{ 195 SourceNamespaces: []string{"tmp", sourceNamespace}, 196 }, 197 } 198 err = r.reconcileRoleForApplicationSourceNamespaces(workloadIdentifier, expectedRules, a) 199 assert.NoError(t, err) 200 reconciledRole = &v1.Role{} 201 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, reconciledRole)) 202 assert.Equal(t, append(expectedRules, policyRuleForServerApplicationSetSourceNamespaces()...), reconciledRole.Rules) 203 204 // check if appset rules are removed for server-role when appset namespace is removed from the list 205 a.Spec = argoproj.ArgoCDSpec{ 206 SourceNamespaces: []string{ 207 sourceNamespace, 208 }, 209 ApplicationSet: &argoproj.ArgoCDApplicationSet{ 210 SourceNamespaces: []string{"tmp"}, 211 }, 212 } 213 err = r.reconcileRoleForApplicationSourceNamespaces(workloadIdentifier, expectedRules, a) 214 assert.NoError(t, err) 215 reconciledRole = &v1.Role{} 216 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, reconciledRole)) 217 assert.Equal(t, expectedRules, reconciledRole.Rules) 218 } 219 220 func TestReconcileArgoCD_RoleHooks(t *testing.T) { 221 defer resetHooks()() 222 a := makeTestArgoCD() 223 224 resObjs := []client.Object{a} 225 subresObjs := []client.Object{a} 226 runtimeObjs := []runtime.Object{} 227 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 228 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 229 r := makeTestReconciler(cl, sch) 230 231 assert.NoError(t, createNamespace(r, a.Namespace, "")) 232 Register(testRoleHook) 233 234 roles, err := r.reconcileRole(common.ArgoCDApplicationControllerComponent, []v1.PolicyRule{}, a) 235 role := roles[0] 236 assert.NoError(t, err) 237 assert.Equal(t, role.Rules, testRules()) 238 239 roles, err = r.reconcileRole("test", []v1.PolicyRule{}, a) 240 role = roles[0] 241 assert.NoError(t, err) 242 assert.Equal(t, role.Rules, []v1.PolicyRule{}) 243 } 244 245 func TestReconcileArgoCD_reconcileRole_custom_role(t *testing.T) { 246 logf.SetLogger(ZapLogger(true)) 247 a := makeTestArgoCD() 248 249 resObjs := []client.Object{a} 250 subresObjs := []client.Object{a} 251 runtimeObjs := []runtime.Object{} 252 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 253 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 254 r := makeTestReconciler(cl, sch) 255 256 assert.NoError(t, createNamespace(r, a.Namespace, "")) 257 assert.NoError(t, createNamespace(r, "namespace-custom-role", a.Namespace)) 258 259 workloadIdentifier := "argocd-application-controller" 260 expectedRules := policyRuleForApplicationController() 261 _, err := r.reconcileRole(workloadIdentifier, expectedRules, a) 262 assert.NoError(t, err) 263 264 expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier) 265 reconciledRole := &v1.Role{} 266 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole)) 267 assert.Equal(t, expectedRules, reconciledRole.Rules) 268 269 // check if roles are created for the new namespace as well 270 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "namespace-custom-role"}, reconciledRole)) 271 assert.Equal(t, expectedRules, reconciledRole.Rules) 272 273 // set the custom role as env variable 274 t.Setenv(common.ArgoCDControllerClusterRoleEnvName, "custom-role") 275 276 _, err = r.reconcileRole(workloadIdentifier, expectedRules, a) 277 assert.NoError(t, err) 278 279 // check if the default cluster roles are removed 280 err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole) 281 if err == nil || !errors.IsNotFound(err) { 282 t.Fatal(err) 283 } 284 285 err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "namespace-custom-role"}, reconciledRole) 286 if err == nil || !errors.IsNotFound(err) { 287 t.Fatal(err) 288 } 289 } 290 291 // This test validates the behavior of the operator reconciliation when a managed namespace is not properly terminated 292 // or remains terminating may be because of some resources in the namespace not getting deleted. 293 func TestReconcileRoles_ManagedTerminatingNamespace(t *testing.T) { 294 295 a := makeTestArgoCD() 296 297 resObjs := []client.Object{a} 298 subresObjs := []client.Object{a} 299 runtimeObjs := []runtime.Object{} 300 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 301 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 302 r := makeTestReconciler(cl, sch) 303 304 assert.NoError(t, createNamespace(r, a.Namespace, "")) 305 306 // Create a managed namespace 307 assert.NoError(t, createNamespace(r, "managedNS", a.Namespace)) 308 309 workloadIdentifier := common.ArgoCDApplicationControllerComponent 310 expectedRules := policyRuleForApplicationController() 311 _, err := r.reconcileRole(workloadIdentifier, expectedRules, a) 312 assert.NoError(t, err) 313 314 expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier) 315 reconciledRole := &v1.Role{} 316 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole)) 317 assert.Equal(t, expectedRules, reconciledRole.Rules) 318 319 // Check if roles are created for the new namespace as well 320 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS"}, reconciledRole)) 321 assert.Equal(t, expectedRules, reconciledRole.Rules) 322 323 // Create a configmap with an invalid finalizer in the "managedNS". 324 configMap := &corev1.ConfigMap{ 325 ObjectMeta: metav1.ObjectMeta{ 326 Name: "dummy", 327 Namespace: "managedNS", 328 Finalizers: []string{ 329 "nonexistent.finalizer/dummy", 330 }, 331 }, 332 } 333 assert.NoError(t, r.Client.Create( 334 context.TODO(), configMap)) 335 336 // Delete the newNamespaceTest ns. 337 // Verify that operator should not reconcile back to create the roles in terminating ns. 338 newNS := &corev1.Namespace{} 339 r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS) 340 r.Client.Delete(context.TODO(), newNS) 341 342 // Verify that the namespace exists and is in terminating state. 343 r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS) 344 assert.NotEqual(t, newNS.DeletionTimestamp, nil) 345 346 _, err = r.reconcileRole(workloadIdentifier, expectedRules, a) 347 assert.NoError(t, err) 348 349 // Verify that the roles are deleted 350 err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, reconciledRole) 351 assert.ErrorContains(t, err, "not found") 352 353 // Create another managed namespace 354 assert.NoError(t, createNamespace(r, "managedNS2", a.Namespace)) 355 356 // Check if roles are created for the new namespace as well 357 _, err = r.reconcileRole(workloadIdentifier, expectedRules, a) 358 assert.NoError(t, err) 359 360 assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, reconciledRole)) 361 assert.Equal(t, expectedRules, reconciledRole.Rules) 362 }