package topology import ( "testing" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func ptr[T any](v T) *T { return &v } func path(p string, pt networkingv1.PathType, svc string) networkingv1.HTTPIngressPath { return networkingv1.HTTPIngressPath{Path: p, PathType: &pt, Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{Name: svc, Port: networkingv1.ServiceBackendPort{Number: 80}}}} } func fixture() Snapshot { ing := networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{Namespace: "shop", Name: "public"}, Spec: networkingv1.IngressSpec{Rules: []networkingv1.IngressRule{ {Host: "shop.example.com", IngressRuleValue: networkingv1.IngressRuleValue{HTTP: &networkingv1.HTTPIngressRuleValue{Paths: []networkingv1.HTTPIngressPath{ path("/", networkingv1.PathTypePrefix, "web"), path("/api", networkingv1.PathTypePrefix, "api"), path("/legacy", networkingv1.PathTypeExact, "old"), path("/pay", networkingv1.PathTypePrefix, "gone"), path("/cdn", networkingv1.PathTypePrefix, "cdn"), }}}}, {Host: "*.example.com", IngressRuleValue: networkingv1.IngressRuleValue{HTTP: &networkingv1.HTTPIngressRuleValue{Paths: []networkingv1.HTTPIngressPath{ path("/", networkingv1.PathTypePrefix, "web"), }}}}, }}} svcs := []corev1.Service{ {ObjectMeta: metav1.ObjectMeta{Namespace: "shop", Name: "web"}, Spec: corev1.ServiceSpec{Selector: map[string]string{"app": "web"}}}, {ObjectMeta: metav1.ObjectMeta{Namespace: "shop", Name: "api"}, Spec: corev1.ServiceSpec{Selector: map[string]string{"app": "api"}}}, {ObjectMeta: metav1.ObjectMeta{Namespace: "shop", Name: "old"}, Spec: corev1.ServiceSpec{Selector: map[string]string{"app": "old"}}}, {ObjectMeta: metav1.ObjectMeta{Namespace: "shop", Name: "cdn"}, Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeExternalName, ExternalName: "cdn.vendor.net"}}, } dep := func(ns, name, app string) appsv1.Deployment { return appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: name}, Spec: appsv1.DeploymentSpec{ Template: corev1.PodTemplateSpec{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": app, "tier": "x"}}}}} } deps := []appsv1.Deployment{dep("shop", "web-v2", "web"), dep("shop", "api", "api"), dep("other", "web-other", "web"), dep("shop", "old", "old")} return Build([]networkingv1.Ingress{ing}, svcs, deps) } func one(t *testing.T, s Snapshot, host, path string) Target { t.Helper() got := s.Resolve(host, path) if len(got) != 1 { t.Fatalf("%s%s: %d targets: %+v", host, path, len(got), got) } return got[0] } func TestResolveLongestPathAndDeployments(t *testing.T) { s := fixture() if g := one(t, s, "shop.example.com", "/api/orders?x=1"); g.Service != "api" || len(g.Deployments) != 1 || g.Deployments[0] != "api" { t.Fatalf("api: %+v", g) } // the selector matches the same-namespace deployment only, not other/web-other if g := one(t, s, "shop.example.com", "/index"); g.Service != "web" || len(g.Deployments) != 1 || g.Deployments[0] != "web-v2" { t.Fatalf("web: %+v", g) } // /apix must not match the /api prefix (segment-wise) if g := one(t, s, "shop.example.com", "/apix"); g.Service != "web" { t.Fatalf("apix: %+v", g) } } func TestResolveHostsPortsAndExact(t *testing.T) { s := fixture() if g := one(t, s, "SHOP.example.com:443", "/"); g.Host != "shop.example.com" { t.Fatalf("host case/port: %+v", g) } if g := one(t, s, "blog.example.com", "/x"); g.Host != "*.example.com" { t.Fatalf("wildcard: %+v", g) } if got := s.Resolve("a.b.example.com", "/"); len(got) != 0 { t.Fatalf("wildcard matched two labels: %+v", got) } if g := one(t, s, "shop.example.com", "/legacy"); g.Service != "old" { t.Fatalf("exact: %+v", g) } if g := one(t, s, "shop.example.com", "/legacy/x"); g.Service != "web" { t.Fatalf("exact matched a subpath: %+v", g) } } func TestMissingAndExternalBackends(t *testing.T) { s := fixture() if g := one(t, s, "shop.example.com", "/pay"); !g.Missing || g.Service != "gone" { t.Fatalf("missing: %+v", g) } if g := one(t, s, "shop.example.com", "/cdn/a.js"); g.External != "cdn.vendor.net" || len(g.Deployments) != 0 { t.Fatalf("external: %+v", g) } }