From 17aaba481f3a5275f3aa63b018331c0c1597899e Mon Sep 17 00:00:00 2001 From: greeshma1196 Date: Thu, 4 Jun 2026 15:33:42 +0200 Subject: [PATCH 1/5] - add support for opt-out annotation Signed-off-by: greeshma1196 --- dataclients/kubernetes/clusterstate.go | 12 +- dataclients/kubernetes/ingress.go | 1 + dataclients/kubernetes/ingressv1.go | 12 +- dataclients/kubernetes/routegroup.go | 6 +- .../mixed-zone-threshold-with-opt-out.eskip | 4 + .../mixed-zone-threshold-with-opt-out.kube | 2 + .../mixed-zone-threshold-with-opt-out.yaml | 135 ++++++++++++++++++ .../mixed-zone-threshold-with-opt-out.eskip | 4 + .../mixed-zone-threshold-with-opt-out.kube | 2 + .../mixed-zone-threshold-with-opt-out.yaml | 133 +++++++++++++++++ eskip/eskip.go | 3 + routesrv/polling.go | 5 + routesrv/routesrv_test.go | 6 + .../mixed-zone-threshold-with-opt-out.eskip | 4 + .../mixed-zone-threshold-with-opt-out.yaml | 135 ++++++++++++++++++ 15 files changed, 459 insertions(+), 5 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml create mode 100644 dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip create mode 100644 dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube create mode 100644 dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml create mode 100644 routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip create mode 100644 routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml diff --git a/dataclients/kubernetes/clusterstate.go b/dataclients/kubernetes/clusterstate.go index 41d7f5ba39..1f6ddfc879 100644 --- a/dataclients/kubernetes/clusterstate.go +++ b/dataclients/kubernetes/clusterstate.go @@ -79,7 +79,7 @@ func (state *clusterState) GetEndpointsByService(namespace, name, protocol strin } // GetEndpointSlicesByService returns the skipper endpointslices for kubernetes endpointslices. -func (state *clusterState) GetEndpointSlicesByService(zone, namespace, name, protocol string, servicePort *servicePort) []skipperEndpoint { +func (state *clusterState) GetEndpointSlicesByService(zone, namespace, name, protocol string, servicePort *servicePort, zoneAwareTraffic string) []skipperEndpoint { epID := endpointID{ ResourceID: newResourceID(namespace, name), Protocol: protocol, @@ -105,6 +105,10 @@ func (state *clusterState) GetEndpointSlicesByService(zone, namespace, name, pro state.cachedEndpointSlices[epID] = targets } + if zoneAwareTraffic == "false" { + return targets + } + return filterByZone(zone, targets) } @@ -167,7 +171,7 @@ func (state *clusterState) GetEndpointsByTarget(namespace, name, protocol, schem } // GetEndpointSlicesByTarget returns the skipper endpointslices for kubernetes endpointslices. -func (state *clusterState) GetEndpointSlicesByTarget(zone, namespace, name, protocol, scheme string, target *definitions.BackendPort) []skipperEndpoint { +func (state *clusterState) GetEndpointSlicesByTarget(zone, namespace, name, protocol, scheme string, target *definitions.BackendPort, zoneAwareTraffic string) []skipperEndpoint { epID := endpointID{ ResourceID: newResourceID(namespace, name), Protocol: protocol, @@ -192,6 +196,10 @@ func (state *clusterState) GetEndpointSlicesByTarget(zone, namespace, name, prot state.cachedEndpointSlices[epID] = targets } + if zoneAwareTraffic == "false" { + return targets + } + return filterByZone(zone, targets) } diff --git a/dataclients/kubernetes/ingress.go b/dataclients/kubernetes/ingress.go index ad969362bd..7a03fe3b1c 100644 --- a/dataclients/kubernetes/ingress.go +++ b/dataclients/kubernetes/ingress.go @@ -24,6 +24,7 @@ const ( skipperLoadBalancerAnnotationKey = "zalando.org/skipper-loadbalancer" skipperBackendProtocolAnnotationKey = "zalando.org/skipper-backend-protocol" pathModeAnnotationKey = "zalando.org/skipper-ingress-path-mode" + tafficZoneAwareAnnotationKey = "zalando.org/traffic-zone-aware" ingressOriginName = "ingress" tlsSecretType = "kubernetes.io/tls" tlsSecretDataCrt = "tls.crt" diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index 6e845a7d0e..6fe10ab5e8 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -103,6 +103,8 @@ func convertPathRuleV1( return nil, err } + zoneAwareTraffic, _ := metadata.Annotations[tafficZoneAwareAnnotationKey] + servicePort, err := svc.getServicePortV1(svcPort) if err != nil { // service definition is wrong or no pods @@ -125,7 +127,7 @@ func convertPathRuleV1( } if state.enableEndpointSlices { - epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort) + epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort, zoneAwareTraffic) for _, ep := range epSlices { eps = append(eps, ep.Address) } @@ -179,6 +181,8 @@ func convertPathRuleV1( r.LBEndpoints = eskip.NewLBEndpoints(eps) } + r.EnableZoneAwareness = zoneAwareTraffic + setPathV1(pathMode, r, prule.PathType, prule.Path) traffic.apply(r) return r, nil @@ -386,6 +390,8 @@ func (ing *ingress) convertDefaultBackendV1( return nil, false, err } + zoneAwareTraffic, _ := i.Metadata.Annotations[tafficZoneAwareAnnotationKey] + servicePort, err := svc.getServicePortV1(svcPort) if err != nil { ic.logger.Errorf("Failed to find target port %v, %s, for service %s add shuntroute: %v", svc.Spec.Ports, svcPort, svcName, err) @@ -407,7 +413,7 @@ func (ing *ingress) convertDefaultBackendV1( } if state.enableEndpointSlices { - epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort) + epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort, zoneAwareTraffic) for _, ep := range epSlices { eps = append(eps, ep.Address) } @@ -454,6 +460,8 @@ func (ing *ingress) convertDefaultBackendV1( r.LBEndpoints = eskip.NewLBEndpoints(eps) } + r.EnableZoneAwareness = zoneAwareTraffic + return r, true, nil } diff --git a/dataclients/kubernetes/routegroup.go b/dataclients/kubernetes/routegroup.go index f4d0954391..0cee8fac55 100644 --- a/dataclients/kubernetes/routegroup.go +++ b/dataclients/kubernetes/routegroup.go @@ -178,6 +178,9 @@ func applyServiceBackend(ctx *routeGroupContext, backend *definitions.SkipperBac protocol = p } + zoneAwareTraffic, _ := ctx.routeGroup.Metadata.Annotations[tafficZoneAwareAnnotationKey] + r.EnableZoneAwareness = zoneAwareTraffic + s, err := getBackendService(ctx, backend) if err != nil { return err @@ -197,7 +200,8 @@ func applyServiceBackend(ctx *routeGroupContext, backend *definitions.SkipperBac s.Meta.Name, "TCP", protocol, - targetPort) + targetPort, + zoneAwareTraffic) for _, epSlice := range epSlices { eps = append(eps, epSlice.Address) } diff --git a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip new file mode 100644 index 0000000000..3fff078826 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip @@ -0,0 +1,4 @@ +kube_default__ingress_with_opt_out__with_example_org____svc_with_opt_out: Host("^(with[.]example[.]org[.]?(:[0-9]+)?)$") + -> ; +kube_default__ingress_without_opt_out__without_example_org____svc_without_opt_out: Host("^(without[.]example[.]org[.]?(:[0-9]+)?)$") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube new file mode 100644 index 0000000000..a9fc3752d3 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube @@ -0,0 +1,2 @@ +enable-kubernetes-endpointslices: true +topology-zone: eu-central-1a diff --git a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml new file mode 100644 index 0000000000..4971f44b54 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml @@ -0,0 +1,135 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: app-with-opt-out + name: svc-with-opt-out +spec: + clusterIP: 10.3.190.1 + ports: + - name: web + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: app-with-opt-out + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: app-without-opt-out + name: svc-without-opt-out +spec: + clusterIP: 10.3.190.2 + ports: + - name: web + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: app-with-opt-out + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: app-with-opt-out + name: ingress-with-opt-out + namespace: default + annotations: + zalando.org/traffic-zone-aware: "false" +spec: + rules: + - host: with.example.org + http: + paths: + - backend: + service: + name: svc-with-opt-out + port: + number: 80 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: app-without-opt-out + name: ingress-without-opt-out + namespace: default +spec: + rules: + - host: without.example.org + http: + paths: + - backend: + service: + name: svc-without-opt-out + port: + number: 80 + pathType: ImplementationSpecific +--- +# svc-with-opt-out: 3 endpoints in eu-central-1a — meets minEndpointsByZone, but with opt-out; should return all endpoints +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: app-with-opt-out + kubernetes.io/service-name: svc-with-opt-out + name: svc-with-opt-out-eps + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: eu-central-1a + - addresses: + - 10.3.0.4 + zone: eu-central-1a + - addresses: + - 10.3.0.5 + zone: eu-central-1a + - addresses: + - 10.3.1.3 + zone: eu-central-1b + - addresses: + - 10.3.1.4 + zone: eu-central-1b + - addresses: + - 10.3.1.5 + zone: eu-central-1b +ports: + - name: web + port: 8080 + protocol: TCP +apiVersion: v1 +--- +# svc-without-opt-out: only 3 endpoints in eu-central-1a — meets minEndpointsByZone +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: app-without-opt-out + kubernetes.io/service-name: svc-without-opt-out + name: svc-without-opt-out-eps + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: eu-central-1a + - addresses: + - 10.3.0.4 + zone: eu-central-1a + - addresses: + - 10.3.0.5 + zone: eu-central-1a + - addresses: + - 10.4.1.4 + zone: eu-central-1b +ports: + - name: web + port: 8080 + protocol: TCP +apiVersion: v1 diff --git a/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip new file mode 100644 index 0000000000..affbdb202a --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip @@ -0,0 +1,4 @@ +kube_rg__default__ingress_with_opt_out__all__0_0: Host("^(with[.]example[.]org[.]?(:[0-9]+)?)$") + -> ; +kube_rg__default__ingress_without_opt_out__all__0_0: Host("^(without[.]example[.]org[.]?(:[0-9]+)?)$") + -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube new file mode 100644 index 0000000000..a9fc3752d3 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.kube @@ -0,0 +1,2 @@ +enable-kubernetes-endpointslices: true +topology-zone: eu-central-1a diff --git a/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml new file mode 100644 index 0000000000..409a3da3bd --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml @@ -0,0 +1,133 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: app-with-opt-out + name: svc-with-opt-out +spec: + clusterIP: 10.3.190.1 + ports: + - name: web + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: app-with-opt-out + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: app-without-opt-out + name: svc-without-opt-out +spec: + clusterIP: 10.3.190.2 + ports: + - name: web + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: app-with-opt-out + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: RouteGroup +metadata: + labels: + app: app-with-opt-out + name: ingress-with-opt-out + namespace: default + annotations: + zalando.org/traffic-zone-aware: "false" +spec: + hosts: + - with.example.org + backends: + - name: app-with-opt-out + type: service + serviceName: svc-with-opt-out + servicePort: 80 + defaultBackends: + - backendName: app-with-opt-out +--- +apiVersion: networking.k8s.io/v1 +kind: RouteGroup +metadata: + labels: + app: app-without-opt-out + name: ingress-without-opt-out + namespace: default +spec: + hosts: + - without.example.org + backends: + - name: app-without-opt-out + type: service + serviceName: svc-without-opt-out + servicePort: 80 + defaultBackends: + - backendName: app-without-opt-out +--- +# svc-with-opt-out: 3 endpoints in eu-central-1a — meets minEndpointsByZone, but with opt-out; should return all endpoints +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: app-with-opt-out + kubernetes.io/service-name: svc-with-opt-out + name: svc-with-opt-out-eps + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: eu-central-1a + - addresses: + - 10.3.0.4 + zone: eu-central-1a + - addresses: + - 10.3.0.5 + zone: eu-central-1a + - addresses: + - 10.3.1.3 + zone: eu-central-1b + - addresses: + - 10.3.1.4 + zone: eu-central-1b + - addresses: + - 10.3.1.5 + zone: eu-central-1b +ports: + - name: web + port: 8080 + protocol: TCP +apiVersion: v1 +--- +# svc-without-opt-out: only 3 endpoints in eu-central-1a — meets minEndpointsByZone +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: app-without-opt-out + kubernetes.io/service-name: svc-without-opt-out + name: svc-without-opt-out-eps + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: eu-central-1a + - addresses: + - 10.3.0.4 + zone: eu-central-1a + - addresses: + - 10.3.0.5 + zone: eu-central-1a + - addresses: + - 10.4.1.4 + zone: eu-central-1b +ports: + - name: web + port: 8080 + protocol: TCP +apiVersion: v1 diff --git a/eskip/eskip.go b/eskip/eskip.go index f02b9d28b2..43c67d19a7 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -347,6 +347,9 @@ type Route struct { // load balancing backends. LBEndpoints []*LBEndpoint + // EnableZoneAwareness indicates that the route should be processed with zone awareness. + EnableZoneAwareness string + // Name is deprecated and not used. Name string diff --git a/routesrv/polling.go b/routesrv/polling.go index 31aa566024..9c65f34760 100644 --- a/routesrv/polling.go +++ b/routesrv/polling.go @@ -141,6 +141,11 @@ func getZones(routes []*eskip.Route) map[string]struct{} { func getRouteForZone(zone string, route *eskip.Route) *eskip.Route { var zoneAwareEps []*eskip.LBEndpoint + + if route.EnableZoneAwareness == "false" { + return route + } + for _, eps := range route.LBEndpoints { if eps.Zone == zone { zoneAwareEps = append(zoneAwareEps, eps) diff --git a/routesrv/routesrv_test.go b/routesrv/routesrv_test.go index 13d1f496b1..33d2cf950f 100644 --- a/routesrv/routesrv_test.go +++ b/routesrv/routesrv_test.go @@ -1090,6 +1090,12 @@ func TestRoutesWithZone(t *testing.T) { ing: "testdata/zone-aware-traffic/mixed-zone-application-with-no-endpoints.yaml", eskip: "testdata/zone-aware-traffic/mixed-zone-application-with-no-endpoints.eskip", }, + { + name: "MixedZoneThresholdApplicationWithOptOut", + zone: "eu-central-1a", + ing: "testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml", + eskip: "testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip", + }, } { t.Run(tc.name, func(t *testing.T) { defer tl.Reset() diff --git a/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip b/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip new file mode 100644 index 0000000000..3fff078826 --- /dev/null +++ b/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.eskip @@ -0,0 +1,4 @@ +kube_default__ingress_with_opt_out__with_example_org____svc_with_opt_out: Host("^(with[.]example[.]org[.]?(:[0-9]+)?)$") + -> ; +kube_default__ingress_without_opt_out__without_example_org____svc_without_opt_out: Host("^(without[.]example[.]org[.]?(:[0-9]+)?)$") + -> ; diff --git a/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml b/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml new file mode 100644 index 0000000000..4971f44b54 --- /dev/null +++ b/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml @@ -0,0 +1,135 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: app-with-opt-out + name: svc-with-opt-out +spec: + clusterIP: 10.3.190.1 + ports: + - name: web + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: app-with-opt-out + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: app-without-opt-out + name: svc-without-opt-out +spec: + clusterIP: 10.3.190.2 + ports: + - name: web + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: app-with-opt-out + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: app-with-opt-out + name: ingress-with-opt-out + namespace: default + annotations: + zalando.org/traffic-zone-aware: "false" +spec: + rules: + - host: with.example.org + http: + paths: + - backend: + service: + name: svc-with-opt-out + port: + number: 80 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: app-without-opt-out + name: ingress-without-opt-out + namespace: default +spec: + rules: + - host: without.example.org + http: + paths: + - backend: + service: + name: svc-without-opt-out + port: + number: 80 + pathType: ImplementationSpecific +--- +# svc-with-opt-out: 3 endpoints in eu-central-1a — meets minEndpointsByZone, but with opt-out; should return all endpoints +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: app-with-opt-out + kubernetes.io/service-name: svc-with-opt-out + name: svc-with-opt-out-eps + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: eu-central-1a + - addresses: + - 10.3.0.4 + zone: eu-central-1a + - addresses: + - 10.3.0.5 + zone: eu-central-1a + - addresses: + - 10.3.1.3 + zone: eu-central-1b + - addresses: + - 10.3.1.4 + zone: eu-central-1b + - addresses: + - 10.3.1.5 + zone: eu-central-1b +ports: + - name: web + port: 8080 + protocol: TCP +apiVersion: v1 +--- +# svc-without-opt-out: only 3 endpoints in eu-central-1a — meets minEndpointsByZone +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: app-without-opt-out + kubernetes.io/service-name: svc-without-opt-out + name: svc-without-opt-out-eps + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: eu-central-1a + - addresses: + - 10.3.0.4 + zone: eu-central-1a + - addresses: + - 10.3.0.5 + zone: eu-central-1a + - addresses: + - 10.4.1.4 + zone: eu-central-1b +ports: + - name: web + port: 8080 + protocol: TCP +apiVersion: v1 From cfdce811d7dc849b1fa11bcc08da3c24850d8c50 Mon Sep 17 00:00:00 2001 From: greeshma1196 Date: Thu, 4 Jun 2026 16:08:35 +0200 Subject: [PATCH 2/5] - address staticcheck issues Signed-off-by: greeshma1196 --- dataclients/kubernetes/ingressv1.go | 4 ++-- dataclients/kubernetes/routegroup.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index 6fe10ab5e8..b71ebc40a4 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -103,7 +103,7 @@ func convertPathRuleV1( return nil, err } - zoneAwareTraffic, _ := metadata.Annotations[tafficZoneAwareAnnotationKey] + zoneAwareTraffic := metadata.Annotations[tafficZoneAwareAnnotationKey] servicePort, err := svc.getServicePortV1(svcPort) if err != nil { @@ -390,7 +390,7 @@ func (ing *ingress) convertDefaultBackendV1( return nil, false, err } - zoneAwareTraffic, _ := i.Metadata.Annotations[tafficZoneAwareAnnotationKey] + zoneAwareTraffic := i.Metadata.Annotations[tafficZoneAwareAnnotationKey] servicePort, err := svc.getServicePortV1(svcPort) if err != nil { diff --git a/dataclients/kubernetes/routegroup.go b/dataclients/kubernetes/routegroup.go index 0cee8fac55..426bc3d052 100644 --- a/dataclients/kubernetes/routegroup.go +++ b/dataclients/kubernetes/routegroup.go @@ -178,7 +178,7 @@ func applyServiceBackend(ctx *routeGroupContext, backend *definitions.SkipperBac protocol = p } - zoneAwareTraffic, _ := ctx.routeGroup.Metadata.Annotations[tafficZoneAwareAnnotationKey] + zoneAwareTraffic := ctx.routeGroup.Metadata.Annotations[tafficZoneAwareAnnotationKey] r.EnableZoneAwareness = zoneAwareTraffic s, err := getBackendService(ctx, backend) From 3b9f24a30e1cf195ef77a836a6c17d084c86ea0d Mon Sep 17 00:00:00 2001 From: greeshma1196 Date: Thu, 4 Jun 2026 16:36:40 +0200 Subject: [PATCH 3/5] - add unit test for `filterRoutesByZone` Signed-off-by: greeshma1196 --- routesrv/polling_test.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/routesrv/polling_test.go b/routesrv/polling_test.go index a799cf9999..1d81601c75 100644 --- a/routesrv/polling_test.go +++ b/routesrv/polling_test.go @@ -7,7 +7,7 @@ import ( "github.com/zalando/skipper/eskip" ) -func makeRoute(id string, zoneEndpoints map[string]int) *eskip.Route { +func makeRoute(id string, zoneEndpoints map[string]int, zoneAwareTraffic ...string) *eskip.Route { var eps []*eskip.LBEndpoint for zone, count := range zoneEndpoints { for i := range count { @@ -17,7 +17,11 @@ func makeRoute(id string, zoneEndpoints map[string]int) *eskip.Route { }) } } - return &eskip.Route{Id: id, LBEndpoints: eps} + zat := "" + if len(zoneAwareTraffic) > 0 { + zat = zoneAwareTraffic[0] + } + return &eskip.Route{Id: id, LBEndpoints: eps, EnableZoneAwareness: zat} } type routeExpect struct { @@ -173,6 +177,27 @@ func TestFilterRoutesByZone(t *testing.T) { wantZone1: []routeExpect{{id: "A", filtered: true}, {id: "B", filtered: false}}, wantZone2: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: false}}, }, + { + name: "A_z1=4_z2=4_opt_out / B_z1=4_z2=4_opt_out", + routeA: makeRoute("A", map[string]int{"zone1": 4, "zone2": 4}, "false"), + routeB: makeRoute("B", map[string]int{"zone1": 4, "zone2": 4}, "false"), + wantZone1: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: false}}, + wantZone2: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: false}}, + }, + { + name: "A_z1=4_z2=4_opt_out / B_z1=4_z2=4", + routeA: makeRoute("A", map[string]int{"zone1": 4, "zone2": 4}, "false"), + routeB: makeRoute("B", map[string]int{"zone1": 4, "zone2": 4}), + wantZone1: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: true}}, + wantZone2: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: true}}, + }, + { + name: "A_z1=2_z2=4_opt_out / B_z1=4_z2=1", + routeA: makeRoute("A", map[string]int{"zone1": 2, "zone2": 4}, "false"), + routeB: makeRoute("B", map[string]int{"zone1": 4, "zone2": 1}), + wantZone1: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: true}}, + wantZone2: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: false}}, + }, } for _, tc := range tests { From a227897e56964f33e0500ac6daccd173bdf5059f Mon Sep 17 00:00:00 2001 From: greeshma1196 Date: Thu, 4 Jun 2026 16:55:25 +0200 Subject: [PATCH 4/5] - fix typo Signed-off-by: greeshma1196 --- dataclients/kubernetes/ingress.go | 2 +- dataclients/kubernetes/ingressv1.go | 4 ++-- dataclients/kubernetes/routegroup.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dataclients/kubernetes/ingress.go b/dataclients/kubernetes/ingress.go index 7a03fe3b1c..c93422e0e8 100644 --- a/dataclients/kubernetes/ingress.go +++ b/dataclients/kubernetes/ingress.go @@ -24,7 +24,7 @@ const ( skipperLoadBalancerAnnotationKey = "zalando.org/skipper-loadbalancer" skipperBackendProtocolAnnotationKey = "zalando.org/skipper-backend-protocol" pathModeAnnotationKey = "zalando.org/skipper-ingress-path-mode" - tafficZoneAwareAnnotationKey = "zalando.org/traffic-zone-aware" + trafficZoneAwareAnnotationKey = "zalando.org/traffic-zone-aware" ingressOriginName = "ingress" tlsSecretType = "kubernetes.io/tls" tlsSecretDataCrt = "tls.crt" diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index b71ebc40a4..fb8b7d377b 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -103,7 +103,7 @@ func convertPathRuleV1( return nil, err } - zoneAwareTraffic := metadata.Annotations[tafficZoneAwareAnnotationKey] + zoneAwareTraffic := metadata.Annotations[trafficZoneAwareAnnotationKey] servicePort, err := svc.getServicePortV1(svcPort) if err != nil { @@ -390,7 +390,7 @@ func (ing *ingress) convertDefaultBackendV1( return nil, false, err } - zoneAwareTraffic := i.Metadata.Annotations[tafficZoneAwareAnnotationKey] + zoneAwareTraffic := i.Metadata.Annotations[trafficZoneAwareAnnotationKey] servicePort, err := svc.getServicePortV1(svcPort) if err != nil { diff --git a/dataclients/kubernetes/routegroup.go b/dataclients/kubernetes/routegroup.go index 426bc3d052..7bbb9fe82f 100644 --- a/dataclients/kubernetes/routegroup.go +++ b/dataclients/kubernetes/routegroup.go @@ -178,7 +178,7 @@ func applyServiceBackend(ctx *routeGroupContext, backend *definitions.SkipperBac protocol = p } - zoneAwareTraffic := ctx.routeGroup.Metadata.Annotations[tafficZoneAwareAnnotationKey] + zoneAwareTraffic := ctx.routeGroup.Metadata.Annotations[trafficZoneAwareAnnotationKey] r.EnableZoneAwareness = zoneAwareTraffic s, err := getBackendService(ctx, backend) From c10b38b9c81bff8482495e9d9c213acfd604c47e Mon Sep 17 00:00:00 2001 From: greeshma1196 Date: Fri, 5 Jun 2026 11:07:51 +0200 Subject: [PATCH 5/5] - fix bug in testdata - add missing test covering defaultBackend + opt-out - change EnableZoneAwareness from string to bool Signed-off-by: greeshma1196 --- dataclients/kubernetes/clusterstate.go | 8 +-- dataclients/kubernetes/ingressv1.go | 12 ++-- dataclients/kubernetes/routegroup.go | 6 +- .../default-backend-with-opt-out.eskip | 3 + .../default-backend-with-opt-out.kube | 2 + .../default-backend-with-opt-out.yaml | 63 +++++++++++++++++++ .../mixed-zone-threshold-with-opt-out.yaml | 2 +- .../mixed-zone-threshold-with-opt-out.yaml | 2 +- eskip/eskip.go | 4 +- routesrv/polling.go | 2 +- routesrv/polling_test.go | 14 ++--- .../mixed-zone-threshold-with-opt-out.yaml | 2 +- 12 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.yaml diff --git a/dataclients/kubernetes/clusterstate.go b/dataclients/kubernetes/clusterstate.go index 1f6ddfc879..1be6568c70 100644 --- a/dataclients/kubernetes/clusterstate.go +++ b/dataclients/kubernetes/clusterstate.go @@ -79,7 +79,7 @@ func (state *clusterState) GetEndpointsByService(namespace, name, protocol strin } // GetEndpointSlicesByService returns the skipper endpointslices for kubernetes endpointslices. -func (state *clusterState) GetEndpointSlicesByService(zone, namespace, name, protocol string, servicePort *servicePort, zoneAwareTraffic string) []skipperEndpoint { +func (state *clusterState) GetEndpointSlicesByService(zone, namespace, name, protocol string, servicePort *servicePort, zoneAwarenessDisabled bool) []skipperEndpoint { epID := endpointID{ ResourceID: newResourceID(namespace, name), Protocol: protocol, @@ -105,7 +105,7 @@ func (state *clusterState) GetEndpointSlicesByService(zone, namespace, name, pro state.cachedEndpointSlices[epID] = targets } - if zoneAwareTraffic == "false" { + if zoneAwarenessDisabled { return targets } @@ -171,7 +171,7 @@ func (state *clusterState) GetEndpointsByTarget(namespace, name, protocol, schem } // GetEndpointSlicesByTarget returns the skipper endpointslices for kubernetes endpointslices. -func (state *clusterState) GetEndpointSlicesByTarget(zone, namespace, name, protocol, scheme string, target *definitions.BackendPort, zoneAwareTraffic string) []skipperEndpoint { +func (state *clusterState) GetEndpointSlicesByTarget(zone, namespace, name, protocol, scheme string, target *definitions.BackendPort, zoneAwarenessDisabled bool) []skipperEndpoint { epID := endpointID{ ResourceID: newResourceID(namespace, name), Protocol: protocol, @@ -196,7 +196,7 @@ func (state *clusterState) GetEndpointSlicesByTarget(zone, namespace, name, prot state.cachedEndpointSlices[epID] = targets } - if zoneAwareTraffic == "false" { + if zoneAwarenessDisabled { return targets } diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index fb8b7d377b..be81487db6 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -103,7 +103,7 @@ func convertPathRuleV1( return nil, err } - zoneAwareTraffic := metadata.Annotations[trafficZoneAwareAnnotationKey] + zoneAwarenessDisabled := metadata.Annotations[trafficZoneAwareAnnotationKey] == "false" servicePort, err := svc.getServicePortV1(svcPort) if err != nil { @@ -127,7 +127,7 @@ func convertPathRuleV1( } if state.enableEndpointSlices { - epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort, zoneAwareTraffic) + epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort, zoneAwarenessDisabled) for _, ep := range epSlices { eps = append(eps, ep.Address) } @@ -181,7 +181,7 @@ func convertPathRuleV1( r.LBEndpoints = eskip.NewLBEndpoints(eps) } - r.EnableZoneAwareness = zoneAwareTraffic + r.DisableZoneAwareness = zoneAwarenessDisabled setPathV1(pathMode, r, prule.PathType, prule.Path) traffic.apply(r) @@ -390,7 +390,7 @@ func (ing *ingress) convertDefaultBackendV1( return nil, false, err } - zoneAwareTraffic := i.Metadata.Annotations[trafficZoneAwareAnnotationKey] + zoneAwarenessDisabled := i.Metadata.Annotations[trafficZoneAwareAnnotationKey] == "false" servicePort, err := svc.getServicePortV1(svcPort) if err != nil { @@ -413,7 +413,7 @@ func (ing *ingress) convertDefaultBackendV1( } if state.enableEndpointSlices { - epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort, zoneAwareTraffic) + epSlices = state.GetEndpointSlicesByService(dataclientZone, ns, svcName, protocol, servicePort, zoneAwarenessDisabled) for _, ep := range epSlices { eps = append(eps, ep.Address) } @@ -460,7 +460,7 @@ func (ing *ingress) convertDefaultBackendV1( r.LBEndpoints = eskip.NewLBEndpoints(eps) } - r.EnableZoneAwareness = zoneAwareTraffic + r.DisableZoneAwareness = zoneAwarenessDisabled return r, true, nil } diff --git a/dataclients/kubernetes/routegroup.go b/dataclients/kubernetes/routegroup.go index 7bbb9fe82f..f3d9a6db3a 100644 --- a/dataclients/kubernetes/routegroup.go +++ b/dataclients/kubernetes/routegroup.go @@ -178,8 +178,8 @@ func applyServiceBackend(ctx *routeGroupContext, backend *definitions.SkipperBac protocol = p } - zoneAwareTraffic := ctx.routeGroup.Metadata.Annotations[trafficZoneAwareAnnotationKey] - r.EnableZoneAwareness = zoneAwareTraffic + zoneAwarenessDisabled := ctx.routeGroup.Metadata.Annotations[trafficZoneAwareAnnotationKey] == "false" + r.DisableZoneAwareness = zoneAwarenessDisabled s, err := getBackendService(ctx, backend) if err != nil { @@ -201,7 +201,7 @@ func applyServiceBackend(ctx *routeGroupContext, backend *definitions.SkipperBac "TCP", protocol, targetPort, - zoneAwareTraffic) + zoneAwarenessDisabled) for _, epSlice := range epSlices { eps = append(eps, epSlice.Address) } diff --git a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.eskip b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.eskip new file mode 100644 index 0000000000..d2a585da9b --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.eskip @@ -0,0 +1,3 @@ +kube_default__ingress_default_backend_opt_out______: + * + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.kube b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.kube new file mode 100644 index 0000000000..a9fc3752d3 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.kube @@ -0,0 +1,2 @@ +enable-kubernetes-endpointslices: true +topology-zone: eu-central-1a diff --git a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.yaml b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.yaml new file mode 100644 index 0000000000..7c11113051 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/default-backend-with-opt-out.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: app-default-backend-opt-out + name: svc-default-backend-opt-out +spec: + clusterIP: 10.3.191.1 + ports: + - name: web + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: app-default-backend-opt-out + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: app-default-backend-opt-out + name: ingress-default-backend-opt-out + namespace: default + annotations: + zalando.org/traffic-zone-aware: "false" +spec: + defaultBackend: + service: + name: svc-default-backend-opt-out + port: + number: 80 +--- +# 3 endpoints in eu-central-1a, 2 in eu-central-1b; opt-out means all 5 are returned +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: app-default-backend-opt-out + kubernetes.io/service-name: svc-default-backend-opt-out + name: svc-default-backend-opt-out-eps + namespace: default +endpoints: + - addresses: + - 10.3.0.10 + zone: eu-central-1a + - addresses: + - 10.3.0.11 + zone: eu-central-1a + - addresses: + - 10.3.0.12 + zone: eu-central-1a + - addresses: + - 10.3.1.10 + zone: eu-central-1b + - addresses: + - 10.3.1.11 + zone: eu-central-1b +ports: + - name: web + port: 8080 + protocol: TCP +apiVersion: v1 diff --git a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml index 4971f44b54..f7a741b553 100644 --- a/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml +++ b/dataclients/kubernetes/testdata/ingressV1/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml @@ -29,7 +29,7 @@ spec: protocol: TCP targetPort: 8080 selector: - app: app-with-opt-out + app: app-without-opt-out type: ClusterIP --- apiVersion: networking.k8s.io/v1 diff --git a/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml index 409a3da3bd..f22d6f2b5f 100644 --- a/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml +++ b/dataclients/kubernetes/testdata/routegroups/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml @@ -29,7 +29,7 @@ spec: protocol: TCP targetPort: 8080 selector: - app: app-with-opt-out + app: app-without-opt-out type: ClusterIP --- apiVersion: networking.k8s.io/v1 diff --git a/eskip/eskip.go b/eskip/eskip.go index 43c67d19a7..fa0d03e701 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -347,8 +347,8 @@ type Route struct { // load balancing backends. LBEndpoints []*LBEndpoint - // EnableZoneAwareness indicates that the route should be processed with zone awareness. - EnableZoneAwareness string + // DisableZoneAwareness indicates that the route should not be processed with zone awareness. + DisableZoneAwareness bool // Name is deprecated and not used. Name string diff --git a/routesrv/polling.go b/routesrv/polling.go index 9c65f34760..e841a59e14 100644 --- a/routesrv/polling.go +++ b/routesrv/polling.go @@ -142,7 +142,7 @@ func getZones(routes []*eskip.Route) map[string]struct{} { func getRouteForZone(zone string, route *eskip.Route) *eskip.Route { var zoneAwareEps []*eskip.LBEndpoint - if route.EnableZoneAwareness == "false" { + if route.DisableZoneAwareness { return route } diff --git a/routesrv/polling_test.go b/routesrv/polling_test.go index 1d81601c75..81edc542d7 100644 --- a/routesrv/polling_test.go +++ b/routesrv/polling_test.go @@ -7,7 +7,7 @@ import ( "github.com/zalando/skipper/eskip" ) -func makeRoute(id string, zoneEndpoints map[string]int, zoneAwareTraffic ...string) *eskip.Route { +func makeRoute(id string, zoneEndpoints map[string]int, zoneAwareTraffic ...bool) *eskip.Route { var eps []*eskip.LBEndpoint for zone, count := range zoneEndpoints { for i := range count { @@ -17,11 +17,11 @@ func makeRoute(id string, zoneEndpoints map[string]int, zoneAwareTraffic ...stri }) } } - zat := "" + zat := false if len(zoneAwareTraffic) > 0 { zat = zoneAwareTraffic[0] } - return &eskip.Route{Id: id, LBEndpoints: eps, EnableZoneAwareness: zat} + return &eskip.Route{Id: id, LBEndpoints: eps, DisableZoneAwareness: zat} } type routeExpect struct { @@ -179,21 +179,21 @@ func TestFilterRoutesByZone(t *testing.T) { }, { name: "A_z1=4_z2=4_opt_out / B_z1=4_z2=4_opt_out", - routeA: makeRoute("A", map[string]int{"zone1": 4, "zone2": 4}, "false"), - routeB: makeRoute("B", map[string]int{"zone1": 4, "zone2": 4}, "false"), + routeA: makeRoute("A", map[string]int{"zone1": 4, "zone2": 4}, true), + routeB: makeRoute("B", map[string]int{"zone1": 4, "zone2": 4}, true), wantZone1: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: false}}, wantZone2: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: false}}, }, { name: "A_z1=4_z2=4_opt_out / B_z1=4_z2=4", - routeA: makeRoute("A", map[string]int{"zone1": 4, "zone2": 4}, "false"), + routeA: makeRoute("A", map[string]int{"zone1": 4, "zone2": 4}, true), routeB: makeRoute("B", map[string]int{"zone1": 4, "zone2": 4}), wantZone1: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: true}}, wantZone2: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: true}}, }, { name: "A_z1=2_z2=4_opt_out / B_z1=4_z2=1", - routeA: makeRoute("A", map[string]int{"zone1": 2, "zone2": 4}, "false"), + routeA: makeRoute("A", map[string]int{"zone1": 2, "zone2": 4}, true), routeB: makeRoute("B", map[string]int{"zone1": 4, "zone2": 1}), wantZone1: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: true}}, wantZone2: []routeExpect{{id: "A", filtered: false}, {id: "B", filtered: false}}, diff --git a/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml b/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml index 4971f44b54..f7a741b553 100644 --- a/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml +++ b/routesrv/testdata/zone-aware-traffic/mixed-zone-threshold-with-opt-out.yaml @@ -29,7 +29,7 @@ spec: protocol: TCP targetPort: 8080 selector: - app: app-with-opt-out + app: app-without-opt-out type: ClusterIP --- apiVersion: networking.k8s.io/v1