Skip to content

Commit e43ec15

Browse files
author
OpenShift Bot
authored
Merge pull request #11768 from marun/bz1383663
Merged by openshift-bot
2 parents 556fe4b + 7b3e0d3 commit e43ec15

File tree

21 files changed

+247
-2
lines changed

21 files changed

+247
-2
lines changed

contrib/completions/bash/openshift

+2
Original file line numberDiff line numberDiff line change
@@ -20096,6 +20096,8 @@ _openshift_infra_router()
2009620096
local_nonpersistent_flags+=("--allowed-domains=")
2009720097
flags+=("--as=")
2009820098
local_nonpersistent_flags+=("--as=")
20099+
flags+=("--bind-ports-after-sync")
20100+
local_nonpersistent_flags+=("--bind-ports-after-sync")
2009920101
flags+=("--certificate-authority=")
2010020102
flags_with_completion+=("--certificate-authority")
2010120103
flags_completion+=("_filedir")

contrib/completions/zsh/openshift

+2
Original file line numberDiff line numberDiff line change
@@ -20257,6 +20257,8 @@ _openshift_infra_router()
2025720257
local_nonpersistent_flags+=("--allowed-domains=")
2025820258
flags+=("--as=")
2025920259
local_nonpersistent_flags+=("--as=")
20260+
flags+=("--bind-ports-after-sync")
20261+
local_nonpersistent_flags+=("--bind-ports-after-sync")
2026020262
flags+=("--certificate-authority=")
2026120263
flags_with_completion+=("--certificate-authority")
2026220264
flags_completion+=("_filedir")

docs/man/man1/openshift-infra-router.1

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ You may restrict the set of routes exposed to a single project (with \-\-namespa
4545
\fB\-\-as\fP=""
4646
Username to impersonate for the operation
4747

48+
.PP
49+
\fB\-\-bind\-ports\-after\-sync\fP=false
50+
Bind ports only after route state has been synchronized
51+
4852
.PP
4953
\fB\-\-certificate\-authority\fP=""
5054
Path to a cert. file for the certificate authority

images/router/haproxy/conf/haproxy-config.template

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ listen stats :1936
9494
stats auth {{.StatsUser}}:{{.StatsPassword}}
9595
{{ end }}
9696

97+
{{ if .BindPorts }}
9798
frontend public
9899
bind :{{env "ROUTER_SERVICE_HTTP_PORT" "80"}}
99100
mode http
@@ -523,6 +524,9 @@ backend be_secure_{{$cfgIdx}}
523524
{{ end }}{{/* end range over serviceUnitNames */}}
524525
{{ end }}{{/* end tls==reencrypt */}}
525526
{{ end }}{{/* end loop over routes */}}
527+
{{ else }}
528+
# Avoiding binding ports until routing configuration has been synchronized.
529+
{{ end }}{{/* end bind ports after sync */}}
526530
{{ end }}{{/* end haproxy config template */}}
527531

528532
{{/*--------------------------------- END OF HAPROXY CONFIG, BELOW ARE MAPPING FILES ------------------------*/}}

pkg/client/cache/eventqueue.go

+24
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ type EventQueue struct {
4949
// item it refers to is explicitly deleted from the store or the
5050
// event is read via Pop().
5151
lastReplaceKey string
52+
// Tracks whether the Replace() method has been called at least once.
53+
replaceCalled bool
54+
// Tracks the number of items queued by the last Replace() call.
55+
replaceCount int
5256
}
5357

5458
// EventQueue implements kcache.Store
@@ -322,6 +326,9 @@ func (eq *EventQueue) Replace(objects []interface{}, resourceVersion string) err
322326
eq.lock.Lock()
323327
defer eq.lock.Unlock()
324328

329+
eq.replaceCalled = true
330+
eq.replaceCount = len(objects)
331+
325332
eq.events = map[string]watch.EventType{}
326333
eq.queue = eq.queue[:0]
327334

@@ -346,6 +353,23 @@ func (eq *EventQueue) Replace(objects []interface{}, resourceVersion string) err
346353
return nil
347354
}
348355

356+
// ListSuccessfulAtLeastOnce indicates whether a List operation was
357+
// successfully completed regardless of whether any items were queued.
358+
func (eq *EventQueue) ListSuccessfulAtLeastOnce() bool {
359+
eq.lock.Lock()
360+
defer eq.lock.Unlock()
361+
362+
return eq.replaceCalled
363+
}
364+
365+
// ListCount returns how many objects were queued by the most recent List operation.
366+
func (eq *EventQueue) ListCount() int {
367+
eq.lock.Lock()
368+
defer eq.lock.Unlock()
369+
370+
return eq.replaceCount
371+
}
372+
349373
// ListConsumed indicates whether the items queued by a List/Relist
350374
// operation have been consumed.
351375
func (eq *EventQueue) ListConsumed() bool {

pkg/cmd/infra/router/template.go

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ type TemplateRouter struct {
6262
DefaultCertificateDir string
6363
ExtendedValidation bool
6464
RouterService *ktypes.NamespacedName
65+
BindPortsAfterSync bool
6566
}
6667

6768
// reloadInterval returns how often to run the router reloads. The interval
@@ -86,6 +87,7 @@ func (o *TemplateRouter) Bind(flag *pflag.FlagSet) {
8687
flag.StringVar(&o.ReloadScript, "reload", util.Env("RELOAD_SCRIPT", ""), "The path to the reload script to use")
8788
flag.DurationVar(&o.ReloadInterval, "interval", reloadInterval(), "Controls how often router reloads are invoked. Mutiple router reload requests are coalesced for the duration of this interval since the last reload time.")
8889
flag.BoolVar(&o.ExtendedValidation, "extended-validation", util.Env("EXTENDED_VALIDATION", "true") == "true", "If set, then an additional extended validation step is performed on all routes admitted in by this router. Defaults to true and enables the extended validation checks.")
90+
flag.BoolVar(&o.BindPortsAfterSync, "bind-ports-after-sync", util.Env("ROUTER_BIND_PORTS_AFTER_SYNC", "") == "true", "Bind ports only after route state has been synchronized")
8991
}
9092

9193
type RouterStats struct {
@@ -188,6 +190,7 @@ func (o *TemplateRouterOptions) Run() error {
188190
StatsUsername: o.StatsUsername,
189191
StatsPassword: o.StatsPassword,
190192
PeerService: o.RouterService,
193+
BindPortsAfterSync: o.BindPortsAfterSync,
191194
IncludeUDP: o.RouterSelection.IncludeUDP,
192195
AllowWildcardRoutes: o.RouterSelection.AllowWildcardRoutes,
193196
}

pkg/router/controller/controller.go

+50
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ type RouterController struct {
3737
endpointsListConsumed bool
3838
filteredByNamespace bool
3939

40+
RoutesListSuccessfulAtLeastOnce func() bool
41+
EndpointsListSuccessfulAtLeastOnce func() bool
42+
RoutesListCount func() int
43+
EndpointsListCount func() int
44+
4045
WatchNodes bool
4146

4247
Namespaces NamespaceLister
@@ -57,6 +62,51 @@ func (c *RouterController) Run() {
5762
if c.WatchNodes {
5863
go utilwait.Forever(c.HandleNode, 0)
5964
}
65+
go c.watchForFirstSync()
66+
}
67+
68+
// handleFirstSync signals the router when it sees that the various
69+
// watchers have successfully listed data from the api.
70+
func (c *RouterController) handleFirstSync() bool {
71+
c.lock.Lock()
72+
defer c.lock.Unlock()
73+
74+
synced := c.RoutesListSuccessfulAtLeastOnce() &&
75+
c.EndpointsListSuccessfulAtLeastOnce() &&
76+
(c.Namespaces == nil || c.filteredByNamespace)
77+
if !synced {
78+
return false
79+
}
80+
81+
// If either of the event queues were empty after the initial
82+
// List, the tracking listConsumed variable's default value of
83+
// 'false' may prevent the router from reloading to indicate the
84+
// readiness status. Set the value to 'true' to ensure that a
85+
// reload will be performed if necessary.
86+
if c.RoutesListCount() == 0 {
87+
c.routesListConsumed = true
88+
}
89+
if c.EndpointsListCount() == 0 {
90+
c.endpointsListConsumed = true
91+
}
92+
c.updateLastSyncProcessed()
93+
94+
err := c.Plugin.SetSyncedAtLeastOnce()
95+
if err == nil {
96+
return true
97+
}
98+
utilruntime.HandleError(err)
99+
return false
100+
}
101+
102+
// watchForFirstSync loops until the first sync has been handled.
103+
func (c *RouterController) watchForFirstSync() {
104+
for {
105+
if c.handleFirstSync() {
106+
return
107+
}
108+
time.Sleep(50 * time.Millisecond)
109+
}
60110
}
61111

62112
func (c *RouterController) HandleNamespaces() {

pkg/router/controller/controller_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
type fakeRouterPlugin struct {
1414
lastSyncProcessed bool
15+
syncedAtLeastOnce bool
1516
}
1617

1718
func (p *fakeRouterPlugin) HandleRoute(t watch.EventType, route *routeapi.Route) error {
@@ -26,11 +27,17 @@ func (p *fakeRouterPlugin) HandleEndpoints(watch.EventType, *kapi.Endpoints) err
2627
func (p *fakeRouterPlugin) HandleNamespaces(namespaces sets.String) error {
2728
return nil
2829
}
30+
2931
func (p *fakeRouterPlugin) SetLastSyncProcessed(processed bool) error {
3032
p.lastSyncProcessed = processed
3133
return nil
3234
}
3335

36+
func (p *fakeRouterPlugin) SetSyncedAtLeastOnce() error {
37+
p.syncedAtLeastOnce = true
38+
return nil
39+
}
40+
3441
type fakeNamespaceLister struct {
3542
}
3643

pkg/router/controller/extended_validator.go

+4
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,7 @@ func (p *ExtendedValidator) HandleNamespaces(namespaces sets.String) error {
8282
func (p *ExtendedValidator) SetLastSyncProcessed(processed bool) error {
8383
return p.plugin.SetLastSyncProcessed(processed)
8484
}
85+
86+
func (p *ExtendedValidator) SetSyncedAtLeastOnce() error {
87+
return p.plugin.SetSyncedAtLeastOnce()
88+
}

pkg/router/controller/factory/factory.go

+12
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ func (factory *RouterControllerFactory) Create(plugin router.Plugin, watchNodes
100100
}
101101
return eventType, obj.(*kapi.Node), nil
102102
},
103+
EndpointsListCount: func() int {
104+
return endpointsEventQueue.ListCount()
105+
},
106+
RoutesListCount: func() int {
107+
return routeEventQueue.ListCount()
108+
},
109+
EndpointsListSuccessfulAtLeastOnce: func() bool {
110+
return endpointsEventQueue.ListSuccessfulAtLeastOnce()
111+
},
112+
RoutesListSuccessfulAtLeastOnce: func() bool {
113+
return routeEventQueue.ListSuccessfulAtLeastOnce()
114+
},
103115
EndpointsListConsumed: func() bool {
104116
return endpointsEventQueue.ListConsumed()
105117
},

pkg/router/controller/host_admitter.go

+4
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ func (p *HostAdmitter) SetLastSyncProcessed(processed bool) error {
152152
return p.plugin.SetLastSyncProcessed(processed)
153153
}
154154

155+
func (p *HostAdmitter) SetSyncedAtLeastOnce() error {
156+
return p.plugin.SetSyncedAtLeastOnce()
157+
}
158+
155159
// addRoute admits routes based on subdomain ownership - returns errors if the route is not admitted.
156160
func (p *HostAdmitter) addRoute(route *routeapi.Route) error {
157161
// Find displaced routes (or error if an existing route displaces us)

pkg/router/controller/status.go

+4
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,7 @@ func (a *StatusAdmitter) HandleNamespaces(namespaces sets.String) error {
315315
func (a *StatusAdmitter) SetLastSyncProcessed(processed bool) error {
316316
return a.plugin.SetLastSyncProcessed(processed)
317317
}
318+
319+
func (a *StatusAdmitter) SetSyncedAtLeastOnce() error {
320+
return a.plugin.SetSyncedAtLeastOnce()
321+
}

pkg/router/controller/status_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ func (p *fakePlugin) SetLastSyncProcessed(processed bool) error {
4343
return fmt.Errorf("not expected")
4444
}
4545

46+
func (p *fakePlugin) SetSyncedAtLeastOnce() error {
47+
return fmt.Errorf("not expected")
48+
}
49+
4650
func TestStatusNoOp(t *testing.T) {
4751
now := nowFn()
4852
touched := unversioned.Time{Time: now.Add(-time.Minute)}

pkg/router/controller/unique_host.go

+4
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ func (p *UniqueHost) SetLastSyncProcessed(processed bool) error {
259259
return p.plugin.SetLastSyncProcessed(processed)
260260
}
261261

262+
func (p *UniqueHost) SetSyncedAtLeastOnce() error {
263+
return p.plugin.SetSyncedAtLeastOnce()
264+
}
265+
262266
// routeKeys returns the internal router key to use for the given Route.
263267
func routeKeys(route *routeapi.Route) []string {
264268
keys := make([]string, 1+len(route.Spec.AlternateBackends))

pkg/router/f5/plugin.go

+5
Original file line numberDiff line numberDiff line change
@@ -633,3 +633,8 @@ func (p *F5Plugin) HandleRoute(eventType watch.EventType,
633633
func (p *F5Plugin) SetLastSyncProcessed(processed bool) error {
634634
return nil
635635
}
636+
637+
// No-op since f5 has its own concept of what 'ready' means
638+
func (p *F5Plugin) SetSyncedAtLeastOnce() error {
639+
return nil
640+
}

pkg/router/interfaces.go

+1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ type Plugin interface {
1717
HandleNamespaces(namespaces sets.String) error
1818
HandleNode(watch.EventType, *kapi.Node) error
1919
SetLastSyncProcessed(processed bool) error
20+
SetSyncedAtLeastOnce() error
2021
}

pkg/router/template/plugin.go

+11
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type TemplatePluginConfig struct {
5050
IncludeUDP bool
5151
AllowWildcardRoutes bool
5252
PeerService *ktypes.NamespacedName
53+
BindPortsAfterSync bool
5354
}
5455

5556
// routerInterface controls the interaction of the plugin with the underlying router implementation
@@ -89,6 +90,9 @@ type routerInterface interface {
8990

9091
// SetSkipCommit indicates to the router whether commits should be skipped
9192
SetSkipCommit(skipCommit bool)
93+
94+
// SetSyncedAtLeastOnce indicates to the router that state has been read from the api at least once
95+
SetSyncedAtLeastOnce()
9296
}
9397

9498
func env(name, defaultValue string) string {
@@ -144,6 +148,7 @@ func NewTemplatePlugin(cfg TemplatePluginConfig, lookupSvc ServiceLookup) (*Temp
144148
statsPort: cfg.StatsPort,
145149
allowWildcardRoutes: cfg.AllowWildcardRoutes,
146150
peerEndpointsKey: peerKey,
151+
bindPortsAfterSync: cfg.BindPortsAfterSync,
147152
}
148153
router, err := newTemplateRouter(templateRouterCfg)
149154
return newDefaultTemplatePlugin(router, cfg.IncludeUDP, lookupSvc), err
@@ -240,6 +245,12 @@ func (p *TemplatePlugin) SetLastSyncProcessed(processed bool) error {
240245
return nil
241246
}
242247

248+
func (p *TemplatePlugin) SetSyncedAtLeastOnce() error {
249+
p.Router.SetSyncedAtLeastOnce()
250+
p.Router.Commit()
251+
return nil
252+
}
253+
243254
// routeKeys returns the internal router keys to use for the given Route.
244255
// A route can have several services that it can point to, now
245256
func routeKeys(route *routeapi.Route) ([]string, []int32) {

pkg/router/template/plugin_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ func (r *TestRouter) Commit() {
243243
func (r *TestRouter) SetSkipCommit(skipCommit bool) {
244244
}
245245

246+
func (r *TestRouter) SetSyncedAtLeastOnce() {
247+
}
248+
246249
func (r *TestRouter) HasServiceUnit(key string) bool {
247250
return false
248251
}

pkg/router/template/router.go

+16
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ type templateRouter struct {
8686
lock sync.Mutex
8787
// the router should only reload when the value is false
8888
skipCommit bool
89+
// If true, haproxy should only bind ports when it has route and endpoint state
90+
bindPortsAfterSync bool
91+
// whether the router state has been read from the api at least once
92+
syncedAtLeastOnce bool
8993
}
9094

9195
// templateRouterCfg holds all configuration items required to initialize the template router
@@ -103,6 +107,7 @@ type templateRouterCfg struct {
103107
allowWildcardRoutes bool
104108
peerEndpointsKey string
105109
includeUDP bool
110+
bindPortsAfterSync bool
106111
}
107112

108113
// templateConfig is a subset of the templateRouter information that should be passed to the template for generating
@@ -124,6 +129,8 @@ type templateData struct {
124129
StatsPassword string
125130
//port to expose stats with (if the template supports it)
126131
StatsPort int
132+
// whether the router should bind the default ports
133+
BindPorts bool
127134
}
128135

129136
func newTemplateRouter(cfg templateRouterCfg) (*templateRouter, error) {
@@ -162,6 +169,7 @@ func newTemplateRouter(cfg templateRouterCfg) (*templateRouter, error) {
162169
allowWildcardRoutes: cfg.allowWildcardRoutes,
163170
peerEndpointsKey: cfg.peerEndpointsKey,
164171
peerEndpoints: []Endpoint{},
172+
bindPortsAfterSync: cfg.bindPortsAfterSync,
165173

166174
rateLimitedCommitFunction: nil,
167175
rateLimitedCommitStopChannel: make(chan struct{}),
@@ -406,6 +414,7 @@ func (r *templateRouter) writeConfig() error {
406414
StatsUser: r.statsUser,
407415
StatsPassword: r.statsPassword,
408416
StatsPort: r.statsPort,
417+
BindPorts: !r.bindPortsAfterSync || r.syncedAtLeastOnce,
409418
}
410419
if err := template.Execute(file, data); err != nil {
411420
file.Close()
@@ -741,6 +750,13 @@ func (r *templateRouter) SetSkipCommit(skipCommit bool) {
741750
}
742751
}
743752

753+
// SetSyncedAtLeastOnce indicates to the router that state has been
754+
// read from the api.
755+
func (r *templateRouter) SetSyncedAtLeastOnce() {
756+
r.syncedAtLeastOnce = true
757+
glog.V(4).Infof("Router state synchronized for the first time")
758+
}
759+
744760
// HasServiceUnit attempts to retrieve a service unit for the given
745761
// key, returning a boolean indication of whether the key is known.
746762
func (r *templateRouter) HasServiceUnit(key string) bool {

0 commit comments

Comments
 (0)