Repository: kubernetes-sigs/aws-alb-ingress-controller
Pull request #1471 (2020-10-08 03:05:33)

add service/secret eventHandler for ingress
add service/secret eventHandler for ingress.

# how it works
The service eventHandler watches for Service changes and enqueue Ingress that using this service as backend.
The secret eventHandler watches for Secret changes and push event to IngressEventHandler or ServiceEventHandler when the secret is used as OIDC secret on ingress or service.
* when secret is used as OIDC secret on ingress, it will push a generic Ingress event to IngressEventHandler.
* when secret is used as OIDC secret on service, it will push a generic Service event to ServiceEventHandler, which in turn will push a generic Ingress event to IngressEventHandler.

# additional changes
1. adjust MaxConcurrentReconciles to 3 for all controllers.
2. adjust code styles

# test done
1. tested by trigger annotation changes on service and observed desired changes.
2. tested by trigger secret changes on secret used as OIDC secret by service, and observed desired changes.
3. tested by trigger secret changes on secret used as OIDC secret by ingress, and observed desired changes.

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/aws-alb-ingress-controller/pull/1471#" title="Author self-approved">M00nF1sh</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign  after the PR has been reviewed.
You can assign the PR to them by writing `/assign ` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Faws-alb-ingress-controller).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/aws-alb-ingress-controller/blob/v2_ga/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/Dockerfile b/Dockerfile
index 3320d9023..3aef80a17 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,6 +15,8 @@ ARG TARGETARCH
 RUN --mount=type=bind,target=. \
     --mount=type=cache,target=/root/.cache/go-build \
     GOOS=${TARGETOS} GOARCH=${TARGETARCH} GO111MODULE=on \
+    CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2" \
+    CGO_LDFLAGS="-Wl,-z,relro,-z,now" \
     go build -ldflags="-s -w" -buildmode=pie -a -o /out/controller main.go
 
 FROM amazonlinux:2 as bin-unix
@@ -25,4 +27,4 @@ ENTRYPOINT ["/controller"]
 FROM bin-unix AS bin-linux
 FROM bin-unix AS bin-darwin
 
-FROM bin-${TARGETOS} as bin
\ No newline at end of file
+FROM bin-${TARGETOS} as bin
diff --git a/config/controller/kustomization.yaml b/config/controller/kustomization.yaml
index 3a7ff14d1..344bbc004 100644
--- a/config/controller/kustomization.yaml
+++ b/config/controller/kustomization.yaml
@@ -2,6 +2,7 @@ resources:
 - controller.yaml
 patchesStrategicMerge:
 - iam_for_sa_patch.yaml
+- security_context_patch.yaml
 
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
diff --git a/config/controller/security_context_patch.yaml b/config/controller/security_context_patch.yaml
new file mode 100644
index 000000000..4017a6ed8
--- /dev/null
+++ b/config/controller/security_context_patch.yaml
@@ -0,0 +1,13 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: controller
+spec:
+  template:
+    spec:
+      containers:
+        - name: controller
+          securityContext:
+            readOnlyRootFilesystem: true
+            allowPrivilegeEscalation: false
+            runAsNonRoot: true
diff --git a/controllers/elbv2/eventhandlers/endpoints.go b/controllers/elbv2/eventhandlers/endpoints.go
index 8698be1ec..9809f3373 100644
--- a/controllers/elbv2/eventhandlers/endpoints.go
+++ b/controllers/elbv2/eventhandlers/endpoints.go
@@ -15,6 +15,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
+// NewEnqueueRequestsForEndpointsEvent constructs new enqueueRequestsForEndpointsEvent.
 func NewEnqueueRequestsForEndpointsEvent(k8sClient client.Client, logger logr.Logger) handler.EventHandler {
 	return &enqueueRequestsForEndpointsEvent{
 		k8sClient: k8sClient,
diff --git a/controllers/elbv2/eventhandlers/node.go b/controllers/elbv2/eventhandlers/node.go
index 45bfc1dd0..e68cba363 100644
--- a/controllers/elbv2/eventhandlers/node.go
+++ b/controllers/elbv2/eventhandlers/node.go
@@ -17,6 +17,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
+// NewEnqueueRequestsForNodeEvent constructs new enqueueRequestsForNodeEvent.
 func NewEnqueueRequestsForNodeEvent(k8sClient client.Client, logger logr.Logger) handler.EventHandler {
 	return &enqueueRequestsForNodeEvent{
 		k8sClient: k8sClient,
diff --git a/controllers/elbv2/targetgroupbinding_controller.go b/controllers/elbv2/targetgroupbinding_controller.go
index 2fe6ac405..376ae7607 100644
--- a/controllers/elbv2/targetgroupbinding_controller.go
+++ b/controllers/elbv2/targetgroupbinding_controller.go
@@ -38,23 +38,21 @@ const (
 )
 
 // NewTargetGroupBindingReconciler constructs new targetGroupBindingReconciler
-func NewTargetGroupBindingReconciler(k8sClient client.Client, k8sFieldIndexer client.FieldIndexer, finalizerManager k8s.FinalizerManager, tgbResourceManager targetgroupbinding.ResourceManager, log logr.Logger) *targetGroupBindingReconciler {
+func NewTargetGroupBindingReconciler(k8sClient client.Client, finalizerManager k8s.FinalizerManager, tgbResourceManager targetgroupbinding.ResourceManager, logger logr.Logger) *targetGroupBindingReconciler {
 	return &targetGroupBindingReconciler{
 		k8sClient:          k8sClient,
-		k8sFieldIndexer:    k8sFieldIndexer,
 		finalizerManager:   finalizerManager,
 		tgbResourceManager: tgbResourceManager,
-		log:                log,
+		logger:             logger,
 	}
 }
 
 // targetGroupBindingReconciler reconciles a TargetGroupBinding object
 type targetGroupBindingReconciler struct {
 	k8sClient          client.Client
-	k8sFieldIndexer    client.FieldIndexer
 	finalizerManager   k8s.FinalizerManager
 	tgbResourceManager targetgroupbinding.ResourceManager
-	log                logr.Logger
+	logger             logr.Logger
 }
 
 // +kubebuilder:rbac:groups=elbv2.k8s.aws,resources=targetgroupbindings,verbs=get;list;watch;update;patch;create;delete
@@ -68,25 +66,7 @@ type targetGroupBindingReconciler struct {
 // +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;update;patch;create;delete
 
 func (r *targetGroupBindingReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
-	return runtime.HandleReconcileError(r.reconcile(req), r.log)
-}
-
-func (r *targetGroupBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
-	r.k8sFieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
-		targetgroupbinding.IndexKeyServiceRefName, targetgroupbinding.IndexFuncServiceRefName)
-	r.k8sFieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
-		targetgroupbinding.IndexKeyTargetType, targetgroupbinding.IndexFuncTargetType)
-
-	epEventsHandler := eventhandlers.NewEnqueueRequestsForEndpointsEvent(r.k8sClient,
-		r.log.WithName("eventHandlers").WithName("endpoints"))
-	nodeEventsHandler := eventhandlers.NewEnqueueRequestsForNodeEvent(r.k8sClient,
-		r.log.WithName("eventHandlers").WithName("node"))
-	return ctrl.NewControllerManagedBy(mgr).
-		For(&elbv2api.TargetGroupBinding{}).
-		Watches(&source.Kind{Type: &corev1.Endpoints{}}, epEventsHandler).
-		Watches(&source.Kind{Type: &corev1.Node{}}, nodeEventsHandler).
-		WithOptions(controller.Options{MaxConcurrentReconciles: 3}).
-		Complete(r)
+	return runtime.HandleReconcileError(r.reconcile(req), r.logger)
 }
 
 func (r *targetGroupBindingReconciler) reconcile(req ctrl.Request) error {
@@ -123,3 +103,32 @@ func (r *targetGroupBindingReconciler) cleanupTargetGroupBinding(ctx context.Con
 	}
 	return nil
 }
+
+func (r *targetGroupBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
+	if err := r.setupIndexes(ctx, mgr.GetFieldIndexer()); err != nil {
+		return err
+	}
+
+	epEventsHandler := eventhandlers.NewEnqueueRequestsForEndpointsEvent(r.k8sClient,
+		r.logger.WithName("eventHandlers").WithName("endpoints"))
+	nodeEventsHandler := eventhandlers.NewEnqueueRequestsForNodeEvent(r.k8sClient,
+		r.logger.WithName("eventHandlers").WithName("node"))
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&elbv2api.TargetGroupBinding{}).
+		Watches(&source.Kind{Type: &corev1.Endpoints{}}, epEventsHandler).
+		Watches(&source.Kind{Type: &corev1.Node{}}, nodeEventsHandler).
+		WithOptions(controller.Options{MaxConcurrentReconciles: 3}).
+		Complete(r)
+}
+
+func (r *targetGroupBindingReconciler) setupIndexes(ctx context.Context, fieldIndexer client.FieldIndexer) error {
+	if err := fieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
+		targetgroupbinding.IndexKeyServiceRefName, targetgroupbinding.IndexFuncServiceRefName); err != nil {
+		return err
+	}
+	if err := fieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
+		targetgroupbinding.IndexKeyTargetType, targetgroupbinding.IndexFuncTargetType); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/controllers/ingress/eventhandlers/ingress_events.go b/controllers/ingress/eventhandlers/ingress_events.go
index cd86e83d4..76b0cf902 100644
--- a/controllers/ingress/eventhandlers/ingress_events.go
+++ b/controllers/ingress/eventhandlers/ingress_events.go
@@ -2,6 +2,7 @@ package eventhandlers
 
 import (
 	"context"
+	"github.com/go-logr/logr"
 	networking "k8s.io/api/networking/v1beta1"
 	"k8s.io/apimachinery/pkg/api/equality"
 	"k8s.io/client-go/tools/record"
@@ -9,13 +10,14 @@ import (
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
 	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/log"
 )
 
-var logger = log.Log.WithName("eventhandlers").WithName("ingress")
-
-func NewEnqueueRequestsForIngressEvent(groupLoader ingress.GroupLoader, eventRecorder record.EventRecorder) *enqueueRequestsForIngressEvent {
-	return &enqueueRequestsForIngressEvent{groupLoader: groupLoader, eventRecorder: eventRecorder}
+func NewEnqueueRequestsForIngressEvent(groupLoader ingress.GroupLoader, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForIngressEvent {
+	return &enqueueRequestsForIngressEvent{
+		groupLoader:   groupLoader,
+		eventRecorder: eventRecorder,
+		logger:        logger,
+	}
 }
 
 var _ handler.EventHandler = (*enqueueRequestsForIngressEvent)(nil)
@@ -23,6 +25,7 @@ var _ handler.EventHandler = (*enqueueRequestsForIngressEvent)(nil)
 type enqueueRequestsForIngressEvent struct {
 	groupLoader   ingress.GroupLoader
 	eventRecorder record.EventRecorder
+	logger        logr.Logger
 }
 
 func (h *enqueueRequestsForIngressEvent) Create(e event.CreateEvent, queue workqueue.RateLimitingInterface) {
@@ -33,11 +36,14 @@ func (h *enqueueRequestsForIngressEvent) Update(e event.UpdateEvent, queue workq
 	ingOld := e.ObjectOld.(*networking.Ingress)
 	ingNew := e.ObjectNew.(*networking.Ingress)
 
-	// we only care three update event: 1. Ingress annotation updates 2. Ingress spec updates 3. Ingress deletion
+	// we only care below update event:
+	//	1. Ingress annotation updates
+	//	2. Ingress spec updates
+	//	3. Ingress deletion
 	if equality.Semantic.DeepEqual(ingOld.Annotations, ingNew.Annotations) &&
 		equality.Semantic.DeepEqual(ingOld.Spec, ingNew.Spec) &&
 		equality.Semantic.DeepEqual(ingOld.DeletionTimestamp.IsZero(), ingNew.DeletionTimestamp.IsZero()) {
-		logger.V(1).Info("ignoring unchanged Ingress Update event", "event", e)
+		h.logger.V(1).Info("ignoring unchanged Ingress Update event", "event", e)
 		return
 	}
 
@@ -52,6 +58,7 @@ func (h *enqueueRequestsForIngressEvent) Delete(e event.DeleteEvent, queue workq
 }
 
 func (h *enqueueRequestsForIngressEvent) Generic(e event.GenericEvent, queue workqueue.RateLimitingInterface) {
+	h.enqueueIfBelongsToGroup(queue, e.Object.(*networking.Ingress))
 }
 
 func (h *enqueueRequestsForIngressEvent) enqueueIfBelongsToGroup(queue workqueue.RateLimitingInterface, ingList ...*networking.Ingress) {
@@ -64,7 +71,7 @@ func (h *enqueueRequestsForIngressEvent) enqueueIfBelongsToGroup(queue workqueue
 			continue
 		}
 		if groupID == nil {
-			logger.V(1).Info("ignoring Ingress", "Ingress", ing)
+			h.logger.V(1).Info("ignoring Ingress", "Ingress", ing)
 			continue
 		}
 		groupIDs[*groupID] = struct{}{}
diff --git a/controllers/ingress/eventhandlers/secret_events.go b/controllers/ingress/eventhandlers/secret_events.go
new file mode 100644
index 000000000..2464fc93c
--- /dev/null
+++ b/controllers/ingress/eventhandlers/secret_events.go
@@ -0,0 +1,101 @@
+package eventhandlers
+
+import (
+	"context"
+	"github.com/go-logr/logr"
+	corev1 "k8s.io/api/core/v1"
+	networking "k8s.io/api/networking/v1beta1"
+	"k8s.io/apimachinery/pkg/api/equality"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/tools/record"
+	"k8s.io/client-go/util/workqueue"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+)
+
+// NewEnqueueRequestsForSecretEvent constructs new enqueueRequestsForSecretEvent.
+func NewEnqueueRequestsForSecretEvent(ingEventChan chan<- event.GenericEvent, svcEventChan chan<- event.GenericEvent,
+	k8sClient client.Client, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForSecretEvent {
+	return &enqueueRequestsForSecretEvent{
+		ingEventChan:  ingEventChan,
+		svcEventChan:  svcEventChan,
+		k8sClient:     k8sClient,
+		eventRecorder: eventRecorder,
+		logger:        logger,
+	}
+}
+
+var _ handler.EventHandler = (*enqueueRequestsForSecretEvent)(nil)
+
+type enqueueRequestsForSecretEvent struct {
+	ingEventChan  chan<- event.GenericEvent
+	svcEventChan  chan<- event.GenericEvent
+	k8sClient     client.Client
+	eventRecorder record.EventRecorder
+	logger        logr.Logger
+}
+
+func (h *enqueueRequestsForSecretEvent) Create(e event.CreateEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedObjects(e.Meta)
+}
+
+func (h *enqueueRequestsForSecretEvent) Update(e event.UpdateEvent, _ workqueue.RateLimitingInterface) {
+	secretOld := e.ObjectOld.(*corev1.Secret)
+	secretNew := e.ObjectNew.(*corev1.Secret)
+
+	// we only care below update event:
+	//	1. Secret data updates
+	//	2. Secret deletions
+	if equality.Semantic.DeepEqual(secretOld.Data, secretNew.Data) &&
+		equality.Semantic.DeepEqual(secretOld.DeletionTimestamp.IsZero(), secretNew.DeletionTimestamp.IsZero()) {
+		h.logger.V(1).Info("ignoring unchanged Secret Update event")
+		return
+	}
+
+	h.enqueueImpactedObjects(e.MetaNew)
+}
+
+func (h *enqueueRequestsForSecretEvent) Delete(e event.DeleteEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedObjects(e.Meta)
+}
+
+func (h *enqueueRequestsForSecretEvent) Generic(e event.GenericEvent, _ workqueue.RateLimitingInterface) {
+	// we don't have any generic event for secrets.
+}
+
+func (h *enqueueRequestsForSecretEvent) enqueueImpactedObjects(secret metav1.Object) {
+	ingList := &networking.IngressList{}
+	if err := h.k8sClient.List(context.Background(), ingList,
+		client.InNamespace(secret.GetNamespace()),
+		client.MatchingFields{ingress.IndexKeySecretRefName: secret.GetName()}); err != nil {
+		h.logger.Error(err, "failed to fetch ingresses")
+		return
+	}
+	for index := range ingList.Items {
+		ing := &ingList.Items[index]
+		meta, _ := meta.Accessor(ing)
+		h.ingEventChan <- event.GenericEvent{
+			Meta:   meta,
+			Object: ing,
+		}
+	}
+
+	svcList := &corev1.ServiceList{}
+	if err := h.k8sClient.List(context.Background(), svcList,
+		client.InNamespace(secret.GetNamespace()),
+		client.MatchingFields{ingress.IndexKeySecretRefName: secret.GetName()}); err != nil {
+		h.logger.Error(err, "failed to fetch services")
+		return
+	}
+	for index := range svcList.Items {
+		svc := &svcList.Items[index]
+		meta, _ := meta.Accessor(svc)
+		h.svcEventChan <- event.GenericEvent{
+			Meta:   meta,
+			Object: svc,
+		}
+	}
+}
diff --git a/controllers/ingress/eventhandlers/service_events.go b/controllers/ingress/eventhandlers/service_events.go
new file mode 100644
index 000000000..f905b3ef4
--- /dev/null
+++ b/controllers/ingress/eventhandlers/service_events.go
@@ -0,0 +1,86 @@
+package eventhandlers
+
+import (
+	"context"
+	"github.com/go-logr/logr"
+	corev1 "k8s.io/api/core/v1"
+	networking "k8s.io/api/networking/v1beta1"
+	"k8s.io/apimachinery/pkg/api/equality"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/tools/record"
+	"k8s.io/client-go/util/workqueue"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+)
+
+// NewEnqueueRequestsForServiceEvent constructs new enqueueRequestsForServiceEvent.
+func NewEnqueueRequestsForServiceEvent(ingEventChan chan<- event.GenericEvent,
+	k8sClient client.Client, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForServiceEvent {
+	return &enqueueRequestsForServiceEvent{
+		ingEventChan:  ingEventChan,
+		k8sClient:     k8sClient,
+		eventRecorder: eventRecorder,
+		logger:        logger,
+	}
+}
+
+var _ handler.EventHandler = (*enqueueRequestsForServiceEvent)(nil)
+
+type enqueueRequestsForServiceEvent struct {
+	ingEventChan  chan<- event.GenericEvent
+	k8sClient     client.Client
+	eventRecorder record.EventRecorder
+	logger        logr.Logger
+}
+
+func (h *enqueueRequestsForServiceEvent) Create(e event.CreateEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedIngresses(e.Meta)
+}
+
+func (h *enqueueRequestsForServiceEvent) Update(e event.UpdateEvent, _ workqueue.RateLimitingInterface) {
+	svcOld := e.ObjectOld.(*corev1.Service)
+	svcNew := e.ObjectNew.(*corev1.Service)
+
+	// we only care below update event:
+	//	1. Service annotation updates
+	//	2. Service spec updates
+	//	3. Service deletions
+	if equality.Semantic.DeepEqual(svcOld.Annotations, svcNew.Annotations) &&
+		equality.Semantic.DeepEqual(svcOld.Spec, svcNew.Spec) &&
+		equality.Semantic.DeepEqual(svcOld.DeletionTimestamp.IsZero(), svcNew.DeletionTimestamp.IsZero()) {
+		h.logger.V(1).Info("ignoring unchanged Service Update event", "event", e)
+		return
+	}
+
+	h.enqueueImpactedIngresses(e.MetaNew)
+}
+
+func (h *enqueueRequestsForServiceEvent) Delete(e event.DeleteEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedIngresses(e.Meta)
+}
+
+func (h *enqueueRequestsForServiceEvent) Generic(e event.GenericEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedIngresses(e.Meta)
+}
+
+func (h *enqueueRequestsForServiceEvent) enqueueImpactedIngresses(svc metav1.Object) {
+	ingList := &networking.IngressList{}
+	if err := h.k8sClient.List(context.Background(), ingList,
+		client.InNamespace(svc.GetNamespace()),
+		client.MatchingFields{ingress.IndexKeyServiceRefName: svc.GetName()}); err != nil {
+		h.logger.Error(err, "failed to fetch ingresses")
+		return
+	}
+
+	for index := range ingList.Items {
+		ing := &ingList.Items[index]
+		meta, _ := meta.Accessor(ing)
+		h.ingEventChan <- event.GenericEvent{
+			Meta:   meta,
+			Object: ing,
+		}
+	}
+}
diff --git a/controllers/ingress/group_controller.go b/controllers/ingress/group_controller.go
index c74b0acd3..3fc7b58b0 100644
--- a/controllers/ingress/group_controller.go
+++ b/controllers/ingress/group_controller.go
@@ -6,6 +6,7 @@ import (
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	networking "k8s.io/api/networking/v1beta1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/tools/record"
 	"sigs.k8s.io/aws-load-balancer-controller/controllers/ingress/eventhandlers"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
@@ -17,50 +18,60 @@ import (
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller"
+	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/source"
 )
 
-const controllerName = "ingress"
+const (
+	ingressTagPrefix        = "ingress.k8s.aws"
+	ingressAnnotationPrefix = "alb.ingress.kubernetes.io"
+	controllerName          = "ingress"
+)
 
 // NewGroupReconciler constructs new GroupReconciler
 func NewGroupReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorder record.EventRecorder,
 	networkingSGManager networkingpkg.SecurityGroupManager, networkingSGReconciler networkingpkg.SecurityGroupReconciler, clusterName string,
 	subnetsResolver networkingpkg.SubnetsResolver, logger logr.Logger) *groupReconciler {
-	annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+	annotationParser := annotations.NewSuffixAnnotationParser(ingressAnnotationPrefix)
 	authConfigBuilder := ingress.NewDefaultAuthConfigBuilder(annotationParser)
 	enhancedBackendBuilder := ingress.NewDefaultEnhancedBackendBuilder(annotationParser)
+	referenceIndexer := ingress.NewDefaultReferenceIndexer(enhancedBackendBuilder, authConfigBuilder, logger)
 	modelBuilder := ingress.NewDefaultModelBuilder(k8sClient, eventRecorder,
 		cloud.EC2(), cloud.ACM(),
 		annotationParser, subnetsResolver,
 		authConfigBuilder, enhancedBackendBuilder,
 		cloud.VpcID(), clusterName, logger)
 	stackMarshaller := deploy.NewDefaultStackMarshaller()
-	stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler, clusterName, "ingress.k8s.aws", logger)
+	stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler, clusterName, ingressTagPrefix, logger)
 	groupLoader := ingress.NewDefaultGroupLoader(k8sClient, annotationParser, "alb")
 	k8sFinalizerManager := k8s.NewDefaultFinalizerManager(k8sClient, logger)
 	finalizerManager := ingress.NewDefaultFinalizerManager(k8sFinalizerManager)
 
 	return &groupReconciler{
 		k8sClient:        k8sClient,
+		eventRecorder:    eventRecorder,
 		groupLoader:      groupLoader,
 		finalizerManager: finalizerManager,
+		referenceIndexer: referenceIndexer,
 		modelBuilder:     modelBuilder,
 		stackMarshaller:  stackMarshaller,
 		stackDeployer:    stackDeployer,
-		log:              logger,
+		logger:           logger,
 	}
 }
 
 // GroupReconciler reconciles a ingress group
 type groupReconciler struct {
-	k8sClient       client.Client
-	modelBuilder    ingress.ModelBuilder
-	stackMarshaller deploy.StackMarshaller
-	stackDeployer   deploy.StackDeployer
+	k8sClient        client.Client
+	eventRecorder    record.EventRecorder
+	referenceIndexer ingress.ReferenceIndexer
+	modelBuilder     ingress.ModelBuilder
+	stackMarshaller  deploy.StackMarshaller
+	stackDeployer    deploy.StackDeployer
 
 	groupLoader      ingress.GroupLoader
 	finalizerManager ingress.FinalizerManager
-	log              logr.Logger
+	logger           logr.Logger
 }
 
 // +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;update;patch
@@ -74,7 +85,7 @@ type groupReconciler struct {
 func (r *groupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	ctx := context.Background()
 	ingGroupID := ingress.DecodeGroupIDFromReconcileRequest(req)
-	_ = r.log.WithValues("groupID", ingGroupID)
+	_ = r.logger.WithValues("groupID", ingGroupID)
 	ingGroup, err := r.groupLoader.Load(ctx, ingGroupID)
 	if err != nil {
 		return ctrl.Result{}, err
@@ -91,11 +102,11 @@ func (r *groupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	if err != nil {
 		return ctrl.Result{}, err
 	}
-	r.log.Info("successfully built model", "model", stackJSON)
+	r.logger.Info("successfully built model", "model", stackJSON)
 	if err := r.stackDeployer.Deploy(ctx, stack); err != nil {
 		return ctrl.Result{}, err
 	}
-	r.log.Info("successfully deployed model")
+	r.logger.Info("successfully deployed model")
 
 	if len(ingGroup.Members) > 0 {
 		lbDNS, err := lb.DNSName().Resolve(ctx)
@@ -139,21 +150,72 @@ func (r *groupReconciler) updateIngressStatus(ctx context.Context, ing *networki
 	return nil
 }
 
-func (r *groupReconciler) SetupWithManager(mgr ctrl.Manager) error {
+func (r *groupReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
 	c, err := controller.New(controllerName, mgr, controller.Options{
-		MaxConcurrentReconciles: 1,
+		MaxConcurrentReconciles: 3,
 		Reconciler:              r,
 	})
 	if err != nil {
 		return err
 	}
-	return r.setupWatches(mgr, c, r.groupLoader)
+	if err := r.setupIndexes(ctx, mgr.GetFieldIndexer()); err != nil {
+		return err
+	}
+	if err := r.setupWatches(ctx, c); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (r *groupReconciler) setupIndexes(ctx context.Context, fieldIndexer client.FieldIndexer) error {
+	if err := fieldIndexer.IndexField(ctx, &networking.Ingress{}, ingress.IndexKeyServiceRefName,
+		func(obj runtime.Object) []string {
+			return r.referenceIndexer.BuildServiceRefIndexes(context.Background(), obj.(*networking.Ingress))
+		},
+	); err != nil {
+		return err
+	}
+	if err := fieldIndexer.IndexField(ctx, &networking.Ingress{}, ingress.IndexKeySecretRefName,
+		func(obj runtime.Object) []string {
+			return r.referenceIndexer.BuildSecretRefIndexes(context.Background(), obj.(*networking.Ingress))
+		},
+	); err != nil {
+		return err
+	}
+	if err := fieldIndexer.IndexField(ctx, &corev1.Service{}, ingress.IndexKeySecretRefName,
+		func(obj runtime.Object) []string {
+			return r.referenceIndexer.BuildSecretRefIndexes(context.Background(), obj.(*corev1.Service))
+		},
+	); err != nil {
+		return err
+	}
+	return nil
 }
 
-func (r *groupReconciler) setupWatches(mgr ctrl.Manager, c controller.Controller, groupLoader ingress.GroupLoader) error {
-	ingEventHandler := eventhandlers.NewEnqueueRequestsForIngressEvent(groupLoader, mgr.GetEventRecorderFor(controllerName))
+func (r *groupReconciler) setupWatches(_ context.Context, c controller.Controller) error {
+	ingEventChan := make(chan event.GenericEvent)
+	svcEventChan := make(chan event.GenericEvent)
+	ingEventHandler := eventhandlers.NewEnqueueRequestsForIngressEvent(r.groupLoader, r.eventRecorder,
+		r.logger.WithName("eventHandlers").WithName("ingress"))
+	svcEventHandler := eventhandlers.NewEnqueueRequestsForServiceEvent(ingEventChan, r.k8sClient, r.eventRecorder,
+		r.logger.WithName("eventHandlers").WithName("service"))
+	secretEventHandler := eventhandlers.NewEnqueueRequestsForSecretEvent(ingEventChan, svcEventChan, r.k8sClient, r.eventRecorder,
+		r.logger.WithName("eventHandlers").WithName("secret"))
+
+	if err := c.Watch(&source.Channel{Source: ingEventChan}, ingEventHandler); err != nil {
+		return err
+	}
+	if err := c.Watch(&source.Channel{Source: svcEventChan}, svcEventHandler); err != nil {
+		return err
+	}
 	if err := c.Watch(&source.Kind{Type: &networking.Ingress{}}, ingEventHandler); err != nil {
 		return err
 	}
+	if err := c.Watch(&source.Kind{Type: &corev1.Service{}}, svcEventHandler); err != nil {
+		return err
+	}
+	if err := c.Watch(&source.Kind{Type: &corev1.Secret{}}, secretEventHandler); err != nil {
+		return err
+	}
 	return nil
 }
diff --git a/controllers/service/eventhandlers/service_events.go b/controllers/service/eventhandlers/service_events.go
index d404c0b06..cd471f8b1 100644
--- a/controllers/service/eventhandlers/service_events.go
+++ b/controllers/service/eventhandlers/service_events.go
@@ -1,6 +1,7 @@
 package eventhandlers
 
 import (
+	"github.com/go-logr/logr"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/equality"
 	"k8s.io/apimachinery/pkg/types"
@@ -9,16 +10,15 @@ import (
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
 	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
-var logger = log.Log.WithName("eventhandlers").WithName("service")
-
-func NewEnqueueRequestForServiceEvent(eventRecorder record.EventRecorder, annotationParser annotations.Parser) *enqueueRequestsForServiceEvent {
+// NewEnqueueRequestForServiceEvent constructs new enqueueRequestsForServiceEvent.
+func NewEnqueueRequestForServiceEvent(eventRecorder record.EventRecorder, annotationParser annotations.Parser, logger logr.Logger) *enqueueRequestsForServiceEvent {
 	return &enqueueRequestsForServiceEvent{
 		eventRecorder:    eventRecorder,
 		annotationParser: annotationParser,
+		logger:           logger,
 	}
 }
 
@@ -27,6 +27,7 @@ var _ handler.EventHandler = (*enqueueRequestsForServiceEvent)(nil)
 type enqueueRequestsForServiceEvent struct {
 	eventRecorder    record.EventRecorder
 	annotationParser annotations.Parser
+	logger           logr.Logger
 }
 
 func (h *enqueueRequestsForServiceEvent) Create(e event.CreateEvent, queue workqueue.RateLimitingInterface) {
@@ -40,7 +41,7 @@ func (h *enqueueRequestsForServiceEvent) Update(e event.UpdateEvent, queue workq
 	if equality.Semantic.DeepEqual(oldSvc.Annotations, newSvc.Annotations) &&
 		equality.Semantic.DeepEqual(oldSvc.Spec, newSvc.Spec) &&
 		equality.Semantic.DeepEqual(oldSvc.DeletionTimestamp.IsZero(), newSvc.DeletionTimestamp.IsZero()) {
-		logger.V(1).Info("Ignoring unchanged Service update event", "event", e)
+		h.logger.V(1).Info("Ignoring unchanged Service update event", "event", e)
 		return
 	}
 
diff --git a/controllers/service/service_controller.go b/controllers/service/service_controller.go
index bc8128aaa..5bd22af89 100644
--- a/controllers/service/service_controller.go
+++ b/controllers/service/service_controller.go
@@ -5,6 +5,7 @@ import (
 	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/client-go/tools/record"
 	"sigs.k8s.io/aws-load-balancer-controller/controllers/service/eventhandlers"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws"
@@ -28,30 +29,34 @@ const (
 	controllerName          = "service"
 )
 
-func NewServiceReconciler(cloud aws.Cloud, k8sClient client.Client,
+func NewServiceReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorder record.EventRecorder,
 	sgManager networking.SecurityGroupManager, sgReconciler networking.SecurityGroupReconciler,
 	clusterName string, resolver networking.SubnetsResolver, logger logr.Logger) *serviceReconciler {
 	annotationParser := annotations.NewSuffixAnnotationParser(serviceAnnotationPrefix)
 	modelBuilder := nlb.NewDefaultModelBuilder(clusterName, resolver, annotationParser)
 	return &serviceReconciler{
 		k8sClient:        k8sClient,
-		logger:           logger,
+		eventRecorder:    eventRecorder,
 		annotationParser: annotationParser,
 		finalizerManager: k8s.NewDefaultFinalizerManager(k8sClient, logger),
 		modelBuilder:     modelBuilder,
 		stackMarshaller:  deploy.NewDefaultStackMarshaller(),
 		stackDeployer:    deploy.NewDefaultStackDeployer(cloud, k8sClient, sgManager, sgReconciler, clusterName, serviceTagPrefix, logger),
+		logger:           logger,
 	}
 }
 
 type serviceReconciler struct {
-	k8sClient        client.Client
-	logger           logr.Logger
+	k8sClient     client.Client
+	eventRecorder record.EventRecorder
+
 	annotationParser annotations.Parser
 	finalizerManager k8s.FinalizerManager
 	modelBuilder     nlb.ModelBuilder
 	stackMarshaller  deploy.StackMarshaller
 	stackDeployer    deploy.StackDeployer
+
+	logger logr.Logger
 }
 
 // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;update;patch
@@ -138,19 +143,23 @@ func (r *serviceReconciler) updateServiceStatus(ctx context.Context, svc *corev1
 	return nil
 }
 
-func (r *serviceReconciler) SetupWithManager(mgr ctrl.Manager) error {
+func (r *serviceReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
 	c, err := controller.New(controllerName, mgr, controller.Options{
-		MaxConcurrentReconciles: 1,
+		MaxConcurrentReconciles: 3,
 		Reconciler:              r,
 	})
 	if err != nil {
 		return err
 	}
-	return r.setupWatches(mgr, c)
+	if err := r.setupWatches(ctx, c); err != nil {
+		return err
+	}
+	return nil
 }
 
-func (r *serviceReconciler) setupWatches(mgr ctrl.Manager, c controller.Controller) error {
-	svcEventHandler := eventhandlers.NewEnqueueRequestForServiceEvent(mgr.GetEventRecorderFor(controllerName), r.annotationParser)
+func (r *serviceReconciler) setupWatches(_ context.Context, c controller.Controller) error {
+	svcEventHandler := eventhandlers.NewEnqueueRequestForServiceEvent(r.eventRecorder, r.annotationParser,
+		r.logger.WithName("eventHandlers").WithName("service"))
 	if err := c.Watch(&source.Kind{Type: &corev1.Service{}}, svcEventHandler); err != nil {
 		return err
 	}
diff --git a/main.go b/main.go
index b409f91c5..11893ad11 100644
--- a/main.go
+++ b/main.go
@@ -121,27 +121,25 @@ func main() {
 
 	subnetResolver := networking.NewSubnetsResolver(cloud.EC2(), cloud.VpcID(), k8sClusterName, ctrl.Log.WithName("subnets-resolver"))
 	ingGroupReconciler := ingress.NewGroupReconciler(cloud, mgr.GetClient(), mgr.GetEventRecorderFor("ingress"),
-		sgManager, sgReconciler, k8sClusterName, subnetResolver, ctrl.Log)
-	tgbReconciler := elbv2controller.NewTargetGroupBindingReconciler(mgr.GetClient(), mgr.GetFieldIndexer(), finalizerManager, tgbResManager,
-		ctrl.Log.WithName("controllers").WithName("TargetGroupBinding"))
-	svcReconciler := service.NewServiceReconciler(
-		cloud,
-		mgr.GetClient(),
-		sgManager,
-		sgReconciler,
-		k8sClusterName,
-		subnetResolver,
+		sgManager, sgReconciler, k8sClusterName, subnetResolver,
+		ctrl.Log.WithName("controllers").WithName("Ingress"))
+	svcReconciler := service.NewServiceReconciler(cloud, mgr.GetClient(), mgr.GetEventRecorderFor("service"),
+		sgManager, sgReconciler, k8sClusterName, subnetResolver,
 		ctrl.Log.WithName("controllers").WithName("Service"))
-	if err = ingGroupReconciler.SetupWithManager(mgr); err != nil {
+	tgbReconciler := elbv2controller.NewTargetGroupBindingReconciler(mgr.GetClient(), finalizerManager, tgbResManager,
+		ctrl.Log.WithName("controllers").WithName("TargetGroupBinding"))
+
+	ctx := context.Background()
+	if err = ingGroupReconciler.SetupWithManager(ctx, mgr); err != nil {
 		setupLog.Error(err, "unable to create controller", "controller", "Ingress")
 		os.Exit(1)
 	}
-	if err := tgbReconciler.SetupWithManager(context.Background(), mgr); err != nil {
-		setupLog.Error(err, "unable to create controller", "controller", "TargetGroupBinding")
+	if err = svcReconciler.SetupWithManager(ctx, mgr); err != nil {
+		setupLog.Error(err, "Unable to create controller", "controller", "Service")
 		os.Exit(1)
 	}
-	if err = svcReconciler.SetupWithManager(mgr); err != nil {
-		setupLog.Error(err, "Unable to create controller", "controller", "Service")
+	if err := tgbReconciler.SetupWithManager(ctx, mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "TargetGroupBinding")
 		os.Exit(1)
 	}
 
diff --git a/pkg/deploy/ec2/security_group_manager.go b/pkg/deploy/ec2/security_group_manager.go
index 689ea24e2..910897954 100644
--- a/pkg/deploy/ec2/security_group_manager.go
+++ b/pkg/deploy/ec2/security_group_manager.go
@@ -2,11 +2,11 @@ package ec2
 
 import (
 	"context"
-	"errors"
 	awssdk "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/awserr"
 	ec2sdk "github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
@@ -131,7 +131,7 @@ func (m *defaultSecurityGroupManager) Delete(ctx context.Context, sdkSG networki
 		}
 		return true, nil
 	}, ctx.Done()); err != nil {
-		return err
+		return errors.Wrap(err, "failed to delete securityGroup")
 	}
 	m.logger.Info("deleted securityGroup",
 		"securityGroupID", sdkSG.SecurityGroupID)
diff --git a/pkg/deploy/elbv2/listener_rule_manager.go b/pkg/deploy/elbv2/listener_rule_manager.go
index c69c51761..47f259e86 100644
--- a/pkg/deploy/elbv2/listener_rule_manager.go
+++ b/pkg/deploy/elbv2/listener_rule_manager.go
@@ -3,12 +3,21 @@ package elbv2
 import (
 	"context"
 	awssdk "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/awserr"
 	elbv2sdk "github.com/aws/aws-sdk-go/service/elbv2"
 	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
+	"github.com/pkg/errors"
+	"k8s.io/apimachinery/pkg/util/wait"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
 	elbv2equality "sigs.k8s.io/aws-load-balancer-controller/pkg/equality/elbv2"
 	elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
+	"time"
+)
+
+const (
+	defaultWaitLSExistencePollInterval = 2 * time.Second
+	defaultWaitLSExistenceTimeout      = 20 * time.Second
 )
 
 // ListenerRuleManager is responsible for create/update/delete ListenerRule resources.
@@ -23,8 +32,10 @@ type ListenerRuleManager interface {
 // NewDefaultListenerRuleManager constructs new defaultListenerRuleManager.
 func NewDefaultListenerRuleManager(elbv2Client services.ELBV2, logger logr.Logger) *defaultListenerRuleManager {
 	return &defaultListenerRuleManager{
-		elbv2Client: elbv2Client,
-		logger:      logger,
+		elbv2Client:                 elbv2Client,
+		logger:                      logger,
+		waitLSExistencePollInterval: defaultWaitLSExistencePollInterval,
+		waitLSExistenceTimeout:      defaultWaitLSExistenceTimeout,
 	}
 }
 
@@ -32,6 +43,9 @@ func NewDefaultListenerRuleManager(elbv2Client services.ELBV2, logger logr.Logge
 type defaultListenerRuleManager struct {
 	elbv2Client services.ELBV2
 	logger      logr.Logger
+
+	waitLSExistencePollInterval time.Duration
+	waitLSExistenceTimeout      time.Duration
 }
 
 func (m *defaultListenerRuleManager) Create(ctx context.Context, resLR *elbv2model.ListenerRule) (elbv2model.ListenerRuleStatus, error) {
@@ -40,14 +54,25 @@ func (m *defaultListenerRuleManager) Create(ctx context.Context, resLR *elbv2mod
 		return elbv2model.ListenerRuleStatus{}, err
 	}
 
+	ctx, cancel := context.WithTimeout(ctx, m.waitLSExistenceTimeout)
+	defer cancel()
 	m.logger.Info("creating listener rule",
 		"stackID", resLR.Stack().StackID(),
 		"resourceID", resLR.ID())
-	resp, err := m.elbv2Client.CreateRuleWithContext(ctx, req)
-	if err != nil {
-		return elbv2model.ListenerRuleStatus{}, err
+	var sdkLR *elbv2sdk.Rule
+	if err := wait.PollImmediateUntil(m.waitLSExistencePollInterval, func() (done bool, err error) {
+		resp, err := m.elbv2Client.CreateRuleWithContext(ctx, req)
+		if err != nil {
+			if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ListenerNotFound" {
+				return false, nil
+			}
+			return false, err
+		}
+		sdkLR = resp.Rules[0]
+		return true, nil
+	}, ctx.Done()); err != nil {
+		return elbv2model.ListenerRuleStatus{}, errors.Wrap(err, "failed to create listener rule")
 	}
-	sdkLR := resp.Rules[0]
 	m.logger.Info("created listener rule",
 		"stackID", resLR.Stack().StackID(),
 		"resourceID", resLR.ID(),
diff --git a/pkg/deploy/elbv2/target_group_binding_manager.go b/pkg/deploy/elbv2/target_group_binding_manager.go
index 9d4105227..011382391 100644
--- a/pkg/deploy/elbv2/target_group_binding_manager.go
+++ b/pkg/deploy/elbv2/target_group_binding_manager.go
@@ -119,7 +119,7 @@ func (m *defaultTargetGroupBindingManager) Delete(ctx context.Context, tgb *elbv
 		return err
 	}
 	if err := m.waitUntilTargetGroupBindingDeleted(ctx, tgb); err != nil {
-		return err
+		return errors.Wrap(err, "failed to wait targetGroupBinding deletion")
 	}
 	m.logger.Info("deleted targetGroupBinding",
 		"targetGroupBinding", k8s.NamespacedName(tgb))
diff --git a/pkg/deploy/elbv2/target_group_manager.go b/pkg/deploy/elbv2/target_group_manager.go
index 290af5f6b..3495989f7 100644
--- a/pkg/deploy/elbv2/target_group_manager.go
+++ b/pkg/deploy/elbv2/target_group_manager.go
@@ -6,6 +6,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws/awserr"
 	elbv2sdk "github.com/aws/aws-sdk-go/service/elbv2"
 	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
@@ -119,7 +120,7 @@ func (m *defaultTargetGroupManager) Delete(ctx context.Context, sdkTG TargetGrou
 		}
 		return true, nil
 	}, ctx.Done()); err != nil {
-		return err
+		return errors.Wrap(err, "failed to delete targetGroup")
 	}
 	m.logger.Info("deleted targetGroup",
 		"arn", awssdk.StringValue(req.TargetGroupArn))
diff --git a/pkg/ingress/auth_config_builder.go b/pkg/ingress/auth_config_builder.go
index 5b98409ff..1f4fc1359 100644
--- a/pkg/ingress/auth_config_builder.go
+++ b/pkg/ingress/auth_config_builder.go
@@ -6,6 +6,14 @@ import (
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
 )
 
+const (
+	defaultAuthType                     = AuthTypeNone
+	defaultAuthScope                    = "openid"
+	defaultAuthSessionCookieName        = "AWSELBAuthSessionCookie"
+	defaultAuthSessionTimeout           = 604800
+	defaultAuthOnUnauthenticatedRequest = "authenticate"
+)
+
 // Auth config for Service / Ingresses
 type AuthConfig struct {
 	Type                     AuthType
@@ -41,41 +49,36 @@ func (b *defaultAuthConfigBuilder) Build(ctx context.Context, svcAndIngAnnotatio
 	if err != nil {
 		return AuthConfig{}, err
 	}
-	authConfig := AuthConfig{
-		Type: authType,
+	authOnUnauthenticatedRequest := b.buildAuthOnUnauthenticatedRequest(ctx, svcAndIngAnnotations)
+	authScope := b.buildAuthScope(ctx, svcAndIngAnnotations)
+	authSessionCookieName := b.buildAuthSessionCookieName(ctx, svcAndIngAnnotations)
+	authSessionTimeout, err := b.buildAuthSessionTimeout(ctx, svcAndIngAnnotations)
+	if err != nil {
+		return AuthConfig{}, err
+	}
+	authIDPCognito, err := b.buildAuthIDPConfigCognito(ctx, svcAndIngAnnotations)
+	if err != nil {
+		return AuthConfig{}, err
 	}
-	if authType != AuthTypeNone {
-		authOnUnauthenticatedRequest := b.buildAuthOnUnauthenticatedRequest(ctx, svcAndIngAnnotations)
-		authScope := b.buildAuthScope(ctx, svcAndIngAnnotations)
-		authSessionCookieName := b.buildAuthSessionCookieName(ctx, svcAndIngAnnotations)
-		authSessionTimeout, err := b.buildAuthSessionTimeout(ctx, svcAndIngAnnotations)
-		if err != nil {
-			return AuthConfig{}, err
-		}
-		authConfig.OnUnauthenticatedRequest = authOnUnauthenticatedRequest
-		authConfig.Scope = authScope
-		authConfig.SessionCookieName = authSessionCookieName
-		authConfig.SessionTimeout = authSessionTimeout
+	authIDPOIDC, err := b.buildAuthIDPConfigOIDC(ctx, svcAndIngAnnotations)
+	if err != nil {
+		return AuthConfig{}, err
 	}
 
-	switch authType {
-	case AuthTypeCognito:
-		authIDP, err := b.buildAuthIDPConfigCognito(ctx, svcAndIngAnnotations)
-		if err != nil {
-			return AuthConfig{}, err
-		}
-		authConfig.IDPConfigCognito = &authIDP
-	case AuthTypeOIDC:
-		authIDP, err := b.buildAuthIDPConfigOIDC(ctx, svcAndIngAnnotations)
-		if err != nil {
-			return AuthConfig{}, err
-		}
-		authConfig.IDPConfigOIDC = &authIDP
+	authConfig := AuthConfig{
+		Type:                     authType,
+		OnUnauthenticatedRequest: authOnUnauthenticatedRequest,
+		Scope:                    authScope,
+		SessionCookieName:        authSessionCookieName,
+		SessionTimeout:           authSessionTimeout,
+		IDPConfigOIDC:            authIDPOIDC,
+		IDPConfigCognito:         authIDPCognito,
 	}
+
 	return authConfig, nil
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthType(ctx context.Context, svcAndIngAnnotations map[string]string) (AuthType, error) {
+func (b *defaultAuthConfigBuilder) buildAuthType(_ context.Context, svcAndIngAnnotations map[string]string) (AuthType, error) {
 	rawAuthType := string(defaultAuthType)
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthType, &rawAuthType, svcAndIngAnnotations)
 	switch rawAuthType {
@@ -90,49 +93,49 @@ func (b *defaultAuthConfigBuilder) buildAuthType(ctx context.Context, svcAndIngA
 	}
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthIDPConfigCognito(ctx context.Context, svcAndIngAnnotations map[string]string) (AuthIDPConfigCognito, error) {
+func (b *defaultAuthConfigBuilder) buildAuthIDPConfigCognito(_ context.Context, svcAndIngAnnotations map[string]string) (*AuthIDPConfigCognito, error) {
 	authIDP := AuthIDPConfigCognito{}
 	exists, err := b.annotationParser.ParseJSONAnnotation(annotations.IngressSuffixAuthIDPCognito, &authIDP, svcAndIngAnnotations)
 	if err != nil {
-		return AuthIDPConfigCognito{}, err
+		return nil, err
 	}
 	if !exists {
-		return AuthIDPConfigCognito{}, errors.Errorf("missing %v configuration", annotations.IngressSuffixAuthIDPCognito)
+		return nil, nil
 	}
-	return authIDP, nil
+	return &authIDP, nil
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthIDPConfigOIDC(ctx context.Context, svcAndIngAnnotations map[string]string) (AuthIDPConfigOIDC, error) {
+func (b *defaultAuthConfigBuilder) buildAuthIDPConfigOIDC(_ context.Context, svcAndIngAnnotations map[string]string) (*AuthIDPConfigOIDC, error) {
 	authIDP := AuthIDPConfigOIDC{}
 	exists, err := b.annotationParser.ParseJSONAnnotation(annotations.IngressSuffixAuthIDPOIDC, &authIDP, svcAndIngAnnotations)
 	if err != nil {
-		return AuthIDPConfigOIDC{}, err
+		return nil, err
 	}
 	if !exists {
-		return AuthIDPConfigOIDC{}, errors.Errorf("missing %v configuration", annotations.IngressSuffixAuthIDPOIDC)
+		return nil, nil
 	}
-	return authIDP, nil
+	return &authIDP, nil
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthOnUnauthenticatedRequest(ctx context.Context, svcAndIngAnnotations map[string]string) string {
+func (b *defaultAuthConfigBuilder) buildAuthOnUnauthenticatedRequest(_ context.Context, svcAndIngAnnotations map[string]string) string {
 	rawOnUnauthenticatedRequest := defaultAuthOnUnauthenticatedRequest
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthOnUnauthenticatedRequest, &rawOnUnauthenticatedRequest, svcAndIngAnnotations)
 	return rawOnUnauthenticatedRequest
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthScope(ctx context.Context, svcAndIngAnnotations map[string]string) string {
+func (b *defaultAuthConfigBuilder) buildAuthScope(_ context.Context, svcAndIngAnnotations map[string]string) string {
 	rawAuthScope := defaultAuthScope
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthScope, &rawAuthScope, svcAndIngAnnotations)
 	return rawAuthScope
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthSessionCookieName(ctx context.Context, svcAndIngAnnotations map[string]string) string {
+func (b *defaultAuthConfigBuilder) buildAuthSessionCookieName(_ context.Context, svcAndIngAnnotations map[string]string) string {
 	rawAuthSessionCookieName := defaultAuthSessionCookieName
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthSessionCookie, &rawAuthSessionCookieName, svcAndIngAnnotations)
 	return rawAuthSessionCookieName
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthSessionTimeout(ctx context.Context, svcAndIngAnnotations map[string]string) (int64, error) {
+func (b *defaultAuthConfigBuilder) buildAuthSessionTimeout(_ context.Context, svcAndIngAnnotations map[string]string) (int64, error) {
 	rawAuthSessionTimeout := int64(defaultAuthSessionTimeout)
 	if _, err := b.annotationParser.ParseInt64Annotation(annotations.IngressSuffixAuthSessionTimeout, &rawAuthSessionTimeout, svcAndIngAnnotations); err != nil {
 		return 0, err
diff --git a/pkg/ingress/enhanced_backend_builder.go b/pkg/ingress/enhanced_backend_builder.go
index a26a58099..3fba0a295 100644
--- a/pkg/ingress/enhanced_backend_builder.go
+++ b/pkg/ingress/enhanced_backend_builder.go
@@ -10,12 +10,6 @@ import (
 )
 
 const (
-	defaultAuthType                     = AuthTypeNone
-	defaultAuthScope                    = "openid"
-	defaultAuthSessionCookieName        = "AWSELBAuthSessionCookie"
-	defaultAuthSessionTimeout           = 604800
-	defaultAuthOnUnauthenticatedRequest = "authenticate"
-
 	magicServicePortUseAnnotation = "use-annotation"
 )
 
diff --git a/pkg/ingress/reference_indexer.go b/pkg/ingress/reference_indexer.go
new file mode 100644
index 000000000..9d47829da
--- /dev/null
+++ b/pkg/ingress/reference_indexer.go
@@ -0,0 +1,106 @@
+package ingress
+
+import (
+	"context"
+	"fmt"
+	"github.com/go-logr/logr"
+	networking "k8s.io/api/networking/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/sets"
+)
+
+const (
+	// IndexKey for services referenced by Ingress.
+	IndexKeyServiceRefName = "ingress.serviceRef.name"
+	// IndexKey for secrets referenced by Ingress or Service.
+	IndexKeySecretRefName = "ingress.secretRef.name"
+)
+
+// ReferenceIndexer has the ability to index Ingresses with referenced objects.
+type ReferenceIndexer interface {
+	BuildServiceRefIndexes(ctx context.Context, ing *networking.Ingress) []string
+	BuildSecretRefIndexes(ctx context.Context, ingOrSvc metav1.Object) []string
+}
+
+// NewDefaultReferenceIndexer constructs new defaultReferenceIndexer.
+func NewDefaultReferenceIndexer(enhancedBackendBuilder EnhancedBackendBuilder, authConfigBuilder AuthConfigBuilder, logger logr.Logger) *defaultReferenceIndexer {
+	return &defaultReferenceIndexer{
+		enhancedBackendBuilder: enhancedBackendBuilder,
+		authConfigBuilder:      authConfigBuilder,
+		logger:                 logger,
+	}
+}
+
+var _ ReferenceIndexer = &defaultReferenceIndexer{}
+
+// default implementation for ReferenceIndexer
+type defaultReferenceIndexer struct {
+	enhancedBackendBuilder EnhancedBackendBuilder
+	authConfigBuilder      AuthConfigBuilder
+	logger                 logr.Logger
+}
+
+func (i *defaultReferenceIndexer) BuildServiceRefIndexes(ctx context.Context, ing *networking.Ingress) []string {
+	var backends []networking.IngressBackend
+	if ing.Spec.Backend != nil {
+		backends = append(backends, *ing.Spec.Backend)
+	}
+	for _, rule := range ing.Spec.Rules {
+		if rule.HTTP == nil {
+			continue
+		}
+		for _, path := range rule.HTTP.Paths {
+			backends = append(backends, path.Backend)
+		}
+	}
+
+	serviceNames := sets.NewString()
+	for _, backend := range backends {
+		enhancedBackend, err := i.enhancedBackendBuilder.Build(ctx, ing, backend)
+		if err != nil {
+			i.logger.Error(err, "failed to build Ingress indexes",
+				"indexKey", IndexKeyServiceRefName)
+			fmt.Println(err)
+			return nil
+		}
+		serviceNamesFromBackend := extractServiceNamesFromAction(enhancedBackend.Action)
+		serviceNames.Insert(serviceNamesFromBackend...)
+	}
+	return serviceNames.List()
+}
+
+func (i *defaultReferenceIndexer) BuildSecretRefIndexes(ctx context.Context, ingOrSvc metav1.Object) []string {
+	authCfg, err := i.authConfigBuilder.Build(ctx, ingOrSvc.GetAnnotations())
+	if err != nil {
+		i.logger.Error(err, "failed to build Ingress indexes",
+			"indexKey", IndexKeySecretRefName)
+		return nil
+	}
+	return extractSecretNamesFromAuthConfig(authCfg)
+}
+
+func extractServiceNamesFromAction(action Action) []string {
+	if action.Type != ActionTypeForward || action.ForwardConfig == nil {
+		return nil
+	}
+	serviceNames := sets.NewString()
+	for _, tgt := range action.ForwardConfig.TargetGroups {
+		serviceNamesFromTGT := extractServiceNamesFromTargetGroupTuple(tgt)
+		serviceNames.Insert(serviceNamesFromTGT...)
+	}
+	return serviceNames.List()
+}
+
+func extractServiceNamesFromTargetGroupTuple(tgt TargetGroupTuple) []string {
+	if tgt.ServiceName == nil {
+		return nil
+	}
+	return []string{*tgt.ServiceName}
+}
+
+func extractSecretNamesFromAuthConfig(authCfg AuthConfig) []string {
+	if authCfg.IDPConfigOIDC == nil {
+		return nil
+	}
+	return []string{authCfg.IDPConfigOIDC.SecretName}
+}


Test patch:
diff --git a/controllers/elbv2/suite_test.go b/controllers/elbv2/suite_test.go
deleted file mode 100644
index f0ca2210e..000000000
--- a/controllers/elbv2/suite_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package controllers
-
-import (
-	"path/filepath"
-	"testing"
-
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/rest"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-	"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
-	logf "sigs.k8s.io/controller-runtime/pkg/log"
-	"sigs.k8s.io/controller-runtime/pkg/log/zap"
-
-	elbv2v1alpha1 "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1alpha1"
-	// +kubebuilder:scaffold:imports
-)
-
-// These tests use Ginkgo (BDD-style Go testing framework). Refer to
-// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
-
-var cfg *rest.Config
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestAPIs(t *testing.T) {
-	RegisterFailHandler(Fail)
-
-	RunSpecsWithDefaultAndCustomReporters(t,
-		"Controller Suite",
-		[]Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func(done Done) {
-	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
-
-	By("bootstrapping test environment")
-	testEnv = &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
-	}
-
-	var err error
-	cfg, err = testEnv.Start()
-	Expect(err).ToNot(HaveOccurred())
-	Expect(cfg).ToNot(BeNil())
-
-	err = elbv2v1alpha1.AddToScheme(scheme.Scheme)
-	Expect(err).NotTo(HaveOccurred())
-
-	// +kubebuilder:scaffold:scheme
-
-	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
-	Expect(err).ToNot(HaveOccurred())
-	Expect(k8sClient).ToNot(BeNil())
-
-	close(done)
-}, 60)
-
-var _ = AfterSuite(func() {
-	By("tearing down the test environment")
-	err := testEnv.Stop()
-	Expect(err).ToNot(HaveOccurred())
-})
diff --git a/pkg/ingress/auth_config_builder_test.go b/pkg/ingress/auth_config_builder_test.go
new file mode 100644
index 000000000..6cf3b0426
--- /dev/null
+++ b/pkg/ingress/auth_config_builder_test.go
@@ -0,0 +1,198 @@
+package ingress
+
+import (
+	"context"
+	"github.com/stretchr/testify/assert"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
+	"testing"
+)
+
+func Test_defaultAuthConfigBuilder_Build(t *testing.T) {
+	type args struct {
+		svcAndIngAnnotations map[string]string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    AuthConfig
+		wantErr error
+	}{
+		{
+			name: "cognito auth annotation",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "cognito",
+					"alb.ingress.kubernetes.io/auth-idp-cognito":                `{"userPoolARN":"arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx","userPoolClientID":"my-clientID","userPoolDomain":"my-domain","authenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeCognito,
+				IDPConfigCognito: &AuthIDPConfigCognito{
+					UserPoolARN:      "arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx",
+					UserPoolClientID: "my-clientID",
+					UserPoolDomain:   "my-domain",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "cognito auth annotation - old camelcase case json key",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "cognito",
+					"alb.ingress.kubernetes.io/auth-idp-cognito":                `{"UserPoolArn":"arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx","UserPoolClientId":"my-clientID","UserPoolDomain":"my-domain","AuthenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeCognito,
+				IDPConfigCognito: &AuthIDPConfigCognito{
+					UserPoolARN:      "arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx",
+					UserPoolClientID: "my-clientID",
+					UserPoolDomain:   "my-domain",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "oidc auth annotation - old camelcase case JSON key",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "oidc",
+					"alb.ingress.kubernetes.io/auth-idp-oidc":                   `{"issuer":"https://example.com","authorizationEndpoint":"https://authorization.example.com","tokenEndpoint":"https://token.example.com","userInfoEndpoint":"https://userinfo.example.com","secretName":"my-k8s-secret","authenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeOIDC,
+				IDPConfigOIDC: &AuthIDPConfigOIDC{
+					Issuer:                "https://example.com",
+					AuthorizationEndpoint: "https://authorization.example.com",
+					TokenEndpoint:         "https://token.example.com",
+					UserInfoEndpoint:      "https://userinfo.example.com",
+					SecretName:            "my-k8s-secret",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "oidc auth annotation - old camelcase case JSON key",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "oidc",
+					"alb.ingress.kubernetes.io/auth-idp-oidc":                   `{"Issuer":"https://example.com","AuthorizationEndpoint":"https://authorization.example.com","TokenEndpoint":"https://token.example.com","UserInfoEndpoint":"https://userinfo.example.com","SecretName":"my-k8s-secret","AuthenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeOIDC,
+				IDPConfigOIDC: &AuthIDPConfigOIDC{
+					Issuer:                "https://example.com",
+					AuthorizationEndpoint: "https://authorization.example.com",
+					TokenEndpoint:         "https://token.example.com",
+					UserInfoEndpoint:      "https://userinfo.example.com",
+					SecretName:            "my-k8s-secret",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "no auth annotation",
+			args: args{
+				svcAndIngAnnotations: map[string]string{},
+			},
+			want: AuthConfig{
+				Type:                     AuthTypeNone,
+				IDPConfigCognito:         nil,
+				IDPConfigOIDC:            nil,
+				OnUnauthenticatedRequest: defaultAuthOnUnauthenticatedRequest,
+				Scope:                    defaultAuthScope,
+				SessionCookieName:        defaultAuthSessionCookieName,
+				SessionTimeout:           defaultAuthSessionTimeout,
+			},
+		},
+		{
+			// The secret index functionality for Ingress/Service relies on this
+			// since we allow these auth annotations be configured on either Ingress/Service
+			name: "oidc configuration should still be populated even authType is unspecified",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-idp-oidc":                   `{"issuer":"https://example.com","authorizationEndpoint":"https://authorization.example.com","tokenEndpoint":"https://token.example.com","userInfoEndpoint":"https://userinfo.example.com","secretName":"my-k8s-secret","authenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeNone,
+				IDPConfigOIDC: &AuthIDPConfigOIDC{
+					Issuer:                "https://example.com",
+					AuthorizationEndpoint: "https://authorization.example.com",
+					TokenEndpoint:         "https://token.example.com",
+					UserInfoEndpoint:      "https://userinfo.example.com",
+					SecretName:            "my-k8s-secret",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+			b := &defaultAuthConfigBuilder{
+				annotationParser: annotationParser,
+			}
+			got, err := b.Build(context.Background(), tt.args.svcAndIngAnnotations)
+			if tt.wantErr != nil {
+				assert.EqualError(t, err, tt.wantErr.Error())
+			} else {
+				assert.NoError(t, err)
+				assert.Equal(t, tt.want, got)
+			}
+		})
+	}
+}
diff --git a/pkg/ingress/reference_indexer_test.go b/pkg/ingress/reference_indexer_test.go
new file mode 100644
index 000000000..97e4c8790
--- /dev/null
+++ b/pkg/ingress/reference_indexer_test.go
@@ -0,0 +1,315 @@
+package ingress
+
+import (
+	"context"
+	"github.com/stretchr/testify/assert"
+	networking "k8s.io/api/networking/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
+	"sigs.k8s.io/controller-runtime/pkg/log"
+	"testing"
+)
+
+func Test_defaultReferenceIndexer_BuildServiceRefIndexes(t *testing.T) {
+	type args struct {
+		ing *networking.Ingress
+	}
+	tests := []struct {
+		name string
+		args args
+		want []string
+	}{
+		{
+			name: "standard Ingress - with default backend",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+					Spec: networking.IngressSpec{
+						Backend: &networking.IngressBackend{
+							ServiceName: "svc-a",
+							ServicePort: intstr.FromInt(80),
+						},
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathC",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-c",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+							{
+								Host: "/hostY",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-d",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-a", "svc-b", "svc-c", "svc-d"},
+		},
+		{
+			name: "standard Ingress - without default backend",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+					Spec: networking.IngressSpec{
+						Backend: nil,
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathC",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-c",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+							{
+								Host: "/hostY",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-d",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-b", "svc-c", "svc-d"},
+		},
+		{
+			name: "empty http path are ignored",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+					Spec: networking.IngressSpec{
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: nil,
+								},
+							},
+							{
+								Host: "/hostY",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-d",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-b", "svc-d"},
+		},
+		{
+			name: "standard Ingress - actions configured via use-annotation",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+						Annotations: map[string]string{
+							"alb.ingress.kubernetes.io/actions.forward-single-svc":   `{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"svc-a","servicePort":"80"}]}}`,
+							"alb.ingress.kubernetes.io/actions.forward-multiple-svc": `{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"svc-b","servicePort":"80","weight":20},{"serviceName":"svc-c","servicePort":"80","weight":80}]}}`,
+							"alb.ingress.kubernetes.io/actions.forward-single-tg":    `{"type":"forward","forwardConfig":{"targetGroups":[{"targetGroupArn":"tg-a"}]}}`,
+							"alb.ingress.kubernetes.io/actions.forward-multiple-tg":  `{"type":"forward","forwardConfig":{"targetGroups":[{"targetGroupArn":"tg-a","weight":20},{"targetGroupArn":"tg-b","weight":20}]}}`,
+						},
+					},
+					Spec: networking.IngressSpec{
+						Backend: &networking.IngressBackend{
+							ServiceName: "forward-single-svc",
+							ServicePort: intstr.FromString("use-annotation"),
+						},
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "forward-multiple-svc",
+													ServicePort: intstr.FromString("use-annotation"),
+												},
+											},
+											{
+												Path: "/pathC",
+												Backend: networking.IngressBackend{
+													ServiceName: "forward-single-tg",
+													ServicePort: intstr.FromString("use-annotation"),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "forward-multiple-tg",
+													ServicePort: intstr.FromString("use-annotation"),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-a", "svc-b", "svc-c"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+			authConfigBuilder := NewDefaultAuthConfigBuilder(annotationParser)
+			enhancedBackendBuilder := NewDefaultEnhancedBackendBuilder(annotationParser)
+			i := &defaultReferenceIndexer{
+				enhancedBackendBuilder: enhancedBackendBuilder,
+				authConfigBuilder:      authConfigBuilder,
+				logger:                 &log.NullLogger{},
+			}
+			got := i.BuildServiceRefIndexes(context.Background(), tt.args.ing)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func Test_defaultReferenceIndexer_BuildSecretRefIndexes(t *testing.T) {
+	type args struct {
+		ingOrSvc metav1.Object
+	}
+	tests := []struct {
+		name string
+		args args
+		want []string
+	}{
+		{
+			name: "ingress with AuthOIDC annotation",
+			args: args{
+				ingOrSvc: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+						Annotations: map[string]string{
+							"alb.ingress.kubernetes.io/auth-idp-oidc": `{"issuer":"https://example.com","authorizationEndpoint":"https://authorization.example.com","tokenEndpoint":"https://token.example.com","userInfoEndpoint":"https://userinfo.example.com","secretName":"my-k8s-secret","authenticationRequestExtraParams":{"key":"value"}}`,
+						},
+					},
+				},
+			},
+			want: []string{"my-k8s-secret"},
+		},
+		{
+			name: "ingress with no annotation",
+			args: args{
+				ingOrSvc: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+				},
+			},
+			want: nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+			authConfigBuilder := NewDefaultAuthConfigBuilder(annotationParser)
+			enhancedBackendBuilder := NewDefaultEnhancedBackendBuilder(annotationParser)
+			i := &defaultReferenceIndexer{
+				enhancedBackendBuilder: enhancedBackendBuilder,
+				authConfigBuilder:      authConfigBuilder,
+				logger:                 &log.NullLogger{},
+			}
+			got := i.BuildSecretRefIndexes(context.Background(), tt.args.ingOrSvc)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}