Skip to content

Commit e09c153

Browse files
MustafaSabergreeshma1196szuecs
authored
routesrv: fix zone aware routing table filtering (#3992)
There are three conditions where we are silently dropping routes: 1. If route endpoints isn't >= minEndpointsByZone 2. If route does not have any endpoints 3. If route does not have any endpoints for a zone We should add all routes, and if routes endpoints match the condition then we filter the endpoints to only get `zone` endpoints only. First commit is a reproducer test case (pipeline should fail) --------- Signed-off-by: Mustafa Abdelrahman <mustafa.abdelrahman@zalando.de> Signed-off-by: greeshma1196 <greeshma.mathew@gmail.com> Signed-off-by: Sandor Szücs <sandor.szuecs@zalando.de> Co-authored-by: greeshma1196 <greeshma.mathew@gmail.com> Co-authored-by: Sandor Szücs <sandor.szuecs@zalando.de>
1 parent d00b7af commit e09c153

16 files changed

Lines changed: 979 additions & 10 deletions
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kube_default__ingress_above_threshold__above_example_org____svc_above_threshold: Host("^(above[.]example[.]org[.]?(:[0-9]+)?)$")
2+
-> <roundRobin,"http://10.3.0.3:8080", "http://10.3.0.4:8080", "http://10.3.0.5:8080">;
3+
kube_default__ingress_below_threshold__below_example_org____svc_below_threshold: Host("^(below[.]example[.]org[.]?(:[0-9]+)?)$")
4+
-> <roundRobin,"http://10.4.0.3:8080", "http://10.4.1.4:8080">;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
enable-kubernetes-endpointslices: true
2+
topology-zone: eu-central-1a
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
labels:
5+
app: app-above-threshold
6+
name: svc-above-threshold
7+
spec:
8+
clusterIP: 10.3.190.1
9+
ports:
10+
- name: web
11+
port: 80
12+
protocol: TCP
13+
targetPort: 8080
14+
selector:
15+
app: app-above-threshold
16+
type: ClusterIP
17+
---
18+
apiVersion: networking.k8s.io/v1
19+
kind: Ingress
20+
metadata:
21+
labels:
22+
app: app-above-threshold
23+
name: ingress-above-threshold
24+
namespace: default
25+
spec:
26+
rules:
27+
- host: above.example.org
28+
http:
29+
paths:
30+
- backend:
31+
service:
32+
name: svc-above-threshold
33+
port:
34+
number: 80
35+
pathType: ImplementationSpecific
36+
---
37+
apiVersion: v1
38+
kind: EndpointSlice
39+
metadata:
40+
labels:
41+
app: app-above-threshold
42+
kubernetes.io/service-name: svc-above-threshold
43+
name: svc-above-threshold-eps
44+
namespace: default
45+
endpoints:
46+
- addresses:
47+
- 10.3.0.3
48+
zone: eu-central-1a
49+
- addresses:
50+
- 10.3.0.4
51+
zone: eu-central-1a
52+
- addresses:
53+
- 10.3.0.5
54+
zone: eu-central-1a
55+
- addresses:
56+
- 10.3.1.3
57+
zone: eu-central-1b
58+
- addresses:
59+
- 10.3.1.4
60+
zone: eu-central-1b
61+
- addresses:
62+
- 10.3.1.5
63+
zone: eu-central-1b
64+
ports:
65+
- name: web
66+
port: 8080
67+
protocol: TCP
68+
apiVersion: v1
69+
---
70+
apiVersion: v1
71+
kind: Service
72+
metadata:
73+
labels:
74+
app: app-below-threshold
75+
name: svc-below-threshold
76+
spec:
77+
clusterIP: 10.3.190.1
78+
ports:
79+
- name: web
80+
port: 80
81+
protocol: TCP
82+
targetPort: 8080
83+
selector:
84+
app: app-below-threshold
85+
type: ClusterIP
86+
---
87+
apiVersion: networking.k8s.io/v1
88+
kind: Ingress
89+
metadata:
90+
labels:
91+
app: app-below-threshold
92+
name: ingress-below-threshold
93+
namespace: default
94+
spec:
95+
rules:
96+
- host: below.example.org
97+
http:
98+
paths:
99+
- backend:
100+
service:
101+
name: svc-below-threshold
102+
port:
103+
number: 80
104+
pathType: ImplementationSpecific
105+
---
106+
apiVersion: v1
107+
kind: EndpointSlice
108+
metadata:
109+
labels:
110+
app: app-below-threshold
111+
kubernetes.io/service-name: svc-below-threshold
112+
name: svc-below-threshold-eps
113+
namespace: default
114+
endpoints:
115+
- addresses:
116+
- 10.4.0.3
117+
zone: eu-central-1a
118+
- addresses:
119+
- 10.4.1.4
120+
zone: eu-central-1b
121+
ports:
122+
- name: web
123+
port: 8080
124+
protocol: TCP
125+
apiVersion: v1
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kube_rg__default__rg_above_threshold__all__0_0: Host("^(above[.]example[.]org[.]?(:[0-9]+)?)$")
2+
-> <roundRobin,"http://10.3.0.3:8080", "http://10.3.0.4:8080", "http://10.3.0.5:8080">;
3+
kube_rg__default__rg_below_threshold__all__0_0: Host("^(below[.]example[.]org[.]?(:[0-9]+)?)$")
4+
-> <roundRobin,"http://10.4.0.3:8080", "http://10.4.1.4:8080">;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
enable-kubernetes-endpointslices: true
2+
topology-zone: eu-central-1a
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
labels:
5+
app: app-above-threshold
6+
name: svc-above-threshold
7+
spec:
8+
clusterIP: 10.3.190.1
9+
ports:
10+
- name: web
11+
port: 80
12+
protocol: TCP
13+
targetPort: 8080
14+
selector:
15+
app: app-above-threshold
16+
type: ClusterIP
17+
---
18+
apiVersion: zalando.org/v1
19+
kind: RouteGroup
20+
metadata:
21+
labels:
22+
app: app-above-threshold
23+
name: rg-above-threshold
24+
spec:
25+
hosts:
26+
- above.example.org
27+
backends:
28+
- name: app-above-threshold
29+
type: service
30+
serviceName: svc-above-threshold
31+
servicePort: 80
32+
defaultBackends:
33+
- backendName: app-above-threshold
34+
---
35+
apiVersion: v1
36+
kind: EndpointSlice
37+
metadata:
38+
labels:
39+
app: app-above-threshold
40+
kubernetes.io/service-name: svc-above-threshold
41+
name: svc-above-threshold-eps
42+
namespace: default
43+
endpoints:
44+
- addresses:
45+
- 10.3.0.3
46+
zone: eu-central-1a
47+
- addresses:
48+
- 10.3.0.4
49+
zone: eu-central-1a
50+
- addresses:
51+
- 10.3.0.5
52+
zone: eu-central-1a
53+
- addresses:
54+
- 10.3.1.3
55+
zone: eu-central-1b
56+
- addresses:
57+
- 10.3.1.4
58+
zone: eu-central-1b
59+
- addresses:
60+
- 10.3.1.5
61+
zone: eu-central-1b
62+
ports:
63+
- name: web
64+
port: 8080
65+
protocol: TCP
66+
apiVersion: v1
67+
---
68+
apiVersion: v1
69+
kind: Service
70+
metadata:
71+
labels:
72+
app: app-below-threshold
73+
name: svc-below-threshold
74+
spec:
75+
clusterIP: 10.3.190.1
76+
ports:
77+
- name: web
78+
port: 80
79+
protocol: TCP
80+
targetPort: 8080
81+
selector:
82+
app: app-below-threshold
83+
type: ClusterIP
84+
---
85+
apiVersion: zalando.org/v1
86+
kind: RouteGroup
87+
metadata:
88+
labels:
89+
app: app-below-threshold
90+
name: rg-below-threshold
91+
spec:
92+
hosts:
93+
- below.example.org
94+
backends:
95+
- name: app-below-threshold
96+
type: service
97+
serviceName: svc-below-threshold
98+
servicePort: 80
99+
defaultBackends:
100+
- backendName: app-below-threshold
101+
---
102+
apiVersion: v1
103+
kind: EndpointSlice
104+
metadata:
105+
labels:
106+
app: app-below-threshold
107+
kubernetes.io/service-name: svc-below-threshold
108+
name: svc-below-threshold-eps
109+
namespace: default
110+
endpoints:
111+
- addresses:
112+
- 10.4.0.3
113+
zone: eu-central-1a
114+
- addresses:
115+
- 10.4.1.4
116+
zone: eu-central-1b
117+
ports:
118+
- name: web
119+
port: 8080
120+
protocol: TCP
121+
apiVersion: v1

routesrv/polling.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,41 @@ func (p *poller) poll(wg *sync.WaitGroup) {
127127
}
128128
}
129129

130-
func filterRoutesByZone(routes []*eskip.Route) map[string][]*eskip.Route {
131-
zoneAwareRoutes := make(map[string][]*eskip.Route)
130+
func getZones(routes []*eskip.Route) map[string]struct{} {
131+
zones := make(map[string]struct{})
132132
for _, r := range routes {
133-
byZone := make(map[string][]*eskip.LBEndpoint)
134133
for _, ep := range r.LBEndpoints {
135134
if ep.Zone != "" {
136-
byZone[ep.Zone] = append(byZone[ep.Zone], ep)
135+
zones[ep.Zone] = struct{}{}
137136
}
138137
}
139-
for zone, eps := range byZone {
140-
if len(eps) >= minEndpointsByZone {
141-
rCopy := *r
142-
rCopy.LBEndpoints = eps
143-
zoneAwareRoutes[zone] = append(zoneAwareRoutes[zone], &rCopy)
144-
}
138+
}
139+
return zones
140+
}
141+
142+
func getRouteForZone(zone string, route *eskip.Route) *eskip.Route {
143+
var zoneAwareEps []*eskip.LBEndpoint
144+
for _, eps := range route.LBEndpoints {
145+
if eps.Zone == zone {
146+
zoneAwareEps = append(zoneAwareEps, eps)
147+
}
148+
}
149+
150+
if len(zoneAwareEps) >= minEndpointsByZone {
151+
routeCopy := *route
152+
routeCopy.LBEndpoints = zoneAwareEps
153+
return &routeCopy
154+
}
155+
return route
156+
}
157+
158+
func filterRoutesByZone(routes []*eskip.Route) map[string][]*eskip.Route {
159+
zones := getZones(routes)
160+
161+
zoneAwareRoutes := make(map[string][]*eskip.Route)
162+
for _, r := range routes {
163+
for zone := range zones {
164+
zoneAwareRoutes[zone] = append(zoneAwareRoutes[zone], getRouteForZone(zone, r))
145165
}
146166
}
147167
return zoneAwareRoutes

0 commit comments

Comments
 (0)