github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/util_test.go (about) 1 package argocd 2 3 import ( 4 "context" 5 b64 "encoding/base64" 6 "reflect" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/assert" 12 "sigs.k8s.io/controller-runtime/pkg/client" 13 logf "sigs.k8s.io/controller-runtime/pkg/log" 14 15 argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" 16 "github.com/argoproj-labs/argocd-operator/common" 17 "github.com/argoproj-labs/argocd-operator/controllers/argoutil" 18 19 v1 "k8s.io/api/core/v1" 20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 "k8s.io/apimachinery/pkg/runtime" 22 testclient "k8s.io/client-go/kubernetes/fake" 23 ) 24 25 const ( 26 dexTestImage = "testing/dex:latest" 27 argoTestImage = "testing/argocd:latest" 28 redisTestImage = "testing/redis:latest" 29 redisHATestImage = "testing/redis:latest-ha" 30 redisHAProxyTestImage = "testing/redis-ha-haproxy:latest-ha" 31 ) 32 33 func parallelismLimit(n int32) argoCDOpt { 34 return func(a *argoproj.ArgoCD) { 35 a.Spec.Controller.ParallelismLimit = n 36 } 37 } 38 39 func logFormat(f string) argoCDOpt { 40 return func(a *argoproj.ArgoCD) { 41 a.Spec.Controller.LogFormat = f 42 } 43 } 44 45 func logLevel(l string) argoCDOpt { 46 return func(a *argoproj.ArgoCD) { 47 a.Spec.Controller.LogLevel = l 48 } 49 } 50 51 func appSync(s int) argoCDOpt { 52 return func(a *argoproj.ArgoCD) { 53 a.Spec.Controller.AppSync = &metav1.Duration{Duration: time.Second * time.Duration(s)} 54 } 55 } 56 57 var imageTests = []struct { 58 name string 59 pre func(t *testing.T) 60 opts []argoCDOpt 61 want string 62 imageFunc func(a *argoproj.ArgoCD) string 63 }{ 64 { 65 name: "dex default configuration", 66 imageFunc: getDexContainerImage, 67 want: argoutil.CombineImageTag(common.ArgoCDDefaultDexImage, common.ArgoCDDefaultDexVersion), 68 }, 69 { 70 name: "dex spec configuration", 71 imageFunc: getDexContainerImage, 72 want: dexTestImage, 73 opts: []argoCDOpt{func(a *argoproj.ArgoCD) { 74 a.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 75 Provider: argoproj.SSOProviderTypeDex, 76 Dex: &argoproj.ArgoCDDexSpec{ 77 Image: "testing/dex", 78 Version: "latest", 79 }, 80 } 81 }}, 82 }, 83 { 84 name: "dex env configuration", 85 imageFunc: getDexContainerImage, 86 want: dexTestImage, 87 pre: func(t *testing.T) { 88 t.Setenv(common.ArgoCDDexImageEnvName, dexTestImage) 89 }, 90 }, 91 { 92 name: "argo default configuration", 93 imageFunc: getArgoContainerImage, 94 want: argoutil.CombineImageTag(common.ArgoCDDefaultArgoImage, common.ArgoCDDefaultArgoVersion), 95 }, 96 { 97 name: "argo spec configuration", 98 imageFunc: getArgoContainerImage, 99 want: argoTestImage, opts: []argoCDOpt{func(a *argoproj.ArgoCD) { 100 a.Spec.Image = "testing/argocd" 101 a.Spec.Version = "latest" 102 }}, 103 }, 104 { 105 name: "argo env configuration", 106 imageFunc: getArgoContainerImage, 107 want: argoTestImage, 108 pre: func(t *testing.T) { 109 t.Setenv(common.ArgoCDImageEnvName, argoTestImage) 110 }, 111 }, 112 { 113 name: "redis default configuration", 114 imageFunc: getRedisContainerImage, 115 want: argoutil.CombineImageTag(common.ArgoCDDefaultRedisImage, common.ArgoCDDefaultRedisVersion), 116 }, 117 { 118 name: "redis spec configuration", 119 imageFunc: getRedisContainerImage, 120 want: redisTestImage, 121 opts: []argoCDOpt{func(a *argoproj.ArgoCD) { 122 a.Spec.Redis.Image = "testing/redis" 123 a.Spec.Redis.Version = "latest" 124 }}, 125 }, 126 { 127 name: "redis env configuration", 128 imageFunc: getRedisContainerImage, 129 want: redisTestImage, 130 pre: func(t *testing.T) { 131 t.Setenv(common.ArgoCDRedisImageEnvName, redisTestImage) 132 }, 133 }, 134 { 135 name: "redis ha default configuration", 136 imageFunc: getRedisHAContainerImage, 137 want: argoutil.CombineImageTag( 138 common.ArgoCDDefaultRedisImage, 139 common.ArgoCDDefaultRedisVersionHA), 140 }, 141 { 142 name: "redis ha spec configuration", 143 imageFunc: getRedisHAContainerImage, 144 want: redisHATestImage, 145 opts: []argoCDOpt{func(a *argoproj.ArgoCD) { 146 a.Spec.Redis.Image = "testing/redis" 147 a.Spec.Redis.Version = "latest-ha" 148 }}, 149 }, 150 { 151 name: "redis ha env configuration", 152 imageFunc: getRedisHAContainerImage, 153 want: redisHATestImage, 154 pre: func(t *testing.T) { 155 t.Setenv(common.ArgoCDRedisHAImageEnvName, redisHATestImage) 156 }, 157 }, 158 { 159 name: "redis ha proxy default configuration", 160 imageFunc: getRedisHAProxyContainerImage, 161 want: argoutil.CombineImageTag( 162 common.ArgoCDDefaultRedisHAProxyImage, 163 common.ArgoCDDefaultRedisHAProxyVersion), 164 }, 165 { 166 name: "redis ha proxy spec configuration", 167 imageFunc: getRedisHAProxyContainerImage, 168 want: redisHAProxyTestImage, 169 opts: []argoCDOpt{func(a *argoproj.ArgoCD) { 170 a.Spec.HA.RedisProxyImage = "testing/redis-ha-haproxy" 171 a.Spec.HA.RedisProxyVersion = "latest-ha" 172 }}, 173 }, 174 { 175 name: "redis ha proxy env configuration", 176 imageFunc: getRedisHAProxyContainerImage, 177 want: redisHAProxyTestImage, 178 pre: func(t *testing.T) { 179 t.Setenv(common.ArgoCDRedisHAProxyImageEnvName, redisHAProxyTestImage) 180 }, 181 }, 182 } 183 184 func TestContainerImages_configuration(t *testing.T) { 185 for _, tt := range imageTests { 186 t.Run(tt.name, func(rt *testing.T) { 187 if tt.pre != nil { 188 tt.pre(rt) 189 } 190 a := makeTestArgoCD(tt.opts...) 191 image := tt.imageFunc(a) 192 if image != tt.want { 193 rt.Errorf("got %q, want %q", image, tt.want) 194 } 195 }) 196 } 197 } 198 199 var argoServerURITests = []struct { 200 name string 201 routeEnabled bool 202 opts []argoCDOpt 203 want string 204 }{ 205 { 206 name: "test with no host name - default", 207 routeEnabled: false, 208 want: "https://argocd-server", 209 }, 210 { 211 name: "test with external host name", 212 routeEnabled: false, 213 opts: []argoCDOpt{func(a *argoproj.ArgoCD) { 214 a.Spec.Server.Host = "test-host-name" 215 }}, 216 want: "https://test-host-name", 217 }, 218 } 219 220 func setRouteAPIFound(t *testing.T, routeEnabled bool) { 221 routeAPIEnabledTemp := routeAPIFound 222 t.Cleanup(func() { 223 routeAPIFound = routeAPIEnabledTemp 224 }) 225 routeAPIFound = routeEnabled 226 } 227 228 func TestGetArgoServerURI(t *testing.T) { 229 for _, tt := range argoServerURITests { 230 t.Run(tt.name, func(t *testing.T) { 231 cr := makeTestArgoCD(tt.opts...) 232 r := &ReconcileArgoCD{} 233 setRouteAPIFound(t, tt.routeEnabled) 234 result := r.getArgoServerURI(cr) 235 if result != tt.want { 236 t.Errorf("%s test failed, got=%q want=%q", tt.name, result, tt.want) 237 } 238 }) 239 } 240 } 241 242 func TestRemoveDeletionFinalizer(t *testing.T) { 243 t.Run("ArgoCD resource present", func(t *testing.T) { 244 a := makeTestArgoCD(addFinalizer(common.ArgoCDDeletionFinalizer)) 245 246 resObjs := []client.Object{a} 247 subresObjs := []client.Object{a} 248 runtimeObjs := []runtime.Object{} 249 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 250 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 251 r := makeTestReconciler(cl, sch) 252 253 err := r.removeDeletionFinalizer(a) 254 assert.NoError(t, err) 255 if a.IsDeletionFinalizerPresent() { 256 t.Fatal("Expected deletion finalizer to be removed") 257 } 258 }) 259 t.Run("ArgoCD resource absent", func(t *testing.T) { 260 a := makeTestArgoCD(addFinalizer(common.ArgoCDDeletionFinalizer)) 261 262 resObjs := []client.Object{} 263 subresObjs := []client.Object{} 264 runtimeObjs := []runtime.Object{} 265 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 266 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 267 r := makeTestReconciler(cl, sch) 268 269 err := r.removeDeletionFinalizer(a) 270 assert.Error(t, err, `failed to remove deletion finalizer from argocd: argocds.argoproj.io "argocd" not found`) 271 }) 272 } 273 274 func TestAddDeletionFinalizer(t *testing.T) { 275 t.Run("ArgoCD resource present", func(t *testing.T) { 276 a := makeTestArgoCD() 277 278 resObjs := []client.Object{a} 279 subresObjs := []client.Object{a} 280 runtimeObjs := []runtime.Object{} 281 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 282 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 283 r := makeTestReconciler(cl, sch) 284 285 err := r.addDeletionFinalizer(a) 286 assert.NoError(t, err) 287 if !a.IsDeletionFinalizerPresent() { 288 t.Fatal("Expected deletion finalizer to be added") 289 } 290 }) 291 t.Run("ArgoCD resource absent", func(t *testing.T) { 292 a := makeTestArgoCD() 293 294 resObjs := []client.Object{} 295 subresObjs := []client.Object{} 296 runtimeObjs := []runtime.Object{} 297 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 298 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 299 r := makeTestReconciler(cl, sch) 300 301 err := r.addDeletionFinalizer(a) 302 assert.Error(t, err, `failed to add deletion finalizer for argocd: argocds.argoproj.io "argocd" not found`) 303 }) 304 } 305 306 func TestArgoCDInstanceSelector(t *testing.T) { 307 t.Run("Selector for a Valid name", func(t *testing.T) { 308 validName := "argocd-server" 309 selector, err := argocdInstanceSelector(validName) 310 assert.NoError(t, err) 311 assert.Equal(t, selector.String(), "app.kubernetes.io/managed-by=argocd-server") 312 }) 313 t.Run("Selector for an Invalid name", func(t *testing.T) { 314 invalidName := "argocd-*/" 315 selector, err := argocdInstanceSelector(invalidName) 316 //assert.ErrorContains(t, err, `failed to create a requirement for values[0][app.kubernetes.io/managed-by]: Invalid value: "argocd-*/`) 317 //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 318 assert.Error(t, err) 319 assert.Contains(t, err.Error(), `failed to create a requirement for values[0][app.kubernetes.io/managed-by]: Invalid value: "argocd-*/`) 320 321 assert.Equal(t, selector, nil) 322 }) 323 } 324 325 func TestGetArgoApplicationControllerCommand(t *testing.T) { 326 327 defaultResult := []string{ 328 "argocd-application-controller", 329 "--operation-processors", 330 "10", 331 "--redis", 332 "argocd-redis.argocd.svc.cluster.local:6379", 333 "--repo-server", 334 "argocd-repo-server.argocd.svc.cluster.local:8081", 335 "--status-processors", 336 "20", 337 "--kubectl-parallelism-limit", 338 "10", 339 "--loglevel", 340 "info", 341 "--logformat", 342 "text", 343 } 344 345 controllerProcesorsChangedResult := func(n string) []string { 346 return []string{ 347 "argocd-application-controller", 348 "--operation-processors", 349 "10", 350 "--redis", 351 "argocd-redis.argocd.svc.cluster.local:6379", 352 "--repo-server", 353 "argocd-repo-server.argocd.svc.cluster.local:8081", 354 "--status-processors", 355 n, 356 "--kubectl-parallelism-limit", 357 "10", 358 "--loglevel", 359 "info", 360 "--logformat", 361 "text", 362 } 363 } 364 365 operationProcesorsChangedResult := func(n string) []string { 366 return []string{ 367 "argocd-application-controller", 368 "--operation-processors", 369 n, 370 "--redis", 371 "argocd-redis.argocd.svc.cluster.local:6379", 372 "--repo-server", 373 "argocd-repo-server.argocd.svc.cluster.local:8081", 374 "--status-processors", 375 "20", 376 "--kubectl-parallelism-limit", 377 "10", 378 "--loglevel", 379 "info", 380 "--logformat", 381 "text", 382 } 383 } 384 385 parallelismLimitChangedResult := func(n string) []string { 386 return []string{ 387 "argocd-application-controller", 388 "--operation-processors", 389 "10", 390 "--redis", 391 "argocd-redis.argocd.svc.cluster.local:6379", 392 "--repo-server", 393 "argocd-repo-server.argocd.svc.cluster.local:8081", 394 "--status-processors", 395 "20", 396 "--kubectl-parallelism-limit", 397 n, 398 "--loglevel", 399 "info", 400 "--logformat", 401 "text", 402 } 403 } 404 405 logFormatChangedResult := func(f string) []string { 406 return []string{ 407 "argocd-application-controller", 408 "--operation-processors", 409 "10", 410 "--redis", 411 "argocd-redis.argocd.svc.cluster.local:6379", 412 "--repo-server", 413 "argocd-repo-server.argocd.svc.cluster.local:8081", 414 "--status-processors", 415 "20", 416 "--kubectl-parallelism-limit", 417 "10", 418 "--loglevel", 419 "info", 420 "--logformat", 421 f, 422 } 423 } 424 425 logLevelChangedResult := func(l string) []string { 426 return []string{ 427 "argocd-application-controller", 428 "--operation-processors", 429 "10", 430 "--redis", 431 "argocd-redis.argocd.svc.cluster.local:6379", 432 "--repo-server", 433 "argocd-repo-server.argocd.svc.cluster.local:8081", 434 "--status-processors", 435 "20", 436 "--kubectl-parallelism-limit", 437 "10", 438 "--loglevel", 439 l, 440 "--logformat", 441 "text", 442 } 443 } 444 445 cmdTests := []struct { 446 name string 447 opts []argoCDOpt 448 want []string 449 }{ 450 { 451 "defaults", 452 []argoCDOpt{}, 453 defaultResult, 454 }, 455 { 456 "configured status processors", 457 []argoCDOpt{controllerProcessors(30)}, 458 controllerProcesorsChangedResult("30"), 459 }, 460 { 461 "configured status processors to zero", 462 []argoCDOpt{controllerProcessors(0)}, 463 defaultResult, 464 }, 465 { 466 "configured status processors to be between zero and default", 467 []argoCDOpt{controllerProcessors(10)}, 468 controllerProcesorsChangedResult("10"), 469 }, 470 { 471 "configured operation processors", 472 []argoCDOpt{operationProcessors(15)}, 473 operationProcesorsChangedResult("15"), 474 }, 475 { 476 "configured operation processors to zero", 477 []argoCDOpt{operationProcessors(0)}, 478 defaultResult, 479 }, 480 { 481 "configured operation processors to be between zero and ten", 482 []argoCDOpt{operationProcessors(5)}, 483 operationProcesorsChangedResult("5"), 484 }, 485 { 486 "configured parallelism limit", 487 []argoCDOpt{parallelismLimit(30)}, 488 parallelismLimitChangedResult("30"), 489 }, 490 { 491 "configured parallelism limit to zero", 492 []argoCDOpt{parallelismLimit(0)}, 493 defaultResult, 494 }, 495 { 496 "configured invalid logformat", 497 []argoCDOpt{logFormat("arbitrary")}, 498 defaultResult, 499 }, 500 { 501 "configured json logformat", 502 []argoCDOpt{logFormat("json")}, 503 logFormatChangedResult("json"), 504 }, 505 { 506 "configured text logformat", 507 []argoCDOpt{logFormat("text")}, 508 logFormatChangedResult("text"), 509 }, 510 { 511 "configured invalid loglevel", 512 []argoCDOpt{logLevel("arbitrary")}, 513 defaultResult, 514 }, 515 { 516 "configured debug loglevel", 517 []argoCDOpt{logLevel("debug")}, 518 logLevelChangedResult("debug"), 519 }, 520 { 521 "configured info loglevel", 522 []argoCDOpt{logLevel("info")}, 523 logLevelChangedResult("info"), 524 }, 525 { 526 "configured warn loglevel", 527 []argoCDOpt{logLevel("warn")}, 528 logLevelChangedResult("warn"), 529 }, 530 { 531 "configured error loglevel", 532 []argoCDOpt{logLevel("error")}, 533 logLevelChangedResult("error"), 534 }, 535 } 536 537 for _, tt := range cmdTests { 538 cr := makeTestArgoCD(tt.opts...) 539 cmd := getArgoApplicationControllerCommand(cr, false) 540 541 if !reflect.DeepEqual(cmd, tt.want) { 542 t.Fatalf("got %#v, want %#v", cmd, tt.want) 543 } 544 } 545 } 546 547 func TestGetArgoApplicationContainerEnv(t *testing.T) { 548 549 sync60s := []v1.EnvVar{ 550 v1.EnvVar{Name: "HOME", Value: "/home/argocd", ValueFrom: (*v1.EnvVarSource)(nil)}, 551 v1.EnvVar{Name: "ARGOCD_RECONCILIATION_TIMEOUT", Value: "60s", ValueFrom: (*v1.EnvVarSource)(nil)}} 552 553 cmdTests := []struct { 554 name string 555 opts []argoCDOpt 556 want []v1.EnvVar 557 }{ 558 { 559 "configured apsync to 60s", 560 []argoCDOpt{appSync(60)}, 561 sync60s, 562 }, 563 } 564 565 for _, tt := range cmdTests { 566 cr := makeTestArgoCD(tt.opts...) 567 env := getArgoControllerContainerEnv(cr) 568 569 if !reflect.DeepEqual(env, tt.want) { 570 t.Fatalf("got %#v, want %#v", env, tt.want) 571 } 572 } 573 } 574 575 func TestDeleteRBACsForNamespace(t *testing.T) { 576 a := makeTestArgoCD() 577 testClient := testclient.NewSimpleClientset() 578 testNameSpace := "testNameSpace" 579 580 role := newRole("xyz", policyRuleForApplicationController(), a) 581 role.Namespace = testNameSpace 582 583 // create role with label 584 _, err := testClient.RbacV1().Roles(testNameSpace).Create(context.TODO(), role, metav1.CreateOptions{}) 585 assert.NoError(t, err) 586 587 role2 := newRole("abc", policyRuleForApplicationController(), a) 588 role2.Namespace = testNameSpace 589 role2.Labels = map[string]string{} 590 591 // create role without label 592 _, err = testClient.RbacV1().Roles(testNameSpace).Create(context.TODO(), role2, metav1.CreateOptions{}) 593 assert.NoError(t, err) 594 595 roleBinding := newRoleBindingWithname("xyz", a) 596 roleBinding.Namespace = testNameSpace 597 598 // create roleBinding with label 599 _, err = testClient.RbacV1().RoleBindings(testNameSpace).Create(context.TODO(), roleBinding, metav1.CreateOptions{}) 600 assert.NoError(t, err) 601 602 roleBinding2 := newRoleBindingWithname("abc", a) 603 roleBinding2.Namespace = testNameSpace 604 roleBinding2.Labels = map[string]string{} 605 606 // create RoleBinding without label 607 _, err = testClient.RbacV1().RoleBindings(testNameSpace).Create(context.TODO(), roleBinding2, metav1.CreateOptions{}) 608 assert.NoError(t, err) 609 610 // run deleteRBACsForNamespace 611 assert.NoError(t, deleteRBACsForNamespace(testNameSpace, testClient)) 612 613 // role with the label should be deleted 614 _, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), role.Name, metav1.GetOptions{}) 615 //assert.ErrorContains(t, err, "not found") 616 //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 617 assert.Error(t, err) 618 assert.Contains(t, err.Error(), "not found") 619 620 // role without the label should still exists, no error 621 _, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), role2.Name, metav1.GetOptions{}) 622 assert.NoError(t, err) 623 624 // roleBinding with the label should be deleted 625 _, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), roleBinding.Name, metav1.GetOptions{}) 626 //assert.ErrorContains(t, err, "not found") 627 //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 628 assert.Error(t, err) 629 assert.Contains(t, err.Error(), "not found") 630 631 // roleBinding without the label should still exists, no error 632 _, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), roleBinding2.Name, metav1.GetOptions{}) 633 assert.NoError(t, err) 634 635 } 636 637 func TestRemoveManagedNamespaceFromClusterSecretAfterDeletion(t *testing.T) { 638 a := makeTestArgoCD() 639 testClient := testclient.NewSimpleClientset() 640 testNameSpace := "testNameSpace" 641 642 secret := argoutil.NewSecretWithSuffix(a, "xyz") 643 secret.Labels = map[string]string{common.ArgoCDSecretTypeLabel: "cluster"} 644 secret.Data = map[string][]byte{ 645 "server": []byte(common.ArgoCDDefaultServer), 646 "namespaces": []byte(strings.Join([]string{testNameSpace, "testNamespace2"}, ",")), 647 } 648 649 // create secret with the label 650 _, err := testClient.CoreV1().Secrets(a.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{}) 651 assert.NoError(t, err) 652 653 // run deleteManagedNamespaceFromClusterSecret 654 assert.NoError(t, deleteManagedNamespaceFromClusterSecret(a.Namespace, testNameSpace, testClient)) 655 656 // secret should still exists with updated list of namespaces 657 s, err := testClient.CoreV1().Secrets(a.Namespace).Get(context.TODO(), secret.Name, metav1.GetOptions{}) 658 assert.NoError(t, err) 659 assert.Equal(t, string(s.Data["namespaces"]), "testNamespace2") 660 661 } 662 663 func TestRemoveManagedByLabelFromNamespaces(t *testing.T) { 664 a := makeTestArgoCD() 665 666 resObjs := []client.Object{a} 667 subresObjs := []client.Object{a} 668 runtimeObjs := []runtime.Object{} 669 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 670 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 671 r := makeTestReconciler(cl, sch) 672 673 nsArgocd := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{ 674 Name: a.Namespace, 675 }} 676 err := r.Client.Create(context.TODO(), nsArgocd) 677 assert.NoError(t, err) 678 679 ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{ 680 Name: "testNamespace", 681 Labels: map[string]string{ 682 common.ArgoCDManagedByLabel: a.Namespace, 683 }}, 684 } 685 686 err = r.Client.Create(context.TODO(), ns) 687 assert.NoError(t, err) 688 689 ns2 := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{ 690 Name: "testNamespace2", 691 Labels: map[string]string{ 692 common.ArgoCDManagedByLabel: a.Namespace, 693 }}, 694 } 695 696 err = r.Client.Create(context.TODO(), ns2) 697 assert.NoError(t, err) 698 699 ns3 := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{ 700 Name: "testNamespace3", 701 Labels: map[string]string{ 702 common.ArgoCDManagedByLabel: "newNamespace", 703 }}, 704 } 705 706 err = r.Client.Create(context.TODO(), ns3) 707 assert.NoError(t, err) 708 709 err = r.removeManagedByLabelFromNamespaces(a.Namespace) 710 assert.NoError(t, err) 711 712 nsList := &v1.NamespaceList{} 713 err = r.Client.List(context.TODO(), nsList) 714 assert.NoError(t, err) 715 for _, n := range nsList.Items { 716 if n.Name == ns3.Name { 717 _, ok := n.Labels[common.ArgoCDManagedByLabel] 718 assert.Equal(t, ok, true) 719 continue 720 } 721 _, ok := n.Labels[common.ArgoCDManagedByLabel] 722 assert.Equal(t, ok, false) 723 } 724 } 725 726 func TestSetManagedNamespaces(t *testing.T) { 727 a := makeTestArgoCD() 728 729 ns1 := v1.Namespace{ 730 ObjectMeta: metav1.ObjectMeta{ 731 Name: "test-namespace-1", 732 Labels: map[string]string{ 733 common.ArgoCDManagedByLabel: testNamespace, 734 }, 735 }, 736 } 737 738 ns2 := v1.Namespace{ 739 ObjectMeta: metav1.ObjectMeta{ 740 Name: "test-namespace-2", 741 Labels: map[string]string{ 742 common.ArgoCDManagedByLabel: testNamespace, 743 }, 744 }, 745 } 746 747 ns3 := v1.Namespace{ 748 ObjectMeta: metav1.ObjectMeta{ 749 Name: "test-namespace-3", 750 Labels: map[string]string{ 751 common.ArgoCDManagedByLabel: "random-namespace", 752 }, 753 }, 754 } 755 756 ns4 := v1.Namespace{ 757 ObjectMeta: metav1.ObjectMeta{ 758 Name: "test-namespace-4", 759 }, 760 } 761 762 resObjs := []client.Object{a, &ns1, &ns2, &ns3, &ns4} 763 764 subresObjs := []client.Object{a} 765 runtimeObjs := []runtime.Object{} 766 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 767 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 768 r := makeTestReconciler(cl, sch) 769 770 err := r.setManagedNamespaces(a) 771 assert.NoError(t, err) 772 773 assert.Equal(t, len(r.ManagedNamespaces.Items), 3) 774 for _, n := range r.ManagedNamespaces.Items { 775 if n.Labels[common.ArgoCDManagedByLabel] != testNamespace && n.Name != testNamespace { 776 t.Errorf("Expected namespace %s to be managed by Argo CD instance %s", n.Name, testNamespace) 777 } 778 } 779 } 780 781 func TestSetManagedSourceNamespaces(t *testing.T) { 782 a := makeTestArgoCD() 783 a.Spec = argoproj.ArgoCDSpec{ 784 SourceNamespaces: []string{ 785 "test-namespace-1", 786 }, 787 } 788 ns1 := v1.Namespace{ 789 ObjectMeta: metav1.ObjectMeta{ 790 Name: "test-namespace-1", 791 Labels: map[string]string{ 792 common.ArgoCDManagedByClusterArgoCDLabel: testNamespace, 793 }, 794 }, 795 } 796 797 resObjs := []client.Object{a, &ns1} 798 subresObjs := []client.Object{a} 799 runtimeObjs := []runtime.Object{} 800 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 801 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 802 r := makeTestReconciler(cl, sch) 803 804 err := r.setManagedSourceNamespaces(a) 805 assert.NoError(t, err) 806 807 assert.Equal(t, 1, len(r.ManagedSourceNamespaces)) 808 assert.Contains(t, r.ManagedSourceNamespaces, "test-namespace-1") 809 } 810 811 func TestGetSourceNamespacesWithWildcardPatternNamespace(t *testing.T) { 812 a := makeTestArgoCD() 813 a.Spec = argoproj.ArgoCDSpec{ 814 SourceNamespaces: []string{ 815 "test*", 816 }, 817 } 818 ns1 := v1.Namespace{ 819 ObjectMeta: metav1.ObjectMeta{ 820 Name: "test-namespace-1", 821 }, 822 } 823 824 ns2 := v1.Namespace{ 825 ObjectMeta: metav1.ObjectMeta{ 826 Name: "test-namespace-2", 827 }, 828 } 829 ns3 := v1.Namespace{ 830 ObjectMeta: metav1.ObjectMeta{ 831 Name: "other-namespace", 832 }, 833 } 834 835 resObjs := []client.Object{a, &ns1, &ns2, &ns3} 836 subresObjs := []client.Object{a} 837 runtimeObjs := []runtime.Object{} 838 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 839 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 840 r := makeTestReconciler(cl, sch) 841 842 sourceNamespaces, err := r.getSourceNamespaces(a) 843 assert.NoError(t, err) 844 assert.Equal(t, 2, len(sourceNamespaces)) 845 assert.Contains(t, sourceNamespaces, "test-namespace-1") 846 assert.Contains(t, sourceNamespaces, "test-namespace-2") 847 assert.NotContains(t, sourceNamespaces, "other-namespace") 848 } 849 850 func TestGetSourceNamespacesWithSpecificNamespace(t *testing.T) { 851 a := makeTestArgoCD() 852 a.Spec = argoproj.ArgoCDSpec{ 853 SourceNamespaces: []string{ 854 "test", 855 }, 856 } 857 ns1 := v1.Namespace{ 858 ObjectMeta: metav1.ObjectMeta{ 859 Name: "test", 860 }, 861 } 862 ns2 := v1.Namespace{ 863 ObjectMeta: metav1.ObjectMeta{ 864 Name: "test-namespace-1", 865 }, 866 } 867 ns3 := v1.Namespace{ 868 ObjectMeta: metav1.ObjectMeta{ 869 Name: "other-namespace", 870 }, 871 } 872 873 resObjs := []client.Object{a, &ns1, &ns2, &ns3} 874 subresObjs := []client.Object{a} 875 runtimeObjs := []runtime.Object{} 876 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 877 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 878 r := makeTestReconciler(cl, sch) 879 880 sourceNamespaces, err := r.getSourceNamespaces(a) 881 assert.NoError(t, err) 882 assert.Equal(t, 1, len(sourceNamespaces)) 883 assert.Contains(t, sourceNamespaces, "test") 884 assert.NotContains(t, sourceNamespaces, "test-namespace-1") 885 assert.NotContains(t, sourceNamespaces, "other-namespace") 886 } 887 888 func TestGetSourceNamespacesWithMultipleSourceNamespaces(t *testing.T) { 889 a := makeTestArgoCD() 890 a.Spec = argoproj.ArgoCDSpec{ 891 SourceNamespaces: []string{ 892 "test*", 893 "dev*", 894 }, 895 } 896 ns1 := v1.Namespace{ 897 ObjectMeta: metav1.ObjectMeta{ 898 Name: "test", 899 }, 900 } 901 ns2 := v1.Namespace{ 902 ObjectMeta: metav1.ObjectMeta{ 903 Name: "test-namespace-1", 904 }, 905 } 906 ns3 := v1.Namespace{ 907 ObjectMeta: metav1.ObjectMeta{ 908 Name: "dev-namespace-1", 909 }, 910 } 911 ns4 := v1.Namespace{ 912 ObjectMeta: metav1.ObjectMeta{ 913 Name: "other-namespace", 914 }, 915 } 916 917 resObjs := []client.Object{a, &ns1, &ns2, &ns3, &ns4} 918 subresObjs := []client.Object{a} 919 runtimeObjs := []runtime.Object{} 920 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 921 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 922 r := makeTestReconciler(cl, sch) 923 924 sourceNamespaces, err := r.getSourceNamespaces(a) 925 assert.NoError(t, err) 926 assert.Equal(t, 3, len(sourceNamespaces)) 927 assert.Contains(t, sourceNamespaces, "test") 928 assert.Contains(t, sourceNamespaces, "test-namespace-1") 929 assert.Contains(t, sourceNamespaces, "dev-namespace-1") 930 assert.NotContains(t, sourceNamespaces, "other-namespace") 931 } 932 933 func TestGetSourceNamespacesWithWildCardNamespace(t *testing.T) { 934 a := makeTestArgoCD() 935 a.Spec = argoproj.ArgoCDSpec{ 936 SourceNamespaces: []string{ 937 "*", 938 }, 939 } 940 ns1 := v1.Namespace{ 941 ObjectMeta: metav1.ObjectMeta{ 942 Name: "test-namespace-1", 943 }, 944 } 945 ns2 := v1.Namespace{ 946 ObjectMeta: metav1.ObjectMeta{ 947 Name: "test-namespace-2", 948 }, 949 } 950 ns3 := v1.Namespace{ 951 ObjectMeta: metav1.ObjectMeta{ 952 Name: "other-namespace", 953 }, 954 } 955 956 resObjs := []client.Object{a, &ns1, &ns2, &ns3} 957 subresObjs := []client.Object{a} 958 runtimeObjs := []runtime.Object{} 959 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 960 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 961 r := makeTestReconciler(cl, sch) 962 963 sourceNamespaces, err := r.getSourceNamespaces(a) 964 assert.NoError(t, err) 965 assert.Equal(t, 3, len(sourceNamespaces)) 966 assert.Contains(t, sourceNamespaces, "other-namespace") 967 assert.Contains(t, sourceNamespaces, "test-namespace-1") 968 assert.Contains(t, sourceNamespaces, "test-namespace-2") 969 } 970 971 func TestGenerateRandomString(t *testing.T) { 972 973 // verify the creation of unique strings 974 s1 := generateRandomString(20) 975 s2 := generateRandomString(20) 976 assert.NotEqual(t, s1, s2) 977 978 // verify length 979 a, _ := b64.URLEncoding.DecodeString(s1) 980 assert.Len(t, a, 20) 981 982 b, _ := b64.URLEncoding.DecodeString(s2) 983 assert.Len(t, b, 20) 984 } 985 986 func generateEncodedPEM(t *testing.T, host string) []byte { 987 key, err := argoutil.NewPrivateKey() 988 assert.NoError(t, err) 989 990 cert, err := argoutil.NewSelfSignedCACertificate("foo", key) 991 assert.NoError(t, err) 992 993 encoded := argoutil.EncodeCertificatePEM(cert) 994 return encoded 995 } 996 997 // TestReconcileArgoCD_reconcileDexOAuthClientSecret This test make sures that if dex is enabled a service account is created with token stored in a secret which is used for oauth 998 func TestReconcileArgoCD_reconcileDexOAuthClientSecret(t *testing.T) { 999 logf.SetLogger(ZapLogger(true)) 1000 a := makeTestArgoCD(func(ac *argoproj.ArgoCD) { 1001 ac.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 1002 Provider: argoproj.SSOProviderTypeDex, 1003 Dex: &argoproj.ArgoCDDexSpec{ 1004 OpenShiftOAuth: true, 1005 }, 1006 } 1007 }) 1008 1009 resObjs := []client.Object{a} 1010 subresObjs := []client.Object{a} 1011 runtimeObjs := []runtime.Object{} 1012 sch := makeTestReconcilerScheme(argoproj.AddToScheme) 1013 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 1014 r := makeTestReconciler(cl, sch) 1015 1016 assert.NoError(t, createNamespace(r, a.Namespace, "")) 1017 _, err := r.reconcileServiceAccount(common.ArgoCDDefaultDexServiceAccountName, a) 1018 assert.NoError(t, err) 1019 _, err = r.getDexOAuthClientSecret(a) 1020 assert.NoError(t, err) 1021 sa := newServiceAccountWithName(common.ArgoCDDefaultDexServiceAccountName, a) 1022 assert.NoError(t, argoutil.FetchObject(r.Client, a.Namespace, sa.Name, sa)) 1023 tokenExists := false 1024 for _, saSecret := range sa.Secrets { 1025 if strings.Contains(saSecret.Name, "dex-server-token") { 1026 tokenExists = true 1027 } 1028 } 1029 assert.True(t, tokenExists, "Dex is enabled but unable to create oauth client secret") 1030 }