Repository: kumahq/kuma
Pull request #1873 (2021-04-26 08:45:16)

test(e2e) make the real e2e tests modular
### Summary

Move the e2e test implementation out of the _test files. This allows for better re-usage.

### Documentation

- [ ] Not needed.


jakubdyszkiewicz: Nice!

Since it's not Go file and not in `_test`. Can we reuse
```
func ShouldSkipCleanup() bool {
	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
}
```
so we don't have `_suite.go` files?
nickolaev: > Nice!
> 
> Since it's not Go file and not in `_test`. Can we reuse
> 
> ```
> func ShouldSkipCleanup() bool {
> 	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
> }
> ```
> 
> so we don't have `_suite.go` files?

Makes total sense. Let me move this to the framework

Patch:
diff --git a/pkg/dns/outbound.go b/pkg/dns/outbound.go
index b565e61f0..e2462aef7 100644
--- a/pkg/dns/outbound.go
+++ b/pkg/dns/outbound.go
@@ -29,10 +29,6 @@ func VIPOutbounds(
 	serviceVIPMap := map[string]vipEntry{}
 	services := []string{}
 	for _, dataplane := range dataplanes {
-		if resourceKey == model.MetaToResourceKey(dataplane.Meta) {
-			continue
-		}
-
 		if dataplane.Spec.IsIngress() {
 			for _, service := range dataplane.Spec.Networking.Ingress.AvailableServices {
 				if service.Mesh == resourceKey.Mesh {


Test patch:
diff --git a/pkg/dns/outbound_test.go b/pkg/dns/outbound_test.go
index 7e08604e3..26e008f06 100644
--- a/pkg/dns/outbound_test.go
+++ b/pkg/dns/outbound_test.go
@@ -74,11 +74,11 @@ var _ = Describe("VIPOutbounds", func() {
 		// when
 		outbounds := dns.VIPOutbounds(model.MetaToResourceKey(dataplane.Meta), dataplanes.Items, vipList, externalServices.Items)
 		// and
-		Expect(outbounds).To(HaveLen(4))
+		Expect(outbounds).To(HaveLen(5))
 		// and
-		Expect(outbounds[3].GetTags()[mesh_proto.ServiceTag]).To(Equal("service-5"))
+		Expect(outbounds[4].GetTags()[mesh_proto.ServiceTag]).To(Equal("service-5"))
 		// and
-		Expect(outbounds[3].Port).To(Equal(dns.VIPListenPort))
+		Expect(outbounds[4].Port).To(Equal(dns.VIPListenPort))
 	})
 
 	It("shouldn't add outbounds from other meshes", func() {
@@ -254,6 +254,10 @@ var _ = Describe("VIPOutbounds", func() {
         port: 80
         tags:
           kuma.io/service: second-external-service
+      - address: 240.0.0.1
+        port: 80
+        tags:
+          kuma.io/service: service-1
       - address: 240.0.0.2
         port: 80
         tags:
diff --git a/pkg/plugins/runtime/universal/outbound/outbound_test.go b/pkg/plugins/runtime/universal/outbound/outbound_test.go
index cd8a37ec1..ff45011fe 100644
--- a/pkg/plugins/runtime/universal/outbound/outbound_test.go
+++ b/pkg/plugins/runtime/universal/outbound/outbound_test.go
@@ -100,9 +100,9 @@ var _ = Describe("UpdateOutbound", func() {
 			dp1 := mesh.NewDataplaneResource()
 			err = rm.Get(context.Background(), dp1, store.GetByKey("dp-1", "default"))
 			Expect(err).ToNot(HaveOccurred())
-			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(1))
-			Expect(dp1.Spec.Networking.Outbound[0].Tags["kuma.io/service"]).To(Equal("service-2"))
-			Expect(dp1.Spec.Networking.Outbound[0].Address).To(Equal("240.0.0.2"))
+			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(2))
+			Expect(dp1.Spec.Networking.Outbound[1].Tags["kuma.io/service"]).To(Equal("service-2"))
+			Expect(dp1.Spec.Networking.Outbound[1].Address).To(Equal("240.0.0.2"))
 		})
 
 		It("should not update dataplane outbounds when new service is added to another mesh", func() {
@@ -132,7 +132,7 @@ var _ = Describe("UpdateOutbound", func() {
 			dp1 := mesh.NewDataplaneResource()
 			err = rm.Get(context.Background(), dp1, store.GetByKey("dp-1", "default"))
 			Expect(err).ToNot(HaveOccurred())
-			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(0))
+			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(1))
 		})
 
 		It("should update dataplane outbounds when new service in the same mesh is added to an ingress", func() {
@@ -169,9 +169,9 @@ var _ = Describe("UpdateOutbound", func() {
 			dp1 := mesh.NewDataplaneResource()
 			err = rm.Get(context.Background(), dp1, store.GetByKey("dp-1", "default"))
 			Expect(err).ToNot(HaveOccurred())
-			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(1))
-			Expect(dp1.Spec.Networking.Outbound[0].Tags["kuma.io/service"]).To(Equal("service-2"))
-			Expect(dp1.Spec.Networking.Outbound[0].Address).To(Equal("240.0.0.2"))
+			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(2))
+			Expect(dp1.Spec.Networking.Outbound[1].Tags["kuma.io/service"]).To(Equal("service-2"))
+			Expect(dp1.Spec.Networking.Outbound[1].Address).To(Equal("240.0.0.2"))
 		})
 
 		It("shouldn't update dataplane outbounds when new service in a different mesh is added to an ingress", func() {
@@ -208,7 +208,7 @@ var _ = Describe("UpdateOutbound", func() {
 			dp1 := mesh.NewDataplaneResource()
 			err = rm.Get(context.Background(), dp1, store.GetByKey("dp-1", "default"))
 			Expect(err).ToNot(HaveOccurred())
-			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(0))
+			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(1))
 		})
 
 		It("should update dataplane outbounds when new service is added to an ingress in a different mesh", func() {
@@ -248,9 +248,9 @@ var _ = Describe("UpdateOutbound", func() {
 			dp1 := mesh.NewDataplaneResource()
 			err = rm.Get(context.Background(), dp1, store.GetByKey("dp-1", "default"))
 			Expect(err).ToNot(HaveOccurred())
-			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(1))
-			Expect(dp1.Spec.Networking.Outbound[0].Tags["kuma.io/service"]).To(Equal("service-2"))
-			Expect(dp1.Spec.Networking.Outbound[0].Address).To(Equal("240.0.0.2"))
+			Expect(dp1.Spec.Networking.Outbound).To(HaveLen(2))
+			Expect(dp1.Spec.Networking.Outbound[1].Tags["kuma.io/service"]).To(Equal("service-2"))
+			Expect(dp1.Spec.Networking.Outbound[1].Address).To(Equal("240.0.0.2"))
 		})
 
 		Context("outbounds already updated", func() {
@@ -297,7 +297,7 @@ var _ = Describe("UpdateOutbound", func() {
 				dp1 := mesh.NewDataplaneResource()
 				err = rm.Get(context.Background(), dp1, store.GetByKey("dp-1", "default"))
 				Expect(err).ToNot(HaveOccurred())
-				Expect(dp1.Spec.Networking.Outbound).To(HaveLen(0))
+				Expect(dp1.Spec.Networking.Outbound).To(HaveLen(1))
 			})
 		})
 	})
diff --git a/test/e2e/compatibility/compatibility_suite_test.go b/test/e2e/compatibility/compatibility_suite_test.go
index 6937db660..aee80a084 100644
--- a/test/e2e/compatibility/compatibility_suite_test.go
+++ b/test/e2e/compatibility/compatibility_suite_test.go
@@ -3,8 +3,6 @@ package compatibility_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/compatibility/dp_compatibility_universal.go b/test/e2e/compatibility/dp_compatibility_universal.go
new file mode 100644
index 000000000..207b38754
--- /dev/null
+++ b/test/e2e/compatibility/dp_compatibility_universal.go
@@ -0,0 +1,52 @@
+package compatibility
+
+import (
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func UniversalCompatibility() {
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err := NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken, WithDPVersion("1.0.8"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithDPVersion("1.0.8"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("client should access server", func() {
+		retry.DoWithRetry(cluster.GetTesting(), "check communication between services", DefaultRetries, DefaultTimeout, func() (string, error) {
+			_, _, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "3", "--fail", "localhost:4001")
+			return "", err
+		})
+	})
+}
diff --git a/test/e2e/compatibility/dp_compatibility_universal_test.go b/test/e2e/compatibility/dp_compatibility_universal_test.go
index dd1bbab4a..6f3651f81 100644
--- a/test/e2e/compatibility/dp_compatibility_universal_test.go
+++ b/test/e2e/compatibility/dp_compatibility_universal_test.go
@@ -1,53 +1,9 @@
 package compatibility_test
 
 import (
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/compatibility"
 )
 
-var _ = Describe("Test Universal Compatibility", func() {
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err := NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken, WithDPVersion("1.0.8"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithDPVersion("1.0.8"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("client should access server", func() {
-		retry.DoWithRetry(cluster.GetTesting(), "check communication between services", DefaultRetries, DefaultTimeout, func() (string, error) {
-			_, _, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "3", "--fail", "localhost:4001")
-			return "", err
-		})
-	})
-})
+var _ = Describe("Test Universal Compatibility", compatibility.UniversalCompatibility)
diff --git a/test/e2e/compatibility/foo.go b/test/e2e/compatibility/foo.go
deleted file mode 100644
index f76975900..000000000
--- a/test/e2e/compatibility/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package compatibility
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/deploy/deploy_suite_test.go b/test/e2e/deploy/deploy_suite_test.go
index cc4dc3f86..0c9029033 100644
--- a/test/e2e/deploy/deploy_suite_test.go
+++ b/test/e2e/deploy/deploy_suite_test.go
@@ -3,8 +3,6 @@ package deploy_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/deploy/foo.go b/test/e2e/deploy/foo.go
deleted file mode 100644
index 0d2168ee2..000000000
--- a/test/e2e/deploy/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package deploy
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/deploy/kuma_deploy_global_remote.go b/test/e2e/deploy/kuma_deploy_global_remote.go
new file mode 100644
index 000000000..c6337069f
--- /dev/null
+++ b/test/e2e/deploy/kuma_deploy_global_remote.go
@@ -0,0 +1,156 @@
+package deploy
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+
+	api_server "github.com/kumahq/kuma/pkg/api-server"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	"github.com/go-errors/errors"
+
+	http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func RemoteAndGLobal() {
+	namespaceWithSidecarInjection := func(namespace string) string {
+		return fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace)
+	}
+
+	var clusters Clusters
+	var c1, c2 Cluster
+	var global, remote ControlPlane
+	var optsGlobal, optsRemote []DeployOptionsFunc
+	var originalKumaNamespace = KumaNamespace
+
+	BeforeEach(func() {
+		// set the new namespace
+		KumaNamespace = "other-kuma-system"
+		var err error
+		clusters, err = NewK8sClusters(
+			[]string{Kuma1, Kuma2},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		c1 = clusters.GetCluster(Kuma1)
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Global, optsGlobal...)).
+			Setup(c1)
+		Expect(err).ToNot(HaveOccurred())
+
+		global = c1.GetKuma()
+		Expect(global).ToNot(BeNil())
+
+		c2 = clusters.GetCluster(Kuma2)
+		optsRemote = []DeployOptionsFunc{
+			WithIngress(),
+			WithGlobalAddress(global.GetKDSServerAddress()),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote...)).
+			Install(KumaDNS()).
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(DemoClientK8s("default")).
+			Install(EchoServerK8s("default")).
+			Setup(c2)
+		Expect(err).ToNot(HaveOccurred())
+
+		remote = c2.GetKuma()
+		Expect(remote).ToNot(BeNil())
+
+		// when
+		err = c1.VerifyKuma()
+		// then
+		Expect(err).ToNot(HaveOccurred())
+
+		// when
+		err = c2.VerifyKuma()
+		// then
+		Expect(err).ToNot(HaveOccurred())
+
+		// then
+		logs1, err := global.GetKumaCPLogs()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(logs1).To(ContainSubstring("\"mode\":\"global\""))
+
+		// and
+		logs2, err := remote.GetKumaCPLogs()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(logs2).To(ContainSubstring("\"mode\":\"remote\""))
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+
+		defer func() {
+			// restore the original namespace
+			KumaNamespace = originalKumaNamespace
+		}()
+
+		err := c2.DeleteNamespace(TestNamespace)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = c1.DeleteKuma(optsGlobal...)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = c2.DeleteKuma(optsRemote...)
+		Expect(err).ToNot(HaveOccurred())
+
+		Expect(clusters.DismissCluster()).To(Succeed())
+	})
+
+	It("should deploy Remote and Global on 2 clusters", func() {
+		// when check if remote is online
+		clustersStatus := api_server.Zones{}
+		Eventually(func() (bool, error) {
+			status, response := http_helper.HttpGet(c1.GetTesting(), global.GetGlobaStatusAPI(), nil)
+			if status != http.StatusOK {
+				return false, errors.Errorf("unable to contact server %s with status %d", global.GetGlobaStatusAPI(), status)
+			}
+			err := json.Unmarshal([]byte(response), &clustersStatus)
+			if err != nil {
+				return false, errors.Errorf("unable to parse response [%s] with error: %v", response, err)
+			}
+			if len(clustersStatus) != 1 {
+				return false, nil
+			}
+			return clustersStatus[0].Active, nil
+		}, time.Minute, DefaultTimeout).Should(BeTrue())
+
+		// then remote is online
+		active := true
+		for _, cluster := range clustersStatus {
+			if !cluster.Active {
+				active = false
+			}
+		}
+		Expect(active).To(BeTrue())
+
+		// and dataplanes are synced to global
+		Eventually(func() string {
+			output, err := k8s.RunKubectlAndGetOutputE(c1.GetTesting(), c1.GetKubectlOptions("default"), "get", "dataplanes")
+			Expect(err).ToNot(HaveOccurred())
+			return output
+		}, "5s", "500ms").Should(ContainSubstring("kuma-2-remote.demo-client"))
+	})
+}
diff --git a/test/e2e/deploy/kuma_deploy_global_remote_test.go b/test/e2e/deploy/kuma_deploy_global_remote_test.go
index b7bca535f..57ea4e1ed 100644
--- a/test/e2e/deploy/kuma_deploy_global_remote_test.go
+++ b/test/e2e/deploy/kuma_deploy_global_remote_test.go
@@ -1,157 +1,9 @@
 package deploy_test
 
 import (
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"time"
-
-	api_server "github.com/kumahq/kuma/pkg/api-server"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
-	"github.com/go-errors/errors"
-
-	http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
-
-	"github.com/gruntwork-io/terratest/modules/k8s"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/deploy"
 )
 
-var _ = Describe("Test Remote and Global", func() {
-	namespaceWithSidecarInjection := func(namespace string) string {
-		return fmt.Sprintf(`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: %s
-  annotations:
-    kuma.io/sidecar-injection: "enabled"
-`, namespace)
-	}
-
-	var clusters Clusters
-	var c1, c2 Cluster
-	var global, remote ControlPlane
-	var optsGlobal, optsRemote []DeployOptionsFunc
-	var originalKumaNamespace = KumaNamespace
-
-	BeforeEach(func() {
-		// set the new namespace
-		KumaNamespace = "other-kuma-system"
-		var err error
-		clusters, err = NewK8sClusters(
-			[]string{Kuma1, Kuma2},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		c1 = clusters.GetCluster(Kuma1)
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Global, optsGlobal...)).
-			Setup(c1)
-		Expect(err).ToNot(HaveOccurred())
-
-		global = c1.GetKuma()
-		Expect(global).ToNot(BeNil())
-
-		c2 = clusters.GetCluster(Kuma2)
-		optsRemote = []DeployOptionsFunc{
-			WithIngress(),
-			WithGlobalAddress(global.GetKDSServerAddress()),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote...)).
-			Install(KumaDNS()).
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(DemoClientK8s("default")).
-			Install(EchoServerK8s("default")).
-			Setup(c2)
-		Expect(err).ToNot(HaveOccurred())
-
-		remote = c2.GetKuma()
-		Expect(remote).ToNot(BeNil())
-
-		// when
-		err = c1.VerifyKuma()
-		// then
-		Expect(err).ToNot(HaveOccurred())
-
-		// when
-		err = c2.VerifyKuma()
-		// then
-		Expect(err).ToNot(HaveOccurred())
-
-		// then
-		logs1, err := global.GetKumaCPLogs()
-		Expect(err).ToNot(HaveOccurred())
-		Expect(logs1).To(ContainSubstring("\"mode\":\"global\""))
-
-		// and
-		logs2, err := remote.GetKumaCPLogs()
-		Expect(err).ToNot(HaveOccurred())
-		Expect(logs2).To(ContainSubstring("\"mode\":\"remote\""))
-
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-
-		defer func() {
-			// restore the original namespace
-			KumaNamespace = originalKumaNamespace
-		}()
-
-		err := c2.DeleteNamespace(TestNamespace)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = c1.DeleteKuma(optsGlobal...)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = c2.DeleteKuma(optsRemote...)
-		Expect(err).ToNot(HaveOccurred())
-
-		Expect(clusters.DismissCluster()).To(Succeed())
-	})
-
-	It("should deploy Remote and Global on 2 clusters", func() {
-		// when check if remote is online
-		clustersStatus := api_server.Zones{}
-		Eventually(func() (bool, error) {
-			status, response := http_helper.HttpGet(c1.GetTesting(), global.GetGlobaStatusAPI(), nil)
-			if status != http.StatusOK {
-				return false, errors.Errorf("unable to contact server %s with status %d", global.GetGlobaStatusAPI(), status)
-			}
-			err := json.Unmarshal([]byte(response), &clustersStatus)
-			if err != nil {
-				return false, errors.Errorf("unable to parse response [%s] with error: %v", response, err)
-			}
-			if len(clustersStatus) != 1 {
-				return false, nil
-			}
-			return clustersStatus[0].Active, nil
-		}, time.Minute, DefaultTimeout).Should(BeTrue())
-
-		// then remote is online
-		active := true
-		for _, cluster := range clustersStatus {
-			if !cluster.Active {
-				active = false
-			}
-		}
-		Expect(active).To(BeTrue())
-
-		// and dataplanes are synced to global
-		Eventually(func() string {
-			output, err := k8s.RunKubectlAndGetOutputE(c1.GetTesting(), c1.GetKubectlOptions("default"), "get", "dataplanes")
-			Expect(err).ToNot(HaveOccurred())
-			return output
-		}, "5s", "500ms").Should(ContainSubstring("kuma-2-remote.demo-client"))
-	})
-})
+var _ = Describe("Test Remote and Global", deploy.RemoteAndGLobal)
diff --git a/test/e2e/deploy/kuma_deploy_universal.go b/test/e2e/deploy/kuma_deploy_universal.go
new file mode 100644
index 000000000..725f1fbf5
--- /dev/null
+++ b/test/e2e/deploy/kuma_deploy_universal.go
@@ -0,0 +1,206 @@
+package deploy
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	"github.com/go-errors/errors"
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func UniversalDeployment() {
+	meshMTLSOn := func(mesh, localityAware string) string {
+		return fmt.Sprintf(`
+type: Mesh
+name: %s
+mtls:
+  enabledBackend: ca-1
+  backends:
+  - name: ca-1
+    type: builtin
+routing:
+  localityAwareLoadBalancing: %s
+`, mesh, localityAware)
+	}
+
+	meshMTLSOff := func(mesh string) string {
+		return fmt.Sprintf(`
+type: Mesh
+name: %s
+`, mesh)
+	}
+
+	const iterations = 100
+	const defaultMesh = "default"
+	const nonDefaultMesh = "non-default"
+
+	var global, remote_1, remote_2 Cluster
+	var optsGlobal, optsRemote1, optsRemote2 []DeployOptionsFunc
+
+	BeforeEach(func() {
+		clusters, err := NewUniversalClusters(
+			[]string{Kuma3, Kuma4, Kuma5},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Global
+		global = clusters.GetCluster(Kuma5)
+		optsGlobal = []DeployOptionsFunc{}
+		err = NewClusterSetup().
+			Install(Kuma(core.Global, optsGlobal...)).
+			Install(YamlUniversal(meshMTLSOn(nonDefaultMesh, "false"))).
+			Install(YamlUniversal(meshMTLSOff(defaultMesh))).
+			Setup(global)
+		Expect(err).ToNot(HaveOccurred())
+		err = global.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		globalCP := global.GetKuma()
+
+		echoServerToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+		ingressToken, err := globalCP.GenerateDpToken(defaultMesh, "ingress")
+		Expect(err).ToNot(HaveOccurred())
+
+		// TODO: right now these tests are deliberately run WithHDS(false)
+		// even if HDS is enabled without any ServiceProbes it still affects
+		// first 2-3 load balancer requests, it's fine but tests should be rewritten
+
+		// Cluster 1
+		remote_1 = clusters.GetCluster(Kuma3)
+		optsRemote1 = []DeployOptionsFunc{
+			WithGlobalAddress(globalCP.GetKDSServerAddress()),
+			WithHDS(false),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote1...)).
+			Install(EchoServerUniversal(AppModeEchoServer, nonDefaultMesh, "universal1", echoServerToken, WithTransparentProxy(true))).
+			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken, WithTransparentProxy(true))).
+			Install(IngressUniversal(defaultMesh, ingressToken)).
+			Setup(remote_1)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_1.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		// Cluster 2
+		remote_2 = clusters.GetCluster(Kuma4)
+		optsRemote2 = []DeployOptionsFunc{
+			WithGlobalAddress(globalCP.GetKDSServerAddress()),
+			WithHDS(false),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote2...)).
+			Install(EchoServerUniversal(AppModeEchoServer, nonDefaultMesh, "universal2", echoServerToken, WithTransparentProxy(true))).
+			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken, WithTransparentProxy(true))).
+			Install(IngressUniversal(defaultMesh, ingressToken)).
+			Setup(remote_2)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_2.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		err := remote_1.DeleteKuma(optsRemote1...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_1.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = remote_2.DeleteKuma(optsRemote2...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_2.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = global.DeleteKuma(optsGlobal...)
+		Expect(err).ToNot(HaveOccurred())
+		err = global.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should access service locally and remotely", func() {
+		retry.DoWithRetry(remote_1.GetTesting(), "curl local service",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err := remote_1.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+
+		retry.DoWithRetry(remote_2.GetTesting(), "curl remote service",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err := remote_2.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+	})
+
+	It("should distribute requests cross zones", func() {
+		// given services in zone1 and zone2 in a mesh with disabled Locality Aware Load Balancing
+
+		// when executing requests from zone 1
+		responses := 0
+		for i := 0; i < iterations; i++ {
+			stdout, _, err := remote_1.ExecWithRetries("", "", "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+			Expect(err).ToNot(HaveOccurred())
+			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+			Expect(stdout).To(ContainSubstring("universal"))
+
+			if strings.Contains(stdout, "universal1") {
+				responses++
+			}
+		}
+
+		// then some requests are routed to the same zone and some are not
+		Expect(responses > iterations/8).To(BeTrue())
+		Expect(responses < iterations*7/8).To(BeTrue())
+	})
+
+	It("should use locality aware load balancing", func() {
+		// given services in zone1 and zone2 in a mesh with enabled Locality Aware Load Balancing
+		err := YamlUniversal(meshMTLSOn(nonDefaultMesh, "true"))(global)
+		Expect(err).ToNot(HaveOccurred())
+
+		// when executing requests from zone 2
+		responses := 0
+		for i := 0; i < iterations; i++ {
+			stdout, _, err := remote_2.ExecWithRetries("", "", "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+			Expect(err).ToNot(HaveOccurred())
+			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+			Expect(stdout).To(ContainSubstring("universal"))
+
+			if strings.Contains(stdout, "universal2") {
+				responses++
+			}
+		}
+
+		// then all the requests are routed to the same zone 2
+		Expect(responses).To(Equal(iterations))
+	})
+}
diff --git a/test/e2e/deploy/kuma_deploy_universal_test.go b/test/e2e/deploy/kuma_deploy_universal_test.go
index 6d57f146f..1777c96d6 100644
--- a/test/e2e/deploy/kuma_deploy_universal_test.go
+++ b/test/e2e/deploy/kuma_deploy_universal_test.go
@@ -1,207 +1,9 @@
 package deploy_test
 
 import (
-	"fmt"
-	"strings"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
-	"github.com/go-errors/errors"
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/deploy"
 )
 
-var _ = Describe("Test Universal deployment", func() {
-
-	meshMTLSOn := func(mesh, localityAware string) string {
-		return fmt.Sprintf(`
-type: Mesh
-name: %s
-mtls:
-  enabledBackend: ca-1
-  backends:
-  - name: ca-1
-    type: builtin
-routing:
-  localityAwareLoadBalancing: %s
-`, mesh, localityAware)
-	}
-
-	meshMTLSOff := func(mesh string) string {
-		return fmt.Sprintf(`
-type: Mesh
-name: %s
-`, mesh)
-	}
-
-	const iterations = 100
-	const defaultMesh = "default"
-	const nonDefaultMesh = "non-default"
-
-	var global, remote_1, remote_2 Cluster
-	var optsGlobal, optsRemote1, optsRemote2 []DeployOptionsFunc
-
-	BeforeEach(func() {
-		clusters, err := NewUniversalClusters(
-			[]string{Kuma3, Kuma4, Kuma5},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Global
-		global = clusters.GetCluster(Kuma5)
-		optsGlobal = []DeployOptionsFunc{}
-		err = NewClusterSetup().
-			Install(Kuma(core.Global, optsGlobal...)).
-			Install(YamlUniversal(meshMTLSOn(nonDefaultMesh, "false"))).
-			Install(YamlUniversal(meshMTLSOff(defaultMesh))).
-			Setup(global)
-		Expect(err).ToNot(HaveOccurred())
-		err = global.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		globalCP := global.GetKuma()
-
-		echoServerToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-		ingressToken, err := globalCP.GenerateDpToken(defaultMesh, "ingress")
-		Expect(err).ToNot(HaveOccurred())
-
-		// TODO: right now these tests are deliberately run WithHDS(false)
-		// even if HDS is enabled without any ServiceProbes it still affects
-		// first 2-3 load balancer requests, it's fine but tests should be rewritten
-
-		// Cluster 1
-		remote_1 = clusters.GetCluster(Kuma3)
-		optsRemote1 = []DeployOptionsFunc{
-			WithGlobalAddress(globalCP.GetKDSServerAddress()),
-			WithHDS(false),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote1...)).
-			Install(EchoServerUniversal(AppModeEchoServer, nonDefaultMesh, "universal1", echoServerToken, WithTransparentProxy(true))).
-			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken, WithTransparentProxy(true))).
-			Install(IngressUniversal(defaultMesh, ingressToken)).
-			Setup(remote_1)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_1.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		// Cluster 2
-		remote_2 = clusters.GetCluster(Kuma4)
-		optsRemote2 = []DeployOptionsFunc{
-			WithGlobalAddress(globalCP.GetKDSServerAddress()),
-			WithHDS(false),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote2...)).
-			Install(EchoServerUniversal(AppModeEchoServer, nonDefaultMesh, "universal2", echoServerToken, WithTransparentProxy(true))).
-			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken, WithTransparentProxy(true))).
-			Install(IngressUniversal(defaultMesh, ingressToken)).
-			Setup(remote_2)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_2.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		err := remote_1.DeleteKuma(optsRemote1...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_1.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = remote_2.DeleteKuma(optsRemote2...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_2.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = global.DeleteKuma(optsGlobal...)
-		Expect(err).ToNot(HaveOccurred())
-		err = global.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should access service locally and remotely", func() {
-		retry.DoWithRetry(remote_1.GetTesting(), "curl local service",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err := remote_1.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-
-		retry.DoWithRetry(remote_2.GetTesting(), "curl remote service",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err := remote_2.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-	})
-
-	It("should distribute requests cross zones", func() {
-		// given services in zone1 and zone2 in a mesh with disabled Locality Aware Load Balancing
-
-		// when executing requests from zone 1
-		responses := 0
-		for i := 0; i < iterations; i++ {
-			stdout, _, err := remote_1.ExecWithRetries("", "", "demo-client",
-				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-			Expect(err).ToNot(HaveOccurred())
-			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-			Expect(stdout).To(ContainSubstring("universal"))
-
-			if strings.Contains(stdout, "universal1") {
-				responses++
-			}
-		}
-
-		// then some requests are routed to the same zone and some are not
-		Expect(responses > iterations/8).To(BeTrue())
-		Expect(responses < iterations*7/8).To(BeTrue())
-	})
-
-	It("should use locality aware load balancing", func() {
-		// given services in zone1 and zone2 in a mesh with enabled Locality Aware Load Balancing
-		err := YamlUniversal(meshMTLSOn(nonDefaultMesh, "true"))(global)
-		Expect(err).ToNot(HaveOccurred())
-
-		// when executing requests from zone 2
-		responses := 0
-		for i := 0; i < iterations; i++ {
-			stdout, _, err := remote_2.ExecWithRetries("", "", "demo-client",
-				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-			Expect(err).ToNot(HaveOccurred())
-			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-			Expect(stdout).To(ContainSubstring("universal"))
-
-			if strings.Contains(stdout, "universal2") {
-				responses++
-			}
-		}
-
-		// then all the requests are routed to the same zone 2
-		Expect(responses).To(Equal(iterations))
-	})
-})
+var _ = Describe("Test Universal deployment", deploy.UniversalDeployment)
diff --git a/test/e2e/deploy/kuma_deploy_universal_transparent_proxy.go b/test/e2e/deploy/kuma_deploy_universal_transparent_proxy.go
new file mode 100644
index 000000000..e960b0e53
--- /dev/null
+++ b/test/e2e/deploy/kuma_deploy_universal_transparent_proxy.go
@@ -0,0 +1,113 @@
+package deploy
+
+import (
+	"strings"
+
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func UniversalTransparentProxyDeployment() {
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err := NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken, WithTransparentProxy(true))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should access the service using .mesh", func() {
+		retry.DoWithRetry(cluster.GetTesting(), "curl remote service",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "echo-server_kuma-test_svc_8080.mesh")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+		retry.DoWithRetry(cluster.GetTesting(), "curl service with dots",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "echo-server.kuma-test.svc.8080.mesh")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+	})
+
+	It("should re-install transparent proxy", func() {
+		retry.DoWithRetry(cluster.GetTesting(), "curl remote service",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "echo-server_kuma-test_svc_8080.mesh")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+
+		stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+			"/usr/bin/kumactl", "install", "transparent-proxy",
+			"--kuma-dp-user", "kuma-dp", "--skip-resolv-conf", "--verbose")
+		Expect(stdout).To(ContainSubstring("Transparent proxy set up successfully"))
+		Expect(err).ToNot(HaveOccurred())
+
+		retry.DoWithRetry(cluster.GetTesting(), "curl service with dots",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "echo-server.kuma-test.svc.8080.mesh")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+	})
+}
diff --git a/test/e2e/deploy/kuma_deploy_universal_transparent_proxy_test.go b/test/e2e/deploy/kuma_deploy_universal_transparent_proxy_test.go
index c7d0265c2..7d10f8940 100644
--- a/test/e2e/deploy/kuma_deploy_universal_transparent_proxy_test.go
+++ b/test/e2e/deploy/kuma_deploy_universal_transparent_proxy_test.go
@@ -1,114 +1,9 @@
 package deploy_test
 
 import (
-	"strings"
-
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"github.com/pkg/errors"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/deploy"
 )
 
-var _ = Describe("Test Universal Transparent Proxy deployment", func() {
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err := NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken, WithTransparentProxy(true))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("should access the service using .mesh", func() {
-		retry.DoWithRetry(cluster.GetTesting(), "curl remote service",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "echo-server_kuma-test_svc_8080.mesh")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-		retry.DoWithRetry(cluster.GetTesting(), "curl service with dots",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "echo-server.kuma-test.svc.8080.mesh")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-	})
-
-	It("should re-install transparent proxy", func() {
-		retry.DoWithRetry(cluster.GetTesting(), "curl remote service",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "echo-server_kuma-test_svc_8080.mesh")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-
-		stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-			"/usr/bin/kumactl", "install", "transparent-proxy",
-			"--kuma-dp-user", "kuma-dp", "--skip-resolv-conf", "--verbose")
-		Expect(stdout).To(ContainSubstring("Transparent proxy set up successfully"))
-		Expect(err).ToNot(HaveOccurred())
-
-		retry.DoWithRetry(cluster.GetTesting(), "curl service with dots",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "echo-server.kuma-test.svc.8080.mesh")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-	})
-
-})
+var _ = Describe("Test Universal Transparent Proxy deployment", deploy.UniversalTransparentProxyDeployment)
diff --git a/test/e2e/externalservices/externalservices_hostheader.go b/test/e2e/externalservices/externalservices_hostheader.go
new file mode 100644
index 000000000..08bf0ddea
--- /dev/null
+++ b/test/e2e/externalservices/externalservices_hostheader.go
@@ -0,0 +1,65 @@
+package externalservices
+
+import (
+	"strings"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func ExternalServiceHostHeader() {
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	externalService := `
+type: ExternalService
+mesh: default
+name: httpbin
+tags:
+  kuma.io/service: httpbin
+  kuma.io/protocol: http
+networking:
+  address: httpbin.org:80
+  tls:
+    enabled: false
+`
+
+	BeforeEach(func() {
+		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err := NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(YamlUniversal(externalService)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = DemoClientUniversal("dp-demo-client", "default", demoClientToken, WithTransparentProxy(true))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should auto rewrite host header", func() {
+		Eventually(func() bool {
+			stdout, _, _ := cluster.Exec("", "", "dp-demo-client",
+				"curl", "httpbin.mesh/get")
+			return strings.Contains(stdout, `"Host": "httpbin.org"`)
+		}, "30s", "500ms").Should(BeTrue())
+	})
+}
diff --git a/test/e2e/externalservices/externalservices_hostheader_test.go b/test/e2e/externalservices/externalservices_hostheader_test.go
index 0be658c48..a77d51716 100644
--- a/test/e2e/externalservices/externalservices_hostheader_test.go
+++ b/test/e2e/externalservices/externalservices_hostheader_test.go
@@ -1,67 +1,9 @@
 package externalservices_test
 
 import (
-	"strings"
-
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/externalservices"
 )
 
-var _ = Describe("ExternalService", func() {
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	externalService := `
-type: ExternalService
-mesh: default
-name: httpbin
-tags:
-  kuma.io/service: httpbin
-  kuma.io/protocol: http
-networking:
-  address: httpbin.org:80
-  tls:
-    enabled: false
-`
-
-	BeforeEach(func() {
-		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err := NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(YamlUniversal(externalService)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = DemoClientUniversal("dp-demo-client", "default", demoClientToken, WithTransparentProxy(true))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("should auto rewrite host header", func() {
-		Eventually(func() bool {
-			stdout, _, _ := cluster.Exec("", "", "dp-demo-client",
-				"curl", "httpbin.mesh/get")
-			return strings.Contains(stdout, `"Host": "httpbin.org"`)
-		}, "30s", "500ms").Should(BeTrue())
-	})
-
-})
+var _ = Describe("ExternalService host header", externalservices.ExternalServiceHostHeader)
diff --git a/test/e2e/externalservices/externalservices_kubernetes.go b/test/e2e/externalservices/externalservices_kubernetes.go
new file mode 100644
index 000000000..f4d446e12
--- /dev/null
+++ b/test/e2e/externalservices/externalservices_kubernetes.go
@@ -0,0 +1,234 @@
+package externalservices
+
+import (
+	"fmt"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/framework/deployments/externalservice"
+)
+
+func ExternalServicesOnKubernetes() {
+	if IsApiV2() {
+		fmt.Println("Test not supported on API v2")
+		return
+	}
+
+	meshDefaulMtlsOn := `
+apiVersion: kuma.io/v1alpha1
+kind: Mesh
+metadata:
+  name: default
+spec:
+  mtls:
+    enabledBackend: ca-1
+    backends:
+      - name: ca-1
+        type: builtin
+  networking:
+    outbound:
+      passthrough: %s
+`
+
+	trafficRoute := `
+apiVersion: kuma.io/v1alpha1
+kind: TrafficRoute
+mesh: default
+metadata:
+  name: rule-example
+  namespace: default
+spec:
+  sources:
+  - match:
+      kuma.io/service: "*"
+  destinations:
+  - match:
+      kuma.io/service: external-service
+  conf:
+    split:
+    - weight: 1
+      destination:
+        kuma.io/service: external-service
+        id: "%s"
+`
+
+	externalService := `
+apiVersion: kuma.io/v1alpha1
+kind: ExternalService
+mesh: default
+metadata:
+  name: external-service-%s
+spec:
+  tags:
+    kuma.io/service: external-service
+    kuma.io/protocol: http
+    id: "%s"
+  networking:
+    address: %s:%d
+    tls:
+      enabled: %s
+`
+	es1 := "1"
+	es2 := "2"
+
+	namespaceWithSidecarInjection := func(namespace string) string {
+		return fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace)
+	}
+
+	var cluster Cluster
+	var clientPod *v1.Pod
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		clusters, err := NewK8sClusters(
+			[]string{Kuma1},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Global
+		cluster = clusters.GetCluster(Kuma1)
+		deployOptsFuncs = []DeployOptionsFunc{
+			WithEnv("KUMA_RUNTIME_KUBERNETES_INJECTOR_BUILTIN_DNS_ENABLED", "true"),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(DemoClientK8s("default")).
+			Install(externalservice.Install(externalservice.HttpServer, []string{})).
+			Install(externalservice.Install(externalservice.HttpsServer, []string{})).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "false"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = YamlK8s(fmt.Sprintf(externalService,
+			es1, es1,
+			"externalservice-http-server.externalservice-namespace.svc.cluster.local", 10080,
+			"false"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		pods, err := k8s.ListPodsE(
+			cluster.GetTesting(),
+			cluster.GetKubectlOptions(TestNamespace),
+			metav1.ListOptions{
+				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
+			},
+		)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(pods).To(HaveLen(1))
+
+		clientPod = &pods[0]
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+
+		err := cluster.DeleteNamespace(TestNamespace)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = cluster.DeleteKuma(deployOptsFuncs...)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = cluster.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should route to external-service", func() {
+		// given Mesh with passthrough enabled
+		err := YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "true"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then communication outside of the Mesh works
+		_, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace:10080")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		// when passthrough is disabled on the Mesh
+		err = YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "false"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then accessing the external service is no longer possible
+		_, err = retry.DoWithRetryE(cluster.GetTesting(), "passthrough access to service", 5, DefaultTimeout, func() (string, error) {
+			_, _, err := cluster.Exec("", "", "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace:10080")
+			if err != nil {
+				return "", err
+			}
+
+			return "", nil
+		})
+		Expect(err).To(HaveOccurred())
+
+		// when apply external service
+		err = YamlK8s(fmt.Sprintf(externalService,
+			es1, es1,
+			"externalservice-http-server.externalservice-namespace.svc.cluster.local", 10080, // .svc.cluster.local is needed, otherwise Kubernetes will resolve this to the real IP
+			"false"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = YamlK8s(fmt.Sprintf(trafficRoute, es1))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then you can access external service again
+		stdout, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace:10080")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+		Expect(stdout).ToNot(ContainSubstring("externalservice-https-server"))
+
+		// and you can also use .mesh on port of the provided host
+		stdout, stderr, err = cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh:10080")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+		Expect(stdout).ToNot(ContainSubstring("externalservice-https-server"))
+
+		// and you can also use .mesh on port 80
+		// todo (lobkovilya): check of backward compatibility, could be deleted in the next major release Kuma 1.2.x
+		stdout, stderr, err = cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+		Expect(stdout).ToNot(ContainSubstring("externalservice-https-server"))
+	})
+
+	It("should route to external-service over tls", func() {
+		err := YamlK8s(fmt.Sprintf(externalService,
+			es2, es2,
+			"externalservice-https-server.externalservice-namespace.svc.cluster.local", 10080,
+			"true"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = YamlK8s(fmt.Sprintf(trafficRoute, es2))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		stdout, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh:10080")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+		Expect(stdout).To(ContainSubstring("externalservice-https-server"))
+	})
+}
diff --git a/test/e2e/externalservices/externalservices_kubernetes_test.go b/test/e2e/externalservices/externalservices_kubernetes_test.go
index 22f3cf1c2..6b321f196 100644
--- a/test/e2e/externalservices/externalservices_kubernetes_test.go
+++ b/test/e2e/externalservices/externalservices_kubernetes_test.go
@@ -1,233 +1,9 @@
 package externalservices_test
 
 import (
-	"fmt"
+	"github.com/kumahq/kuma/test/e2e/externalservices"
 
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"github.com/gruntwork-io/terratest/modules/k8s"
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	v1 "k8s.io/api/core/v1"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
-	. "github.com/kumahq/kuma/test/framework"
-	"github.com/kumahq/kuma/test/framework/deployments/externalservice"
 )
 
-var _ = Describe("Test ExternalServices on Kubernetes", func() {
-	if IsApiV2() {
-		fmt.Println("Test not supported on API v2")
-		return
-	}
-	meshDefaulMtlsOn := `
-apiVersion: kuma.io/v1alpha1
-kind: Mesh
-metadata:
-  name: default
-spec:
-  mtls:
-    enabledBackend: ca-1
-    backends:
-      - name: ca-1
-        type: builtin
-  networking:
-    outbound:
-      passthrough: %s
-`
-
-	trafficRoute := `
-apiVersion: kuma.io/v1alpha1
-kind: TrafficRoute
-mesh: default
-metadata:
-  name: rule-example
-  namespace: default
-spec:
-  sources:
-  - match:
-      kuma.io/service: "*"
-  destinations:
-  - match:
-      kuma.io/service: external-service
-  conf:
-    split:
-    - weight: 1
-      destination:
-        kuma.io/service: external-service
-        id: "%s"
-`
-
-	externalService := `
-apiVersion: kuma.io/v1alpha1
-kind: ExternalService
-mesh: default
-metadata:
-  name: external-service-%s
-spec:
-  tags:
-    kuma.io/service: external-service
-    kuma.io/protocol: http
-    id: "%s"
-  networking:
-    address: %s:%d
-    tls:
-      enabled: %s
-`
-	es1 := "1"
-	es2 := "2"
-
-	namespaceWithSidecarInjection := func(namespace string) string {
-		return fmt.Sprintf(`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: %s
-  annotations:
-    kuma.io/sidecar-injection: "enabled"
-`, namespace)
-	}
-
-	var cluster Cluster
-	var clientPod *v1.Pod
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		clusters, err := NewK8sClusters(
-			[]string{Kuma1},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Global
-		cluster = clusters.GetCluster(Kuma1)
-		deployOptsFuncs = []DeployOptionsFunc{
-			WithEnv("KUMA_RUNTIME_KUBERNETES_INJECTOR_BUILTIN_DNS_ENABLED", "true"),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(DemoClientK8s("default")).
-			Install(externalservice.Install(externalservice.HttpServer, []string{})).
-			Install(externalservice.Install(externalservice.HttpsServer, []string{})).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "false"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = YamlK8s(fmt.Sprintf(externalService,
-			es1, es1,
-			"externalservice-http-server.externalservice-namespace.svc.cluster.local", 10080,
-			"false"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		pods, err := k8s.ListPodsE(
-			cluster.GetTesting(),
-			cluster.GetKubectlOptions(TestNamespace),
-			metav1.ListOptions{
-				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		clientPod = &pods[0]
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-
-		err := cluster.DeleteNamespace(TestNamespace)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = cluster.DeleteKuma(deployOptsFuncs...)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = cluster.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should route to external-service", func() {
-		// given Mesh with passthrough enabled
-		err := YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "true"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// then communication outside of the Mesh works
-		_, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace:10080")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
-
-		// when passthrough is disabled on the Mesh
-		err = YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "false"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// then accessing the external service is no longer possible
-		_, err = retry.DoWithRetryE(cluster.GetTesting(), "passthrough access to service", 5, DefaultTimeout, func() (string, error) {
-			_, _, err := cluster.Exec("", "", "demo-client",
-				"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace:10080")
-			if err != nil {
-				return "", err
-			}
-
-			return "", nil
-		})
-		Expect(err).To(HaveOccurred())
-
-		// when apply external service
-		err = YamlK8s(fmt.Sprintf(externalService,
-			es1, es1,
-			"externalservice-http-server.externalservice-namespace.svc.cluster.local", 10080, // .svc.cluster.local is needed, otherwise Kubernetes will resolve this to the real IP
-			"false"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = YamlK8s(fmt.Sprintf(trafficRoute, es1))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// then you can access external service again
-		stdout, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace:10080")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
-		Expect(stdout).ToNot(ContainSubstring("externalservice-https-server"))
-
-		// and you can also use .mesh on port of the provided host
-		stdout, stderr, err = cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh:10080")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
-		Expect(stdout).ToNot(ContainSubstring("externalservice-https-server"))
-
-		// and you can also use .mesh on port 80
-		// todo (lobkovilya): check of backward compatibility, could be deleted in the next major release Kuma 1.2.x
-		stdout, stderr, err = cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
-		Expect(stdout).ToNot(ContainSubstring("externalservice-https-server"))
-	})
-
-	It("should route to external-service over tls", func() {
-		err := YamlK8s(fmt.Sprintf(externalService,
-			es2, es2,
-			"externalservice-https-server.externalservice-namespace.svc.cluster.local", 10080,
-			"true"))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = YamlK8s(fmt.Sprintf(trafficRoute, es2))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		stdout, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh:10080")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
-		Expect(stdout).To(ContainSubstring("externalservice-https-server"))
-	})
-})
+var _ = Describe("Test ExternalServices on Kubernetes", externalservices.ExternalServicesOnKubernetes)
diff --git a/test/e2e/externalservices/externalservices_suite_test.go b/test/e2e/externalservices/externalservices_suite_test.go
index c6f2982bc..9286ef054 100644
--- a/test/e2e/externalservices/externalservices_suite_test.go
+++ b/test/e2e/externalservices/externalservices_suite_test.go
@@ -3,8 +3,6 @@ package externalservices_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/externalservices/externalservices_universal.go b/test/e2e/externalservices/externalservices_universal.go
new file mode 100644
index 000000000..68bd71c97
--- /dev/null
+++ b/test/e2e/externalservices/externalservices_universal.go
@@ -0,0 +1,179 @@
+package externalservices
+
+import (
+	"encoding/base64"
+	"fmt"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/framework/deployments/externalservice"
+)
+
+func ExternalServicesOnUniversal() {
+	if IsApiV2() {
+		fmt.Println("Test not supported on API v2")
+		return
+	}
+
+	meshDefaulMtlsOn := `
+type: Mesh
+name: default
+mtls:
+  enabledBackend: ca-1
+  backends:
+  - name: ca-1
+    type: builtin
+networking:
+  outbound:
+    passthrough: false
+`
+
+	trafficRoute := `
+type: TrafficRoute
+name: route-example
+mesh: default
+sources:
+- match:
+   kuma.io/service: "*"
+destinations:
+- match:
+   kuma.io/service: external-service-%s
+conf:
+  split:
+  - weight: 1
+    destination:
+      kuma.io/service: external-service-%s
+`
+
+	externalService := `
+type: ExternalService
+mesh: default
+name: external-service-%s
+tags:
+  kuma.io/service: external-service-%s
+  kuma.io/protocol: http
+networking:
+  address: %s
+  tls:
+    enabled: %s
+    caCert:
+      inline: "%s"
+`
+	es1 := "1"
+	es2 := "2"
+
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		clusters, err := NewUniversalClusters(
+			[]string{Kuma3},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Global
+		cluster = clusters.GetCluster(Kuma3)
+		deployOptsFuncs = []DeployOptionsFunc{
+			WithIPv6(true),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = NewClusterSetup().
+			Install(externalservice.Install(externalservice.HttpServer, externalservice.UniversalAppEchoServer)).
+			Install(externalservice.Install(externalservice.HttpsServer, externalservice.UniversalAppHttpsEchoServer)).
+			Install(DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true), WithBuiltinDNS(true))).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = YamlUniversal(meshDefaulMtlsOn)(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		externalServiceAddress := externalservice.From(cluster, externalservice.HttpServer).GetExternalAppAddress()
+		Expect(externalServiceAddress).ToNot(BeEmpty())
+
+		err = YamlUniversal(fmt.Sprintf(externalService,
+			es1, es1,
+			"kuma-3_externalservice-http-server:80",
+			"false", ""))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		err := cluster.DeleteKuma(deployOptsFuncs...)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = cluster.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should route to external-service", func() {
+		err := YamlUniversal(fmt.Sprintf(trafficRoute, es1, es1))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "external-service-1.mesh")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+		Expect(stdout).ToNot(ContainSubstring("HTTPS"))
+
+		stdout, _, err = cluster.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "kuma-3_externalservice-http-server:80")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+		Expect(stdout).ToNot(ContainSubstring("HTTPS"))
+	})
+
+	It("should route to external-service over tls", func() {
+		// set the route to the secured external service
+		err := YamlUniversal(fmt.Sprintf(trafficRoute, es2, es2))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// when set invalid certificate
+		otherCert := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMRENDQWhTZ0F3SUJBZ0lRSGRQaHhPZlhnV3VOeG9GbFYvRXdxVEFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJd01Ea3hOakV5TWpnME5Gb1hEVE13TURreE5ERXlNamcwTkZvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFPWkdiV2hTbFFTUnhGTnQ1cC8yV0NLRnlIWjNDdXdOZ3lMRVA3blM0Wlh5a3hzRmJZU3VWM2JJZ0Y3YlQvdXEKYTVRaXJlK0M2MGd1aEZicExjUGgyWjZVZmdJZDY5R2xRekhNVlljbUxHalZRdXlBdDRGTU1rVGZWRWw1STRPYQorMml0M0J2aWhWa0toVXo4eTVSUjVLYnFKZkdwNFoyMEZoNmZ0dG9DRmJlT0RtdkJzWUpGbVVRUytpZm95TVkvClAzUjAzU3U3ZzVpSXZuejd0bWt5ZG9OQzhuR1JEemRENUM4Zkp2clZJMVVYNkpSR3lMS3Q0NW9RWHQxbXhLMTAKNUthTjJ6TlYyV3RIc2FKcDlid3JQSCtKaVpHZVp5dnVoNVV3ckxkSENtcUs3c205VG9kR3p0VVpZMFZ6QWM0cQprWVZpWFk4Z1VqZk5tK2NRclBPMWtOOENBd0VBQWFPQmd6Q0JnREFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGR01EQlBQaUJGSjNtdjJvQTlDVHFqZW1GVFYyTUI4R0ExVWRFUVFZTUJhQ0NXeHZZMkZzYUc5egpkSUlKYkc5allXeG9iM04wTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDLzE3UXdlT3BHZGIxTUVCSjhYUEc3CjNzSy91dG9XTFgxdGpmOFN1MURnYTZDRFQvZVRXSFpyV1JmODFLT1ZZMDdkbGU1U1JJREsxUWhmYkdHdEZQK1QKdlprcm9vdXNJOVVTMmFDV2xrZUNaV0dUbnF2TG1Eb091anFhZ0RvS1JSdWs0bVFkdE5Ob254aUwvd1p0VEZLaQorMWlOalVWYkxXaURYZEJMeG9SSVZkTE96cWIvTU54d0VsVXlhVERBa29wUXlPV2FURGtZUHJHbWFXamNzZlBHCmFPS293MHplK3pIVkZxVEhiam5DcUVWM2huc1V5UlV3c0JsbjkrakRKWGd3Wk0vdE1sVkpyWkNoMFNsZTlZNVoKTU9CMGZDZjZzVE1OUlRHZzVMcGw2dUlZTS81SU5wbUhWTW8zbjdNQlNucEVEQVVTMmJmL3VvNWdJaXE2WENkcAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
+		err = YamlUniversal(fmt.Sprintf(externalService,
+			es2, es2,
+			"kuma-3_externalservice-https-server:443",
+			"true",
+			otherCert))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then accessing the secured external service fails
+		_, _, err = cluster.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://kuma-3_externalservice-https-server:443")
+		Expect(err).To(HaveOccurred())
+
+		// when set proper certificate
+		externalServiceCaCert := externalservice.From(cluster, externalservice.HttpsServer).GetCert()
+		Expect(externalServiceCaCert).ToNot(BeEmpty())
+
+		err = YamlUniversal(fmt.Sprintf(externalService,
+			es2, es2,
+			"kuma-3_externalservice-https-server:443",
+			"true",
+			base64.StdEncoding.EncodeToString([]byte(externalServiceCaCert))))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then accessing the secured external service succeeds
+		stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://kuma-3_externalservice-https-server:443")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+		Expect(stdout).To(ContainSubstring("HTTPS"))
+	})
+}
diff --git a/test/e2e/externalservices/externalservices_universal_test.go b/test/e2e/externalservices/externalservices_universal_test.go
index e6b8d6711..50bb3e98a 100644
--- a/test/e2e/externalservices/externalservices_universal_test.go
+++ b/test/e2e/externalservices/externalservices_universal_test.go
@@ -1,179 +1,9 @@
 package externalservices_test
 
 import (
-	"encoding/base64"
-	"fmt"
+	"github.com/kumahq/kuma/test/e2e/externalservices"
 
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
-	. "github.com/kumahq/kuma/test/framework"
-	"github.com/kumahq/kuma/test/framework/deployments/externalservice"
 )
 
-var _ = Describe("Test ExternalServices on Universal", func() {
-	if IsApiV2() {
-		fmt.Println("Test not supported on API v2")
-		return
-	}
-	meshDefaulMtlsOn := `
-type: Mesh
-name: default
-mtls:
-  enabledBackend: ca-1
-  backends:
-  - name: ca-1
-    type: builtin
-networking:
-  outbound:
-    passthrough: false
-`
-
-	trafficRoute := `
-type: TrafficRoute
-name: route-example
-mesh: default
-sources:
-- match:
-   kuma.io/service: "*"
-destinations:
-- match:
-   kuma.io/service: external-service-%s
-conf:
-  split:
-  - weight: 1
-    destination:
-      kuma.io/service: external-service-%s
-`
-
-	externalService := `
-type: ExternalService
-mesh: default
-name: external-service-%s
-tags:
-  kuma.io/service: external-service-%s
-  kuma.io/protocol: http
-networking:
-  address: %s
-  tls:
-    enabled: %s
-    caCert:
-      inline: "%s"
-`
-	es1 := "1"
-	es2 := "2"
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		clusters, err := NewUniversalClusters(
-			[]string{Kuma3},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Global
-		cluster = clusters.GetCluster(Kuma3)
-		deployOptsFuncs = []DeployOptionsFunc{
-			WithIPv6(true),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = NewClusterSetup().
-			Install(externalservice.Install(externalservice.HttpServer, externalservice.UniversalAppEchoServer)).
-			Install(externalservice.Install(externalservice.HttpsServer, externalservice.UniversalAppHttpsEchoServer)).
-			Install(DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true), WithBuiltinDNS(true))).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = YamlUniversal(meshDefaulMtlsOn)(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		externalServiceAddress := externalservice.From(cluster, externalservice.HttpServer).GetExternalAppAddress()
-		Expect(externalServiceAddress).ToNot(BeEmpty())
-
-		err = YamlUniversal(fmt.Sprintf(externalService,
-			es1, es1,
-			"kuma-3_externalservice-http-server:80",
-			"false", ""))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		err := cluster.DeleteKuma(deployOptsFuncs...)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = cluster.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should route to external-service", func() {
-		err := YamlUniversal(fmt.Sprintf(trafficRoute, es1, es1))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "external-service-1.mesh")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-		Expect(stdout).ToNot(ContainSubstring("HTTPS"))
-
-		stdout, _, err = cluster.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "kuma-3_externalservice-http-server:80")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-		Expect(stdout).ToNot(ContainSubstring("HTTPS"))
-	})
-
-	It("should route to external-service over tls", func() {
-		// set the route to the secured external service
-		err := YamlUniversal(fmt.Sprintf(trafficRoute, es2, es2))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// when set invalid certificate
-		otherCert := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMRENDQWhTZ0F3SUJBZ0lRSGRQaHhPZlhnV3VOeG9GbFYvRXdxVEFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJd01Ea3hOakV5TWpnME5Gb1hEVE13TURreE5ERXlNamcwTkZvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFPWkdiV2hTbFFTUnhGTnQ1cC8yV0NLRnlIWjNDdXdOZ3lMRVA3blM0Wlh5a3hzRmJZU3VWM2JJZ0Y3YlQvdXEKYTVRaXJlK0M2MGd1aEZicExjUGgyWjZVZmdJZDY5R2xRekhNVlljbUxHalZRdXlBdDRGTU1rVGZWRWw1STRPYQorMml0M0J2aWhWa0toVXo4eTVSUjVLYnFKZkdwNFoyMEZoNmZ0dG9DRmJlT0RtdkJzWUpGbVVRUytpZm95TVkvClAzUjAzU3U3ZzVpSXZuejd0bWt5ZG9OQzhuR1JEemRENUM4Zkp2clZJMVVYNkpSR3lMS3Q0NW9RWHQxbXhLMTAKNUthTjJ6TlYyV3RIc2FKcDlid3JQSCtKaVpHZVp5dnVoNVV3ckxkSENtcUs3c205VG9kR3p0VVpZMFZ6QWM0cQprWVZpWFk4Z1VqZk5tK2NRclBPMWtOOENBd0VBQWFPQmd6Q0JnREFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGR01EQlBQaUJGSjNtdjJvQTlDVHFqZW1GVFYyTUI4R0ExVWRFUVFZTUJhQ0NXeHZZMkZzYUc5egpkSUlKYkc5allXeG9iM04wTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDLzE3UXdlT3BHZGIxTUVCSjhYUEc3CjNzSy91dG9XTFgxdGpmOFN1MURnYTZDRFQvZVRXSFpyV1JmODFLT1ZZMDdkbGU1U1JJREsxUWhmYkdHdEZQK1QKdlprcm9vdXNJOVVTMmFDV2xrZUNaV0dUbnF2TG1Eb091anFhZ0RvS1JSdWs0bVFkdE5Ob254aUwvd1p0VEZLaQorMWlOalVWYkxXaURYZEJMeG9SSVZkTE96cWIvTU54d0VsVXlhVERBa29wUXlPV2FURGtZUHJHbWFXamNzZlBHCmFPS293MHplK3pIVkZxVEhiam5DcUVWM2huc1V5UlV3c0JsbjkrakRKWGd3Wk0vdE1sVkpyWkNoMFNsZTlZNVoKTU9CMGZDZjZzVE1OUlRHZzVMcGw2dUlZTS81SU5wbUhWTW8zbjdNQlNucEVEQVVTMmJmL3VvNWdJaXE2WENkcAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
-		err = YamlUniversal(fmt.Sprintf(externalService,
-			es2, es2,
-			"kuma-3_externalservice-https-server:443",
-			"true",
-			otherCert))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// then accessing the secured external service fails
-		_, _, err = cluster.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "http://kuma-3_externalservice-https-server:443")
-		Expect(err).To(HaveOccurred())
-
-		// when set proper certificate
-		externalServiceCaCert := externalservice.From(cluster, externalservice.HttpsServer).GetCert()
-		Expect(externalServiceCaCert).ToNot(BeEmpty())
-
-		err = YamlUniversal(fmt.Sprintf(externalService,
-			es2, es2,
-			"kuma-3_externalservice-https-server:443",
-			"true",
-			base64.StdEncoding.EncodeToString([]byte(externalServiceCaCert))))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// then accessing the secured external service succeeds
-		stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "http://kuma-3_externalservice-https-server:443")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-		Expect(stdout).To(ContainSubstring("HTTPS"))
-	})
-
-})
+var _ = Describe("Test ExternalServices on Universal", externalservices.ExternalServicesOnUniversal)
diff --git a/test/e2e/externalservices/foo.go b/test/e2e/externalservices/foo.go
deleted file mode 100644
index 8e811f892..000000000
--- a/test/e2e/externalservices/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package externalservices
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/healthcheck/foo.go b/test/e2e/healthcheck/foo.go
deleted file mode 100644
index 9c399bf6e..000000000
--- a/test/e2e/healthcheck/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package healthcheck
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/healthcheck/healthcheck_hybrid.go b/test/e2e/healthcheck/healthcheck_hybrid.go
new file mode 100644
index 000000000..a779bf19d
--- /dev/null
+++ b/test/e2e/healthcheck/healthcheck_hybrid.go
@@ -0,0 +1,174 @@
+package healthcheck
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func ApplicationHealthCheckOnKubernetesUniversal() {
+	meshMTLSOn := func(mesh string) string {
+		return fmt.Sprintf(`
+apiVersion: kuma.io/v1alpha1
+kind: Mesh
+metadata:
+  name: %s
+spec:
+  mtls:
+    enabledBackend: ca-1
+    backends:
+    - name: ca-1
+      type: builtin
+`, mesh)
+	}
+
+	namespaceWithSidecarInjection := func(namespace string) string {
+		return fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace)
+	}
+
+	var global, remoteK8s, remoteUniversal Cluster
+	var optsRemoteK8s, optsRemoteUniversal []DeployOptionsFunc
+
+	BeforeEach(func() {
+		k8sClusters, err := NewK8sClusters([]string{Kuma1, Kuma2}, Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		universalClusters, err := NewUniversalClusters([]string{Kuma3}, Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		global = k8sClusters.GetCluster(Kuma1)
+		err = NewClusterSetup().
+			Install(Kuma(core.Global)).
+			Install(YamlK8s(meshMTLSOn("default"))).
+			Setup(global)
+		Expect(err).ToNot(HaveOccurred())
+
+		optsRemoteK8s = []DeployOptionsFunc{
+			WithIngress(),
+			WithGlobalAddress(global.GetKuma().GetKDSServerAddress()),
+		}
+		remoteK8s = k8sClusters.GetCluster(Kuma2)
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemoteK8s...)).
+			Install(KumaDNS()).
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(DemoClientK8s("default")).
+			Setup(remoteK8s)
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := global.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		ingressToken, err := global.GetKuma().GenerateDpToken("default", "ingress")
+		Expect(err).ToNot(HaveOccurred())
+
+		optsRemoteUniversal = []DeployOptionsFunc{
+			WithGlobalAddress(global.GetKuma().GetKDSServerAddress()),
+		}
+		remoteUniversal = universalClusters.GetCluster(Kuma3)
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemoteUniversal...)).
+			Install(EchoServerUniversal("dp-echo-1", "default", "echo-universal-1", echoServerToken, WithProtocol("tcp"))).
+			Install(EchoServerUniversal("dp-echo-2", "default", "echo-universal-2", echoServerToken, WithProtocol("tcp"), ProxyOnly(), ServiceProbe())).
+			Install(EchoServerUniversal("dp-echo-3", "default", "echo-universal-3", echoServerToken, WithProtocol("tcp"))).
+			Install(IngressUniversal("default", ingressToken)).
+			Setup(remoteUniversal)
+		Expect(err).ToNot(HaveOccurred())
+		err = remoteUniversal.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+
+		Expect(remoteK8s.DeleteNamespace(TestNamespace)).To(Succeed())
+		err := remoteK8s.DeleteKuma(optsRemoteK8s...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remoteK8s.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = remoteUniversal.DeleteKuma(optsRemoteUniversal...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remoteUniversal.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = global.DeleteKuma()
+		Expect(err).ToNot(HaveOccurred())
+		err = global.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should not load balance requests to unhealthy instance", func() {
+		pods, err := k8s.ListPodsE(remoteK8s.GetTesting(), remoteK8s.GetKubectlOptions(TestNamespace), metav1.ListOptions{
+			LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
+		})
+		Expect(err).ToNot(HaveOccurred())
+		Expect(pods).To(HaveLen(1))
+
+		cmd := []string{"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh"}
+
+		instances := []string{"echo-universal-1", "echo-universal-3"}
+		instanceSet := map[string]bool{}
+
+		_, err = retry.DoWithRetryE(remoteK8s.GetTesting(), fmt.Sprintf("kubectl exec %s -- %s", pods[0].GetName(), strings.Join(cmd, " ")),
+			100, 500*time.Millisecond, func() (string, error) {
+				stdout, _, err := remoteK8s.Exec(TestNamespace, pods[0].GetName(), "demo-client", cmd...)
+				if err != nil {
+					return "", err
+				}
+				for _, instance := range instances {
+					if strings.Contains(stdout, instance) {
+						instanceSet[instance] = true
+					}
+				}
+				if len(instanceSet) != len(instances) {
+					return "", errors.Errorf("checked %d/%d instances", len(instanceSet), len(instances))
+				}
+				return "", nil
+			},
+		)
+		Expect(err).ToNot(HaveOccurred())
+
+		var counter1, counter2, counter3 int
+		const numOfRequest = 100
+
+		for i := 0; i < numOfRequest; i++ {
+			var stdout string
+
+			stdout, _, err = remoteK8s.Exec(TestNamespace, pods[0].GetName(), "demo-client", cmd...)
+			Expect(err).ToNot(HaveOccurred())
+
+			switch {
+			case strings.Contains(stdout, "Echo echo-universal-1"):
+				counter1++
+			case strings.Contains(stdout, "Echo echo-universal-2"):
+				counter2++
+			case strings.Contains(stdout, "Echo echo-universal-3"):
+				counter3++
+			}
+		}
+
+		Expect(counter2).To(Equal(0))
+		Expect(counter1 > 0).To(BeTrue())
+		Expect(counter3 > 0).To(BeTrue())
+		Expect(counter1 + counter3).To(Equal(numOfRequest))
+	})
+}
diff --git a/test/e2e/healthcheck/healthcheck_hybrid_test.go b/test/e2e/healthcheck/healthcheck_hybrid_test.go
index ffb76a024..010213683 100644
--- a/test/e2e/healthcheck/healthcheck_hybrid_test.go
+++ b/test/e2e/healthcheck/healthcheck_hybrid_test.go
@@ -1,175 +1,9 @@
 package healthcheck_test
 
 import (
-	"fmt"
-	"strings"
-	"time"
-
-	"github.com/gruntwork-io/terratest/modules/k8s"
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"github.com/pkg/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/healthcheck"
 )
 
-var _ = Describe("Test application HealthCheck on Kubernetes/Universal", func() {
-	meshMTLSOn := func(mesh string) string {
-		return fmt.Sprintf(`
-apiVersion: kuma.io/v1alpha1
-kind: Mesh
-metadata:
-  name: %s
-spec:
-  mtls:
-    enabledBackend: ca-1
-    backends:
-    - name: ca-1
-      type: builtin
-`, mesh)
-	}
-
-	namespaceWithSidecarInjection := func(namespace string) string {
-		return fmt.Sprintf(`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: %s
-  annotations:
-    kuma.io/sidecar-injection: "enabled"
-`, namespace)
-	}
-
-	var global, remoteK8s, remoteUniversal Cluster
-	var optsRemoteK8s, optsRemoteUniversal []DeployOptionsFunc
-
-	BeforeEach(func() {
-		k8sClusters, err := NewK8sClusters([]string{Kuma1, Kuma2}, Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		universalClusters, err := NewUniversalClusters([]string{Kuma3}, Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		global = k8sClusters.GetCluster(Kuma1)
-		err = NewClusterSetup().
-			Install(Kuma(core.Global)).
-			Install(YamlK8s(meshMTLSOn("default"))).
-			Setup(global)
-		Expect(err).ToNot(HaveOccurred())
-
-		optsRemoteK8s = []DeployOptionsFunc{
-			WithIngress(),
-			WithGlobalAddress(global.GetKuma().GetKDSServerAddress()),
-		}
-		remoteK8s = k8sClusters.GetCluster(Kuma2)
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemoteK8s...)).
-			Install(KumaDNS()).
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(DemoClientK8s("default")).
-			Setup(remoteK8s)
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := global.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		ingressToken, err := global.GetKuma().GenerateDpToken("default", "ingress")
-		Expect(err).ToNot(HaveOccurred())
-
-		optsRemoteUniversal = []DeployOptionsFunc{
-			WithGlobalAddress(global.GetKuma().GetKDSServerAddress()),
-		}
-		remoteUniversal = universalClusters.GetCluster(Kuma3)
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemoteUniversal...)).
-			Install(EchoServerUniversal("dp-echo-1", "default", "echo-universal-1", echoServerToken, WithProtocol("tcp"))).
-			Install(EchoServerUniversal("dp-echo-2", "default", "echo-universal-2", echoServerToken, WithProtocol("tcp"), ProxyOnly(), ServiceProbe())).
-			Install(EchoServerUniversal("dp-echo-3", "default", "echo-universal-3", echoServerToken, WithProtocol("tcp"))).
-			Install(IngressUniversal("default", ingressToken)).
-			Setup(remoteUniversal)
-		Expect(err).ToNot(HaveOccurred())
-		err = remoteUniversal.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-
-		Expect(remoteK8s.DeleteNamespace(TestNamespace)).To(Succeed())
-		err := remoteK8s.DeleteKuma(optsRemoteK8s...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remoteK8s.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = remoteUniversal.DeleteKuma(optsRemoteUniversal...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remoteUniversal.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = global.DeleteKuma()
-		Expect(err).ToNot(HaveOccurred())
-		err = global.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-	})
-
-	It("should not load balance requests to unhealthy instance", func() {
-		pods, err := k8s.ListPodsE(remoteK8s.GetTesting(), remoteK8s.GetKubectlOptions(TestNamespace), metav1.ListOptions{
-			LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-		})
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		cmd := []string{"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh"}
-
-		instances := []string{"echo-universal-1", "echo-universal-3"}
-		instanceSet := map[string]bool{}
-
-		_, err = retry.DoWithRetryE(remoteK8s.GetTesting(), fmt.Sprintf("kubectl exec %s -- %s", pods[0].GetName(), strings.Join(cmd, " ")),
-			100, 500*time.Millisecond, func() (string, error) {
-				stdout, _, err := remoteK8s.Exec(TestNamespace, pods[0].GetName(), "demo-client", cmd...)
-				if err != nil {
-					return "", err
-				}
-				for _, instance := range instances {
-					if strings.Contains(stdout, instance) {
-						instanceSet[instance] = true
-					}
-				}
-				if len(instanceSet) != len(instances) {
-					return "", errors.Errorf("checked %d/%d instances", len(instanceSet), len(instances))
-				}
-				return "", nil
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-
-		var counter1, counter2, counter3 int
-		const numOfRequest = 100
-
-		for i := 0; i < numOfRequest; i++ {
-			var stdout string
-
-			stdout, _, err = remoteK8s.Exec(TestNamespace, pods[0].GetName(), "demo-client", cmd...)
-			Expect(err).ToNot(HaveOccurred())
-
-			switch {
-			case strings.Contains(stdout, "Echo echo-universal-1"):
-				counter1++
-			case strings.Contains(stdout, "Echo echo-universal-2"):
-				counter2++
-			case strings.Contains(stdout, "Echo echo-universal-3"):
-				counter3++
-			}
-		}
-
-		Expect(counter2).To(Equal(0))
-		Expect(counter1 > 0).To(BeTrue())
-		Expect(counter3 > 0).To(BeTrue())
-		Expect(counter1 + counter3).To(Equal(numOfRequest))
-	})
-})
+var _ = Describe("Test application HealthCheck on Kubernetes/Universal", healthcheck.ApplicationHealthCheckOnKubernetesUniversal)
diff --git a/test/e2e/healthcheck/healthcheck_panic.go b/test/e2e/healthcheck/healthcheck_panic.go
new file mode 100644
index 000000000..82e65b937
--- /dev/null
+++ b/test/e2e/healthcheck/healthcheck_panic.go
@@ -0,0 +1,99 @@
+package healthcheck
+
+import (
+	"fmt"
+	"strings"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func HealthCheckPanicThreshold() {
+	var universalCluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	healthCheck := `
+type: HealthCheck
+name: hc-1
+mesh: default
+sources:
+- match:
+    kuma.io/service: '*'
+destinations:
+- match:
+    kuma.io/service: echo-server_kuma-test_svc_8080
+conf:
+  interval: 10s
+  timeout: 2s
+  unhealthyThreshold: 3
+  healthyThreshold: 1
+  healthyPanicThreshold: 61
+  failTrafficOnPanic: true
+  tcp: {}`
+
+	dp := func(idx int) string {
+		return fmt.Sprintf(`
+type: Dataplane
+mesh: default
+name: dp-echo-%d
+networking:
+  address: 192.168.0.%d
+  inbound:
+  - port: 8080
+    servicePort: 80
+    tags:
+      kuma.io/service: echo-server_kuma-test_svc_8080
+      kuma.io/protocol: http`, idx, idx)
+	}
+
+	BeforeEach(func() {
+		universalCluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err := NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(YamlUniversal(healthCheck)).
+			Setup(universalCluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = universalCluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := universalCluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := universalCluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		for i := 1; i <= 6; i++ {
+			dpName := fmt.Sprintf("dp-echo-%d", i)
+			response := fmt.Sprintf("universal-%d", i)
+			err = EchoServerUniversal(dpName, "default", response, echoServerToken)(universalCluster)
+			Expect(err).ToNot(HaveOccurred())
+		}
+		for i := 7; i <= 10; i++ {
+			err := NewClusterSetup().Install(YamlUniversal(dp(i))).Setup(universalCluster)
+			Expect(err).ToNot(HaveOccurred())
+		}
+
+		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true))(universalCluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(universalCluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(universalCluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should switch to panic mode and dismiss all requests", func() {
+		Eventually(func() bool {
+			stdout, _, _ := universalCluster.Exec("", "", "demo-client",
+				"curl", "-v", "echo-server_kuma-test_svc_8080.mesh")
+			return strings.Contains(stdout, "no healthy upstream")
+		}, "30s", "500ms").Should(BeTrue())
+	})
+}
diff --git a/test/e2e/healthcheck/healthcheck_panic_test.go b/test/e2e/healthcheck/healthcheck_panic_test.go
index d2620096d..7c994ffdf 100644
--- a/test/e2e/healthcheck/healthcheck_panic_test.go
+++ b/test/e2e/healthcheck/healthcheck_panic_test.go
@@ -1,100 +1,9 @@
 package healthcheck_test
 
 import (
-	"fmt"
-	"strings"
-
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/healthcheck"
 )
 
-var _ = Describe("Test HealthCheck panic threshold", func() {
-
-	var universalCluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	healthCheck := `
-type: HealthCheck
-name: hc-1
-mesh: default
-sources:
-- match:
-    kuma.io/service: '*'
-destinations:
-- match:
-    kuma.io/service: echo-server_kuma-test_svc_8080
-conf:
-  interval: 10s
-  timeout: 2s
-  unhealthyThreshold: 3
-  healthyThreshold: 1
-  healthyPanicThreshold: 61
-  failTrafficOnPanic: true
-  tcp: {}`
-
-	dp := func(idx int) string {
-		return fmt.Sprintf(`
-type: Dataplane
-mesh: default
-name: dp-echo-%d
-networking:
-  address: 192.168.0.%d
-  inbound:
-  - port: 8080
-    servicePort: 80
-    tags:
-      kuma.io/service: echo-server_kuma-test_svc_8080
-      kuma.io/protocol: http`, idx, idx)
-	}
-
-	BeforeEach(func() {
-		universalCluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err := NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(YamlUniversal(healthCheck)).
-			Setup(universalCluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = universalCluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := universalCluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := universalCluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		for i := 1; i <= 6; i++ {
-			dpName := fmt.Sprintf("dp-echo-%d", i)
-			response := fmt.Sprintf("universal-%d", i)
-			err = EchoServerUniversal(dpName, "default", response, echoServerToken)(universalCluster)
-			Expect(err).ToNot(HaveOccurred())
-		}
-		for i := 7; i <= 10; i++ {
-			err := NewClusterSetup().Install(YamlUniversal(dp(i))).Setup(universalCluster)
-			Expect(err).ToNot(HaveOccurred())
-		}
-
-		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true))(universalCluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(universalCluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(universalCluster.DismissCluster()).To(Succeed())
-	})
-
-	It("should switch to panic mode and dismiss all requests", func() {
-		Eventually(func() bool {
-			stdout, _, _ := universalCluster.Exec("", "", "demo-client",
-				"curl", "-v", "echo-server_kuma-test_svc_8080.mesh")
-			return strings.Contains(stdout, "no healthy upstream")
-		}, "30s", "500ms").Should(BeTrue())
-	})
-})
+var _ = Describe("Test HealthCheck panic threshold", healthcheck.HealthCheckPanicThreshold)
diff --git a/test/e2e/healthcheck/e2e_suite_test.go b/test/e2e/healthcheck/healthcheck_suite_test.go
similarity index 78%
rename from test/e2e/healthcheck/e2e_suite_test.go
rename to test/e2e/healthcheck/healthcheck_suite_test.go
index e8524966d..242266b8b 100644
--- a/test/e2e/healthcheck/e2e_suite_test.go
+++ b/test/e2e/healthcheck/healthcheck_suite_test.go
@@ -3,8 +3,6 @@ package healthcheck_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/healthcheck/healthcheck_universal.go b/test/e2e/healthcheck/healthcheck_universal.go
new file mode 100644
index 000000000..09e539a1f
--- /dev/null
+++ b/test/e2e/healthcheck/healthcheck_universal.go
@@ -0,0 +1,62 @@
+package healthcheck
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func ApplicationHealthCheckOnUniversal() {
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err := NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = EchoServerUniversal("dp-echo-server", "default", "universal", echoServerToken, ProxyOnly(), ServiceProbe())(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = DemoClientUniversal("dp-demo-client", "default", demoClientToken, ServiceProbe())(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should update dataplane.inbound.health", func() {
+		Eventually(func() (string, error) {
+			output, err := cluster.GetKumactlOptions().RunKumactlAndGetOutputV(Verbose, "get", "dataplane", "dp-echo-server", "-oyaml")
+			if err != nil {
+				return "", err
+			}
+			return output, nil
+		}, "30s", "500ms").Should(ContainSubstring("health: {}"))
+
+		Eventually(func() (string, error) {
+			output, err := cluster.GetKumactlOptions().RunKumactlAndGetOutputV(Verbose, "get", "dataplane", "dp-demo-client", "-oyaml")
+			if err != nil {
+				return "", err
+			}
+			return output, nil
+		}, "30s", "500ms").Should(ContainSubstring("ready: true"))
+	})
+}
diff --git a/test/e2e/healthcheck/healthcheck_universal_test.go b/test/e2e/healthcheck/healthcheck_universal_test.go
index 2d3ef4c85..3c5629057 100644
--- a/test/e2e/healthcheck/healthcheck_universal_test.go
+++ b/test/e2e/healthcheck/healthcheck_universal_test.go
@@ -2,63 +2,8 @@ package healthcheck_test
 
 import (
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/healthcheck"
 )
 
-var _ = Describe("Test application HealthCheck on Universal", func() {
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		cluster = NewUniversalCluster(NewTestingT(), Kuma3, Silent)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err := NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = EchoServerUniversal("dp-echo-server", "default", "universal", echoServerToken, ProxyOnly(), ServiceProbe())(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = DemoClientUniversal("dp-demo-client", "default", demoClientToken, ServiceProbe())(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("should update dataplane.inbound.health", func() {
-		Eventually(func() (string, error) {
-			output, err := cluster.GetKumactlOptions().RunKumactlAndGetOutputV(Verbose, "get", "dataplane", "dp-echo-server", "-oyaml")
-			if err != nil {
-				return "", err
-			}
-			return output, nil
-		}, "30s", "500ms").Should(ContainSubstring("health: {}"))
-
-		Eventually(func() (string, error) {
-			output, err := cluster.GetKumactlOptions().RunKumactlAndGetOutputV(Verbose, "get", "dataplane", "dp-demo-client", "-oyaml")
-			if err != nil {
-				return "", err
-			}
-			return output, nil
-		}, "30s", "500ms").Should(ContainSubstring("ready: true"))
-	})
-
-})
+var _ = Describe("Test application HealthCheck on Universal", healthcheck.ApplicationHealthCheckOnUniversal)
diff --git a/test/e2e/helm/foo.go b/test/e2e/helm/foo.go
deleted file mode 100644
index 1ba0146c6..000000000
--- a/test/e2e/helm/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package helm
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/helm/helm_suite_test.go b/test/e2e/helm/helm_suite_test.go
index 2ee29f2bd..0be128d9f 100644
--- a/test/e2e/helm/helm_suite_test.go
+++ b/test/e2e/helm/helm_suite_test.go
@@ -3,8 +3,6 @@ package helm_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/helm/kuma_helm_deploy_autoscaling.go b/test/e2e/helm/kuma_helm_deploy_autoscaling.go
new file mode 100644
index 000000000..56e7640db
--- /dev/null
+++ b/test/e2e/helm/kuma_helm_deploy_autoscaling.go
@@ -0,0 +1,74 @@
+package helm
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/random"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func ControlPlaneAutoscalingWithHelmChart() {
+	minReplicas := 3
+
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		c, err := NewK8sClusterWithTimeout(
+			NewTestingT(),
+			Kuma1,
+			Silent,
+			6*time.Second)
+		Expect(err).ToNot(HaveOccurred())
+
+		cluster = c.WithRetries(60)
+
+		releaseName := fmt.Sprintf(
+			"kuma-%s",
+			strings.ToLower(random.UniqueId()),
+		)
+		deployOptsFuncs = []DeployOptionsFunc{
+			WithInstallationMode(HelmInstallationMode),
+			WithHelmReleaseName(releaseName),
+			WithHelmOpt("controlPlane.autoscaling.enabled", "true"),
+			WithHelmOpt("controlPlane.autoscaling.minReplicas", strconv.Itoa(minReplicas)),
+			WithCNI(),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(KumaDNS()).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		// tear down Kuma
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		// tear down cluster
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("Should scale to the minimum replicas", func() {
+		// given a kuma deployment with autoscaling and min replicas
+		k8sCluster := cluster.(*K8sCluster)
+
+		// when waiting for autoscaling
+		err := k8sCluster.WaitApp(KumaServiceName, KumaNamespace, minReplicas)
+
+		// then the min replicas should come up
+		Expect(err).ToNot(HaveOccurred())
+	})
+}
diff --git a/test/e2e/helm/kuma_helm_deploy_autoscaling_test.go b/test/e2e/helm/kuma_helm_deploy_autoscaling_test.go
index b52b825f1..6611737c7 100644
--- a/test/e2e/helm/kuma_helm_deploy_autoscaling_test.go
+++ b/test/e2e/helm/kuma_helm_deploy_autoscaling_test.go
@@ -1,74 +1,9 @@
 package helm_test
 
 import (
-	"fmt"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/gruntwork-io/terratest/modules/random"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/helm"
 )
 
-var _ = Describe("Test Control Plane autoscaling with Helm chart", func() {
-	minReplicas := 3
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		c, err := NewK8sClusterWithTimeout(
-			NewTestingT(),
-			Kuma1,
-			Silent,
-			6*time.Second)
-		Expect(err).ToNot(HaveOccurred())
-
-		cluster = c.WithRetries(60)
-
-		releaseName := fmt.Sprintf(
-			"kuma-%s",
-			strings.ToLower(random.UniqueId()),
-		)
-		deployOptsFuncs = []DeployOptionsFunc{
-			WithInstallationMode(HelmInstallationMode),
-			WithHelmReleaseName(releaseName),
-			WithHelmOpt("controlPlane.autoscaling.enabled", "true"),
-			WithHelmOpt("controlPlane.autoscaling.minReplicas", strconv.Itoa(minReplicas)),
-			WithCNI(),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(KumaDNS()).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		// tear down Kuma
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		// tear down cluster
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("Should scale to the minimum replicas", func() {
-		// given a kuma deployment with autoscaling and min replicas
-		k8sCluster := cluster.(*K8sCluster)
-
-		// when waiting for autoscaling
-		err := k8sCluster.WaitApp(KumaServiceName, KumaNamespace, minReplicas)
-
-		// then the min replicas should come up
-		Expect(err).ToNot(HaveOccurred())
-	})
-})
+var _ = Describe("Test Control Plane autoscaling with Helm chart", helm.ControlPlaneAutoscalingWithHelmChart)
diff --git a/test/e2e/helm/kuma_helm_deploy_global_remote.go b/test/e2e/helm/kuma_helm_deploy_global_remote.go
new file mode 100644
index 000000000..764bd4edf
--- /dev/null
+++ b/test/e2e/helm/kuma_helm_deploy_global_remote.go
@@ -0,0 +1,161 @@
+package helm
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/random"
+
+	api_server "github.com/kumahq/kuma/pkg/api-server"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	"github.com/go-errors/errors"
+
+	http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func RemoteAndGlobalWithHelmChart() {
+	namespaceWithSidecarInjection := func(namespace string) string {
+		return fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace)
+	}
+
+	var clusters Clusters
+	var c1, c2 Cluster
+	var global, remote ControlPlane
+	var optsGlobal, optsRemote []DeployOptionsFunc
+
+	BeforeEach(func() {
+		var err error
+		clusters, err = NewK8sClusters(
+			[]string{Kuma1, Kuma2},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		c1 = clusters.GetCluster(Kuma1).
+			WithTimeout(6 * time.Second).
+			WithRetries(60)
+		c2 = clusters.GetCluster(Kuma2).
+			WithTimeout(6 * time.Second).
+			WithRetries(60)
+
+		releaseName := fmt.Sprintf(
+			"kuma-%s",
+			strings.ToLower(random.UniqueId()),
+		)
+		optsGlobal = []DeployOptionsFunc{
+			WithInstallationMode(HelmInstallationMode),
+			WithHelmReleaseName(releaseName),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Global, optsGlobal...)).
+			Setup(c1)
+		Expect(err).ToNot(HaveOccurred())
+
+		global = c1.GetKuma()
+		Expect(global).ToNot(BeNil())
+
+		optsRemote = []DeployOptionsFunc{
+			WithInstallationMode(HelmInstallationMode),
+			WithHelmReleaseName(releaseName),
+			WithGlobalAddress(global.GetKDSServerAddress()),
+			WithHelmOpt("ingress.enabled", "true"),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote...)).
+			Install(KumaDNS()).
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(DemoClientK8s("default")).
+			Install(EchoServerK8s("default")).
+			Setup(c2)
+		Expect(err).ToNot(HaveOccurred())
+
+		remote = c2.GetKuma()
+		Expect(remote).ToNot(BeNil())
+
+		// when
+		err = c1.VerifyKuma()
+		// then
+		Expect(err).ToNot(HaveOccurred())
+
+		// when
+		err = c2.VerifyKuma()
+		// then
+		Expect(err).ToNot(HaveOccurred())
+
+		// then
+		logs1, err := global.GetKumaCPLogs()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(logs1).To(ContainSubstring("\"mode\":\"global\""))
+
+		// and
+		logs2, err := remote.GetKumaCPLogs()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(logs2).To(ContainSubstring("\"mode\":\"remote\""))
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		// tear down apps
+		Expect(c2.DeleteNamespace(TestNamespace)).To(Succeed())
+		// tear down Kuma
+		Expect(c1.DeleteKuma(optsGlobal...)).To(Succeed())
+		Expect(c2.DeleteKuma(optsRemote...)).To(Succeed())
+		// tear down clusters
+		Expect(clusters.DismissCluster()).To(Succeed())
+	})
+
+	It("Should deploy Remote and Global on 2 clusters", func() {
+		clustersStatus := api_server.Zones{}
+		Eventually(func() (bool, error) {
+			status, response := http_helper.HttpGet(c1.GetTesting(), global.GetGlobaStatusAPI(), nil)
+			if status != http.StatusOK {
+				return false, errors.Errorf("unable to contact server %s with status %d", global.GetGlobaStatusAPI(), status)
+			}
+			err := json.Unmarshal([]byte(response), &clustersStatus)
+			if err != nil {
+				return false, errors.Errorf("unable to parse response [%s] with error: %v", response, err)
+			}
+			if len(clustersStatus) != 1 {
+				return false, nil
+			}
+			return clustersStatus[0].Active, nil
+		}, time.Minute, DefaultTimeout).Should(BeTrue())
+
+		// then
+		active := true
+		for _, cluster := range clustersStatus {
+			if !cluster.Active {
+				active = false
+			}
+		}
+		Expect(active).To(BeTrue())
+
+		// and dataplanes are synced to global
+		Eventually(func() string {
+			output, err := k8s.RunKubectlAndGetOutputE(c1.GetTesting(), c1.GetKubectlOptions("default"), "get", "dataplanes")
+			Expect(err).ToNot(HaveOccurred())
+			return output
+		}, "5s", "500ms").Should(ContainSubstring("kuma-2-remote.demo-client"))
+	})
+}
diff --git a/test/e2e/helm/kuma_helm_deploy_global_remote_test.go b/test/e2e/helm/kuma_helm_deploy_global_remote_test.go
index a9bcd01a0..e3fc7762c 100644
--- a/test/e2e/helm/kuma_helm_deploy_global_remote_test.go
+++ b/test/e2e/helm/kuma_helm_deploy_global_remote_test.go
@@ -1,163 +1,9 @@
 package helm_test
 
 import (
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"strings"
-	"time"
-
-	"github.com/gruntwork-io/terratest/modules/random"
-
-	api_server "github.com/kumahq/kuma/pkg/api-server"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
-	"github.com/go-errors/errors"
-
-	http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
-
-	"github.com/gruntwork-io/terratest/modules/k8s"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/helm"
 )
 
-var _ = Describe("Test Remote and Global with Helm chart", func() {
-
-	namespaceWithSidecarInjection := func(namespace string) string {
-		return fmt.Sprintf(`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: %s
-  annotations:
-    kuma.io/sidecar-injection: "enabled"
-`, namespace)
-	}
-
-	var clusters Clusters
-	var c1, c2 Cluster
-	var global, remote ControlPlane
-	var optsGlobal, optsRemote []DeployOptionsFunc
-
-	BeforeEach(func() {
-		var err error
-		clusters, err = NewK8sClusters(
-			[]string{Kuma1, Kuma2},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		c1 = clusters.GetCluster(Kuma1).
-			WithTimeout(6 * time.Second).
-			WithRetries(60)
-		c2 = clusters.GetCluster(Kuma2).
-			WithTimeout(6 * time.Second).
-			WithRetries(60)
-
-		releaseName := fmt.Sprintf(
-			"kuma-%s",
-			strings.ToLower(random.UniqueId()),
-		)
-		optsGlobal = []DeployOptionsFunc{
-			WithInstallationMode(HelmInstallationMode),
-			WithHelmReleaseName(releaseName),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Global, optsGlobal...)).
-			Setup(c1)
-		Expect(err).ToNot(HaveOccurred())
-
-		global = c1.GetKuma()
-		Expect(global).ToNot(BeNil())
-
-		optsRemote = []DeployOptionsFunc{
-			WithInstallationMode(HelmInstallationMode),
-			WithHelmReleaseName(releaseName),
-			WithGlobalAddress(global.GetKDSServerAddress()),
-			WithHelmOpt("ingress.enabled", "true"),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote...)).
-			Install(KumaDNS()).
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(DemoClientK8s("default")).
-			Install(EchoServerK8s("default")).
-			Setup(c2)
-		Expect(err).ToNot(HaveOccurred())
-
-		remote = c2.GetKuma()
-		Expect(remote).ToNot(BeNil())
-
-		// when
-		err = c1.VerifyKuma()
-		// then
-		Expect(err).ToNot(HaveOccurred())
-
-		// when
-		err = c2.VerifyKuma()
-		// then
-		Expect(err).ToNot(HaveOccurred())
-
-		// then
-		logs1, err := global.GetKumaCPLogs()
-		Expect(err).ToNot(HaveOccurred())
-		Expect(logs1).To(ContainSubstring("\"mode\":\"global\""))
-
-		// and
-		logs2, err := remote.GetKumaCPLogs()
-		Expect(err).ToNot(HaveOccurred())
-		Expect(logs2).To(ContainSubstring("\"mode\":\"remote\""))
-
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		// tear down apps
-		Expect(c2.DeleteNamespace(TestNamespace)).To(Succeed())
-		// tear down Kuma
-		Expect(c1.DeleteKuma(optsGlobal...)).To(Succeed())
-		Expect(c2.DeleteKuma(optsRemote...)).To(Succeed())
-		// tear down clusters
-		Expect(clusters.DismissCluster()).To(Succeed())
-	})
-
-	It("Should deploy Remote and Global on 2 clusters", func() {
-		clustersStatus := api_server.Zones{}
-		Eventually(func() (bool, error) {
-			status, response := http_helper.HttpGet(c1.GetTesting(), global.GetGlobaStatusAPI(), nil)
-			if status != http.StatusOK {
-				return false, errors.Errorf("unable to contact server %s with status %d", global.GetGlobaStatusAPI(), status)
-			}
-			err := json.Unmarshal([]byte(response), &clustersStatus)
-			if err != nil {
-				return false, errors.Errorf("unable to parse response [%s] with error: %v", response, err)
-			}
-			if len(clustersStatus) != 1 {
-				return false, nil
-			}
-			return clustersStatus[0].Active, nil
-		}, time.Minute, DefaultTimeout).Should(BeTrue())
-
-		// then
-		active := true
-		for _, cluster := range clustersStatus {
-			if !cluster.Active {
-				active = false
-			}
-		}
-		Expect(active).To(BeTrue())
-
-		// and dataplanes are synced to global
-		Eventually(func() string {
-			output, err := k8s.RunKubectlAndGetOutputE(c1.GetTesting(), c1.GetKubectlOptions("default"), "get", "dataplanes")
-			Expect(err).ToNot(HaveOccurred())
-			return output
-		}, "5s", "500ms").Should(ContainSubstring("kuma-2-remote.demo-client"))
-	})
-})
+var _ = Describe("Test Remote and Global with Helm chart", helm.RemoteAndGlobalWithHelmChart)
diff --git a/test/e2e/helm/kuma_helm_deploy_multi_apps.go b/test/e2e/helm/kuma_helm_deploy_multi_apps.go
new file mode 100644
index 000000000..1b1ff894f
--- /dev/null
+++ b/test/e2e/helm/kuma_helm_deploy_multi_apps.go
@@ -0,0 +1,117 @@
+package helm
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/gruntwork-io/terratest/modules/random"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func AppDeploymentWithHelmChart() {
+	namespaceWithSidecarInjection := func(namespace string) string {
+		return fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace)
+	}
+
+	defaultMesh := `
+apiVersion: kuma.io/v1alpha1
+kind: Mesh
+metadata:
+  name: default
+`
+
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		c, err := NewK8sClusterWithTimeout(
+			NewTestingT(),
+			Kuma1,
+			Silent,
+			6*time.Second)
+		Expect(err).ToNot(HaveOccurred())
+
+		cluster = c.WithRetries(60)
+
+		releaseName := fmt.Sprintf(
+			"kuma-%s",
+			strings.ToLower(random.UniqueId()),
+		)
+		deployOptsFuncs = []DeployOptionsFunc{
+			WithInstallationMode(HelmInstallationMode),
+			WithHelmReleaseName(releaseName),
+			WithSkipDefaultMesh(true), // it's common case for HELM deployments that Mesh is also managed by HELM therefore it's not created by default
+			WithCPReplicas(3),         // test HA capability
+			WithCNI(),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(KumaDNS()).
+			Install(YamlK8s(defaultMesh)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = NewClusterSetup().
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(DemoClientK8s("default")).
+			Install(EchoServerK8s("default")).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		// tear down apps
+		Expect(cluster.DeleteNamespace(TestNamespace)).To(Succeed())
+		// tear down Kuma
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		// tear down cluster
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("Should deploy two apps", func() {
+		pods, err := k8s.ListPodsE(
+			cluster.GetTesting(),
+			cluster.GetKubectlOptions(TestNamespace),
+			metav1.ListOptions{
+				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
+			},
+		)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(pods).To(HaveLen(1))
+
+		clientPod := pods[0]
+
+		Eventually(func() (string, error) {
+			_, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "echo-server")
+			return stderr, err
+		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
+
+		Eventually(func() (string, error) {
+			_, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_80.mesh")
+			return stderr, err
+		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
+	})
+}
diff --git a/test/e2e/helm/kuma_helm_deploy_multi_apps_test.go b/test/e2e/helm/kuma_helm_deploy_multi_apps_test.go
index 853f47f0d..e3d387788 100644
--- a/test/e2e/helm/kuma_helm_deploy_multi_apps_test.go
+++ b/test/e2e/helm/kuma_helm_deploy_multi_apps_test.go
@@ -1,118 +1,9 @@
 package helm_test
 
 import (
-	"fmt"
-	"strings"
-	"time"
-
-	"github.com/gruntwork-io/terratest/modules/k8s"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"github.com/gruntwork-io/terratest/modules/random"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/helm"
 )
 
-var _ = Describe("Test App deployment with Helm chart", func() {
-
-	namespaceWithSidecarInjection := func(namespace string) string {
-		return fmt.Sprintf(`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: %s
-  annotations:
-    kuma.io/sidecar-injection: "enabled"
-`, namespace)
-	}
-
-	defaultMesh := `
-apiVersion: kuma.io/v1alpha1
-kind: Mesh
-metadata:
-  name: default
-`
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		c, err := NewK8sClusterWithTimeout(
-			NewTestingT(),
-			Kuma1,
-			Silent,
-			6*time.Second)
-		Expect(err).ToNot(HaveOccurred())
-
-		cluster = c.WithRetries(60)
-
-		releaseName := fmt.Sprintf(
-			"kuma-%s",
-			strings.ToLower(random.UniqueId()),
-		)
-		deployOptsFuncs = []DeployOptionsFunc{
-			WithInstallationMode(HelmInstallationMode),
-			WithHelmReleaseName(releaseName),
-			WithSkipDefaultMesh(true), // it's common case for HELM deployments that Mesh is also managed by HELM therefore it's not created by default
-			WithCPReplicas(3),         // test HA capability
-			WithCNI(),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(KumaDNS()).
-			Install(YamlK8s(defaultMesh)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = NewClusterSetup().
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(DemoClientK8s("default")).
-			Install(EchoServerK8s("default")).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		// tear down apps
-		Expect(cluster.DeleteNamespace(TestNamespace)).To(Succeed())
-		// tear down Kuma
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		// tear down cluster
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("Should deploy two apps", func() {
-		pods, err := k8s.ListPodsE(
-			cluster.GetTesting(),
-			cluster.GetKubectlOptions(TestNamespace),
-			metav1.ListOptions{
-				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		clientPod := pods[0]
-
-		Eventually(func() (string, error) {
-			_, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-				"curl", "-v", "-m", "3", "--fail", "echo-server")
-			return stderr, err
-		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
-
-		Eventually(func() (string, error) {
-			_, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_80.mesh")
-			return stderr, err
-		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
-	})
-})
+var _ = Describe("Test App deployment with Helm chart", helm.AppDeploymentWithHelmChart)
diff --git a/test/e2e/helm/kuma_helm_upgrade.go b/test/e2e/helm/kuma_helm_upgrade.go
new file mode 100644
index 000000000..bc7e099af
--- /dev/null
+++ b/test/e2e/helm/kuma_helm_upgrade.go
@@ -0,0 +1,85 @@
+package helm
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/random"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/extensions/table"
+	. "github.com/onsi/gomega"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func UpgradingWithHelmChart() {
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	type testCase struct {
+		initialChartVersion string
+	}
+
+	DescribeTable(
+		"should successfully upgrade Kuma via Helm",
+		func(given testCase) {
+			c, err := NewK8sClusterWithTimeout(
+				NewTestingT(),
+				Kuma1,
+				Silent,
+				6*time.Second)
+			Expect(err).ToNot(HaveOccurred())
+
+			cluster = c.WithRetries(60)
+
+			releaseName := fmt.Sprintf(
+				"kuma-%s",
+				strings.ToLower(random.UniqueId()),
+			)
+
+			deployOptsFuncs = []DeployOptionsFunc{
+				WithInstallationMode(HelmInstallationMode),
+				WithHelmChartPath("kuma/kuma"),
+				WithHelmReleaseName(releaseName),
+				WithHelmChartVersion(given.initialChartVersion),
+				WithoutHelmOpt("global.image.tag"),
+				WithoutHelmOpt("global.image.registry"),
+			}
+
+			err = NewClusterSetup().
+				Install(Kuma(core.Standalone, deployOptsFuncs...)).
+				Setup(cluster)
+			Expect(err).ToNot(HaveOccurred())
+
+			Expect(cluster.VerifyKuma()).To(Succeed())
+
+			k8sCluster := cluster.(*K8sCluster)
+
+			upgradeOptsFuncs := []DeployOptionsFunc{
+				WithHelmReleaseName(releaseName),
+			}
+
+			err = k8sCluster.UpgradeKuma(core.Standalone, upgradeOptsFuncs...)
+			Expect(err).ToNot(HaveOccurred())
+		},
+		Entry("should successfully upgrade from chart v0.4.4", testCase{
+			initialChartVersion: "0.4.4",
+		}),
+		Entry("should successfully upgrade from chart v0.4.5", testCase{
+			initialChartVersion: "0.4.5",
+		}),
+	)
+}
diff --git a/test/e2e/helm/kuma_helm_upgrade_test.go b/test/e2e/helm/kuma_helm_upgrade_test.go
index 7700d1706..d04f0136b 100644
--- a/test/e2e/helm/kuma_helm_upgrade_test.go
+++ b/test/e2e/helm/kuma_helm_upgrade_test.go
@@ -1,85 +1,9 @@
 package helm_test
 
 import (
-	"fmt"
-	"strings"
-	"time"
-
-	"github.com/gruntwork-io/terratest/modules/random"
-
-	"github.com/kumahq/kuma/pkg/config/core"
-
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/ginkgo/extensions/table"
-	. "github.com/onsi/gomega"
 
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/helm"
 )
 
-var _ = Describe("Test upgrading with Helm chart", func() {
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	type testCase struct {
-		initialChartVersion string
-	}
-
-	DescribeTable(
-		"should successfully upgrade Kuma via Helm",
-		func(given testCase) {
-			c, err := NewK8sClusterWithTimeout(
-				NewTestingT(),
-				Kuma1,
-				Silent,
-				6*time.Second)
-			Expect(err).ToNot(HaveOccurred())
-
-			cluster = c.WithRetries(60)
-
-			releaseName := fmt.Sprintf(
-				"kuma-%s",
-				strings.ToLower(random.UniqueId()),
-			)
-
-			deployOptsFuncs = []DeployOptionsFunc{
-				WithInstallationMode(HelmInstallationMode),
-				WithHelmChartPath("kuma/kuma"),
-				WithHelmReleaseName(releaseName),
-				WithHelmChartVersion(given.initialChartVersion),
-				WithoutHelmOpt("global.image.tag"),
-				WithoutHelmOpt("global.image.registry"),
-			}
-
-			err = NewClusterSetup().
-				Install(Kuma(core.Standalone, deployOptsFuncs...)).
-				Setup(cluster)
-			Expect(err).ToNot(HaveOccurred())
-
-			Expect(cluster.VerifyKuma()).To(Succeed())
-
-			k8sCluster := cluster.(*K8sCluster)
-
-			upgradeOptsFuncs := []DeployOptionsFunc{
-				WithHelmReleaseName(releaseName),
-			}
-
-			err = k8sCluster.UpgradeKuma(core.Standalone, upgradeOptsFuncs...)
-			Expect(err).ToNot(HaveOccurred())
-		},
-		Entry("should successfully upgrade from chart v0.4.4", testCase{
-			initialChartVersion: "0.4.4",
-		}),
-		Entry("should successfully upgrade from chart v0.4.5", testCase{
-			initialChartVersion: "0.4.5",
-		}),
-	)
-})
+var _ = Describe("Test upgrading with Helm chart", helm.UpgradingWithHelmChart)
diff --git a/test/e2e/hybrid/foo.go b/test/e2e/hybrid/foo.go
deleted file mode 100644
index dde53f0ae..000000000
--- a/test/e2e/hybrid/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package hybrid
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/hybrid/deploy_suite_test.go b/test/e2e/hybrid/hybrid_suite_test.go
similarity index 77%
rename from test/e2e/hybrid/deploy_suite_test.go
rename to test/e2e/hybrid/hybrid_suite_test.go
index 4638c4914..18d36065e 100644
--- a/test/e2e/hybrid/deploy_suite_test.go
+++ b/test/e2e/hybrid/hybrid_suite_test.go
@@ -3,8 +3,6 @@ package hybrid_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/hybrid/kuma_hybrid.go b/test/e2e/hybrid/kuma_hybrid.go
new file mode 100644
index 000000000..45df17e3e
--- /dev/null
+++ b/test/e2e/hybrid/kuma_hybrid.go
@@ -0,0 +1,304 @@
+package hybrid
+
+import (
+	"fmt"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func KubernetesUniversalDeployment() {
+	if IsApiV2() {
+		fmt.Println("Test not supported on API v2")
+		return
+	}
+
+	meshMTLSOn := func(mesh string) string {
+		return fmt.Sprintf(`
+type: Mesh
+name: %s
+mtls:
+  enabledBackend: ca-1
+  backends:
+  - name: ca-1
+    type: builtin
+`, mesh)
+	}
+
+	trafficPermissionAllTo2Remote := func(mesh string) string {
+		return fmt.Sprintf(`
+type: TrafficPermission
+name: all-to-2-remote
+mesh: %s
+sources:
+- match:
+   kuma.io/service: "*"
+destinations:
+- match:
+   kuma.io/service: "*"
+   kuma.io/zone: kuma-2-remote
+`, mesh)
+	}
+
+	namespaceWithSidecarInjection := func(namespace string) string {
+		return fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace)
+	}
+
+	var global, remote_1, remote_2, remote_3, remote_4 Cluster
+	var optsGlobal, optsRemote1, optsRemote2, optsRemote3, optsRemote4 []DeployOptionsFunc
+
+	const nonDefaultMesh = "non-default"
+	const defaultMesh = "default"
+
+	BeforeEach(func() {
+		k8sClusters, err := NewK8sClusters(
+			[]string{Kuma1, Kuma2},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		universalClusters, err := NewUniversalClusters(
+			[]string{Kuma3, Kuma4, Kuma5},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Global
+		global = universalClusters.GetCluster(Kuma5)
+		optsGlobal = []DeployOptionsFunc{}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Global, optsGlobal...)).
+			Install(YamlUniversal(meshMTLSOn(nonDefaultMesh))).
+			Install(YamlUniversal(meshMTLSOn(defaultMesh))).
+			Setup(global)
+		Expect(err).ToNot(HaveOccurred())
+		err = global.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		globalCP := global.GetKuma()
+
+		echoServerToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+		ingressToken, err := globalCP.GenerateDpToken(defaultMesh, "ingress")
+		Expect(err).ToNot(HaveOccurred())
+
+		// K8s Cluster 1
+		remote_1 = k8sClusters.GetCluster(Kuma1)
+		optsRemote1 = []DeployOptionsFunc{
+			WithIngress(),
+			WithGlobalAddress(globalCP.GetKDSServerAddress()),
+			WithCNI(),
+			WithEnv("KUMA_RUNTIME_KUBERNETES_INJECTOR_BUILTIN_DNS_ENABLED", "true"),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote1...)).
+			Install(KumaDNS()).
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(DemoClientK8s(nonDefaultMesh)).
+			Setup(remote_1)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_1.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		// K8s Cluster 2
+		remote_2 = k8sClusters.GetCluster(Kuma2)
+		optsRemote2 = []DeployOptionsFunc{
+			WithIngress(),
+			WithGlobalAddress(globalCP.GetKDSServerAddress()),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote2...)).
+			Install(KumaDNS()).
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(EchoServerK8s(nonDefaultMesh)).
+			Install(DemoClientK8s(nonDefaultMesh)).
+			Setup(remote_2)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_2.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		// Universal Cluster 3
+		remote_3 = universalClusters.GetCluster(Kuma3)
+		optsRemote3 = []DeployOptionsFunc{
+			WithGlobalAddress(globalCP.GetKDSServerAddress()),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote3...)).
+			Install(EchoServerUniversal(AppModeEchoServer, nonDefaultMesh, "universal", echoServerToken, WithTransparentProxy(true))).
+			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken, WithTransparentProxy(true))).
+			Install(IngressUniversal(defaultMesh, ingressToken)).
+			Setup(remote_3)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_3.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		// Universal Cluster 4
+		remote_4 = universalClusters.GetCluster(Kuma4)
+		optsRemote4 = []DeployOptionsFunc{
+			WithGlobalAddress(globalCP.GetKDSServerAddress()),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote4...)).
+			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken)).
+			Install(IngressUniversal(defaultMesh, ingressToken)).
+			Setup(remote_4)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_4.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		err := remote_1.DeleteNamespace(TestNamespace)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_1.DeleteKuma(optsRemote1...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_1.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = remote_2.DeleteNamespace(TestNamespace)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_2.DeleteKuma(optsRemote2...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_2.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = remote_3.DeleteKuma(optsRemote3...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_3.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = remote_4.DeleteKuma(optsRemote4...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remote_4.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = global.DeleteKuma(optsGlobal...)
+		Expect(err).ToNot(HaveOccurred())
+		err = global.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should access allservices", func() {
+		// Remote 1
+		pods, err := k8s.ListPodsE(
+			remote_1.GetTesting(),
+			remote_1.GetKubectlOptions(TestNamespace),
+			metav1.ListOptions{
+				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
+			},
+		)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(pods).To(HaveLen(1))
+
+		clientPod := pods[0]
+
+		// k8s access remote k8s service
+		_, stderr, err := remote_1.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_80.mesh")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		// Remote 2
+		pods, err = k8s.ListPodsE(
+			remote_2.GetTesting(),
+			remote_2.GetKubectlOptions(TestNamespace),
+			metav1.ListOptions{
+				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
+			},
+		)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(pods).To(HaveLen(1))
+
+		clientPod = pods[0]
+
+		// k8s access remote universal service
+		_, stderr, err = remote_2.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		// Remote 3
+		// universal access remote k8s service
+		stdout, _, err := remote_3.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		// Remote 4
+		// universal access remote universal service
+		stdout, _, err = remote_4.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "localhost:4001")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		// Remote 1
+		// check for job support
+		// k8s access remote k8s service
+		err = DemoClientJobK8s(nonDefaultMesh, "echo-server_kuma-test_svc_80.mesh")(remote_1)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Remote 2
+		// k8s access remote universal service
+		err = DemoClientJobK8s(nonDefaultMesh, "echo-server_kuma-test_svc_8080.mesh")(remote_2)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should sync traffic permissions", func() {
+		// Remote 4
+		// universal access remote universal service
+		Eventually(func() (string, error) {
+			stdout, _, err := remote_4.ExecWithRetries("", "", "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "localhost:4001")
+			return stdout, err
+		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
+
+		err := global.GetKumactlOptions().KumactlDelete("traffic-permission", "allow-all-non-default", nonDefaultMesh) // remove builtin traffic permission
+		Expect(err).ToNot(HaveOccurred())
+
+		err = YamlUniversal(trafficPermissionAllTo2Remote(nonDefaultMesh))(global)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Remote 3
+		// universal access remote k8s service
+		Eventually(func() (string, error) {
+			stdout, _, err := remote_3.ExecWithRetries("", "", "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+			return stdout, err
+		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
+
+		// Remote 4
+		// universal can't access remote universal service
+		Eventually(func() (string, error) {
+			stdout, _, err := remote_4.ExecWithRetries("", "", "demo-client",
+				"curl", "-v", "-m", "3", "localhost:4001")
+			return stdout, err
+		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 503 Service Unavailable"))
+
+		// Remote 1
+		// check for failing job support
+		// k8s can not access remote k8s service
+		err = DemoClientJobK8s(nonDefaultMesh, "echo-server_kuma-test_svc_8080.mesh")(remote_1)
+		Expect(err).To(HaveOccurred())
+	})
+}
diff --git a/test/e2e/hybrid/kuma_hybrid_kube_global.go b/test/e2e/hybrid/kuma_hybrid_kube_global.go
new file mode 100644
index 000000000..feba259b2
--- /dev/null
+++ b/test/e2e/hybrid/kuma_hybrid_kube_global.go
@@ -0,0 +1,101 @@
+package hybrid
+
+import (
+	"strings"
+
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func KubernetesUniversalDeploymentWhenGlobalIsOnK8S() {
+	var globalCluster, remoteCluster Cluster
+	var optsGlobal, optsRemote []DeployOptionsFunc
+
+	BeforeEach(func() {
+		k8sClusters, err := NewK8sClusters(
+			[]string{Kuma1},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		universalClusters, err := NewUniversalClusters(
+			[]string{Kuma3},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Global
+		globalCluster = k8sClusters.GetCluster(Kuma1)
+		optsGlobal = []DeployOptionsFunc{}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Global, optsGlobal...)).
+			Setup(globalCluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = globalCluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+		globalCP := globalCluster.GetKuma()
+
+		echoServerToken, err := globalCP.GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := globalCP.GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+		ingressToken, err := globalCP.GenerateDpToken("default", "ingress")
+		Expect(err).ToNot(HaveOccurred())
+
+		// Remote
+		remoteCluster = universalClusters.GetCluster(Kuma3)
+		optsRemote = []DeployOptionsFunc{
+			WithGlobalAddress(globalCP.GetKDSServerAddress()),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Remote, optsRemote...)).
+			Install(EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken)).
+			Install(DemoClientUniversal(AppModeDemoClient, "default", demoClientToken)).
+			Install(IngressUniversal("default", ingressToken)).
+			Setup(remoteCluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = remoteCluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		err := globalCluster.DeleteKuma(optsGlobal...)
+		Expect(err).ToNot(HaveOccurred())
+		err = globalCluster.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+
+		err = remoteCluster.DeleteKuma(optsRemote...)
+		Expect(err).ToNot(HaveOccurred())
+		err = remoteCluster.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("communication in between apps in remote zone works", func() {
+		stdout, _, err := remoteCluster.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "localhost:4001")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		retry.DoWithRetry(remoteCluster.GetTesting(), "curl remote service",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err = remoteCluster.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "localhost:4001")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+	})
+}
diff --git a/test/e2e/hybrid/kuma_hybrid_kube_global_test.go b/test/e2e/hybrid/kuma_hybrid_kube_global_test.go
index 6200528a9..2bee4ee62 100644
--- a/test/e2e/hybrid/kuma_hybrid_kube_global_test.go
+++ b/test/e2e/hybrid/kuma_hybrid_kube_global_test.go
@@ -1,102 +1,9 @@
 package hybrid_test
 
 import (
-	"strings"
-
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"github.com/pkg/errors"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/hybrid"
 )
 
-var _ = Describe("Test Kubernetes/Universal deployment when Global is on K8S", func() {
-
-	var globalCluster, remoteCluster Cluster
-	var optsGlobal, optsRemote []DeployOptionsFunc
-
-	BeforeEach(func() {
-		k8sClusters, err := NewK8sClusters(
-			[]string{Kuma1},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		universalClusters, err := NewUniversalClusters(
-			[]string{Kuma3},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Global
-		globalCluster = k8sClusters.GetCluster(Kuma1)
-		optsGlobal = []DeployOptionsFunc{}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Global, optsGlobal...)).
-			Setup(globalCluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = globalCluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-		globalCP := globalCluster.GetKuma()
-
-		echoServerToken, err := globalCP.GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := globalCP.GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-		ingressToken, err := globalCP.GenerateDpToken("default", "ingress")
-		Expect(err).ToNot(HaveOccurred())
-
-		// Remote
-		remoteCluster = universalClusters.GetCluster(Kuma3)
-		optsRemote = []DeployOptionsFunc{
-			WithGlobalAddress(globalCP.GetKDSServerAddress()),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote...)).
-			Install(EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken)).
-			Install(DemoClientUniversal(AppModeDemoClient, "default", demoClientToken)).
-			Install(IngressUniversal("default", ingressToken)).
-			Setup(remoteCluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = remoteCluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		err := globalCluster.DeleteKuma(optsGlobal...)
-		Expect(err).ToNot(HaveOccurred())
-		err = globalCluster.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = remoteCluster.DeleteKuma(optsRemote...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remoteCluster.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("communication in between apps in remote zone works", func() {
-		stdout, _, err := remoteCluster.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "localhost:4001")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-
-		retry.DoWithRetry(remoteCluster.GetTesting(), "curl remote service",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err = remoteCluster.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "localhost:4001")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-	})
-})
+var _ = Describe("Test Kubernetes/Universal deployment when Global is on K8S", hybrid.KubernetesUniversalDeploymentWhenGlobalIsOnK8S)
diff --git a/test/e2e/hybrid/kuma_hybrid_test.go b/test/e2e/hybrid/kuma_hybrid_test.go
index ce957f524..901f3f078 100644
--- a/test/e2e/hybrid/kuma_hybrid_test.go
+++ b/test/e2e/hybrid/kuma_hybrid_test.go
@@ -1,304 +1,9 @@
 package hybrid_test
 
 import (
-	"fmt"
+	"github.com/kumahq/kuma/test/e2e/hybrid"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-
-	"github.com/gruntwork-io/terratest/modules/k8s"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	. "github.com/kumahq/kuma/test/framework"
 )
 
-var _ = Describe("Test Kubernetes/Universal deployment", func() {
-	if IsApiV2() {
-		fmt.Println("Test not supported on API v2")
-		return
-	}
-	meshMTLSOn := func(mesh string) string {
-		return fmt.Sprintf(`
-type: Mesh
-name: %s
-mtls:
-  enabledBackend: ca-1
-  backends:
-  - name: ca-1
-    type: builtin
-`, mesh)
-	}
-
-	trafficPermissionAllTo2Remote := func(mesh string) string {
-		return fmt.Sprintf(`
-type: TrafficPermission
-name: all-to-2-remote
-mesh: %s
-sources:
-- match:
-   kuma.io/service: "*"
-destinations:
-- match:
-   kuma.io/service: "*"
-   kuma.io/zone: kuma-2-remote
-`, mesh)
-	}
-
-	namespaceWithSidecarInjection := func(namespace string) string {
-		return fmt.Sprintf(`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: %s
-  annotations:
-    kuma.io/sidecar-injection: "enabled"
-`, namespace)
-	}
-
-	var global, remote_1, remote_2, remote_3, remote_4 Cluster
-	var optsGlobal, optsRemote1, optsRemote2, optsRemote3, optsRemote4 []DeployOptionsFunc
-
-	const nonDefaultMesh = "non-default"
-	const defaultMesh = "default"
-
-	BeforeEach(func() {
-		k8sClusters, err := NewK8sClusters(
-			[]string{Kuma1, Kuma2},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		universalClusters, err := NewUniversalClusters(
-			[]string{Kuma3, Kuma4, Kuma5},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Global
-		global = universalClusters.GetCluster(Kuma5)
-		optsGlobal = []DeployOptionsFunc{}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Global, optsGlobal...)).
-			Install(YamlUniversal(meshMTLSOn(nonDefaultMesh))).
-			Install(YamlUniversal(meshMTLSOn(defaultMesh))).
-			Setup(global)
-		Expect(err).ToNot(HaveOccurred())
-		err = global.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		globalCP := global.GetKuma()
-
-		echoServerToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := globalCP.GenerateDpToken(nonDefaultMesh, "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-		ingressToken, err := globalCP.GenerateDpToken(defaultMesh, "ingress")
-		Expect(err).ToNot(HaveOccurred())
-
-		// K8s Cluster 1
-		remote_1 = k8sClusters.GetCluster(Kuma1)
-		optsRemote1 = []DeployOptionsFunc{
-			WithIngress(),
-			WithGlobalAddress(globalCP.GetKDSServerAddress()),
-			WithCNI(),
-			WithEnv("KUMA_RUNTIME_KUBERNETES_INJECTOR_BUILTIN_DNS_ENABLED", "true"),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote1...)).
-			Install(KumaDNS()).
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(DemoClientK8s(nonDefaultMesh)).
-			Setup(remote_1)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_1.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		// K8s Cluster 2
-		remote_2 = k8sClusters.GetCluster(Kuma2)
-		optsRemote2 = []DeployOptionsFunc{
-			WithIngress(),
-			WithGlobalAddress(globalCP.GetKDSServerAddress()),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote2...)).
-			Install(KumaDNS()).
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(EchoServerK8s(nonDefaultMesh)).
-			Install(DemoClientK8s(nonDefaultMesh)).
-			Setup(remote_2)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_2.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		// Universal Cluster 3
-		remote_3 = universalClusters.GetCluster(Kuma3)
-		optsRemote3 = []DeployOptionsFunc{
-			WithGlobalAddress(globalCP.GetKDSServerAddress()),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote3...)).
-			Install(EchoServerUniversal(AppModeEchoServer, nonDefaultMesh, "universal", echoServerToken, WithTransparentProxy(true))).
-			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken, WithTransparentProxy(true))).
-			Install(IngressUniversal(defaultMesh, ingressToken)).
-			Setup(remote_3)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_3.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		// Universal Cluster 4
-		remote_4 = universalClusters.GetCluster(Kuma4)
-		optsRemote4 = []DeployOptionsFunc{
-			WithGlobalAddress(globalCP.GetKDSServerAddress()),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Remote, optsRemote4...)).
-			Install(DemoClientUniversal(AppModeDemoClient, nonDefaultMesh, demoClientToken)).
-			Install(IngressUniversal(defaultMesh, ingressToken)).
-			Setup(remote_4)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_4.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		err := remote_1.DeleteNamespace(TestNamespace)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_1.DeleteKuma(optsRemote1...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_1.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = remote_2.DeleteNamespace(TestNamespace)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_2.DeleteKuma(optsRemote2...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_2.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = remote_3.DeleteKuma(optsRemote3...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_3.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = remote_4.DeleteKuma(optsRemote4...)
-		Expect(err).ToNot(HaveOccurred())
-		err = remote_4.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-
-		err = global.DeleteKuma(optsGlobal...)
-		Expect(err).ToNot(HaveOccurred())
-		err = global.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should access allservices", func() {
-		// Remote 1
-		pods, err := k8s.ListPodsE(
-			remote_1.GetTesting(),
-			remote_1.GetKubectlOptions(TestNamespace),
-			metav1.ListOptions{
-				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		clientPod := pods[0]
-
-		// k8s access remote k8s service
-		_, stderr, err := remote_1.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_80.mesh")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
-
-		// Remote 2
-		pods, err = k8s.ListPodsE(
-			remote_2.GetTesting(),
-			remote_2.GetKubectlOptions(TestNamespace),
-			metav1.ListOptions{
-				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		clientPod = pods[0]
-
-		// k8s access remote universal service
-		_, stderr, err = remote_2.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
-
-		// Remote 3
-		// universal access remote k8s service
-		stdout, _, err := remote_3.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-
-		// Remote 4
-		// universal access remote universal service
-		stdout, _, err = remote_4.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "-m", "3", "--fail", "localhost:4001")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-
-		// Remote 1
-		// check for job support
-		// k8s access remote k8s service
-		err = DemoClientJobK8s(nonDefaultMesh, "echo-server_kuma-test_svc_80.mesh")(remote_1)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Remote 2
-		// k8s access remote universal service
-		err = DemoClientJobK8s(nonDefaultMesh, "echo-server_kuma-test_svc_8080.mesh")(remote_2)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync traffic permissions", func() {
-		// Remote 4
-		// universal access remote universal service
-		Eventually(func() (string, error) {
-			stdout, _, err := remote_4.ExecWithRetries("", "", "demo-client",
-				"curl", "-v", "-m", "3", "--fail", "localhost:4001")
-			return stdout, err
-		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
-
-		err := global.GetKumactlOptions().KumactlDelete("traffic-permission", "allow-all-non-default", nonDefaultMesh) // remove builtin traffic permission
-		Expect(err).ToNot(HaveOccurred())
-
-		err = YamlUniversal(trafficPermissionAllTo2Remote(nonDefaultMesh))(global)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Remote 3
-		// universal access remote k8s service
-		Eventually(func() (string, error) {
-			stdout, _, err := remote_3.ExecWithRetries("", "", "demo-client",
-				"curl", "-v", "-m", "3", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-			return stdout, err
-		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 200 OK"))
-
-		// Remote 4
-		// universal can't access remote universal service
-		Eventually(func() (string, error) {
-			stdout, _, err := remote_4.ExecWithRetries("", "", "demo-client",
-				"curl", "-v", "-m", "3", "localhost:4001")
-			return stdout, err
-		}, "10s", "1s").Should(ContainSubstring("HTTP/1.1 503 Service Unavailable"))
-
-		// Remote 1
-		// check for failing job support
-		// k8s can not access remote k8s service
-		err = DemoClientJobK8s(nonDefaultMesh, "echo-server_kuma-test_svc_8080.mesh")(remote_1)
-		Expect(err).To(HaveOccurred())
-	})
-
-})
+var _ = Describe("Test Kubernetes/Universal deployment", hybrid.KubernetesUniversalDeployment)
diff --git a/test/e2e/retry/foo.go b/test/e2e/retry/foo.go
deleted file mode 100644
index ee8d867ec..000000000
--- a/test/e2e/retry/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package retry
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/retry/retry_suite_test.go b/test/e2e/retry/retry_suite_test.go
index 0d216e2f3..269c896e2 100644
--- a/test/e2e/retry/retry_suite_test.go
+++ b/test/e2e/retry/retry_suite_test.go
@@ -3,8 +3,6 @@ package retry_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/retry/retry_universal.go b/test/e2e/retry/retry_universal.go
new file mode 100644
index 000000000..c8f79e5b5
--- /dev/null
+++ b/test/e2e/retry/retry_universal.go
@@ -0,0 +1,146 @@
+package retry
+
+import (
+	"strings"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func RetryOnUniversal() {
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		clusters, err := NewUniversalClusters(
+			[]string{Kuma1},
+			Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		// Global
+		cluster = clusters.GetCluster(Kuma1)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = cluster.GetKumactlOptions().RunKumactl("delete", "retry", "retry-all-default")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = NewClusterSetup().
+			Install(DemoClientUniversal(AppModeDemoClient, "default", demoClientToken)).
+			Install(EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken)).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		err := cluster.DeleteKuma(deployOptsFuncs...)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = cluster.DismissCluster()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should retry on TCP connection failure", func() {
+		echoServerDataplane := `
+type: Dataplane
+mesh: default
+name: fake-echo-server
+networking:
+  address:  241.0.0.1
+  inbound:
+  - port: 7777
+    servicePort: 7777
+    tags:
+      kuma.io/service: echo-server_kuma-test_svc_8080
+      kuma.io/protocol: http
+`
+		retryPolicy := `
+type: Retry
+mesh: default
+name: fake-retry-policy
+sources:
+- match:
+    kuma.io/service: demo-client
+destinations:
+- match:
+    kuma.io/service: echo-server_kuma-test_svc_8080
+conf:
+  http:
+    numRetries: 5
+`
+
+		retry.DoWithRetry(cluster.GetTesting(), "curl local service",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
+					"curl", "-v", "-m", "3", "--fail", "localhost:4001")
+				if err != nil {
+					return "should retry", err
+				}
+				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
+					return "Accessing service successful", nil
+				}
+				return "should retry", errors.Errorf("should retry")
+			})
+
+		for i := 0; i < 10; i++ {
+			// -m 8 to wait for 8 seconds to beat the default 5s connect timeout
+			stdout, stderr, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "8", "--fail", "localhost:4001")
+			Expect(err).ToNot(HaveOccurred())
+			Expect(stderr).To(BeEmpty())
+			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+		}
+
+		err := YamlUniversal(echoServerDataplane)(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		time.Sleep(5 * time.Second)
+
+		var errs []error
+
+		for i := 0; i < 10; i++ {
+			_, _, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "8", "--fail", "localhost:4001")
+
+			if err != nil {
+				errs = append(errs, err)
+			}
+		}
+
+		Expect(errs).ToNot(BeEmpty())
+
+		err = YamlUniversal(retryPolicy)(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		time.Sleep(5 * time.Second)
+
+		for i := 0; i < 10; i++ {
+			stdout, stderr, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "8", "--fail", "localhost:4001")
+			Expect(err).ToNot(HaveOccurred())
+			Expect(stderr).To(BeEmpty())
+			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+		}
+	})
+}
diff --git a/test/e2e/retry/retry_universal_test.go b/test/e2e/retry/retry_universal_test.go
index b71121982..57963a7c4 100644
--- a/test/e2e/retry/retry_universal_test.go
+++ b/test/e2e/retry/retry_universal_test.go
@@ -1,146 +1,9 @@
 package retry_test
 
 import (
-	"strings"
-	"time"
-
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"github.com/pkg/errors"
-
-	"github.com/kumahq/kuma/pkg/config/core"
 
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/retry"
 )
 
-var _ = Describe("Test Retry on Universal", func() {
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		clusters, err := NewUniversalClusters(
-			[]string{Kuma1},
-			Silent)
-		Expect(err).ToNot(HaveOccurred())
-
-		// Global
-		cluster = clusters.GetCluster(Kuma1)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = cluster.GetKumactlOptions().RunKumactl("delete", "retry", "retry-all-default")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = NewClusterSetup().
-			Install(DemoClientUniversal(AppModeDemoClient, "default", demoClientToken)).
-			Install(EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken)).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		err := cluster.DeleteKuma(deployOptsFuncs...)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = cluster.DismissCluster()
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should retry on TCP connection failure", func() {
-		echoServerDataplane := `
-type: Dataplane
-mesh: default
-name: fake-echo-server
-networking:
-  address:  241.0.0.1
-  inbound:
-  - port: 7777
-    servicePort: 7777
-    tags:
-      kuma.io/service: echo-server_kuma-test_svc_8080
-      kuma.io/protocol: http
-`
-		retryPolicy := `
-type: Retry
-mesh: default
-name: fake-retry-policy
-sources:
-- match:
-    kuma.io/service: demo-client
-destinations:
-- match:
-    kuma.io/service: echo-server_kuma-test_svc_8080
-conf:
-  http:
-    numRetries: 5
-`
-
-		retry.DoWithRetry(cluster.GetTesting(), "curl local service",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				stdout, _, err := cluster.ExecWithRetries("", "", "demo-client",
-					"curl", "-v", "-m", "3", "--fail", "localhost:4001")
-				if err != nil {
-					return "should retry", err
-				}
-				if strings.Contains(stdout, "HTTP/1.1 200 OK") {
-					return "Accessing service successful", nil
-				}
-				return "should retry", errors.Errorf("should retry")
-			})
-
-		for i := 0; i < 10; i++ {
-			// -m 8 to wait for 8 seconds to beat the default 5s connect timeout
-			stdout, stderr, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "8", "--fail", "localhost:4001")
-			Expect(err).ToNot(HaveOccurred())
-			Expect(stderr).To(BeEmpty())
-			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-		}
-
-		err := YamlUniversal(echoServerDataplane)(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		time.Sleep(5 * time.Second)
-
-		var errs []error
-
-		for i := 0; i < 10; i++ {
-			_, _, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "8", "--fail", "localhost:4001")
-
-			if err != nil {
-				errs = append(errs, err)
-			}
-		}
-
-		Expect(errs).ToNot(BeEmpty())
-
-		err = YamlUniversal(retryPolicy)(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		time.Sleep(5 * time.Second)
-
-		for i := 0; i < 10; i++ {
-			stdout, stderr, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "8", "--fail", "localhost:4001")
-			Expect(err).ToNot(HaveOccurred())
-			Expect(stderr).To(BeEmpty())
-			Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-		}
-	})
-})
+var _ = Describe("Test Retry on Universal", retry.RetryOnUniversal)
diff --git a/test/e2e/timeout/foo.go b/test/e2e/timeout/foo.go
deleted file mode 100644
index 2871d77a6..000000000
--- a/test/e2e/timeout/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package timeout
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/timeout/e2e_suite_test.go b/test/e2e/timeout/timeout_suite_test.go
similarity index 78%
rename from test/e2e/timeout/e2e_suite_test.go
rename to test/e2e/timeout/timeout_suite_test.go
index 1c1100fae..50b03deeb 100644
--- a/test/e2e/timeout/e2e_suite_test.go
+++ b/test/e2e/timeout/timeout_suite_test.go
@@ -3,8 +3,6 @@ package timeout_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/timeout/timeout_universal.go b/test/e2e/timeout/timeout_universal.go
new file mode 100644
index 000000000..160f31a15
--- /dev/null
+++ b/test/e2e/timeout/timeout_universal.go
@@ -0,0 +1,107 @@
+package timeout
+
+import (
+	"strings"
+	"time"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+func TimeoutPolicyOnUniversal() {
+	var universalCluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	faultInjection := `
+type: FaultInjection
+mesh: default
+name: fi1
+sources:
+   - match:
+       kuma.io/service: demo-client
+destinations:
+   - match:
+       kuma.io/service: echo-server_kuma-test_svc_8080
+       kuma.io/protocol: http
+conf:
+   delay:
+     percentage: 100
+     value: 5s
+`
+
+	timeout := `
+type: Timeout
+mesh: default
+name: echo-service-timeouts
+sources:
+- match:
+    kuma.io/service: '*'
+destinations:
+- match:
+    kuma.io/service: echo-server_kuma-test_svc_8080
+conf:
+  connectTimeout: 10s
+  http:
+    requestTimeout: 2s
+`
+
+	BeforeEach(func() {
+		universalCluster = NewUniversalCluster(NewTestingT(), Kuma1, Silent)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err := NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(YamlUniversal(faultInjection)).
+			Setup(universalCluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = universalCluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := universalCluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := universalCluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = EchoServerUniversal(AppModeEchoServer, "default", "universal-1", echoServerToken)(universalCluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true))(universalCluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(universalCluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(universalCluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should reset the connection by timeout", func() {
+		// check echo-server is up and running
+		stdout, _, err := universalCluster.ExecWithRetries("", "", "demo-client",
+			"curl", "-v", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		start := time.Now()
+		_, _, err = universalCluster.Exec("", "", "demo-client",
+			"curl", "-v", "--fail", "echo-server_kuma-test_svc_8080.mesh")
+		Expect(err).ToNot(HaveOccurred())
+		elapsed := time.Since(start)
+		Expect(elapsed > 5*time.Second).To(BeTrue())
+
+		// when apply Timeout policy
+		err = NewClusterSetup().Install(YamlUniversal(timeout)).Setup(universalCluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then
+		Eventually(func() bool {
+			stdout, _, _ := universalCluster.Exec("", "", "demo-client",
+				"curl", "-v", "echo-server_kuma-test_svc_8080.mesh")
+			return strings.Contains(stdout, "upstream request timeout")
+		}, "30s", "500ms").Should(BeTrue())
+	})
+}
diff --git a/test/e2e/timeout/timeout_universal_test.go b/test/e2e/timeout/timeout_universal_test.go
index 065da36a3..c7d6a5050 100644
--- a/test/e2e/timeout/timeout_universal_test.go
+++ b/test/e2e/timeout/timeout_universal_test.go
@@ -1,108 +1,9 @@
 package timeout_test
 
 import (
-	"strings"
-	"time"
-
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/e2e/timeout"
 )
 
-var _ = Describe("Test Timeout policy on Universal", func() {
-
-	var universalCluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	faultInjection := `
-type: FaultInjection
-mesh: default
-name: fi1
-sources:
-   - match:
-       kuma.io/service: demo-client
-destinations:
-   - match:
-       kuma.io/service: echo-server_kuma-test_svc_8080
-       kuma.io/protocol: http
-conf:
-   delay:
-     percentage: 100
-     value: 5s
-`
-
-	timeout := `
-type: Timeout
-mesh: default
-name: echo-service-timeouts
-sources:
-- match:
-    kuma.io/service: '*'
-destinations:
-- match:
-    kuma.io/service: echo-server_kuma-test_svc_8080
-conf:
-  connectTimeout: 10s
-  http:
-    requestTimeout: 2s
-`
-
-	BeforeEach(func() {
-		universalCluster = NewUniversalCluster(NewTestingT(), Kuma1, Silent)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err := NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(YamlUniversal(faultInjection)).
-			Setup(universalCluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = universalCluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := universalCluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := universalCluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = EchoServerUniversal(AppModeEchoServer, "default", "universal-1", echoServerToken)(universalCluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken, WithTransparentProxy(true))(universalCluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(universalCluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(universalCluster.DismissCluster()).To(Succeed())
-	})
-
-	It("should reset the connection by timeout", func() {
-		// check echo-server is up and running
-		stdout, _, err := universalCluster.ExecWithRetries("", "", "demo-client",
-			"curl", "-v", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-		Expect(err).ToNot(HaveOccurred())
-		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
-
-		start := time.Now()
-		_, _, err = universalCluster.Exec("", "", "demo-client",
-			"curl", "-v", "--fail", "echo-server_kuma-test_svc_8080.mesh")
-		Expect(err).ToNot(HaveOccurred())
-		elapsed := time.Since(start)
-		Expect(elapsed > 5*time.Second).To(BeTrue())
-
-		// when apply Timeout policy
-		err = NewClusterSetup().Install(YamlUniversal(timeout)).Setup(universalCluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// then
-		Eventually(func() bool {
-			stdout, _, _ := universalCluster.Exec("", "", "demo-client",
-				"curl", "-v", "echo-server_kuma-test_svc_8080.mesh")
-			return strings.Contains(stdout, "upstream request timeout")
-		}, "30s", "500ms").Should(BeTrue())
-	})
-})
+var _ = Describe("Test Timeout policy on Universal", timeout.TimeoutPolicyOnUniversal)
diff --git a/test/e2e/tracing/foo.go b/test/e2e/tracing/foo.go
deleted file mode 100644
index eb64d15c9..000000000
--- a/test/e2e/tracing/foo.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package tracing
-
-// Keep this one empty here to make go test happy that there are non-test files in this folder
diff --git a/test/e2e/tracing/tracing_kubernetes.go b/test/e2e/tracing/tracing_kubernetes.go
new file mode 100644
index 000000000..54f9ec8cd
--- /dev/null
+++ b/test/e2e/tracing/tracing_kubernetes.go
@@ -0,0 +1,139 @@
+package tracing
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/framework/deployments/tracing"
+)
+
+func TracingK8S() {
+	if IsApiV2() {
+		fmt.Println("Test not supported on API v2")
+		return
+	}
+
+	namespaceWithSidecarInjection := func(namespace string) string {
+		return fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace)
+	}
+
+	meshWithTracing := func(zipkinURL string) string {
+		return fmt.Sprintf(`
+apiVersion: kuma.io/v1alpha1
+kind: Mesh
+metadata:
+  name: default
+spec:
+  tracing:
+    defaultBackend: zipkin
+    backends:
+    - name: zipkin
+      type: zipkin
+      conf:
+        url: %s
+`, zipkinURL)
+	}
+
+	traceAll := `
+apiVersion: kuma.io/v1alpha1
+kind: TrafficTrace
+mesh: default
+metadata:
+  namespace: default
+  name: trace-all
+spec:
+  selectors:
+  - match:
+      kuma.io/service: '*'
+`
+
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		c, err := NewK8SCluster(NewTestingT(), Kuma1, Silent)
+		Expect(err).ToNot(HaveOccurred())
+		cluster = c
+		deployOptsFuncs = []DeployOptionsFunc{
+			WithEnv("KUMA_RUNTIME_KUBERNETES_INJECTOR_BUILTIN_DNS_ENABLED", "true"),
+		}
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(KumaDNS()).
+			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
+			Install(DemoClientK8s("default")).
+			Install(EchoServerK8s("default")).
+			Install(tracing.Install()).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(cluster.DeleteNamespace(TestNamespace)).To(Succeed())
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should emit traces to jaeger", func() {
+		// given TrafficTrace and mesh with tracing backend
+		err := YamlK8s(meshWithTracing(tracing.From(cluster).ZipkinCollectorURL()))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = YamlK8s(traceAll)(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// when client sends requests to server
+		pods, err := k8s.ListPodsE(
+			cluster.GetTesting(),
+			cluster.GetKubectlOptions(TestNamespace),
+			metav1.ListOptions{
+				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
+			},
+		)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(pods).To(HaveLen(1))
+
+		clientPod := pods[0]
+
+		retry.DoWithRetry(cluster.GetTesting(), "curl remote service",
+			DefaultRetries, DefaultTimeout,
+			func() (string, error) {
+				_, _, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+					"curl", "-v", "-m", "3", "--fail", "echo-server")
+				if err != nil {
+					return "", err
+				}
+
+				// then traces are published
+				services, err := tracing.From(cluster).TracedServices()
+				if err != nil {
+					return "", err
+				}
+
+				expectedServices := []string{"demo-client_kuma-test_svc", "echo-server_kuma-test_svc_80", "jaeger-query"}
+				if !reflect.DeepEqual(services, expectedServices) {
+					return "", errors.Errorf("services not traced. Expected %q, got %q", expectedServices, services)
+				}
+				return "ok", nil
+			})
+	})
+}
diff --git a/test/e2e/tracing/tracing_kubernetes_test.go b/test/e2e/tracing/tracing_kubernetes_test.go
index 25bdab113..2a761b900 100644
--- a/test/e2e/tracing/tracing_kubernetes_test.go
+++ b/test/e2e/tracing/tracing_kubernetes_test.go
@@ -1,138 +1,9 @@
 package tracing_test
 
 import (
-	"fmt"
-	"reflect"
-
-	"github.com/gruntwork-io/terratest/modules/k8s"
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"github.com/pkg/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
-	"github.com/kumahq/kuma/test/framework/deployments/tracing"
+	"github.com/kumahq/kuma/test/e2e/tracing"
 )
 
-var _ = Describe("Tracing K8S", func() {
-	if IsApiV2() {
-		fmt.Println("Test not supported on API v2")
-		return
-	}
-	namespaceWithSidecarInjection := func(namespace string) string {
-		return fmt.Sprintf(`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: %s
-  annotations:
-    kuma.io/sidecar-injection: "enabled"
-`, namespace)
-	}
-
-	meshWithTracing := func(zipkinURL string) string {
-		return fmt.Sprintf(`
-apiVersion: kuma.io/v1alpha1
-kind: Mesh
-metadata:
-  name: default
-spec:
-  tracing:
-    defaultBackend: zipkin
-    backends:
-    - name: zipkin
-      type: zipkin
-      conf:
-        url: %s
-`, zipkinURL)
-	}
-
-	traceAll := `
-apiVersion: kuma.io/v1alpha1
-kind: TrafficTrace
-mesh: default
-metadata:
-  namespace: default
-  name: trace-all
-spec:
-  selectors:
-  - match:
-      kuma.io/service: '*'
-`
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		c, err := NewK8SCluster(NewTestingT(), Kuma1, Silent)
-		Expect(err).ToNot(HaveOccurred())
-		cluster = c
-		deployOptsFuncs = []DeployOptionsFunc{
-			WithEnv("KUMA_RUNTIME_KUBERNETES_INJECTOR_BUILTIN_DNS_ENABLED", "true"),
-		}
-
-		err = NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(KumaDNS()).
-			Install(YamlK8s(namespaceWithSidecarInjection(TestNamespace))).
-			Install(DemoClientK8s("default")).
-			Install(EchoServerK8s("default")).
-			Install(tracing.Install()).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(cluster.DeleteNamespace(TestNamespace)).To(Succeed())
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("should emit traces to jaeger", func() {
-		// given TrafficTrace and mesh with tracing backend
-		err := YamlK8s(meshWithTracing(tracing.From(cluster).ZipkinCollectorURL()))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = YamlK8s(traceAll)(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		// when client sends requests to server
-		pods, err := k8s.ListPodsE(
-			cluster.GetTesting(),
-			cluster.GetKubectlOptions(TestNamespace),
-			metav1.ListOptions{
-				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		clientPod := pods[0]
-
-		retry.DoWithRetry(cluster.GetTesting(), "curl remote service",
-			DefaultRetries, DefaultTimeout,
-			func() (string, error) {
-				_, _, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
-					"curl", "-v", "-m", "3", "--fail", "echo-server")
-				if err != nil {
-					return "", err
-				}
-
-				// then traces are published
-				services, err := tracing.From(cluster).TracedServices()
-				if err != nil {
-					return "", err
-				}
-
-				expectedServices := []string{"demo-client_kuma-test_svc", "echo-server_kuma-test_svc_80", "jaeger-query"}
-				if !reflect.DeepEqual(services, expectedServices) {
-					return "", errors.Errorf("services not traced. Expected %q, got %q", expectedServices, services)
-				}
-				return "ok", nil
-			})
-	})
-})
+var _ = Describe("Tracing K8S", tracing.TracingK8S)
diff --git a/test/e2e/tracing/tracing_suite_test.go b/test/e2e/tracing/tracing_suite_test.go
index bad9da7db..3b8f653ab 100644
--- a/test/e2e/tracing/tracing_suite_test.go
+++ b/test/e2e/tracing/tracing_suite_test.go
@@ -3,8 +3,6 @@ package tracing_test
 import (
 	"testing"
 
-	ginkgo_config "github.com/onsi/ginkgo/config"
-
 	"github.com/kumahq/kuma/test/framework"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,3 @@ var _ = BeforeSuite(func() {
 	core.SetLogger = func(l logr.Logger) {}
 	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
 })
-
-func ShouldSkipCleanup() bool {
-	return CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
-}
diff --git a/test/e2e/tracing/tracing_universal.go b/test/e2e/tracing/tracing_universal.go
new file mode 100644
index 000000000..dfa56bba2
--- /dev/null
+++ b/test/e2e/tracing/tracing_universal.go
@@ -0,0 +1,102 @@
+package tracing
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/gruntwork-io/terratest/modules/retry"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/framework/deployments/tracing"
+)
+
+func TracingUniversal() {
+	meshWithTracing := func(zipkinURL string) string {
+		return fmt.Sprintf(`
+type: Mesh
+name: default
+tracing:
+  defaultBackend: zipkin
+  backends:
+  - name: zipkin
+    type: zipkin
+    conf:
+      url: %s
+`, zipkinURL)
+	}
+
+	traceAll := `
+type: TrafficTrace
+name: traffic-trace-all
+mesh: default
+selectors:
+- match:
+   kuma.io/service: "*"
+`
+
+	var cluster Cluster
+	var deployOptsFuncs []DeployOptionsFunc
+
+	BeforeEach(func() {
+		cluster = NewUniversalCluster(NewTestingT(), Kuma1, Silent)
+		deployOptsFuncs = []DeployOptionsFunc{}
+
+		err := NewClusterSetup().
+			Install(Kuma(core.Standalone, deployOptsFuncs...)).
+			Install(tracing.Install()).
+			Setup(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = cluster.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+
+		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
+		Expect(err).ToNot(HaveOccurred())
+		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
+		Expect(err).ToNot(HaveOccurred())
+
+		err = EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken)(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken)(cluster)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
+		Expect(cluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should emit traces to jaeger", func() {
+		// given TrafficTrace and mesh with tracing backend
+		err := YamlUniversal(meshWithTracing(tracing.From(cluster).ZipkinCollectorURL()))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+		err = YamlUniversal(traceAll)(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		retry.DoWithRetry(cluster.GetTesting(), "check traced services", DefaultRetries, DefaultTimeout, func() (string, error) {
+			// when client sends requests to server
+			_, _, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "3", "--fail", "localhost:4001")
+			if err != nil {
+				return "", err
+			}
+
+			// then traces are published
+			services, err := tracing.From(cluster).TracedServices()
+			if err != nil {
+				return "", err
+			}
+
+			expectedServices := []string{"demo-client", "echo-server_kuma-test_svc_8080", "jaeger-query"}
+			if !reflect.DeepEqual(services, expectedServices) {
+				return "", errors.Errorf("services not traced. Expected %q, got %q", expectedServices, services)
+			}
+			return "", nil
+		})
+	})
+}
diff --git a/test/e2e/tracing/tracing_universal_test.go b/test/e2e/tracing/tracing_universal_test.go
index 8223a2502..260fd5c81 100644
--- a/test/e2e/tracing/tracing_universal_test.go
+++ b/test/e2e/tracing/tracing_universal_test.go
@@ -1,103 +1,9 @@
 package tracing_test
 
 import (
-	"fmt"
-	"reflect"
-
-	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"github.com/pkg/errors"
 
-	"github.com/kumahq/kuma/pkg/config/core"
-	. "github.com/kumahq/kuma/test/framework"
-	"github.com/kumahq/kuma/test/framework/deployments/tracing"
+	"github.com/kumahq/kuma/test/e2e/tracing"
 )
 
-var _ = Describe("Tracing Universal", func() {
-
-	meshWithTracing := func(zipkinURL string) string {
-		return fmt.Sprintf(`
-type: Mesh
-name: default
-tracing:
-  defaultBackend: zipkin
-  backends:
-  - name: zipkin
-    type: zipkin
-    conf:
-      url: %s
-`, zipkinURL)
-	}
-
-	traceAll := `
-type: TrafficTrace
-name: traffic-trace-all
-mesh: default
-selectors:
-- match:
-   kuma.io/service: "*"
-`
-
-	var cluster Cluster
-	var deployOptsFuncs []DeployOptionsFunc
-
-	BeforeEach(func() {
-		cluster = NewUniversalCluster(NewTestingT(), Kuma1, Silent)
-		deployOptsFuncs = []DeployOptionsFunc{}
-
-		err := NewClusterSetup().
-			Install(Kuma(core.Standalone, deployOptsFuncs...)).
-			Install(tracing.Install()).
-			Setup(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = cluster.VerifyKuma()
-		Expect(err).ToNot(HaveOccurred())
-
-		echoServerToken, err := cluster.GetKuma().GenerateDpToken("default", "echo-server_kuma-test_svc_8080")
-		Expect(err).ToNot(HaveOccurred())
-		demoClientToken, err := cluster.GetKuma().GenerateDpToken("default", "demo-client")
-		Expect(err).ToNot(HaveOccurred())
-
-		err = EchoServerUniversal(AppModeEchoServer, "default", "universal", echoServerToken)(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = DemoClientUniversal(AppModeDemoClient, "default", demoClientToken)(cluster)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	AfterEach(func() {
-		if ShouldSkipCleanup() {
-			return
-		}
-		Expect(cluster.DeleteKuma(deployOptsFuncs...)).To(Succeed())
-		Expect(cluster.DismissCluster()).To(Succeed())
-	})
-
-	It("should emit traces to jaeger", func() {
-		// given TrafficTrace and mesh with tracing backend
-		err := YamlUniversal(meshWithTracing(tracing.From(cluster).ZipkinCollectorURL()))(cluster)
-		Expect(err).ToNot(HaveOccurred())
-		err = YamlUniversal(traceAll)(cluster)
-		Expect(err).ToNot(HaveOccurred())
-
-		retry.DoWithRetry(cluster.GetTesting(), "check traced services", DefaultRetries, DefaultTimeout, func() (string, error) {
-			// when client sends requests to server
-			_, _, err := cluster.Exec("", "", "demo-client", "curl", "-v", "-m", "3", "--fail", "localhost:4001")
-			if err != nil {
-				return "", err
-			}
-
-			// then traces are published
-			services, err := tracing.From(cluster).TracedServices()
-			if err != nil {
-				return "", err
-			}
-
-			expectedServices := []string{"demo-client", "echo-server_kuma-test_svc_8080", "jaeger-query"}
-			if !reflect.DeepEqual(services, expectedServices) {
-				return "", errors.Errorf("services not traced. Expected %q, got %q", expectedServices, services)
-			}
-			return "", nil
-		})
-	})
-})
+var _ = Describe("Tracing Universal", tracing.TracingUniversal)
diff --git a/test/framework/ginkgo.go b/test/framework/ginkgo.go
new file mode 100644
index 000000000..ccb6f406d
--- /dev/null
+++ b/test/framework/ginkgo.go
@@ -0,0 +1,10 @@
+package framework
+
+import (
+	"github.com/onsi/ginkgo"
+	ginkgo_config "github.com/onsi/ginkgo/config"
+)
+
+func ShouldSkipCleanup() bool {
+	return ginkgo.CurrentGinkgoTestDescription().Failed && ginkgo_config.GinkgoConfig.FailFast
+}