Repository: envoyproxy/gateway
Pull request #771 (2022-12-02 17:02:38)

Adds Step to Quickstart
Adds a step to wait for Envoy Gateway to become available before creating Gateway API resources.

Signed-off-by: danehans <daneyonhansen@gmail.com>


Patch:
diff --git a/docs/v0.2.0/user/quickstart.md b/docs/v0.2.0/user/quickstart.md
index 46da189d2..02da2b769 100644
--- a/docs/v0.2.0/user/quickstart.md
+++ b/docs/v0.2.0/user/quickstart.md
@@ -16,6 +16,12 @@ Install the Gateway API CRDs and Envoy Gateway:
 kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/install.yaml
 ```
 
+Wait for Envoy Gateway to become available:
+
+```shell
+kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available
+```
+
 Install the GatewayClass, Gateway, HTTPRoute and example app:
 
 ```shell
diff --git a/internal/cmd/server.go b/internal/cmd/server.go
index 87aaf21fa..446f4f09f 100644
--- a/internal/cmd/server.go
+++ b/internal/cmd/server.go
@@ -151,16 +151,9 @@ func setupRunners(cfg *config.Server) error {
 	// Wait until done
 	<-ctx.Done()
 	// Close messages
-	pResources.GatewayClasses.Close()
-	pResources.Gateways.Close()
-	pResources.HTTPRoutes.Close()
-	pResources.Services.Close()
-	pResources.Secrets.Close()
-	pResources.ReferenceGrants.Close()
-	pResources.Namespaces.Close()
+	pResources.GatewayAPIResources.Close()
 	pResources.GatewayStatuses.Close()
 	pResources.HTTPRouteStatuses.Close()
-	pResources.TLSRoutes.Close()
 	pResources.TLSRouteStatuses.Close()
 	xdsIR.Close()
 	infraIR.Close()
diff --git a/internal/gatewayapi/helpers_v1alpha2.go b/internal/gatewayapi/helpers_v1alpha2.go
index 1dc934a27..49213031e 100644
--- a/internal/gatewayapi/helpers_v1alpha2.go
+++ b/internal/gatewayapi/helpers_v1alpha2.go
@@ -137,6 +137,55 @@ func DowngradeRouteParentStatuses(routeParentStatuses []v1beta1.RouteParentStatu
 	return res
 }
 
+// UpgradeBackendRef converts v1alpha2.BackendRef to v1beta1.BackendRef
+func UpgradeBackendRef(old v1alpha2.BackendRef) v1beta1.BackendRef {
+	upgraded := v1beta1.BackendRef{}
+
+	if old.Group != nil {
+		upgraded.Group = GroupPtr(string(*old.Group))
+	}
+
+	if old.Kind != nil {
+		upgraded.Kind = KindPtr(string(*old.Kind))
+	}
+
+	if old.Namespace != nil {
+		upgraded.Namespace = NamespacePtr(string(*old.Namespace))
+	}
+
+	upgraded.Name = v1beta1.ObjectName(old.Name)
+
+	if old.Port != nil {
+		upgraded.Port = PortNumPtr(int32(*old.Port))
+	}
+
+	return upgraded
+}
+
+func DowngradeBackendRef(old v1beta1.BackendRef) v1alpha2.BackendRef {
+	downgraded := v1alpha2.BackendRef{}
+
+	if old.Group != nil {
+		downgraded.Group = GroupPtrV1Alpha2(string(*old.Group))
+	}
+
+	if old.Kind != nil {
+		downgraded.Kind = KindPtrV1Alpha2(string(*old.Kind))
+	}
+
+	if old.Namespace != nil {
+		downgraded.Namespace = NamespacePtrV1Alpha2(string(*old.Namespace))
+	}
+
+	downgraded.Name = v1alpha2.ObjectName(old.Name)
+
+	if old.Port != nil {
+		downgraded.Port = PortNumPtrV1Alpha2(int(*old.Port))
+	}
+
+	return downgraded
+}
+
 func NamespaceDerefOrAlpha(namespace *v1alpha2.Namespace, defaultNamespace string) string {
 	if namespace != nil && *namespace != "" {
 		return string(*namespace)
diff --git a/internal/gatewayapi/runner/runner.go b/internal/gatewayapi/runner/runner.go
index 091b92bf6..290192779 100644
--- a/internal/gatewayapi/runner/runner.go
+++ b/internal/gatewayapi/runner/runner.go
@@ -45,52 +45,20 @@ func (r *Runner) Start(ctx context.Context) error {
 }
 
 func (r *Runner) subscribeAndTranslate(ctx context.Context) {
-	// Subscribe to resources
-	gatewayClassesCh := r.ProviderResources.GatewayClasses.Subscribe(ctx)
-	gatewaysCh := r.ProviderResources.Gateways.Subscribe(ctx)
-	secretsCh := r.ProviderResources.Secrets.Subscribe(ctx)
-	refGrantsCh := r.ProviderResources.ReferenceGrants.Subscribe(ctx)
-	httpRoutesCh := r.ProviderResources.HTTPRoutes.Subscribe(ctx)
-	tlsRoutesCh := r.ProviderResources.TLSRoutes.Subscribe(ctx)
-	servicesCh := r.ProviderResources.Services.Subscribe(ctx)
-	namespacesCh := r.ProviderResources.Namespaces.Subscribe(ctx)
-
-	for ctx.Err() == nil {
-		var in gatewayapi.Resources
-		// Receive subscribed resource notifications
-		select {
-		case <-gatewayClassesCh:
-		case <-gatewaysCh:
-		case <-secretsCh:
-		case <-refGrantsCh:
-		case <-httpRoutesCh:
-		case <-tlsRoutesCh:
-		case <-servicesCh:
-		case <-namespacesCh:
-		}
-		r.Logger.Info("received a notification")
-		// Load all resources required for translation
-		in.Gateways = r.ProviderResources.GetGateways()
-		in.Secrets = r.ProviderResources.GetSecrets()
-		in.ReferenceGrants = r.ProviderResources.GetReferenceGrants()
-		in.HTTPRoutes = r.ProviderResources.GetHTTPRoutes()
-		in.TLSRoutes = r.ProviderResources.GetTLSRoutes()
-		in.Services = r.ProviderResources.GetServices()
-		in.Namespaces = r.ProviderResources.GetNamespaces()
-		gatewayClasses := r.ProviderResources.GetGatewayClasses()
-		// Fetch the first gateway class since there should be only 1
-		// gateway class linked to this controller
-		switch {
-		case gatewayClasses == nil:
-			// Envoy Gateway startup.
-			continue
-		default:
+	message.HandleSubscription(r.ProviderResources.GatewayAPIResources.Subscribe(ctx),
+		func(update message.Update[string, *gatewayapi.Resources]) {
+			val := update.Value
+
+			if update.Delete || val == nil {
+				return
+			}
+
 			// Translate and publish IRs.
 			t := &gatewayapi.Translator{
-				GatewayClassName: v1beta1.ObjectName(gatewayClasses[0].GetName()),
+				GatewayClassName: v1beta1.ObjectName(update.Key),
 			}
 			// Translate to IR
-			result := t.Translate(&in)
+			result := t.Translate(val)
 
 			yamlXdsIR, _ := yaml.Marshal(&result.XdsIR)
 			r.Logger.WithValues("output", "xds-ir").Info(string(yamlXdsIR))
@@ -143,8 +111,8 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) {
 				key := utils.NamespacedName(tlsRoute)
 				r.ProviderResources.TLSRouteStatuses.Store(key, tlsRoute)
 			}
-		}
-	}
+		},
+	)
 	r.Logger.Info("shutting down")
 }
 
diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go
index 7a3c6b33f..d50545266 100644
--- a/internal/gatewayapi/translator.go
+++ b/internal/gatewayapi/translator.go
@@ -48,6 +48,7 @@ type InfraIRMap map[string]*ir.Infra
 
 // Resources holds the Gateway API and related
 // resources that the translators needs as inputs.
+// +k8s:deepcopy-gen=true
 type Resources struct {
 	Gateways        []*v1beta1.Gateway
 	HTTPRoutes      []*v1beta1.HTTPRoute
diff --git a/internal/gatewayapi/zz_generated.deepcopy.go b/internal/gatewayapi/zz_generated.deepcopy.go
new file mode 100644
index 000000000..8c553492e
--- /dev/null
+++ b/internal/gatewayapi/zz_generated.deepcopy.go
@@ -0,0 +1,109 @@
+//go:build !ignore_autogenerated
+// +build !ignore_autogenerated
+
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+// Code generated by controller-gen. DO NOT EDIT.
+
+package gatewayapi
+
+import (
+	"k8s.io/api/core/v1"
+	"sigs.k8s.io/gateway-api/apis/v1alpha2"
+	"sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Resources) DeepCopyInto(out *Resources) {
+	*out = *in
+	if in.Gateways != nil {
+		in, out := &in.Gateways, &out.Gateways
+		*out = make([]*v1beta1.Gateway, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(v1beta1.Gateway)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	if in.HTTPRoutes != nil {
+		in, out := &in.HTTPRoutes, &out.HTTPRoutes
+		*out = make([]*v1beta1.HTTPRoute, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(v1beta1.HTTPRoute)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	if in.TLSRoutes != nil {
+		in, out := &in.TLSRoutes, &out.TLSRoutes
+		*out = make([]*v1alpha2.TLSRoute, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(v1alpha2.TLSRoute)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	if in.ReferenceGrants != nil {
+		in, out := &in.ReferenceGrants, &out.ReferenceGrants
+		*out = make([]*v1alpha2.ReferenceGrant, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(v1alpha2.ReferenceGrant)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	if in.Namespaces != nil {
+		in, out := &in.Namespaces, &out.Namespaces
+		*out = make([]*v1.Namespace, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(v1.Namespace)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	if in.Services != nil {
+		in, out := &in.Services, &out.Services
+		*out = make([]*v1.Service, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(v1.Service)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	if in.Secrets != nil {
+		in, out := &in.Secrets, &out.Secrets
+		*out = make([]*v1.Secret, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(v1.Secret)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources.
+func (in *Resources) DeepCopy() *Resources {
+	if in == nil {
+		return nil
+	}
+	out := new(Resources)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/internal/infrastructure/kubernetes/bootstrap.yaml.tpl b/internal/infrastructure/kubernetes/bootstrap.yaml.tpl
index 1c231ab35..6b3364b93 100644
--- a/internal/infrastructure/kubernetes/bootstrap.yaml.tpl
+++ b/internal/infrastructure/kubernetes/bootstrap.yaml.tpl
@@ -1,5 +1,9 @@
 admin:
-  access_log_path: {{ .AdminServer.AccessLogPath }}
+  access_log:
+  - name: envoy.access_loggers.file
+    typed_config:
+      "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
+      path: {{ .AdminServer.AccessLogPath }}
   address:
     socket_address:
       address: {{ .AdminServer.Address }}
@@ -38,7 +42,11 @@ static_resources:
               socket_address:
                 address: {{ .XdsServer.Address }}
                 port_value: {{ .XdsServer.Port }}
-    http2_protocol_options: {}
+    typed_extension_protocol_options:
+      "envoy.extensions.upstreams.http.v3.HttpProtocolOptions":
+         "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions"
+         "explicit_http_config":
+           "http2_protocol_options": {}
     name: xds_cluster
     type: STRICT_DNS
     transport_socket:
diff --git a/internal/message/types.go b/internal/message/types.go
index e986d5762..827ce7d53 100644
--- a/internal/message/types.go
+++ b/internal/message/types.go
@@ -7,120 +7,44 @@ package message
 
 import (
 	"github.com/telepresenceio/watchable"
-	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
 	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
 
+	"github.com/envoyproxy/gateway/internal/gatewayapi"
 	"github.com/envoyproxy/gateway/internal/ir"
 	xdstypes "github.com/envoyproxy/gateway/internal/xds/types"
 )
 
 // ProviderResources message
 type ProviderResources struct {
-	GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass]
-	Gateways       watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway]
-	HTTPRoutes     watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute]
-	TLSRoutes      watchable.Map[types.NamespacedName, *gwapiv1a2.TLSRoute]
-	Namespaces     watchable.Map[string, *corev1.Namespace]
-	Services       watchable.Map[types.NamespacedName, *corev1.Service]
-	Secrets        watchable.Map[types.NamespacedName, *corev1.Secret]
-
-	ReferenceGrants watchable.Map[types.NamespacedName, *gwapiv1a2.ReferenceGrant]
+	// GatewayAPIResources is a map from a GatewayClass name to
+	// a group of gateway API resources.
+	GatewayAPIResources watchable.Map[string, *gatewayapi.Resources]
 
 	GatewayStatuses   watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway]
 	HTTPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute]
 	TLSRouteStatuses  watchable.Map[types.NamespacedName, *gwapiv1a2.TLSRoute]
 }
 
-func (p *ProviderResources) GetGatewayClasses() []*gwapiv1b1.GatewayClass {
-	if p.GatewayClasses.Len() == 0 {
-		return nil
-	}
-
-	res := make([]*gwapiv1b1.GatewayClass, 0, p.GatewayClasses.Len())
-	for _, v := range p.GatewayClasses.LoadAll() {
-		res = append(res, v)
-	}
-	return res
-}
-
-func (p *ProviderResources) GetGateways() []*gwapiv1b1.Gateway {
-	if p.Gateways.Len() == 0 {
-		return nil
-	}
-	res := make([]*gwapiv1b1.Gateway, 0, p.Gateways.Len())
-	for _, v := range p.Gateways.LoadAll() {
-		res = append(res, v)
-	}
-	return res
-}
-
-func (p *ProviderResources) GetHTTPRoutes() []*gwapiv1b1.HTTPRoute {
-	if p.HTTPRoutes.Len() == 0 {
-		return nil
-	}
-	res := make([]*gwapiv1b1.HTTPRoute, 0, p.HTTPRoutes.Len())
-	for _, v := range p.HTTPRoutes.LoadAll() {
-		res = append(res, v)
-	}
-	return res
-}
-
-func (p *ProviderResources) GetTLSRoutes() []*gwapiv1a2.TLSRoute {
-	if p.TLSRoutes.Len() == 0 {
+func (p *ProviderResources) GetResources() *gatewayapi.Resources {
+	if p.GatewayAPIResources.Len() == 0 {
 		return nil
 	}
-	res := make([]*gwapiv1a2.TLSRoute, 0, p.TLSRoutes.Len())
-	for _, v := range p.TLSRoutes.LoadAll() {
-		res = append(res, v)
+	for _, v := range p.GatewayAPIResources.LoadAll() {
+		return v
 	}
-	return res
+	return nil
 }
 
-func (p *ProviderResources) GetNamespaces() []*corev1.Namespace {
-	if p.Namespaces.Len() == 0 {
-		return nil
-	}
-
-	res := make([]*corev1.Namespace, 0, p.Namespaces.Len())
-	for _, v := range p.Namespaces.LoadAll() {
-		res = append(res, v)
-	}
-	return res
-}
-
-func (p *ProviderResources) GetServices() []*corev1.Service {
-	if p.Services.Len() == 0 {
-		return nil
-	}
-	res := make([]*corev1.Service, 0, p.Services.Len())
-	for _, v := range p.Services.LoadAll() {
-		res = append(res, v)
-	}
-	return res
-}
-
-func (p *ProviderResources) GetSecrets() []*corev1.Secret {
-	if p.Secrets.Len() == 0 {
-		return nil
-	}
-	res := make([]*corev1.Secret, 0, p.Secrets.Len())
-	for _, v := range p.Secrets.LoadAll() {
-		res = append(res, v)
-	}
-	return res
-}
-
-func (p *ProviderResources) GetReferenceGrants() []*gwapiv1a2.ReferenceGrant {
-	if p.ReferenceGrants.Len() == 0 {
-		return nil
+func (p *ProviderResources) GetResourcesKey() string {
+	if p.GatewayAPIResources.Len() == 0 {
+		return ""
 	}
-	res := make([]*gwapiv1a2.ReferenceGrant, 0, p.ReferenceGrants.Len())
-	for _, v := range p.ReferenceGrants.LoadAll() {
-		res = append(res, v)
+	for k := range p.GatewayAPIResources.LoadAll() {
+		return k
 	}
-	return res
+	return ""
 }
 
 // XdsIR message
diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go
new file mode 100644
index 000000000..a4fe3a252
--- /dev/null
+++ b/internal/provider/kubernetes/controller.go
@@ -0,0 +1,736 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package kubernetes
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/gatewayapi"
+	"github.com/envoyproxy/gateway/internal/message"
+	"github.com/envoyproxy/gateway/internal/provider/utils"
+	"github.com/envoyproxy/gateway/internal/status"
+	"github.com/envoyproxy/gateway/internal/utils/slice"
+
+	"github.com/go-logr/logr"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/fields"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/util/retry"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/controller"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+	"sigs.k8s.io/controller-runtime/pkg/source"
+	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
+	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+const (
+	classGatewayIndex        = "classGatewayIndex"
+	gatewayTLSRouteIndex     = "gatewayTLSRouteIndex"
+	gatewayHTTPRouteIndex    = "gatewayHTTPRouteIndex"
+	secretGatewayIndex       = "secretGatewayIndex"
+	targetRefGrantRouteIndex = "targetRefGrantRouteIndex"
+	serviceHTTPRouteIndex    = "serviceHTTPRouteIndex"
+	serviceTLSRouteIndex     = "serviceTLSRouteIndex"
+)
+
+type gatewayAPIReconciler struct {
+	client          client.Client
+	log             logr.Logger
+	statusUpdater   status.Updater
+	classController gwapiv1b1.GatewayController
+
+	resources *message.ProviderResources
+}
+
+// newGatewayAPIController
+func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources) error {
+	ctx := context.Background()
+
+	r := &gatewayAPIReconciler{
+		client:          mgr.GetClient(),
+		log:             cfg.Logger,
+		classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName),
+		statusUpdater:   su,
+		resources:       resources,
+	}
+
+	c, err := controller.New("gatewayapi", mgr, controller.Options{Reconciler: r})
+	if err != nil {
+		return err
+	}
+	r.log.Info("created gatewayapi controller")
+
+	// Subscribe to status updates
+	r.subscribeAndUpdateStatus(ctx)
+
+	// Only enqueue GatewayClass objects that match this Envoy Gateway's controller name.
+	if err := c.Watch(
+		&source.Kind{Type: &gwapiv1b1.GatewayClass{}},
+		&handler.EnqueueRequestForObject{},
+		predicate.NewPredicateFuncs(r.hasMatchingController),
+	); err != nil {
+		return err
+	}
+
+	// Watch Gateway CRUDs and reconcile affected GatewayClass.
+	if err := c.Watch(
+		&source.Kind{Type: &gwapiv1b1.Gateway{}},
+		&handler.EnqueueRequestForObject{},
+		predicate.NewPredicateFuncs(r.validateGatewayForReconcile),
+	); err != nil {
+		return err
+	}
+	if err := addGatewayIndexers(ctx, mgr); err != nil {
+		return err
+	}
+
+	// Watch HTTPRoute CRUDs and process affected Gateways.
+	if err := c.Watch(
+		&source.Kind{Type: &gwapiv1b1.HTTPRoute{}},
+		&handler.EnqueueRequestForObject{},
+	); err != nil {
+		return err
+	}
+	if err := addHTTPRouteIndexers(ctx, mgr); err != nil {
+		return err
+	}
+
+	// Watch TLSRoute CRUDs and process affected Gateways.
+	if err := c.Watch(
+		&source.Kind{Type: &gwapiv1a2.TLSRoute{}},
+		&handler.EnqueueRequestForObject{},
+	); err != nil {
+		return err
+	}
+	if err := addTLSRouteIndexers(ctx, mgr); err != nil {
+		return err
+	}
+
+	// Watch Service CRUDs and process affected *Route objects.
+	if err := c.Watch(
+		&source.Kind{Type: &corev1.Service{}},
+		&handler.EnqueueRequestForObject{},
+		predicate.NewPredicateFuncs(r.validateServiceForReconcile)); err != nil {
+		return err
+	}
+
+	// Watch Secret CRUDs and process affected Gateways.
+	if err := c.Watch(
+		&source.Kind{Type: &corev1.Secret{}},
+		&handler.EnqueueRequestForObject{},
+		predicate.NewPredicateFuncs(r.validateSecretForReconcile),
+	); err != nil {
+		return err
+	}
+
+	// Watch ReferenceGrant CRUDs and process affected Gateways.
+	if err := c.Watch(
+		&source.Kind{Type: &gwapiv1a2.ReferenceGrant{}},
+		&handler.EnqueueRequestForObject{},
+	); err != nil {
+		return err
+	}
+	if err := addReferenceGrantIndexers(ctx, mgr); err != nil {
+		return err
+	}
+
+	// Watch Deployment CRUDs and process affected Gateways.
+	if err := c.Watch(
+		&source.Kind{Type: &appsv1.Deployment{}},
+		&handler.EnqueueRequestForObject{},
+		predicate.NewPredicateFuncs(r.validateDeploymentForReconcile),
+	); err != nil {
+		return err
+	}
+
+	r.log.Info("watching gatewayAPI related objects")
+	return nil
+}
+
+type resourceMappings struct {
+	// Map for storing namespaces for Route, Service and Gateway objects.
+	allAssociatedNamespaces map[string]struct{}
+	// Map for storing service NamespaceNames referred by various Route objects.
+	allAssociatedBackendRefs map[types.NamespacedName]struct{}
+	// Map for storing referenceGrant NamespaceNames for BackendRefs, SecretRefs.
+	allAssociatedRefGrants map[types.NamespacedName]*gwapiv1a2.ReferenceGrant
+}
+
+func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
+	r.log.WithName(request.Name).Info("reconciling gatewayAPI object", "namespace", request.Namespace, "name", request.Name)
+
+	var gatewayClasses gwapiv1b1.GatewayClassList
+	if err := r.client.List(ctx, &gatewayClasses); err != nil {
+		return reconcile.Result{}, fmt.Errorf("error listing gatewayclasses: %v", err)
+	}
+
+	var cc controlledClasses
+
+	for _, gwClass := range gatewayClasses.Items {
+		gwClass := gwClass
+		if gwClass.Spec.ControllerName == r.classController {
+			// The gatewayclass was marked for deletion and the finalizer removed,
+			// so clean-up dependents.
+			if !gwClass.DeletionTimestamp.IsZero() &&
+				!slice.ContainsString(gwClass.Finalizers, gatewayClassFinalizer) {
+				r.log.Info("gatewayclass marked for deletion")
+				cc.removeMatch(&gwClass)
+
+				// Delete the gatewayclass from the watchable map.
+				r.resources.GatewayAPIResources.Delete(gwClass.Name)
+				continue
+			}
+
+			cc.addMatch(&gwClass)
+		}
+	}
+
+	// The gatewayclass was already deleted/finalized and there are stale queue entries.
+	acceptedGC := cc.acceptedClass()
+	if acceptedGC == nil {
+		r.log.Info("failed to find an accepted gatewayclass")
+		return reconcile.Result{}, nil
+	}
+
+	updater := func(gc *gwapiv1b1.GatewayClass, accepted bool) error {
+		if r.statusUpdater != nil {
+			r.statusUpdater.Send(status.Update{
+				NamespacedName: types.NamespacedName{Name: gc.Name},
+				Resource:       &gwapiv1b1.GatewayClass{},
+				Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+					gc, ok := obj.(*gwapiv1b1.GatewayClass)
+					if !ok {
+						panic(fmt.Sprintf("unsupported object type %T", obj))
+					}
+
+					return status.SetGatewayClassAccepted(gc.DeepCopy(), accepted)
+				}),
+			})
+		} else {
+			// this branch makes testing easier by not going through the status.Updater.
+			copy := status.SetGatewayClassAccepted(gc.DeepCopy(), accepted)
+
+			if err := r.client.Status().Update(ctx, copy); err != nil && !kerrors.IsNotFound(err) {
+				return fmt.Errorf("error updating status of gatewayclass %s: %w", copy.Name, err)
+			}
+		}
+		return nil
+	}
+
+	// Update status for all gateway classes
+	for _, gc := range cc.notAcceptedClasses() {
+		if err := updater(gc, false); err != nil {
+			r.resources.GatewayAPIResources.Delete(acceptedGC.Name)
+			return reconcile.Result{}, err
+		}
+	}
+
+	resourceTree := &gatewayapi.Resources{
+		Gateways:        []*gwapiv1b1.Gateway{},
+		HTTPRoutes:      []*gwapiv1b1.HTTPRoute{},
+		TLSRoutes:       []*gwapiv1a2.TLSRoute{},
+		Services:        []*corev1.Service{},
+		Secrets:         []*corev1.Secret{},
+		ReferenceGrants: []*gwapiv1a2.ReferenceGrant{},
+		Namespaces:      []*corev1.Namespace{},
+	}
+
+	resourceMap := &resourceMappings{
+		allAssociatedNamespaces:  map[string]struct{}{},
+		allAssociatedBackendRefs: map[types.NamespacedName]struct{}{},
+		allAssociatedRefGrants:   map[types.NamespacedName]*gwapiv1a2.ReferenceGrant{},
+	}
+
+	// Find gateways for the acceptedGC
+	// Find the Gateways that reference this Class.
+	gatewayList := &gwapiv1b1.GatewayList{}
+	if err := r.client.List(ctx, gatewayList, &client.ListOptions{
+		FieldSelector: fields.OneTermEqualSelector(classGatewayIndex, acceptedGC.Name),
+	}); err != nil {
+		r.log.Info("no associated Gateways found for GatewayClass", "name", acceptedGC.Name)
+		return reconcile.Result{}, err
+	}
+
+	for _, gtw := range gatewayList.Items {
+		gtw := gtw
+		r.log.Info("processing Gateway", "namespace", gtw.Namespace, "name", gtw.Name)
+		resourceMap.allAssociatedNamespaces[gtw.Namespace] = struct{}{}
+
+		for _, listener := range gtw.Spec.Listeners {
+			listener := listener
+			// Get Secret for gateway if it exists.
+			if terminatesTLS(&listener) {
+				for _, certRef := range listener.TLS.CertificateRefs {
+					certRef := certRef
+					if refsSecret(&certRef) {
+						secret := new(corev1.Secret)
+						secretNamespace := gatewayapi.NamespaceDerefOr(certRef.Namespace, gtw.Namespace)
+						err := r.client.Get(ctx,
+							types.NamespacedName{Namespace: secretNamespace, Name: string(certRef.Name)},
+							secret,
+						)
+						if err != nil && !kerrors.IsNotFound(err) {
+							r.log.Error(err, "unable to find Secret")
+							return reconcile.Result{}, err
+						}
+
+						r.log.Info("processing Secret", "namespace", secretNamespace, "name", string(certRef.Name))
+
+						if secretNamespace != gtw.Namespace {
+							from := ObjectKindNamespacedName{kind: gatewayapi.KindGateway, namespace: gtw.Namespace, name: gtw.Name}
+							to := ObjectKindNamespacedName{kind: gatewayapi.KindSecret, namespace: secretNamespace, name: string(certRef.Name)}
+							refGrant, err := r.findReferenceGrant(ctx, from, to)
+							if err != nil {
+								r.log.Error(err, "unable to find ReferenceGrant that links the Secret to Gateway")
+								continue
+							}
+
+							resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant
+						}
+
+						resourceMap.allAssociatedNamespaces[secretNamespace] = struct{}{}
+						resourceTree.Secrets = append(resourceTree.Secrets, secret)
+					}
+				}
+			}
+		}
+
+		// Route Processing
+		// Get TLSRoute objects and check if it exists.
+		if err := r.processTLSRoutes(ctx, utils.NamespacedName(&gtw).String(), resourceMap, resourceTree); err != nil {
+			return reconcile.Result{}, err
+		}
+
+		// Get HTTPRoute objects and check if it exists.
+		if err := r.processHTTPRoutes(ctx, utils.NamespacedName(&gtw).String(), resourceMap, resourceTree); err != nil {
+			return reconcile.Result{}, err
+		}
+
+		resourceTree.Gateways = append(resourceTree.Gateways, &gtw)
+	}
+
+	for serviceNamespaceName := range resourceMap.allAssociatedBackendRefs {
+		r.log.Info("processing Service", "namespace", serviceNamespaceName.Namespace,
+			"name", serviceNamespaceName.Name)
+
+		service := new(corev1.Service)
+		err := r.client.Get(ctx, serviceNamespaceName, service)
+		if err != nil {
+			r.log.Error(err, "unable to find associated Services")
+			if kerrors.IsNotFound(err) {
+				return reconcile.Result{}, nil
+			}
+			return reconcile.Result{}, err
+		}
+
+		resourceMap.allAssociatedNamespaces[service.Namespace] = struct{}{}
+		resourceTree.Services = append(resourceTree.Services, service)
+	}
+
+	// Add all ReferenceGrants to the resourceTree
+	for _, referenceGrant := range resourceMap.allAssociatedRefGrants {
+		resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, referenceGrant)
+	}
+
+	// For this particular Gateway, and all associated objects, check whether the
+	// namespace exists. Add to the resourceTree.
+	for ns := range resourceMap.allAssociatedNamespaces {
+		namespace, err := r.getNamespace(ctx, ns)
+		if err != nil {
+			r.log.Error(err, "unable to find the namespace")
+			if kerrors.IsNotFound(err) {
+				return reconcile.Result{}, nil
+			}
+			return reconcile.Result{}, err
+		}
+
+		resourceTree.Namespaces = append(resourceTree.Namespaces, namespace)
+	}
+
+	if err := updater(acceptedGC, true); err != nil {
+		r.log.Error(err, "unable to update GatewayClass status")
+		return reconcile.Result{}, err
+	}
+
+	// Update finalizer on the gateway class based on the resource tree.
+	if len(resourceTree.Gateways) == 0 {
+		r.log.Info("No gateways found for accepted gatewayclass")
+
+		// If needed, remove the finalizer from the accepted GatewayClass.
+		if err := r.removeFinalizer(ctx, acceptedGC); err != nil {
+			r.log.Error(err, fmt.Sprintf("failed to remove finalizer from gatewayclass %s",
+				acceptedGC.Name))
+			return reconcile.Result{}, err
+		}
+	} else {
+		// finalize the accepted GatewayClass.
+		if err := r.addFinalizer(ctx, acceptedGC); err != nil {
+			r.log.Error(err, fmt.Sprintf("failed adding finalizer to gatewayclass %s",
+				acceptedGC.Name))
+			return reconcile.Result{}, err
+		}
+	}
+
+	// The Store is triggered even when there are no Gateways associated to the
+	// GatewayClass. This would happen in case the last Gateway is removed and the
+	// Store will be required to trigger a cleanup of envoy infra resources.
+	r.resources.GatewayAPIResources.Store(acceptedGC.Name, resourceTree)
+
+	r.log.WithName(request.Name).Info("reconciled gatewayAPI object successfully", "namespace", request.Namespace, "name", request.Name)
+	return reconcile.Result{}, nil
+}
+
+func (r *gatewayAPIReconciler) getNamespace(ctx context.Context, name string) (*corev1.Namespace, error) {
+	nsKey := types.NamespacedName{Name: name}
+	ns := new(corev1.Namespace)
+	if err := r.client.Get(ctx, nsKey, ns); err != nil {
+		r.log.Error(err, "unable to get Namespace")
+		return nil, err
+	}
+	return ns, nil
+}
+
+func (r *gatewayAPIReconciler) statusUpdateForGateway(gtw *gwapiv1b1.Gateway, svc *corev1.Service, deploy *appsv1.Deployment) {
+	// update scheduled condition
+	status.UpdateGatewayStatusScheduledCondition(gtw, true)
+	// update address field and ready condition
+	status.UpdateGatewayStatusReadyCondition(gtw, svc, deploy)
+
+	key := utils.NamespacedName(gtw)
+	// publish status
+	// do it inline since this code flow updates the
+	// Status.Addresses field whereas the message bus / subscriber
+	// does not.
+	r.statusUpdater.Send(status.Update{
+		NamespacedName: key,
+		Resource:       new(gwapiv1b1.Gateway),
+		Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+			g, ok := obj.(*gwapiv1b1.Gateway)
+			if !ok {
+				panic(fmt.Sprintf("unsupported object type %T", obj))
+			}
+			gCopy := g.DeepCopy()
+			gCopy.Status.Conditions = status.MergeConditions(gCopy.Status.Conditions, gtw.Status.Conditions...)
+			gCopy.Status.Addresses = gtw.Status.Addresses
+			return gCopy
+		}),
+	})
+}
+
+func (r *gatewayAPIReconciler) findReferenceGrant(ctx context.Context, from, to ObjectKindNamespacedName) (*gwapiv1a2.ReferenceGrant, error) {
+	refGrantList := new(gwapiv1a2.ReferenceGrantList)
+	if err := r.client.List(ctx, refGrantList, &client.ListOptions{
+		FieldSelector: fields.OneTermEqualSelector(targetRefGrantRouteIndex, to.kind),
+	}); err != nil {
+		return nil, err
+	}
+
+	for _, refGrant := range refGrantList.Items {
+		if refGrant.Namespace == to.namespace {
+			for _, source := range refGrant.Spec.From {
+				if source.Kind == gwapiv1a2.Kind(from.kind) && string(source.Namespace) == from.namespace {
+					return &refGrant, nil
+				}
+			}
+		}
+	}
+
+	return nil, fmt.Errorf("no reference grants found that target %s in namespace %s for kind %s in namespace %s",
+		to.kind, to.namespace, from.kind, from.namespace)
+}
+
+func addReferenceGrantIndexers(ctx context.Context, mgr manager.Manager) error {
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.ReferenceGrant{}, targetRefGrantRouteIndex, func(rawObj client.Object) []string {
+		refGrant := rawObj.(*gwapiv1a2.ReferenceGrant)
+		var referredServices []string
+		for _, target := range refGrant.Spec.To {
+			referredServices = append(referredServices, string(target.Kind))
+		}
+		return referredServices
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+// addHTTPRouteIndexers adds indexing on HTTPRoute, for Service objects that are
+// referenced in HTTPRoute objects via `.spec.rules.backendRefs`. This helps in
+// querying for HTTPRoutes that are affected by a particular Service CRUD.
+func addHTTPRouteIndexers(ctx context.Context, mgr manager.Manager) error {
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.HTTPRoute{}, gatewayHTTPRouteIndex, func(rawObj client.Object) []string {
+		httproute := rawObj.(*gwapiv1b1.HTTPRoute)
+		var gateways []string
+		for _, parent := range httproute.Spec.ParentRefs {
+			if string(*parent.Kind) == gatewayapi.KindGateway {
+				// If an explicit Gateway namespace is not provided, use the HTTPRoute namespace to
+				// lookup the provided Gateway Name.
+				gateways = append(gateways,
+					types.NamespacedName{
+						Namespace: gatewayapi.NamespaceDerefOr(parent.Namespace, httproute.Namespace),
+						Name:      string(parent.Name),
+					}.String(),
+				)
+			}
+		}
+		return gateways
+	}); err != nil {
+		return err
+	}
+
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.HTTPRoute{}, serviceHTTPRouteIndex, func(rawObj client.Object) []string {
+		httproute := rawObj.(*gwapiv1b1.HTTPRoute)
+		var services []string
+		for _, rule := range httproute.Spec.Rules {
+			for _, backend := range rule.BackendRefs {
+				if string(*backend.Kind) == gatewayapi.KindService {
+					// If an explicit Service namespace is not provided, use the HTTPRoute namespace to
+					// lookup the provided Gateway Name.
+					services = append(services,
+						types.NamespacedName{
+							Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, httproute.Namespace),
+							Name:      string(backend.Name),
+						}.String(),
+					)
+				}
+			}
+		}
+		return services
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+// addTLSRouteIndexers adds indexing on TLSRoute, for Service objects that are
+// referenced in TLSRoute objects via `.spec.rules.backendRefs`. This helps in
+// querying for TLSRoutes that are affected by a particular Service CRUD.
+func addTLSRouteIndexers(ctx context.Context, mgr manager.Manager) error {
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TLSRoute{}, gatewayTLSRouteIndex, func(rawObj client.Object) []string {
+		tlsRoute := rawObj.(*gwapiv1a2.TLSRoute)
+		var gateways []string
+		for _, parent := range tlsRoute.Spec.ParentRefs {
+			if string(*parent.Kind) == gatewayapi.KindGateway {
+				// If an explicit Gateway namespace is not provided, use the TLSRoute namespace to
+				// lookup the provided Gateway Name.
+				gateways = append(gateways,
+					types.NamespacedName{
+						Namespace: gatewayapi.NamespaceDerefOrAlpha(parent.Namespace, tlsRoute.Namespace),
+						Name:      string(parent.Name),
+					}.String(),
+				)
+			}
+		}
+		return gateways
+	}); err != nil {
+		return err
+	}
+
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TLSRoute{}, serviceTLSRouteIndex, func(rawObj client.Object) []string {
+		tlsroute := rawObj.(*gwapiv1a2.TLSRoute)
+		var services []string
+		for _, rule := range tlsroute.Spec.Rules {
+			for _, backend := range rule.BackendRefs {
+				if string(*backend.Kind) == gatewayapi.KindService {
+					// If an explicit Service namespace is not provided, use the TLSRoute namespace to
+					// lookup the provided Gateway Name.
+					services = append(services,
+						types.NamespacedName{
+							Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, tlsroute.Namespace),
+							Name:      string(backend.Name),
+						}.String(),
+					)
+				}
+			}
+		}
+		return services
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+// addGatewayIndexers adds indexing on Gateway, for Secret objects that are
+// referenced in Gateway objects. This helps in querying for Gateways that are
+// affected by a particular Secret CRUD.
+func addGatewayIndexers(ctx context.Context, mgr manager.Manager) error {
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.Gateway{}, secretGatewayIndex, func(rawObj client.Object) []string {
+		gateway := rawObj.(*gwapiv1b1.Gateway)
+		var secretReferences []string
+		for _, listener := range gateway.Spec.Listeners {
+			if listener.TLS == nil || *listener.TLS.Mode != gwapiv1b1.TLSModeTerminate {
+				continue
+			}
+			for _, cert := range listener.TLS.CertificateRefs {
+				if *cert.Kind == gatewayapi.KindSecret {
+					// If an explicit Secret namespace is not provided, use the Gateway namespace to
+					// lookup the provided Secret Name.
+					secretReferences = append(secretReferences,
+						types.NamespacedName{
+							Namespace: gatewayapi.NamespaceDerefOr(cert.Namespace, gateway.Namespace),
+							Name:      string(cert.Name),
+						}.String(),
+					)
+				}
+			}
+		}
+		return secretReferences
+	}); err != nil {
+		return err
+	}
+
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.Gateway{}, classGatewayIndex, func(rawObj client.Object) []string {
+		gateway := rawObj.(*gwapiv1b1.Gateway)
+		return []string{string(gateway.Spec.GatewayClassName)}
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+// removeFinalizer removes the gatewayclass finalizer from the provided gc, if it exists.
+func (r *gatewayAPIReconciler) removeFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error {
+	if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
+		// Get the resource.
+		if err := r.client.Get(ctx, utils.NamespacedName(gc), gc); err != nil {
+			if kerrors.IsNotFound(err) {
+				return nil
+			}
+			return err
+		}
+
+		if slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) {
+			updated := gc.DeepCopy()
+			updated.Finalizers = slice.RemoveString(updated.Finalizers, gatewayClassFinalizer)
+			if err := r.client.Update(ctx, updated); err != nil {
+				return fmt.Errorf("failed to remove finalizer from gatewayclass %s: %w", gc.Name, err)
+			}
+		}
+		return nil
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+// addFinalizer adds the gatewayclass finalizer to the provided gc, if it doesn't exist.
+func (r *gatewayAPIReconciler) addFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error {
+	if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
+		// Get the resource.
+		if err := r.client.Get(ctx, utils.NamespacedName(gc), gc); err != nil {
+			if kerrors.IsNotFound(err) {
+				return nil
+			}
+			return err
+		}
+
+		if !slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) {
+			updated := gc.DeepCopy()
+			updated.Finalizers = append(updated.Finalizers, gatewayClassFinalizer)
+			if err := r.client.Update(ctx, updated); err != nil {
+				return fmt.Errorf("failed to add finalizer to gatewayclass %s: %w", gc.Name, err)
+			}
+		}
+		return nil
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+// subscribeAndUpdateStatus subscribes to gateway API object status updates and
+// writes it into the Kubernetes API Server.
+func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) {
+	// Gateway object status updater
+	go func() {
+		message.HandleSubscription(r.resources.GatewayStatuses.Subscribe(ctx),
+			func(update message.Update[types.NamespacedName, *gwapiv1b1.Gateway]) {
+				// skip delete updates.
+				if update.Delete {
+					return
+				}
+				key := update.Key
+				val := update.Value
+				r.statusUpdater.Send(status.Update{
+					NamespacedName: key,
+					Resource:       new(gwapiv1b1.Gateway),
+					Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+						g, ok := obj.(*gwapiv1b1.Gateway)
+						if !ok {
+							panic(fmt.Sprintf("unsupported object type %T", obj))
+						}
+						gCopy := g.DeepCopy()
+						gCopy.Status.Listeners = val.Status.Listeners
+						return gCopy
+					}),
+				})
+			},
+		)
+	}()
+
+	// HTTPRoute object status updater
+	go func() {
+		message.HandleSubscription(r.resources.HTTPRouteStatuses.Subscribe(ctx),
+			func(update message.Update[types.NamespacedName, *gwapiv1b1.HTTPRoute]) {
+				// skip delete updates.
+				if update.Delete {
+					return
+				}
+				key := update.Key
+				val := update.Value
+				r.statusUpdater.Send(status.Update{
+					NamespacedName: key,
+					Resource:       new(gwapiv1b1.HTTPRoute),
+					Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+						h, ok := obj.(*gwapiv1b1.HTTPRoute)
+						if !ok {
+							panic(fmt.Sprintf("unsupported object type %T", obj))
+						}
+						hCopy := h.DeepCopy()
+						hCopy.Status.Parents = val.Status.Parents
+						return hCopy
+					}),
+				})
+			},
+		)
+	}()
+
+	// TLSRoute object status updater
+	go func() {
+		message.HandleSubscription(r.resources.TLSRouteStatuses.Subscribe(ctx),
+			func(update message.Update[types.NamespacedName, *gwapiv1a2.TLSRoute]) {
+				// skip delete updates.
+				if update.Delete {
+					return
+				}
+				key := update.Key
+				val := update.Value
+				r.statusUpdater.Send(status.Update{
+					NamespacedName: key,
+					Resource:       new(gwapiv1a2.TLSRoute),
+					Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+						t, ok := obj.(*gwapiv1a2.TLSRoute)
+						if !ok {
+							panic(fmt.Sprintf("unsupported object type %T", obj))
+						}
+						tCopy := t.DeepCopy()
+						tCopy.Status.Parents = val.Status.Parents
+						return tCopy
+					}),
+				})
+			},
+		)
+	}()
+
+	r.log.Info("status subscriber shutting down")
+}
diff --git a/internal/provider/kubernetes/gateway.go b/internal/provider/kubernetes/gateway.go
deleted file mode 100644
index f7f958da0..000000000
--- a/internal/provider/kubernetes/gateway.go
+++ /dev/null
@@ -1,680 +0,0 @@
-// Copyright Envoy Gateway Authors
-// SPDX-License-Identifier: Apache-2.0
-// The full text of the Apache license is available in the LICENSE file at
-// the root of the repo.
-
-// This file contains code derived from Contour,
-// https://github.com/projectcontour/contour
-// from the source file
-// https://github.com/projectcontour/contour/blob/main/internal/controller/gateway.go// and is provided here subject to the following:
-// Copyright Project Contour Authors
-// SPDX-License-Identifier: Apache-2.0
-
-package kubernetes
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/go-logr/logr"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-	kerrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/controller"
-	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/predicate"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-	"sigs.k8s.io/controller-runtime/pkg/source"
-	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
-	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
-
-	"github.com/envoyproxy/gateway/internal/envoygateway/config"
-	"github.com/envoyproxy/gateway/internal/gatewayapi"
-	"github.com/envoyproxy/gateway/internal/message"
-	"github.com/envoyproxy/gateway/internal/provider/utils"
-	"github.com/envoyproxy/gateway/internal/status"
-	"github.com/envoyproxy/gateway/internal/utils/slice"
-)
-
-const gatewayClassFinalizer = gwapiv1b1.GatewayClassFinalizerGatewaysExist
-
-type gatewayReconciler struct {
-	client client.Client
-	// classController is the configured gatewayclass controller name.
-	classController gwapiv1b1.GatewayController
-	statusUpdater   status.Updater
-	log             logr.Logger
-
-	resources *message.ProviderResources
-}
-
-// newGatewayController creates a gateway controller. The controller will watch for
-// Gateway objects across all namespaces and reconcile those that match the configured
-// gatewayclass controller name.
-func newGatewayController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources) error {
-	r := &gatewayReconciler{
-		client:          mgr.GetClient(),
-		classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName),
-		statusUpdater:   su,
-		log:             cfg.Logger,
-		resources:       resources,
-	}
-
-	c, err := controller.New("gateway", mgr, controller.Options{Reconciler: r})
-	if err != nil {
-		return err
-	}
-	r.log.Info("created gateway controller")
-
-	// Subscribe to status updates
-	go r.subscribeAndUpdateStatus(context.Background())
-
-	// Only enqueue Gateway objects that match this Envoy Gateway's controller name.
-	if err := c.Watch(
-		&source.Kind{Type: &gwapiv1b1.Gateway{}},
-		&handler.EnqueueRequestForObject{},
-		predicate.NewPredicateFuncs(r.hasMatchingController),
-	); err != nil {
-		return err
-	}
-	r.log.Info("watching gateway objects")
-
-	// Trigger gateway reconciliation when the Envoy Service or Deployment has changed.
-	if err := c.Watch(&source.Kind{Type: &corev1.Service{}}, r.enqueueRequestForOwningGateway()); err != nil {
-		return err
-	}
-	if err := c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, r.enqueueRequestForOwningGateway()); err != nil {
-		return err
-	}
-	// Trigger gateway reconciliation when a Secret that is referenced
-	// by a managed Gateway has changed.
-	if err := c.Watch(&source.Kind{Type: &corev1.Secret{}}, r.enqueueRequestForGatewaySecrets()); err != nil {
-		return err
-	}
-	// Trigger gateway reconciliation when a ReferenceGrant that refers
-	// to a managed Gateway has changed.
-	if err := c.Watch(&source.Kind{Type: &gwapiv1a2.ReferenceGrant{}}, r.enqueueRequestForReferencedGateway()); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// hasMatchingController returns true if the provided object is a Gateway
-// using a GatewayClass matching the configured gatewayclass controller name.
-func (r *gatewayReconciler) hasMatchingController(obj client.Object) bool {
-	gw, ok := obj.(*gwapiv1b1.Gateway)
-	if !ok {
-		r.log.Info("unexpected object type, bypassing reconciliation", "object", obj)
-		return false
-	}
-
-	gc := &gwapiv1b1.GatewayClass{}
-	key := types.NamespacedName{Name: string(gw.Spec.GatewayClassName)}
-	if err := r.client.Get(context.Background(), key, gc); err != nil {
-		r.log.Error(err, "failed to get gatewayclass", "name", gw.Spec.GatewayClassName)
-		return false
-	}
-
-	if gc.Spec.ControllerName != r.classController {
-		r.log.Info("gatewayclass name for gateway doesn't match configured name",
-			"namespace", gw.Namespace, "name", gw.Name)
-		return false
-	}
-
-	return true
-}
-
-// enqueueRequestForOwningGateway returns an event handler that maps events for
-// resources with Gateway owning labels to reconcile requests for those Gateway objects.
-func (r *gatewayReconciler) enqueueRequestForOwningGateway() handler.EventHandler {
-	return handler.EnqueueRequestsFromMapFunc(func(a client.Object) []reconcile.Request {
-		labels := a.GetLabels()
-		if labels == nil {
-			return nil
-		}
-
-		gatewayNamespace := labels[gatewayapi.OwningGatewayNamespaceLabel]
-		gatewayName := labels[gatewayapi.OwningGatewayNameLabel]
-
-		if len(gatewayNamespace) == 0 || len(gatewayName) == 0 {
-			return nil
-		}
-
-		return []reconcile.Request{
-			{
-				NamespacedName: types.NamespacedName{
-					Namespace: gatewayNamespace,
-					Name:      gatewayName,
-				},
-			},
-		}
-	})
-}
-
-// enqueueRequestForGatewaySecrets returns an event handler that maps events for
-// Secrets referenced by managed Gateways to reconcile requests for those Gateway objects.
-func (r *gatewayReconciler) enqueueRequestForGatewaySecrets() handler.EventHandler {
-	return handler.EnqueueRequestsFromMapFunc(func(a client.Object) []reconcile.Request {
-		secret, ok := a.(*corev1.Secret)
-		if !ok {
-			r.log.Info("bypassing reconciliation due to unexpected object type", "type", a)
-			return nil
-		}
-
-		ctx := context.Background()
-		var gateways gwapiv1b1.GatewayList
-		if err := r.client.List(ctx, &gateways); err != nil {
-			return nil
-		}
-
-		var reqs []reconcile.Request
-		for i := range gateways.Items {
-			gw := gateways.Items[i]
-			if r.hasMatchingController(&gw) {
-				for j := range gw.Spec.Listeners {
-					if terminatesTLS(&gw.Spec.Listeners[j]) {
-						secrets, _, err := r.secretsAndRefGrantsForGateway(ctx, &gw)
-						if err != nil {
-							return nil
-						}
-						for _, s := range secrets {
-							if s.Namespace == secret.Namespace && s.Name == secret.Name {
-								req := reconcile.Request{
-									NamespacedName: types.NamespacedName{
-										Namespace: gw.Namespace,
-										Name:      gw.Name,
-									},
-								}
-								reqs = append(reqs, req)
-							}
-						}
-					}
-				}
-			}
-		}
-
-		return reqs
-	})
-}
-
-// enqueueRequestForReferencedGateway returns an event handler that maps events for
-// resources that reference a managed Gateway to reconcile requests for those Gateway objects.
-// Note: A ReferenceGrant is the only supported object type.
-func (r *gatewayReconciler) enqueueRequestForReferencedGateway() handler.EventHandler {
-	return handler.EnqueueRequestsFromMapFunc(func(a client.Object) []reconcile.Request {
-		rg, ok := a.(*gwapiv1a2.ReferenceGrant)
-		if !ok {
-			r.log.Info("bypassing reconciliation due to unexpected object type", "type", a)
-			return nil
-		}
-
-		var refs []types.NamespacedName
-		for _, to := range rg.Spec.To {
-			if to.Group == gwapiv1a2.GroupName &&
-				to.Kind == gatewayapi.KindGateway &&
-				to.Name != nil {
-				ref := types.NamespacedName{Namespace: rg.Namespace, Name: string(*to.Name)}
-				refs = append(refs, ref)
-			}
-		}
-		for _, from := range rg.Spec.From {
-			if from.Group == gwapiv1a2.GroupName &&
-				from.Kind == gatewayapi.KindGateway {
-				ref := types.NamespacedName{Namespace: string(from.Namespace), Name: rg.Name}
-				refs = append(refs, ref)
-			}
-		}
-
-		ctx := context.Background()
-		var gateways gwapiv1b1.GatewayList
-		if err := r.client.List(ctx, &gateways); err != nil {
-			return nil
-		}
-
-		var reqs []reconcile.Request
-		for i := range gateways.Items {
-			gw := gateways.Items[i]
-			for _, ref := range refs {
-				if gw.Namespace == ref.Namespace && gw.Name == ref.Name && r.hasMatchingController(&gw) {
-					req := reconcile.Request{
-						NamespacedName: types.NamespacedName{
-							Namespace: gw.Namespace,
-							Name:      gw.Name,
-						},
-					}
-					reqs = append(reqs, req)
-				}
-			}
-		}
-
-		return reqs
-	})
-}
-
-// Reconcile finds all the Gateways for the GatewayClass with an "Accepted: true" condition
-// and passes all Gateways for the configured GatewayClass to the IR for processing.
-func (r *gatewayReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
-	r.log.Info("reconciling gateway", "namespace", request.Namespace, "name", request.Name)
-
-	allClasses := &gwapiv1b1.GatewayClassList{}
-	if err := r.client.List(ctx, allClasses); err != nil {
-		return reconcile.Result{}, fmt.Errorf("error listing gatewayclasses")
-	}
-	// Find the GatewayClass for this controller with Accepted=true status condition.
-	acceptedClass := r.acceptedClass(allClasses)
-	if acceptedClass == nil {
-		r.log.Info("No accepted gatewayclass found for gateway", "namespace", request.Namespace,
-			"name", request.Name)
-		for namespacedName := range r.resources.Gateways.LoadAll() {
-			r.resources.Gateways.Delete(namespacedName)
-		}
-		return reconcile.Result{}, nil
-	}
-
-	allGateways := &gwapiv1b1.GatewayList{}
-	if err := r.client.List(ctx, allGateways); err != nil {
-		return reconcile.Result{}, fmt.Errorf("error listing gateways")
-	}
-
-	// Get all the Gateways for the Accepted=true GatewayClass.
-	acceptedGateways := gatewaysOfClass(acceptedClass, allGateways)
-	if len(acceptedGateways) == 0 {
-		r.log.Info("No gateways found for accepted gatewayclass")
-		// If needed, remove the finalizer from the accepted GatewayClass.
-		if err := r.removeFinalizer(ctx, acceptedClass); err != nil {
-			return reconcile.Result{}, fmt.Errorf("failed to remove finalizer from gatewayclass %s: %w",
-				acceptedClass.Name, err)
-		}
-	} else {
-		// If needed, finalize the accepted GatewayClass.
-		if err := r.addFinalizer(ctx, acceptedClass); err != nil {
-			return reconcile.Result{}, fmt.Errorf("failed adding finalizer to gatewayclass %s: %w",
-				acceptedClass.Name, err)
-		}
-	}
-
-	found := false
-	// Set status conditions for all accepted gateways.
-	for i := range acceptedGateways {
-		gw := acceptedGateways[i]
-
-		// Get the status of the Gateway's associated Envoy Deployment.
-		deployment, err := r.envoyDeploymentForGateway(ctx, &gw)
-		if err != nil {
-			r.log.Info("failed to get deployment for gateway",
-				"namespace", gw.Namespace, "name", gw.Name)
-		}
-
-		// Get the status address of the Gateway's associated Envoy Service.
-		svc, err := r.envoyServiceForGateway(ctx, &gw)
-		if err != nil {
-			r.log.Info("failed to get service for gateway",
-				"namespace", gw.Namespace, "name", gw.Name)
-		}
-
-		// Get the secret and referenceGrants of the Gateway's TLS configuration.
-		secrets, refGrants, err := r.secretsAndRefGrantsForGateway(ctx, &gw)
-		if err != nil {
-			r.log.Info("failed to get secrets and referencegrants for gateway",
-				"namespace", gw.Namespace, "name", gw.Name)
-		}
-		for i := range secrets {
-			secret := secrets[i]
-			// Store the secrets in the resource map.
-			key := utils.NamespacedName(&secret)
-			r.resources.Secrets.Store(key, &secret)
-		}
-		for i := range refGrants {
-			rg := refGrants[i]
-			// Store the referencegrants in the resource map.
-			key := utils.NamespacedName(&rg)
-			r.resources.ReferenceGrants.Store(key, &rg)
-			// Store the referencegrant namespace in the resource map.
-			key = types.NamespacedName{Name: rg.Namespace}
-			refNs := corev1.Namespace{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: rg.Namespace,
-				},
-			}
-			if err := r.client.Get(ctx, key, &refNs); err != nil {
-				r.log.Info("failed to get referencegrant namespace", "name", refNs.Name)
-				return reconcile.Result{}, nil
-			}
-			r.resources.Namespaces.Store(refNs.Name, &refNs)
-		}
-
-		// update scheduled condition
-		status.UpdateGatewayStatusScheduledCondition(&gw, true)
-		// update address field and ready condition
-		status.UpdateGatewayStatusReadyCondition(&gw, svc, deployment)
-
-		key := utils.NamespacedName(&gw)
-		// publish status
-		// do it inline since this code flow updates the
-		// Status.Addresses field whereas the message bus / subscriber
-		// does not.
-		r.statusUpdater.Send(status.Update{
-			NamespacedName: key,
-			Resource:       new(gwapiv1b1.Gateway),
-			Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
-				g, ok := obj.(*gwapiv1b1.Gateway)
-				if !ok {
-					panic(fmt.Sprintf("unsupported object type %T", obj))
-				}
-				gCopy := g.DeepCopy()
-				gCopy.Status.Conditions = status.MergeConditions(gCopy.Status.Conditions, gw.Status.Conditions...)
-				gCopy.Status.Addresses = gw.Status.Addresses
-				return gCopy
-
-			}),
-		})
-
-		// only store the resource if it does not exist or it has a newer spec.
-		if v, ok := r.resources.Gateways.Load(key); !ok || (gw.Generation > v.Generation) {
-			r.resources.Gateways.Store(key, &gw)
-		}
-		if key == request.NamespacedName {
-			found = true
-		}
-	}
-
-	if !found {
-		gw, ok := r.resources.Gateways.Load(request.NamespacedName)
-		if !ok {
-			r.log.Info("failed to find accepted gateway in the watchable map", "namespace", request.Namespace, "name", request.Name)
-			return reconcile.Result{}, nil
-		}
-
-		r.resources.Gateways.Delete(request.NamespacedName)
-		// Delete the TLS secrets from the resource map if no other managed
-		// Gateways reference them.
-		secrets, _, err := r.secretsAndRefGrantsForGateway(ctx, gw)
-		if err != nil {
-			r.log.Info("failed to get secrets and referencegrants for gateway",
-				"namespace", gw.Namespace, "name", gw.Name)
-		}
-		for i := range secrets {
-			secret := secrets[i]
-			referenced, err := r.gatewaysRefSecret(ctx, &secret)
-			switch {
-			case err != nil:
-				r.log.Error(err, "failed to verify if other gateways reference secret")
-			case !referenced:
-				r.log.Info("no other gateways reference secret; deleting from resource map",
-					"namespace", secret.Namespace, "name", secret.Name)
-				key := utils.NamespacedName(&secret)
-				r.resources.Secrets.Delete(key)
-			default:
-				r.log.Info("other gateways reference secret; keeping the secret in the resource map",
-					"namespace", secret.Namespace, "name", secret.Name)
-			}
-		}
-	}
-
-	r.log.WithName(request.Namespace).WithName(request.Name).Info("reconciled gateway")
-
-	return reconcile.Result{}, nil
-}
-
-// acceptedClass returns the GatewayClass from the provided list that matches
-// the configured controller name and contains the Accepted=true status condition.
-func (r *gatewayReconciler) acceptedClass(gcList *gwapiv1b1.GatewayClassList) *gwapiv1b1.GatewayClass {
-	if gcList == nil {
-		return nil
-	}
-	for i := range gcList.Items {
-		gc := &gcList.Items[i]
-		if gc.Spec.ControllerName == r.classController && isAccepted(gc) {
-			return gc
-		}
-	}
-	return nil
-}
-
-// isAccepted returns true if the provided gatewayclass contains the Accepted=true
-// status condition.
-func isAccepted(gc *gwapiv1b1.GatewayClass) bool {
-	if gc == nil {
-		return false
-	}
-	for _, cond := range gc.Status.Conditions {
-		if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue {
-			return true
-		}
-	}
-	return false
-}
-
-// gatewaysOfClass returns a list of gateways that reference gc from the provided gwList.
-func gatewaysOfClass(gc *gwapiv1b1.GatewayClass, gwList *gwapiv1b1.GatewayList) []gwapiv1b1.Gateway {
-	var ret []gwapiv1b1.Gateway
-	if gwList == nil || gc == nil {
-		return ret
-	}
-	for i := range gwList.Items {
-		gw := gwList.Items[i]
-		if string(gw.Spec.GatewayClassName) == gc.Name {
-			ret = append(ret, gw)
-		}
-	}
-	return ret
-}
-
-// envoyServiceForGateway returns the Envoy service, returning nil if the service doesn't exist.
-func (r *gatewayReconciler) envoyServiceForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*corev1.Service, error) {
-	key := types.NamespacedName{
-		Namespace: config.EnvoyGatewayNamespace,
-		Name:      infraServiceName(gateway),
-	}
-	svc := new(corev1.Service)
-	if err := r.client.Get(ctx, key, svc); err != nil {
-		if kerrors.IsNotFound(err) {
-			return nil, nil
-		}
-		return nil, err
-	}
-	return svc, nil
-}
-
-// gatewaysRefSecret returns true if a managed Gateway references the provided secret.
-// An error is returned if an error is encountered while checking.
-func (r *gatewayReconciler) gatewaysRefSecret(ctx context.Context, secret *corev1.Secret) (bool, error) {
-	if secret == nil {
-		return false, fmt.Errorf("secret is nil")
-	}
-	gateways := &gwapiv1b1.GatewayList{}
-	if err := r.client.List(ctx, gateways); err != nil {
-		return false, fmt.Errorf("error listing gatewayclasses: %v", err)
-	}
-	for i := range gateways.Items {
-		gw := gateways.Items[i]
-		if r.hasMatchingController(&gw) {
-			secrets, _, err := r.secretsAndRefGrantsForGateway(ctx, &gw)
-			if err != nil {
-				return false, err
-			}
-			for _, s := range secrets {
-				if s.Namespace == secret.Namespace && s.Name == secret.Name {
-					return true, nil
-				}
-			}
-		}
-	}
-
-	return false, nil
-}
-
-// secretsAndRefGrantsForGateway returns the Secrets referenced by the provided gateway listeners.
-// If the provided Gateway references a Secret in a different namespace, a list of
-// ReferenceGrants is returned that permit the cross namespace Secret reference.
-func (r *gatewayReconciler) secretsAndRefGrantsForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) ([]corev1.Secret, []gwapiv1a2.ReferenceGrant, error) {
-	var secrets []corev1.Secret
-	var returnedGrants []gwapiv1a2.ReferenceGrant
-	for i := range gateway.Spec.Listeners {
-		listener := gateway.Spec.Listeners[i]
-		if terminatesTLS(&listener) {
-			for j := range listener.TLS.CertificateRefs {
-				ref := listener.TLS.CertificateRefs[j]
-				if refsSecret(&ref) {
-					if ref.Namespace != nil {
-						// A ReferenceGrant is required for cross namespace secret references.
-						refGrants := &gwapiv1a2.ReferenceGrantList{}
-						opts := client.ListOptions{Namespace: string(*ref.Namespace)}
-						if err := r.client.List(ctx, refGrants, &opts); err != nil {
-							return nil, nil, fmt.Errorf("error listing referencegrants")
-						}
-						var gwRefd, secretRefd bool
-						for _, rg := range refGrants.Items {
-							for _, from := range rg.Spec.From {
-								if from.Group == gwapiv1a2.GroupName &&
-									from.Kind == gatewayapi.KindGateway {
-									gwRefd = true
-									break
-								}
-							}
-							for _, to := range rg.Spec.To {
-								if to.Group == corev1.GroupName &&
-									to.Kind == gatewayapi.KindSecret {
-									if to.Name == nil || *to.Name == gwapiv1a2.ObjectName(ref.Name) {
-										secretRefd = true
-										break
-									}
-								}
-							}
-							if gwRefd && secretRefd {
-								returnedGrants = append(returnedGrants, rg)
-								key := types.NamespacedName{
-									Namespace: string(*ref.Namespace),
-									Name:      string(ref.Name),
-								}
-								secret := new(corev1.Secret)
-								if err := r.client.Get(ctx, key, secret); err != nil {
-									r.resources.Secrets.Delete(key)
-									return nil, nil, fmt.Errorf("failed to get secret: %v", err)
-								}
-								secrets = append(secrets, *secret)
-							}
-						}
-					} else {
-						// The secret is in the Gateway's namespace.
-						key := types.NamespacedName{
-							Namespace: gateway.Namespace,
-							Name:      string(ref.Name),
-						}
-						secret := new(corev1.Secret)
-						if err := r.client.Get(ctx, key, secret); err != nil {
-							r.resources.Secrets.Delete(key)
-							return nil, nil, fmt.Errorf("failed to get secret: %v", err)
-						}
-						secrets = append(secrets, *secret)
-					}
-				}
-			}
-		}
-	}
-
-	return secrets, returnedGrants, nil
-}
-
-// terminatesTLS returns true if the provided gateway contains a listener configured
-// for TLS termination.
-func terminatesTLS(listener *gwapiv1b1.Listener) bool {
-	if listener.TLS != nil &&
-		listener.Protocol == gwapiv1b1.HTTPSProtocolType &&
-		listener.TLS.Mode != nil &&
-		*listener.TLS.Mode == gwapiv1b1.TLSModeTerminate {
-		return true
-	}
-	return false
-}
-
-// refsSecret returns true if ref refers to a Secret.
-func refsSecret(ref *gwapiv1b1.SecretObjectReference) bool {
-	return (ref.Group == nil || *ref.Group == corev1.GroupName) &&
-		(ref.Kind == nil || *ref.Kind == gatewayapi.KindSecret)
-}
-
-// addFinalizer adds the gatewayclass finalizer to the provided gc, if it doesn't exist.
-func (r *gatewayReconciler) addFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error {
-	if !slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) {
-		updated := gc.DeepCopy()
-		updated.Finalizers = append(updated.Finalizers, gatewayClassFinalizer)
-		if err := r.client.Update(ctx, updated); err != nil {
-			return fmt.Errorf("failed to add finalizer to gatewayclass %s: %w", gc.Name, err)
-		}
-	}
-	return nil
-}
-
-// removeFinalizer removes the gatewayclass finalizer from the provided gc, if it exists.
-func (r *gatewayReconciler) removeFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error {
-	if slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) {
-		updated := gc.DeepCopy()
-		updated.Finalizers = slice.RemoveString(updated.Finalizers, gatewayClassFinalizer)
-		if err := r.client.Update(ctx, updated); err != nil {
-			return fmt.Errorf("failed to remove finalizer from gatewayclass %s: %w", gc.Name, err)
-		}
-	}
-	return nil
-}
-
-// envoyDeploymentForGateway returns the Envoy Deployment, returning nil if the Deployment doesn't exist.
-func (r *gatewayReconciler) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*appsv1.Deployment, error) {
-	key := types.NamespacedName{
-		Namespace: config.EnvoyGatewayNamespace,
-		Name:      infraDeploymentName(gateway),
-	}
-	deployment := new(appsv1.Deployment)
-	if err := r.client.Get(ctx, key, deployment); err != nil {
-		if kerrors.IsNotFound(err) {
-			return nil, nil
-		}
-		return nil, err
-	}
-	return deployment, nil
-}
-
-// subscribeAndUpdateStatus subscribes to gateway status updates and writes it into the
-// Kubernetes API Server
-func (r *gatewayReconciler) subscribeAndUpdateStatus(ctx context.Context) {
-	// Subscribe to resources
-	message.HandleSubscription(r.resources.GatewayStatuses.Subscribe(ctx),
-		func(update message.Update[types.NamespacedName, *gwapiv1b1.Gateway]) {
-			// skip delete updates.
-			if update.Delete {
-				return
-			}
-			key := update.Key
-			val := update.Value
-			r.statusUpdater.Send(status.Update{
-				NamespacedName: key,
-				Resource:       new(gwapiv1b1.Gateway),
-				Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
-					g, ok := obj.(*gwapiv1b1.Gateway)
-					if !ok {
-						panic(fmt.Sprintf("unsupported object type %T", obj))
-					}
-					gCopy := g.DeepCopy()
-					gCopy.Status.Listeners = val.Status.Listeners
-					return gCopy
-				}),
-			})
-		},
-	)
-	r.log.Info("status subscriber shutting down")
-}
-
-func infraServiceName(gateway *gwapiv1b1.Gateway) string {
-	infraName := utils.GetHashedName(fmt.Sprintf("%s-%s", gateway.Namespace, gateway.Name))
-	return fmt.Sprintf("%s-%s", config.EnvoyPrefix, infraName)
-}
-
-func infraDeploymentName(gateway *gwapiv1b1.Gateway) string {
-	infraName := utils.GetHashedName(fmt.Sprintf("%s-%s", gateway.Namespace, gateway.Name))
-	return fmt.Sprintf("%s-%s", config.EnvoyPrefix, infraName)
-}
diff --git a/internal/provider/kubernetes/gatewayclass.go b/internal/provider/kubernetes/gatewayclass.go
deleted file mode 100644
index 8a360202b..000000000
--- a/internal/provider/kubernetes/gatewayclass.go
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright Envoy Gateway Authors
-// SPDX-License-Identifier: Apache-2.0
-// The full text of the Apache license is available in the LICENSE file at
-// the root of the repo.
-
-// TODO Portions of this code are based on code from Contour, available at:
-// https://github.com/projectcontour/contour/blob/main/internal/controller/gatewayclass.go
-
-package kubernetes
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/go-logr/logr"
-	"k8s.io/apimachinery/pkg/types"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/controller"
-	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/predicate"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-	"sigs.k8s.io/controller-runtime/pkg/source"
-	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
-
-	"github.com/envoyproxy/gateway/internal/envoygateway/config"
-	"github.com/envoyproxy/gateway/internal/message"
-	"github.com/envoyproxy/gateway/internal/status"
-	"github.com/envoyproxy/gateway/internal/utils/slice"
-)
-
-type gatewayClassReconciler struct {
-	client        client.Client
-	controller    gwapiv1b1.GatewayController
-	statusUpdater status.Updater
-	log           logr.Logger
-
-	resources *message.ProviderResources
-}
-
-// newGatewayClassController creates the gatewayclass controller. The controller
-// will be pre-configured to watch for cluster-scoped GatewayClass objects with
-// a controller field that matches name.
-func newGatewayClassController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources) error {
-	r := &gatewayClassReconciler{
-		client:        mgr.GetClient(),
-		controller:    gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName),
-		statusUpdater: su,
-		log:           cfg.Logger,
-		resources:     resources,
-	}
-
-	c, err := controller.New("gatewayclass", mgr, controller.Options{Reconciler: r})
-	if err != nil {
-		return err
-	}
-	r.log.Info("created gatewayclass controller")
-
-	// Only enqueue GatewayClass objects that match this Envoy Gateway's controller name.
-	if err := c.Watch(
-		&source.Kind{Type: &gwapiv1b1.GatewayClass{}},
-		&handler.EnqueueRequestForObject{},
-		predicate.NewPredicateFuncs(r.hasMatchingController),
-	); err != nil {
-		return err
-	}
-	r.log.Info("watching gatewayclass objects")
-
-	return nil
-}
-
-// hasMatchingController returns true if the provided object is a GatewayClass
-// with a Spec.Controller string matching this Envoy Gateway's controller string,
-// or false otherwise.
-func (r *gatewayClassReconciler) hasMatchingController(obj client.Object) bool {
-	log := r.log.WithName(obj.GetName())
-
-	gc, ok := obj.(*gwapiv1b1.GatewayClass)
-	if !ok {
-		log.Info("bypassing reconciliation due to unexpected object type", "type", obj)
-		return false
-	}
-
-	if gc.Spec.ControllerName == r.controller {
-		log.Info("enqueueing gatewayclass")
-		return true
-	}
-
-	log.Info("bypassing reconciliation due to controller name", "controller", gc.Spec.ControllerName)
-	return false
-}
-
-func (r *gatewayClassReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
-	r.log.WithName(request.Name).Info("reconciling gatewayclass")
-
-	var gatewayClasses gwapiv1b1.GatewayClassList
-	if err := r.client.List(ctx, &gatewayClasses); err != nil {
-		return reconcile.Result{}, fmt.Errorf("error listing gatewayclasses: %v", err)
-	}
-
-	var cc controlledClasses
-
-	for i := range gatewayClasses.Items {
-		if gatewayClasses.Items[i].Spec.ControllerName == r.controller {
-			// The gatewayclass was marked for deletion and the finalizer removed,
-			// so clean-up dependents.
-			if !gatewayClasses.Items[i].DeletionTimestamp.IsZero() &&
-				!slice.ContainsString(gatewayClasses.Items[i].Finalizers, gatewayClassFinalizer) {
-				r.log.Info("gatewayclass marked for deletion")
-				cc.removeMatch(&gatewayClasses.Items[i])
-				// Delete the gatewayclass from the watchable map.
-				r.resources.GatewayClasses.Delete(request.Name)
-				continue
-			}
-
-			cc.addMatch(&gatewayClasses.Items[i])
-		}
-	}
-
-	// The gatewayclass was already deleted/finalized and there are stale queue entries.
-	acceptedGC := cc.acceptedClass()
-	if acceptedGC == nil {
-		r.log.Info("failed to find an accepted gatewayclass")
-		return reconcile.Result{}, nil
-	}
-
-	// Store the accepted gatewayclass in the resource map.
-	r.resources.GatewayClasses.Store(acceptedGC.GetName(), acceptedGC)
-
-	updater := func(gc *gwapiv1b1.GatewayClass, accepted bool) error {
-		if r.statusUpdater != nil {
-			r.statusUpdater.Send(status.Update{
-				NamespacedName: types.NamespacedName{Name: gc.Name},
-				Resource:       &gwapiv1b1.GatewayClass{},
-				Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
-					gc, ok := obj.(*gwapiv1b1.GatewayClass)
-					if !ok {
-						panic(fmt.Sprintf("unsupported object type %T", obj))
-					}
-
-					return status.SetGatewayClassAccepted(gc.DeepCopy(), accepted)
-				}),
-			})
-		} else {
-			// this branch makes testing easier by not going through the status.Updater.
-			copy := status.SetGatewayClassAccepted(gc.DeepCopy(), accepted)
-
-			if err := r.client.Status().Update(ctx, copy); err != nil {
-				return fmt.Errorf("error updating status of gatewayclass %s: %w", copy.Name, err)
-			}
-		}
-		return nil
-	}
-
-	// Update status for all gateway classes
-	for _, gc := range cc.notAcceptedClasses() {
-		if err := updater(gc, false); err != nil {
-			return reconcile.Result{}, err
-		}
-	}
-	if acceptedGC != nil {
-		if err := updater(acceptedGC, true); err != nil {
-			return reconcile.Result{}, err
-		}
-	}
-
-	r.log.WithName(request.Name).Info("reconciled gatewayclass")
-	return reconcile.Result{}, nil
-}
-
-type controlledClasses struct {
-	// matchedClasses holds all GatewayClass objects with matching controllerName.
-	matchedClasses []*gwapiv1b1.GatewayClass
-
-	// oldestClass stores the first GatewayClass encountered with matching
-	// controllerName. This is maintained so that the oldestClass does not change
-	// during reboots.
-	oldestClass *gwapiv1b1.GatewayClass
-}
-
-func (cc *controlledClasses) addMatch(gc *gwapiv1b1.GatewayClass) {
-	cc.matchedClasses = append(cc.matchedClasses, gc)
-
-	switch {
-	case cc.oldestClass == nil:
-		cc.oldestClass = gc
-	case gc.CreationTimestamp.Time.Before(cc.oldestClass.CreationTimestamp.Time):
-		cc.oldestClass = gc
-	case gc.CreationTimestamp.Time.Equal(cc.oldestClass.CreationTimestamp.Time) && gc.Name < cc.oldestClass.Name:
-		// tie-breaker: first one in alphabetical order is considered oldest/accepted
-		cc.oldestClass = gc
-	}
-}
-
-func (cc *controlledClasses) removeMatch(gc *gwapiv1b1.GatewayClass) {
-	// First remove gc from matchedClasses.
-	for i, matchedGC := range cc.matchedClasses {
-		if matchedGC.Name == gc.Name {
-			cc.matchedClasses[i] = cc.matchedClasses[len(cc.matchedClasses)-1]
-			cc.matchedClasses = cc.matchedClasses[:len(cc.matchedClasses)-1]
-			break
-		}
-	}
-
-	// If the oldestClass is removed, find the new oldestClass candidate
-	// from matchedClasses.
-	if cc.oldestClass != nil && cc.oldestClass.Name == gc.Name {
-		if len(cc.matchedClasses) == 0 {
-			cc.oldestClass = nil
-			return
-		}
-
-		cc.oldestClass = cc.matchedClasses[0]
-		for i := 1; i < len(cc.matchedClasses); i++ {
-			current := cc.matchedClasses[i]
-			if current.CreationTimestamp.Time.Before(cc.oldestClass.CreationTimestamp.Time) ||
-				(current.CreationTimestamp.Time.Equal(cc.oldestClass.CreationTimestamp.Time) &&
-					current.Name < cc.oldestClass.Name) {
-				cc.oldestClass = current
-				return
-			}
-		}
-	}
-}
-
-func (cc *controlledClasses) acceptedClass() *gwapiv1b1.GatewayClass {
-	return cc.oldestClass
-}
-
-func (cc *controlledClasses) notAcceptedClasses() []*gwapiv1b1.GatewayClass {
-	var res []*gwapiv1b1.GatewayClass
-	for _, gc := range cc.matchedClasses {
-		// skip the oldest one since it will be accepted.
-		if gc.Name != cc.oldestClass.Name {
-			res = append(res, gc)
-		}
-	}
-
-	return res
-}
diff --git a/internal/provider/kubernetes/helpers.go b/internal/provider/kubernetes/helpers.go
index 7c94208b2..aa7348bf0 100644
--- a/internal/provider/kubernetes/helpers.go
+++ b/internal/provider/kubernetes/helpers.go
@@ -9,12 +9,27 @@ import (
 	"context"
 	"fmt"
 
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
-	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
 	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
+
+	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/gatewayapi"
+	"github.com/envoyproxy/gateway/internal/provider/utils"
+)
+
+const (
+	gatewayClassFinalizer = gwapiv1b1.GatewayClassFinalizerGatewaysExist
 )
 
+type ObjectKindNamespacedName struct {
+	kind      string
+	namespace string
+	name      string
+}
+
 // validateParentRefs validates the provided routeParentReferences, returning the
 // referenced Gateways managed by Envoy Gateway. The only supported parentRef
 // is a Gateway.
@@ -61,21 +76,152 @@ func validateParentRefs(ctx context.Context, client client.Client, namespace str
 	return ret, nil
 }
 
-// isRoutePresentInNamespace checks if any kind of Routes - HTTPRoute, TLSRoute
-// exists in the namespace ns.
-func isRoutePresentInNamespace(ctx context.Context, c client.Client, ns string) (bool, error) {
-	tlsRouteList := &gwapiv1a2.TLSRouteList{}
-	if err := c.List(ctx, tlsRouteList, &client.ListOptions{Namespace: ns}); err != nil {
-		return false, fmt.Errorf("error listing tlsroutes")
+type controlledClasses struct {
+	// matchedClasses holds all GatewayClass objects with matching controllerName.
+	matchedClasses []*gwapiv1b1.GatewayClass
+
+	// oldestClass stores the first GatewayClass encountered with matching
+	// controllerName. This is maintained so that the oldestClass does not change
+	// during reboots.
+	oldestClass *gwapiv1b1.GatewayClass
+}
+
+func (cc *controlledClasses) addMatch(gc *gwapiv1b1.GatewayClass) {
+	cc.matchedClasses = append(cc.matchedClasses, gc)
+
+	switch {
+	case cc.oldestClass == nil:
+		cc.oldestClass = gc
+	case gc.CreationTimestamp.Time.Before(cc.oldestClass.CreationTimestamp.Time):
+		cc.oldestClass = gc
+	case gc.CreationTimestamp.Time.Equal(cc.oldestClass.CreationTimestamp.Time) && gc.Name < cc.oldestClass.Name:
+		// tie-breaker: first one in alphabetical order is considered oldest/accepted
+		cc.oldestClass = gc
 	}
+}
 
-	httpRouteList := &gwapiv1b1.HTTPRouteList{}
-	if err := c.List(ctx, httpRouteList, &client.ListOptions{Namespace: ns}); err != nil {
-		return false, fmt.Errorf("error listing httproutes")
+func (cc *controlledClasses) removeMatch(gc *gwapiv1b1.GatewayClass) {
+	// First remove gc from matchedClasses.
+	for i, matchedGC := range cc.matchedClasses {
+		if matchedGC.Name == gc.Name {
+			cc.matchedClasses[i] = cc.matchedClasses[len(cc.matchedClasses)-1]
+			cc.matchedClasses = cc.matchedClasses[:len(cc.matchedClasses)-1]
+			break
+		}
 	}
 
-	if len(tlsRouteList.Items)+len(httpRouteList.Items) > 0 {
-		return true, nil
+	// If the oldestClass is removed, find the new oldestClass candidate
+	// from matchedClasses.
+	if cc.oldestClass != nil && cc.oldestClass.Name == gc.Name {
+		if len(cc.matchedClasses) == 0 {
+			cc.oldestClass = nil
+			return
+		}
+
+		cc.oldestClass = cc.matchedClasses[0]
+		for i := 1; i < len(cc.matchedClasses); i++ {
+			current := cc.matchedClasses[i]
+			if current.CreationTimestamp.Time.Before(cc.oldestClass.CreationTimestamp.Time) ||
+				(current.CreationTimestamp.Time.Equal(cc.oldestClass.CreationTimestamp.Time) &&
+					current.Name < cc.oldestClass.Name) {
+				cc.oldestClass = current
+				return
+			}
+		}
 	}
-	return false, nil
+}
+
+func (cc *controlledClasses) acceptedClass() *gwapiv1b1.GatewayClass {
+	return cc.oldestClass
+}
+
+func (cc *controlledClasses) notAcceptedClasses() []*gwapiv1b1.GatewayClass {
+	var res []*gwapiv1b1.GatewayClass
+	for _, gc := range cc.matchedClasses {
+		// skip the oldest one since it will be accepted.
+		if gc.Name != cc.oldestClass.Name {
+			res = append(res, gc)
+		}
+	}
+
+	return res
+}
+
+// isAccepted returns true if the provided gatewayclass contains the Accepted=true
+// status condition.
+func isAccepted(gc *gwapiv1b1.GatewayClass) bool {
+	if gc == nil {
+		return false
+	}
+	for _, cond := range gc.Status.Conditions {
+		if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue {
+			return true
+		}
+	}
+	return false
+}
+
+// gatewaysOfClass returns a list of gateways that reference gc from the provided gwList.
+func gatewaysOfClass(gc *gwapiv1b1.GatewayClass, gwList *gwapiv1b1.GatewayList) []gwapiv1b1.Gateway {
+	var ret []gwapiv1b1.Gateway
+	if gwList == nil || gc == nil {
+		return ret
+	}
+	for i := range gwList.Items {
+		gw := gwList.Items[i]
+		if string(gw.Spec.GatewayClassName) == gc.Name {
+			ret = append(ret, gw)
+		}
+	}
+	return ret
+}
+
+// terminatesTLS returns true if the provided gateway contains a listener configured
+// for TLS termination.
+func terminatesTLS(listener *gwapiv1b1.Listener) bool {
+	if listener.TLS != nil &&
+		listener.Protocol == gwapiv1b1.HTTPSProtocolType &&
+		listener.TLS.Mode != nil &&
+		*listener.TLS.Mode == gwapiv1b1.TLSModeTerminate {
+		return true
+	}
+	return false
+}
+
+// refsSecret returns true if ref refers to a Secret.
+func refsSecret(ref *gwapiv1b1.SecretObjectReference) bool {
+	return (ref.Group == nil || *ref.Group == corev1.GroupName) &&
+		(ref.Kind == nil || *ref.Kind == gatewayapi.KindSecret)
+}
+
+func infraServiceName(gateway *gwapiv1b1.Gateway) string {
+	infraName := utils.GetHashedName(fmt.Sprintf("%s-%s", gateway.Namespace, gateway.Name))
+	return fmt.Sprintf("%s-%s", config.EnvoyPrefix, infraName)
+}
+
+func infraDeploymentName(gateway *gwapiv1b1.Gateway) string {
+	infraName := utils.GetHashedName(fmt.Sprintf("%s-%s", gateway.Namespace, gateway.Name))
+	return fmt.Sprintf("%s-%s", config.EnvoyPrefix, infraName)
+}
+
+// validateBackendRef validates that ref is a reference to a local Service.
+// TODO: Add support for:
+//   - Validating weights.
+//   - Validating ports.
+//   - Referencing HTTPRoutes.
+//   - Referencing Services/HTTPRoutes from other namespaces using ReferenceGrant.
+func validateBackendRef(ref *gwapiv1b1.BackendRef) error {
+	switch {
+	case ref == nil:
+		return nil
+	case ref.Group != nil && *ref.Group != corev1.GroupName:
+		return fmt.Errorf("invalid group; must be nil or empty string")
+	case ref.Kind != nil && *ref.Kind != gatewayapi.KindService:
+		return fmt.Errorf("invalid kind %q; must be %q",
+			*ref.BackendObjectReference.Kind, gatewayapi.KindService)
+	case ref.Namespace != nil:
+		return fmt.Errorf("invalid namespace; must be nil")
+	}
+
+	return nil
 }
diff --git a/internal/provider/kubernetes/httproute.go b/internal/provider/kubernetes/httproute.go
deleted file mode 100644
index e99ef92cb..000000000
--- a/internal/provider/kubernetes/httproute.go
+++ /dev/null
@@ -1,376 +0,0 @@
-// Copyright Envoy Gateway Authors
-// SPDX-License-Identifier: Apache-2.0
-// The full text of the Apache license is available in the LICENSE file at
-// the root of the repo.
-
-// This file contains code derived from Contour,
-// https://github.com/projectcontour/contour
-// from the source file
-// https://github.com/projectcontour/contour/blob/main/internal/controller/httproute.go
-// and is provided here subject to the following:
-// Copyright Project Contour Authors
-// SPDX-License-Identifier: Apache-2.0
-
-package kubernetes
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/go-logr/logr"
-	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/fields"
-	"k8s.io/apimachinery/pkg/types"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/controller"
-	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-	"sigs.k8s.io/controller-runtime/pkg/source"
-	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
-
-	"github.com/envoyproxy/gateway/internal/envoygateway/config"
-	"github.com/envoyproxy/gateway/internal/gatewayapi"
-	"github.com/envoyproxy/gateway/internal/message"
-	"github.com/envoyproxy/gateway/internal/provider/utils"
-	"github.com/envoyproxy/gateway/internal/status"
-)
-
-const (
-	kindHTTPRoute = "HTTPRoute"
-
-	serviceHTTPRouteIndex = "serviceHTTPRouteBackendRef"
-)
-
-type httpRouteReconciler struct {
-	client          client.Client
-	log             logr.Logger
-	statusUpdater   status.Updater
-	classController gwapiv1b1.GatewayController
-
-	resources      *message.ProviderResources
-	referenceStore *providerReferenceStore
-}
-
-// newHTTPRouteController creates the httproute controller from mgr. The controller will be pre-configured
-// to watch for HTTPRoute objects across all namespaces.
-func newHTTPRouteController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources, referenceStore *providerReferenceStore) error {
-	r := &httpRouteReconciler{
-		client:          mgr.GetClient(),
-		log:             cfg.Logger,
-		classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName),
-		statusUpdater:   su,
-		resources:       resources,
-		referenceStore:  referenceStore,
-	}
-
-	c, err := controller.New("httproute", mgr, controller.Options{Reconciler: r})
-	if err != nil {
-		return err
-	}
-	r.log.Info("created httproute controller")
-
-	if err := c.Watch(&source.Kind{Type: &gwapiv1b1.HTTPRoute{}}, &handler.EnqueueRequestForObject{}); err != nil {
-		return err
-	}
-
-	// Subscribe to status updates
-	go r.subscribeAndUpdateStatus(context.Background())
-
-	// Add indexing on HTTPRoute, for Service objects that are referenced in HTTPRoute objects
-	// via `.spec.rules.backendRefs`. This helps in querying for HTTPRoutes that are affected by
-	// a particular Service CRUD.
-	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &gwapiv1b1.HTTPRoute{}, serviceHTTPRouteIndex, func(rawObj client.Object) []string {
-		httpRoute := rawObj.(*gwapiv1b1.HTTPRoute)
-		var backendServices []string
-		for _, rule := range httpRoute.Spec.Rules {
-			for _, backend := range rule.BackendRefs {
-				if string(*backend.Kind) == gatewayapi.KindService {
-					// If an explicit Service namespace is not provided, use the HTTPRoute namespace to
-					// lookup the provided Service Name.
-					backendServices = append(backendServices,
-						types.NamespacedName{
-							Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, httpRoute.Namespace),
-							Name:      string(backend.Name),
-						}.String(),
-					)
-				}
-			}
-		}
-		return backendServices
-	}); err != nil {
-		return err
-	}
-
-	// Watch Gateway CRUDs and reconcile affected HTTPRoutes.
-	if err := c.Watch(
-		&source.Kind{Type: &gwapiv1b1.Gateway{}},
-		handler.EnqueueRequestsFromMapFunc(r.getHTTPRoutesForGateway),
-	); err != nil {
-		return err
-	}
-
-	// Watch Service CRUDs and reconcile affected HTTPRoutes.
-	if err := c.Watch(
-		&source.Kind{Type: &corev1.Service{}},
-		handler.EnqueueRequestsFromMapFunc(r.getHTTPRoutesForService),
-	); err != nil {
-		return err
-	}
-
-	r.log.Info("watching httproute objects")
-	return nil
-}
-
-// getHTTPRoutesForGateway uses a Gateway obj to fetch HTTPRoutes, iterating
-// through them and creating a reconciliation request for each valid HTTPRoute
-// that references obj.
-func (r *httpRouteReconciler) getHTTPRoutesForGateway(obj client.Object) []reconcile.Request {
-	ctx := context.Background()
-
-	gw, ok := obj.(*gwapiv1b1.Gateway)
-	if !ok {
-		r.log.Info("unexpected object type, bypassing reconciliation", "object", obj)
-		return []reconcile.Request{}
-	}
-
-	routes := &gwapiv1b1.HTTPRouteList{}
-	if err := r.client.List(ctx, routes); err != nil {
-		return []reconcile.Request{}
-	}
-
-	requests := []reconcile.Request{}
-	for i := range routes.Items {
-		route := routes.Items[i]
-		gateways, err := validateParentRefs(ctx, r.client, route.Namespace, r.classController, route.Spec.ParentRefs)
-		if err != nil {
-			r.log.Info("invalid parentRefs for httproute, bypassing reconciliation", "object", obj)
-			continue
-		}
-		for j := range gateways {
-			if gateways[j].Namespace == gw.Namespace && gateways[j].Name == gw.Name {
-				req := reconcile.Request{
-					NamespacedName: types.NamespacedName{
-						Namespace: route.Namespace,
-						Name:      route.Name,
-					},
-				}
-				requests = append(requests, req)
-				break
-			}
-		}
-	}
-
-	return requests
-}
-
-// getHTTPRoutesForService uses a Service obj to fetch HTTPRoutes that references
-// the Service using `.spec.rules.backendRefs`. The affected HTTPRoutes are then
-// pushed for reconciliation.
-func (r *httpRouteReconciler) getHTTPRoutesForService(obj client.Object) []reconcile.Request {
-	affectedHTTPRouteList := &gwapiv1b1.HTTPRouteList{}
-
-	if err := r.client.List(context.Background(), affectedHTTPRouteList, &client.ListOptions{
-		FieldSelector: fields.OneTermEqualSelector(serviceHTTPRouteIndex, utils.NamespacedName(obj).String()),
-	}); err != nil {
-		return []reconcile.Request{}
-	}
-
-	requests := make([]reconcile.Request, len(affectedHTTPRouteList.Items))
-	for i, item := range affectedHTTPRouteList.Items {
-		item := item
-		requests[i] = reconcile.Request{
-			NamespacedName: utils.NamespacedName(&item),
-		}
-	}
-
-	return requests
-}
-
-func (r *httpRouteReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
-	log := r.log.WithValues("namespace", request.Namespace, "name", request.Name)
-
-	log.Info("reconciling httproute")
-
-	// Fetch all HTTPRoutes from the cache.
-	routeList := &gwapiv1b1.HTTPRouteList{}
-	if err := r.client.List(ctx, routeList); err != nil {
-		return reconcile.Result{}, fmt.Errorf("error listing httproutes")
-	}
-
-	found := false
-	for i := range routeList.Items {
-		// See if this route from the list matched the reconciled route.
-		route := routeList.Items[i]
-		routeKey := utils.NamespacedName(&route)
-		if routeKey == request.NamespacedName {
-			found = true
-		}
-
-		// Validate the route.
-		gws, err := validateParentRefs(ctx, r.client, route.Namespace, r.classController, route.Spec.ParentRefs)
-		if err != nil {
-			// Remove the route from the watchable map since it's invalid.
-			r.resources.HTTPRoutes.Delete(routeKey)
-			r.log.Error(err, "invalid parentRefs for httproute")
-			return reconcile.Result{}, nil
-		}
-		log.Info("validated httproute parentRefs")
-
-		if len(gws) == 0 {
-			// Remove the route from the watchable map since it doesn't reference
-			// a managed Gateway.
-			log.Info("httproute doesn't reference any managed gateways")
-			r.resources.HTTPRoutes.Delete(routeKey)
-			return reconcile.Result{}, nil
-		}
-
-		// only store the resource if it does not exist or it has a newer spec.
-		if v, ok := r.resources.HTTPRoutes.Load(routeKey); !ok || (route.Generation > v.Generation) {
-			r.resources.HTTPRoutes.Store(routeKey, &route)
-			log.Info("added httproute to resource map")
-		}
-		// Get the route's namespace from the cache.
-		nsKey := types.NamespacedName{Name: route.Namespace}
-		ns := new(corev1.Namespace)
-		if err := r.client.Get(ctx, nsKey, ns); err != nil {
-			if errors.IsNotFound(err) {
-				// The route's namespace doesn't exist in the cache, so remove it from
-				// the namespace resource map if it exists.
-				if _, ok := r.resources.Namespaces.Load(nsKey.Name); ok {
-					r.resources.Namespaces.Delete(nsKey.Name)
-					log.Info("deleted namespace from resource map")
-				}
-			}
-			return reconcile.Result{}, fmt.Errorf("failed to get namespace %s", nsKey.Name)
-		}
-
-		// The route's namespace exists, so add it to the resource map.
-		r.resources.Namespaces.Store(nsKey.Name, ns)
-		log.Info("added namespace to resource map")
-
-		// Get the route's backendRefs from the cache. Note that a Service is the
-		// only supported kind.
-		for i := range route.Spec.Rules {
-			for j := range route.Spec.Rules[i].BackendRefs {
-				ref := route.Spec.Rules[i].BackendRefs[j]
-				if err := validateBackendRef(&ref); err != nil {
-					return reconcile.Result{}, fmt.Errorf("invalid backendRef: %w", err)
-				}
-
-				// The backendRef is valid, so get the referenced service from the cache.
-				svcKey := types.NamespacedName{Namespace: route.Namespace, Name: string(ref.Name)}
-				svc := new(corev1.Service)
-				if err := r.client.Get(ctx, svcKey, svc); err != nil {
-					if errors.IsNotFound(err) {
-						// The ref's service doesn't exist in the cache, so remove it from
-						// the resource map if it exists.
-						if _, ok := r.resources.Services.Load(svcKey); ok {
-							r.resources.Services.Delete(svcKey)
-							r.referenceStore.removeRouteToServicesMapping(
-								ObjectKindNamespacedName{kindHTTPRoute, route.Namespace, route.Name},
-								svcKey,
-							)
-							log.Info("deleted service from resource map")
-						}
-					}
-					return reconcile.Result{}, fmt.Errorf("failed to get service %s/%s",
-						svcKey.Namespace, svcKey.Name)
-				}
-
-				// The backendRef Service exists, so add it to the resource map.
-				r.resources.Services.Store(svcKey, svc)
-				r.referenceStore.updateRouteToServicesMapping(
-					ObjectKindNamespacedName{kindHTTPRoute, route.Namespace, route.Name},
-					svcKey,
-				)
-				log.Info("added service to resource map")
-			}
-		}
-	}
-
-	if !found {
-		// Delete the httproute from the resource map.
-		r.resources.HTTPRoutes.Delete(request.NamespacedName)
-		log.Info("deleted httproute from resource map")
-
-		// Delete the Namespace and Service from the resource maps if no other
-		// routes (TLSRoute or HTTPRoute) exist in the namespace.
-		found, err := isRoutePresentInNamespace(ctx, r.client, request.NamespacedName.Namespace)
-		if err != nil {
-			return reconcile.Result{}, err
-		}
-		if !found {
-			r.resources.Namespaces.Delete(request.Namespace)
-			log.Info("deleted namespace from resource map")
-		}
-
-		// Delete the Service from the resource maps if no other
-		// routes (TLSRoute or HTTPRoute) reference that Service.
-		routeServices := r.referenceStore.getRouteToServicesMapping(ObjectKindNamespacedName{kindHTTPRoute, request.Namespace, request.Name})
-		for svc := range routeServices {
-			r.referenceStore.removeRouteToServicesMapping(ObjectKindNamespacedName{kindHTTPRoute, request.Namespace, request.Name}, svc)
-			if !r.referenceStore.isServiceReferredByRoutes(svc) {
-				r.resources.Services.Delete(svc)
-				log.Info("deleted service from resource map", "namespace", svc.Namespace, "name", svc.Name)
-			}
-		}
-	}
-
-	log.Info("reconciled httproute")
-
-	return reconcile.Result{}, nil
-}
-
-// validateBackendRef validates that ref is a reference to a local Service.
-// TODO: Add support for:
-//   - Validating weights.
-//   - Validating ports.
-//   - Referencing HTTPRoutes.
-//   - Referencing Services/HTTPRoutes from other namespaces using ReferenceGrant.
-func validateBackendRef(ref *gwapiv1b1.HTTPBackendRef) error {
-	switch {
-	case ref == nil:
-		return nil
-	case ref.Group != nil && *ref.Group != corev1.GroupName:
-		return fmt.Errorf("invalid group; must be nil or empty string")
-	case ref.Kind != nil && *ref.Kind != gatewayapi.KindService:
-		return fmt.Errorf("invalid kind %q; must be %q",
-			*ref.BackendRef.BackendObjectReference.Kind, gatewayapi.KindService)
-	case ref.Namespace != nil:
-		return fmt.Errorf("invalid namespace; must be nil")
-	}
-
-	return nil
-}
-
-// subscribeAndUpdateStatus subscribes to httproute status updates and writes it into the
-// Kubernetes API Server
-func (r *httpRouteReconciler) subscribeAndUpdateStatus(ctx context.Context) {
-	// Subscribe to resources
-	message.HandleSubscription(r.resources.HTTPRouteStatuses.Subscribe(ctx),
-		func(update message.Update[types.NamespacedName, *gwapiv1b1.HTTPRoute]) {
-			// skip delete updates.
-			if update.Delete {
-				return
-			}
-			key := update.Key
-			val := update.Value
-			r.statusUpdater.Send(status.Update{
-				NamespacedName: key,
-				Resource:       new(gwapiv1b1.HTTPRoute),
-				Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
-					h, ok := obj.(*gwapiv1b1.HTTPRoute)
-					if !ok {
-						panic(fmt.Sprintf("unsupported object type %T", obj))
-					}
-					hCopy := h.DeepCopy()
-					hCopy.Status.Parents = val.Status.Parents
-					return hCopy
-				}),
-			})
-		},
-	)
-	r.log.Info("status subscriber shutting down")
-}
diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go
index 0a6903e3d..33f8420a4 100644
--- a/internal/provider/kubernetes/kubernetes.go
+++ b/internal/provider/kubernetes/kubernetes.go
@@ -50,22 +50,9 @@ func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResour
 		return nil, fmt.Errorf("failed to add status update handler %v", err)
 	}
 
-	// Initialize kubernetes provider referenceStore to store additional object mappings.
-	referenceStore := newProviderReferenceStore()
-
 	// Create and register the controllers with the manager.
-	if err := newGatewayClassController(mgr, svr, updateHandler.Writer(), resources); err != nil {
-		return nil, fmt.Errorf("failed to create gatewayclass controller: %w", err)
-	}
-	if err := newGatewayController(mgr, svr, updateHandler.Writer(), resources); err != nil {
-		return nil, fmt.Errorf("failed to create gateway controller: %w", err)
-	}
-
-	if err := newHTTPRouteController(mgr, svr, updateHandler.Writer(), resources, referenceStore); err != nil {
-		return nil, fmt.Errorf("failed to create httproute controller: %w", err)
-	}
-	if err := newTLSRouteController(mgr, svr, updateHandler.Writer(), resources, referenceStore); err != nil {
-		return nil, fmt.Errorf("failed to create tlsroute controller: %w", err)
+	if err := newGatewayAPIController(mgr, svr, updateHandler.Writer(), resources); err != nil {
+		return nil, fmt.Errorf("failted to create gatewayapi controller: %w", err)
 	}
 
 	// Add health check health probes.
diff --git a/internal/provider/kubernetes/predicates.go b/internal/provider/kubernetes/predicates.go
new file mode 100644
index 000000000..7c1204173
--- /dev/null
+++ b/internal/provider/kubernetes/predicates.go
@@ -0,0 +1,232 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package kubernetes
+
+import (
+	"context"
+
+	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/gatewayapi"
+	"github.com/envoyproxy/gateway/internal/provider/utils"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/fields"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
+	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+// TODO: all predicate functions are unti test candidates.
+
+// hasMatchingController returns true if the provided object is a GatewayClass
+// with a Spec.Controller string matching this Envoy Gateway's controller string,
+// or false otherwise.
+func (r *gatewayAPIReconciler) hasMatchingController(obj client.Object) bool {
+	gc, ok := obj.(*gwapiv1b1.GatewayClass)
+	if !ok {
+		r.log.Info("bypassing reconciliation due to unexpected object type", "type", obj)
+		return false
+	}
+
+	if gc.Spec.ControllerName == r.classController {
+		r.log.Info("gatewayclass has matching controller name, processing", "name", gc.Name)
+		return true
+	}
+
+	r.log.Info("bypassing reconciliation due to controller name", "controller", gc.Spec.ControllerName)
+	return false
+}
+
+// validateGatewayForReconcile returns true if the provided object is a Gateway
+// using a GatewayClass matching the configured gatewayclass controller name.
+func (r *gatewayAPIReconciler) validateGatewayForReconcile(obj client.Object) bool {
+	gw, ok := obj.(*gwapiv1b1.Gateway)
+	if !ok {
+		r.log.Info("unexpected object type, bypassing reconciliation", "object", obj)
+		return false
+	}
+
+	gc := &gwapiv1b1.GatewayClass{}
+	key := types.NamespacedName{Name: string(gw.Spec.GatewayClassName)}
+	if err := r.client.Get(context.Background(), key, gc); err != nil {
+		r.log.Error(err, "failed to get gatewayclass", "name", gw.Spec.GatewayClassName)
+		return false
+	}
+
+	if gc.Spec.ControllerName != r.classController {
+		r.log.Info("gatewayclass name for gateway doesn't match configured name",
+			"namespace", gw.Namespace, "name", gw.Name)
+		return false
+	}
+
+	return true
+}
+
+// validateSecretForReconcile checks whether the Secret belongs to a valid Gateway.
+func (r *gatewayAPIReconciler) validateSecretForReconcile(obj client.Object) bool {
+	secret, ok := obj.(*corev1.Secret)
+	if !ok {
+		r.log.Info("unexpected object type, bypassing reconciliation", "object", obj)
+		return false
+	}
+
+	gwList := &gwapiv1b1.GatewayList{}
+	if err := r.client.List(context.Background(), gwList, &client.ListOptions{
+		FieldSelector: fields.OneTermEqualSelector(secretGatewayIndex, utils.NamespacedName(secret).String()),
+	}); err != nil {
+		r.log.Error(err, "unable to find associated HTTPRoutes")
+		return false
+	}
+
+	if len(gwList.Items) == 0 {
+		return false
+	}
+
+	for _, gw := range gwList.Items {
+		gw := gw
+		if !r.validateGatewayForReconcile(&gw) {
+			return false
+		}
+	}
+
+	return true
+}
+
+// validateServiceForReconcile tries finding the owning Gateway of the Service
+// if it exists, finds the Gateway's Deployment, and further updates the Gateway
+// status Ready condition. All Services are pushed for reconciliation.
+func (r *gatewayAPIReconciler) validateServiceForReconcile(obj client.Object) bool {
+	ctx := context.Background()
+	svc, ok := obj.(*corev1.Service)
+	if !ok {
+		r.log.Info("unexpected object type, bypassing reconciliation", "object", obj)
+		return false
+	}
+
+	// Check if the Service belongs to a Gateway, if so, find the Gateway. If
+	gtw := r.findOwningGateway(ctx, svc.GetLabels())
+	if gtw != nil {
+		// Check if the Deployment for the Gateway also exists, if it does, proceed with
+		// the Gateway status update.
+		deployment, err := r.envoyDeploymentForGateway(ctx, gtw)
+		if err != nil {
+			r.log.Info("failed to get Deployment for gateway",
+				"namespace", gtw.Namespace, "name", gtw.Name)
+			return false
+		}
+
+		r.statusUpdateForGateway(gtw, svc, deployment)
+		return true
+	}
+
+	httpRouteList := &gwapiv1b1.HTTPRouteList{}
+	if err := r.client.List(ctx, httpRouteList, &client.ListOptions{
+		FieldSelector: fields.OneTermEqualSelector(serviceHTTPRouteIndex, utils.NamespacedName(svc).String()),
+	}); err != nil {
+		r.log.Error(err, "unable to find associated HTTPRoutes")
+		return false
+	}
+
+	tlsRouteList := &gwapiv1a2.TLSRouteList{}
+	if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{
+		FieldSelector: fields.OneTermEqualSelector(serviceTLSRouteIndex, utils.NamespacedName(svc).String()),
+	}); err != nil {
+		r.log.Error(err, "unable to find associated HTTPRoutes")
+		return false
+	}
+
+	// Check how many Route objects refer this Service
+	allAssociatedRoutes := len(httpRouteList.Items) +
+		len(tlsRouteList.Items)
+
+	return allAssociatedRoutes != 0
+}
+
+// validateDeploymentForReconcile tries finding the owning Gateway of the Deployment
+// if it exists, finds the Gateway's Service, and further updates the Gateway
+// status Ready condition. No Deployments are pushed for reconciliation.
+func (r *gatewayAPIReconciler) validateDeploymentForReconcile(obj client.Object) bool {
+	ctx := context.Background()
+	deployment, ok := obj.(*appsv1.Deployment)
+	if !ok {
+		r.log.Info("unexpected object type, bypassing reconciliation", "object", obj)
+		return false
+	}
+
+	// Check if the deployment belongs to a Gateway, if so, find the Gateway.
+	gtw := r.findOwningGateway(ctx, deployment.GetLabels())
+	if gtw != nil {
+		// Check if the Service for the Gateway also exists, if it does, proceed with
+		// the Gateway status update.
+		svc, err := r.envoyServiceForGateway(ctx, gtw)
+		if err != nil {
+			r.log.Info("failed to get Service for gateway",
+				"namespace", gtw.Namespace, "name", gtw.Name)
+			return false
+		}
+
+		r.statusUpdateForGateway(gtw, svc, deployment)
+	}
+
+	// There is no need to reconcile the Deployment any further.
+	return false
+}
+
+// envoyDeploymentForGateway returns the Envoy Deployment, returning nil if the Deployment doesn't exist.
+func (r *gatewayAPIReconciler) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*appsv1.Deployment, error) {
+	key := types.NamespacedName{
+		Namespace: config.EnvoyGatewayNamespace,
+		Name:      infraDeploymentName(gateway),
+	}
+	deployment := new(appsv1.Deployment)
+	if err := r.client.Get(ctx, key, deployment); err != nil {
+		if kerrors.IsNotFound(err) {
+			return nil, nil
+		}
+		return nil, err
+	}
+	return deployment, nil
+}
+
+// envoyServiceForGateway returns the Envoy service, returning nil if the service doesn't exist.
+func (r *gatewayAPIReconciler) envoyServiceForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*corev1.Service, error) {
+	key := types.NamespacedName{
+		Namespace: config.EnvoyGatewayNamespace,
+		Name:      infraServiceName(gateway),
+	}
+	svc := new(corev1.Service)
+	if err := r.client.Get(ctx, key, svc); err != nil {
+		if kerrors.IsNotFound(err) {
+			return nil, nil
+		}
+		return nil, err
+	}
+	return svc, nil
+}
+
+// findOwningGateway attempts finds a Gateway using "labels".
+func (r gatewayAPIReconciler) findOwningGateway(ctx context.Context, labels map[string]string) *gwapiv1b1.Gateway {
+	gwName, ok := labels[gatewayapi.OwningGatewayNameLabel]
+	if !ok {
+		return nil
+	}
+
+	gwNamespace, ok := labels[gatewayapi.OwningGatewayNamespaceLabel]
+	if !ok {
+		return nil
+	}
+
+	gatewayKey := types.NamespacedName{Namespace: gwNamespace, Name: gwName}
+	gtw := new(gwapiv1b1.Gateway)
+	if err := r.client.Get(ctx, gatewayKey, gtw); err != nil {
+		r.log.Error(err, "gateway not found")
+		return nil
+	}
+
+	return gtw
+}
diff --git a/internal/provider/kubernetes/routes.go b/internal/provider/kubernetes/routes.go
new file mode 100644
index 000000000..b13a494f8
--- /dev/null
+++ b/internal/provider/kubernetes/routes.go
@@ -0,0 +1,120 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package kubernetes
+
+import (
+	"context"
+
+	"github.com/envoyproxy/gateway/internal/gatewayapi"
+	"github.com/envoyproxy/gateway/internal/provider/utils"
+	"k8s.io/apimachinery/pkg/fields"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
+	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+// processTLSRoutes finds TLSRoutes corresponding to a gatewayNamespaceName, further checks for
+// the backend references and pushes the TLSRoutes to the resourceTree.
+func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayNamespaceName string,
+	resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error {
+	tlsRouteList := &gwapiv1a2.TLSRouteList{}
+	if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{
+		FieldSelector: fields.OneTermEqualSelector(gatewayTLSRouteIndex, gatewayNamespaceName),
+	}); err != nil {
+		r.log.Error(err, "unable to find associated TLSRoutes")
+		return err
+	}
+
+	for _, tlsRoute := range tlsRouteList.Items {
+		tlsRoute := tlsRoute
+		r.log.Info("processing TLSRoute", "namespace", tlsRoute.Namespace, "name", tlsRoute.Name)
+
+		for _, rule := range tlsRoute.Spec.Rules {
+			for _, backendRef := range rule.BackendRefs {
+				backendRef := backendRef
+				ref := gatewayapi.UpgradeBackendRef(backendRef)
+				if err := validateBackendRef(&ref); err != nil {
+					r.log.Error(err, "invalid backendRef")
+					continue
+				}
+
+				backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, tlsRoute.Namespace)
+				resourceMap.allAssociatedBackendRefs[types.NamespacedName{
+					Namespace: backendNamespace,
+					Name:      string(backendRef.Name),
+				}] = struct{}{}
+
+				if backendNamespace != tlsRoute.Namespace {
+					from := ObjectKindNamespacedName{kind: gatewayapi.KindTLSRoute, namespace: tlsRoute.Namespace, name: tlsRoute.Name}
+					to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)}
+					refGrant, err := r.findReferenceGrant(ctx, from, to)
+					if err != nil {
+						r.log.Error(err, "unable to find ReferenceGrant that links the Service to TLSRoute")
+						continue
+					}
+
+					resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant
+				}
+			}
+		}
+
+		resourceMap.allAssociatedNamespaces[tlsRoute.Namespace] = struct{}{}
+		resourceTree.TLSRoutes = append(resourceTree.TLSRoutes, &tlsRoute)
+	}
+
+	return nil
+}
+
+// processHTTPRoutes finds HTTPRoutes corresponding to a gatewayNamespaceName, further checks for
+// the backend references and pushes the HTTPRoutes to the resourceTree.
+func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNamespaceName string,
+	resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error {
+	httpRouteList := &gwapiv1b1.HTTPRouteList{}
+	if err := r.client.List(ctx, httpRouteList, &client.ListOptions{
+		FieldSelector: fields.OneTermEqualSelector(gatewayHTTPRouteIndex, gatewayNamespaceName),
+	}); err != nil {
+		r.log.Error(err, "unable to find associated HTTPRoutes")
+		return err
+	}
+	for _, httpRoute := range httpRouteList.Items {
+		httpRoute := httpRoute
+		r.log.Info("processing HTTPRoute", "namespace", httpRoute.Namespace, "name", httpRoute.Name)
+
+		for _, rule := range httpRoute.Spec.Rules {
+			for _, backendRef := range rule.BackendRefs {
+				backendRef := backendRef
+				if err := validateBackendRef(&backendRef.BackendRef); err != nil {
+					r.log.Error(err, "invalid backendRef")
+					continue
+				}
+
+				backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, httpRoute.Namespace)
+				resourceMap.allAssociatedBackendRefs[types.NamespacedName{
+					Namespace: backendNamespace,
+					Name:      string(backendRef.Name),
+				}] = struct{}{}
+
+				if backendNamespace != httpRoute.Namespace {
+					from := ObjectKindNamespacedName{kind: gatewayapi.KindHTTPRoute, namespace: httpRoute.Namespace, name: httpRoute.Name}
+					to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)}
+					refGrant, err := r.findReferenceGrant(ctx, from, to)
+					if err != nil {
+						r.log.Error(err, "unable to find ReferenceGrant that links the Service to HTTPRoute")
+						continue
+					}
+
+					resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant
+				}
+			}
+		}
+
+		resourceMap.allAssociatedNamespaces[httpRoute.Namespace] = struct{}{}
+		resourceTree.HTTPRoutes = append(resourceTree.HTTPRoutes, &httpRoute)
+	}
+
+	return nil
+}
diff --git a/internal/provider/kubernetes/store.go b/internal/provider/kubernetes/store.go
deleted file mode 100644
index 3ecc514fd..000000000
--- a/internal/provider/kubernetes/store.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright Envoy Gateway Authors
-// SPDX-License-Identifier: Apache-2.0
-// The full text of the Apache license is available in the LICENSE file at
-// the root of the repo.
-
-package kubernetes
-
-import (
-	"sync"
-
-	"k8s.io/apimachinery/pkg/types"
-)
-
-// providerReferenceStore maintains additional mappings related to Kubernetes provider
-// resources. The mappings are regularly updated from the reconcilers based
-// on the existence of the object in the Kubernetes datastore.
-type providerReferenceStore struct {
-	mu sync.Mutex
-
-	// routeToServicesMappings maintains a mapping of a Route object,
-	// and the Services it references. For instance
-	// HTTPRoute/ns1/route1	-> { ns1/svc1, ns1/svc2, ns2/svc1 }
-	// TLSRoute/ns1/route1	-> { ns1/svc1, ns2/svc2 }
-	routeToServicesMappings map[ObjectKindNamespacedName]map[types.NamespacedName]struct{}
-}
-
-type ObjectKindNamespacedName struct {
-	kind      string
-	namespace string
-	name      string
-}
-
-func newProviderReferenceStore() *providerReferenceStore {
-	return &providerReferenceStore{
-		routeToServicesMappings: map[ObjectKindNamespacedName]map[types.NamespacedName]struct{}{},
-	}
-}
-
-func (p *providerReferenceStore) getRouteToServicesMapping(route ObjectKindNamespacedName) map[types.NamespacedName]struct{} {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	return p.routeToServicesMappings[route]
-}
-
-func (p *providerReferenceStore) updateRouteToServicesMapping(route ObjectKindNamespacedName, service types.NamespacedName) {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	if len(p.routeToServicesMappings[route]) == 0 {
-		p.routeToServicesMappings[route] = map[types.NamespacedName]struct{}{service: {}}
-	} else {
-		p.routeToServicesMappings[route][service] = struct{}{}
-	}
-}
-
-func (p *providerReferenceStore) removeRouteToServicesMapping(route ObjectKindNamespacedName, service types.NamespacedName) {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	delete(p.routeToServicesMappings[route], service)
-	if len(p.routeToServicesMappings[route]) == 0 {
-		delete(p.routeToServicesMappings, route)
-	}
-}
-
-func (p *providerReferenceStore) isServiceReferredByRoutes(service types.NamespacedName) bool {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	for _, svcs := range p.routeToServicesMappings {
-		if _, ok := svcs[service]; ok {
-			return true
-		}
-	}
-	return false
-}
diff --git a/internal/provider/kubernetes/tlsroute.go b/internal/provider/kubernetes/tlsroute.go
deleted file mode 100644
index 9cd3db39e..000000000
--- a/internal/provider/kubernetes/tlsroute.go
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright Envoy Gateway Authors
-// SPDX-License-Identifier: Apache-2.0
-// The full text of the Apache license is available in the LICENSE file at
-// the root of the repo.
-
-// This file contains code derived from Contour,
-// https://github.com/projectcontour/contour
-// from the source file
-// https://github.com/projectcontour/contour/blob/main/internal/controller/tlsroute.go
-// and is provided here subject to the following:
-// Copyright Project Contour Authors
-// SPDX-License-Identifier: Apache-2.0
-
-package kubernetes
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/go-logr/logr"
-	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/fields"
-	"k8s.io/apimachinery/pkg/types"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/controller"
-	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-	"sigs.k8s.io/controller-runtime/pkg/source"
-	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
-	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
-
-	"github.com/envoyproxy/gateway/internal/envoygateway/config"
-	"github.com/envoyproxy/gateway/internal/gatewayapi"
-	"github.com/envoyproxy/gateway/internal/message"
-	"github.com/envoyproxy/gateway/internal/provider/utils"
-	"github.com/envoyproxy/gateway/internal/status"
-)
-
-const (
-	kindTLSRoute = "TLSRoute"
-
-	serviceTLSRouteIndex = "serviceTLSRouteBackendRef"
-)
-
-type tlsRouteReconciler struct {
-	client          client.Client
-	log             logr.Logger
-	statusUpdater   status.Updater
-	classController gwapiv1b1.GatewayController
-
-	resources      *message.ProviderResources
-	referenceStore *providerReferenceStore
-}
-
-// newTLSRouteController creates the tlsroute controller from mgr. The controller will be pre-configured
-// to watch for TLSRoute objects across all namespaces.
-func newTLSRouteController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources, referenceStore *providerReferenceStore) error {
-	r := &tlsRouteReconciler{
-		client:          mgr.GetClient(),
-		log:             cfg.Logger,
-		classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName),
-		statusUpdater:   su,
-		resources:       resources,
-		referenceStore:  referenceStore,
-	}
-
-	c, err := controller.New("tlsroute", mgr, controller.Options{Reconciler: r})
-	if err != nil {
-		return err
-	}
-	r.log.Info("created tlsroute controller")
-
-	if err := c.Watch(
-		&source.Kind{Type: &gwapiv1a2.TLSRoute{}},
-		&handler.EnqueueRequestForObject{},
-	); err != nil {
-		return err
-	}
-
-	// Subscribe to status updates
-	go r.subscribeAndUpdateStatus(context.Background())
-
-	// Add indexing on TLSRoute, for Service objects that are referenced in TLSRoute objects
-	// via `.spec.rules.backendRefs`. This helps in querying for TLSRoutes that are affected by
-	// a particular Service CRUD.
-	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &gwapiv1a2.TLSRoute{}, serviceTLSRouteIndex, func(rawObj client.Object) []string {
-		tlsRoute := rawObj.(*gwapiv1a2.TLSRoute)
-		var backendServices []string
-		for _, rule := range tlsRoute.Spec.Rules {
-			for _, backend := range rule.BackendRefs {
-				if string(*backend.Kind) == gatewayapi.KindService {
-					// If an explicit Service namespace is not provided, use the TLSRoute namespace to
-					// lookup the provided Service Name.
-					backendServices = append(backendServices,
-						types.NamespacedName{
-							Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, tlsRoute.Namespace),
-							Name:      string(backend.Name),
-						}.String(),
-					)
-				}
-			}
-		}
-		return backendServices
-	}); err != nil {
-		return err
-	}
-
-	// Watch Gateway CRUDs and reconcile affected TLSRoutes.
-	if err := c.Watch(
-		&source.Kind{Type: &gwapiv1b1.Gateway{}},
-		handler.EnqueueRequestsFromMapFunc(r.getTLSRoutesForGateway),
-	); err != nil {
-		return err
-	}
-
-	// Watch Service CRUDs and reconcile affected TLSRoutes.
-	if err := c.Watch(
-		&source.Kind{Type: &corev1.Service{}},
-		handler.EnqueueRequestsFromMapFunc(r.getTLSRoutesForService),
-	); err != nil {
-		return err
-	}
-
-	r.log.Info("watching tlsroute objects")
-	return nil
-}
-
-// getTLSRoutesForGateway uses a Gateway obj to fetch TLSRoutes, iterating
-// through them and creating a reconciliation request for each valid TLSRoute
-// that references obj.
-func (r *tlsRouteReconciler) getTLSRoutesForGateway(obj client.Object) []reconcile.Request {
-	ctx := context.Background()
-
-	gw, ok := obj.(*gwapiv1b1.Gateway)
-	if !ok {
-		r.log.Info("unexpected object type, bypassing reconciliation", "object", obj)
-		return []reconcile.Request{}
-	}
-
-	routes := &gwapiv1a2.TLSRouteList{}
-	if err := r.client.List(ctx, routes); err != nil {
-		return []reconcile.Request{}
-	}
-
-	requests := []reconcile.Request{}
-	for i := range routes.Items {
-		route := routes.Items[i]
-		gateways, err := validateParentRefs(ctx, r.client, route.Namespace, r.classController, gatewayapi.UpgradeParentReferences(route.Spec.ParentRefs))
-		if err != nil {
-			r.log.Info("invalid parentRefs for tlsroute, bypassing reconciliation", "object", obj)
-			continue
-		}
-		for j := range gateways {
-			if gateways[j].Namespace == gw.Namespace && gateways[j].Name == gw.Name {
-				req := reconcile.Request{
-					NamespacedName: types.NamespacedName{
-						Namespace: route.Namespace,
-						Name:      route.Name,
-					},
-				}
-				requests = append(requests, req)
-				break
-			}
-		}
-	}
-
-	return requests
-}
-
-// getTLSRoutesForService uses a Service obj to fetch TLSRoutes that references
-// the Service using `.spec.rules.backendRefs`. The affected TLSRoutes are then
-// pushed for reconciliation.
-func (r *tlsRouteReconciler) getTLSRoutesForService(obj client.Object) []reconcile.Request {
-	affectedTLSRouteList := &gwapiv1a2.TLSRouteList{}
-
-	if err := r.client.List(context.Background(), affectedTLSRouteList, &client.ListOptions{
-		FieldSelector: fields.OneTermEqualSelector(serviceTLSRouteIndex, utils.NamespacedName(obj).String()),
-	}); err != nil {
-		return []reconcile.Request{}
-	}
-
-	requests := make([]reconcile.Request, len(affectedTLSRouteList.Items))
-	for i, item := range affectedTLSRouteList.Items {
-		requests[i] = reconcile.Request{
-			NamespacedName: utils.NamespacedName(item.DeepCopy()),
-		}
-	}
-
-	return requests
-}
-
-func (r *tlsRouteReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
-	log := r.log.WithValues("namespace", request.Namespace, "name", request.Name)
-
-	log.Info("reconciling tlsroute")
-
-	// Fetch all TLSRoutes from the cache.
-	routeList := &gwapiv1a2.TLSRouteList{}
-	if err := r.client.List(ctx, routeList); err != nil {
-		return reconcile.Result{}, fmt.Errorf("error listing tlsroutes")
-	}
-
-	found := false
-	for i := range routeList.Items {
-		// See if this route from the list matched the reconciled route.
-		route := routeList.Items[i]
-		routeKey := utils.NamespacedName(&route)
-		if routeKey == request.NamespacedName {
-			found = true
-		}
-
-		// Store the tlsroute in the resource map.
-		r.resources.TLSRoutes.Store(routeKey, &route)
-		log.Info("added tlsroute to resource map")
-
-		// Get the route's namespace from the cache.
-		nsKey := types.NamespacedName{Name: route.Namespace}
-		ns := new(corev1.Namespace)
-		if err := r.client.Get(ctx, nsKey, ns); err != nil {
-			if errors.IsNotFound(err) {
-				// The route's namespace doesn't exist in the cache, so remove it from
-				// the namespace resource map if it exists.
-				if _, ok := r.resources.Namespaces.Load(nsKey.Name); ok {
-					r.resources.Namespaces.Delete(nsKey.Name)
-					log.Info("deleted namespace from resource map")
-				}
-			}
-			return reconcile.Result{}, fmt.Errorf("failed to get namespace %s", nsKey.Name)
-		}
-
-		// The route's namespace exists, so add it to the resource map.
-		r.resources.Namespaces.Store(nsKey.Name, ns)
-		log.Info("added namespace to resource map")
-
-		// Get the route's backendRefs from the cache. Note that a Service is the
-		// only supported kind.
-		for i := range route.Spec.Rules {
-			for j := range route.Spec.Rules[i].BackendRefs {
-				ref := route.Spec.Rules[i].BackendRefs[j]
-				if err := validateTLSRouteBackendRef(&ref); err != nil {
-					return reconcile.Result{}, fmt.Errorf("invalid backendRef: %w", err)
-				}
-
-				// The backendRef is valid, so get the referenced service from the cache.
-				svcKey := types.NamespacedName{Namespace: route.Namespace, Name: string(ref.Name)}
-				svc := new(corev1.Service)
-				if err := r.client.Get(ctx, svcKey, svc); err != nil {
-					if errors.IsNotFound(err) {
-						// The ref's service doesn't exist in the cache, so remove it from
-						// the resource map if it exists.
-						if _, ok := r.resources.Services.Load(svcKey); ok {
-							r.resources.Services.Delete(svcKey)
-							r.referenceStore.removeRouteToServicesMapping(
-								ObjectKindNamespacedName{kindTLSRoute, route.Namespace, route.Name},
-								svcKey,
-							)
-							log.Info("deleted service from resource map")
-						}
-					}
-					return reconcile.Result{}, fmt.Errorf("failed to get service %s/%s",
-						svcKey.Namespace, svcKey.Name)
-				}
-
-				// The backendRef Service exists, so add it to the resource map.
-				r.resources.Services.Store(svcKey, svc)
-				r.referenceStore.updateRouteToServicesMapping(
-					ObjectKindNamespacedName{kindTLSRoute, route.Namespace, route.Name},
-					svcKey,
-				)
-				log.Info("added service to resource map")
-			}
-		}
-	}
-
-	if !found {
-		// Delete the tlsroute from the resource map.
-		r.resources.TLSRoutes.Delete(request.NamespacedName)
-		log.Info("deleted tlsroute from resource map")
-
-		// Delete the Namespace from the resource maps if no other
-		// routes (TLSRoute/HTTPRoute) exist in the namespace.
-		if found, err := isRoutePresentInNamespace(ctx, r.client, request.NamespacedName.Namespace); err != nil {
-			return reconcile.Result{}, err
-		} else if !found {
-			r.resources.Namespaces.Delete(request.Namespace)
-			log.Info("deleted namespace from resource map")
-		}
-
-		// Delete the Service from the resource maps if no other
-		// routes (TLSRoute or HTTPRoute) reference that Service.
-		routeServices := r.referenceStore.getRouteToServicesMapping(ObjectKindNamespacedName{kindTLSRoute, request.Namespace, request.Name})
-		for svc := range routeServices {
-			r.referenceStore.removeRouteToServicesMapping(ObjectKindNamespacedName{kindTLSRoute, request.Namespace, request.Name}, svc)
-			if !r.referenceStore.isServiceReferredByRoutes(svc) {
-				r.resources.Services.Delete(svc)
-				log.Info("deleted service from resource map", "namespace", svc.Namespace, "name", svc.Name)
-			}
-		}
-	}
-
-	log.Info("reconciled tlsroute")
-
-	return reconcile.Result{}, nil
-}
-
-// validateTLSRouteBackendRef validates that ref is a reference to a local Service.
-func validateTLSRouteBackendRef(ref *gwapiv1a2.BackendRef) error {
-	switch {
-	case ref == nil:
-		return nil
-	case ref.Group != nil && *ref.Group != corev1.GroupName:
-		return fmt.Errorf("invalid group; must be nil or empty string")
-	case ref.Kind != nil && *ref.Kind != gatewayapi.KindService:
-		return fmt.Errorf("invalid kind %q; must be %q",
-			*ref.BackendObjectReference.Kind, gatewayapi.KindService)
-	case ref.Namespace != nil:
-		return fmt.Errorf("invalid namespace; must be nil")
-	}
-
-	return nil
-}
-
-// subscribeAndUpdateStatus subscribes to tlsroute status updates and writes it into the
-// Kubernetes API Server
-func (r *tlsRouteReconciler) subscribeAndUpdateStatus(ctx context.Context) {
-	// Subscribe to resources
-	message.HandleSubscription(r.resources.TLSRouteStatuses.Subscribe(ctx),
-		func(update message.Update[types.NamespacedName, *gwapiv1a2.TLSRoute]) {
-			// skip delete updates.
-			if update.Delete {
-				return
-			}
-			key := update.Key
-			val := update.Value
-			r.statusUpdater.Send(status.Update{
-				NamespacedName: key,
-				Resource:       new(gwapiv1a2.TLSRoute),
-				Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
-					t, ok := obj.(*gwapiv1a2.TLSRoute)
-					if !ok {
-						panic(fmt.Sprintf("unsupported object type %T", obj))
-					}
-					tCopy := t.DeepCopy()
-					tCopy.Status.Parents = val.Status.Parents
-					return tCopy
-				}),
-			})
-		},
-	)
-	r.log.Info("status subscriber shutting down")
-}
diff --git a/internal/status/status.go b/internal/status/status.go
index 58dca4b57..c84fee2f9 100644
--- a/internal/status/status.go
+++ b/internal/status/status.go
@@ -19,6 +19,7 @@ import (
 	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/util/retry"
@@ -75,6 +76,9 @@ func (u *UpdateHandler) apply(update Update) {
 
 		// Get the resource.
 		if err := u.client.Get(context.Background(), update.NamespacedName, obj); err != nil {
+			if kerrors.IsNotFound(err) {
+				return nil
+			}
 			return err
 		}
 
@@ -151,6 +155,7 @@ func (u *UpdateWriter) Send(update Update) {
 // isStatusEqual checks if two objects have equivalent status.
 //
 // Supported objects:
+//
 //  GatewayClasses
 //  Gateway
 //  HTTPRoute
diff --git a/tools/make/kube.mk b/tools/make/kube.mk
index 5630c13bb..d0d55d22f 100644
--- a/tools/make/kube.mk
+++ b/tools/make/kube.mk
@@ -31,7 +31,7 @@ manifests: $(tools/controller-gen) ## Generate WebhookConfiguration, ClusterRole
 generate: $(tools/controller-gen) ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
 # Note that the paths can't just be "./..." with the header file, or the tool will panic on run. Sorry.
 	@$(LOG_TARGET)
-	$(tools/controller-gen) $(CONTROLLERGEN_OBJECT_FLAGS) paths="{$(ROOT_DIR)/api/config/...,$(ROOT_DIR)/internal/ir/...}" 
+	$(tools/controller-gen) $(CONTROLLERGEN_OBJECT_FLAGS) paths="{$(ROOT_DIR)/api/config/...,$(ROOT_DIR)/internal/ir/...,$(ROOT_DIR)/internal/gatewayapi/...}" 
 
 .PHONY: kube-test
 kube-test: manifests generate $(tools/setup-envtest) ## Run Kubernetes provider tests.


Test patch:
diff --git a/docs/latest/user/quickstart.md b/docs/latest/user/quickstart.md
index 993003e24..2dbdde29c 100644
--- a/docs/latest/user/quickstart.md
+++ b/docs/latest/user/quickstart.md
@@ -16,6 +16,12 @@ Install the Gateway API CRDs and Envoy Gateway:
 kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/install.yaml
 ```
 
+Wait for Envoy Gateway to become available:
+
+```shell
+kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available
+```
+
 Install the GatewayClass, Gateway, HTTPRoute and example app:
 
 ```shell
diff --git a/internal/gatewayapi/runner/runner_test.go b/internal/gatewayapi/runner/runner_test.go
index c6a84eb5f..c1ee328c8 100644
--- a/internal/gatewayapi/runner/runner_test.go
+++ b/internal/gatewayapi/runner/runner_test.go
@@ -13,7 +13,6 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
-	"k8s.io/apimachinery/pkg/types"
 
 	"github.com/envoyproxy/gateway/internal/envoygateway/config"
 	"github.com/envoyproxy/gateway/internal/ir"
@@ -45,8 +44,7 @@ func TestRunner(t *testing.T) {
 	// TODO: pass valid provider resources
 
 	// Delete gateway from the map.
-	key := types.NamespacedName{Namespace: "test", Name: "test"}
-	pResources.Gateways.Delete(key)
+	pResources.GatewayAPIResources.Delete("test")
 	require.Eventually(t, func() bool {
 		out := xdsIR.LoadAll()
 		if out == nil {
diff --git a/internal/message/types_test.go b/internal/message/types_test.go
deleted file mode 100644
index 9525efac8..000000000
--- a/internal/message/types_test.go
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright Envoy Gateway Authors
-// SPDX-License-Identifier: Apache-2.0
-// The full text of the Apache license is available in the LICENSE file at
-// the root of the repo.
-
-package message
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
-	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
-)
-
-func TestProviderResources(t *testing.T) {
-	resources := new(ProviderResources)
-	ns1 := &corev1.Namespace{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "test-ns1",
-		},
-	}
-	gc1 := &gwapiv1b1.GatewayClass{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "test-gc1",
-		},
-	}
-	gw1 := &gwapiv1b1.Gateway{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "test1",
-			Namespace: "test",
-		},
-	}
-	r1 := &gwapiv1b1.HTTPRoute{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "route1",
-			Namespace: "test",
-		},
-	}
-	t1 := &gwapiv1a2.TLSRoute{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "tlsroute1",
-			Namespace: "test",
-		},
-	}
-	s1 := &corev1.Service{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "service1",
-			Namespace: "test",
-		},
-	}
-
-	// Check init state
-	assert.Nil(t, resources.GetNamespaces())
-	assert.Nil(t, resources.GetGatewayClasses())
-	assert.Nil(t, resources.GetGateways())
-	assert.Nil(t, resources.GetHTTPRoutes())
-	assert.Nil(t, resources.GetTLSRoutes())
-	assert.Nil(t, resources.GetServices())
-
-	// Add resources
-	resources.Namespaces.Store("test-ns1", ns1)
-	resources.GatewayClasses.Store("test-gc1", gc1)
-
-	gw1Key := types.NamespacedName{
-		Namespace: gw1.GetNamespace(),
-		Name:      gw1.GetName(),
-	}
-	resources.Gateways.Store(gw1Key, gw1)
-
-	r1Key := types.NamespacedName{
-		Namespace: r1.GetNamespace(),
-		Name:      r1.GetName(),
-	}
-	resources.HTTPRoutes.Store(r1Key, r1)
-
-	t1Key := types.NamespacedName{
-		Namespace: t1.GetNamespace(),
-		Name:      t1.GetName(),
-	}
-	resources.TLSRoutes.Store(t1Key, t1)
-
-	s1Key := types.NamespacedName{
-		Namespace: s1.GetNamespace(),
-		Name:      s1.GetName(),
-	}
-	resources.Services.Store(s1Key, s1)
-
-	// Test
-	namespaces := resources.GetNamespaces()
-	assert.Equal(t, len(namespaces), 1)
-
-	gcs := resources.GetGatewayClasses()
-	assert.Equal(t, len(gcs), 1)
-
-	gws := resources.GetGateways()
-	assert.Equal(t, len(gws), 1)
-
-	hrs := resources.GetHTTPRoutes()
-	assert.Equal(t, len(hrs), 1)
-
-	trs := resources.GetTLSRoutes()
-	assert.Equal(t, len(trs), 1)
-
-	svcs := resources.GetServices()
-	assert.Equal(t, len(svcs), 1)
-
-	// Add more resources
-	ns2 := &corev1.Namespace{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "test-ns2",
-		},
-	}
-
-	gc2 := &gwapiv1b1.GatewayClass{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "test-gc2",
-		},
-	}
-	gw2 := &gwapiv1b1.Gateway{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "test2",
-			Namespace: "test",
-		},
-	}
-	r2 := &gwapiv1b1.HTTPRoute{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "route2",
-			Namespace: "test",
-		},
-	}
-	t2 := &gwapiv1a2.TLSRoute{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "tlsroute2",
-			Namespace: "test",
-		},
-	}
-	s2 := &corev1.Service{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "service2",
-			Namespace: "test",
-		},
-	}
-
-	resources.Namespaces.Store("test-ns2", ns2)
-	resources.GatewayClasses.Store("test-gc2", gc2)
-	gw2Key := types.NamespacedName{
-		Namespace: gw2.GetNamespace(),
-		Name:      gw2.GetName(),
-	}
-	resources.Gateways.Store(gw2Key, gw2)
-
-	r2Key := types.NamespacedName{
-		Namespace: r2.GetNamespace(),
-		Name:      r2.GetName(),
-	}
-	resources.HTTPRoutes.Store(r2Key, r2)
-
-	t2Key := types.NamespacedName{
-		Namespace: t2.GetNamespace(),
-		Name:      t2.GetName(),
-	}
-	resources.TLSRoutes.Store(t2Key, t2)
-
-	s2Key := types.NamespacedName{
-		Namespace: s2.GetNamespace(),
-		Name:      s2.GetName(),
-	}
-	resources.Services.Store(s2Key, s2)
-
-	// Test contents
-	namespaces = resources.GetNamespaces()
-	assert.ElementsMatch(t, namespaces, []*corev1.Namespace{ns1, ns2})
-
-	gcs = resources.GetGatewayClasses()
-	assert.ElementsMatch(t, gcs, []*gwapiv1b1.GatewayClass{gc1, gc2})
-
-	gws = resources.GetGateways()
-	assert.ElementsMatch(t, gws, []*gwapiv1b1.Gateway{gw1, gw2})
-
-	hrs = resources.GetHTTPRoutes()
-	assert.ElementsMatch(t, hrs, []*gwapiv1b1.HTTPRoute{r1, r2})
-
-	trs = resources.GetTLSRoutes()
-	assert.ElementsMatch(t, trs, []*gwapiv1a2.TLSRoute{t1, t2})
-
-	svcs = resources.GetServices()
-	assert.ElementsMatch(t, svcs, []*corev1.Service{s1, s2})
-}
diff --git a/internal/provider/kubernetes/gateway_test.go b/internal/provider/kubernetes/gateway_test.go
index 7d7e1fe25..d2b9e897b 100644
--- a/internal/provider/kubernetes/gateway_test.go
+++ b/internal/provider/kubernetes/gateway_test.go
@@ -7,140 +7,18 @@ package kubernetes
 
 import (
 	"context"
-	"fmt"
 	"testing"
 
 	"github.com/stretchr/testify/require"
-	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
-	"sigs.k8s.io/controller-runtime/pkg/client"
 	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
-	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
 	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
 
 	"github.com/envoyproxy/gateway/api/config/v1alpha1"
 	"github.com/envoyproxy/gateway/internal/envoygateway"
-	"github.com/envoyproxy/gateway/internal/gatewayapi"
-	"github.com/envoyproxy/gateway/internal/log"
 )
 
-func TestGatewayHasMatchingController(t *testing.T) {
-	match := &gwapiv1b1.GatewayClass{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "matched",
-		},
-		Spec: gwapiv1b1.GatewayClassSpec{
-			ControllerName: v1alpha1.GatewayControllerName,
-		},
-	}
-
-	nonMatch := &gwapiv1b1.GatewayClass{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "non-matched",
-		},
-		Spec: gwapiv1b1.GatewayClassSpec{
-			ControllerName: "not.configured/controller-name",
-		},
-	}
-
-	testCases := []struct {
-		name   string
-		obj    client.Object
-		expect bool
-	}{
-		{
-			name: "matching object type, gatewayclass, and controller name",
-			obj: &gwapiv1b1.Gateway{
-				TypeMeta: metav1.TypeMeta{
-					Kind:       "Gateway",
-					APIVersion: fmt.Sprintf("%s/%s", gwapiv1b1.GroupName, gwapiv1b1.GroupVersion.Version),
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test",
-					Namespace: "test",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: gwapiv1b1.ObjectName(match.Name),
-				},
-			},
-			expect: true,
-		},
-		{
-			name: "matching object type but gatewayclass doesn't exist",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test",
-					Namespace: "test",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "non-existent-gc",
-				},
-			},
-			expect: false,
-		},
-		{
-			name: "matching object type and gatewayclass but not controller name",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test",
-					Namespace: "test",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: gwapiv1b1.ObjectName(nonMatch.Name),
-				},
-			},
-			expect: false,
-		},
-		{
-			name: "gatewayclass name match but object type doesn't match",
-			obj: &gwapiv1b1.GatewayClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "test",
-				},
-				Spec: gwapiv1b1.GatewayClassSpec{
-					ControllerName: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName),
-				},
-			},
-			expect: false,
-		},
-		{
-			name: "matching but very long name",
-			obj: &gwapiv1b1.Gateway{
-				TypeMeta: metav1.TypeMeta{
-					Kind:       "Gateway",
-					APIVersion: fmt.Sprintf("%s/%s", gwapiv1b1.GroupName, gwapiv1b1.GroupVersion.Version),
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "superdupermegalongnamethatisridiculouslylongandwaylongerthanitshouldeverbeinsideofkubernetes",
-					Namespace: "test",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: gwapiv1b1.ObjectName(match.Name),
-				},
-			},
-			expect: true,
-		},
-	}
-
-	// Create the reconciler.
-	logger, err := log.NewLogger()
-	require.NoError(t, err)
-	r := gatewayReconciler{
-		classController: v1alpha1.GatewayControllerName,
-		log:             logger,
-	}
-
-	for _, tc := range testCases {
-		tc := tc
-		t.Run(tc.name, func(t *testing.T) {
-			r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(match, nonMatch, tc.obj).Build()
-			actual := r.hasMatchingController(tc.obj)
-			require.Equal(t, tc.expect, actual)
-		})
-	}
-}
-
 func TestIsAccepted(t *testing.T) {
 	testCases := []struct {
 		name   string
@@ -364,7 +242,7 @@ func TestAddFinalizer(t *testing.T) {
 	}
 
 	// Create the reconciler.
-	r := new(gatewayReconciler)
+	r := new(gatewayAPIReconciler)
 	ctx := context.Background()
 
 	for _, tc := range testCases {
@@ -428,7 +306,7 @@ func TestRemoveFinalizer(t *testing.T) {
 	}
 
 	// Create the reconciler.
-	r := new(gatewayReconciler)
+	r := new(gatewayAPIReconciler)
 	ctx := context.Background()
 
 	for _, tc := range testCases {
@@ -444,422 +322,3 @@ func TestRemoveFinalizer(t *testing.T) {
 		})
 	}
 }
-
-func TestSecretsAndRefGrantsForGateway(t *testing.T) {
-	testCases := []struct {
-		name      string
-		gw        *gwapiv1b1.Gateway
-		secrets   []corev1.Secret
-		refGrants []gwapiv1a2.ReferenceGrant
-	}{
-		{
-			name: "gateway with no https listeners",
-			gw: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-gw",
-					Namespace: "test-ns",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "test-gc",
-					Listeners: []gwapiv1b1.Listener{
-						{
-							Name:     "http",
-							Port:     gwapiv1b1.PortNumber(int32(80)),
-							Protocol: gwapiv1b1.HTTPProtocolType,
-						},
-					},
-				},
-			},
-		},
-		{
-			name: "gateway with one https listener and one same namespace secret",
-			gw: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-gw",
-					Namespace: "test-ns",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "test-gc",
-					Listeners: []gwapiv1b1.Listener{
-						{
-							Name:     "tls",
-							Port:     gwapiv1b1.PortNumber(int32(443)),
-							Protocol: gwapiv1b1.HTTPSProtocolType,
-							TLS: &gwapiv1b1.GatewayTLSConfig{
-								Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate),
-								CertificateRefs: []gwapiv1b1.SecretObjectReference{
-									{
-										Name: gwapiv1b1.ObjectName("test-secret"),
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []corev1.Secret{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-			},
-		},
-		{
-			name: "gateway with one http and one https listener with one same namespace secret",
-			gw: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-gw",
-					Namespace: "test-ns",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "test-gc",
-					Listeners: []gwapiv1b1.Listener{
-						{
-							Name:     "http",
-							Port:     gwapiv1b1.PortNumber(int32(80)),
-							Protocol: gwapiv1b1.HTTPProtocolType,
-						},
-						{
-							Name:     "tls",
-							Port:     gwapiv1b1.PortNumber(int32(443)),
-							Protocol: gwapiv1b1.HTTPSProtocolType,
-							TLS: &gwapiv1b1.GatewayTLSConfig{
-								Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate),
-								CertificateRefs: []gwapiv1b1.SecretObjectReference{
-									{
-										Name: gwapiv1b1.ObjectName("test-secret"),
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []corev1.Secret{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-			},
-		},
-		{
-			name: "gateway with two https listeners each with two same namespace secrets",
-			gw: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-gw",
-					Namespace: "test-ns",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "test-gc",
-					Listeners: []gwapiv1b1.Listener{
-						{
-							Name:     "tls1",
-							Port:     gwapiv1b1.PortNumber(int32(443)),
-							Protocol: gwapiv1b1.HTTPSProtocolType,
-							TLS: &gwapiv1b1.GatewayTLSConfig{
-								Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate),
-								CertificateRefs: []gwapiv1b1.SecretObjectReference{
-									{
-										Name: gwapiv1b1.ObjectName("test-secret1"),
-									},
-									{
-										Name: gwapiv1b1.ObjectName("test-secret2"),
-									},
-								},
-							},
-						},
-						{
-							Name:     "tls2",
-							Port:     gwapiv1b1.PortNumber(int32(443)),
-							Protocol: gwapiv1b1.HTTPSProtocolType,
-							TLS: &gwapiv1b1.GatewayTLSConfig{
-								Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate),
-								CertificateRefs: []gwapiv1b1.SecretObjectReference{
-									{
-										Name: gwapiv1b1.ObjectName("test-secret3"),
-									},
-									{
-										Name: gwapiv1b1.ObjectName("test-secret4"),
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []corev1.Secret{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret1",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret2",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret3",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret4",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-			},
-		},
-		{
-			name: "gateway with one https listener and two same namespace secret",
-			gw: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-gw",
-					Namespace: "test-ns",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "test-gc",
-					Listeners: []gwapiv1b1.Listener{
-						{
-							Name:     "tls",
-							Port:     gwapiv1b1.PortNumber(int32(443)),
-							Protocol: gwapiv1b1.HTTPSProtocolType,
-							TLS: &gwapiv1b1.GatewayTLSConfig{
-								Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate),
-								CertificateRefs: []gwapiv1b1.SecretObjectReference{
-									{
-										Name: gwapiv1b1.ObjectName("test-secret1"),
-									},
-									{
-										Name: gwapiv1b1.ObjectName("test-secret2"),
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []corev1.Secret{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret1",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret2",
-						Namespace:       "test-ns",
-						ResourceVersion: "1",
-					},
-				},
-			},
-		},
-		{
-			name: "gateway with one https listener and one different namespace secret",
-			gw: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-gw",
-					Namespace: "test-ns",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "test-gc",
-					Listeners: []gwapiv1b1.Listener{
-						{
-							Name:     "tls",
-							Port:     gwapiv1b1.PortNumber(int32(443)),
-							Protocol: gwapiv1b1.HTTPSProtocolType,
-							TLS: &gwapiv1b1.GatewayTLSConfig{
-								Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate),
-								CertificateRefs: []gwapiv1b1.SecretObjectReference{
-									{
-										Name:      gwapiv1b1.ObjectName("test-secret"),
-										Namespace: gatewayapi.NamespacePtr("test-ns2"),
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []corev1.Secret{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret",
-						Namespace:       "test-ns2",
-						ResourceVersion: "1",
-					},
-				},
-			},
-			refGrants: []gwapiv1a2.ReferenceGrant{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "ReferenceGrant",
-						APIVersion: gwapiv1a2.GroupVersion.Version,
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "test-refgrant",
-						Namespace: "test-ns2",
-					},
-					Spec: gwapiv1a2.ReferenceGrantSpec{
-						From: []gwapiv1a2.ReferenceGrantFrom{
-							{
-								Group:     gwapiv1a2.GroupName,
-								Kind:      gatewayapi.KindGateway,
-								Namespace: gwapiv1a2.Namespace("test-ns"),
-							},
-						},
-						To: []gwapiv1a2.ReferenceGrantTo{
-							{
-								Group: corev1.GroupName,
-								Kind:  gatewayapi.KindSecret,
-							},
-						},
-					},
-				},
-			},
-		},
-		{
-			name: "gateway with one https listener and one different namespace secret with specific referencegrant",
-			gw: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-gw",
-					Namespace: "test-ns",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "test-gc",
-					Listeners: []gwapiv1b1.Listener{
-						{
-							Name:     "tls",
-							Port:     gwapiv1b1.PortNumber(int32(443)),
-							Protocol: gwapiv1b1.HTTPSProtocolType,
-							TLS: &gwapiv1b1.GatewayTLSConfig{
-								Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate),
-								CertificateRefs: []gwapiv1b1.SecretObjectReference{
-									{
-										Name:      gwapiv1b1.ObjectName("test-secret"),
-										Namespace: gatewayapi.NamespacePtr("test-ns2"),
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []corev1.Secret{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:            "test-secret",
-						Namespace:       "test-ns2",
-						ResourceVersion: "1",
-					},
-				},
-			},
-			refGrants: []gwapiv1a2.ReferenceGrant{
-				{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "ReferenceGrant",
-						APIVersion: gwapiv1a2.GroupVersion.Version,
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "test-refgrant",
-						Namespace: "test-ns2",
-					},
-					Spec: gwapiv1a2.ReferenceGrantSpec{
-						From: []gwapiv1a2.ReferenceGrantFrom{
-							{
-								Group:     gwapiv1a2.GroupName,
-								Kind:      gatewayapi.KindGateway,
-								Namespace: gwapiv1a2.Namespace("test-ns"),
-							},
-						},
-						To: []gwapiv1a2.ReferenceGrantTo{
-							{
-								Group: corev1.GroupName,
-								Kind:  gatewayapi.KindSecret,
-								Name:  gatewayapi.ObjectNamePtr("test-secret"),
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-
-	// Create the reconciler.
-	r := new(gatewayReconciler)
-	ctx := context.Background()
-
-	for i := range testCases {
-		tc := testCases[i]
-		t.Run(tc.name, func(t *testing.T) {
-			var objs []client.Object
-			for j := range tc.secrets {
-				objs = append(objs, &tc.secrets[j])
-			}
-			for k := range tc.refGrants {
-				objs = append(objs, &tc.refGrants[k])
-			}
-			r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(objs...).Build()
-			secrets, refGrants, err := r.secretsAndRefGrantsForGateway(ctx, tc.gw)
-			require.NoError(t, err)
-			require.Equal(t, tc.secrets, secrets)
-			require.Equal(t, tc.refGrants, refGrants)
-		})
-	}
-}
diff --git a/internal/provider/kubernetes/gatewayclass_test.go b/internal/provider/kubernetes/gatewayclass_test.go
index 4200a2379..9f5b79a46 100644
--- a/internal/provider/kubernetes/gatewayclass_test.go
+++ b/internal/provider/kubernetes/gatewayclass_test.go
@@ -53,9 +53,9 @@ func TestGatewayClassHasMatchingController(t *testing.T) {
 	// Create the reconciler.
 	logger, err := log.NewLogger()
 	require.NoError(t, err)
-	r := gatewayClassReconciler{
-		controller: v1alpha1.GatewayControllerName,
-		log:        logger,
+	r := gatewayAPIReconciler{
+		classController: v1alpha1.GatewayControllerName,
+		log:             logger,
 	}
 
 	for _, tc := range testCases {
diff --git a/internal/provider/kubernetes/httproute_test.go b/internal/provider/kubernetes/httproute_test.go
index 605213984..ac973a079 100644
--- a/internal/provider/kubernetes/httproute_test.go
+++ b/internal/provider/kubernetes/httproute_test.go
@@ -12,392 +12,15 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 	gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
 
 	"github.com/envoyproxy/gateway/api/config/v1alpha1"
 	"github.com/envoyproxy/gateway/internal/envoygateway"
 	"github.com/envoyproxy/gateway/internal/gatewayapi"
-	"github.com/envoyproxy/gateway/internal/log"
 )
 
-func TestGetHTTPRoutesForGateway(t *testing.T) {
-	testCases := []struct {
-		name    string
-		obj     client.Object
-		routes  []gwapiv1b1.HTTPRoute
-		classes []gwapiv1b1.GatewayClass
-		expect  []reconcile.Request
-	}{
-		{
-			name: "valid route",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "test",
-					Name:      "gw1",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "gc1",
-				},
-			},
-			routes: []gwapiv1b1.HTTPRoute{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test",
-						Name:      "h1",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:  gatewayapi.KindPtr("Gateway"),
-									Name:  gwapiv1b1.ObjectName("gw1"),
-								},
-							},
-						},
-					},
-				},
-			},
-			classes: []gwapiv1b1.GatewayClass{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "gc1",
-					},
-					Spec: gwapiv1b1.GatewayClassSpec{
-						ControllerName: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName),
-					},
-				},
-			},
-			expect: []reconcile.Request{
-				{
-					NamespacedName: types.NamespacedName{
-						Namespace: "test",
-						Name:      "h1",
-					},
-				},
-			},
-		},
-		{
-			name: "one valid route in different namespace",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "test",
-					Name:      "gw1",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "gc1",
-				},
-			},
-			routes: []gwapiv1b1.HTTPRoute{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test2",
-						Name:      "h1",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group:     gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:      gatewayapi.KindPtr("Gateway"),
-									Name:      gwapiv1b1.ObjectName("gw1"),
-									Namespace: gatewayapi.NamespacePtr("test"),
-								},
-							},
-						},
-					},
-				},
-			},
-			classes: []gwapiv1b1.GatewayClass{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "gc1",
-					},
-					Spec: gwapiv1b1.GatewayClassSpec{
-						ControllerName: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName),
-					},
-				},
-			},
-			expect: []reconcile.Request{
-				{
-					NamespacedName: types.NamespacedName{
-						Namespace: "test2",
-						Name:      "h1",
-					},
-				},
-			},
-		},
-		{
-			name: "two valid routes",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "test",
-					Name:      "gw1",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "gc1",
-				},
-			},
-			routes: []gwapiv1b1.HTTPRoute{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test",
-						Name:      "h1",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:  gatewayapi.KindPtr("Gateway"),
-									Name:  gwapiv1b1.ObjectName("gw1"),
-								},
-							},
-						},
-					},
-				},
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test",
-						Name:      "h2",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:  gatewayapi.KindPtr("Gateway"),
-									Name:  gwapiv1b1.ObjectName("gw1"),
-								},
-							},
-						},
-					},
-				},
-			},
-			classes: []gwapiv1b1.GatewayClass{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "gc1",
-					},
-					Spec: gwapiv1b1.GatewayClassSpec{
-						ControllerName: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName),
-					},
-				},
-			},
-			expect: []reconcile.Request{
-				{
-					NamespacedName: types.NamespacedName{
-						Namespace: "test",
-						Name:      "h1",
-					},
-				},
-				{
-					NamespacedName: types.NamespacedName{
-						Namespace: "test",
-						Name:      "h2",
-					},
-				},
-			},
-		},
-		{
-			name: "object referenced unmanaged gateway",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "test",
-					Name:      "gw1",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "gc1",
-				},
-			},
-			routes: []gwapiv1b1.HTTPRoute{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test",
-						Name:      "h1",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:  gatewayapi.KindPtr("Gateway"),
-									Name:  gwapiv1b1.ObjectName("gw1"),
-								},
-							},
-						},
-					},
-				},
-			},
-			classes: []gwapiv1b1.GatewayClass{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "gc1",
-					},
-					Spec: gwapiv1b1.GatewayClassSpec{
-						ControllerName: gwapiv1b1.GatewayController("unmanaged.controller"),
-					},
-				},
-			},
-			expect: []reconcile.Request{},
-		},
-		{
-			name: "valid route",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "test",
-					Name:      "gw1",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "gc1",
-				},
-			},
-			routes: []gwapiv1b1.HTTPRoute{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test",
-						Name:      "h1",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:  gatewayapi.KindPtr("Gateway"),
-									Name:  gwapiv1b1.ObjectName("gw1"),
-								},
-							},
-						},
-					},
-				},
-			},
-			classes: []gwapiv1b1.GatewayClass{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "gc1",
-					},
-					Spec: gwapiv1b1.GatewayClassSpec{
-						ControllerName: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName),
-					},
-				},
-			},
-			expect: []reconcile.Request{
-				{
-					NamespacedName: types.NamespacedName{
-						Namespace: "test",
-						Name:      "h1",
-					},
-				},
-			},
-		},
-		{
-			name: "no valid routes",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "test",
-					Name:      "gw1",
-				},
-				Spec: gwapiv1b1.GatewaySpec{
-					GatewayClassName: "gc1",
-				},
-			},
-			routes: []gwapiv1b1.HTTPRoute{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test",
-						Name:      "h1",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:  gatewayapi.KindPtr("UnsupportedKind"),
-									Name:  gwapiv1b1.ObjectName("unsupported"),
-								},
-							},
-						},
-					},
-				},
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: "test",
-						Name:      "h2",
-					},
-					Spec: gwapiv1b1.HTTPRouteSpec{
-						CommonRouteSpec: gwapiv1b1.CommonRouteSpec{
-							ParentRefs: []gwapiv1b1.ParentReference{
-								{
-									Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName),
-									Kind:  gatewayapi.KindPtr("UnsupportedKind"),
-									Name:  gwapiv1b1.ObjectName("unsupported2"),
-								},
-							},
-						},
-					},
-				},
-			},
-			classes: []gwapiv1b1.GatewayClass{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "gc1",
-					},
-					Spec: gwapiv1b1.GatewayClassSpec{
-						ControllerName: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName),
-					},
-				},
-			},
-			expect: []reconcile.Request{},
-		},
-		{
-			name: "no routes",
-			obj: &gwapiv1b1.Gateway{
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "test",
-					Name:      "gw1",
-				},
-			},
-			expect: []reconcile.Request{},
-		},
-		{
-			name: "invalid object type",
-			obj: &gwapiv1b1.GatewayClass{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "gc1",
-				},
-			},
-			expect: []reconcile.Request{},
-		},
-	}
-
-	// Create the reconciler.
-	logger, err := log.NewLogger()
-	require.NoError(t, err)
-	r := &httpRouteReconciler{
-		log:             logger,
-		classController: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName)}
-
-	for i := range testCases {
-		tc := testCases[i]
-		t.Run(tc.name, func(t *testing.T) {
-			objs := []client.Object{tc.obj}
-			for i := range tc.routes {
-				objs = append(objs, &tc.routes[i])
-			}
-			for i := range tc.classes {
-				objs = append(objs, &tc.classes[i])
-			}
-			r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(objs...).Build()
-			reqs := r.getHTTPRoutesForGateway(tc.obj)
-			assert.Equal(t, tc.expect, reqs)
-		})
-	}
-}
-
 func TestValidateParentRefs(t *testing.T) {
 	testCases := []struct {
 		name     string
@@ -723,7 +346,7 @@ func TestValidateParentRefs(t *testing.T) {
 	}
 
 	// Create the reconciler.
-	r := &httpRouteReconciler{classController: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName)}
+	r := &gatewayAPIReconciler{classController: gwapiv1b1.GatewayController(v1alpha1.GatewayControllerName)}
 	ctx := context.Background()
 
 	for _, tc := range testCases {
diff --git a/internal/provider/kubernetes/kubernetes_test.go b/internal/provider/kubernetes/kubernetes_test.go
index 13202eed5..17db1e65d 100644
--- a/internal/provider/kubernetes/kubernetes_test.go
+++ b/internal/provider/kubernetes/kubernetes_test.go
@@ -6,8 +6,6 @@
 // The full text of the Apache license is available in the LICENSE file at
 // the root of the repo.
 
-
-
 package kubernetes
 
 import (
@@ -20,6 +18,8 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	"golang.org/x/exp/slices"
+	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -35,7 +35,6 @@ import (
 	"github.com/envoyproxy/gateway/internal/envoygateway/config"
 	"github.com/envoyproxy/gateway/internal/gatewayapi"
 	"github.com/envoyproxy/gateway/internal/message"
-	"github.com/envoyproxy/gateway/internal/provider/utils"
 )
 
 const (
@@ -163,9 +162,12 @@ func testGatewayClassAcceptedStatus(ctx context.Context, t *testing.T, provider
 		return false
 	}, defaultWait, defaultTick)
 
-	// Ensure the GatewayClass resource map contains the gatewayclass under test.
-	gcs, _ := resources.GatewayClasses.Load(gc.Name)
-	assert.Equal(t, gc, gcs)
+	// Even though no gateways exist, the controller loads the empty resource map
+	// to support gateway deletions.
+	require.Eventually(t, func() bool {
+		_, ok := resources.GatewayAPIResources.Load(gc.Name)
+		return ok
+	}, defaultWait, defaultTick)
 }
 
 func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) {
@@ -215,6 +217,60 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro
 	}
 	require.NoError(t, cli.Create(ctx, gw))
 
+	labels := map[string]string{
+		gatewayapi.OwningGatewayNameLabel:      gw.Name,
+		gatewayapi.OwningGatewayNamespaceLabel: gw.Namespace,
+	}
+
+	deploy := &appsv1.Deployment{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: gw.Namespace,
+			Name:      gw.Name + "-deployment",
+			Labels:    labels,
+		},
+		Spec: appsv1.DeploymentSpec{
+			Selector: &metav1.LabelSelector{MatchLabels: labels},
+			Template: corev1.PodTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Labels: labels,
+				},
+				Spec: corev1.PodSpec{
+					Containers: []corev1.Container{{
+						Name:  "dummy",
+						Image: "dummy",
+						Ports: []corev1.ContainerPort{{
+							ContainerPort: 8080,
+						}},
+					}},
+				},
+			},
+		},
+		Status: appsv1.DeploymentStatus{
+			AvailableReplicas: 1,
+		},
+	}
+
+	svc := &corev1.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: gw.Namespace,
+			Name:      gw.Name + "-svc",
+			Labels:    labels,
+		},
+		Spec: corev1.ServiceSpec{
+			Ports: []corev1.ServicePort{{
+				Port: 80,
+			}},
+		},
+		Status: corev1.ServiceStatus{
+			LoadBalancer: corev1.LoadBalancerStatus{
+				Ingress: []corev1.LoadBalancerIngress{{IP: "1.1.1.1"}},
+			},
+		},
+	}
+
+	require.NoError(t, cli.Create(ctx, deploy))
+	require.NoError(t, cli.Create(ctx, svc))
+
 	// Ensure the Gateway reports "Scheduled".
 	require.Eventually(t, func() bool {
 		if err := cli.Get(ctx, types.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}, gw); err != nil {
@@ -222,7 +278,7 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro
 		}
 
 		for _, cond := range gw.Status.Conditions {
-			fmt.Printf("Condition: %v", cond)
+			fmt.Printf("Condition: %v\n", cond)
 			if cond.Type == string(gwapiv1b1.GatewayConditionScheduled) && cond.Status == metav1.ConditionTrue {
 				return true
 			}
@@ -236,13 +292,16 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro
 		require.NoError(t, cli.Delete(ctx, gw))
 	}()
 
-	// Ensure the gatewayclass has been finalized.
-	require.NoError(t, cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc))
-	require.Contains(t, gc.Finalizers, gatewayClassFinalizer)
-
 	// Ensure the number of Gateways in the Gateway resource table is as expected.
 	require.Eventually(t, func() bool {
-		return resources.Gateways.Len() == 1
+		res, _ := resources.GatewayAPIResources.Load("gc-scheduled-status-test")
+		return res != nil && len(res.Gateways) == 1
+	}, defaultWait, defaultTick)
+
+	// Ensure the gatewayclass has been finalized.
+	require.Eventually(t, func() bool {
+		err := cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc)
+		return err == nil && slices.Contains(gc.Finalizers, gatewayClassFinalizer)
 	}, defaultWait, defaultTick)
 
 	// Ensure the test Gateway in the Gateway resources is as expected.
@@ -253,13 +312,14 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro
 	require.Eventually(t, func() bool {
 		return cli.Get(ctx, key, gw) == nil
 	}, defaultWait, defaultTick)
-	gws, _ := resources.Gateways.Load(key)
+
+	res, _ := resources.GatewayAPIResources.Load("gc-scheduled-status-test")
 	// Only check if the spec is equal
 	// The watchable map will not store a resource
 	// with an updated status if the spec has not changed
 	// to eliminate this endless loop:
 	// reconcile->store->translate->update-status->reconcile
-	assert.Equal(t, gw.Spec, gws.Spec)
+	assert.Equal(t, gw.Spec, res.Gateways[0].Spec)
 }
 
 // Test that even when resources such as the Service/Deployment get hashed names (because of a gateway with a very long name)
@@ -319,7 +379,7 @@ func testLongNameHashedResources(ctx context.Context, t *testing.T, provider *Pr
 		}
 
 		for _, cond := range gw.Status.Conditions {
-			fmt.Printf("Condition: %v", cond)
+			fmt.Printf("Condition: %v\n", cond)
 			if cond.Type == string(gwapiv1b1.GatewayConditionReady) && cond.Status == metav1.ConditionTrue {
 				ready = true
 			}
@@ -337,12 +397,15 @@ func testLongNameHashedResources(ctx context.Context, t *testing.T, provider *Pr
 	}()
 
 	// Ensure the gatewayclass has been finalized.
-	require.NoError(t, cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc))
-	require.Contains(t, gc.Finalizers, gatewayClassFinalizer)
+	require.Eventually(t, func() bool {
+		err := cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc)
+		return err == nil && slices.Contains(gc.Finalizers, gatewayClassFinalizer)
+	}, defaultWait, defaultTick)
 
 	// Ensure the number of Gateways in the Gateway resource table is as expected.
 	require.Eventually(t, func() bool {
-		return resources.Gateways.Len() == 1
+		res, _ := resources.GatewayAPIResources.Load("envoy-gateway-class")
+		return len(res.Gateways) == 1
 	}, defaultWait, defaultTick)
 
 	// Ensure the test Gateway in the Gateway resources is as expected.
@@ -353,13 +416,13 @@ func testLongNameHashedResources(ctx context.Context, t *testing.T, provider *Pr
 	require.Eventually(t, func() bool {
 		return cli.Get(ctx, key, gw) == nil
 	}, defaultWait, defaultTick)
-	gws, _ := resources.Gateways.Load(key)
+	res, _ := resources.GatewayAPIResources.Load("envoy-gateway-class")
 	// Only check if the spec is equal
 	// The watchable map will not store a resource
 	// with an updated status if the spec has not changed
 	// to eliminate this endless loop:
 	// reconcile->store->translate->update-status->reconcile
-	assert.Equal(t, gw.Spec, gws.Spec)
+	assert.Equal(t, gw.Spec, res.Gateways[0].Spec)
 }
 
 func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) {
@@ -652,7 +715,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
 			}()
 
 			require.Eventually(t, func() bool {
-				return resources.HTTPRoutes.Len() == 1
+				return resources.GatewayAPIResources.Len() != 0
 			}, defaultWait, defaultTick)
 
 			// Ensure the test HTTPRoute in the HTTPRoute resources is as expected.
@@ -663,22 +726,41 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
 			require.Eventually(t, func() bool {
 				return cli.Get(ctx, key, &testCase.route) == nil
 			}, defaultWait, defaultTick)
-			hroutes, _ := resources.HTTPRoutes.Load(key)
-			assert.Equal(t, &testCase.route, hroutes)
+
+			require.Eventually(t, func() bool {
+				res, ok := resources.GatewayAPIResources.Load("httproute-test")
+				return ok && len(res.HTTPRoutes) != 0
+			}, defaultWait, defaultTick)
+			res, _ := resources.GatewayAPIResources.Load("httproute-test")
+			assert.Equal(t, &testCase.route, res.HTTPRoutes[0])
 
 			// Ensure the HTTPRoute Namespace is in the Namespace resource map.
 			require.Eventually(t, func() bool {
-				_, ok := resources.Namespaces.Load(testCase.route.Namespace)
-				return ok
+				res, ok := resources.GatewayAPIResources.Load(testCase.route.Namespace)
+				if !ok {
+					return false
+				}
+				for _, ns := range res.Namespaces {
+					if ns.Name == testCase.route.Namespace {
+						return true
+					}
+				}
+				return false
 			}, defaultWait, defaultTick)
 
 			// Ensure the Service is in the resource map.
-			svcKey := utils.NamespacedName(svc)
 			require.Eventually(t, func() bool {
-				_, ok := resources.Services.Load(svcKey)
-				return ok
+				res, ok := resources.GatewayAPIResources.Load("httproute-test")
+				if !ok {
+					return false
+				}
+				for _, s := range res.Services {
+					if s.Name == svc.Name && s.Namespace == svc.Namespace {
+						return true
+					}
+				}
+				return false
 			}, defaultWait, defaultTick)
-
 		})
 	}
 }
@@ -771,7 +853,7 @@ func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resourc
 			}()
 
 			require.Eventually(t, func() bool {
-				return resources.TLSRoutes.Len() == 1
+				return resources.GatewayAPIResources.Len() != 0
 			}, defaultWait, defaultTick)
 
 			// Ensure the test TLSRoute in the TLSRoute resources is as expected.
@@ -782,20 +864,40 @@ func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resourc
 			require.Eventually(t, func() bool {
 				return cli.Get(ctx, key, &testCase.route) == nil
 			}, defaultWait, defaultTick)
-			troutes, _ := resources.TLSRoutes.Load(key)
-			assert.Equal(t, &testCase.route, troutes)
 
-			// Ensure the TLSRoute Namespace is in the Namespace resource map.
 			require.Eventually(t, func() bool {
-				_, ok := resources.Namespaces.Load(testCase.route.Namespace)
-				return ok
+				res, ok := resources.GatewayAPIResources.Load("tlsroute-test")
+				return ok && len(res.TLSRoutes) != 0
+			}, defaultWait, defaultTick)
+			res, _ := resources.GatewayAPIResources.Load("tlsroute-test")
+			assert.Equal(t, &testCase.route, res.TLSRoutes[0])
+
+			// Ensure the HTTPRoute Namespace is in the Namespace resource map.
+			require.Eventually(t, func() bool {
+				res, ok := resources.GatewayAPIResources.Load(testCase.route.Namespace)
+				if !ok {
+					return false
+				}
+				for _, ns := range res.Namespaces {
+					if ns.Name == testCase.route.Namespace {
+						return true
+					}
+				}
+				return false
 			}, defaultWait, defaultTick)
 
 			// Ensure the Service is in the resource map.
-			svcKey := utils.NamespacedName(svc)
 			require.Eventually(t, func() bool {
-				_, ok := resources.Services.Load(svcKey)
-				return ok
+				res, ok := resources.GatewayAPIResources.Load("tlsroute-test")
+				if !ok {
+					return false
+				}
+				for _, s := range res.Services {
+					if s.Name == svc.Name && s.Namespace == svc.Namespace {
+						return true
+					}
+				}
+				return false
 			}, defaultWait, defaultTick)
 		})
 	}
@@ -909,27 +1011,46 @@ func testServiceCleanupForMultipleRoutes(ctx context.Context, t *testing.T, prov
 	require.NoError(t, cli.Create(ctx, &httpRoute))
 
 	// Check that the Service is present in the resource map
-	key := types.NamespacedName{
-		Namespace: svc.Namespace,
-		Name:      svc.Name,
-	}
-
 	require.Eventually(t, func() bool {
-		rSvc, _ := resources.Services.Load(key)
-		return rSvc != nil
+		res, ok := resources.GatewayAPIResources.Load("service-cleanup-test")
+		if !ok {
+			return false
+		}
+		for _, s := range res.Services {
+			if s.Namespace == svc.Namespace && s.Name == svc.Name {
+				return true
+			}
+		}
+		return false
 	}, defaultWait, defaultTick)
 
 	// Delete the TLSRoute, and check if the Service is still present
 	require.NoError(t, cli.Delete(ctx, &tlsRoute))
 	require.Eventually(t, func() bool {
-		rSvc, _ := resources.Services.Load(key)
-		return rSvc != nil
+		res, ok := resources.GatewayAPIResources.Load("service-cleanup-test")
+		if !ok {
+			return false
+		}
+		for _, s := range res.Services {
+			if s.Namespace == svc.Namespace && s.Name == svc.Name {
+				return true
+			}
+		}
+		return false
 	}, defaultWait, defaultTick)
 
 	// Delete the HTTPRoute, and check if the Service is also removed
 	require.NoError(t, cli.Delete(ctx, &httpRoute))
 	require.Eventually(t, func() bool {
-		rSvc, _ := resources.Services.Load(key)
-		return rSvc == nil
+		res, ok := resources.GatewayAPIResources.Load("service-cleanup-test")
+		if !ok {
+			return false
+		}
+		for _, s := range res.Services {
+			if s.Namespace == svc.Namespace && s.Name == svc.Name {
+				return false
+			}
+		}
+		return true
 	}, defaultWait, defaultTick)
 }
diff --git a/internal/provider/kubernetes/store_test.go b/internal/provider/kubernetes/store_test.go
deleted file mode 100644
index 66c3c4bfd..000000000
--- a/internal/provider/kubernetes/store_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright Envoy Gateway Authors
-// SPDX-License-Identifier: Apache-2.0
-// The full text of the Apache license is available in the LICENSE file at
-// the root of the repo.
-
-package kubernetes
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/require"
-	"k8s.io/apimachinery/pkg/types"
-)
-
-func TestProviderReferenceStore(t *testing.T) {
-	cache := newProviderReferenceStore()
-
-	testCases := []struct {
-		name string
-		test func(t *testing.T, c *providerReferenceStore)
-	}{
-		{
-			name: "route to service mappings",
-			test: testRouteToServicesMappings,
-		},
-	}
-
-	for _, tc := range testCases {
-		tc := tc
-		t.Run(tc.name, func(t *testing.T) {
-			tc.test(t, cache)
-		})
-	}
-}
-
-func testRouteToServicesMappings(t *testing.T, cache *providerReferenceStore) {
-	httpr1 := ObjectKindNamespacedName{"HTTPRoute", "ns1", "r1"}
-	tlsr1 := ObjectKindNamespacedName{"TLSRoute", "ns1", "r1"}
-
-	ns1svc1 := types.NamespacedName{Namespace: "ns1", Name: "svc1"}
-	ns1svc2 := types.NamespacedName{Namespace: "ns1", Name: "svc2"}
-
-	// Add HTTPRoute/ns1/r1 -> ns1/svc1 mapping
-	cache.updateRouteToServicesMapping(httpr1, ns1svc1)
-	require.Equal(t, map[types.NamespacedName]struct{}{ns1svc1: {}}, cache.getRouteToServicesMapping(httpr1))
-
-	// Add HTTPRoute/ns1/r1 -> ns1/svc2 mapping
-	// Add TLSRoute/ns1/r1 -> ns1/svc2 mapping
-	cache.updateRouteToServicesMapping(tlsr1, ns1svc2)
-	cache.updateRouteToServicesMapping(httpr1, ns1svc2)
-	require.Equal(t, map[types.NamespacedName]struct{}{ns1svc2: {}}, cache.getRouteToServicesMapping(tlsr1))
-	require.Equal(t, map[types.NamespacedName]struct{}{ns1svc1: {}, ns1svc2: {}}, cache.getRouteToServicesMapping(httpr1))
-
-	// Remove HTTPRoute/ns1/r1 -> ns1/svc1 mapping
-	cache.removeRouteToServicesMapping(httpr1, ns1svc1)
-	require.Equal(t, map[types.NamespacedName]struct{}{ns1svc2: {}}, cache.getRouteToServicesMapping(httpr1))
-
-	// Remove TLSRoute/ns1/r1 -> ns1/svc2 mapping
-	cache.removeRouteToServicesMapping(tlsr1, ns1svc2)
-	require.Equal(t, map[types.NamespacedName]struct{}(nil), cache.getRouteToServicesMapping(tlsr1))
-
-	// Verify that ns1svc2 is still referred by another route (HTTPRoute/ns1/r1)
-	require.Equal(t, true, cache.isServiceReferredByRoutes(ns1svc2))
-
-	// Verify that ns1svc1 is not referred by any other route
-	require.Equal(t, false, cache.isServiceReferredByRoutes(ns1svc1))
-}
diff --git a/test/conformance/conformance_test.go b/test/conformance/conformance_test.go
index 09f576105..a5a8aa952 100644
--- a/test/conformance/conformance_test.go
+++ b/test/conformance/conformance_test.go
@@ -70,9 +70,8 @@ func TestGatewayAPIConformance(t *testing.T) {
 		tests.HTTPRouteInvalidCrossNamespaceBackendRef,
 		tests.GatewaySecretReferenceGrantAllInNamespace,
 		tests.GatewaySecretReferenceGrantSpecific,
-		// Uncomment when https://github.com/envoyproxy/gateway/issues/539 is fixed.
-		/*tests.GatewaySecretMissingReferenceGrant,
-		tests.GatewaySecretInvalidReferenceGrant,*/
+		tests.GatewaySecretMissingReferenceGrant,
+		tests.GatewaySecretInvalidReferenceGrant,
 	}
 	cSuite.Run(t, egTests)