Repository: containous/maesh
Pull request #348 (2019-11-08 08:13:03)

Run in single namespace
## What does this PR do

This PR makes Maesh able to run in a single namespace.

Fixes: #253 

## How to test this

Install maesh in the same namespace of your applications, and enjoy your shiny service mesh  as usual :tada:

## How does it work

Now, all generated mesh services are labeled with `app=maesh` too.
Then the controller is now able to a list of apps, right now `maesh` and `jaeger` are hardcoded, and we might want to change that. But it makes maesh ignore the maesh services. 
For the initial `createMaeshService` I used  [fields selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/#supported-operators) instead of filtering everything in memory.

I also made the filtering more time efficient using a map instead of iterating over the services, as we can have a lot of services this can be important. 

## Additional information

I kind of hacked my way to get it working, that is definitely not a final implementation.
Also It doesn't support CNI just yet. 

Draft for the moment as CI is super broken and it is not finished.

dtomcej: I ❤️ Where this is going.

Design LGTM.

Switching to review.
dtomcej: This is looking _really_ good. I look forwards to working on a follow up to expand this!

Patch:
diff --git a/helm/chart/maesh/templates/controller/controller-deployment.yaml b/helm/chart/maesh/templates/controller/controller-deployment.yaml
index b8211f4..f67cb8e 100644
--- a/helm/chart/maesh/templates/controller/controller-deployment.yaml
+++ b/helm/chart/maesh/templates/controller/controller-deployment.yaml
@@ -113,6 +113,12 @@ spec:
             {{- end }}
             - "--clusterdomain"
             - {{ default "cluster.local" .Values.clusterDomain | quote }}
+            - "--namespace=$(POD_NAMESPACE)"
+          env:
+            - name: POD_NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
           securityContext:
             capabilities:
               drop:
diff --git a/helm/chart/maesh/templates/mesh/mesh-service.yaml b/helm/chart/maesh/templates/mesh/mesh-service.yaml
index b69651e..128d2cb 100644
--- a/helm/chart/maesh/templates/mesh/mesh-service.yaml
+++ b/helm/chart/maesh/templates/mesh/mesh-service.yaml
@@ -5,7 +5,7 @@ metadata:
   name: maesh-mesh-api
   namespace: {{ .Release.Namespace }}
   labels:
-    app: mesh-api
+    app: maesh
 spec:
   type: ClusterIP
   ports:
diff --git a/internal/controller/controller.go b/internal/controller/controller.go
index 9654f2a..112ccb8 100644
--- a/internal/controller/controller.go
+++ b/internal/controller/controller.go
@@ -23,6 +23,7 @@ import (
 	log "github.com/sirupsen/logrus"
 	"golang.org/x/sync/errgroup"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -55,7 +56,6 @@ type Controller struct {
 // and return an initialized mesh controller object.
 func NewMeshController(clients *k8s.ClientWrapper, smiEnabled bool, defaultMode string, meshNamespace string, ignoreNamespaces []string, apiPort int) *Controller {
 	ignored := k8s.NewIgnored()
-	ignored.SetMeshNamespace(meshNamespace)
 
 	for _, ns := range ignoreNamespaces {
 		ignored.AddIgnoredNamespace(ns)
@@ -63,6 +63,7 @@ func NewMeshController(clients *k8s.ClientWrapper, smiEnabled bool, defaultMode
 
 	ignored.AddIgnoredService("kubernetes", metav1.NamespaceDefault)
 	ignored.AddIgnoredNamespace(metav1.NamespaceSystem)
+	ignored.AddIgnoredApps("maesh", "jaeger")
 
 	// configRefreshChan is used to trigger configuration refreshes and deploys.
 	configRefreshChan := make(chan string)
@@ -103,7 +104,7 @@ func (c *Controller) Init() error {
 	c.api = NewAPI(c.apiPort, &c.lastConfiguration, c.deployLog, c.clients, c.meshNamespace)
 
 	if c.smiEnabled {
-		c.provider = smi.New(c.clients, c.defaultMode, c.meshNamespace, c.tcpStateTable, c.ignored)
+		c.provider = smi.New(c.clients, c.defaultMode, c.tcpStateTable, c.ignored)
 		// Create new SharedInformerFactories, and register the event handler to informers.
 		c.smiAccessFactory = smiAccessExternalversions.NewSharedInformerFactoryWithOptions(c.clients.SmiAccessClient, k8s.ResyncPeriod)
 		c.smiAccessFactory.Access().V1alpha1().TrafficTargets().Informer().AddEventHandler(c.handler)
@@ -118,7 +119,7 @@ func (c *Controller) Init() error {
 	}
 
 	// If SMI is not configured, use the kubernetes provider.
-	c.provider = kubernetes.New(c.clients, c.defaultMode, c.meshNamespace, c.tcpStateTable, c.ignored)
+	c.provider = kubernetes.New(c.clients, c.defaultMode, c.tcpStateTable, c.ignored)
 
 	return nil
 }
@@ -225,30 +226,42 @@ func (c *Controller) startInformers(stopCh <-chan struct{}) {
 }
 
 func (c *Controller) createMeshServices() error {
-	services, err := c.clients.GetServices(metav1.NamespaceAll)
+	// Because createMeshServices is called after startInformers,
+	// then we already have the cache built, so we can use it.
+	svcs := c.kubernetesFactory.Core().V1().Services().Lister()
+
+	sel, err := c.ignored.LabelSelector()
 	if err != nil {
-		return fmt.Errorf("unable to get services: %v", err)
+		return fmt.Errorf("unable to build label selectors: %w", err)
 	}
 
-	for _, service := range services {
-		if c.ignored.IsIgnoredService(service.Name, service.Namespace) {
+	potentialSvcs, err := svcs.List(sel)
+	if err != nil {
+		return fmt.Errorf("unable to get services: %w", err)
+	}
+
+	for _, service := range potentialSvcs {
+		if c.ignored.IsIgnored(service.ObjectMeta) {
 			continue
 		}
 
 		log.Debugf("Creating mesh for service: %v", service.Name)
+
 		meshServiceName := c.userServiceToMeshServiceName(service.Name, service.Namespace)
 
-		for _, subservice := range services {
-			// If there is already a mesh service created, don't bother recreating
-			if subservice.Name == meshServiceName && subservice.Namespace == c.meshNamespace {
-				continue
-			}
+		_, err := svcs.Services(c.meshNamespace).Get(meshServiceName)
+		if err == nil {
+			continue
+		}
+		// We're expecting an IsNotFound error here, to only create the maesh service if it does not exist.
+		if err != nil && !errors.IsNotFound(err) {
+			return fmt.Errorf("unable to check if maesh service exists: %w", err)
 		}
 
 		log.Infof("Creating associated mesh service: %s", meshServiceName)
 
 		if err := c.createMeshService(service); err != nil {
-			return fmt.Errorf("unable to get create mesh service: %v", err)
+			return fmt.Errorf("unable to create mesh service: %w", err)
 		}
 	}
 
@@ -302,6 +315,9 @@ func (c *Controller) createMeshService(service *corev1.Service) error {
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      meshServiceName,
 				Namespace: c.meshNamespace,
+				Labels: map[string]string{
+					"app": "maesh",
+				},
 			},
 			Spec: corev1.ServiceSpec{
 				Ports: ports,
diff --git a/internal/controller/handler.go b/internal/controller/handler.go
index 4e6d7ba..16bc271 100644
--- a/internal/controller/handler.go
+++ b/internal/controller/handler.go
@@ -48,7 +48,7 @@ func (h *Handler) OnAdd(obj interface{}) {
 	// assert the type to an object to pull out relevant data
 	switch obj := obj.(type) {
 	case *corev1.Service:
-		if h.ignored.IsIgnoredService(obj.Name, obj.Namespace) {
+		if h.ignored.IsIgnored(obj.ObjectMeta) {
 			return
 		}
 
@@ -72,7 +72,7 @@ func (h *Handler) OnUpdate(oldObj, newObj interface{}) {
 	// Assert the type to an object to pull out relevant data.
 	switch obj := newObj.(type) {
 	case *corev1.Service:
-		if h.ignored.IsIgnoredService(obj.Name, obj.Namespace) {
+		if h.ignored.IsIgnored(obj.ObjectMeta) {
 			return
 		}
 
@@ -84,7 +84,7 @@ func (h *Handler) OnUpdate(oldObj, newObj interface{}) {
 		log.Debugf("MeshControllerHandler ObjectUpdated with type: *corev1.Service: %s/%s", obj.Namespace, obj.Name)
 	case *corev1.Endpoints:
 		// We can use the same ignore for services and endpoints.
-		if h.ignored.IsIgnoredService(obj.Name, obj.Namespace) {
+		if h.ignored.IsIgnored(obj.ObjectMeta) {
 			return
 		}
 
@@ -111,7 +111,7 @@ func (h *Handler) OnDelete(obj interface{}) {
 	// Assert the type to an object to pull out relevant data.
 	switch obj := obj.(type) {
 	case *corev1.Service:
-		if h.ignored.IsIgnoredService(obj.Name, obj.Namespace) {
+		if h.ignored.IsIgnored(obj.ObjectMeta) {
 			return
 		}
 
@@ -122,7 +122,7 @@ func (h *Handler) OnDelete(obj interface{}) {
 		}
 	case *corev1.Endpoints:
 		// We can use the same ignore for services and endpoints.
-		if h.ignored.IsIgnoredService(obj.Name, obj.Namespace) {
+		if h.ignored.IsIgnored(obj.ObjectMeta) {
 			return
 		}
 
diff --git a/internal/k8s/client.go b/internal/k8s/client.go
index 0bb207d..3829257 100644
--- a/internal/k8s/client.go
+++ b/internal/k8s/client.go
@@ -243,7 +243,7 @@ func (w *ClientWrapper) InitCluster(namespace string, clusterDomain string) erro
 	log.Infoln("Preparing Cluster...")
 	log.Debugln("Patching DNS...")
 
-	if err := w.patchDNS(metav1.NamespaceSystem, clusterDomain); err != nil {
+	if err := w.patchDNS(metav1.NamespaceSystem, clusterDomain, namespace); err != nil {
 		return err
 	}
 
@@ -252,8 +252,8 @@ func (w *ClientWrapper) InitCluster(namespace string, clusterDomain string) erro
 	return nil
 }
 
-func (w *ClientWrapper) patchDNS(namespace string, clusterDomain string) error {
-	deployment, exist, err := w.GetDeployment(namespace, "coredns")
+func (w *ClientWrapper) patchDNS(coreNamespace, clusterDomain, maeshNamespace string) error {
+	deployment, exist, err := w.GetDeployment(coreNamespace, "coredns")
 	if err != nil {
 		return err
 	}
@@ -264,7 +264,7 @@ func (w *ClientWrapper) patchDNS(namespace string, clusterDomain string) error {
 
 		var patched bool
 
-		patched, err = w.patchCoreDNSConfigMap(deployment, clusterDomain)
+		patched, err = w.patchCoreDNSConfigMap(deployment, clusterDomain, maeshNamespace)
 		if err != nil {
 			return err
 		}
@@ -284,13 +284,13 @@ func (w *ClientWrapper) patchDNS(namespace string, clusterDomain string) error {
 
 	log.Debugln("coredns not available fallback to kube-dns")
 	// If coreDNS does not exist we try to get the kube-dns
-	deployment, exist, err = w.GetDeployment(namespace, "kube-dns")
+	deployment, exist, err = w.GetDeployment(coreNamespace, "kube-dns")
 	if err != nil {
 		return err
 	}
 
 	if !exist {
-		return fmt.Errorf("nor CoreDNS and KubeDNS are available in namespace %q", namespace)
+		return fmt.Errorf("neither CoreDNS or KubeDNS are available in namespace %q", coreNamespace)
 	}
 
 	ebo := backoff.NewConstantBackOff(10 * time.Second)
@@ -336,7 +336,7 @@ func (w *ClientWrapper) patchDNS(namespace string, clusterDomain string) error {
 	return nil
 }
 
-func (w *ClientWrapper) patchCoreDNSConfigMap(coreDeployment *appsv1.Deployment, clusterDomain string) (bool, error) {
+func (w *ClientWrapper) patchCoreDNSConfigMap(coreDeployment *appsv1.Deployment, clusterDomain, maeshNamespace string) (bool, error) {
 	var coreConfigMapName string
 
 	if len(coreDeployment.Spec.Template.Spec.Volumes) == 0 {
@@ -362,8 +362,8 @@ func (w *ClientWrapper) patchCoreDNSConfigMap(coreDeployment *appsv1.Deployment,
 maesh:53 {
     errors
     rewrite continue {
-        name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh maesh-{1}-{2}.maesh.svc.%[1]s
-        answer name maesh-([a-zA-Z0-9-_]*)-([a-zA-Z0-9-_]*)\.maesh\.svc\.%[2]s {1}.{2}.maesh
+        name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh %[3]s-{1}-{2}.%[3]s.svc.%[1]s
+        answer name %[3]s-([a-zA-Z0-9-_]*)-([a-zA-Z0-9-_]*)\.%[3]s\.svc\.%[2]s {1}.{2}.maesh
     }
     kubernetes %[1]s in-addr.arpa ip6.arpa {
         pods insecure
@@ -378,7 +378,9 @@ maesh:53 {
 }
 `,
 		clusterDomain,
-		strings.Replace(clusterDomain, ".", "\\.", -1))
+		strings.Replace(clusterDomain, ".", "\\.", -1),
+		maeshNamespace,
+	)
 
 	originalBlock := coreConfigMap.Data["Corefile"]
 	newBlock := originalBlock + serverBlock
diff --git a/internal/k8s/ignore.go b/internal/k8s/ignore.go
index a52f749..51db68c 100644
--- a/internal/k8s/ignore.go
+++ b/internal/k8s/ignore.go
@@ -1,26 +1,27 @@
 package k8s
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/selection"
+)
+
 // IgnoreWrapper holds namespaces and services to ignore.
 type IgnoreWrapper struct {
-	Namespaces    Namespaces
-	Services      Services
-	MeshNamespace string
+	Namespaces Namespaces
+	Services   Services
+	Apps       []string
 }
 
 // NewIgnored returns a new IgnoreWrapper.
 func NewIgnored() IgnoreWrapper {
 	return IgnoreWrapper{
-		Namespaces:    Namespaces{},
-		Services:      Services{},
-		MeshNamespace: "",
+		Namespaces: Namespaces{},
+		Services:   Services{},
+		Apps:       []string{},
 	}
 }
 
-// SetMeshNamespace sets the meshNamespace.
-func (i *IgnoreWrapper) SetMeshNamespace(namespace string) {
-	i.MeshNamespace = namespace
-}
-
 // AddIgnoredNamespace adds a namespace to the list of ignored namespaces.
 func (i *IgnoreWrapper) AddIgnoredNamespace(namespace string) {
 	i.Namespaces = append(i.Namespaces, namespace)
@@ -31,20 +32,39 @@ func (i *IgnoreWrapper) AddIgnoredService(serviceName, serviceNamespace string)
 	i.Services = append(i.Services, Service{Name: serviceName, Namespace: serviceNamespace})
 }
 
-// IsIgnoredService returns if the service's events should be ignored.
-func (i *IgnoreWrapper) IsIgnoredService(name, namespace string) bool {
-	// Is the service's namespace ignored?
-	if i.Namespaces.Contains(namespace) {
+// AddIgnoredApps add an app to the list of ignored apps.
+func (i *IgnoreWrapper) AddIgnoredApps(app ...string) {
+	i.Apps = append(i.Apps, app...)
+}
+
+// LabelSelector returns the labels.Selector image of the ignored object.
+func (i *IgnoreWrapper) LabelSelector() (labels.Selector, error) {
+	sel := labels.Everything()
+
+	r, err := labels.NewRequirement("app", selection.NotIn, i.Apps)
+	if err != nil {
+		return nil, err
+	}
+
+	sel = sel.Add(*r)
+
+	return sel, nil
+}
+
+// IsIgnored returns if the object events should be ignored.
+func (i *IgnoreWrapper) IsIgnored(obj metav1.ObjectMeta) bool {
+	// Is the object's namespace ignored?
+	if i.Namespaces.Contains(obj.GetNamespace()) {
 		return true
 	}
 
-	// Is the service explicitly ignored?
-	if i.Services.Contains(name, namespace) {
+	// Is the object explicitly ignored?
+	if i.Services.Contains(obj.GetName(), obj.GetNamespace()) {
 		return true
 	}
 
-	// Is the service in the mesh namespace?
-	if i.MeshNamespace != "" && namespace == i.MeshNamespace {
+	// Is the app ignored ?
+	if contains(i.Apps, obj.GetLabels()["app"]) {
 		return true
 	}
 
@@ -53,15 +73,5 @@ func (i *IgnoreWrapper) IsIgnoredService(name, namespace string) bool {
 
 // IsIgnoredNamespace returns if the service's events should be ignored.
 func (i *IgnoreWrapper) IsIgnoredNamespace(namespace string) bool {
-	// Is the namespace ignored?
-	if i.Namespaces.Contains(namespace) {
-		return true
-	}
-
-	// Is the namespace the mesh namespace?
-	if i.MeshNamespace != "" && namespace == i.MeshNamespace {
-		return true
-	}
-
-	return false
+	return i.Namespaces.Contains(namespace)
 }
diff --git a/internal/k8s/namespace.go b/internal/k8s/namespace.go
index a47c269..5c83965 100644
--- a/internal/k8s/namespace.go
+++ b/internal/k8s/namespace.go
@@ -5,7 +5,11 @@ type Namespaces []string
 
 // Contains returns true if x is in the slice, false otherwise.
 func (n Namespaces) Contains(x string) bool {
-	for _, v := range n {
+	return contains(n, x)
+}
+
+func contains(s []string, x string) bool {
+	for _, v := range s {
 		if x == v {
 			return true
 		}
diff --git a/internal/providers/kubernetes/provider.go b/internal/providers/kubernetes/provider.go
index 6aacd44..9019735 100644
--- a/internal/providers/kubernetes/provider.go
+++ b/internal/providers/kubernetes/provider.go
@@ -19,7 +19,6 @@ import (
 type Provider struct {
 	client        k8s.CoreV1Client
 	defaultMode   string
-	meshNamespace string
 	tcpStateTable *k8s.State
 	ignored       k8s.IgnoreWrapper
 }
@@ -30,11 +29,10 @@ func (p *Provider) Init() {
 }
 
 // New creates a new provider.
-func New(client k8s.CoreV1Client, defaultMode string, meshNamespace string, tcpStateTable *k8s.State, ignored k8s.IgnoreWrapper) *Provider {
+func New(client k8s.CoreV1Client, defaultMode string, tcpStateTable *k8s.State, ignored k8s.IgnoreWrapper) *Provider {
 	p := &Provider{
 		client:        client,
 		defaultMode:   defaultMode,
-		meshNamespace: meshNamespace,
 		tcpStateTable: tcpStateTable,
 		ignored:       ignored,
 	}
@@ -47,7 +45,7 @@ func New(client k8s.CoreV1Client, defaultMode string, meshNamespace string, tcpS
 func (p *Provider) buildRouter(name, namespace, ip string, port int, serviceName string, addMiddlewares bool) *dynamic.Router {
 	if addMiddlewares {
 		return &dynamic.Router{
-			Rule:        fmt.Sprintf("Host(`%s.%s.%s`) || Host(`%s`)", name, namespace, p.meshNamespace, ip),
+			Rule:        fmt.Sprintf("Host(`%s.%s.maesh`) || Host(`%s`)", name, namespace, ip),
 			EntryPoints: []string{fmt.Sprintf("http-%d", port)},
 			Middlewares: []string{serviceName},
 			Service:     serviceName,
@@ -55,7 +53,7 @@ func (p *Provider) buildRouter(name, namespace, ip string, port int, serviceName
 	}
 
 	return &dynamic.Router{
-		Rule:        fmt.Sprintf("Host(`%s.%s.%s`) || Host(`%s`)", name, namespace, p.meshNamespace, ip),
+		Rule:        fmt.Sprintf("Host(`%s.%s.maesh`) || Host(`%s`)", name, namespace, ip),
 		EntryPoints: []string{fmt.Sprintf("http-%d", port)},
 		Service:     serviceName,
 	}
@@ -136,7 +134,7 @@ func (p *Provider) BuildConfig() (*dynamic.Configuration, error) {
 	config := base.CreateBaseConfigWithReadiness()
 
 	for _, service := range services {
-		if p.ignored.IsIgnoredService(service.Name, service.Namespace) {
+		if p.ignored.IsIgnored(service.ObjectMeta) {
 			continue
 		}
 
diff --git a/internal/providers/smi/provider.go b/internal/providers/smi/provider.go
index 1b2c256..0f1d608 100644
--- a/internal/providers/smi/provider.go
+++ b/internal/providers/smi/provider.go
@@ -23,7 +23,6 @@ import (
 type Provider struct {
 	client        k8s.Client
 	defaultMode   string
-	meshNamespace string
 	tcpStateTable *k8s.State
 	ignored       k8s.IgnoreWrapper
 }
@@ -39,11 +38,10 @@ type destinationKey struct {
 func (p *Provider) Init() {}
 
 // New creates a new provider.
-func New(client k8s.Client, defaultMode string, meshNamespace string, tcpStateTable *k8s.State, ignored k8s.IgnoreWrapper) *Provider {
+func New(client k8s.Client, defaultMode string, tcpStateTable *k8s.State, ignored k8s.IgnoreWrapper) *Provider {
 	p := &Provider{
 		client:        client,
 		defaultMode:   defaultMode,
-		meshNamespace: meshNamespace,
 		tcpStateTable: tcpStateTable,
 		ignored:       ignored,
 	}
@@ -80,24 +78,24 @@ func (p *Provider) BuildConfig() (*dynamic.Configuration, error) {
 	}
 
 	for _, service := range services {
-		if p.ignored.IsIgnoredService(service.Name, service.Namespace) {
+		if p.ignored.IsIgnored(service.ObjectMeta) {
 			continue
 		}
 
 		serviceMode := p.getServiceMode(service.Annotations[k8s.AnnotationServiceType])
 		// Get all traffic targets in the service's namespace.
 		trafficTargetsInNamespace := p.getTrafficTargetsWithDestinationInNamespace(service.Namespace, trafficTargets)
-		log.Debugf("Found traffictargets for service %s/%s: %+v\n", service.Namespace, service.Name, trafficTargets)
+		log.Debugf("Found traffictargets for service %s/%s: %+v", service.Namespace, service.Name, trafficTargets)
 		// Find all traffic targets that are applicable to the service in question.
 		applicableTrafficTargets := p.getApplicableTrafficTargets(base.GetEndpointsFromList(service.Name, service.Namespace, endpoints), trafficTargetsInNamespace)
-		log.Debugf("Found applicable traffictargets for service %s/%s: %+v\n", service.Namespace, service.Name, applicableTrafficTargets)
+		log.Debugf("Found applicable traffictargets for service %s/%s: %+v", service.Namespace, service.Name, applicableTrafficTargets)
 		// Group the traffic targets by destination, so that they can be built separately.
 		groupedByDestinationTrafficTargets := p.groupTrafficTargetsByDestination(applicableTrafficTargets)
-		log.Debugf("Found grouped traffictargets for service %s/%s: %+v\n", service.Namespace, service.Name, groupedByDestinationTrafficTargets)
+		log.Debugf("Found grouped traffictargets for service %s/%s: %+v", service.Namespace, service.Name, groupedByDestinationTrafficTargets)
 
 		// Get all traffic split in the service's namespace.
 		trafficSplitsInNamespace := p.getTrafficSplitsWithDestinationInNamespace(service.Namespace, trafficSplits)
-		log.Debugf("Found trafficsplits for service %s/%s: %+v\n", service.Namespace, service.Name, trafficSplitsInNamespace)
+		log.Debugf("Found trafficsplits for service %s/%s: %+v", service.Namespace, service.Name, trafficSplitsInNamespace)
 
 		for _, groupedTrafficTargets := range groupedByDestinationTrafficTargets {
 			for _, groupedTrafficTarget := range groupedTrafficTargets {
@@ -376,7 +374,7 @@ func (p *Provider) buildRuleSnippetFromServiceAndMatch(name, namespace, ip strin
 		result = append(result, fmt.Sprintf("Method(`%s`)", methods))
 	}
 
-	result = append(result, fmt.Sprintf("(Host(`%s.%s.%s`) || Host(`%s`))", name, namespace, p.meshNamespace, ip))
+	result = append(result, fmt.Sprintf("(Host(`%s.%s.maesh`) || Host(`%s`))", name, namespace, ip))
 
 	return strings.Join(result, " && ")
 }


Test patch:
diff --git a/internal/k8s/ignore_test.go b/internal/k8s/ignore_test.go
index 5d0ae2e..44976fc 100644
--- a/internal/k8s/ignore_test.go
+++ b/internal/k8s/ignore_test.go
@@ -4,10 +4,10 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func TestIgnoredNamespace(t *testing.T) {
-	meshNamespace := "maesh"
 	testCases := []struct {
 		desc      string
 		namespace string
@@ -31,7 +31,7 @@ func TestIgnoredNamespace(t *testing.T) {
 		{
 			desc:      "ignored mesh namespace",
 			namespace: "maesh",
-			expected:  true,
+			expected:  false,
 		},
 	}
 
@@ -40,7 +40,6 @@ func TestIgnoredNamespace(t *testing.T) {
 		t.Run(test.desc, func(t *testing.T) {
 			t.Parallel()
 			i := NewIgnored()
-			i.SetMeshNamespace(meshNamespace)
 			i.AddIgnoredNamespace("someNamespace")
 			actual := i.IsIgnoredNamespace(test.namespace)
 			assert.Equal(t, test.expected, actual)
@@ -49,35 +48,39 @@ func TestIgnoredNamespace(t *testing.T) {
 }
 
 func TestIgnoredService(t *testing.T) {
-	meshNamespace := "maesh"
 	testCases := []struct {
 		desc      string
 		name      string
 		namespace string
+		app       string
 		expected  bool
 	}{
 		{
 			desc:      "empty ignored",
 			name:      "",
 			namespace: "",
+			app:       "",
 			expected:  false,
 		},
 		{
 			desc:      "ignored service due to namespace",
 			name:      "foo",
 			namespace: "someNamespace",
+			app:       "notignored",
 			expected:  true,
 		},
 		{
 			desc:      "explicit ignored service",
 			name:      "foo",
 			namespace: "bar",
+			app:       "notignored",
 			expected:  true,
 		},
 		{
-			desc:      "ignored mesh service",
+			desc:      "ignored app",
 			name:      "omg",
-			namespace: "maesh",
+			namespace: "foo",
+			app:       "ignoredapp",
 			expected:  true,
 		},
 	}
@@ -87,11 +90,21 @@ func TestIgnoredService(t *testing.T) {
 		t.Run(test.desc, func(t *testing.T) {
 			t.Parallel()
 			i := NewIgnored()
-			i.SetMeshNamespace(meshNamespace)
 			i.AddIgnoredNamespace("someNamespace")
 			i.AddIgnoredService("foo", "bar")
-			actual := i.IsIgnoredService(test.name, test.namespace)
+			i.AddIgnoredApps("ignoredapp")
+			actual := i.IsIgnored(buildMeta(test.name, test.namespace, test.app))
 			assert.Equal(t, test.expected, actual)
 		})
 	}
 }
+
+func buildMeta(name, ns, app string) metav1.ObjectMeta {
+	return metav1.ObjectMeta{
+		Name:      name,
+		Namespace: ns,
+		Labels: map[string]string{
+			"app": app,
+		},
+	}
+}
diff --git a/internal/providers/kubernetes/provider_test.go b/internal/providers/kubernetes/provider_test.go
index 7201c24..552b8d2 100644
--- a/internal/providers/kubernetes/provider_test.go
+++ b/internal/providers/kubernetes/provider_test.go
@@ -12,8 +12,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-const meshNamespace string = "maesh"
-
 func TestBuildRouter(t *testing.T) {
 	expectedWithMiddlewares := &dynamic.Router{
 		Rule:        "Host(`test.foo.maesh`) || Host(`10.0.0.1`)",
@@ -29,9 +27,7 @@ func TestBuildRouter(t *testing.T) {
 	}
 
 	ignored := k8s.NewIgnored()
-	ignored.SetMeshNamespace(meshNamespace)
-
-	provider := New(nil, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+	provider := New(nil, k8s.ServiceTypeHTTP, nil, ignored)
 
 	name := "test"
 	namespace := "foo"
@@ -53,9 +49,8 @@ func TestBuildTCPRouter(t *testing.T) {
 	}
 
 	ignored := k8s.NewIgnored()
-	ignored.SetMeshNamespace(meshNamespace)
 
-	provider := New(nil, k8s.ServiceTypeTCP, meshNamespace, nil, ignored)
+	provider := New(nil, k8s.ServiceTypeTCP, nil, ignored)
 
 	port := 10000
 	associatedService := "bar"
@@ -282,9 +277,8 @@ func TestBuildConfiguration(t *testing.T) {
 			}
 
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, stateTable, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, stateTable, ignored)
 			config, err := provider.BuildConfig()
 			assert.Equal(t, test.expected, config)
 			if test.endpointsError || test.serviceError {
@@ -393,9 +387,8 @@ func TestBuildService(t *testing.T) {
 
 			clientMock := k8s.NewCoreV1ClientMock(test.mockFile)
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, nil, ignored)
 			actual := provider.buildService(test.endpoints, test.scheme)
 			assert.Equal(t, test.expected, actual)
 		})
@@ -467,9 +460,8 @@ func TestBuildTCPService(t *testing.T) {
 
 			clientMock := k8s.NewCoreV1ClientMock(test.mockFile)
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, stateTable, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, stateTable, ignored)
 			actual := provider.buildTCPService(test.endpoints)
 			assert.Equal(t, test.expected, actual)
 		})
@@ -516,9 +508,8 @@ func TestGetMeshPort(t *testing.T) {
 			t.Parallel()
 
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(nil, k8s.ServiceTypeHTTP, meshNamespace, stateTable, ignored)
+			provider := New(nil, k8s.ServiceTypeHTTP, stateTable, ignored)
 			actual := provider.getMeshPort(test.name, test.namespace, test.port)
 			assert.Equal(t, test.expected, actual)
 		})
@@ -608,7 +599,7 @@ func TestBuildHTTPMiddlewares(t *testing.T) {
 		t.Run(test.desc, func(t *testing.T) {
 			t.Parallel()
 
-			provider := New(nil, k8s.ServiceTypeHTTP, meshNamespace, nil, k8s.NewIgnored())
+			provider := New(nil, k8s.ServiceTypeHTTP, nil, k8s.NewIgnored())
 			actual := provider.buildHTTPMiddlewares(test.annotations)
 			assert.Equal(t, test.expected, actual)
 		})
diff --git a/internal/providers/smi/provider_test.go b/internal/providers/smi/provider_test.go
index 3ad9e93..95fa40f 100644
--- a/internal/providers/smi/provider_test.go
+++ b/internal/providers/smi/provider_test.go
@@ -14,13 +14,9 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-const meshNamespace string = "maesh"
-
 func TestBuildRuleSnippetFromServiceAndMatch(t *testing.T) {
 	ignored := k8s.NewIgnored()
-	ignored.SetMeshNamespace(meshNamespace)
-
-	provider := New(nil, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+	provider := New(nil, k8s.ServiceTypeHTTP, nil, ignored)
 
 	testCases := []struct {
 		desc     string
@@ -70,9 +66,8 @@ func TestBuildRuleSnippetFromServiceAndMatch(t *testing.T) {
 func TestGetTrafficTargetsWithDestinationInNamespace(t *testing.T) {
 	clientMock := k8s.NewClientMock("mock.yaml")
 	ignored := k8s.NewIgnored()
-	ignored.SetMeshNamespace(meshNamespace)
 
-	provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+	provider := New(clientMock, k8s.ServiceTypeHTTP, nil, ignored)
 
 	expected := []*accessv1alpha1.TrafficTarget{
 		{
@@ -340,9 +335,8 @@ func TestBuildHTTPRouterFromTrafficTarget(t *testing.T) {
 				clientMock.EnableHTTPRouteGroupError()
 			}
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, nil, ignored)
 			middleware := "block-all"
 			actual := provider.buildHTTPRouterFromTrafficTarget(test.serviceName, test.serviceNamespace, test.serviceIP, test.trafficTarget, test.port, test.key, middleware)
 			assert.Equal(t, test.expected, actual)
@@ -484,9 +478,8 @@ func TestBuildTCPRouterFromTrafficTarget(t *testing.T) {
 				clientMock.EnableTCPRouteError()
 			}
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, nil, ignored)
 			actual := provider.buildTCPRouterFromTrafficTarget(test.trafficTarget, test.port, test.key)
 			assert.Equal(t, test.expected, actual)
 		})
@@ -495,9 +488,8 @@ func TestBuildTCPRouterFromTrafficTarget(t *testing.T) {
 
 func TestGetServiceMode(t *testing.T) {
 	ignored := k8s.NewIgnored()
-	ignored.SetMeshNamespace(meshNamespace)
 
-	provider := New(nil, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+	provider := New(nil, k8s.ServiceTypeHTTP, nil, ignored)
 
 	testCases := []struct {
 		desc     string
@@ -879,9 +871,8 @@ func TestGetApplicableTrafficTargets(t *testing.T) {
 			}
 
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, nil, ignored)
 
 			actual := provider.getApplicableTrafficTargets(test.endpoints, test.trafficTargets)
 			assert.Equal(t, test.expected, actual)
@@ -1221,9 +1212,8 @@ func TestBuildHTTPServiceFromTrafficTarget(t *testing.T) {
 			}
 
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, nil, ignored)
 
 			actual := provider.buildHTTPServiceFromTrafficTarget(test.endpoints, test.trafficTarget, k8s.SchemeHTTP)
 			assert.Equal(t, test.expected, actual)
@@ -1233,9 +1223,8 @@ func TestBuildHTTPServiceFromTrafficTarget(t *testing.T) {
 
 func TestGroupTrafficTargetsByDestination(t *testing.T) {
 	ignored := k8s.NewIgnored()
-	ignored.SetMeshNamespace(meshNamespace)
 
-	provider := New(nil, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+	provider := New(nil, k8s.ServiceTypeHTTP, nil, ignored)
 
 	trafficTargets := []*accessv1alpha1.TrafficTarget{
 		{
@@ -1474,9 +1463,8 @@ func TestBuildConfiguration(t *testing.T) {
 			}
 
 			ignored := k8s.NewIgnored()
-			ignored.SetMeshNamespace(meshNamespace)
 
-			provider := New(clientMock, k8s.ServiceTypeHTTP, meshNamespace, nil, ignored)
+			provider := New(clientMock, k8s.ServiceTypeHTTP, nil, ignored)
 			config, err := provider.BuildConfig()
 			assert.Equal(t, test.expected, config)
 			if test.endpointsError || test.serviceError {