Repository: kumahq/kuma
Pull request #11457 (2024-09-18 14:07:30)

fix(kuma-cp): do not sync policies with empty topLevel targetRef to zones that does not support it
Fixes: #11426

Since `Policy` interface exposes only function `GetTargetRef` which returns default targetRef, we don't have an easy way of checking if policy had targetRef set. To deal with this, we can introduce `UsesSyntacticSugar` to make sure we are not syncing policies not supported by older version. This can be easily removed in two versions

### Checklist prior to review

<!--
Each of these sections need to be filled by the author when opening the PR.

If something doesn't apply please check the box and add a justification after the `--`
-->

- [x] [Link to relevant issue][1] as well as docs and UI issues --
- [x] This will not break child repos: it doesn't hardcode values (.e.g "kumahq" as a image registry) and it will work on Windows, system specific functions like `syscall.Mkfifo` have equivalent implementation on the other OS --
- [x] Tests (Unit test, E2E tests, manual test on universal and k8s) --
  - Don't forget `ci/` labels to run additional/fewer tests
- [x] Do you need to update [`UPGRADE.md`](../blob/master/UPGRADE.md)? --
- [x] Does it need to be backported according to the [backporting policy](../blob/master/CONTRIBUTING.md#backporting)? ([this](https://github.com/kumahq/kuma/actions/workflows/auto-backport.yaml) GH action will add "backport" label based on these [file globs](https://github.com/kumahq/kuma/blob/master/.github/workflows/auto-backport.yaml#L6), if you want to prevent it from adding the "backport" label use [no-backport-autolabel](https://github.com/kumahq/kuma/blob/master/.github/workflows/auto-backport.yaml#L8) label) --

<!--
> Changelog: skip
-->
<!--
Uncomment the above section to explicitly set a [`> Changelog:` entry here](https://github.com/kumahq/kuma/blob/master/CONTRIBUTING.md#submitting-a-patch)?
-->

[1]: https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword



Patch:
diff --git a/api/common/v1alpha1/ref.go b/api/common/v1alpha1/ref.go
index 97b80ea9c..83df5bea5 100644
--- a/api/common/v1alpha1/ref.go
+++ b/api/common/v1alpha1/ref.go
@@ -80,6 +80,10 @@ func (x TargetRefKindSlice) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 
 // TargetRef defines structure that allows attaching policy to various objects
 type TargetRef struct {
+	// This is needed to not sync policies with empty topLevelTarget ref to old zones that does not support it
+	// This can be removed in 2.11.x
+	UsesSyntacticSugar bool `json:"-"`
+
 	// Kind of the referenced resource
 	// +kubebuilder:validation:Enum=Mesh;MeshSubset;MeshGateway;MeshService;MeshExternalService;MeshMultiZoneService;MeshServiceSubset;MeshHTTPRoute
 	Kind TargetRefKind `json:"kind,omitempty"`
diff --git a/app/kumactl/data/install/k8s/metrics/grafana/grafana.yaml b/app/kumactl/data/install/k8s/metrics/grafana/grafana.yaml
index a14d7889a..d95e43073 100644
--- a/app/kumactl/data/install/k8s/metrics/grafana/grafana.yaml
+++ b/app/kumactl/data/install/k8s/metrics/grafana/grafana.yaml
@@ -215,6 +215,7 @@ metadata:
   namespace: {{ .Namespace }}
   labels:
     app: grafana
+    kuma.io/mesh: {{ .Mesh }}
 spec:
   type: ClusterIP
   ports:
diff --git a/app/kumactl/data/install/k8s/metrics/prometheus/server.yaml b/app/kumactl/data/install/k8s/metrics/prometheus/server.yaml
index 695e71a7b..2591eb08c 100644
--- a/app/kumactl/data/install/k8s/metrics/prometheus/server.yaml
+++ b/app/kumactl/data/install/k8s/metrics/prometheus/server.yaml
@@ -382,6 +382,7 @@ metadata:
   labels:
     component: "server"
     app: prometheus
+    kuma.io/mesh: {{ .Mesh }}
   name: prometheus-server
   namespace: {{ .Namespace }}
 spec:
diff --git a/pkg/kds/context/context.go b/pkg/kds/context/context.go
index 519092cac..3ec0e3066 100644
--- a/pkg/kds/context/context.go
+++ b/pkg/kds/context/context.go
@@ -273,6 +273,12 @@ func GlobalProvidedFilter(rm manager.ResourceManager, configs map[string]bool) r
 
 		switch {
 		case isGlobal && r.Descriptor().KDSFlags.Has(core_model.GlobalToAllZonesFlag):
+			if r.Descriptor().IsPluginOriginated && r.Descriptor().IsPolicy {
+				policy := r.GetSpec().(core_model.Policy)
+				if policy.GetTargetRef().UsesSyntacticSugar && !features.HasFeature(kds.FeatureOptionalTopLevelTargetRef) {
+					return false
+				}
+			}
 			return true
 		case !isGlobal && r.Descriptor().KDSFlags.Has(core_model.GlobalToAllButOriginalZoneFlag):
 			if r.Descriptor().IsPluginOriginated && r.Descriptor().IsPolicy {
@@ -280,6 +286,9 @@ func GlobalProvidedFilter(rm manager.ResourceManager, configs map[string]bool) r
 					return false
 				}
 				policy := r.GetSpec().(core_model.Policy)
+				if policy.GetTargetRef().UsesSyntacticSugar && !features.HasFeature(kds.FeatureOptionalTopLevelTargetRef) {
+					return false
+				}
 				role, err := core_model.ComputePolicyRole(policy, r.GetMeta().GetLabels()[mesh_proto.KubeNamespaceTag])
 				if err != nil {
 					ri := core_model.NewResourceIdentifier(r)
diff --git a/pkg/kds/features.go b/pkg/kds/features.go
index 60bf78eb8..caba2caa6 100644
--- a/pkg/kds/features.go
+++ b/pkg/kds/features.go
@@ -37,6 +37,8 @@ const FeatureHostnameGeneratorMzSelector string = "hg-mz-selector"
 // FeatureProducerPolicyFlow means that the zone control plane supports the producer policy flow.
 const FeatureProducerPolicyFlow string = "producer-policy-flow"
 
+const FeatureOptionalTopLevelTargetRef string = "optional-top-level-target-ref"
+
 func ContextHasFeature(ctx context.Context, feature string) bool {
 	md, _ := metadata.FromIncomingContext(ctx)
 	features := md.Get(FeaturesMetadataKey)
diff --git a/pkg/kds/v2/client/stream.go b/pkg/kds/v2/client/stream.go
index d15153cab..e3e9245c8 100644
--- a/pkg/kds/v2/client/stream.go
+++ b/pkg/kds/v2/client/stream.go
@@ -79,6 +79,7 @@ func (s *stream) DeltaDiscoveryRequest(resourceType core_model.ResourceType) err
 							{Kind: &structpb.Value_StringValue{StringValue: kds.FeatureHashSuffix}},
 							{Kind: &structpb.Value_StringValue{StringValue: kds.FeatureHostnameGeneratorMzSelector}},
 							{Kind: &structpb.Value_StringValue{StringValue: kds.FeatureProducerPolicyFlow}},
+							{Kind: &structpb.Value_StringValue{StringValue: kds.FeatureOptionalTopLevelTargetRef}},
 						},
 					}}},
 				},
diff --git a/pkg/plugins/policies/donothingpolicy/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/donothingpolicy/api/v1alpha1/zz_generated.helpers.go
index c51b81fa3..7d83a056e 100644
--- a/pkg/plugins/policies/donothingpolicy/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/donothingpolicy/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *DoNothingPolicy) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshaccesslog/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshaccesslog/api/v1alpha1/zz_generated.helpers.go
index 764afb0cd..4de0d03a6 100644
--- a/pkg/plugins/policies/meshaccesslog/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshaccesslog/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshAccessLog) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshcircuitbreaker/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshcircuitbreaker/api/v1alpha1/zz_generated.helpers.go
index d5843a44a..4076c48cb 100644
--- a/pkg/plugins/policies/meshcircuitbreaker/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshcircuitbreaker/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshCircuitBreaker) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshfaultinjection/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshfaultinjection/api/v1alpha1/zz_generated.helpers.go
index e5dae974c..97c7b6526 100644
--- a/pkg/plugins/policies/meshfaultinjection/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshfaultinjection/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshFaultInjection) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshhealthcheck/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshhealthcheck/api/v1alpha1/zz_generated.helpers.go
index fd84dc320..2e2738455 100644
--- a/pkg/plugins/policies/meshhealthcheck/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshhealthcheck/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshHealthCheck) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *To) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshhttproute/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshhttproute/api/v1alpha1/zz_generated.helpers.go
index d37203655..a8603a7f9 100644
--- a/pkg/plugins/policies/meshhttproute/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshhttproute/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshHTTPRoute) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *To) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshloadbalancingstrategy/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshloadbalancingstrategy/api/v1alpha1/zz_generated.helpers.go
index ecebf8ba5..13ef47e91 100644
--- a/pkg/plugins/policies/meshloadbalancingstrategy/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshloadbalancingstrategy/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshLoadBalancingStrategy) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *To) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshmetric/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshmetric/api/v1alpha1/zz_generated.helpers.go
index 54e3c38df..d596b6ace 100644
--- a/pkg/plugins/policies/meshmetric/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshmetric/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshMetric) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *MeshMetric) GetDefault() interface{} {
diff --git a/pkg/plugins/policies/meshpassthrough/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshpassthrough/api/v1alpha1/zz_generated.helpers.go
index ad7d25ec6..a1067af49 100644
--- a/pkg/plugins/policies/meshpassthrough/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshpassthrough/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshPassthrough) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *MeshPassthrough) GetDefault() interface{} {
diff --git a/pkg/plugins/policies/meshproxypatch/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshproxypatch/api/v1alpha1/zz_generated.helpers.go
index 87b5aa568..6b9620e3b 100644
--- a/pkg/plugins/policies/meshproxypatch/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshproxypatch/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshProxyPatch) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *MeshProxyPatch) GetDefault() interface{} {
diff --git a/pkg/plugins/policies/meshratelimit/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshratelimit/api/v1alpha1/zz_generated.helpers.go
index d0d3139a7..6aaaf7926 100644
--- a/pkg/plugins/policies/meshratelimit/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshratelimit/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshRateLimit) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshretry/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshretry/api/v1alpha1/zz_generated.helpers.go
index 07a52ec23..e55b0a2dc 100644
--- a/pkg/plugins/policies/meshretry/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshretry/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshRetry) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *To) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshtcproute/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshtcproute/api/v1alpha1/zz_generated.helpers.go
index b4f129a21..10bb366c7 100644
--- a/pkg/plugins/policies/meshtcproute/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshtcproute/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshTCPRoute) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *To) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshtimeout/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshtimeout/api/v1alpha1/zz_generated.helpers.go
index a0751e0bb..0ab90c65b 100644
--- a/pkg/plugins/policies/meshtimeout/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshtimeout/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshTimeout) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshtls/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshtls/api/v1alpha1/zz_generated.helpers.go
index 043b1417b..daa51fdcc 100644
--- a/pkg/plugins/policies/meshtls/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshtls/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshTLS) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/pkg/plugins/policies/meshtrace/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshtrace/api/v1alpha1/zz_generated.helpers.go
index 4710929e9..92305eeab 100644
--- a/pkg/plugins/policies/meshtrace/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshtrace/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshTrace) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *MeshTrace) GetDefault() interface{} {
diff --git a/pkg/plugins/policies/meshtrafficpermission/api/v1alpha1/zz_generated.helpers.go b/pkg/plugins/policies/meshtrafficpermission/api/v1alpha1/zz_generated.helpers.go
index beee60470..fbcf6a98a 100644
--- a/pkg/plugins/policies/meshtrafficpermission/api/v1alpha1/zz_generated.helpers.go
+++ b/pkg/plugins/policies/meshtrafficpermission/api/v1alpha1/zz_generated.helpers.go
@@ -11,7 +11,7 @@ import (
 )
 
 func (x *MeshTrafficPermission) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 func (x *From) GetTargetRef() common_api.TargetRef {
diff --git a/tools/policy-gen/generator/cmd/helpers.go b/tools/policy-gen/generator/cmd/helpers.go
index a42a0ef49..56f04f1ab 100644
--- a/tools/policy-gen/generator/cmd/helpers.go
+++ b/tools/policy-gen/generator/cmd/helpers.go
@@ -62,7 +62,7 @@ import (
 )
 
 func (x *{{.name}}) GetTargetRef() common_api.TargetRef {
-	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh})
+	return pointer.DerefOr(x.TargetRef, common_api.TargetRef{Kind: common_api.Mesh, UsesSyntacticSugar: true})
 }
 
 {{ if .generateFrom }}


Test patch:
diff --git a/app/kumactl/cmd/install/testdata/install-observability.defaults.golden.yaml b/app/kumactl/cmd/install/testdata/install-observability.defaults.golden.yaml
index 55cb3c3b4..946fd18ac 100644
--- a/app/kumactl/cmd/install/testdata/install-observability.defaults.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-observability.defaults.golden.yaml
@@ -11909,6 +11909,7 @@ metadata:
   namespace: mesh-observability
   labels:
     app: grafana
+    kuma.io/mesh: default
 spec:
   type: ClusterIP
   ports:
@@ -11951,6 +11952,7 @@ metadata:
   labels:
     component: "server"
     app: prometheus
+    kuma.io/mesh: default
   name: prometheus-server
   namespace: mesh-observability
 spec:
diff --git a/app/kumactl/cmd/install/testdata/install-observability.no-grafana.golden.yaml b/app/kumactl/cmd/install/testdata/install-observability.no-grafana.golden.yaml
index 523bf4710..7c0b80563 100644
--- a/app/kumactl/cmd/install/testdata/install-observability.no-grafana.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-observability.no-grafana.golden.yaml
@@ -518,6 +518,7 @@ metadata:
   labels:
     component: "server"
     app: prometheus
+    kuma.io/mesh: default
   name: prometheus-server
   namespace: mesh-observability
 spec:
diff --git a/app/kumactl/cmd/install/testdata/install-observability.no-jaeger.golden.yaml b/app/kumactl/cmd/install/testdata/install-observability.no-jaeger.golden.yaml
index 5dbd352c2..8c15de111 100644
--- a/app/kumactl/cmd/install/testdata/install-observability.no-jaeger.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-observability.no-jaeger.golden.yaml
@@ -11909,6 +11909,7 @@ metadata:
   namespace: mesh-observability
   labels:
     app: grafana
+    kuma.io/mesh: default
 spec:
   type: ClusterIP
   ports:
@@ -11951,6 +11952,7 @@ metadata:
   labels:
     component: "server"
     app: prometheus
+    kuma.io/mesh: default
   name: prometheus-server
   namespace: mesh-observability
 spec:
diff --git a/app/kumactl/cmd/install/testdata/install-observability.no-loki.golden.yaml b/app/kumactl/cmd/install/testdata/install-observability.no-loki.golden.yaml
index ecc40b25c..7207f54c1 100644
--- a/app/kumactl/cmd/install/testdata/install-observability.no-loki.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-observability.no-loki.golden.yaml
@@ -11909,6 +11909,7 @@ metadata:
   namespace: mesh-observability
   labels:
     app: grafana
+    kuma.io/mesh: default
 spec:
   type: ClusterIP
   ports:
@@ -11951,6 +11952,7 @@ metadata:
   labels:
     component: "server"
     app: prometheus
+    kuma.io/mesh: default
   name: prometheus-server
   namespace: mesh-observability
 spec:
diff --git a/app/kumactl/cmd/install/testdata/install-observability.no-prometheus.golden.yaml b/app/kumactl/cmd/install/testdata/install-observability.no-prometheus.golden.yaml
index 893d47fd2..538a9b731 100644
--- a/app/kumactl/cmd/install/testdata/install-observability.no-prometheus.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-observability.no-prometheus.golden.yaml
@@ -11406,6 +11406,7 @@ metadata:
   namespace: mesh-observability
   labels:
     app: grafana
+    kuma.io/mesh: default
 spec:
   type: ClusterIP
   ports:
diff --git a/app/kumactl/cmd/install/testdata/install-observability.overrides.golden.yaml b/app/kumactl/cmd/install/testdata/install-observability.overrides.golden.yaml
index c62d3052e..791807676 100644
--- a/app/kumactl/cmd/install/testdata/install-observability.overrides.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-observability.overrides.golden.yaml
@@ -11909,6 +11909,7 @@ metadata:
   namespace: kuma
   labels:
     app: grafana
+    kuma.io/mesh: mesh-1
 spec:
   type: ClusterIP
   ports:
@@ -11951,6 +11952,7 @@ metadata:
   labels:
     component: "server"
     app: prometheus
+    kuma.io/mesh: mesh-1
   name: prometheus-server
   namespace: kuma
 spec:
diff --git a/pkg/kds/context/context_test.go b/pkg/kds/context/context_test.go
index afadb1fc2..287af3aed 100644
--- a/pkg/kds/context/context_test.go
+++ b/pkg/kds/context/context_test.go
@@ -438,7 +438,7 @@ var _ = Describe("Context", func() {
 				func(given testCase) {
 					ctx := stdcontext.Background()
 					// when
-					ok := predicate(ctx, clusterID, kds.Features{}, given.resource)
+					ok := predicate(ctx, clusterID, kds.Features{kds.FeatureOptionalTopLevelTargetRef: true}, given.resource)
 
 					// then
 					Expect(ok).To(BeTrue())
diff --git a/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.dataplane.yaml b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.dataplane.yaml
new file mode 100644
index 000000000..8e795c210
--- /dev/null
+++ b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.dataplane.yaml
@@ -0,0 +1,16 @@
+type: Dataplane
+mesh: mesh-1
+name: kong
+networking:
+  address: 10.244.0.13
+  admin:
+    port: 9901
+  gateway:
+    tags:
+      app.kubernetes.io/component: controller
+      k8s.kuma.io/namespace: kong
+      k8s.kuma.io/service-name: kong
+      k8s.kuma.io/service-port: "80"
+      kuma.io/service: kong_kong_svc_80
+      kuma.io/zone: default
+      pod-template-hash: 6b5f99ddff
diff --git a/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.golden.yaml b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.golden.yaml
new file mode 100644
index 000000000..865c98b55
--- /dev/null
+++ b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.golden.yaml
@@ -0,0 +1,45 @@
+ResourceRules:
+  meshexternalservice:mesh/mesh-1:name/mes:
+    BackendRefOriginIndex: {}
+    Conf:
+    - http:
+        backOff:
+          baseInterval: 15s
+          maxInterval: 20m0s
+        numRetries: 4
+        retryOn:
+        - 5xx
+    Origin:
+    - Resource:
+        creationTime: "0001-01-01T00:00:00Z"
+        mesh: mesh-1
+        modificationTime: "0001-01-01T00:00:00Z"
+        name: mr-2
+        type: MeshRetry
+      RuleIndex: 0
+    - Resource:
+        creationTime: "0001-01-01T00:00:00Z"
+        mesh: mesh-1
+        modificationTime: "0001-01-01T00:00:00Z"
+        name: mr-3
+        type: MeshRetry
+      RuleIndex: 0
+    Resource:
+      creationTime: "0001-01-01T00:00:00Z"
+      mesh: mesh-1
+      modificationTime: "0001-01-01T00:00:00Z"
+      name: mes
+      type: MeshExternalService
+    ResourceSectionName: ""
+Rules:
+- BackendRefOriginIndex: {}
+  Conf:
+    http:
+      numRetries: 3
+  Origin:
+  - creationTime: "0001-01-01T00:00:00Z"
+    mesh: mesh-1
+    modificationTime: "0001-01-01T00:00:00Z"
+    name: mr-1
+    type: MeshRetry
+  Subset: []
diff --git a/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.mes.yaml b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.mes.yaml
new file mode 100644
index 000000000..8dcf0190c
--- /dev/null
+++ b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.mes.yaml
@@ -0,0 +1,11 @@
+type: MeshExternalService
+mesh: mesh-1
+name: mes
+spec:
+  match:
+    type: HostnameGenerator
+    port: 80
+    protocol: http
+  endpoints:
+    - address: my-es.io
+      port: 80
diff --git a/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.policies.yaml b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.policies.yaml
new file mode 100644
index 000000000..ad06d7b60
--- /dev/null
+++ b/pkg/plugins/policies/core/matchers/testdata/matchedpolicies/meshexternalservice/delegated.policies.yaml
@@ -0,0 +1,48 @@
+type: MeshRetry
+mesh: mesh-1
+name: mr-1
+spec:
+  targetRef:
+    kind: Mesh
+  to:
+    - targetRef:
+        kind: Mesh
+      default:
+        http:
+          numRetries: 3
+---
+type: MeshRetry
+mesh: mesh-1
+name: mr-2
+spec:
+  targetRef:
+    kind: Mesh
+  to:
+    - targetRef:
+        kind: MeshExternalService
+        name: mes
+      default:
+        http:
+          numRetries: 4
+          retryOn:
+            - "5xx"
+---
+type: MeshRetry
+mesh: mesh-1
+name: mr-3
+spec:
+  targetRef:
+    kind: MeshSubset
+    tags:
+      kuma.io/service: kong_kong_svc_80
+  to:
+    - targetRef:
+        kind: MeshExternalService
+        name: mes
+      default:
+        http:
+          backOff:
+            baseInterval: 15s
+            maxInterval: 20m
+          retryOn:
+            - "5xx"
diff --git a/test/e2e_env/kubernetes/gateway/delegated.go b/test/e2e_env/kubernetes/gateway/delegated.go
index bdecd114c..a6a4d5519 100644
--- a/test/e2e_env/kubernetes/gateway/delegated.go
+++ b/test/e2e_env/kubernetes/gateway/delegated.go
@@ -6,6 +6,10 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+	mcb_api "github.com/kumahq/kuma/pkg/plugins/policies/meshcircuitbreaker/api/v1alpha1"
+	mr_api "github.com/kumahq/kuma/pkg/plugins/policies/meshretry/api/v1alpha1"
+	mt_api "github.com/kumahq/kuma/pkg/plugins/policies/meshtimeout/api/v1alpha1"
 	"github.com/kumahq/kuma/pkg/test/resources/samples"
 	"github.com/kumahq/kuma/test/e2e_env/kubernetes/gateway/delegated"
 	. "github.com/kumahq/kuma/test/framework"
@@ -26,10 +30,25 @@ func Delegated() {
 		CpNamespace:                 Config.KumaNamespace,
 		ObservabilityDeploymentName: "observability-delegated-meshtrace",
 		IPV6:                        Config.IPV6,
+		MeshServiceEnabled:          mesh_proto.Mesh_MeshServices_Disabled,
+		UseEgress:                   false,
 	}
 
-	externalNameService := func(serviceName string) string {
-		return fmt.Sprintf(`apiVersion: v1
+	configMs := delegated.Config{
+		Namespace:                   "delegated-gateway-ms",
+		NamespaceOutsideMesh:        "delegated-gateway-outside-mesh-ms",
+		Mesh:                        "delegated-gateway-ms",
+		KicIP:                       "",
+		CpNamespace:                 Config.KumaNamespace,
+		ObservabilityDeploymentName: "observability-delegated-meshtrace-ms",
+		IPV6:                        Config.IPV6,
+		MeshServiceEnabled:          mesh_proto.Mesh_MeshServices_Exclusive,
+		UseEgress:                   true,
+	}
+	contextFor := func(name string, config *delegated.Config, testMatrix map[string]func()) {
+		Context(name, func() {
+			externalNameService := func(serviceName string) string {
+				return fmt.Sprintf(`apiVersion: v1
 kind: Service
 metadata:
   name: %s
@@ -37,65 +56,68 @@ metadata:
 spec:
   type: ExternalName
   externalName: %s.%s.svc.cluster.local`, serviceName, config.Namespace, serviceName, config.NamespaceOutsideMesh)
-	}
-
-	BeforeAll(func() {
-		err := NewClusterSetup().
-			Install(YamlK8s(samples.MeshMTLSBuilder().WithName(config.Mesh).KubeYaml())).
-			Install(MeshTrafficPermissionAllowAllKubernetes(config.Mesh)).
-			Install(NamespaceWithSidecarInjection(config.Namespace)).
-			Install(Namespace(config.NamespaceOutsideMesh)).
-			Install(democlient.Install(
-				democlient.WithNamespace(config.NamespaceOutsideMesh),
-				democlient.WithService(true),
-			)).
-			Install(testserver.Install(
-				testserver.WithMesh(config.Mesh),
-				testserver.WithNamespace(config.Namespace),
-				testserver.WithName("test-server"),
-				testserver.WithStatefulSet(),
-				testserver.WithReplicas(3),
-			)).
-			Install(testserver.Install(
-				testserver.WithNamespace(config.NamespaceOutsideMesh),
-				testserver.WithName("external-service"),
-			)).
-			Install(testserver.Install(
-				testserver.WithNamespace(config.NamespaceOutsideMesh),
-				testserver.WithName("another-external-service"),
-			)).
-			Install(testserver.Install(
-				testserver.WithNamespace(config.NamespaceOutsideMesh),
-				testserver.WithName("external-tcp-service"),
-			)).
-			Install(otelcollector.Install(
-				otelcollector.WithNamespace(config.NamespaceOutsideMesh),
-				otelcollector.WithIPv6(Config.IPV6),
-			)).
-			Install(observability.Install(
-				config.ObservabilityDeploymentName,
-				observability.WithNamespace(config.NamespaceOutsideMesh),
-				observability.WithComponents(observability.JaegerComponent),
-			)).
-			Install(kic.KongIngressController(
-				kic.WithName("delegated"),
-				kic.WithNamespace(config.Namespace),
-				kic.WithMesh(config.Mesh),
-			)).
-			Install(kic.KongIngressService(
-				kic.WithName("delegated"),
-				kic.WithNamespace(config.Namespace),
-			)).
-			Install(YamlK8s(externalNameService("external-service"))).
-			Install(YamlK8s(externalNameService("another-external-service"))).
-			Install(YamlK8s(fmt.Sprintf(`
+			}
+			BeforeAll(func() {
+				mesh := samples.MeshMTLSBuilder().WithName(config.Mesh).WithMeshServicesEnabled(config.MeshServiceEnabled)
+				if config.UseEgress {
+					mesh.WithEgressRoutingEnabled()
+				}
+				err := NewClusterSetup().
+					Install(YamlK8s(mesh.KubeYaml())).
+					Install(MeshTrafficPermissionAllowAllKubernetes(config.Mesh)).
+					Install(NamespaceWithSidecarInjection(config.Namespace)).
+					Install(Namespace(config.NamespaceOutsideMesh)).
+					Install(democlient.Install(
+						democlient.WithNamespace(config.NamespaceOutsideMesh),
+						democlient.WithService(true),
+					)).
+					Install(testserver.Install(
+						testserver.WithMesh(config.Mesh),
+						testserver.WithNamespace(config.Namespace),
+						testserver.WithName("test-server"),
+						testserver.WithStatefulSet(),
+						testserver.WithReplicas(3),
+					)).
+					Install(testserver.Install(
+						testserver.WithNamespace(config.NamespaceOutsideMesh),
+						testserver.WithName("external-service"),
+					)).
+					Install(testserver.Install(
+						testserver.WithNamespace(config.NamespaceOutsideMesh),
+						testserver.WithName("another-external-service"),
+					)).
+					Install(testserver.Install(
+						testserver.WithNamespace(config.NamespaceOutsideMesh),
+						testserver.WithName("external-tcp-service"),
+					)).
+					Install(otelcollector.Install(
+						otelcollector.WithNamespace(config.NamespaceOutsideMesh),
+						otelcollector.WithIPv6(Config.IPV6),
+					)).
+					Install(observability.Install(
+						config.ObservabilityDeploymentName,
+						observability.WithNamespace(config.NamespaceOutsideMesh),
+						observability.WithComponents(observability.JaegerComponent),
+					)).
+					Install(kic.KongIngressController(
+						kic.WithName(config.Mesh),
+						kic.WithNamespace(config.Namespace),
+						kic.WithMesh(config.Mesh),
+					)).
+					Install(kic.KongIngressService(
+						kic.WithName(config.Mesh),
+						kic.WithNamespace(config.Namespace),
+					)).
+					Install(YamlK8s(externalNameService("external-service"))).
+					Install(YamlK8s(externalNameService("another-external-service"))).
+					Install(YamlK8s(fmt.Sprintf(`
 apiVersion: networking.k8s.io/v1
 kind: Ingress
 metadata:
   namespace: %s
   name: %s-ingress
   annotations:
-    kubernetes.io/ingress.class: delegated
+    kubernetes.io/ingress.class: %s
     konghq.com/strip-path: 'true'
 spec:
   rules:
@@ -125,41 +147,57 @@ spec:
           service:
             name: another-external-service
             port:
-              number: 80
-`, config.Namespace, config.Mesh))).
-			Setup(kubernetes.Cluster)
-		Expect(err).ToNot(HaveOccurred())
+              number: 80`, config.Namespace, config.Mesh, config.Mesh))).
+					Setup(kubernetes.Cluster)
+				Expect(err).ToNot(HaveOccurred())
 
-		kicIP, err := kic.From(kubernetes.Cluster).IP(config.Namespace)
-		Expect(err).ToNot(HaveOccurred())
+				kicIP, err := kic.From(kubernetes.Cluster).IP(config.Namespace)
+				Expect(err).ToNot(HaveOccurred())
 
-		config.KicIP = kicIP
-	})
+				config.KicIP = kicIP
+				Expect(DeleteMeshResources(
+					kubernetes.Cluster,
+					config.Mesh,
+					mcb_api.MeshCircuitBreakerResourceTypeDescriptor,
+					mt_api.MeshTimeoutResourceTypeDescriptor,
+					mr_api.MeshRetryResourceTypeDescriptor,
+				)).To(Succeed())
+			})
 
-	E2EAfterAll(func() {
-		Expect(kubernetes.Cluster.TriggerDeleteNamespace(config.Namespace)).
-			To(Succeed())
-		Expect(kubernetes.Cluster.TriggerDeleteNamespace(config.NamespaceOutsideMesh)).
-			To(Succeed())
-		Expect(kubernetes.Cluster.DeleteMesh(config.Mesh)).To(Succeed())
-		Expect(kubernetes.Cluster.DeleteDeployment(config.ObservabilityDeploymentName)).
-			To(Succeed())
-	})
+			E2EAfterAll(func() {
+				Expect(kubernetes.Cluster.TriggerDeleteNamespace(config.Namespace)).
+					To(Succeed())
+				Expect(kubernetes.Cluster.TriggerDeleteNamespace(config.NamespaceOutsideMesh)).
+					To(Succeed())
+				Expect(kubernetes.Cluster.DeleteMesh(config.Mesh)).To(Succeed())
+				Expect(kubernetes.Cluster.DeleteDeployment(config.ObservabilityDeploymentName)).
+					To(Succeed())
+			})
+
+			// If you copy the test case from a non-gateway test or create a new test,
+			// remember the the name of policies needs to be unique.
+			// If they have the same name, one might override the other, causing a flake.
+			for policyName, test := range testMatrix {
+				Context(policyName, test)
+			}
+		})
+	}
 
-	// If you copy the test case from a non-gateway test or create a new test,
-	// remember the the name of policies needs to be unique.
-	// If they have the same name, one might override the other, causing a flake.
-	Context("MeshCircuitBreaker", delegated.CircuitBreaker(&config))
-	Context("MeshProxyPatch", delegated.MeshProxyPatch(&config))
-	Context("MeshHealthCheck", delegated.MeshHealthCheck(&config))
-	Context("MeshRetry", delegated.MeshRetry(&config))
-	Context("MeshHTTPRoute", delegated.MeshHTTPRoute(&config))
-	Context("MeshTimeout", delegated.MeshTimeout(&config))
-	Context("MeshMetric", delegated.MeshMetric(&config))
-	Context("MeshTrace", delegated.MeshTrace(&config))
-	Context("MeshLoadBalancingStrategy", delegated.MeshLoadBalancingStrategy(&config))
-	Context("MeshAccessLog", delegated.MeshAccessLog(&config))
-	XContext("MeshTCPRoute", delegated.MeshTCPRoute(&config))
-	Context("MeshPassthrough", delegated.MeshPassthrough(&config))
-	Context("MeshTLS", delegated.MeshTLS(&config))
+	contextFor("delegated with kuma.io/service", &config, map[string]func(){
+		"MeshCircuitBreaker":        delegated.CircuitBreaker(&config),
+		"MeshProxyPatch":            delegated.MeshProxyPatch(&config),
+		"MeshHealthCheck":           delegated.MeshHealthCheck(&config),
+		"MeshRetry":                 delegated.MeshRetry(&config),
+		"MeshHTTPRoute":             delegated.MeshHTTPRoute(&config),
+		"MeshTimeout":               delegated.MeshTimeout(&config),
+		"MeshMetric":                delegated.MeshMetric(&config),
+		"MeshTrace":                 delegated.MeshTrace(&config),
+		"MeshLoadBalancingStrategy": delegated.MeshLoadBalancingStrategy(&config),
+		"MeshAccessLog":             delegated.MeshAccessLog(&config),
+		"MeshPassthrough":           delegated.MeshPassthrough(&config),
+		"MeshTLS":                   delegated.MeshTLS(&config),
+	})
+	contextFor("delegated with MeshService", &configMs, map[string]func(){
+		"MeshHTTPRoute": delegated.MeshHTTPRouteMeshService(&configMs),
+	})
 }
diff --git a/test/e2e_env/kubernetes/gateway/delegated/common.go b/test/e2e_env/kubernetes/gateway/delegated/common.go
index ae244189a..d58a5caec 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/common.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/common.go
@@ -1,5 +1,7 @@
 package delegated
 
+import mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+
 type Config struct {
 	Namespace                   string
 	NamespaceOutsideMesh        string
@@ -8,4 +10,6 @@ type Config struct {
 	CpNamespace                 string
 	ObservabilityDeploymentName string
 	IPV6                        bool
+	MeshServiceEnabled          mesh_proto.Mesh_MeshServices_Enabled
+	UseEgress                   bool
 }
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshaccesslog.go b/test/e2e_env/kubernetes/gateway/delegated/meshaccesslog.go
index d3abaa852..63a65551f 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshaccesslog.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshaccesslog.go
@@ -118,7 +118,7 @@ spec:
 				g.Expect(parseLogs(logs)).To(ContainElement(
 					And(
 						HaveField("Start", WithTransform(parseTimestamp, BeTemporally("~", time.Now(), time.Hour))),
-						HaveField("Source", fmt.Sprintf("delegated-gateway-admin_%s_svc_8444", config.Namespace)),
+						HaveField("Source", fmt.Sprintf("%s-gateway-admin_%s_svc_8444", config.Mesh, config.Namespace)),
 						HaveField("Destination", fmt.Sprintf("test-server_%s_svc_80", config.Namespace)),
 					),
 				))
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshcircuitbreaker.go b/test/e2e_env/kubernetes/gateway/delegated/meshcircuitbreaker.go
index 67d564d1b..24cff184c 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshcircuitbreaker.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshcircuitbreaker.go
@@ -7,7 +7,6 @@ import (
 	. "github.com/onsi/gomega"
 
 	"github.com/kumahq/kuma/pkg/plugins/policies/meshcircuitbreaker/api/v1alpha1"
-	meshretry_api "github.com/kumahq/kuma/pkg/plugins/policies/meshretry/api/v1alpha1"
 	"github.com/kumahq/kuma/test/framework"
 	"github.com/kumahq/kuma/test/framework/client"
 	"github.com/kumahq/kuma/test/framework/envs/kubernetes"
@@ -17,19 +16,6 @@ func CircuitBreaker(config *Config) func() {
 	GinkgoHelper()
 
 	return func() {
-		BeforeAll(func() {
-			Expect(framework.DeleteMeshPolicyOrError(
-				kubernetes.Cluster,
-				v1alpha1.MeshCircuitBreakerResourceTypeDescriptor,
-				fmt.Sprintf("mesh-circuit-breaker-all-%s", config.Mesh),
-			)).To(Succeed())
-			Expect(framework.DeleteMeshPolicyOrError(
-				kubernetes.Cluster,
-				meshretry_api.MeshRetryResourceTypeDescriptor,
-				fmt.Sprintf("mesh-retry-all-%s", config.Mesh),
-			)).To(Succeed())
-		})
-
 		framework.AfterEachFailure(func() {
 			framework.DebugKube(kubernetes.Cluster, config.Mesh, config.Namespace, config.ObservabilityDeploymentName)
 		})
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshhealthcheck.go b/test/e2e_env/kubernetes/gateway/delegated/meshhealthcheck.go
index 44d4c286f..00353057f 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshhealthcheck.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshhealthcheck.go
@@ -46,17 +46,6 @@ spec:
           - %s`, config.CpNamespace, config.Mesh, path, status)
 		}
 
-		BeforeAll(func() {
-			Expect(framework.DeleteMeshResources(
-				kubernetes.Cluster,
-				config.Mesh,
-				v1alpha1.MeshHealthCheckResourceTypeDescriptor,
-			)).To(Succeed())
-
-			Expect(framework.YamlK8s(healthCheck("/probes?type=liveness", "200"))(kubernetes.Cluster)).
-				To(Succeed())
-		})
-
 		framework.AfterEachFailure(func() {
 			framework.DebugKube(kubernetes.Cluster, config.Mesh, config.Namespace, config.ObservabilityDeploymentName)
 		})
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshhttproute.go b/test/e2e_env/kubernetes/gateway/delegated/meshhttproute.go
index f44d92bf2..bf7c8abd1 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshhttproute.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshhttproute.go
@@ -7,6 +7,7 @@ import (
 	. "github.com/onsi/gomega"
 
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
+	meshexternalservice_api "github.com/kumahq/kuma/pkg/core/resources/apis/meshexternalservice/api/v1alpha1"
 	"github.com/kumahq/kuma/pkg/plugins/policies/meshhttproute/api/v1alpha1"
 	"github.com/kumahq/kuma/test/framework"
 	"github.com/kumahq/kuma/test/framework/client"
@@ -63,7 +64,7 @@ metadata:
 spec:
   targetRef:
     kind: MeshService
-    name: delegated-gateway-admin_%[2]s_svc_8444
+    name: %[2]s-gateway-admin_%[2]s_svc_8444
   to:
     - targetRef:
         kind: MeshService
@@ -109,3 +110,103 @@ spec:
 		})
 	}
 }
+
+func MeshHTTPRouteMeshService(config *Config) func() {
+	GinkgoHelper()
+
+	return func() {
+		framework.AfterEachFailure(func() {
+			framework.DebugKube(kubernetes.Cluster, config.Mesh, config.Namespace, config.ObservabilityDeploymentName)
+		})
+
+		framework.E2EAfterEach(func() {
+			Expect(framework.DeleteMeshResources(
+				kubernetes.Cluster,
+				config.Mesh,
+				v1alpha1.MeshHTTPRouteResourceTypeDescriptor,
+			)).To(Succeed())
+
+			Expect(framework.DeleteMeshResources(
+				kubernetes.Cluster,
+				config.Mesh,
+				meshexternalservice_api.MeshExternalServiceResourceTypeDescriptor,
+			)).To(Succeed())
+		})
+
+		It("should split traffic between MeshService and MeshExternalServices", func() {
+			// given
+			Expect(framework.YamlK8s(fmt.Sprintf(`
+apiVersion: kuma.io/v1alpha1
+kind: MeshExternalService
+metadata:
+  name: plain-external-service-delegated-ms
+  namespace: %s
+  labels:
+    kuma.io/mesh: %s
+spec:
+  match:
+    type: HostnameGenerator
+    port: 80
+    protocol: http
+  endpoints:
+    - address: external-service.%s.svc.cluster.local
+      port: 80
+`, config.CpNamespace, config.Mesh, config.NamespaceOutsideMesh))(kubernetes.Cluster)).To(Succeed())
+			// when
+			Expect(framework.YamlK8s(fmt.Sprintf(`
+apiVersion: kuma.io/v1alpha1
+kind: MeshHTTPRoute
+metadata:
+  name: mhr-delegated-ms
+  namespace: %s
+  labels:
+    kuma.io/mesh: %[2]s
+spec:
+  targetRef:
+    kind: MeshSubset
+    tags:
+      kuma.io/service: %[2]s-gateway-admin_%[2]s_svc_8444
+  to:
+    - targetRef:
+        kind: MeshService
+        name: test-server
+        namespace: %[3]s
+      rules:
+        - matches:
+            - path:
+                type: PathPrefix
+                value: /
+          default:
+            backendRefs:
+              - kind: MeshService
+                name: test-server
+                namespace: %[3]s
+                port: 80
+                weight: 50
+              - kind: MeshExternalService
+                name: plain-external-service-delegated-ms
+                port: 80
+                weight: 50
+`, config.CpNamespace, config.Mesh, config.Namespace))(kubernetes.Cluster)).To(Succeed())
+
+			// and then receive responses from 'external-service'
+			Eventually(func(g Gomega) {
+				response, err := client.CollectResponsesByInstance(
+					kubernetes.Cluster,
+					"demo-client",
+					fmt.Sprintf("http://%s/test-server", config.KicIP),
+					client.FromKubernetesPod(config.NamespaceOutsideMesh, "demo-client"),
+					client.WithNumberOfRequests(50),
+				)
+				g.Expect(err).ToNot(HaveOccurred())
+				g.Expect(response).To(HaveLen(4))
+				g.Expect(response).To(And(
+					HaveKeyWithValue(Equal(`test-server-0`), BeNumerically("~", 8, 3)),
+					HaveKeyWithValue(Equal(`test-server-1`), BeNumerically("~", 8, 3)),
+					HaveKeyWithValue(Equal(`test-server-2`), BeNumerically("~", 8, 3)),
+					HaveKeyWithValue(ContainSubstring(`external-service`), BeNumerically("~", 25, 6)),
+				))
+			}, "30s", "5s").Should(Succeed())
+		})
+	}
+}
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshloadbalancingstrategy.go b/test/e2e_env/kubernetes/gateway/delegated/meshloadbalancingstrategy.go
index 46de5288a..3d2d36acc 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshloadbalancingstrategy.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshloadbalancingstrategy.go
@@ -51,7 +51,7 @@ metadata:
 spec:
   targetRef:
     kind: MeshService
-    name: delegated-gateway-admin_%[2]s_svc_8444
+    name: %[2]s-gateway-admin_%[2]s_svc_8444
   to:
     - targetRef:
         kind: MeshService
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshmetric.go b/test/e2e_env/kubernetes/gateway/delegated/meshmetric.go
index 1b17b92fa..666dfcb5f 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshmetric.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshmetric.go
@@ -75,7 +75,7 @@ spec:
 						return strings.Split(stdout, "\n")
 					},
 					ContainElement(MatchRegexp(
-						`envoy_cluster_external_upstream_rq_time_bucket\{.*service="delegated-gateway-admin_%s_svc_8444"`,
+						`envoy_cluster_external_upstream_rq_time_bucket\{.*service="%[1]s-gateway-admin_%[1]s_svc_8444"`,
 						config.Mesh,
 					)),
 				))
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshpassthrough.go b/test/e2e_env/kubernetes/gateway/delegated/meshpassthrough.go
index eaea0ada6..d817861e1 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshpassthrough.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshpassthrough.go
@@ -17,7 +17,7 @@ func MeshPassthrough(config *Config) func() {
 	GinkgoHelper()
 
 	return func() {
-		AfterEach(func() {
+		framework.E2EAfterEach(func() {
 			Expect(framework.DeleteMeshResources(kubernetes.Cluster, config.Mesh, meshpassthrough_api.MeshPassthroughResourceTypeDescriptor)).To(Succeed())
 			Expect(framework.DeleteMeshResources(kubernetes.Cluster, config.Mesh, meshproxypatch_api.MeshProxyPatchResourceTypeDescriptor)).To(Succeed())
 		})
@@ -36,13 +36,13 @@ metadata:
   name: delegated-idle-connection
   namespace: %s
   labels:
-    kuma.io/mesh: %s
+    kuma.io/mesh: %[2]s
 spec:
   targetRef:
     kind: MeshSubset
     proxyTypes: ["Gateway"]
     tags:
-      kuma.io/service: delegated-gateway-admin_delegated-gateway_svc_8444
+      kuma.io/service: %[2]s-gateway-admin_delegated-gateway_svc_8444
   default:
     appendModifications:
       - networkFilter:
@@ -66,13 +66,13 @@ metadata:
   name: disable-passthrough-delegated
   namespace: %s
   labels:
-    kuma.io/mesh: %s
+    kuma.io/mesh: %[2]s
 spec:
   targetRef:
     kind: MeshSubset
     proxyTypes: ["Gateway"]
     tags:
-      kuma.io/service: delegated-gateway-admin_delegated-gateway_svc_8444
+      kuma.io/service: %[2]s-gateway-admin_delegated-gateway_svc_8444
   default:
     passthroughMode: None
 `, config.CpNamespace, config.Mesh)
@@ -99,13 +99,13 @@ metadata:
   name: allow-specified-delegated
   namespace: %s
   labels:
-    kuma.io/mesh: %s
+    kuma.io/mesh: %[2]s
 spec:
   targetRef:
     kind: MeshSubset
     proxyTypes: ["Gateway"]
     tags:
-      kuma.io/service: delegated-gateway-admin_delegated-gateway_svc_8444
+      kuma.io/service: %[2]s-gateway-admin_delegated-gateway_svc_8444
   default:
     passthroughMode: Matched
     appendMatch:
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshproxypatch.go b/test/e2e_env/kubernetes/gateway/delegated/meshproxypatch.go
index 9505e6c35..40e98004c 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshproxypatch.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshproxypatch.go
@@ -6,6 +6,7 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
+	"github.com/kumahq/kuma/pkg/plugins/policies/meshproxypatch/api/v1alpha1"
 	"github.com/kumahq/kuma/test/framework"
 	"github.com/kumahq/kuma/test/framework/client"
 	"github.com/kumahq/kuma/test/framework/envs/kubernetes"
@@ -16,6 +17,14 @@ func MeshProxyPatch(config *Config) func() {
 	GinkgoHelper()
 
 	return func() {
+		framework.E2EAfterEach(func() {
+			Expect(framework.DeleteMeshResources(
+				kubernetes.Cluster,
+				config.Mesh,
+				v1alpha1.MeshProxyPatchResourceTypeDescriptor,
+			)).To(Succeed())
+		})
+
 		It("should add a header using Lua filter", func() {
 			// given
 			meshProxyPatch := fmt.Sprintf(`
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshtcproute.go b/test/e2e_env/kubernetes/gateway/delegated/meshtcproute.go
index 8cd4d88b3..902387024 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshtcproute.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshtcproute.go
@@ -78,7 +78,7 @@ metadata:
 spec:
   targetRef:
     kind: MeshService
-    name: delegated-gateway-admin_%[2]s_svc_8444
+    name: %[2]s-gateway-admin_%[2]s_svc_8444
   to:
   - targetRef:
       kind: MeshService
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshtimeout.go b/test/e2e_env/kubernetes/gateway/delegated/meshtimeout.go
index 418c0527e..a2174bdf9 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshtimeout.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshtimeout.go
@@ -7,7 +7,6 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
-	meshretry_api "github.com/kumahq/kuma/pkg/plugins/policies/meshretry/api/v1alpha1"
 	"github.com/kumahq/kuma/pkg/plugins/policies/meshtimeout/api/v1alpha1"
 	"github.com/kumahq/kuma/test/framework"
 	"github.com/kumahq/kuma/test/framework/client"
@@ -18,20 +17,6 @@ func MeshTimeout(config *Config) func() {
 	GinkgoHelper()
 
 	return func() {
-		BeforeAll(func() {
-			Expect(framework.DeleteMeshResources(
-				kubernetes.Cluster,
-				config.Mesh,
-				v1alpha1.MeshTimeoutResourceTypeDescriptor,
-			)).To(Succeed())
-
-			Expect(framework.DeleteMeshResources(
-				kubernetes.Cluster,
-				config.Mesh,
-				meshretry_api.MeshRetryResourceTypeDescriptor,
-			)).To(Succeed())
-		})
-
 		framework.AfterEachFailure(func() {
 			framework.DebugKube(kubernetes.Cluster, config.Mesh, config.Namespace, config.ObservabilityDeploymentName)
 		})
diff --git a/test/e2e_env/kubernetes/gateway/delegated/meshtrace.go b/test/e2e_env/kubernetes/gateway/delegated/meshtrace.go
index 7660fb42d..090e6c3d5 100644
--- a/test/e2e_env/kubernetes/gateway/delegated/meshtrace.go
+++ b/test/e2e_env/kubernetes/gateway/delegated/meshtrace.go
@@ -42,10 +42,6 @@ spec:
 `, config.CpNamespace, config.Mesh, zipkinUrl))
 		}
 
-		BeforeAll(func() {
-			observabilityClient = observability.From(config.ObservabilityDeploymentName, kubernetes.Cluster)
-		})
-
 		framework.AfterEachFailure(func() {
 			framework.DebugKube(kubernetes.Cluster, config.Mesh, config.Namespace, config.ObservabilityDeploymentName)
 		})
@@ -59,6 +55,8 @@ spec:
 		})
 
 		It("should emit traces to jaeger", func() {
+			observabilityClient = observability.From(config.ObservabilityDeploymentName, kubernetes.Cluster)
+
 			// given MeshTrace and with tracing backend
 			Expect(kubernetes.Cluster.Install(meshTrace(observabilityClient.ZipkinCollectorURL()))).
 				To(Succeed())
@@ -74,7 +72,7 @@ spec:
 				srvs, err := observabilityClient.TracedServices()
 				g.Expect(err).ToNot(HaveOccurred())
 				g.Expect(srvs).To(Equal([]string{
-					fmt.Sprintf("delegated-gateway-admin_%s_svc_8444", config.Mesh),
+					fmt.Sprintf("%[1]s-gateway-admin_%[1]s_svc_8444", config.Mesh),
 					"jaeger-query",
 					fmt.Sprintf("test-server_%s_svc_80", config.Mesh),
 				}))