Skip to content

Commit 3649d4c

Browse files
committed
BUG/MEDIUM: servicePort should be resolved before constructing
backendName We used to construct backendName with either: - service.Namespace-service.Name-path.ServicePortString - service.Namespace-service.Name-path.ServicePortInt Depending on the format of servicePort provided in Ingress Rule. The problem with this implementation is that we can have two backends pointing to the same endpoints (one with PortString and one with PortInt) while only one backendName can be associated to PortEndpoints for runtime updates. This leads to potential backend desync at runtime. This has been fixed by resolving ServicePort from Ingress Rule to get the corresponding servicePort name and use it in backendName: - service.Namespace-service.Name-service.Port.Name
1 parent 67b6ac4 commit 3649d4c

File tree

6 files changed

+56
-46
lines changed

6 files changed

+56
-46
lines changed

controller/controller.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,11 @@ func (c *HAProxyController) updateHAProxy() {
209209
}
210210
// Default Backend
211211
if ingress.DefaultBackend != nil {
212-
c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
212+
logger.Error(c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
213213
Namespace: namespace,
214214
Ingress: ingress,
215215
Path: ingress.DefaultBackend,
216-
})
216+
}))
217217
}
218218
// Ingress secrets
219219
logger.Tracef("ingress '%s/%s': processing secrets...", ingress.Namespace, ingress.Name)
@@ -241,14 +241,14 @@ func (c *HAProxyController) updateHAProxy() {
241241
logger.Tracef("ingress '%s/%s': processing rules...", ingress.Namespace, ingress.Name)
242242
for _, rule := range ingress.Rules {
243243
for _, path := range rule.Paths {
244-
c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
244+
logger.Error(c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
245245
Namespace: namespace,
246246
Ingress: ingress,
247247
Host: rule.Host,
248248
Path: path,
249249
HAProxyRules: c.cfg.HAProxyRules.GetIngressRuleIDs(ingress.Name),
250250
SSLPassthrough: c.sslPassthroughEnabled(namespace, ingress, path),
251-
})
251+
}))
252252
}
253253
}
254254
}
@@ -451,13 +451,14 @@ func (c *HAProxyController) handlePprof() (err error) {
451451
return err
452452
}
453453
logger.Debug("pprof backend created")
454-
c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
454+
logger.Error(c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
455455
Path: &store.IngressPath{
456456
Path: "/debug/pprof",
457457
ExactPathMatch: false,
458458
},
459-
BackendName: pprofBackend,
460-
})
459+
BackendName: pprofBackend,
460+
LocalBackend: true,
461+
}))
461462
return nil
462463
}
463464

@@ -497,11 +498,11 @@ func (c *HAProxyController) handleDefaultService() {
497498
ServicePortInt: service.Ports[0].Port,
498499
IsDefaultBackend: true,
499500
}
500-
c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
501+
logger.Error(c.cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
501502
Namespace: namespace,
502503
Ingress: ingress,
503504
Path: path,
504-
})
505+
}))
505506
}
506507

507508
// handleDefaultCert configures default/fallback HAProxy certificate to use for client HTTPS requests.

controller/handler-tcp.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@ func (t TCPHandler) Update(k store.K8s, cfg *Configuration, api api.HAProxyClien
116116
Status: svc.Status,
117117
}
118118
nsmmp := k.GetNamespace(namespace)
119-
cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
119+
logger.Error(cfg.IngressRoutes.AddRoute(&ingressRoute.Route{
120120
Namespace: nsmmp,
121121
Ingress: ingress,
122122
Path: path,
123123
TCPService: true,
124-
})
124+
}))
125125
}
126126
return reload, err
127127
}

controller/ingress/endpoints.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -188,19 +188,12 @@ func (route *Route) getEndpoints() {
188188
}
189189
return
190190
}
191-
for _, sp := range route.service.Ports {
192-
if sp.Name == route.Path.ServicePortString || sp.Port == route.Path.ServicePortInt {
193-
if endpoints, ok := endpoints.Ports[sp.Name]; ok {
194-
route.endpoints = endpoints
195-
return
196-
}
197-
logger.Warningf("ingress %s/%s: no matching endpoints for service '%s' and port '%s'", route.Namespace.Name, route.Ingress.Name, route.service.Name, sp.Name)
191+
if route.Path.ResolvedSvcPort != "" {
192+
portName := route.Path.ResolvedSvcPort
193+
if endpoints, ok := endpoints.Ports[portName]; ok {
194+
route.endpoints = endpoints
198195
return
199196
}
200197
}
201-
ingressPort := route.Path.ServicePortString
202-
if route.Path.ServicePortInt != 0 {
203-
ingressPort = fmt.Sprintf("%d", route.Path.ServicePortInt)
204-
}
205-
logger.Warningf("ingress %s/%s: service %s: no service port matching '%s'", route.Namespace.Name, route.Ingress.Name, route.service.Name, ingressPort)
198+
logger.Warningf("ingress %s/%s: no matching endpoints for service '%s' and port '%d:%s'", route.Namespace.Name, route.Ingress.Name, route.service.Name, route.Path.ServicePortInt, route.Path.ServicePortString)
206199
}

controller/ingress/route.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type Route struct {
3737
Host string
3838
BackendName string
3939
NewBackend bool
40+
LocalBackend bool
4041
SSLPassthrough bool
4142
TCPService bool
4243
reload bool
@@ -81,15 +82,8 @@ func (route *Route) addToMapFile(mapFiles haproxy.Maps) error {
8182
return nil
8283
}
8384

84-
// handleService processes an Ingress Route and make corresponding backend configuration in HAProxy
85-
func (route *Route) handleService() (err error) {
86-
// Set backendName
87-
if route.Path.ServicePortInt == 0 {
88-
route.BackendName = fmt.Sprintf("%s-%s-%s", route.Namespace.Name, route.service.Name, route.Path.ServicePortString)
89-
} else {
90-
route.BackendName = fmt.Sprintf("%s-%s-%d", route.Namespace.Name, route.service.Name, route.Path.ServicePortInt)
91-
}
92-
85+
// handleBackend processes an Ingress Route and makes corresponding backend configuration in HAProxy
86+
func (route *Route) handleBackend() (err error) {
9387
// Get/Create Backend
9488
var backend models.Backend
9589
if backend, err = client.BackendGet(route.BackendName); err != nil {
@@ -105,7 +99,6 @@ func (route *Route) handleService() (err error) {
10599
route.NewBackend = true
106100
route.reload = true
107101
}
108-
109102
// Update Backend
110103
var switchMode bool
111104
if backend.Mode == "http" {
@@ -137,6 +130,29 @@ func (route *Route) handleService() (err error) {
137130
return nil
138131
}
139132

133+
// SetBackendName checks if Ingress ServiceName and ServicePort exists and construct corresponding backend name
134+
func (route *Route) SetBackendName() (err error) {
135+
route.service = route.Namespace.Services[route.Path.ServiceName]
136+
if route.service == nil {
137+
return fmt.Errorf("ingress %s/%s: service '%s' not found", route.Namespace.Name, route.Ingress.Name, route.Path.ServiceName)
138+
}
139+
route.Path.ResolvedSvcPort = ""
140+
for _, sp := range route.service.Ports {
141+
if sp.Name == route.Path.ServicePortString || sp.Port == route.Path.ServicePortInt {
142+
route.Path.ResolvedSvcPort = sp.Name
143+
break
144+
}
145+
}
146+
if route.Path.ResolvedSvcPort == "" {
147+
if route.Path.ServicePortInt != 0 {
148+
return fmt.Errorf("ingress %s/%s: service %s: no service port matching '%d'", route.Namespace.Name, route.Ingress.Name, route.service.Name, route.Path.ServicePortInt)
149+
}
150+
return fmt.Errorf("ingress %s/%s: service %s: no service port matching '%s'", route.Namespace.Name, route.Ingress.Name, route.service.Name, route.Path.ServicePortString)
151+
}
152+
route.BackendName = fmt.Sprintf("%s-%s-%s", route.service.Namespace, route.service.Name, route.Path.ResolvedSvcPort)
153+
return nil
154+
}
155+
140156
func (route *Route) setStatus() {
141157
if route.Path.Status == DELETED || route.service.Status == DELETED {
142158
route.status = DELETED

controller/ingress/routes.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,12 @@ const (
5454
PATH_PREFIX = "path-prefix"
5555
)
5656

57-
func (r *Routes) AddRoute(route *Route) {
58-
route.service = route.Namespace.Services[route.Path.ServiceName]
59-
if route.service == nil {
60-
logger.Warningf("ingress %s/%s: service '%s' not found", route.Namespace.Name, route.Ingress.Name, route.Path.ServiceName)
61-
return
57+
func (r *Routes) AddRoute(route *Route) (err error) {
58+
if route.BackendName == "" {
59+
err = route.SetBackendName()
60+
if err != nil {
61+
return err
62+
}
6263
}
6364
route.setStatus()
6465
switch {
@@ -69,6 +70,7 @@ func (r *Routes) AddRoute(route *Route) {
6970
default:
7071
r.http = append(r.http, route)
7172
}
73+
return nil
7274
}
7375

7476
func (r *Routes) Refresh(c api.HAProxyClient, k store.K8s, mapFiles haproxy.Maps, certs *haproxy.Certificates) (reload bool, activeBackends map[string]struct{}) {
@@ -90,11 +92,8 @@ func (r *Routes) refreshHTTP(mapFiles haproxy.Maps) {
9092
r.reload = true
9193
continue
9294
}
93-
// Configure Route backend
94-
// BackendName != "" then it is a local backend
95-
// Example: Pprof
96-
if route.BackendName == "" {
97-
if err := route.handleService(); err != nil {
95+
if !route.LocalBackend {
96+
if err := route.handleBackend(); err != nil {
9897
logger.Error(err)
9998
continue
10099
}
@@ -115,7 +114,7 @@ func (r *Routes) refreshHTTPDefault() {
115114
// pick latest pushed default route
116115
for _, route := range r.httpDefault {
117116
if route.status != DELETED {
118-
err := route.handleService()
117+
err := route.handleBackend()
119118
if err != nil {
120119
logger.Error(err)
121120
continue
@@ -154,7 +153,7 @@ func (r *Routes) refreshTCP() {
154153
if route.status == DELETED {
155154
continue
156155
}
157-
if err := route.handleService(); err != nil {
156+
if err := route.handleBackend(); err != nil {
158157
logger.Error(err)
159158
continue
160159
}

controller/store/types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type HAProxySrv struct {
3232
// PortEndpoints describes endpionts of a service port
3333
type PortEndpoints struct {
3434
Port int64
35-
BackendName string
35+
BackendName string // For runtime operations
3636
DynUpdateFailed bool
3737
AddrCount int
3838
AddrNew map[string]struct{}
@@ -83,6 +83,7 @@ type IngressPath struct {
8383
ServiceName string
8484
ServicePortInt int64
8585
ServicePortString string
86+
ResolvedSvcPort string
8687
Path string
8788
ExactPathMatch bool
8889
IsDefaultBackend bool

0 commit comments

Comments
 (0)