Repository: argoproj-labs/argocd-operator
Pull request #757 (2022-08-16 15:43:46)

delete left out SSO test files that are no longer required
**What type of PR is this?**
> /kind code-refactoring

**What does this PR do / why we need it**:
We have added e2e tests for the repo with the [PR](https://github.com/argoproj-labs/argocd-operator/pull/590). In the same PR we have also refactored some existing e2e tests and SSO tests are one of them.

Before to this [PR](https://github.com/argoproj-labs/argocd-operator/pull/590), we used to maintain individual kUTTL configuration files but now all the tests kUTTL configuration is  moved into a single [file](https://github.com/argoproj-labs/argocd-operator/blob/master/tests/kuttl-tests.yaml). It can be triggered using a single make target `make test-e2e`.

If you would like to run the SSO tests individually you can run them using the below commands.
**Kubernetes**
- `kubectl kuttl test ./tests/k8s --config ./tests/kuttl-tests.yaml --test 1-016_validate_keycloak`

**OpenShift** 
- `kubectl kuttl test ./tests/ocp --config ./tests/kuttl-tests.yaml --test 1-001_validate_rhsso`

**Have you updated the necessary documentation?**
NA

**Which issue(s) this PR fixes**:
Removes unwanted files

**How to test changes / Special notes to the reviewer**:
If you would like to run the SSO tests individually you can run them using the below commands.
**Kubernetes**
- `kubectl kuttl test ./tests/k8s --config ./tests/kuttl-tests.yaml --test 1-016_validate_keycloak`

**OpenShift** 
- `kubectl kuttl test ./tests/ocp --config ./tests/kuttl-tests.yaml --test 1-001_validate_rhsso`


iam-veeramalla: @jaideepr97 PTAL

Patch:
diff --git a/OWNERS b/OWNERS
index 529d5a55..c205cd16 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,5 +2,15 @@ owners:
 - jomkz
 
 approvers:
+- iam-veeramalla
+- wtam2018
+- jannfis
+- jopit
 
 reviewers:
+- iam-veeramalla
+- wtam2018
+- jannfis
+- jopit
+- jaideepr97
+- ishitasequeira 
diff --git a/api/v1alpha1/argocd_types.go b/api/v1alpha1/argocd_types.go
index c84d9790..f36aa68b 100644
--- a/api/v1alpha1/argocd_types.go
+++ b/api/v1alpha1/argocd_types.go
@@ -131,6 +131,8 @@ type ArgoCDApplicationSet struct {
 
 	// LogLevel describes the log level that should be used by the ApplicationSet controller. Defaults to ArgoCDDefaultLogLevel if not set.  Valid options are debug,info, error, and warn.
 	LogLevel string `json:"logLevel,omitempty"`
+
+	WebhookServer WebhookServerSpec `json:"webhookServer,omitempty"`
 }
 
 // ArgoCDCASpec defines the CA options for ArgCD.
@@ -272,6 +274,9 @@ type ArgoCDKeycloakSpec struct {
 	// Resources defines the Compute Resources required by the container for Keycloak.
 	Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
 
+	// Custom root CA certificate for communicating with the Keycloak OIDC provider
+	RootCA string `json:"rootCA,omitempty"`
+
 	// Version is the Keycloak container image tag.
 	Version string `json:"version,omitempty"`
 
@@ -352,6 +357,10 @@ type ArgoCDRBACSpec struct {
 	// If omitted, defaults to: '[groups]'.
 	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Scopes",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:fieldGroup:RBAC","urn:alm:descriptor:com.tectonic.ui:text"}
 	Scopes *string `json:"scopes,omitempty"`
+
+	// PolicyMatcherMode configures the matchers function mode for casbin.
+	// There are two options for this, 'glob' for glob matcher or 'regex' for regex matcher.
+	PolicyMatcherMode *string `json:"policyMatcherMode,omitempty"`
 }
 
 // ArgoCDRedisSpec defines the desired state for the Redis server component.
@@ -830,6 +839,20 @@ type SSHHostsSpec struct {
 	Keys string `json:"keys,omitempty"`
 }
 
+// WebhookServerSpec defines the options for the ApplicationSet Webhook Server component.
+type WebhookServerSpec struct {
+
+	// Host is the hostname to use for Ingress/Route resources.
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Host",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server","urn:alm:descriptor:com.tectonic.ui:text"}
+	Host string `json:"host,omitempty"`
+
+	// Ingress defines the desired state for an Ingress for the Application set webhook component.
+	Ingress ArgoCDIngressSpec `json:"ingress,omitempty"`
+
+	// Route defines the desired state for an OpenShift Route for the Application set webhook component.
+	Route ArgoCDRouteSpec `json:"route,omitempty"`
+}
+
 // IsDeletionFinalizerPresent checks if the instance has deletion finalizer
 func (argocd *ArgoCD) IsDeletionFinalizerPresent() bool {
 	for _, finalizer := range argocd.GetFinalizers() {
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index fd62289c..d946dcd9 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -129,6 +129,7 @@ func (in *ArgoCDApplicationSet) DeepCopyInto(out *ArgoCDApplicationSet) {
 		*out = new(v1.ResourceRequirements)
 		(*in).DeepCopyInto(*out)
 	}
+	in.WebhookServer.DeepCopyInto(&out.WebhookServer)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoCDApplicationSet.
@@ -582,6 +583,11 @@ func (in *ArgoCDRBACSpec) DeepCopyInto(out *ArgoCDRBACSpec) {
 		*out = new(string)
 		**out = **in
 	}
+	if in.PolicyMatcherMode != nil {
+		in, out := &in.PolicyMatcherMode, &out.PolicyMatcherMode
+		*out = new(string)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoCDRBACSpec.
@@ -996,3 +1002,20 @@ func (in *SSHHostsSpec) DeepCopy() *SSHHostsSpec {
 	in.DeepCopyInto(out)
 	return out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WebhookServerSpec) DeepCopyInto(out *WebhookServerSpec) {
+	*out = *in
+	in.Ingress.DeepCopyInto(&out.Ingress)
+	in.Route.DeepCopyInto(&out.Route)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookServerSpec.
+func (in *WebhookServerSpec) DeepCopy() *WebhookServerSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(WebhookServerSpec)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/bundle/manifests/argocd-operator.clusterserviceversion.yaml b/bundle/manifests/argocd-operator.clusterserviceversion.yaml
index 431ac954..59fb1b2a 100644
--- a/bundle/manifests/argocd-operator.clusterserviceversion.yaml
+++ b/bundle/manifests/argocd-operator.clusterserviceversion.yaml
@@ -301,6 +301,28 @@ spec:
         x-descriptors:
         - urn:alm:descriptor:com.tectonic.ui:text
         - urn:alm:descriptor:com.tectonic.ui:advanced
+      - description: Host is the hostname to use for Ingress/Route resources.
+        displayName: Host
+        path: applicationSet.webhookServer.host
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:text
+      - description: Enabled will toggle the creation of the Ingress.
+        displayName: Ingress Enabled'
+        path: applicationSet.webhookServer.ingress.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
+      - description: Enabled will toggle the creation of the OpenShift Route.
+        displayName: Route Enabled'
+        path: applicationSet.webhookServer.route.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
       - description: ConfigManagementPlugins is used to specify additional config
           management plugins.
         displayName: Config Management Plugins'
diff --git a/bundle/manifests/argoproj.io_argocds.yaml b/bundle/manifests/argoproj.io_argocds.yaml
index 0e6e1618..cce444f9 100644
--- a/bundle/manifests/argoproj.io_argocds.yaml
+++ b/bundle/manifests/argoproj.io_argocds.yaml
@@ -81,6 +81,142 @@ spec:
                     description: Version is the Argo CD ApplicationSet image tag.
                       (optional)
                     type: string
+                  webhookServer:
+                    description: WebhookServerSpec defines the options for the ApplicationSet
+                      Webhook Server component.
+                    properties:
+                      host:
+                        description: Host is the hostname to use for Ingress/Route
+                          resources.
+                        type: string
+                      ingress:
+                        description: Ingress defines the desired state for an Ingress
+                          for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              apply to the Ingress.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the Ingress.
+                            type: boolean
+                          ingressClassName:
+                            description: IngressClassName for the Ingress resource.
+                            type: string
+                          path:
+                            description: Path used for the Ingress resource.
+                            type: string
+                          tls:
+                            description: TLS configuration. Currently the Ingress
+                              only supports a single TLS port, 443. If multiple members
+                              of this list specify different hosts, they will be multiplexed
+                              on the same port according to the hostname specified
+                              through the SNI TLS extension, if the ingress controller
+                              fulfilling the ingress supports SNI.
+                            items:
+                              description: IngressTLS describes the transport layer
+                                security associated with an Ingress.
+                              properties:
+                                hosts:
+                                  description: Hosts are a list of hosts included
+                                    in the TLS certificate. The values in this list
+                                    must match the name/s used in the tlsSecret. Defaults
+                                    to the wildcard host setting for the loadbalancer
+                                    controller fulfilling this Ingress, if left unspecified.
+                                  items:
+                                    type: string
+                                  type: array
+                                  x-kubernetes-list-type: atomic
+                                secretName:
+                                  description: SecretName is the name of the secret
+                                    used to terminate TLS traffic on port 443. Field
+                                    is left optional to allow TLS routing based on
+                                    SNI hostname alone. If the SNI host in a listener
+                                    conflicts with the "Host" header field used by
+                                    an IngressRule, the SNI host is used for termination
+                                    and value of the Host header is used for routing.
+                                  type: string
+                              type: object
+                            type: array
+                        required:
+                        - enabled
+                        type: object
+                      route:
+                        description: Route defines the desired state for an OpenShift
+                          Route for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              use for the Route resource.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the OpenShift
+                              Route.
+                            type: boolean
+                          labels:
+                            additionalProperties:
+                              type: string
+                            description: Labels is the map of labels to use for the
+                              Route resource
+                            type: object
+                          path:
+                            description: Path the router watches for, to route traffic
+                              for to the service.
+                            type: string
+                          tls:
+                            description: TLS provides the ability to configure certificates
+                              and termination for the Route.
+                            properties:
+                              caCertificate:
+                                description: caCertificate provides the cert authority
+                                  certificate contents
+                                type: string
+                              certificate:
+                                description: certificate provides certificate contents
+                                type: string
+                              destinationCACertificate:
+                                description: destinationCACertificate provides the
+                                  contents of the ca certificate of the final destination.  When
+                                  using reencrypt termination this file should be
+                                  provided in order to have routers use it for health
+                                  checks on the secure connection. If this field is
+                                  not specified, the router may provide its own destination
+                                  CA and perform hostname validation using the short
+                                  service name (service.namespace.svc), which allows
+                                  infrastructure generated certificates to automatically
+                                  verify.
+                                type: string
+                              insecureEdgeTerminationPolicy:
+                                description: "insecureEdgeTerminationPolicy indicates
+                                  the desired behavior for insecure connections to
+                                  a route. While each router may make its own decisions
+                                  on which ports to expose, this is normally port
+                                  80. \n * Allow - traffic is sent to the server on
+                                  the insecure port (default) * Disable - no traffic
+                                  is allowed on the insecure port. * Redirect - clients
+                                  are redirected to the secure port."
+                                type: string
+                              key:
+                                description: key provides key file contents
+                                type: string
+                              termination:
+                                description: termination indicates termination type.
+                                type: string
+                            required:
+                            - termination
+                            type: object
+                          wildcardPolicy:
+                            description: WildcardPolicy if any for the route. Currently
+                              only 'Subdomain' or 'None' is allowed.
+                            type: string
+                        required:
+                        - enabled
+                        type: object
+                    type: object
                 type: object
               banner:
                 description: Banner defines an additional banner to be displayed in
@@ -917,6 +1053,11 @@ spec:
                       are in the form:   g, subject, inherited-subject See https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/rbac.md
                       for additional information.'
                     type: string
+                  policyMatcherMode:
+                    description: PolicyMatcherMode configures the matchers function
+                      mode for casbin. There are two options for this, 'glob' for
+                      glob matcher or 'regex' for regex matcher.
+                    type: string
                   scopes:
                     description: 'Scopes controls which OIDC scopes to examine during
                       rbac enforcement (in addition to `sub` scope). If omitted, defaults
@@ -5767,6 +5908,10 @@ spec:
                               More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
                             type: object
                         type: object
+                      rootCA:
+                        description: Custom root CA certificate for communicating
+                          with the Keycloak OIDC provider
+                        type: string
                       verifyTLS:
                         description: VerifyTLS set to false disables strict TLS validation.
                         type: boolean
diff --git a/common/defaults.go b/common/defaults.go
index ec60020a..69bda900 100644
--- a/common/defaults.go
+++ b/common/defaults.go
@@ -315,3 +315,10 @@ func DefaultAnnotations(name string, namespace string) map[string]string {
 		AnnotationNamespace: namespace,
 	}
 }
+
+// DefaultNodeSelector returns the defult nodeSelector for ArgoCD workloads
+func DefaultNodeSelector() map[string]string {
+	return map[string]string{
+		"kubernetes.io/os": "linux",
+	}
+}
diff --git a/common/keys.go b/common/keys.go
index 64f7dcee..6b12a32d 100644
--- a/common/keys.go
+++ b/common/keys.go
@@ -170,6 +170,10 @@ const (
 	// ArgoCDKeyTLSPrivateKey is the key for TLS private keys.
 	ArgoCDKeyTLSPrivateKey = corev1.TLSPrivateKeyKey
 
+	// ArgoCDPolicyMatcherMode is the key for matchers function for casbin.
+	// There are two options for this, 'glob' for glob matcher or 'regex' for regex matcher.
+	ArgoCDPolicyMatcherMode = "policy.matchMode"
+
 	// ArgoCDKeyTolerateUnreadyEndpounts is the resource tolerate unready endpoints key for labels.
 	ArgoCDKeyTolerateUnreadyEndpounts = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
 
diff --git a/common/values.go b/common/values.go
index 1613c255..da670791 100644
--- a/common/values.go
+++ b/common/values.go
@@ -85,4 +85,7 @@ const (
 
 	// ArgoCDServerTLSSecretName is the name of the TLS secret for the argocd-server
 	ArgoCDServerTLSSecretName = "argocd-server-tls"
+
+	//ApplicationSetServiceNameSuffix is the suffix for Apllication Set Controller Service
+	ApplicationSetServiceNameSuffix = "applicationset-controller"
 )
diff --git a/config/crd/bases/argoproj.io_argocds.yaml b/config/crd/bases/argoproj.io_argocds.yaml
index 22da49a7..3859de0d 100644
--- a/config/crd/bases/argoproj.io_argocds.yaml
+++ b/config/crd/bases/argoproj.io_argocds.yaml
@@ -83,6 +83,142 @@ spec:
                     description: Version is the Argo CD ApplicationSet image tag.
                       (optional)
                     type: string
+                  webhookServer:
+                    description: WebhookServerSpec defines the options for the ApplicationSet
+                      Webhook Server component.
+                    properties:
+                      host:
+                        description: Host is the hostname to use for Ingress/Route
+                          resources.
+                        type: string
+                      ingress:
+                        description: Ingress defines the desired state for an Ingress
+                          for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              apply to the Ingress.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the Ingress.
+                            type: boolean
+                          ingressClassName:
+                            description: IngressClassName for the Ingress resource.
+                            type: string
+                          path:
+                            description: Path used for the Ingress resource.
+                            type: string
+                          tls:
+                            description: TLS configuration. Currently the Ingress
+                              only supports a single TLS port, 443. If multiple members
+                              of this list specify different hosts, they will be multiplexed
+                              on the same port according to the hostname specified
+                              through the SNI TLS extension, if the ingress controller
+                              fulfilling the ingress supports SNI.
+                            items:
+                              description: IngressTLS describes the transport layer
+                                security associated with an Ingress.
+                              properties:
+                                hosts:
+                                  description: Hosts are a list of hosts included
+                                    in the TLS certificate. The values in this list
+                                    must match the name/s used in the tlsSecret. Defaults
+                                    to the wildcard host setting for the loadbalancer
+                                    controller fulfilling this Ingress, if left unspecified.
+                                  items:
+                                    type: string
+                                  type: array
+                                  x-kubernetes-list-type: atomic
+                                secretName:
+                                  description: SecretName is the name of the secret
+                                    used to terminate TLS traffic on port 443. Field
+                                    is left optional to allow TLS routing based on
+                                    SNI hostname alone. If the SNI host in a listener
+                                    conflicts with the "Host" header field used by
+                                    an IngressRule, the SNI host is used for termination
+                                    and value of the Host header is used for routing.
+                                  type: string
+                              type: object
+                            type: array
+                        required:
+                        - enabled
+                        type: object
+                      route:
+                        description: Route defines the desired state for an OpenShift
+                          Route for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              use for the Route resource.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the OpenShift
+                              Route.
+                            type: boolean
+                          labels:
+                            additionalProperties:
+                              type: string
+                            description: Labels is the map of labels to use for the
+                              Route resource
+                            type: object
+                          path:
+                            description: Path the router watches for, to route traffic
+                              for to the service.
+                            type: string
+                          tls:
+                            description: TLS provides the ability to configure certificates
+                              and termination for the Route.
+                            properties:
+                              caCertificate:
+                                description: caCertificate provides the cert authority
+                                  certificate contents
+                                type: string
+                              certificate:
+                                description: certificate provides certificate contents
+                                type: string
+                              destinationCACertificate:
+                                description: destinationCACertificate provides the
+                                  contents of the ca certificate of the final destination.  When
+                                  using reencrypt termination this file should be
+                                  provided in order to have routers use it for health
+                                  checks on the secure connection. If this field is
+                                  not specified, the router may provide its own destination
+                                  CA and perform hostname validation using the short
+                                  service name (service.namespace.svc), which allows
+                                  infrastructure generated certificates to automatically
+                                  verify.
+                                type: string
+                              insecureEdgeTerminationPolicy:
+                                description: "insecureEdgeTerminationPolicy indicates
+                                  the desired behavior for insecure connections to
+                                  a route. While each router may make its own decisions
+                                  on which ports to expose, this is normally port
+                                  80. \n * Allow - traffic is sent to the server on
+                                  the insecure port (default) * Disable - no traffic
+                                  is allowed on the insecure port. * Redirect - clients
+                                  are redirected to the secure port."
+                                type: string
+                              key:
+                                description: key provides key file contents
+                                type: string
+                              termination:
+                                description: termination indicates termination type.
+                                type: string
+                            required:
+                            - termination
+                            type: object
+                          wildcardPolicy:
+                            description: WildcardPolicy if any for the route. Currently
+                              only 'Subdomain' or 'None' is allowed.
+                            type: string
+                        required:
+                        - enabled
+                        type: object
+                    type: object
                 type: object
               banner:
                 description: Banner defines an additional banner to be displayed in
@@ -919,6 +1055,11 @@ spec:
                       are in the form:   g, subject, inherited-subject See https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/rbac.md
                       for additional information.'
                     type: string
+                  policyMatcherMode:
+                    description: PolicyMatcherMode configures the matchers function
+                      mode for casbin. There are two options for this, 'glob' for
+                      glob matcher or 'regex' for regex matcher.
+                    type: string
                   scopes:
                     description: 'Scopes controls which OIDC scopes to examine during
                       rbac enforcement (in addition to `sub` scope). If omitted, defaults
@@ -5769,6 +5910,10 @@ spec:
                               More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
                             type: object
                         type: object
+                      rootCA:
+                        description: Custom root CA certificate for communicating
+                          with the Keycloak OIDC provider
+                        type: string
                       verifyTLS:
                         description: VerifyTLS set to false disables strict TLS validation.
                         type: boolean
diff --git a/config/manifests/bases/argocd-operator.clusterserviceversion.yaml b/config/manifests/bases/argocd-operator.clusterserviceversion.yaml
index 34034767..88db44a0 100644
--- a/config/manifests/bases/argocd-operator.clusterserviceversion.yaml
+++ b/config/manifests/bases/argocd-operator.clusterserviceversion.yaml
@@ -175,6 +175,28 @@ spec:
         x-descriptors:
         - urn:alm:descriptor:com.tectonic.ui:text
         - urn:alm:descriptor:com.tectonic.ui:advanced
+      - description: Host is the hostname to use for Ingress/Route resources.
+        displayName: Host
+        path: applicationSet.webhookServer.host
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:text
+      - description: Enabled will toggle the creation of the Ingress.
+        displayName: Ingress Enabled'
+        path: applicationSet.webhookServer.ingress.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
+      - description: Enabled will toggle the creation of the OpenShift Route.
+        displayName: Route Enabled'
+        path: applicationSet.webhookServer.route.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
       - description: ConfigManagementPlugins is used to specify additional config
           management plugins.
         displayName: Config Management Plugins'
diff --git a/controllers/argocd/applicationset.go b/controllers/argocd/applicationset.go
index f1daa4fe..a9beecf5 100644
--- a/controllers/argocd/applicationset.go
+++ b/controllers/argocd/applicationset.go
@@ -25,6 +25,7 @@ import (
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/intstr"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 
 	argoprojv1a1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
@@ -72,6 +73,11 @@ func (r *ReconcileArgoCD) reconcileApplicationSetController(cr *argoprojv1a1.Arg
 		return err
 	}
 
+	log.Info("reconciling applicationset service")
+	if err := r.reconcileApplicationSetService(cr); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -455,3 +461,50 @@ func setAppSetLabels(obj *metav1.ObjectMeta) {
 	obj.Labels["app.kubernetes.io/part-of"] = "argocd-applicationset"
 	obj.Labels["app.kubernetes.io/component"] = "controller"
 }
+
+// reconcileApplicationSetService will ensure that the Service is present for the ApplicationSet webhook and metrics component.
+func (r *ReconcileArgoCD) reconcileApplicationSetService(cr *argoprojv1a1.ArgoCD) error {
+	log.Info("reconciling applicationset service")
+
+	svc := newServiceWithSuffix(common.ApplicationSetServiceNameSuffix, common.ApplicationSetServiceNameSuffix, cr)
+	if cr.Spec.ApplicationSet == nil {
+
+		if argoutil.IsObjectFound(r.Client, cr.Namespace, svc.Name, svc) {
+			err := argoutil.FetchObject(r.Client, cr.Namespace, svc.Name, svc)
+			if err != nil {
+				return err
+			}
+			log.Info(fmt.Sprintf("Deleting applicationset controller service %s as applicationset is disabled", svc.Name))
+			err = r.Delete(context.TODO(), svc)
+			if err != nil {
+				return err
+			}
+		}
+	} else {
+		if argoutil.IsObjectFound(r.Client, cr.Namespace, svc.Name, svc) {
+			return nil // Service found, do nothing
+		}
+	}
+	svc.Spec.Ports = []corev1.ServicePort{
+		{
+			Name:       "webhook",
+			Port:       7000,
+			Protocol:   corev1.ProtocolTCP,
+			TargetPort: intstr.FromInt(7000),
+		}, {
+			Name:       "metrics",
+			Port:       8080,
+			Protocol:   corev1.ProtocolTCP,
+			TargetPort: intstr.FromInt(8080),
+		},
+	}
+
+	svc.Spec.Selector = map[string]string{
+		common.ArgoCDKeyName: nameWithSuffix(common.ApplicationSetServiceNameSuffix, cr),
+	}
+
+	if err := controllerutil.SetControllerReference(cr, svc, r.Scheme); err != nil {
+		return err
+	}
+	return r.Client.Create(context.TODO(), svc)
+}
diff --git a/controllers/argocd/configmap.go b/controllers/argocd/configmap.go
index ce8cc908..a59dceb1 100644
--- a/controllers/argocd/configmap.go
+++ b/controllers/argocd/configmap.go
@@ -509,6 +509,12 @@ func (r *ReconcileArgoCD) reconcileRBACConfigMap(cm *corev1.ConfigMap, cr *argop
 		changed = true
 	}
 
+	// Default Policy Matcher Mode
+	if cr.Spec.RBAC.PolicyMatcherMode != nil && cm.Data[common.ArgoCDPolicyMatcherMode] != *cr.Spec.RBAC.PolicyMatcherMode {
+		cm.Data[common.ArgoCDPolicyMatcherMode] = *cr.Spec.RBAC.PolicyMatcherMode
+		changed = true
+	}
+
 	// Scopes
 	if cr.Spec.RBAC.Scopes != nil && cm.Data[common.ArgoCDKeyRBACScopes] != *cr.Spec.RBAC.Scopes {
 		cm.Data[common.ArgoCDKeyRBACScopes] = *cr.Spec.RBAC.Scopes
diff --git a/controllers/argocd/deployment.go b/controllers/argocd/deployment.go
index 9ba3baee..b316ffae 100644
--- a/controllers/argocd/deployment.go
+++ b/controllers/argocd/deployment.go
@@ -372,11 +372,14 @@ func newDeploymentWithName(name string, component string, cr *argoprojv1a1.ArgoC
 					common.ArgoCDKeyName: name,
 				},
 			},
+			Spec: corev1.PodSpec{
+				NodeSelector: common.DefaultNodeSelector(),
+			},
 		},
 	}
 
 	if cr.Spec.NodePlacement != nil {
-		deploy.Spec.Template.Spec.NodeSelector = cr.Spec.NodePlacement.NodeSelector
+		deploy.Spec.Template.Spec.NodeSelector = argoutil.AppendStringMap(deploy.Spec.Template.Spec.NodeSelector, cr.Spec.NodePlacement.NodeSelector)
 		deploy.Spec.Template.Spec.Tolerations = cr.Spec.NodePlacement.Tolerations
 	}
 	return deploy
diff --git a/controllers/argocd/ingress.go b/controllers/argocd/ingress.go
index 903454ae..f28596b3 100644
--- a/controllers/argocd/ingress.go
+++ b/controllers/argocd/ingress.go
@@ -81,6 +81,11 @@ func (r *ReconcileArgoCD) reconcileIngresses(cr *argoprojv1a1.ArgoCD) error {
 	if err := r.reconcilePrometheusIngress(cr); err != nil {
 		return err
 	}
+
+	if err := r.reconcileApplicationSetControllerIngress(cr); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -385,3 +390,67 @@ func (r *ReconcileArgoCD) reconcilePrometheusIngress(cr *argoprojv1a1.ArgoCD) er
 	}
 	return r.Client.Create(context.TODO(), ingress)
 }
+
+// reconcileApplicationSetControllerIngress will ensure that the ApplicationSetController Ingress is present.
+func (r *ReconcileArgoCD) reconcileApplicationSetControllerIngress(cr *argoprojv1a1.ArgoCD) error {
+	ingress := newIngressWithSuffix(common.ApplicationSetServiceNameSuffix, cr)
+	if argoutil.IsObjectFound(r.Client, cr.Namespace, ingress.Name, ingress) {
+		if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Ingress.Enabled {
+			return r.Client.Delete(context.TODO(), ingress)
+		}
+		return nil // Ingress found and enabled, do nothing
+	}
+
+	if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Ingress.Enabled {
+		log.Info("not enabled")
+		return nil // Ingress not enabled, move along...
+	}
+
+	// Add annotations
+	atns := make(map[string]string)
+	atns[common.ArgoCDKeyIngressSSLRedirect] = "true"
+	atns[common.ArgoCDKeyIngressBackendProtocol] = "HTTP"
+
+	// Override default annotations if specified
+	if len(cr.Spec.ApplicationSet.WebhookServer.Ingress.Annotations) > 0 {
+		atns = cr.Spec.ApplicationSet.WebhookServer.Ingress.Annotations
+	}
+
+	ingress.ObjectMeta.Annotations = atns
+
+	pathType := networkingv1.PathTypeImplementationSpecific
+	// Add rules
+	ingress.Spec.Rules = []networkingv1.IngressRule{
+		{
+			Host: getApplicationSetHTTPServerHost(cr),
+			IngressRuleValue: networkingv1.IngressRuleValue{
+				HTTP: &networkingv1.HTTPIngressRuleValue{
+					Paths: []networkingv1.HTTPIngressPath{
+						{
+							Path: "/api/webhook",
+							Backend: networkingv1.IngressBackend{
+								Service: &networkingv1.IngressServiceBackend{
+									Name: nameWithSuffix(common.ApplicationSetServiceNameSuffix, cr),
+									Port: networkingv1.ServiceBackendPort{
+										Name: "webhook",
+									},
+								},
+							},
+							PathType: &pathType,
+						},
+					},
+				},
+			},
+		},
+	}
+
+	// Allow override of TLS options if specified
+	if len(cr.Spec.ApplicationSet.WebhookServer.Ingress.TLS) > 0 {
+		ingress.Spec.TLS = cr.Spec.ApplicationSet.WebhookServer.Ingress.TLS
+	}
+
+	if err := controllerutil.SetControllerReference(cr, ingress, r.Scheme); err != nil {
+		return err
+	}
+	return r.Client.Create(context.TODO(), ingress)
+}
diff --git a/controllers/argocd/keycloak.go b/controllers/argocd/keycloak.go
index 44798580..4b331f6e 100644
--- a/controllers/argocd/keycloak.go
+++ b/controllers/argocd/keycloak.go
@@ -107,6 +107,7 @@ type oidcConfig struct {
 	ClientID       string   `json:"clientID"`
 	ClientSecret   string   `json:"clientSecret"`
 	RequestedScope []string `json:"requestedScopes"`
+	RootCA         string   `json:"rootCA,omitempty"`
 }
 
 // KeycloakIdentityProviderMapper defines IdentityProvider Mappers
@@ -307,7 +308,7 @@ func getKeycloakContainer(cr *argoprojv1a1.ArgoCD) corev1.Container {
 			{ContainerPort: 8888, Name: "ping", Protocol: "TCP"},
 		},
 		ReadinessProbe: &corev1.Probe{
-			FailureThreshold: 10,
+			FailureThreshold: 20,
 			ProbeHandler: corev1.ProbeHandler{
 				Exec: &corev1.ExecAction{
 					Command: []string{
@@ -407,6 +408,7 @@ func getKeycloakDeploymentConfigTemplate(cr *argoprojv1a1.ArgoCD) *appsv1.Deploy
 							},
 						},
 					},
+					NodeSelector: common.DefaultNodeSelector(),
 				},
 			},
 			Triggers: appsv1.DeploymentTriggerPolicies{
@@ -418,7 +420,7 @@ func getKeycloakDeploymentConfigTemplate(cr *argoprojv1a1.ArgoCD) *appsv1.Deploy
 	}
 
 	if cr.Spec.NodePlacement != nil {
-		dc.Spec.Template.Spec.NodeSelector = cr.Spec.NodePlacement.NodeSelector
+		dc.Spec.Template.Spec.NodeSelector = argoutil.AppendStringMap(dc.Spec.Template.Spec.NodeSelector, cr.Spec.NodePlacement.NodeSelector)
 		dc.Spec.Template.Spec.Tolerations = cr.Spec.NodePlacement.Tolerations
 	}
 
@@ -1093,6 +1095,10 @@ func (r *ReconcileArgoCD) updateArgoCDConfiguration(cr *argoprojv1a1.ArgoCD, kRo
 	}
 
 	// Update ArgoCD instance for OIDC Config with Keycloakrealm URL
+	rootCA := ""
+	if cr.Spec.SSO.Keycloak.RootCA != "" {
+		rootCA = cr.Spec.SSO.Keycloak.RootCA
+	}
 	o, err := yaml.Marshal(oidcConfig{
 		Name: "Keycloak",
 		Issuer: fmt.Sprintf("%s/auth/realms/%s",
@@ -1100,6 +1106,7 @@ func (r *ReconcileArgoCD) updateArgoCDConfiguration(cr *argoprojv1a1.ArgoCD, kRo
 		ClientID:       keycloakClient,
 		ClientSecret:   "$oidc.keycloak.clientSecret",
 		RequestedScope: []string{"openid", "profile", "email", "groups"},
+		RootCA:         rootCA,
 	})
 
 	if err != nil {
@@ -1392,9 +1399,8 @@ func (r *ReconcileArgoCD) reconcileKeycloakForOpenShift(cr *argoprojv1a1.ArgoCD)
 		}
 	}
 
-	// If Keycloak deployment exists and a realm is already created for ArgoCD, Do not create a new one.
-	if existingDC.Status.AvailableReplicas == expectedReplicas &&
-		existingDC.Annotations["argocd.argoproj.io/realm-created"] == "false" {
+	// Proceed with the keycloak configuration only once the keycloak pod is up and running.
+	if existingDC.Status.AvailableReplicas == expectedReplicas {
 
 		cfg, err := r.prepareKeycloakConfig(cr)
 		if err != nil {
@@ -1404,49 +1410,56 @@ func (r *ReconcileArgoCD) reconcileKeycloakForOpenShift(cr *argoprojv1a1.ArgoCD)
 		// keycloakRouteURL is used to update the OIDC configuration for ArgoCD.
 		keycloakRouteURL := cfg.KeycloakURL
 
-		// Create a keycloak realm and publish.
-		response, err := createRealm(cfg)
-		if err != nil {
-			log.Error(err, fmt.Sprintf("Failed posting keycloak realm configuration for ArgoCD %s in namespace %s",
-				cr.Name, cr.Namespace))
-			return err
-		}
-
-		if response == successResponse {
-			log.Info(fmt.Sprintf("Successfully created keycloak realm for ArgoCD %s in namespace %s",
-				cr.Name, cr.Namespace))
+		// If Keycloak deployment exists and a realm is already created for ArgoCD, Do not create a new one.
+		if existingDC.Annotations["argocd.argoproj.io/realm-created"] == "false" {
 
-			// TODO: Remove the deleteOAuthClient invocation once the issue is resolved.
-			// OAuthClient configuration does not get deleted from previous instances occasionally.
-			// It is safe to delete before updating the OIDC config.
-			// https://github.com/openshift/client-go/issues/209
-			err = deleteOAuthClient(cr)
+			// Create a keycloak realm and publish.
+			response, err := createRealm(cfg)
 			if err != nil {
+				log.Error(err, fmt.Sprintf("Failed posting keycloak realm configuration for ArgoCD %s in namespace %s",
+					cr.Name, cr.Namespace))
 				return err
 			}
 
-			// Update Realm creation. This will avoid posting of realm configuration on further reconciliations.
-			err = r.Client.Get(context.TODO(), types.NamespacedName{Name: existingDC.Name, Namespace: existingDC.Namespace}, existingDC)
-			if err != nil {
-				return err
-			}
+			if response == successResponse {
+				log.Info(fmt.Sprintf("Successfully created keycloak realm for ArgoCD %s in namespace %s",
+					cr.Name, cr.Namespace))
 
-			existingDC.Annotations["argocd.argoproj.io/realm-created"] = "true"
-			err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
-				return r.Client.Update(context.TODO(), existingDC)
-			})
+				// TODO: Remove the deleteOAuthClient invocation once the issue is resolved.
+				// OAuthClient configuration does not get deleted from previous instances occasionally.
+				// It is safe to delete before updating the OIDC config.
+				// https://github.com/openshift/client-go/issues/209
+				err = deleteOAuthClient(cr)
+				if err != nil {
+					return err
+				}
 
-			if err != nil {
-				return err
-			}
+				// Update Realm creation. This will avoid posting of realm configuration on further reconciliations.
+				err = r.Client.Get(context.TODO(), types.NamespacedName{Name: existingDC.Name, Namespace: existingDC.Namespace}, existingDC)
+				if err != nil {
+					return err
+				}
+
+				existingDC.Annotations["argocd.argoproj.io/realm-created"] = "true"
+				err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
+					return r.Client.Update(context.TODO(), existingDC)
+				})
+
+				if err != nil {
+					return err
+				}
 
-			err = r.updateArgoCDConfiguration(cr, keycloakRouteURL)
-			if err != nil {
-				log.Error(err, fmt.Sprintf("Failed to update OIDC Configuration for ArgoCD %s in namespace %s",
-					cr.Name, cr.Namespace))
-				return err
 			}
 		}
+
+		// Updates OIDC Configuration in the argocd-cm when Keycloak is initially configured
+		// or when user requests to update the OIDC configuration through `.spec.sso.keycloak.rootCA`.
+		err = r.updateArgoCDConfiguration(cr, keycloakRouteURL)
+		if err != nil {
+			log.Error(err, fmt.Sprintf("Failed to update OIDC Configuration for ArgoCD %s in namespace %s",
+				cr.Name, cr.Namespace))
+			return err
+		}
 	}
 
 	return nil
@@ -1489,9 +1502,8 @@ func (r *ReconcileArgoCD) reconcileKeycloak(cr *argoprojv1a1.ArgoCD) error {
 		}
 	}
 
-	// If Keycloak deployment exists and a realm is already created for ArgoCD, Do not create a new one.
-	if existingDeployment.Status.AvailableReplicas == expectedReplicas &&
-		existingDeployment.Annotations["argocd.argoproj.io/realm-created"] == "false" {
+	// Proceed with the keycloak configuration only once the keycloak pod is up and running.
+	if existingDeployment.Status.AvailableReplicas == expectedReplicas {
 
 		cfg, err := r.prepareKeycloakConfigForK8s(cr)
 		if err != nil {
@@ -1501,25 +1513,30 @@ func (r *ReconcileArgoCD) reconcileKeycloak(cr *argoprojv1a1.ArgoCD) error {
 		// kIngURL is used to update the OIDC configuration for ArgoCD.
 		kIngURL := cfg.KeycloakURL
 
-		// Create a keycloak realm and publish.
-		response, err := createRealm(cfg)
-		if err != nil {
-			log.Error(err, fmt.Sprintf("Failed posting keycloak realm configuration for ArgoCD %s in namespace %s",
-				cr.Name, cr.Namespace))
-			return err
-		}
-
-		if response == successResponse {
-			log.Info("Successfully created keycloak realm for ArgoCD %s in namespace %s")
-
-			// Update Realm creation. This will avoid posting of realm configuration on further reconciliations.
-			existingDeployment.Annotations["argocd.argoproj.io/realm-created"] = "true"
-			err = r.Client.Update(context.TODO(), existingDeployment)
+		// If Keycloak deployment exists and a realm is already created for ArgoCD, Do not create a new one.
+		if existingDeployment.Annotations["argocd.argoproj.io/realm-created"] == "false" {
+			// Create a keycloak realm and publish.
+			response, err := createRealm(cfg)
 			if err != nil {
+				log.Error(err, fmt.Sprintf("Failed posting keycloak realm configuration for ArgoCD %s in namespace %s",
+					cr.Name, cr.Namespace))
 				return err
 			}
+
+			if response == successResponse {
+				log.Info("Successfully created keycloak realm for ArgoCD %s in namespace %s")
+
+				// Update Realm creation. This will avoid posting of realm configuration on further reconciliations.
+				existingDeployment.Annotations["argocd.argoproj.io/realm-created"] = "true"
+				err = r.Client.Update(context.TODO(), existingDeployment)
+				if err != nil {
+					return err
+				}
+			}
 		}
 
+		// Updates OIDC Configuration in the argocd-cm when Keycloak is initially configured
+		// or when user requests to update the OIDC configuration through `.spec.sso.keycloak.rootCA`.
 		err = r.updateArgoCDConfiguration(cr, kIngURL)
 		if err != nil {
 			log.Error(err, fmt.Sprintf("Failed to update OIDC Configuration for ArgoCD %s in namespace %s",
diff --git a/controllers/argocd/role.go b/controllers/argocd/role.go
index 1e7e1ce2..1874661e 100644
--- a/controllers/argocd/role.go
+++ b/controllers/argocd/role.go
@@ -78,6 +78,11 @@ func (r *ReconcileArgoCD) reconcileRole(name string, policyRules []v1.PolicyRule
 
 	// create policy rules for each namespace
 	for _, namespace := range r.ManagedNamespaces.Items {
+		// Skip terminating namespaces.
+		if namespace.DeletionTimestamp != nil {
+			continue
+		}
+
 		list := &argoprojv1a1.ArgoCDList{}
 		listOption := &client.ListOptions{Namespace: namespace.Name}
 		err := r.Client.List(context.TODO(), list, listOption)
diff --git a/controllers/argocd/rolebinding.go b/controllers/argocd/rolebinding.go
index a9ac988f..1c9f32df 100644
--- a/controllers/argocd/rolebinding.go
+++ b/controllers/argocd/rolebinding.go
@@ -95,6 +95,11 @@ func (r *ReconcileArgoCD) reconcileRoleBinding(name string, rules []v1.PolicyRul
 	}
 
 	for _, namespace := range r.ManagedNamespaces.Items {
+		// Skip terminating namespaces.
+		if namespace.DeletionTimestamp != nil {
+			continue
+		}
+
 		list := &argoprojv1a1.ArgoCDList{}
 		listOption := &client.ListOptions{Namespace: namespace.Name}
 		err := r.Client.List(context.TODO(), list, listOption)
diff --git a/controllers/argocd/route.go b/controllers/argocd/route.go
index 2a5ec957..70daa62e 100644
--- a/controllers/argocd/route.go
+++ b/controllers/argocd/route.go
@@ -86,6 +86,11 @@ func (r *ReconcileArgoCD) reconcileRoutes(cr *argoprojv1a1.ArgoCD) error {
 	if err := r.reconcileServerRoute(cr); err != nil {
 		return err
 	}
+
+	if err := r.reconcileApplicationSetControllerWebhookRoute(cr); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -284,3 +289,80 @@ func (r *ReconcileArgoCD) reconcileServerRoute(cr *argoprojv1a1.ArgoCD) error {
 	}
 	return r.Client.Update(context.TODO(), route)
 }
+
+// reconcileApplicationSetControllerWebhookRoute will ensure that the ArgoCD Server Route is present.
+func (r *ReconcileArgoCD) reconcileApplicationSetControllerWebhookRoute(cr *argoprojv1a1.ArgoCD) error {
+	name := fmt.Sprintf("%s-%s", common.ApplicationSetServiceNameSuffix, "webhook")
+	route := newRouteWithSuffix(name, cr)
+	found := argoutil.IsObjectFound(r.Client, cr.Namespace, route.Name, route)
+	if found {
+		if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Route.Enabled {
+			// Route exists but enabled flag has been set to false, delete the Route
+			return r.Client.Delete(context.TODO(), route)
+		}
+	}
+
+	if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Route.Enabled {
+		return nil // Route not enabled, move along...
+	}
+
+	// Allow override of the Annotations for the Route.
+	if len(cr.Spec.ApplicationSet.WebhookServer.Route.Annotations) > 0 {
+		route.Annotations = cr.Spec.Server.Route.Annotations
+	}
+
+	// Allow override of the Labels for the Route.
+	if len(cr.Spec.ApplicationSet.WebhookServer.Route.Labels) > 0 {
+		labels := route.Labels
+		for key, val := range cr.Spec.Server.Route.Labels {
+			labels[key] = val
+		}
+		route.Labels = labels
+	}
+
+	// Allow override of the Host for the Route.
+	if len(cr.Spec.Server.Host) > 0 {
+		route.Spec.Host = cr.Spec.ApplicationSet.WebhookServer.Host
+	}
+
+	if cr.Spec.Server.Insecure {
+		// Disable TLS and rely on the cluster certificate.
+		route.Spec.Port = &routev1.RoutePort{
+			TargetPort: intstr.FromString("webhook"),
+		}
+		route.Spec.TLS = &routev1.TLSConfig{
+			InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect,
+			Termination:                   routev1.TLSTerminationEdge,
+		}
+	} else {
+		// Server is using TLS configure passthrough.
+		route.Spec.Port = &routev1.RoutePort{
+			TargetPort: intstr.FromString("webhook"),
+		}
+		route.Spec.TLS = &routev1.TLSConfig{
+			InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect,
+			Termination:                   routev1.TLSTerminationPassthrough,
+		}
+	}
+
+	// Allow override of TLS options for the Route
+	if cr.Spec.Server.Route.TLS != nil {
+		route.Spec.TLS = cr.Spec.Server.Route.TLS
+	}
+
+	route.Spec.To.Kind = "Service"
+	route.Spec.To.Name = nameWithSuffix(common.ApplicationSetServiceNameSuffix, cr)
+
+	// Allow override of the WildcardPolicy for the Route
+	if cr.Spec.Server.Route.WildcardPolicy != nil && len(*cr.Spec.Server.Route.WildcardPolicy) > 0 {
+		route.Spec.WildcardPolicy = *cr.Spec.Server.Route.WildcardPolicy
+	}
+
+	if err := controllerutil.SetControllerReference(cr, route, r.Scheme); err != nil {
+		return err
+	}
+	if !found {
+		return r.Client.Create(context.TODO(), route)
+	}
+	return r.Client.Update(context.TODO(), route)
+}
diff --git a/controllers/argocd/sso.go b/controllers/argocd/sso.go
index e598cdf3..0bd965b0 100644
--- a/controllers/argocd/sso.go
+++ b/controllers/argocd/sso.go
@@ -85,7 +85,8 @@ func (r *ReconcileArgoCD) reconcileSSO(cr *argoprojv1a1.ArgoCD) error {
 		}
 	}
 
-	if cr.Spec.SSO != nil && (cr.Spec.SSO.Image != "" || cr.Spec.SSO.Version != "" || cr.Spec.SSO.VerifyTLS != nil || cr.Spec.SSO.Resources != nil) {
+	if cr.Spec.SSO != nil && (cr.Spec.SSO.Image != "" || cr.Spec.SSO.Version != "" ||
+		cr.Spec.SSO.VerifyTLS != nil || cr.Spec.SSO.Resources != nil) {
 
 		// Emit event warning users about deprecation notice for `.spec.SSO` field users
 		err := argoutil.CreateEvent(r.Client, "Warning", "Deprecated", "`.spec.SSO.Image`, `.spec.SSO.Version`, `.spec.SSO.Resources` and `.spec.SSO.VerifyTLS` are deprecated, and support will be removed in Argo CD Operator v0.6.0/OpenShift GitOps v1.9.0. Keycloak configuration can be managed through `.spec.sso.keycloak`", "DeprecationNotice", cr.ObjectMeta)
@@ -164,7 +165,8 @@ func (r *ReconcileArgoCD) reconcileSSO(cr *argoprojv1a1.ArgoCD) error {
 		if cr.Spec.SSO.Provider == v1alpha1.SSOProviderTypeKeycloak {
 			// Relevant SSO settings at play are `DISABLE_DEX`, `.spec.dex`, `.spec.sso` fields, `.spec.sso.keycloak`, `.spec.sso.dex`
 
-			if (cr.Spec.SSO.Keycloak != nil) && (cr.Spec.SSO.Image != "" || cr.Spec.SSO.Version != "" || cr.Spec.SSO.Resources != nil || cr.Spec.SSO.VerifyTLS != cr.Spec.SSO.Keycloak.VerifyTLS) {
+			if (cr.Spec.SSO.Keycloak != nil) && (cr.Spec.SSO.Image != "" || cr.Spec.SSO.Version != "" ||
+				cr.Spec.SSO.Resources != nil || cr.Spec.SSO.VerifyTLS != nil) {
 				// Keycloak specs expressed both in old `.spec.sso` fields as well as in `.spec.sso.keycloak` simultaneously and they don't match
 				// ==> conflict
 				errMsg = "cannot specify keycloak fields in .spec.sso when keycloak is configured through .spec.sso.keycloak"
diff --git a/controllers/argocd/statefulset.go b/controllers/argocd/statefulset.go
index f15003cb..1ccb7a29 100644
--- a/controllers/argocd/statefulset.go
+++ b/controllers/argocd/statefulset.go
@@ -71,10 +71,13 @@ func newStatefulSetWithName(name string, component string, cr *argoprojv1a1.Argo
 					common.ArgoCDKeyName: name,
 				},
 			},
+			Spec: corev1.PodSpec{
+				NodeSelector: common.DefaultNodeSelector(),
+			},
 		},
 	}
 	if cr.Spec.NodePlacement != nil {
-		ss.Spec.Template.Spec.NodeSelector = cr.Spec.NodePlacement.NodeSelector
+		ss.Spec.Template.Spec.NodeSelector = argoutil.AppendStringMap(ss.Spec.Template.Spec.NodeSelector, cr.Spec.NodePlacement.NodeSelector)
 		ss.Spec.Template.Spec.Tolerations = cr.Spec.NodePlacement.Tolerations
 	}
 	ss.Spec.ServiceName = name
diff --git a/controllers/argocd/util.go b/controllers/argocd/util.go
index 044be379..b49a434e 100644
--- a/controllers/argocd/util.go
+++ b/controllers/argocd/util.go
@@ -1476,3 +1476,12 @@ func contains(s []string, g string) bool {
 	}
 	return false
 }
+
+// getApplicationSetHTTPServerHost will return the host for the given ArgoCD.
+func getApplicationSetHTTPServerHost(cr *argoprojv1a1.ArgoCD) string {
+	host := cr.Name
+	if len(cr.Spec.ApplicationSet.WebhookServer.Host) > 0 {
+		host = cr.Spec.ApplicationSet.WebhookServer.Host
+	}
+	return host
+}
diff --git a/deploy/catalog_source.yaml b/deploy/catalog_source.yaml
deleted file mode 100644
index 59538e2f..00000000
--- a/deploy/catalog_source.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-apiVersion: operators.coreos.com/v1alpha1
-kind: CatalogSource
-metadata:
-  name: argocd-catalog
-spec:
-  sourceType: grpc
-  image: quay.io/argoprojlabs/argocd-operator-registry@sha256:becf932b398c1b89b6abb486b18725c3d408f7cce136d0d2784fd305b6ea41e2
-  displayName: Argo CD Operators
-  publisher: Argo CD Community
diff --git a/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml b/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml
index c5536cda..92f0ab6b 100644
--- a/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml
+++ b/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml
@@ -301,6 +301,28 @@ spec:
         x-descriptors:
         - urn:alm:descriptor:com.tectonic.ui:text
         - urn:alm:descriptor:com.tectonic.ui:advanced
+      - description: Host is the hostname to use for Ingress/Route resources.
+        displayName: Host
+        path: applicationSet.webhookServer.host
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:text
+      - description: Enabled will toggle the creation of the Ingress.
+        displayName: Ingress Enabled'
+        path: applicationSet.webhookServer.ingress.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
+      - description: Enabled will toggle the creation of the OpenShift Route.
+        displayName: Route Enabled'
+        path: applicationSet.webhookServer.route.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
       - description: ConfigManagementPlugins is used to specify additional config
           management plugins.
         displayName: Config Management Plugins'
diff --git a/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml b/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml
index 0e6e1618..d869c2ec 100644
--- a/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml
+++ b/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml
@@ -81,6 +81,139 @@ spec:
                     description: Version is the Argo CD ApplicationSet image tag.
                       (optional)
                     type: string
+                  webhookServer:
+                    description: WebhookServerSpec defines the options for the ApplicationSet
+                      Webhook Server component.
+                    properties:
+                      host:
+                        description: Host is the hostname to use for Ingress/Route
+                          resources.
+                        type: string
+                      ingress:
+                        description: Ingress defines the desired state for an Ingress
+                          for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              apply to the Ingress.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the Ingress.
+                            type: boolean
+                          path:
+                            description: Path used for the Ingress resource.
+                            type: string
+                          tls:
+                            description: TLS configuration. Currently the Ingress
+                              only supports a single TLS port, 443. If multiple members
+                              of this list specify different hosts, they will be multiplexed
+                              on the same port according to the hostname specified
+                              through the SNI TLS extension, if the ingress controller
+                              fulfilling the ingress supports SNI.
+                            items:
+                              description: IngressTLS describes the transport layer
+                                security associated with an Ingress.
+                              properties:
+                                hosts:
+                                  description: Hosts are a list of hosts included
+                                    in the TLS certificate. The values in this list
+                                    must match the name/s used in the tlsSecret. Defaults
+                                    to the wildcard host setting for the loadbalancer
+                                    controller fulfilling this Ingress, if left unspecified.
+                                  items:
+                                    type: string
+                                  type: array
+                                  x-kubernetes-list-type: atomic
+                                secretName:
+                                  description: SecretName is the name of the secret
+                                    used to terminate TLS traffic on port 443. Field
+                                    is left optional to allow TLS routing based on
+                                    SNI hostname alone. If the SNI host in a listener
+                                    conflicts with the "Host" header field used by
+                                    an IngressRule, the SNI host is used for termination
+                                    and value of the Host header is used for routing.
+                                  type: string
+                              type: object
+                            type: array
+                        required:
+                        - enabled
+                        type: object
+                      route:
+                        description: Route defines the desired state for an OpenShift
+                          Route for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              use for the Route resource.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the OpenShift
+                              Route.
+                            type: boolean
+                          labels:
+                            additionalProperties:
+                              type: string
+                            description: Labels is the map of labels to use for the
+                              Route resource
+                            type: object
+                          path:
+                            description: Path the router watches for, to route traffic
+                              for to the service.
+                            type: string
+                          tls:
+                            description: TLS provides the ability to configure certificates
+                              and termination for the Route.
+                            properties:
+                              caCertificate:
+                                description: caCertificate provides the cert authority
+                                  certificate contents
+                                type: string
+                              certificate:
+                                description: certificate provides certificate contents
+                                type: string
+                              destinationCACertificate:
+                                description: destinationCACertificate provides the
+                                  contents of the ca certificate of the final destination.  When
+                                  using reencrypt termination this file should be
+                                  provided in order to have routers use it for health
+                                  checks on the secure connection. If this field is
+                                  not specified, the router may provide its own destination
+                                  CA and perform hostname validation using the short
+                                  service name (service.namespace.svc), which allows
+                                  infrastructure generated certificates to automatically
+                                  verify.
+                                type: string
+                              insecureEdgeTerminationPolicy:
+                                description: "insecureEdgeTerminationPolicy indicates
+                                  the desired behavior for insecure connections to
+                                  a route. While each router may make its own decisions
+                                  on which ports to expose, this is normally port
+                                  80. \n * Allow - traffic is sent to the server on
+                                  the insecure port (default) * Disable - no traffic
+                                  is allowed on the insecure port. * Redirect - clients
+                                  are redirected to the secure port."
+                                type: string
+                              key:
+                                description: key provides key file contents
+                                type: string
+                              termination:
+                                description: termination indicates termination type.
+                                type: string
+                            required:
+                            - termination
+                            type: object
+                          wildcardPolicy:
+                            description: WildcardPolicy if any for the route. Currently
+                              only 'Subdomain' or 'None' is allowed.
+                            type: string
+                        required:
+                        - enabled
+                        type: object
+                    type: object
                 type: object
               banner:
                 description: Banner defines an additional banner to be displayed in
diff --git a/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml b/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml
index 431ac954..59fb1b2a 100644
--- a/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml
+++ b/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml
@@ -301,6 +301,28 @@ spec:
         x-descriptors:
         - urn:alm:descriptor:com.tectonic.ui:text
         - urn:alm:descriptor:com.tectonic.ui:advanced
+      - description: Host is the hostname to use for Ingress/Route resources.
+        displayName: Host
+        path: applicationSet.webhookServer.host
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:text
+      - description: Enabled will toggle the creation of the Ingress.
+        displayName: Ingress Enabled'
+        path: applicationSet.webhookServer.ingress.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
+      - description: Enabled will toggle the creation of the OpenShift Route.
+        displayName: Route Enabled'
+        path: applicationSet.webhookServer.route.enabled
+        x-descriptors:
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus
+        - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server
+        - urn:alm:descriptor:com.tectonic.ui:booleanSwitch
       - description: ConfigManagementPlugins is used to specify additional config
           management plugins.
         displayName: Config Management Plugins'
diff --git a/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml b/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml
index 0e6e1618..cce444f9 100644
--- a/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml
+++ b/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml
@@ -81,6 +81,142 @@ spec:
                     description: Version is the Argo CD ApplicationSet image tag.
                       (optional)
                     type: string
+                  webhookServer:
+                    description: WebhookServerSpec defines the options for the ApplicationSet
+                      Webhook Server component.
+                    properties:
+                      host:
+                        description: Host is the hostname to use for Ingress/Route
+                          resources.
+                        type: string
+                      ingress:
+                        description: Ingress defines the desired state for an Ingress
+                          for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              apply to the Ingress.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the Ingress.
+                            type: boolean
+                          ingressClassName:
+                            description: IngressClassName for the Ingress resource.
+                            type: string
+                          path:
+                            description: Path used for the Ingress resource.
+                            type: string
+                          tls:
+                            description: TLS configuration. Currently the Ingress
+                              only supports a single TLS port, 443. If multiple members
+                              of this list specify different hosts, they will be multiplexed
+                              on the same port according to the hostname specified
+                              through the SNI TLS extension, if the ingress controller
+                              fulfilling the ingress supports SNI.
+                            items:
+                              description: IngressTLS describes the transport layer
+                                security associated with an Ingress.
+                              properties:
+                                hosts:
+                                  description: Hosts are a list of hosts included
+                                    in the TLS certificate. The values in this list
+                                    must match the name/s used in the tlsSecret. Defaults
+                                    to the wildcard host setting for the loadbalancer
+                                    controller fulfilling this Ingress, if left unspecified.
+                                  items:
+                                    type: string
+                                  type: array
+                                  x-kubernetes-list-type: atomic
+                                secretName:
+                                  description: SecretName is the name of the secret
+                                    used to terminate TLS traffic on port 443. Field
+                                    is left optional to allow TLS routing based on
+                                    SNI hostname alone. If the SNI host in a listener
+                                    conflicts with the "Host" header field used by
+                                    an IngressRule, the SNI host is used for termination
+                                    and value of the Host header is used for routing.
+                                  type: string
+                              type: object
+                            type: array
+                        required:
+                        - enabled
+                        type: object
+                      route:
+                        description: Route defines the desired state for an OpenShift
+                          Route for the Application set webhook component.
+                        properties:
+                          annotations:
+                            additionalProperties:
+                              type: string
+                            description: Annotations is the map of annotations to
+                              use for the Route resource.
+                            type: object
+                          enabled:
+                            description: Enabled will toggle the creation of the OpenShift
+                              Route.
+                            type: boolean
+                          labels:
+                            additionalProperties:
+                              type: string
+                            description: Labels is the map of labels to use for the
+                              Route resource
+                            type: object
+                          path:
+                            description: Path the router watches for, to route traffic
+                              for to the service.
+                            type: string
+                          tls:
+                            description: TLS provides the ability to configure certificates
+                              and termination for the Route.
+                            properties:
+                              caCertificate:
+                                description: caCertificate provides the cert authority
+                                  certificate contents
+                                type: string
+                              certificate:
+                                description: certificate provides certificate contents
+                                type: string
+                              destinationCACertificate:
+                                description: destinationCACertificate provides the
+                                  contents of the ca certificate of the final destination.  When
+                                  using reencrypt termination this file should be
+                                  provided in order to have routers use it for health
+                                  checks on the secure connection. If this field is
+                                  not specified, the router may provide its own destination
+                                  CA and perform hostname validation using the short
+                                  service name (service.namespace.svc), which allows
+                                  infrastructure generated certificates to automatically
+                                  verify.
+                                type: string
+                              insecureEdgeTerminationPolicy:
+                                description: "insecureEdgeTerminationPolicy indicates
+                                  the desired behavior for insecure connections to
+                                  a route. While each router may make its own decisions
+                                  on which ports to expose, this is normally port
+                                  80. \n * Allow - traffic is sent to the server on
+                                  the insecure port (default) * Disable - no traffic
+                                  is allowed on the insecure port. * Redirect - clients
+                                  are redirected to the secure port."
+                                type: string
+                              key:
+                                description: key provides key file contents
+                                type: string
+                              termination:
+                                description: termination indicates termination type.
+                                type: string
+                            required:
+                            - termination
+                            type: object
+                          wildcardPolicy:
+                            description: WildcardPolicy if any for the route. Currently
+                              only 'Subdomain' or 'None' is allowed.
+                            type: string
+                        required:
+                        - enabled
+                        type: object
+                    type: object
                 type: object
               banner:
                 description: Banner defines an additional banner to be displayed in
@@ -917,6 +1053,11 @@ spec:
                       are in the form:   g, subject, inherited-subject See https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/rbac.md
                       for additional information.'
                     type: string
+                  policyMatcherMode:
+                    description: PolicyMatcherMode configures the matchers function
+                      mode for casbin. There are two options for this, 'glob' for
+                      glob matcher or 'regex' for regex matcher.
+                    type: string
                   scopes:
                     description: 'Scopes controls which OIDC scopes to examine during
                       rbac enforcement (in addition to `sub` scope). If omitted, defaults
@@ -5767,6 +5908,10 @@ spec:
                               More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
                             type: object
                         type: object
+                      rootCA:
+                        description: Custom root CA certificate for communicating
+                          with the Keycloak OIDC provider
+                        type: string
                       verifyTLS:
                         description: VerifyTLS set to false disables strict TLS validation.
                         type: boolean
diff --git a/docs/reference/argocd.md b/docs/reference/argocd.md
index 79756256..e72874f6 100644
--- a/docs/reference/argocd.md
+++ b/docs/reference/argocd.md
@@ -644,6 +644,7 @@ Name | Default | Description
 --- | --- | ---
 Image | OpenShift - `registry.redhat.io/rh-sso-7/sso75-openshift-rhel8` <br/> Kuberentes - `quay.io/keycloak/keycloak` | The container image for keycloak. This overrides the `ARGOCD_KEYCLOAK_IMAGE` environment variable.
 Resources | `Requests`: CPU=500m, Mem=512Mi, `Limits`: CPU=1000m, Mem=1024Mi | The container compute resources.
+RootCA | "" | root CA certificate for communicating with the OIDC provider
 VerifyTLS | true | Whether to enforce strict TLS checking when communicating with Keycloak service.
 Version | OpenShift - `sha256:720a7e4c4926c41c1219a90daaea3b971a3d0da5a152a96fed4fb544d80f52e3` (7.5.1) <br/> Kubernetes - `sha256:64fb81886fde61dee55091e6033481fa5ccdac62ae30a4fd29b54eb5e97df6a9` (15.0.2) | The tag to use with the keycloak container image.
 
@@ -842,6 +843,7 @@ Name | Default | Description
 --- | --- | ---
 DefaultPolicy | `role:readonly` | The `policy.default` property in the `argocd-rbac-cm` ConfigMap. The name of the default role which Argo CD will falls back to, when authorizing API requests.
 Policy | [Empty] | The `policy.csv` property in the `argocd-rbac-cm` ConfigMap. CSV data containing user-defined RBAC policies and role definitions.
+PolicyMatcherMode | `glob` | The `policy.matchMode` property in the `argocd-rbac-cm` ConfigMap. There are two options for this, 'glob' for glob matcher and 'regex' for regex matcher.
 Scopes | `[groups]` | The `scopes` property in the `argocd-rbac-cm` ConfigMap.  Controls which OIDC scopes to examine during rbac enforcement (in addition to `sub` scope).
 
 ### RBAC Example
@@ -858,6 +860,7 @@ metadata:
 spec:
   rbac:
     defaultPolicy: 'role:readonly'
+    policyMatcherMode: 'glob'
     policy: |
       g, system:cluster-admins, role:admin
     scopes: '[groups]'
diff --git a/docs/usage/keycloak/kubernetes.md b/docs/usage/keycloak/kubernetes.md
index 437f2574..bce07ae0 100644
--- a/docs/usage/keycloak/kubernetes.md
+++ b/docs/usage/keycloak/kubernetes.md
@@ -45,6 +45,35 @@ spec:
     insecure: true
 ```
 
+If your keycloak is setup with a certificate which is not signed by one of the well known certificate authorities you can provide a custom certificate which will be used in verifying the Keycloak's TLS certificate when communicating with it.
+Add the rootCA to your Argo CD custom resource `.spec.keycloak.rootCA` field. The operator reconciles to this change and updates the `oidc.config` in `argocd-cm` configmap with the PEM encoded root certificate.
+
+!!! note
+    Argo CD server pod should be restarted after updating the `.spec.keycloak.rootCA`.
+
+Please refer to the below example:
+
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: ArgoCD
+metadata:
+  name: example-argocd
+  labels:
+    example: basic
+spec:
+  sso:
+    provider: keycloak
+    keycloak:
+     rootCA: |
+       ---- BEGIN CERTIFICATE ----
+       This is a dummy certificate
+       Please place this section with appropriate rootCA
+       ---- END CERTIFICATE ----
+  server:
+    ingress:
+      enabled: true
+```
+
 !!! warning
     `.spec.sso.Image`, `.spec.sso.Version`, `.spec.sso.Resources` and `.spec.sso.verifyTLS` are deprecated and support will be removed in Argo CD operator v0.6.0. Please use equivalent fields under `.spec.sso.keycloak` to configure your keycloak instance.
 
diff --git a/docs/usage/keycloak/openshift.md b/docs/usage/keycloak/openshift.md
index 72023ba9..e3294671 100644
--- a/docs/usage/keycloak/openshift.md
+++ b/docs/usage/keycloak/openshift.md
@@ -19,6 +19,35 @@ spec:
      enabled: true
 ```
 
+If your keycloak is setup with a certificate which is not signed by one of the well known certificate authorities you can provide a custom certificate which will be used in verifying the Keycloak's TLS certificate when communicating with it.
+Add the rootCA to your Argo CD custom resource `.spec.keycloak.rootCA` field. The operator reconciles to this change and updates the `oidc.config` in `argocd-cm` configmap with the PEM encoded root certificate.
+
+!!! note
+    Argo CD server pod should be restarted after updating the `.spec.keycloak.rootCA`.
+
+Please refer to the below example:
+
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: ArgoCD
+metadata:
+  name: example-argocd
+  labels:
+    example: basic
+spec:
+  sso:
+    provider: keycloak
+    keycloak:
+     rootCA: |
+       ---- BEGIN CERTIFICATE ----
+       This is a dummy certificate
+       Please place this section with appropriate rootCA
+       ---- END CERTIFICATE ----
+  server:
+    route:
+      enabled: true
+```
+
 ## Create
 
 Create a new Argo CD Instance in the `argocd` namespace using the provided example.
diff --git a/examples/argocd-keycloak-k8s.yaml b/examples/argocd-keycloak-k8s.yaml
index 9c7492f3..94f03453 100644
--- a/examples/argocd-keycloak-k8s.yaml
+++ b/examples/argocd-keycloak-k8s.yaml
@@ -7,8 +7,9 @@ metadata:
 spec:
   sso:
     provider: keycloak
-    verifyTLS: false
+    keycloak:
+     verifyTLS: false
   server:
     ingress:
       enabled: true
-    insecure: true
\ No newline at end of file
+    insecure: true
diff --git a/go.mod b/go.mod
index 08df0655..914769cb 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/argoproj/argo-cd/v2 v2.4.7
 	github.com/coreos/prometheus-operator v0.40.0
 	github.com/go-logr/logr v1.2.3
-	github.com/google/go-cmp v0.5.7
+	github.com/google/go-cmp v0.5.8
 	github.com/json-iterator/go v1.1.12
 	github.com/keycloak/keycloak-operator v0.0.0-20220104081708-ab17327be9e1
 	github.com/onsi/ginkgo v1.16.5
@@ -17,7 +17,7 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/sethvargo/go-password v0.2.0
 	github.com/stretchr/testify v1.7.1
-	golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4
+	golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3
 	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/api v0.23.3
 	k8s.io/apimachinery v0.23.3
diff --git a/go.sum b/go.sum
index 1c0d0dad..6e577223 100644
--- a/go.sum
+++ b/go.sum
@@ -569,8 +569,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -1337,8 +1337,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4 h1:7Qds88gNaRx0Dz/1wOwXlR7asekh1B1u26wEwN6FcEI=
-golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1669,7 +1669,6 @@ golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpd
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
 gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=


Test patch:
diff --git a/controllers/argocd/applicationset_test.go b/controllers/argocd/applicationset_test.go
index 9b1e9d12..b99bba01 100644
--- a/controllers/argocd/applicationset_test.go
+++ b/controllers/argocd/applicationset_test.go
@@ -486,3 +486,14 @@ func setProxyEnvVars(t *testing.T) {
 	t.Setenv("HTTP_PROXY", "http://example.com")
 	t.Setenv("NO_PROXY", ".cluster.local")
 }
+
+func TestReconcileApplicationSet_Service(t *testing.T) {
+	logf.SetLogger(ZapLogger(true))
+	a := makeTestArgoCD()
+	r := makeTestReconciler(t, a)
+
+	s := newServiceWithSuffix(common.ApplicationSetServiceNameSuffix, common.ApplicationSetServiceNameSuffix, a)
+
+	assert.NoError(t, r.reconcileApplicationSetService(a))
+	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Namespace: s.Namespace, Name: s.Name}, s))
+}
diff --git a/controllers/argocd/configmap_test.go b/controllers/argocd/configmap_test.go
index 3ad05e0b..996b01df 100644
--- a/controllers/argocd/configmap_test.go
+++ b/controllers/argocd/configmap_test.go
@@ -869,3 +869,27 @@ func TestReconcileArgoCD_reconcileArgoConfigMap_withExtraConfig(t *testing.T) {
 	assert.Equal(t, cm.Data["admin.enabled"], "false")
 
 }
+
+func Test_reconcileRBAC(t *testing.T) {
+	a := makeTestArgoCD()
+	r := makeTestReconciler(t, a)
+
+	err := r.reconcileRBAC(a)
+	assert.NoError(t, err)
+
+	// Verify ArgoCD CR can be used to configure the RBAC policy matcher mode.\
+	matcherMode := "regex"
+	a.Spec.RBAC.PolicyMatcherMode = &matcherMode
+
+	err = r.reconcileRBAC(a)
+	assert.NoError(t, err)
+
+	cm := &corev1.ConfigMap{}
+	err = r.Client.Get(context.TODO(), types.NamespacedName{
+		Name:      common.ArgoCDRBACConfigMapName,
+		Namespace: testNamespace,
+	}, cm)
+
+	assert.NoError(t, err)
+	assert.Equal(t, cm.Data["policy.matchMode"], matcherMode)
+}
diff --git a/controllers/argocd/deployment_test.go b/controllers/argocd/deployment_test.go
index 94f876b0..f518369d 100644
--- a/controllers/argocd/deployment_test.go
+++ b/controllers/argocd/deployment_test.go
@@ -743,7 +743,10 @@ func TestReconcileArgoCD_reconcileDeployment_nodePlacement(t *testing.T) {
 	}, deployment)
 	assert.NoError(t, err)
 
-	if diff := cmp.Diff(deploymentDefaultNodeSelector(), deployment.Spec.Template.Spec.NodeSelector); diff != "" {
+	nSelectors := deploymentDefaultNodeSelector()
+	nSelectors = argoutil.AppendStringMap(nSelectors, common.DefaultNodeSelector())
+
+	if diff := cmp.Diff(nSelectors, deployment.Spec.Template.Spec.NodeSelector); diff != "" {
 		t.Fatalf("reconcileDeployment failed:\n%s", diff)
 	}
 	if diff := cmp.Diff(deploymentDefaultTolerations(), deployment.Spec.Template.Spec.Tolerations); diff != "" {
@@ -906,6 +909,7 @@ func TestReconcileArgoCD_reconcileServerDeployment(t *testing.T) {
 		},
 		Volumes:            serverDefaultVolumes(),
 		ServiceAccountName: "argocd-argocd-server",
+		NodeSelector:       common.DefaultNodeSelector(),
 	}
 
 	assert.Equal(t, want, deployment.Spec.Template.Spec)
@@ -1120,6 +1124,7 @@ func TestReconcileArgoCD_reconcileServerDeploymentWithInsecure(t *testing.T) {
 		},
 		Volumes:            serverDefaultVolumes(),
 		ServiceAccountName: "argocd-argocd-server",
+		NodeSelector:       common.DefaultNodeSelector(),
 	}
 
 	assert.Equal(t, want, deployment.Spec.Template.Spec)
@@ -1205,6 +1210,7 @@ func TestReconcileArgoCD_reconcileServerDeploymentChangedToInsecure(t *testing.T
 		},
 		Volumes:            serverDefaultVolumes(),
 		ServiceAccountName: "argocd-argocd-server",
+		NodeSelector:       common.DefaultNodeSelector(),
 	}
 
 	assert.Equal(t, want, deployment.Spec.Template.Spec)
diff --git a/controllers/argocd/dex_test.go b/controllers/argocd/dex_test.go
index c143ed51..7107bcb1 100644
--- a/controllers/argocd/dex_test.go
+++ b/controllers/argocd/dex_test.go
@@ -424,6 +424,7 @@ func TestReconcileArgoCD_reconcileDexDeployment(t *testing.T) {
 			},
 		},
 		ServiceAccountName: "argocd-argocd-dex-server",
+		NodeSelector:       common.DefaultNodeSelector(),
 	}
 	assert.Equal(t, want, deployment.Spec.Template.Spec)
 }
@@ -514,6 +515,7 @@ func TestReconcileArgoCD_reconcileDexDeployment_withUpdate(t *testing.T) {
 			},
 		},
 		ServiceAccountName: "argocd-argocd-dex-server",
+		NodeSelector:       common.DefaultNodeSelector(),
 	}
 
 	tests := []struct {
diff --git a/controllers/argocd/ingress_test.go b/controllers/argocd/ingress_test.go
index 9141ce33..bc32ca75 100644
--- a/controllers/argocd/ingress_test.go
+++ b/controllers/argocd/ingress_test.go
@@ -4,13 +4,13 @@ import (
 	"context"
 	"testing"
 
+	"github.com/argoproj-labs/argocd-operator/api/v1alpha1"
+	argoprojv1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
+	"github.com/argoproj-labs/argocd-operator/common"
+	"github.com/stretchr/testify/assert"
 	networkingv1 "k8s.io/api/networking/v1"
 	"k8s.io/apimachinery/pkg/types"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
-
-	"github.com/stretchr/testify/assert"
-
-	argoprojv1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
 )
 
 func TestReconcileArgoCD_reconcile_ServerIngress_ingressClassName(t *testing.T) {
@@ -182,3 +182,20 @@ func TestReconcileArgoCD_reconcile_PrometheusIngress_ingressClassName(t *testing
 		})
 	}
 }
+
+func TestReconcileApplicationSetService_Ingress(t *testing.T) {
+	logf.SetLogger(ZapLogger(true))
+	a := makeTestArgoCD()
+	obj := v1alpha1.ArgoCDApplicationSet{
+		WebhookServer: v1alpha1.WebhookServerSpec{
+			Ingress: v1alpha1.ArgoCDIngressSpec{
+				Enabled: true,
+			},
+		},
+	}
+	a.Spec.ApplicationSet = &obj
+	r := makeTestReconciler(t, a)
+	ingress := newIngressWithSuffix(common.ApplicationSetServiceNameSuffix, a)
+	assert.NoError(t, r.reconcileApplicationSetControllerIngress(a))
+	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Namespace: ingress.Namespace, Name: ingress.Name}, ingress))
+}
diff --git a/controllers/argocd/keycloak_test.go b/controllers/argocd/keycloak_test.go
index b93d56e9..06afdd2c 100644
--- a/controllers/argocd/keycloak_test.go
+++ b/controllers/argocd/keycloak_test.go
@@ -30,6 +30,7 @@ import (
 	argoappv1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
 	argoprojv1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
 	"github.com/argoproj-labs/argocd-operator/common"
+	"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
 )
 
 var (
@@ -521,7 +522,10 @@ func TestKeycloak_NodeLabelSelector(t *testing.T) {
 	}
 
 	dc := getKeycloakDeploymentConfigTemplate(a)
-	assert.Equal(t, dc.Spec.Template.Spec.NodeSelector, a.Spec.NodePlacement.NodeSelector)
+
+	nSelectors := deploymentDefaultNodeSelector()
+	nSelectors = argoutil.AppendStringMap(nSelectors, common.DefaultNodeSelector())
+	assert.Equal(t, dc.Spec.Template.Spec.NodeSelector, nSelectors)
 	assert.Equal(t, dc.Spec.Template.Spec.Tolerations, a.Spec.NodePlacement.Tolerations)
 }
 
diff --git a/controllers/argocd/role_test.go b/controllers/argocd/role_test.go
index d74777ff..1f012965 100644
--- a/controllers/argocd/role_test.go
+++ b/controllers/argocd/role_test.go
@@ -7,8 +7,10 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	corev1 "k8s.io/api/core/v1"
 	v1 "k8s.io/api/rbac/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 
@@ -175,3 +177,69 @@ func TestReconcileArgoCD_reconcileRole_custom_role(t *testing.T) {
 		t.Fatal(err)
 	}
 }
+
+// This test validates the behavior of the operator reconciliation when a managed namespace is not properly terminated
+// or remains terminating may be because of some resources in the namespace not getting deleted.
+func TestReconcileRoles_ManagedTerminatingNamespace(t *testing.T) {
+
+	a := makeTestArgoCD()
+	r := makeTestReconciler(t, a)
+	assert.NoError(t, createNamespace(r, a.Namespace, ""))
+
+	// Create a managed namespace
+	assert.NoError(t, createNamespace(r, "managedNS", a.Namespace))
+
+	workloadIdentifier := "x"
+	expectedRules := policyRuleForApplicationController()
+	_, err := r.reconcileRole(workloadIdentifier, expectedRules, a)
+	assert.NoError(t, err)
+
+	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
+	reconciledRole := &v1.Role{}
+	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole))
+	assert.Equal(t, expectedRules, reconciledRole.Rules)
+
+	// Check if roles are created for the new namespace as well
+	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS"}, reconciledRole))
+	assert.Equal(t, expectedRules, reconciledRole.Rules)
+
+	// Create a configmap with an invalid finalizer in the "managedNS".
+	configMap := &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "dummy",
+			Namespace: "managedNS",
+			Finalizers: []string{
+				"nonexistent.finalizer/dummy",
+			},
+		},
+	}
+	assert.NoError(t, r.Client.Create(
+		context.TODO(), configMap))
+
+	// Delete the newNamespaceTest ns.
+	// Verify that operator should not reconcile back to create the roles in terminating ns.
+	newNS := &corev1.Namespace{}
+	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
+	r.Client.Delete(context.TODO(), newNS)
+
+	// Verify that the namespace exists and is in terminating state.
+	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
+	assert.NotEqual(t, newNS.DeletionTimestamp, nil)
+
+	_, err = r.reconcileRole(workloadIdentifier, expectedRules, a)
+	assert.NoError(t, err)
+
+	// Verify that the roles are deleted
+	err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, reconciledRole)
+	assert.ErrorContains(t, err, "not found")
+
+	// Create another managed namespace
+	assert.NoError(t, createNamespace(r, "managedNS2", a.Namespace))
+
+	// Check if roles are created for the new namespace as well
+	_, err = r.reconcileRole(workloadIdentifier, expectedRules, a)
+	assert.NoError(t, err)
+
+	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, reconciledRole))
+	assert.Equal(t, expectedRules, reconciledRole.Rules)
+}
diff --git a/controllers/argocd/rolebinding_test.go b/controllers/argocd/rolebinding_test.go
index f3c5cb44..27877aa2 100644
--- a/controllers/argocd/rolebinding_test.go
+++ b/controllers/argocd/rolebinding_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -67,6 +68,62 @@ func TestReconcileArgoCD_reconcileRoleBinding_for_new_namespace(t *testing.T) {
 	assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding))
 }
 
+// This test validates the behavior of the operator reconciliation when a managed namespace is not properly terminated
+// or remains terminating may be because of some resources in the namespace not getting deleted.
+func TestReconcileRoleBinding_for_Managed_Teminating_Namespace(t *testing.T) {
+	a := makeTestArgoCD()
+	r := makeTestReconciler(t, a)
+
+	assert.NoError(t, createNamespace(r, a.Namespace, ""))
+	assert.NoError(t, createNamespace(r, "managedNS", a.Namespace))
+
+	// Verify role bindings are created for the new namespace with managed-by label
+	roleBinding := &rbacv1.RoleBinding{}
+	workloadIdentifier := common.ArgoCDApplicationControllerComponent
+	expectedRules := policyRuleForApplicationController()
+	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
+	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedRules, a))
+	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS"}, roleBinding))
+
+	// Create a configmap with an invalid finalizer in the "managedNS".
+	configMap := &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "dummy",
+			Namespace: "managedNS",
+			Finalizers: []string{
+				"nonexistent.finalizer/dummy",
+			},
+		},
+	}
+	assert.NoError(t, r.Client.Create(
+		context.TODO(), configMap))
+
+	// Delete the newNamespaceTest ns.
+	// Verify that operator should not reconcile back to create the roleBindings in terminating ns.
+	newNS := &corev1.Namespace{}
+	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
+	r.Client.Delete(context.TODO(), newNS)
+
+	// Verify that the namespace exists and is in terminating state.
+	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
+	assert.NotEqual(t, newNS.DeletionTimestamp, nil)
+
+	err := r.reconcileRoleBinding(workloadIdentifier, expectedRules, a)
+	assert.NoError(t, err)
+
+	// Verify that the role bindings are deleted
+	err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, roleBinding)
+	assert.ErrorContains(t, err, "not found")
+
+	// Create another managed namespace
+	assert.NoError(t, createNamespace(r, "managedNS2", a.Namespace))
+
+	// Check if roleBindings are created for the new namespace as well
+	err = r.reconcileRoleBinding(workloadIdentifier, expectedRules, a)
+	assert.NoError(t, err)
+	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, roleBinding))
+}
+
 func TestReconcileArgoCD_reconcileClusterRoleBinding(t *testing.T) {
 	logf.SetLogger(ZapLogger(true))
 	a := makeTestArgoCD()
diff --git a/kuttl-test-keycloak.yaml b/kuttl-test-keycloak.yaml
deleted file mode 100644
index 4b0cf1ee..00000000
--- a/kuttl-test-keycloak.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: kuttl.dev/v1beta1
-kind: TestSuite
-testDirs:
-- ./tests/e2e-keycloak/
-timeout: 90
-startKIND: false
-namespace: argocd-e2e-keycloak
-commands:
-- command: kubectl create ns argocd-e2e-keycloak
-  ignoreFailure: true
\ No newline at end of file
diff --git a/kuttl-test-rhsso.yaml b/kuttl-test-rhsso.yaml
deleted file mode 100644
index 0ca638ab..00000000
--- a/kuttl-test-rhsso.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-apiVersion: kuttl.dev/v1beta1
-kind: TestSuite
-testDirs:
-- ./tests/e2e-rhsso/
-timeout: 180
-startKIND: false
-namespace: argocd-e2e-rhsso
-commands:
-- command: kubectl create ns argocd-e2e-rhsso
-  ignoreFailure: true
-
diff --git a/tests/k8s/1-001_validate_basic/05-applicationset.yaml b/tests/k8s/1-001_validate_basic/05-applicationset.yaml
new file mode 100644
index 00000000..f6582332
--- /dev/null
+++ b/tests/k8s/1-001_validate_basic/05-applicationset.yaml
@@ -0,0 +1,20 @@
+# Delete previous cluster
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+delete:
+- apiVersion: argoproj.io/v1alpha1
+  kind: ArgoCD
+  name: example-argocd
+commands:
+  # Sleep to allow resources to be completely deleted
+  - command: sleep 30s
+---
+apiVersion: argoproj.io/v1alpha1
+kind: ArgoCD
+metadata:
+  name: example-argocd
+spec:
+  applicationSet: 
+    webhookServer:
+     ingress:
+       enabled: true
\ No newline at end of file
diff --git a/tests/k8s/1-001_validate_basic/05-assert.yaml b/tests/k8s/1-001_validate_basic/05-assert.yaml
new file mode 100644
index 00000000..871040c6
--- /dev/null
+++ b/tests/k8s/1-001_validate_basic/05-assert.yaml
@@ -0,0 +1,42 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestAssert
+timeout: 720
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-applicationset-controller
+status:
+  readyReplicas: 1
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: example-argocd-applicationset-controller
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: example-argocd-applicationset-controller
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: example-argocd-applicationset-controller
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: example-argocd-applicationset-controller
+spec:
+  rules:
+  - host: example-argocd
+    http:
+      paths:
+      - backend:
+          service:
+            name: example-argocd-applicationset-controller
+            port:
+              name: webhook
+        path: /api/webhook
+        pathType: ImplementationSpecific
diff --git a/tests/k8s/1-001_validate_basic/06-disable-applicationset-ingress.yaml b/tests/k8s/1-001_validate_basic/06-disable-applicationset-ingress.yaml
new file mode 100644
index 00000000..a8685cc5
--- /dev/null
+++ b/tests/k8s/1-001_validate_basic/06-disable-applicationset-ingress.yaml
@@ -0,0 +1,7 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+commands:
+# patches to disable applicationset
+- script: |
+    kubectl patch --type=merge -n $NAMESPACE argocd/example-argocd --patch '{"spec":{"applicationSet":{"webhookServer":{"ingress":{"enabled":false}}}}}'
+- script: sleep 10
\ No newline at end of file
diff --git a/tests/k8s/1-001_validate_basic/06-errors.yaml b/tests/k8s/1-001_validate_basic/06-errors.yaml
new file mode 100644
index 00000000..923a2e82
--- /dev/null
+++ b/tests/k8s/1-001_validate_basic/06-errors.yaml
@@ -0,0 +1,4 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: example-argocd-applicationset-controller
\ No newline at end of file
diff --git a/tests/k8s/1-023_validate_node_placement/01-assert.yaml b/tests/k8s/1-023_validate_node_placement/01-assert.yaml
new file mode 100644
index 00000000..dff34170
--- /dev/null
+++ b/tests/k8s/1-023_validate_node_placement/01-assert.yaml
@@ -0,0 +1,43 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestAssert
+timeout: 120
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: example-argocd-application-controller
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux 
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-redis
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-repo-server
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-server
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
\ No newline at end of file
diff --git a/tests/k8s/1-023_validate_node_placement/01-default-node-selector.yaml b/tests/k8s/1-023_validate_node_placement/01-default-node-selector.yaml
new file mode 100644
index 00000000..2ccddd82
--- /dev/null
+++ b/tests/k8s/1-023_validate_node_placement/01-default-node-selector.yaml
@@ -0,0 +1,7 @@
+apiVersion: argoproj.io/v1alpha1
+kind: ArgoCD
+metadata:
+  name: example-argocd
+  labels:
+    example: basic
+spec: {}
\ No newline at end of file
diff --git a/tests/k8s/1-023_validate_node_placement/02-assert.yaml b/tests/k8s/1-023_validate_node_placement/02-assert.yaml
new file mode 100644
index 00000000..39759aad
--- /dev/null
+++ b/tests/k8s/1-023_validate_node_placement/02-assert.yaml
@@ -0,0 +1,43 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: example-argocd-application-controller
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux 
+        key1: value1
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-redis
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+        key1: value1
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-repo-server
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+        key1: value1
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-server
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+        key1: value1
\ No newline at end of file
diff --git a/tests/k8s/1-023_validate_node_placement/02-custom-node-placement.yaml b/tests/k8s/1-023_validate_node_placement/02-custom-node-placement.yaml
new file mode 100644
index 00000000..67a5ad9f
--- /dev/null
+++ b/tests/k8s/1-023_validate_node_placement/02-custom-node-placement.yaml
@@ -0,0 +1,10 @@
+apiVersion: argoproj.io/v1alpha1
+kind: ArgoCD
+metadata:
+  name: example-argocd
+  labels:
+    example: basic
+spec:
+  nodePlacement:
+    nodeSelector:
+      key1: value1
\ No newline at end of file
diff --git a/tests/k8s/1-023_validate_node_placement/03-assert.yaml b/tests/k8s/1-023_validate_node_placement/03-assert.yaml
new file mode 100644
index 00000000..c41d1b1f
--- /dev/null
+++ b/tests/k8s/1-023_validate_node_placement/03-assert.yaml
@@ -0,0 +1,64 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: example-argocd-application-controller
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux 
+        key1: value1
+      tolerations: 
+      - key: key1
+        operator: Equal
+        value: value1
+        effect: NoSchedule  
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-redis
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+        key1: value1
+      tolerations: 
+      - key: key1
+        operator: Equal
+        value: value1
+        effect: NoSchedule  
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-repo-server
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+        key1: value1
+      tolerations: 
+      - key: key1
+        operator: Equal
+        value: value1
+        effect: NoSchedule  
+        
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-argocd-server
+spec:
+  template:
+    spec:
+      nodeSelector: 
+        kubernetes.io/os: linux
+        key1: value1
+      tolerations: 
+      - key: key1
+        operator: Equal
+        value: value1
+        effect: NoSchedule  
\ No newline at end of file
diff --git a/tests/k8s/1-023_validate_node_placement/03-node-placement-with-toleration.yaml b/tests/k8s/1-023_validate_node_placement/03-node-placement-with-toleration.yaml
new file mode 100644
index 00000000..d3e895cd
--- /dev/null
+++ b/tests/k8s/1-023_validate_node_placement/03-node-placement-with-toleration.yaml
@@ -0,0 +1,16 @@
+apiVersion: argoproj.io/v1alpha1
+kind: ArgoCD
+metadata:
+  name: example-argocd
+  labels:
+    example: basic
+spec:
+  nodePlacement:
+    nodeSelector:
+      key1: value1
+    tolerations: 
+    - key: key1
+      operator: Equal
+      value: value1
+      effect: NoSchedule
+         
\ No newline at end of file
diff --git a/tests/ocp/1-001_validate_rhsso/01-argocd-rhsso.yaml b/tests/ocp/1-001_validate_rhsso/01-argocd-rhsso.yaml
index 012b1765..550a67cb 100644
--- a/tests/ocp/1-001_validate_rhsso/01-argocd-rhsso.yaml
+++ b/tests/ocp/1-001_validate_rhsso/01-argocd-rhsso.yaml
@@ -7,7 +7,9 @@ metadata:
 spec:
   sso: 
     provider: keycloak
-    verifyTLS: false # required when running operator locally
+    keycloak:
+      verifyTLS: false # required when running operator locally
+      rootCA: "---BEGIN---END---"
   server:
     route: 
       enabled: true
\ No newline at end of file
diff --git a/tests/ocp/1-001_validate_rhsso/03-verify-oidc.yaml b/tests/ocp/1-001_validate_rhsso/03-verify-oidc.yaml
index 8a390ace..cd8a5d9e 100644
--- a/tests/ocp/1-001_validate_rhsso/03-verify-oidc.yaml
+++ b/tests/ocp/1-001_validate_rhsso/03-verify-oidc.yaml
@@ -4,8 +4,8 @@ commands:
 # verify OIDC config
 # verify issuer
 - script: |
-    issuer=$(kubectl get configmap argocd-cm -o jsonpath='{.data.oidc\.config}' -n argocd-e2e-rhsso | grep issuer | awk -F' ' '{print $2}')
-    keycloakRoute=$(kubectl get route keycloak -n argocd-e2e-rhsso -o jsonpath='{.spec.host}')
+    issuer=$(kubectl get configmap argocd-cm -o jsonpath='{.data.oidc\.config}' -n $NAMESPACE | grep issuer | awk -F' ' '{print $2}')
+    keycloakRoute=$(kubectl get route keycloak -n $NAMESPACE -o jsonpath='{.spec.host}')
     if [[ "${issuer}" == "https://${keycloakRoute}/auth/realms/argocd" ]]; then 
       echo "issuer matched"
     else 
@@ -13,15 +13,16 @@ commands:
       echo "${issuer} not equals ${keycloakRoute}/auth/realms/argocd"
       exit 1
     fi  
-# verify oidc config name and clientid
+# verify oidc config name, clientid
 - script: | 
-    clientid=$(kubectl get configmap argocd-cm -o jsonpath='{.data.oidc\.config}' -n argocd-e2e-rhsso | grep clientid | awk -F' ' '{print $2}')
-    name=$(kubectl get configmap argocd-cm -o jsonpath='{.data.oidc\.config}' -n argocd-e2e-rhsso | grep name | awk -F' ' '{print $2}')
-    
-    if [[ "${clientid}" == "argocd" && "${name}" == "Keycloak" ]]; then 
-      echo "oidc name and clientid matched"
+    clientid=$(kubectl get configmap argocd-cm -o jsonpath='{.data.oidc\.config}' -n $NAMESPACE | grep clientid | awk -F' ' '{print $2}')
+    name=$(kubectl get configmap argocd-cm -o jsonpath='{.data.oidc\.config}' -n $NAMESPACE | grep name | awk -F' ' '{print $2}')
+    rootCA=$(kubectl get configmap argocd-cm -o jsonpath='{.data.oidc\.config}' -n $NAMESPACE | grep rootca | awk -F' ' '{print $2}')
+
+    if [[ "${clientid}" == "argocd" && "${name}" == "Keycloak" && "${rootCA}" == "'---BEGIN---END---'"  ]]; then 
+      echo "oidc name, clientid and rootCA matched"
     else 
-      echo "oidc name and clientid mismatched"
-      echo "${clientid} and ${name}"
+      echo "oidc name, clientid or rootCA mismatched"
+      echo "${clientid}, ${name} and ${rootCA}"
       exit 1
     fi
\ No newline at end of file
diff --git a/tests/ocp/1-001_validate_rhsso/04-verifyRealmCreation.yaml b/tests/ocp/1-001_validate_rhsso/04-verifyRealmCreation.yaml
index 7b7c0ac0..432daf15 100644
--- a/tests/ocp/1-001_validate_rhsso/04-verifyRealmCreation.yaml
+++ b/tests/ocp/1-001_validate_rhsso/04-verifyRealmCreation.yaml
@@ -7,10 +7,10 @@ kind: TestStep
 commands:
 - script: |
     # Set the needed parameter for the authorization
-    KEYCLOAK_URL=$(oc get route keycloak -n argocd-e2e-rhsso -o jsonpath='{.spec.host}')
+    KEYCLOAK_URL=$(oc get route keycloak -n $NAMESPACE -o jsonpath='{.spec.host}')
     tenant=argocd
-    USER=$(oc get secret keycloak-secret -n argocd-e2e-rhsso -o jsonpath='{.data.SSO_USERNAME}' | base64 --decode)
-    PASSWORD=$(oc get secret keycloak-secret -n argocd-e2e-rhsso -o jsonpath='{.data.SSO_PASSWORD}' | base64 --decode)
+    USER=$(oc get secret keycloak-secret -n $NAMESPACE -o jsonpath='{.data.SSO_USERNAME}' | base64 --decode)
+    PASSWORD=$(oc get secret keycloak-secret -n $NAMESPACE -o jsonpath='{.data.SSO_PASSWORD}' | base64 --decode)
     GRANT_TYPE=password
     CLIENT_ID=admin-cli
     
diff --git a/tests/ocp/1-001_validate_rhsso/99-delete.yaml b/tests/ocp/1-001_validate_rhsso/99-delete.yaml
index 6402602a..09d88eb3 100644
--- a/tests/ocp/1-001_validate_rhsso/99-delete.yaml
+++ b/tests/ocp/1-001_validate_rhsso/99-delete.yaml
@@ -1,5 +1,4 @@
 apiVersion: kuttl.dev/v1beta1
 kind: TestStep
 commands:
-  - command: oc delete argocd example-argocd-keycloak -n argocd-e2e-rhsso
-  - command: oc delete ns argocd-e2e-rhsso
\ No newline at end of file
+  - command: oc delete argocd example-argocd-keycloak -n $NAMESPACE
\ No newline at end of file