Repository: envoyproxy/gateway

Issue:
Envoy Proxy deployment is not cleaned up when we customize the deployment name, replicas using EnvoyProxy resource
*Description*:
Envoy Proxy deployment is not cleaned up when we customize the deployment name, replicas etc using EnvoyProxy resource,

*Repro steps*:
1. Create a Gateway and use a [EnvoyProxy](https://gateway.envoyproxy.io/docs/api/extension_types/#envoyproxy) to set a custom name and increase the replicas. 
2. I tried deleting the Gateway and expected the envoy gateway to delete the envoy proxy deployment and service, but it did not. '
3. Next I created another Gateway with an EnvoyProxy resource. Then I deleted the EnvoyProxy, envoy gateway created a single replica deployment with the a default name but did not delete the older deployment. 
4. Now when I deleted the Gateway , the new deployment with default name and the service were deleted but the older deployment with custom name still exists.  


*Environment*:
Envoy Gateway version - 1.1.2 

*Logs*:
I could not see any logs in Envoy Gateway pods when I deleted the Gateway.


Discussion:
@arkodg: @tejaboppana thanks for flagging this
in step 1, did you create the `Gateway` resource first (a.) and then create a `EnvoyProxy` specifying the custom deployment name (b.) and then link the `EnvoyProxy` resource to the `Gateway` by editing it (c.) ?

For this case the I think the infra layer will create a new deployment with the new name but not delete the older deployment
When a custom name is set, we also need to delete the older deployment
https://github.com/envoyproxy/gateway/blob/0f4cb27f0110051f7811122a2dd73652c93a15b2/internal/infrastructure/kubernetes/proxy/resource_provider.go#L153@tejaboppana: @arkodg I tried 2 things and below are my observations 

1. Steps that you mentioned i.e a) Create `Gateway` b) Create `EnvoyProxy` c) Edit `Gateway `to link it with `EnvoyProxy`
 In this case a new deployment is created but the older deployment is not deleted which is not ideal. In addition to that,  when I delete the `Gateway` and `EnvoyProxy`, I expected everything to be cleaned up. Only the older deployment and the service are deleted but the deployment with custom name is not deleted. 

2. a) Create `EnvoyProxy` b) Create `Gateway` and link it to the `EnvoyProxy`
In this case the deployment is created with the custom name that I specified which is expected. But when I delete the `Gateway` and `EnvoyProxy` resources I expected the deployment and service to be deleted. However, this did not happen and both the deployment and service were still running. I had to manually delete them.  @arkodg: thanks, yeah an additional item here to delete based on gateway owning labels, and not just deployment to svc name
https://github.com/envoyproxy/gateway/blob/0f4cb27f0110051f7811122a2dd73652c93a15b2/internal/infrastructure/kubernetes/infra_resource.go#L402@zhaohuabing: @zirain can we consider this as done? Can it be closed?@arkodg: @zhaohuabing there's additional work here of cleaning up the resource when the name has changed @zirain: @tejaboppana can you share a reproducable yaml configuration?@arkodg: @zirain the issue here when the name changes, https://github.com/envoyproxy/gateway/blob/82ce107e76bfc9b8d7f1e2704c7d294bbb8b9606/internal/infrastructure/kubernetes/proxy/resource_provider.go#L322 we need to also delete the previously named resource, but this is called during the IR create/update flow@zirain: I think deletion is fine, because we use labels to filter now.
Still need to a patch for update.

Step 1:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg-custom-name
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: All
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: custom-name
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        name: custom-name
```

Step 2:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg-custom-name
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: All
  infrastructure:
    parametersRef:
      group: gateway.envoyproxy.io
      kind: EnvoyProxy
      name: custom-name
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: custom-name
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        name: custom-name
```@github-actions: This issue has been automatically marked as stale because it has not had activity in the last 30 days.@kahirokunn: keep@showchan33: I created k8s resources using only the manifest based on Step 2 of the link below.
https://github.com/envoyproxy/gateway/issues/4380#issuecomment-2449326778
At this time, the Step 1 manifest is not APPLIED in advance.

Then, in addition to `custom-name`, a Deployment named `envoy-default-envoy-gateway-12b6bb46` was also created.
As a side note, the resource name of the Gateway I created is `envoy-gateway`, which is different from the `eg-custom-name` in the sample above.

```
$ kubectl get deploy -n envoy-gateway-system
NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
custom-name                            1/1     1            1           65s
envoy-default-envoy-gateway-12b6bb46   1/1     1            1           65s
envoy-gateway                          1/1     1            1           45h
```

On the other hand, when the Gateway was created after the EnvoyProxy was created, only the `custom-name` Deployment was created and not the `envoy-default-envoy-gateway-12b6bb46`.
@illrill: I've noticed this behaviour applies not only for the `Deployment` but also for the `Service`.@zirain: @illrill the behaviour is same for all resources supported changing name.@travisghansen: Yes, deployments, hpa, svc, etc

Solution patch:
diff --git a/internal/gatewayapi/resource/load.go b/internal/gatewayapi/resource/load.go
index 23f64c96a..4cbec0765 100644
--- a/internal/gatewayapi/resource/load.go
+++ b/internal/gatewayapi/resource/load.go
@@ -315,6 +315,8 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res
 		case KindSecret:
 			typedData := data.Interface()
 			typedStringData := stringData.Interface()
+			typedSecretType := kobjVal.FieldByName("Type").Interface()
+
 			secret := &corev1.Secret{
 				TypeMeta: metav1.TypeMeta{
 					Kind: KindSecret,
@@ -323,6 +325,7 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res
 					Name:      name,
 					Namespace: namespace,
 				},
+				Type:       typedSecretType.(corev1.SecretType),
 				Data:       typedData.(map[string][]byte),
 				StringData: typedStringData.(map[string]string),
 			}
diff --git a/internal/infrastructure/kubernetes/infra.go b/internal/infrastructure/kubernetes/infra.go
index a0615a4e8..82d51d066 100644
--- a/internal/infrastructure/kubernetes/infra.go
+++ b/internal/infrastructure/kubernetes/infra.go
@@ -20,6 +20,7 @@ import (
 	"github.com/envoyproxy/gateway/internal/envoygateway/config"
 	"github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/proxy"
 	"github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit"
+	"github.com/envoyproxy/gateway/internal/logging"
 )
 
 var _ ResourceRender = &proxy.ResourceRender{}
@@ -58,6 +59,8 @@ type Infra struct {
 
 	// Client wrap k8s client.
 	Client *InfraClient
+
+	logger logging.Logger
 }
 
 // NewInfra returns a new Infra.
@@ -67,6 +70,7 @@ func NewInfra(cli client.Client, cfg *config.Server) *Infra {
 		DNSDomain:    cfg.DNSDomain,
 		EnvoyGateway: cfg.EnvoyGateway,
 		Client:       New(cli),
+		logger:       cfg.Logger.WithName(string(egv1a1.LogComponentInfrastructureRunner)),
 	}
 }
 
diff --git a/internal/infrastructure/kubernetes/infra_client.go b/internal/infrastructure/kubernetes/infra_client.go
index 0d3a01e5d..2cee23604 100644
--- a/internal/infrastructure/kubernetes/infra_client.go
+++ b/internal/infrastructure/kubernetes/infra_client.go
@@ -8,6 +8,7 @@ package kubernetes
 import (
 	"context"
 	"fmt"
+	"reflect"
 
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/types"
@@ -33,6 +34,41 @@ func (cli *InfraClient) ServerSideApply(ctx context.Context, obj client.Object)
 	return nil
 }
 
+// DeleteAllExcept delete all resources filter by ListOption except the one specified by key.
+func (cli *InfraClient) DeleteAllExcept(ctx context.Context, objList client.ObjectList, key client.ObjectKey, listOpts ...client.ListOption) error {
+	if err := cli.List(ctx, objList, listOpts...); err != nil {
+		if kerrors.IsNotFound(err) {
+			return nil
+		}
+		return err
+	}
+
+	v := reflect.ValueOf(objList).Elem()
+	items := v.FieldByName("Items")
+
+	// If there is only one item, we don't need to delete it,
+	// because it normally means custom resource name is not enabled.
+	if items.Len() <= 1 {
+		return nil
+	}
+
+	for i := range items.Len() {
+		item := items.Index(i)
+		name := item.FieldByName("Name")
+		namespace := item.FieldByName("Namespace")
+
+		if name.String() == key.Name && namespace.String() == key.Namespace {
+			continue
+		}
+
+		if err := cli.Delete(ctx, item.Addr().Interface().(client.Object)); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 func (cli *InfraClient) Delete(ctx context.Context, object client.Object) error {
 	if err := cli.Client.Delete(ctx, object); err != nil {
 		if kerrors.IsNotFound(err) {
diff --git a/internal/infrastructure/kubernetes/infra_resource.go b/internal/infrastructure/kubernetes/infra_resource.go
index 1bec17c09..6c2cacc2b 100644
--- a/internal/infrastructure/kubernetes/infra_resource.go
+++ b/internal/infrastructure/kubernetes/infra_resource.go
@@ -119,6 +119,17 @@ func (i *Infra) createOrUpdateDeployment(ctx context.Context, r ResourceRender)
 	}
 
 	defer func() {
+		deleteErr := i.Client.DeleteAllExcept(ctx, &appsv1.DeploymentList{}, client.ObjectKey{
+			Namespace: deployment.Namespace,
+			Name:      deployment.Name,
+		}, &client.ListOptions{
+			Namespace:     deployment.Namespace,
+			LabelSelector: r.LabelSelector(),
+		})
+		if deleteErr != nil {
+			i.logger.Error(deleteErr, "failed to delete all except deployment", "name", r.Name())
+		}
+
 		if err == nil {
 			resourceApplyDurationSeconds.With(labels...).Record(time.Since(startTime).Seconds())
 			resourceApplyTotal.WithSuccess(labels...).Increment()
@@ -192,7 +203,7 @@ func (i *Infra) createOrUpdateDaemonSet(ctx context.Context, r ResourceRender) (
 		return err
 	}
 
-	// delete the daemonset and return early
+	// delete the DaemonSet and return early
 	// this handles the case where a deployment has been
 	// configured.
 	if daemonSet == nil {
@@ -200,6 +211,17 @@ func (i *Infra) createOrUpdateDaemonSet(ctx context.Context, r ResourceRender) (
 	}
 
 	defer func() {
+		deleteErr := i.Client.DeleteAllExcept(ctx, &appsv1.DaemonSetList{}, client.ObjectKey{
+			Namespace: daemonSet.Namespace,
+			Name:      daemonSet.Name,
+		}, &client.ListOptions{
+			Namespace:     daemonSet.Namespace,
+			LabelSelector: r.LabelSelector(),
+		})
+		if deleteErr != nil {
+			i.logger.Error(deleteErr, "failed to delete all except deployment", "name", r.Name())
+		}
+
 		if err == nil {
 			resourceApplyDurationSeconds.With(labels...).Record(time.Since(startTime).Seconds())
 			resourceApplyTotal.WithSuccess(labels...).Increment()
@@ -358,6 +380,17 @@ func (i *Infra) createOrUpdateService(ctx context.Context, r ResourceRender) (er
 	}
 
 	defer func() {
+		deleteErr := i.Client.DeleteAllExcept(ctx, &corev1.ServiceList{}, client.ObjectKey{
+			Namespace: svc.Namespace,
+			Name:      svc.Name,
+		}, &client.ListOptions{
+			Namespace:     svc.Namespace,
+			LabelSelector: r.LabelSelector(),
+		})
+		if deleteErr != nil {
+			i.logger.Error(deleteErr, "failed to delete all except deployment", "name", r.Name())
+		}
+
 		if err == nil {
 			resourceApplyDurationSeconds.With(labels...).Record(time.Since(startTime).Seconds())
 			resourceApplyTotal.WithSuccess(labels...).Increment()
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index 7f2b62ad4..1d7679956 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -38,6 +38,7 @@ bug fixes: |
   Fix not logging an error and returning it in the K8s Reconcile method when a GatewayClass is not accepted.
   Fix allowing empty text field for opentelemetry sink when using JSON format.
   Fix an issue that SamplingFraction was not working.
+  Fix kubernetes resources not being deleted when the customized name used.
 
 # Enhancements that improve performance.
 performance improvements: |



Test patch:
diff --git a/internal/gatewayapi/resource/testdata/all-resources.in.yaml b/internal/gatewayapi/resource/testdata/all-resources.in.yaml
index 6bb599437..f5cba5ff6 100644
--- a/internal/gatewayapi/resource/testdata/all-resources.in.yaml
+++ b/internal/gatewayapi/resource/testdata/all-resources.in.yaml
@@ -257,6 +257,15 @@ stringData:
   secret: "literal value"
 ---
 apiVersion: v1
+kind: Secret
+metadata:
+  name: secret-with-type
+  namespace: default
+type: "type value"
+data:
+  .secret-file: dmFsdWUtMg0KDQo=
+---
+apiVersion: v1
 kind: ConfigMap
 metadata:
   name: configmap
diff --git a/internal/gatewayapi/resource/testdata/all-resources.out.yaml b/internal/gatewayapi/resource/testdata/all-resources.out.yaml
index b9cd72b99..55e2fadb4 100644
--- a/internal/gatewayapi/resource/testdata/all-resources.out.yaml
+++ b/internal/gatewayapi/resource/testdata/all-resources.out.yaml
@@ -270,6 +270,14 @@ secrets:
     namespace: default
   stringData:
     secret: literal value
+- data:
+    .secret-file: dmFsdWUtMg0KDQo=
+  kind: Secret
+  metadata:
+    creationTimestamp: null
+    name: secret-with-type
+    namespace: default
+  type: type value
 securityPolicies:
 - kind: SecurityPolicy
   metadata:
diff --git a/test/e2e/testdata/envoyproxy-custom-name.yaml b/test/e2e/testdata/envoyproxy-custom-name.yaml
new file mode 100644
index 000000000..a1061025e
--- /dev/null
+++ b/test/e2e/testdata/envoyproxy-custom-name.yaml
@@ -0,0 +1,120 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: deploy-custom-name
+  namespace: gateway-conformance-infra
+spec:
+  gatewayClassName: "{GATEWAY_CLASS_NAME}"
+  listeners:
+    - name: http
+      port: 80
+      protocol: HTTP
+      allowedRoutes:
+        namespaces:
+          from: All
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+  name: deploy-custom-name
+  namespace: gateway-conformance-infra
+spec:
+  ipFamily: IPv4
+  provider:
+    type: Kubernetes
+    kubernetes:
+      envoyDeployment:
+        name: deploy-custom-name
+      envoyService:
+        name: deploy-custom-name
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  name: deploy-route
+  namespace: gateway-conformance-infra
+spec:
+  parentRefs:
+    - name: deploy-custom-name
+  rules:
+    - backendRefs:
+        - name: infra-backend-v1
+          port: 8080
+      matches:
+        - path:
+            type: PathPrefix
+            value: /deploy
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: ds-custom-name
+  namespace: gateway-conformance-infra
+spec:
+  gatewayClassName: "{GATEWAY_CLASS_NAME}"
+  listeners:
+    - name: http
+      port: 80
+      protocol: HTTP
+      allowedRoutes:
+        namespaces:
+          from: All
+  infrastructure:
+    parametersRef:
+      group: gateway.envoyproxy.io
+      kind: EnvoyProxy
+      name: eg-daemonset
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+  name: eg-daemonset
+  namespace: gateway-conformance-infra
+spec:
+  ipFamily: IPv4
+  provider:
+    type: Kubernetes
+    kubernetes:
+      envoyDaemonSet:
+        patch:
+          type: StrategicMerge
+          value:
+            spec:
+              template:
+                spec:
+                  containers:
+                    - name: envoy
+                      readinessProbe:
+                        initialDelaySeconds: 5
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+  name: ds-custom-name
+  namespace: gateway-conformance-infra
+spec:
+  ipFamily: IPv4
+  provider:
+    type: Kubernetes
+    kubernetes:
+      envoyDaemonSet:
+        name: ds-custom-name
+      envoyService:
+        name: ds-custom-name
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  name: ds-route
+  namespace: gateway-conformance-infra
+spec:
+  parentRefs:
+    - name: ds-custom-name
+  rules:
+    - backendRefs:
+        - name: infra-backend-v1
+          port: 8080
+      matches:
+        - path:
+            type: PathPrefix
+            value: /daemonset
diff --git a/test/e2e/tests/envoyproxy.go b/test/e2e/tests/envoyproxy.go
new file mode 100644
index 000000000..b8980bd09
--- /dev/null
+++ b/test/e2e/tests/envoyproxy.go
@@ -0,0 +1,315 @@
+// 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.
+
+//go:build e2e
+
+package tests
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"testing"
+	"time"
+
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/wait"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+	"sigs.k8s.io/gateway-api/conformance/utils/http"
+	"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
+	"sigs.k8s.io/gateway-api/conformance/utils/suite"
+	"sigs.k8s.io/gateway-api/conformance/utils/tlog"
+)
+
+func init() {
+	ConformanceTests = append(ConformanceTests, EnvoyProxyCustomNameTest)
+}
+
+var EnvoyProxyCustomNameTest = suite.ConformanceTest{
+	ShortName:   "EnvoyProxyCustomName",
+	Description: "Test running Envoy with custom name",
+	Manifests:   []string{"testdata/envoyproxy-custom-name.yaml"},
+	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+		t.Run("Deployment", func(t *testing.T) {
+			ns := "gateway-conformance-infra"
+			routeNN := types.NamespacedName{Name: "deploy-route", Namespace: ns}
+			gwNN := types.NamespacedName{Name: "deploy-custom-name", Namespace: ns}
+			OkResp := http.ExpectedResponse{
+				Request: http.Request{
+					Path: "/deploy",
+				},
+				Response: http.Response{
+					StatusCode: 200,
+				},
+				Namespace: ns,
+			}
+
+			// Make sure there's deployment for the gateway
+			err := checkEnvoyProxyDeployment(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy deployment: %v", err)
+			}
+			err = checkEnvoyProxyService(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy service: %v", err)
+			}
+			gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+			// Send a request to a valid path and expect a successful response
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp)
+
+			// Update the Gateway to use a custom name
+			gw := &gwapiv1.Gateway{}
+			err = suite.Client.Get(context.Background(), gwNN, gw)
+			if err != nil {
+				t.Fatalf("Failed to get Gateway: %v", err)
+			}
+			gw.Spec.Infrastructure = &gwapiv1.GatewayInfrastructure{
+				ParametersRef: &gwapiv1.LocalParametersReference{
+					Name:  "deploy-custom-name",
+					Kind:  "EnvoyProxy",
+					Group: "gateway.envoyproxy.io",
+				},
+			}
+			err = suite.Client.Update(context.Background(), gw)
+			if err != nil {
+				t.Fatalf("Failed to update Gateway: %v", err)
+			}
+
+			err = checkEnvoyProxyDeployment(t, suite, gwNN, "deploy-custom-name")
+			if err != nil {
+				t.Fatalf("Failed to delete Gateway: %v", err)
+			}
+			err = checkEnvoyProxyService(t, suite, gwNN, "deploy-custom-name")
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy service: %v", err)
+			}
+			gwAddr = kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+			// Send a request to a valid path and expect a successful response
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp)
+
+			// Rollback the Gateway to without custom name
+			gw = &gwapiv1.Gateway{}
+			err = suite.Client.Get(context.Background(), gwNN, gw)
+			if err != nil {
+				t.Fatalf("Failed to get Gateway: %v", err)
+			}
+			gw.Spec.Infrastructure = &gwapiv1.GatewayInfrastructure{}
+			err = suite.Client.Update(context.Background(), gw)
+			if err != nil {
+				t.Fatalf("Failed to update Gateway: %v", err)
+			}
+
+			// Make sure there's deployment for the gateway
+			err = checkEnvoyProxyDeployment(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy deployment: %v", err)
+			}
+			err = checkEnvoyProxyService(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy service: %v", err)
+			}
+			gwAddr = kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+			// Send a request to a valid path and expect a successful response
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp)
+		})
+
+		t.Run("DaemonSet", func(t *testing.T) {
+			ns := "gateway-conformance-infra"
+			routeNN := types.NamespacedName{Name: "ds-route", Namespace: ns}
+			gwNN := types.NamespacedName{Name: "ds-custom-name", Namespace: ns}
+			OkResp := http.ExpectedResponse{
+				Request: http.Request{
+					Path: "/daemonset",
+				},
+				Response: http.Response{
+					StatusCode: 200,
+				},
+				Namespace: ns,
+			}
+
+			// Make sure there's DaemonSet for the gateway
+			err := checkEnvoyProxyDaemonSet(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy deployment: %v", err)
+			}
+			err = checkEnvoyProxyService(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy service: %v", err)
+			}
+			gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+			// Send a request to a valid path and expect a successful response
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp)
+
+			// Update the Gateway to use a custom name
+			gw := &gwapiv1.Gateway{}
+			err = suite.Client.Get(context.Background(), gwNN, gw)
+			if err != nil {
+				t.Fatalf("Failed to get Gateway: %v", err)
+			}
+			gw.Spec.Infrastructure = &gwapiv1.GatewayInfrastructure{
+				ParametersRef: &gwapiv1.LocalParametersReference{
+					Name:  "ds-custom-name",
+					Kind:  "EnvoyProxy",
+					Group: "gateway.envoyproxy.io",
+				},
+			}
+			err = suite.Client.Update(context.Background(), gw)
+			if err != nil {
+				t.Fatalf("Failed to update Gateway: %v", err)
+			}
+
+			err = checkEnvoyProxyDaemonSet(t, suite, gwNN, "ds-custom-name")
+			if err != nil {
+				t.Fatalf("Failed to delete Gateway: %v", err)
+			}
+			err = checkEnvoyProxyService(t, suite, gwNN, "ds-custom-name")
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy service: %v", err)
+			}
+			gwAddr = kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+			// Send a request to a valid path and expect a successful response
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp)
+
+			// Rollback the Gateway to without custom name
+			gw = &gwapiv1.Gateway{}
+			err = suite.Client.Get(context.Background(), gwNN, gw)
+			if err != nil {
+				t.Fatalf("Failed to get Gateway: %v", err)
+			}
+			gw.Spec.Infrastructure = &gwapiv1.GatewayInfrastructure{
+				ParametersRef: &gwapiv1.LocalParametersReference{
+					Name:  "eg-daemonset",
+					Kind:  "EnvoyProxy",
+					Group: "gateway.envoyproxy.io",
+				},
+			}
+			err = suite.Client.Update(context.Background(), gw)
+			if err != nil {
+				t.Fatalf("Failed to update Gateway: %v", err)
+			}
+
+			// Make sure there's DaemonSet for the gateway
+			err = checkEnvoyProxyDaemonSet(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy deployment: %v", err)
+			}
+			err = checkEnvoyProxyService(t, suite, gwNN, fmt.Sprintf("envoy-%s-%s", gwNN.Namespace, gwNN.Name))
+			if err != nil {
+				t.Fatalf("Failed to check EnvoyProxy service: %v", err)
+			}
+			gwAddr = kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+			// Send a request to a valid path and expect a successful response
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp)
+		})
+	},
+}
+
+func checkEnvoyProxyDeployment(t *testing.T, suite *suite.ConformanceTestSuite, gwNN types.NamespacedName, exceptName string) error {
+	// Make sure there's deployment for the gateway
+	return wait.PollUntilContextTimeout(context.TODO(), time.Second, suite.TimeoutConfig.CreateTimeout, true, func(ctx context.Context) (bool, error) {
+		deploys := &appsv1.DeploymentList{}
+		err := suite.Client.List(ctx, deploys, &client.ListOptions{
+			Namespace: "envoy-gateway-system",
+			LabelSelector: labels.SelectorFromSet(map[string]string{
+				"app.kubernetes.io/managed-by":                   "envoy-gateway",
+				"app.kubernetes.io/name":                         "envoy",
+				"gateway.envoyproxy.io/owning-gateway-name":      gwNN.Name,
+				"gateway.envoyproxy.io/owning-gateway-namespace": gwNN.Namespace,
+			}),
+		})
+		if err != nil {
+			return false, err
+		}
+		if len(deploys.Items) != 1 {
+			tlog.Logf(t, "Expected 1 Deployment for the Gateway, got %d", len(deploys.Items))
+			return false, nil
+		}
+
+		dp := deploys.Items[0]
+		if !strings.HasPrefix(dp.Name, exceptName) {
+			tlog.Logf(t, "Expected Deployment name has prefix '%s', got %s", exceptName, dp.Name)
+			return false, nil
+		}
+
+		if dp.Status.ReadyReplicas <= 0 {
+			tlog.Logf(t, "Expected Deployment %s ready", dp.Name)
+		}
+
+		tlog.Logf(t, "Check Envoy proxy deployment pass, name: %s", dp.Name)
+		return true, nil
+	})
+}
+
+func checkEnvoyProxyService(t *testing.T, suite *suite.ConformanceTestSuite, gwNN types.NamespacedName, exceptName string) error {
+	// Make sure there's deployment for the gateway
+	return wait.PollUntilContextTimeout(context.TODO(), time.Second, suite.TimeoutConfig.CreateTimeout, true, func(ctx context.Context) (bool, error) {
+		svcList := &corev1.ServiceList{}
+		err := suite.Client.List(ctx, svcList, &client.ListOptions{
+			Namespace: "envoy-gateway-system",
+			LabelSelector: labels.SelectorFromSet(map[string]string{
+				"app.kubernetes.io/managed-by":                   "envoy-gateway",
+				"app.kubernetes.io/name":                         "envoy",
+				"gateway.envoyproxy.io/owning-gateway-name":      gwNN.Name,
+				"gateway.envoyproxy.io/owning-gateway-namespace": gwNN.Namespace,
+			}),
+		})
+		if err != nil {
+			return false, err
+		}
+		if len(svcList.Items) != 1 {
+			tlog.Logf(t, "Expected 1 Service for the Gateway, got %d", len(svcList.Items))
+			return false, nil
+		}
+
+		svc := svcList.Items[0]
+		if !strings.HasPrefix(svc.Name, exceptName) {
+			tlog.Logf(t, "Expected Service name has prefix '%s', got %s", exceptName, svc.Name)
+			return false, nil
+		}
+
+		tlog.Logf(t, "Check envoy proxy service pass, name: %s", svc.Name)
+		return true, nil
+	})
+}
+
+func checkEnvoyProxyDaemonSet(t *testing.T, suite *suite.ConformanceTestSuite, gwNN types.NamespacedName, exceptName string) error {
+	// Make sure there's deployment for the gateway
+	return wait.PollUntilContextTimeout(context.TODO(), time.Second, suite.TimeoutConfig.CreateTimeout, true, func(ctx context.Context) (bool, error) {
+		dsList := &appsv1.DaemonSetList{}
+		err := suite.Client.List(ctx, dsList, &client.ListOptions{
+			Namespace: "envoy-gateway-system",
+			LabelSelector: labels.SelectorFromSet(map[string]string{
+				"app.kubernetes.io/managed-by":                   "envoy-gateway",
+				"app.kubernetes.io/name":                         "envoy",
+				"gateway.envoyproxy.io/owning-gateway-name":      gwNN.Name,
+				"gateway.envoyproxy.io/owning-gateway-namespace": gwNN.Namespace,
+			}),
+		})
+		if err != nil {
+			return false, err
+		}
+		if len(dsList.Items) != 1 {
+			tlog.Logf(t, "Expected 1 DaemonSet for the Gateway, got %d", len(dsList.Items))
+			return false, nil
+		}
+
+		ds := dsList.Items[0]
+		if !strings.HasPrefix(ds.Name, exceptName) {
+			tlog.Logf(t, "Expected DaemonSet name has prefix '%s', got %s", exceptName, ds.Name)
+			return false, nil
+		}
+
+		if ds.Status.NumberReady <= 0 {
+			tlog.Logf(t, "Expected DaemonSet %s ready", ds.Name)
+		}
+
+		tlog.Logf(t, "Check envoy proxy DaemonSet pass, name: %s", ds.Name)
+		return true, nil
+	})
+}