Repository: argoproj-labs/argocd-operator
Pull request #758 (2022-08-17 05:47:21)

chore: update OWNERS file
Signed-off-by: iam-veeramalla <abhishek.veeramalla@gmail.com>

**What type of PR is this?**
> /kind chore

**What does this PR do / why we need it**:
Updates the OWNERS file


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..c9ac7e14 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.
@@ -830,6 +832,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..35d5d89c 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.
@@ -996,3 +997,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..ff9379d6 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
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..03f2eddc 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
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/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/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/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..ff9379d6 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


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/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/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/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