Copilot commented on code in PR #2564:
URL: 
https://github.com/apache/apisix-ingress-controller/pull/2564#discussion_r2366551246


##########
internal/controller/tcproute_controller.go:
##########
@@ -0,0 +1,504 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package controller
+
+import (
+       "cmp"
+       "context"
+       "fmt"
+
+       "github.com/apache/apisix-ingress-controller/api/v1alpha1"
+       
"github.com/apache/apisix-ingress-controller/internal/controller/indexer"
+       "github.com/apache/apisix-ingress-controller/internal/controller/status"
+       "github.com/apache/apisix-ingress-controller/internal/manager/readiness"
+       "github.com/apache/apisix-ingress-controller/internal/provider"
+       "github.com/apache/apisix-ingress-controller/internal/types"
+       "github.com/apache/apisix-ingress-controller/internal/utils"
+       "github.com/go-logr/logr"
+       corev1 "k8s.io/api/core/v1"
+       discoveryv1 "k8s.io/api/discovery/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       k8stypes "k8s.io/apimachinery/pkg/types"
+       ctrl "sigs.k8s.io/controller-runtime"
+       "sigs.k8s.io/controller-runtime/pkg/builder"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+       "sigs.k8s.io/controller-runtime/pkg/event"
+       "sigs.k8s.io/controller-runtime/pkg/handler"
+       "sigs.k8s.io/controller-runtime/pkg/predicate"
+       "sigs.k8s.io/controller-runtime/pkg/reconcile"
+       gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+       gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
+       "sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+// TCPRouteReconciler reconciles a TCPRoute object.
+type TCPRouteReconciler struct { //nolint:revive
+       client.Client
+       Scheme *runtime.Scheme
+
+       Log logr.Logger
+
+       Provider provider.Provider
+
+       Updater status.Updater
+       Readier readiness.ReadinessManager
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *TCPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
+
+       bdr := ctrl.NewControllerManagedBy(mgr).
+               For(&gatewayv1alpha2.TCPRoute{}).
+               WithEventFilter(predicate.GenerationChangedPredicate{}).
+               Watches(&discoveryv1.EndpointSlice{},
+                       
handler.EnqueueRequestsFromMapFunc(r.listTCPRoutesByServiceBef),
+               ).
+               Watches(&gatewayv1.Gateway{},
+                       
handler.EnqueueRequestsFromMapFunc(r.listTCPRoutesForGateway),
+                       builder.WithPredicates(
+                               predicate.Funcs{
+                                       GenericFunc: func(e event.GenericEvent) 
bool {
+                                               return false
+                                       },
+                                       DeleteFunc: func(e event.DeleteEvent) 
bool {
+                                               return false
+                                       },
+                                       CreateFunc: func(e event.CreateEvent) 
bool {
+                                               return true
+                                       },
+                                       UpdateFunc: func(e event.UpdateEvent) 
bool {
+                                               return true
+                                       },
+                               },
+                       ),
+               ).
+               Watches(&v1alpha1.BackendTrafficPolicy{},
+                       
handler.EnqueueRequestsFromMapFunc(r.listTCPRoutesForBackendTrafficPolicy),
+               ).
+               Watches(&v1alpha1.GatewayProxy{},
+                       
handler.EnqueueRequestsFromMapFunc(r.listTCPRoutesForGatewayProxy),
+               )
+
+       if GetEnableReferenceGrant() {
+               bdr.Watches(&v1beta1.ReferenceGrant{},
+                       
handler.EnqueueRequestsFromMapFunc(r.listTCPRoutesForReferenceGrant),
+                       
builder.WithPredicates(referenceGrantPredicates(KindTCPRoute)),
+               )
+       }
+
+       return bdr.Complete(r)
+}
+
+func (r *TCPRouteReconciler) listTCPRoutesForBackendTrafficPolicy(ctx 
context.Context, obj client.Object) []reconcile.Request {
+       policy, ok := obj.(*v1alpha1.BackendTrafficPolicy)
+       if !ok {
+               r.Log.Error(fmt.Errorf("unexpected object type"), "failed to 
convert object to BackendTrafficPolicy")
+               return nil
+       }
+
+       tcprouteList := []gatewayv1alpha2.TCPRoute{}
+       for _, targetRef := range policy.Spec.TargetRefs {
+               service := &corev1.Service{}
+               if err := r.Get(ctx, client.ObjectKey{
+                       Namespace: policy.Namespace,
+                       Name:      string(targetRef.Name),
+               }, service); err != nil {
+                       if client.IgnoreNotFound(err) != nil {
+                               r.Log.Error(err, "failed to get service", 
"namespace", policy.Namespace, "name", targetRef.Name)
+                       }
+                       continue
+               }
+               tcprList := &gatewayv1alpha2.TCPRouteList{}
+               if err := r.List(ctx, tcprList, client.MatchingFields{
+                       indexer.ServiceIndexRef: 
indexer.GenIndexKey(policy.Namespace, string(targetRef.Name)),
+               }); err != nil {
+                       r.Log.Error(err, "failed to list tcproutes by service 
reference", "service", targetRef.Name)
+                       return nil
+               }
+               tcprouteList = append(tcprouteList, tcprList.Items...)
+       }
+       var namespacedNameMap = make(map[k8stypes.NamespacedName]struct{})
+       requests := make([]reconcile.Request, 0, len(tcprouteList))
+       for _, tr := range tcprouteList {
+               key := k8stypes.NamespacedName{
+                       Namespace: tr.Namespace,
+                       Name:      tr.Name,
+               }
+               if _, ok := namespacedNameMap[key]; !ok {
+                       namespacedNameMap[key] = struct{}{}
+                       requests = append(requests, reconcile.Request{
+                               NamespacedName: client.ObjectKey{
+                                       Namespace: tr.Namespace,
+                                       Name:      tr.Name,
+                               },
+                       })
+               }
+       }
+       return requests
+}
+
+func (r *TCPRouteReconciler) listTCPRoutesForGateway(ctx context.Context, obj 
client.Object) []reconcile.Request {
+       gateway, ok := obj.(*gatewayv1.Gateway)
+       if !ok {
+               r.Log.Error(fmt.Errorf("unexpected object type"), "failed to 
convert object to Gateway")
+       }
+       tcprList := &gatewayv1alpha2.TCPRouteList{}
+       if err := r.List(ctx, tcprList, client.MatchingFields{
+               indexer.ParentRefs: indexer.GenIndexKey(gateway.Namespace, 
gateway.Name),
+       }); err != nil {
+               r.Log.Error(err, "failed to list tcproutes by gateway", 
"gateway", gateway.Name)
+               return nil
+       }
+
+       requests := make([]reconcile.Request, 0, len(tcprList.Items))
+       for _, tcr := range tcprList.Items {
+               requests = append(requests, reconcile.Request{
+                       NamespacedName: client.ObjectKey{
+                               Namespace: tcr.Namespace,
+                               Name:      tcr.Name,
+                       },
+               })
+       }
+       return requests
+}
+
+// listTCPRoutesForGatewayProxy list all TCPRoute resources that are affected 
by a given GatewayProxy
+func (r *TCPRouteReconciler) listTCPRoutesForGatewayProxy(ctx context.Context, 
obj client.Object) []reconcile.Request {
+       gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy)
+       if !ok {
+               r.Log.Error(fmt.Errorf("unexpected object type"), "failed to 
convert object to GatewayProxy")
+               return nil
+       }
+
+       namespace := gatewayProxy.GetNamespace()
+       name := gatewayProxy.GetName()
+
+       // find all gateways that reference this gateway proxy
+       gatewayList := &gatewayv1.GatewayList{}
+       if err := r.List(ctx, gatewayList, client.MatchingFields{
+               indexer.ParametersRef: indexer.GenIndexKey(namespace, name),
+       }); err != nil {
+               r.Log.Error(err, "failed to list gateways for gateway proxy", 
"gatewayproxy", gatewayProxy.GetName())
+               return nil
+       }
+
+       var requests []reconcile.Request
+
+       // for each gateway, find all TCPRoute resources that reference it
+       for _, gateway := range gatewayList.Items {
+               tcpRouteList := &gatewayv1alpha2.TCPRouteList{}
+               if err := r.List(ctx, tcpRouteList, client.MatchingFields{
+                       indexer.ParentRefs: 
indexer.GenIndexKey(gateway.Namespace, gateway.Name),
+               }); err != nil {
+                       r.Log.Error(err, "failed to list tcproutes for 
gateway", "gateway", gateway.Name)
+                       continue
+               }
+
+               for _, tcpRoute := range tcpRouteList.Items {
+                       requests = append(requests, reconcile.Request{
+                               NamespacedName: client.ObjectKey{
+                                       Namespace: tcpRoute.Namespace,
+                                       Name:      tcpRoute.Name,
+                               },
+                       })
+               }
+       }
+
+       return requests
+}
+
+func (r *TCPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) 
(ctrl.Result, error) {
+       defer r.Readier.Done(&gatewayv1alpha2.TCPRoute{}, req.NamespacedName)
+       tr := new(gatewayv1alpha2.TCPRoute)
+       if err := r.Get(ctx, req.NamespacedName, tr); err != nil {
+               if client.IgnoreNotFound(err) == nil {
+                       tr.Namespace = req.Namespace
+                       tr.Name = req.Name
+
+                       tr.TypeMeta = metav1.TypeMeta{
+                               Kind:       KindTCPRoute,
+                               APIVersion: gatewayv1.GroupVersion.String(),

Review Comment:
   APIVersion is set to gatewayv1 for a TCPRoute, but TCPRoute lives in 
v1alpha2. Use gatewayv1alpha2.GroupVersion.String() so Provider.Delete applies 
the correct GVK.
   ```suggestion
                                APIVersion: 
gatewayv1alpha2.GroupVersion.String(),
   ```



##########
internal/adc/translator/tcproute.go:
##########
@@ -0,0 +1,160 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package translator
+
+import (
+       "fmt"
+
+       adctypes "github.com/apache/apisix-ingress-controller/api/adc"
+       apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
+       "github.com/apache/apisix-ingress-controller/internal/controller/label"
+       "github.com/apache/apisix-ingress-controller/internal/id"
+       "github.com/apache/apisix-ingress-controller/internal/provider"
+       gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+       gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
+)
+
+func newDefaultUpstreamWithoutScheme() *adctypes.Upstream {
+       return &adctypes.Upstream{
+               Metadata: adctypes.Metadata{
+                       Labels: map[string]string{
+                               "managed-by": "apisix-ingress-controller",
+                       },
+               },
+               Nodes: make(adctypes.UpstreamNodes, 0),
+       }
+}
+
+func (t *Translator) TranslateTCPRoute(tctx *provider.TranslateContext, 
tcpRoute *gatewayv1alpha2.TCPRoute) (*TranslateResult, error) {
+       result := &TranslateResult{}
+       rules := tcpRoute.Spec.Rules
+       labels := label.GenLabel(tcpRoute)
+       for ruleIndex, rule := range rules {
+               service := adctypes.NewDefaultService()
+               service.Labels = labels
+               service.Name = 
adctypes.ComposeServiceNameWithStream(tcpRoute.Namespace, tcpRoute.Name, 
fmt.Sprintf("%d", ruleIndex))
+               service.ID = id.GenID(service.Name)
+               var (
+                       upstreams         = make([]*adctypes.Upstream, 0)
+                       weightedUpstreams = 
make([]adctypes.TrafficSplitConfigRuleWeightedUpstream, 0)
+               )
+               for _, backend := range rule.BackendRefs {
+                       if backend.Namespace == nil {
+                               namespace := 
gatewayv1.Namespace(tcpRoute.Namespace)
+                               backend.Namespace = &namespace
+                       }
+                       upstream := newDefaultUpstreamWithoutScheme()
+                       upNodes, err := t.translateBackendRef(tctx, backend, 
DefaultEndpointFilter)

Review Comment:
   translateBackendRef expects a gatewayv1.BackendRef, but backend here is 
gatewayv1alpha2.BackendRef. Construct a gatewayv1.BackendRef (copying 
Name/Namespace/Port/Kind/Weight) and pass that to translateBackendRef.
   ```suggestion
                        convertedBackend := gatewayv1.BackendRef{
                                BackendObjectReference: 
gatewayv1.BackendObjectReference{
                                        Name:      backend.Name,
                                        Namespace: backend.Namespace,
                                        Port:      backend.Port,
                                        Kind:      backend.Kind,
                                },
                                Weight: backend.Weight,
                        }
                        upNodes, err := t.translateBackendRef(tctx, 
convertedBackend, DefaultEndpointFilter)
   ```



##########
test/e2e/gatewayapi/tcproute.go:
##########
@@ -0,0 +1,171 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package gatewayapi
+
+import (
+       "fmt"
+       "time"
+
+       "github.com/apache/apisix-ingress-controller/test/e2e/framework"
+       "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+       . "github.com/onsi/ginkgo/v2"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("TCPRoute E2E Test", func() {
+       s := scaffold.NewDefaultScaffold()
+
+       var gatewayProxyYaml = `
+apiVersion: apisix.apache.org/v1alpha1
+kind: GatewayProxy
+metadata:
+  name: %s
+spec:
+  provider:
+    type: ControlPlane
+    controlPlane:
+      service:
+        name: %s
+        port: 9180
+      auth:
+        type: AdminKey
+        adminKey:
+          value: "%s"
+`
+       getGatewayProxySpec := func() string {
+               return fmt.Sprintf(gatewayProxyYaml, s.Namespace(), 
framework.ProviderType, s.AdminKey())
+       }
+
+       var gatewayClassYaml = `
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+  name: %s
+spec:
+  controllerName: %s
+`
+       Context("TCPRoute Base", func() {
+               var tcpGateway = `
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: %s
+spec:
+  gatewayClassName: %s
+  listeners:
+  - name: tcp
+    protocol: TCP
+    port: 80
+    allowedRoutes:
+      kinds:
+      - kind: TCPRoute
+  infrastructure:
+    parametersRef:
+      group: apisix.apache.org
+      kind: GatewayProxy
+      name: %s
+`
+
+               var tcpRoute = `
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: TCPRoute
+metadata:
+  name: tcp-app-1
+spec:
+  parentRefs:
+  - name: %s
+    sectionName: tcp
+  rules:
+  - backendRefs:
+    - name: httpbin-service-e2e-test
+      port: 80
+`
+
+               BeforeEach(func() {
+                       // Create GatewayProxy
+                       
Expect(s.CreateResourceFromStringWithNamespace(getGatewayProxySpec(), 
s.Namespace())).
+                               NotTo(HaveOccurred(), "creating GatewayProxy")
+
+                       // Create GatewayClass
+                       gatewayClassName := s.Namespace()
+                       
Expect(s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayClassYaml, 
gatewayClassName, s.GetControllerName()), "")).
+                               NotTo(HaveOccurred(), "creating GatewayClass")
+
+                       s.RetryAssertion(func() string {
+                               gcyaml, _ := s.GetResourceYaml("GatewayClass", 
gatewayClassName)
+                               return gcyaml
+                       }).Should(
+                               And(
+                                       ContainSubstring(`status: "True"`),
+                                       ContainSubstring("message: the 
gatewayclass has been accepted by the apisix-ingress-controller"),
+                               ),
+                               "check GatewayClass condition",
+                       )
+
+                       // Create Gateway with TCP listener
+                       gatewayName := s.Namespace()
+                       
Expect(s.CreateResourceFromStringWithNamespace(fmt.Sprintf(tcpGateway, 
gatewayName, gatewayClassName, s.Namespace()), s.Namespace())).
+                               NotTo(HaveOccurred(), "creating Gateway")
+
+                       s.RetryAssertion(func() string {
+                               gwyaml, _ := s.GetResourceYaml("Gateway", 
gatewayName)
+                               return gwyaml
+                       }).Should(
+                               And(
+                                       ContainSubstring(`status: "True"`),
+                                       ContainSubstring("message: the gateway 
has been accepted by the apisix-ingress-controlle"),

Review Comment:
   Typo in expected message ('controlle'). Append the missing 'r' to match 
'apisix-ingress-controller'.
   ```suggestion
                                        ContainSubstring("message: the gateway 
has been accepted by the apisix-ingress-controller"),
   ```



##########
test/e2e/scaffold/scaffold.go:
##########
@@ -193,6 +193,27 @@ func (s *Scaffold) NewAPISIXClient() *httpexpect.Expect {
        })
 }
 
+func (s *Scaffold) NewAPISIXClientOnTCPPort() *httpexpect.Expect {
+       u := url.URL{
+               Scheme: "http",
+               Host:   s.apisixTunnels.TCP.Endpoint(),
+       }
+       fmt.Println("tcp endpoint:", u.String())
+       fmt.Println("http endpoint", s.apisixTunnels.HTTP.Endpoint())

Review Comment:
   Remove stdout debug prints in test scaffolding to keep E2E logs clean, or 
route them via GinkgoWriter if you need them for debugging.
   ```suggestion
        fmt.Fprintln(GinkgoWriter, "tcp endpoint:", u.String())
        fmt.Fprintln(GinkgoWriter, "http endpoint", 
s.apisixTunnels.HTTP.Endpoint())
   ```



##########
test/e2e/scaffold/scaffold.go:
##########
@@ -193,6 +193,27 @@ func (s *Scaffold) NewAPISIXClient() *httpexpect.Expect {
        })
 }
 
+func (s *Scaffold) NewAPISIXClientOnTCPPort() *httpexpect.Expect {
+       u := url.URL{
+               Scheme: "http",
+               Host:   s.apisixTunnels.TCP.Endpoint(),
+       }
+       fmt.Println("tcp endpoint:", u.String())
+       fmt.Println("http endpoint", s.apisixTunnels.HTTP.Endpoint())
+       return httpexpect.WithConfig(httpexpect.Config{
+               BaseURL: u.String(),
+               Client: &http.Client{
+                       Transport: &http.Transport{},
+                       CheckRedirect: func(req *http.Request, via 
[]*http.Request) error {
+                               return http.ErrUseLastResponse
+                       },
+               },
+               Reporter: httpexpect.NewAssertReporter(
+                       httpexpect.NewAssertReporter(GinkgoT()),
+               ),

Review Comment:
   Reporter is double-wrapped with NewAssertReporter; NewAssertReporter expects 
a testing.TB. Use a single call: Reporter: 
httpexpect.NewAssertReporter(GinkgoT()),



##########
internal/adc/translator/translator.go:
##########
@@ -40,4 +40,5 @@ type TranslateResult struct {
        GlobalRules    adctypes.GlobalRule
        PluginMetadata adctypes.PluginMetadata
        Consumers      []*adctypes.Consumer
+       StreamRoutes   []*adctypes.StreamRoute

Review Comment:
   [nitpick] TranslateResult now includes StreamRoutes, but TranslateTCPRoute 
currently attaches stream routes to Services and does not populate this slice. 
If callers don't consume StreamRoutes directly, remove this field; otherwise, 
populate it in TranslateTCPRoute and ensure the provider applies them.
   ```suggestion
   
   ```



##########
test/e2e/gatewayapi/tcproute.go:
##########
@@ -0,0 +1,171 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package gatewayapi
+
+import (
+       "fmt"
+       "time"
+
+       "github.com/apache/apisix-ingress-controller/test/e2e/framework"
+       "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+       . "github.com/onsi/ginkgo/v2"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("TCPRoute E2E Test", func() {
+       s := scaffold.NewDefaultScaffold()
+
+       var gatewayProxyYaml = `
+apiVersion: apisix.apache.org/v1alpha1
+kind: GatewayProxy
+metadata:
+  name: %s
+spec:
+  provider:
+    type: ControlPlane
+    controlPlane:
+      service:
+        name: %s
+        port: 9180
+      auth:
+        type: AdminKey
+        adminKey:
+          value: "%s"
+`
+       getGatewayProxySpec := func() string {
+               return fmt.Sprintf(gatewayProxyYaml, s.Namespace(), 
framework.ProviderType, s.AdminKey())
+       }
+
+       var gatewayClassYaml = `
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+  name: %s
+spec:
+  controllerName: %s
+`
+       Context("TCPRoute Base", func() {
+               var tcpGateway = `
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: %s
+spec:
+  gatewayClassName: %s
+  listeners:
+  - name: tcp
+    protocol: TCP
+    port: 80
+    allowedRoutes:
+      kinds:
+      - kind: TCPRoute
+  infrastructure:
+    parametersRef:
+      group: apisix.apache.org
+      kind: GatewayProxy
+      name: %s
+`
+
+               var tcpRoute = `
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: TCPRoute
+metadata:
+  name: tcp-app-1
+spec:
+  parentRefs:
+  - name: %s
+    sectionName: tcp
+  rules:
+  - backendRefs:
+    - name: httpbin-service-e2e-test
+      port: 80
+`
+
+               BeforeEach(func() {
+                       // Create GatewayProxy
+                       
Expect(s.CreateResourceFromStringWithNamespace(getGatewayProxySpec(), 
s.Namespace())).
+                               NotTo(HaveOccurred(), "creating GatewayProxy")
+
+                       // Create GatewayClass
+                       gatewayClassName := s.Namespace()
+                       
Expect(s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayClassYaml, 
gatewayClassName, s.GetControllerName()), "")).
+                               NotTo(HaveOccurred(), "creating GatewayClass")
+
+                       s.RetryAssertion(func() string {
+                               gcyaml, _ := s.GetResourceYaml("GatewayClass", 
gatewayClassName)
+                               return gcyaml
+                       }).Should(
+                               And(
+                                       ContainSubstring(`status: "True"`),
+                                       ContainSubstring("message: the 
gatewayclass has been accepted by the apisix-ingress-controller"),
+                               ),
+                               "check GatewayClass condition",
+                       )
+
+                       // Create Gateway with TCP listener
+                       gatewayName := s.Namespace()
+                       
Expect(s.CreateResourceFromStringWithNamespace(fmt.Sprintf(tcpGateway, 
gatewayName, gatewayClassName, s.Namespace()), s.Namespace())).
+                               NotTo(HaveOccurred(), "creating Gateway")
+
+                       s.RetryAssertion(func() string {
+                               gwyaml, _ := s.GetResourceYaml("Gateway", 
gatewayName)
+                               return gwyaml
+                       }).Should(
+                               And(
+                                       ContainSubstring(`status: "True"`),
+                                       ContainSubstring("message: the gateway 
has been accepted by the apisix-ingress-controlle"),
+                               ),
+                               "check Gateway condition status",
+                       )
+               })
+
+               It("should route TCP traffic to backend service", func() {
+                       gatewayName := s.Namespace()
+                       By("creating TCPRoute")
+                       Expect(s.CreateResourceFromString(fmt.Sprintf(tcpRoute, 
gatewayName))).
+                               NotTo(HaveOccurred(), "creating TCPRoute")
+
+                       // Verify TCPRoute status becomes programmed
+                       s.RetryAssertion(func() string {
+                               routeYaml, _ := s.GetResourceYaml("TCPRoute", 
"tcp-app-1")
+                               return routeYaml
+                       }).Should(
+                               ContainSubstring(`status: "True"`),
+                               "check TCPRoute status",
+                       )
+
+                       By("verifying TCPRoute is functional")
+                       s.HTTPOverTCPConnectAssert(true, time.Second*10) // 
should be able to connect
+                       By("sending TCP traffic to verify routing")
+                       s.RequestAssert(&scaffold.RequestAssert{
+                               Client:   s.NewAPISIXClientOnTCPPort(),
+                               Method:   "GET",
+                               Path:     "/get",
+                               Check:    scaffold.WithExpectedStatus(200),
+                               Timeout:  time.Minute * 30,

Review Comment:
   A 30-minute timeout will significantly slow E2E runs and mask issues. 
Consider reducing to a more reasonable bound (e.g., 60–120 seconds) with the 
same polling interval.
   ```suggestion
                                Timeout:  time.Second * 120,
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to