Repository: Kong/kubernetes-ingress-controller
Pull request #6583 (2024-10-30 14:56:05)

chore(tests): migrate TestExampleTCPRoute and TestIngressExample to isolated
<!-- Thanks for sending a pull request! Here are some tips for you:
1. If this is your first time, read our contributor guidelines https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#the-pull-request-submit-process and developer guide https://github.com/kubernetes/community/blob/master/contributors/devel/development.md
2. If you want *faster* PR reviews, read how: https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
3. Follow the instructions for writing a release note: https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md and ensure your changes are being reflected in CHANGELOG.md for the next upcoming release
-->

**What this PR does / why we need it**:

All integration tests should be migrated to isolated one (furthermore we want to get rid of those two mutexes), this PR migrates
- `TestExampleTCPRoute`
- `TestIngressExample`

<!-- Please describe why this particular PR is necessary or why you see it as a nice addition -->

**Which issue this PR fixes**:

Part of https://github.com/Kong/kubernetes-ingress-controller/issues/5757

<!--
Here you can add any links to issues that this PR is relevant for.
You can use Github keywords (like: closes, fixes or resolves) to auto-resolve
the linked issue(s) when this PR gets merged.

For example: fixes #<issue number>
-->


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/pull/6583?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 72.3%. Comparing base [(`35b1374`)](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/commit/35b13749ee8c902d6cc807b3dc6ac4289522aec2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong) to head [(`977af72`)](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/commit/977af727db67aaa248ee0b53dacc5b141e9bce9b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong).
> Report is 5 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##            main   #6583     +/-   ##
=======================================
+ Coverage   69.5%   72.3%   +2.8%     
=======================================
  Files        202     202             
  Lines      25796   25801      +5     
=======================================
+ Hits       17935   18665    +730     
+ Misses      6853    6134    -719     
+ Partials    1008    1002      -6     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/pull/6583?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong).


Patch:
diff --git a/examples/gateway-tcproute.yaml b/examples/gateway-tcproute.yaml
index 8a882abaf..8162a5452 100644
--- a/examples/gateway-tcproute.yaml
+++ b/examples/gateway-tcproute.yaml
@@ -42,7 +42,7 @@ spec:
 apiVersion: gateway.networking.k8s.io/v1
 kind: GatewayClass
 metadata:
-  name: kong
+  name: example-tcproute
   annotations:
     konghq.com/gatewayclass-unmanaged: "true"
 spec:
@@ -51,9 +51,9 @@ spec:
 apiVersion: gateway.networking.k8s.io/v1
 kind: Gateway
 metadata:
-  name: kong
+  name: example-tcproute
 spec:
-  gatewayClassName: kong
+  gatewayClassName: example-tcproute
   listeners:
   - name: http
     protocol: HTTP
@@ -68,7 +68,7 @@ metadata:
   name: tcpecho
 spec:
   parentRefs:
-  - name: kong
+  - name: example-tcproute
   rules:
   - backendRefs:
     - name: tcpecho
diff --git a/go.mod b/go.mod
index 85b41ac15..356e1d11a 100644
--- a/go.mod
+++ b/go.mod
@@ -53,7 +53,7 @@ require (
 	github.com/testcontainers/testcontainers-go/modules/postgres v0.34.0
 	github.com/tonglil/buflogr v1.1.1
 	go.uber.org/zap v1.27.0
-	google.golang.org/api v0.203.0
+	google.golang.org/api v0.204.0
 	k8s.io/api v0.31.2
 	k8s.io/apiextensions-apiserver v0.31.2
 	k8s.io/apimachinery v0.31.2
@@ -68,8 +68,8 @@ require (
 )
 
 require (
-	cloud.google.com/go/auth v0.9.9 // indirect
-	cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
+	cloud.google.com/go/auth v0.10.0 // indirect
+	cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect
 	cloud.google.com/go/compute/metadata v0.5.2 // indirect
 	dario.cat/mergo v1.0.1
 	github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
@@ -211,8 +211,8 @@ require (
 	golang.org/x/time v0.7.0 // indirect
 	golang.org/x/tools v0.24.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
 	google.golang.org/grpc v1.67.1
 	google.golang.org/protobuf v1.35.1 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
diff --git a/go.sum b/go.sum
index 54723a60f..d984db72d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,10 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
 cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
-cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ=
-cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
-cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
-cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
+cloud.google.com/go/auth v0.10.0 h1:tWlkvFAh+wwTOzXIjrwM64karR1iTBZ/GRr0S/DULYo=
+cloud.google.com/go/auth v0.10.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
+cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzKpFyKGt3MTk=
+cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
 cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
 cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
 cloud.google.com/go/container v1.41.0 h1:f20+lv3PBeQKgAL7X3VeuDzvF2iYao2AVBTsuTpPk68=
@@ -549,17 +549,17 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
 gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
-google.golang.org/api v0.203.0 h1:SrEeuwU3S11Wlscsn+LA1kb/Y5xT8uggJSkIhD08NAU=
-google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI=
+google.golang.org/api v0.204.0 h1:3PjmQQEDkR/ENVZZwIYB4W/KzYtN8OrqnNcHWpeR8E4=
+google.golang.org/api v0.204.0/go.mod h1:69y8QSoKIbL9F94bWgWAq6wGqGwyjBgi2y8rAK8zLag=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
-google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
+google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
+google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
diff --git a/internal/dataplane/kongstate/consumer.go b/internal/dataplane/kongstate/consumer.go
index 21d9621e3..f6f86911a 100644
--- a/internal/dataplane/kongstate/consumer.go
+++ b/internal/dataplane/kongstate/consumer.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/kong/go-kong/kong"
+	"github.com/samber/lo"
 
 	"github.com/kong/kubernetes-ingress-controller/v3/internal/util"
 	kongv1 "github.com/kong/kubernetes-ingress-controller/v3/pkg/apis/configuration/v1"
@@ -28,39 +29,49 @@ type Consumer struct {
 }
 
 // SanitizedCopy returns a shallow copy with sensitive values redacted best-effort.
-func (c *Consumer) SanitizedCopy(uuidGenerator util.UUIDGenerator) *Consumer {
-	return &Consumer{
+func (c *Consumer) SanitizedCopy(uuidGenerator util.UUIDGenerator) Consumer {
+	return Consumer{
 		Consumer: c.Consumer,
 		Plugins:  c.Plugins,
-		KeyAuths: func() (res []*KeyAuth) {
-			for _, v := range c.KeyAuths {
-				res = append(res, v.SanitizedCopy(uuidGenerator))
+		KeyAuths: func() []*KeyAuth {
+			if c.KeyAuths == nil {
+				return nil
 			}
-			return
+			return lo.Map(c.KeyAuths, func(c *KeyAuth, _ int) *KeyAuth {
+				return c.SanitizedCopy(uuidGenerator)
+			})
 		}(),
-		HMACAuths: func() (res []*HMACAuth) {
-			for _, v := range c.HMACAuths {
-				res = append(res, v.SanitizedCopy())
+		HMACAuths: func() []*HMACAuth {
+			if c.HMACAuths == nil {
+				return nil
 			}
-			return
+			return lo.Map(c.HMACAuths, func(c *HMACAuth, _ int) *HMACAuth {
+				return c.SanitizedCopy()
+			})
 		}(),
-		JWTAuths: func() (res []*JWTAuth) {
-			for _, v := range c.JWTAuths {
-				res = append(res, v.SanitizedCopy())
+		JWTAuths: func() []*JWTAuth {
+			if c.JWTAuths == nil {
+				return nil
 			}
-			return
+			return lo.Map(c.JWTAuths, func(c *JWTAuth, _ int) *JWTAuth {
+				return c.SanitizedCopy()
+			})
 		}(),
-		BasicAuths: func() (res []*BasicAuth) {
-			for _, v := range c.BasicAuths {
-				res = append(res, v.SanitizedCopy())
+		BasicAuths: func() []*BasicAuth {
+			if c.BasicAuths == nil {
+				return nil
 			}
-			return
+			return lo.Map(c.BasicAuths, func(c *BasicAuth, _ int) *BasicAuth {
+				return c.SanitizedCopy()
+			})
 		}(),
-		Oauth2Creds: func() (res []*Oauth2Credential) {
-			for _, v := range c.Oauth2Creds {
-				res = append(res, v.SanitizedCopy())
+		Oauth2Creds: func() []*Oauth2Credential {
+			if c.Oauth2Creds == nil {
+				return nil
 			}
-			return
+			return lo.Map(c.Oauth2Creds, func(c *Oauth2Credential, _ int) *Oauth2Credential {
+				return c.SanitizedCopy()
+			})
 		}(),
 		ACLGroups:       c.ACLGroups,
 		MTLSAuths:       c.MTLSAuths,
diff --git a/internal/dataplane/kongstate/kongstate.go b/internal/dataplane/kongstate/kongstate.go
index 6342f738d..954d54ad8 100644
--- a/internal/dataplane/kongstate/kongstate.go
+++ b/internal/dataplane/kongstate/kongstate.go
@@ -46,25 +46,33 @@ func (ks *KongState) SanitizedCopy(uuidGenerator util.UUIDGenerator) *KongState
 	return &KongState{
 		Services:  ks.Services,
 		Upstreams: ks.Upstreams,
-		Certificates: func() (res []Certificate) {
-			for _, v := range ks.Certificates {
-				res = append(res, *v.SanitizedCopy())
+		Certificates: func() []Certificate {
+			if ks.Certificates == nil {
+				return nil
 			}
-			return
+
+			return lo.Map(ks.Certificates, func(c Certificate, _ int) Certificate {
+				return c.SanitizedCopy()
+			})
 		}(),
 		CACertificates: ks.CACertificates,
 		Plugins:        ks.Plugins,
-		Consumers: func() (res []Consumer) {
-			for _, v := range ks.Consumers {
-				res = append(res, *v.SanitizedCopy(uuidGenerator))
+		Consumers: func() []Consumer {
+			if ks.Consumers == nil {
+				return nil
 			}
-			return
+
+			return lo.Map(ks.Consumers, func(c Consumer, _ int) Consumer {
+				return c.SanitizedCopy(uuidGenerator)
+			})
 		}(),
-		Licenses: func() (res []License) {
-			for _, v := range ks.Licenses {
-				res = append(res, *v.SanitizedCopy())
+		Licenses: func() []License {
+			if ks.Licenses == nil {
+				return nil
 			}
-			return
+			return lo.Map(ks.Licenses, func(l License, _ int) License {
+				return l.SanitizedCopy()
+			})
 		}(),
 		ConsumerGroups: ks.ConsumerGroups,
 		Vaults:         ks.Vaults,
diff --git a/internal/dataplane/kongstate/license.go b/internal/dataplane/kongstate/license.go
index 2d714cbb6..52b064da3 100644
--- a/internal/dataplane/kongstate/license.go
+++ b/internal/dataplane/kongstate/license.go
@@ -10,8 +10,8 @@ type License struct {
 }
 
 // SanitizedCopy returns a shallow copy with sensitive values redacted best-effort.
-func (l License) SanitizedCopy() *License {
-	return &License{
+func (l License) SanitizedCopy() License {
+	return License{
 		License: kong.License{
 			ID:        l.ID,
 			Payload:   redactedString,
diff --git a/internal/dataplane/kongstate/types.go b/internal/dataplane/kongstate/types.go
index fcfa7afe5..9c47856b8 100644
--- a/internal/dataplane/kongstate/types.go
+++ b/internal/dataplane/kongstate/types.go
@@ -54,8 +54,8 @@ type Certificate struct {
 }
 
 // SanitizedCopy returns a shallow copy with sensitive values redacted best-effort.
-func (c *Certificate) SanitizedCopy() *Certificate {
-	return &Certificate{
+func (c *Certificate) SanitizedCopy() Certificate {
+	return Certificate{
 		kong.Certificate{
 			ID:        c.ID,
 			Cert:      c.Cert,


Test patch:
diff --git a/.github/workflows/_test_reports.yaml b/.github/workflows/_test_reports.yaml
index fde5ba423..79625b0ee 100644
--- a/.github/workflows/_test_reports.yaml
+++ b/.github/workflows/_test_reports.yaml
@@ -64,7 +64,7 @@ jobs:
 
       - name: Upload test results to BuildPulse for flaky test detection
         if: ${{ !cancelled() }}
-        uses: buildpulse/buildpulse-action@v0.11.0
+        uses: buildpulse/buildpulse-action@v0.12.0
         with:
           account: 962416
           repository: 127765544
diff --git a/internal/dataplane/kongstate/consumer_test.go b/internal/dataplane/kongstate/consumer_test.go
index 8933b9de3..b45e51f07 100644
--- a/internal/dataplane/kongstate/consumer_test.go
+++ b/internal/dataplane/kongstate/consumer_test.go
@@ -65,7 +65,7 @@ func TestConsumer_SanitizedCopy(t *testing.T) {
 		},
 	} {
 		t.Run(tt.name, func(t *testing.T) {
-			got := *tt.in.SanitizedCopy(mocks.StaticUUIDGenerator{UUID: "52fdfc07-2182-454f-963f-5f0f9a621d72"})
+			got := tt.in.SanitizedCopy(mocks.StaticUUIDGenerator{UUID: "52fdfc07-2182-454f-963f-5f0f9a621d72"})
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/internal/dataplane/kongstate/kongstate_test.go b/internal/dataplane/kongstate/kongstate_test.go
index 23c532e87..4f964243e 100644
--- a/internal/dataplane/kongstate/kongstate_test.go
+++ b/internal/dataplane/kongstate/kongstate_test.go
@@ -147,6 +147,46 @@ func TestKongState_SanitizedCopy(t *testing.T) {
 	ensureAllKongStateFieldsAreCoveredInTest(t, testedFields.UnsortedList())
 }
 
+func BenchmarkSanitizedCopy(b *testing.B) {
+	const count = 1000
+	ks := KongState{
+		Certificates: func() []Certificate {
+			certificates := make([]Certificate, 0, count)
+			for i := 0; i < count; i++ {
+				certificates = append(certificates,
+					Certificate{kong.Certificate{ID: kong.String(strconv.Itoa(i)), Key: kong.String("secret")}},
+				)
+			}
+			return certificates
+		}(),
+		Consumers: func() []Consumer {
+			consumers := make([]Consumer, 0, count)
+			for i := 0; i < count; i++ {
+				consumers = append(consumers,
+					Consumer{
+						Consumer: kong.Consumer{ID: kong.String(strconv.Itoa(i))},
+					},
+				)
+			}
+			return consumers
+		}(),
+		Licenses: func() []License {
+			licenses := make([]License, 0, count)
+			for i := 0; i < count; i++ {
+				licenses = append(licenses,
+					License{kong.License{ID: kong.String(strconv.Itoa(i)), Payload: kong.String("secret")}},
+				)
+			}
+			return licenses
+		}(),
+	}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		ret := ks.SanitizedCopy(mocks.StaticUUIDGenerator{UUID: "52fdfc07-2182-454f-963f-5f0f9a621d72"})
+		_ = ret
+	}
+}
+
 // extractNotEmptyFieldNames returns the names of all non-empty fields in the given KongState.
 // This is to programmatically find out what fields are used in a test case.
 func extractNotEmptyFieldNames(s KongState) []string {
diff --git a/internal/dataplane/kongstate/types_test.go b/internal/dataplane/kongstate/types_test.go
index adb201c2e..bf74b9ba5 100644
--- a/internal/dataplane/kongstate/types_test.go
+++ b/internal/dataplane/kongstate/types_test.go
@@ -34,7 +34,7 @@ func TestCertificate_SanitizedCopy(t *testing.T) {
 		},
 	} {
 		t.Run(tt.name, func(t *testing.T) {
-			got := *tt.in.SanitizedCopy()
+			got := tt.in.SanitizedCopy()
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/test/integration/examples_test.go b/test/integration/examples_test.go
index 585a5c8c0..2dedb7b3b 100644
--- a/test/integration/examples_test.go
+++ b/test/integration/examples_test.go
@@ -3,56 +3,22 @@
 package integration
 
 import (
-	"bytes"
 	"context"
 	"crypto/x509"
 	"encoding/base64"
 	"fmt"
-	"net/http"
 	"os"
-	"strings"
 	"testing"
 
 	"github.com/kong/kubernetes-testing-framework/pkg/clusters"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
-	"github.com/kong/kubernetes-ingress-controller/v3/test"
-	"github.com/kong/kubernetes-ingress-controller/v3/test/consts"
 	"github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers"
 )
 
 const examplesDIR = "../../examples"
 
-func TestTCPRouteExample(t *testing.T) {
-	RunWhenKongExpressionRouter(context.Background(), t)
-	t.Log("locking TCP port")
-	tcpMutex.Lock()
-	t.Cleanup(func() {
-		t.Log("unlocking TCP port")
-		tcpMutex.Unlock()
-	})
-
-	var (
-		ctx                      = context.Background()
-		tcprouteExampleManifests = fmt.Sprintf("%s/gateway-tcproute.yaml", examplesDIR)
-	)
-	_, cleaner := helpers.Setup(ctx, t, env)
-
-	t.Logf("applying yaml manifest %s", tcprouteExampleManifests)
-	b, err := os.ReadFile(tcprouteExampleManifests)
-	require.NoError(t, err)
-	require.NoError(t, clusters.ApplyManifestByYAML(ctx, env.Cluster(), string(b)))
-	cleaner.AddManifest(string(b))
-
-	t.Log("verifying that TCPRoute becomes routable")
-	require.Eventually(t, func() bool {
-		return test.EchoResponds(test.ProtocolTCP, proxyTCPURL, "tcproute-example-manifest") == nil
-	}, ingressWait, waitTick)
-}
-
 func TestTLSRouteExample(t *testing.T) {
 	t.Log("locking Gateway TLS ports")
 	tlsMutex.Lock()
@@ -87,58 +53,3 @@ func TestTLSRouteExample(t *testing.T) {
 		assert.NoError(c, err)
 	}, ingressWait, waitTick)
 }
-
-func TestIngressExample(t *testing.T) {
-	var (
-		ingressExampleManifests = fmt.Sprintf("%s/ingress.yaml", examplesDIR)
-		ctx                     = context.Background()
-	)
-
-	_, cleaner := helpers.Setup(ctx, t, env)
-
-	t.Logf("applying yaml manifest %s", ingressExampleManifests)
-	b, err := os.ReadFile(ingressExampleManifests)
-	require.NoError(t, err)
-	manifests := replaceIngressClassSpecFieldInManifests(string(b))
-	require.NoError(t, clusters.ApplyManifestByYAML(ctx, env.Cluster(), manifests))
-	cleaner.AddManifest(string(b))
-
-	t.Log("waiting for ingress resource to have an address")
-	var ingAddr string
-	require.Eventually(t, func() bool {
-		ing, err := env.Cluster().Client().NetworkingV1().Ingresses(corev1.NamespaceDefault).Get(ctx, "httpbin-ingress", metav1.GetOptions{})
-		if err != nil {
-			return false
-		}
-
-		for _, lbing := range ing.Status.LoadBalancer.Ingress {
-			if lbing.IP != "" {
-				ingAddr = lbing.IP
-				return true
-			}
-		}
-
-		return false
-	}, ingressWait, waitTick)
-
-	t.Logf("verifying that the Ingress resource becomes routable")
-	require.Eventually(t, func() bool {
-		resp, err := helpers.DefaultHTTPClient().Get(fmt.Sprintf("http://%s/", ingAddr))
-		if err != nil {
-			return false
-		}
-		defer resp.Body.Close()
-		if resp.StatusCode == http.StatusOK {
-			b := new(bytes.Buffer)
-			n, err := b.ReadFrom(resp.Body)
-			require.NoError(t, err)
-			require.True(t, n > 0)
-			return strings.Contains(b.String(), "<title>httpbin.org</title>")
-		}
-		return false
-	}, ingressWait, waitTick)
-}
-
-func replaceIngressClassSpecFieldInManifests(manifests string) string {
-	return strings.ReplaceAll(manifests, `ingressClassName: kong`, fmt.Sprintf(`ingressClassName: %s`, consts.IngressClass))
-}
diff --git a/test/integration/isolated/examples_ingress_test.go b/test/integration/isolated/examples_ingress_test.go
new file mode 100644
index 000000000..31cffc174
--- /dev/null
+++ b/test/integration/isolated/examples_ingress_test.go
@@ -0,0 +1,70 @@
+//go:build integration_tests
+
+package isolated
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/kong/kubernetes-testing-framework/pkg/clusters"
+	"github.com/stretchr/testify/assert"
+	"sigs.k8s.io/e2e-framework/pkg/envconf"
+	"sigs.k8s.io/e2e-framework/pkg/features"
+
+	"github.com/kong/kubernetes-ingress-controller/v3/test/integration/consts"
+	"github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers"
+	"github.com/kong/kubernetes-ingress-controller/v3/test/internal/testlabels"
+)
+
+func TestIngressExample(t *testing.T) {
+	ingressExampleManifests := examplesManifestPath("ingress.yaml")
+
+	replaceIngressClassAnnotationInManifests := func(manifests string, ingressClass string) string {
+		return strings.ReplaceAll(manifests, "ingressClassName: kong", fmt.Sprintf("ingressClassName: %s", ingressClass))
+	}
+
+	f := features.
+		New("example").
+		WithLabel(testlabels.Example, testlabels.ExampleTrue).
+		WithLabel(testlabels.NetworkingFamily, testlabels.NetworkingFamilyIngress).
+		WithLabel(testlabels.Kind, testlabels.KindIngress).
+		WithSetup("deploy kong addon into cluster", featureSetup(
+			withControllerManagerOpts(helpers.ControllerManagerOptAdditionalWatchNamespace("default")),
+		)).
+		Assess("deploying to cluster works and HTTP traffic is routed to the service",
+			func(ctx context.Context, t *testing.T, _ *envconf.Config) context.Context {
+				cleaner := GetFromCtxForT[*clusters.Cleaner](ctx, t)
+				cluster := GetClusterFromCtx(ctx)
+				proxyURL := GetHTTPURLFromCtx(ctx)
+
+				t.Logf("applying yaml manifest %s", ingressExampleManifests)
+				b, err := os.ReadFile(ingressExampleManifests)
+				assert.NoError(t, err)
+				manifest := replaceIngressClassAnnotationInManifests(string(b), GetIngressClassFromCtx(ctx))
+				assert.NoError(t, clusters.ApplyManifestByYAML(ctx, cluster, manifest))
+				cleaner.AddManifest(manifest)
+
+				t.Logf("verifying that the Ingress routes traffic properly")
+				helpers.EventuallyGETPath(
+					t,
+					proxyURL,
+					proxyURL.Host,
+					"/",
+					nil,
+					http.StatusOK,
+					"<title>httpbin.org</title>",
+					nil,
+					consts.IngressWait,
+					consts.WaitTick,
+				)
+
+				return ctx
+			}).
+		Teardown(featureTeardown())
+
+	tenv.Test(t, f.Feature())
+}
diff --git a/test/integration/isolated/examples_tcproute_test.go b/test/integration/isolated/examples_tcproute_test.go
new file mode 100644
index 000000000..1d6e64083
--- /dev/null
+++ b/test/integration/isolated/examples_tcproute_test.go
@@ -0,0 +1,53 @@
+//go:build integration_tests
+
+package isolated
+
+import (
+	"context"
+	"os"
+	"testing"
+
+	"github.com/kong/kubernetes-testing-framework/pkg/clusters"
+	"github.com/stretchr/testify/assert"
+	"sigs.k8s.io/e2e-framework/pkg/envconf"
+	"sigs.k8s.io/e2e-framework/pkg/features"
+
+	"github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers"
+	"github.com/kong/kubernetes-ingress-controller/v3/test/internal/testlabels"
+)
+
+func TestExampleTCPRoute(t *testing.T) {
+	tcpRouteExampleManifests := examplesManifestPath("gateway-tcproute.yaml")
+
+	f := features.
+		New("example").
+		WithLabel(testlabels.Example, testlabels.ExampleTrue).
+		WithLabel(testlabels.NetworkingFamily, testlabels.NetworkingFamilyGatewayAPI).
+		WithLabel(testlabels.Kind, testlabels.KindTCPRoute).
+		Setup(SkipIfRouterNotExpressions).
+		WithSetup("deploy kong addon into cluster", featureSetup(
+			withControllerManagerOpts(helpers.ControllerManagerOptAdditionalWatchNamespace("default")),
+		)).
+		Assess("deploying to cluster works and tcp traffic is routed to the service",
+			func(ctx context.Context, t *testing.T, _ *envconf.Config) context.Context {
+				cleaner := GetFromCtxForT[*clusters.Cleaner](ctx, t)
+				cluster := GetClusterFromCtx(ctx)
+				proxyTCPURL := GetTCPURLFromCtx(ctx)
+
+				t.Logf("applying yaml manifest %s", tcpRouteExampleManifests)
+				b, err := os.ReadFile(tcpRouteExampleManifests)
+				assert.NoError(t, err)
+
+				s := string(b)
+				assert.NoError(t, clusters.ApplyManifestByYAML(ctx, cluster, s))
+				cleaner.AddManifest(s)
+
+				t.Logf("verifying that the TCPRoute becomes routable")
+				assertEventuallyResponseTCP(t, proxyTCPURL, "tcproute-example-manifest")
+
+				return ctx
+			}).
+		Teardown(featureTeardown())
+
+	tenv.Test(t, f.Feature())
+}
diff --git a/test/integration/isolated/examples_udproute_test.go b/test/integration/isolated/examples_udproute_test.go
index 689dad5b4..67f632cbd 100644
--- a/test/integration/isolated/examples_udproute_test.go
+++ b/test/integration/isolated/examples_udproute_test.go
@@ -12,8 +12,6 @@ import (
 	"sigs.k8s.io/e2e-framework/pkg/envconf"
 	"sigs.k8s.io/e2e-framework/pkg/features"
 
-	"github.com/kong/kubernetes-ingress-controller/v3/test"
-	"github.com/kong/kubernetes-ingress-controller/v3/test/integration/consts"
 	"github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers"
 	"github.com/kong/kubernetes-ingress-controller/v3/test/internal/testlabels"
 )
@@ -45,11 +43,7 @@ func TestExampleUDPRoute(t *testing.T) {
 				cleaner.AddManifest(s)
 
 				t.Logf("verifying that the UDPRoute becomes routable")
-				assert.EventuallyWithT(t, func(c *assert.CollectT) {
-					assert.NoError(
-						c, test.EchoResponds(test.ProtocolUDP, proxyUDPURL, "udproute-example-manifest"),
-					)
-				}, consts.IngressWait, consts.WaitTick)
+				assertEventuallyResponseUDP(t, proxyUDPURL, "udproute-example-manifest")
 
 				return ctx
 			}).