Skip to content

Commit c9f43f7

Browse files
author
Ravi Sankar Penta
committed
Sharded router based on namespace labels should notice routes immediately
- Currently, sharded router based on namespace labels could take 2 resync intervals (10 to 15 mins) to notice new routes which may not be acceptable to some customers. This change allows routes to work immediately just like the non-sharded router behavior. - Watching project resource may not guarantee the order of the events, so there is no behavior change to shared router based on project labels.
1 parent 5295973 commit c9f43f7

File tree

5 files changed

+220
-117
lines changed

5 files changed

+220
-117
lines changed

pkg/cmd/infra/router/router.go

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ import (
88
"github.com/golang/glog"
99
"github.com/spf13/pflag"
1010

11-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1211
"k8s.io/apimachinery/pkg/fields"
1312
"k8s.io/apimachinery/pkg/labels"
1413
"k8s.io/apimachinery/pkg/util/sets"
1514
kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
16-
kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
1715

1816
cmdutil "github.com/openshift/origin/pkg/cmd/util"
1917
"github.com/openshift/origin/pkg/cmd/util/variable"
@@ -212,18 +210,18 @@ func (o *RouterSelection) Complete() error {
212210

213211
// NewFactory initializes a factory that will watch the requested routes
214212
func (o *RouterSelection) NewFactory(routeclient routeinternalclientset.Interface, projectclient projectclient.ProjectResourceInterface, kc kclientset.Interface) *controllerfactory.RouterControllerFactory {
215-
factory := controllerfactory.NewDefaultRouterControllerFactory(routeclient, kc)
213+
factory := controllerfactory.NewDefaultRouterControllerFactory(routeclient, projectclient, kc)
216214
factory.LabelSelector = o.LabelSelector
217215
factory.FieldSelector = o.FieldSelector
218216
factory.Namespace = o.Namespace
219217
factory.ResyncInterval = o.ResyncInterval
220218
switch {
221219
case o.NamespaceLabels != nil:
222220
glog.Infof("Router is only using routes in namespaces matching %s", o.NamespaceLabels)
223-
factory.Namespaces = namespaceNames{kc.Core().Namespaces(), o.NamespaceLabels}
221+
factory.NamespaceLabels = o.NamespaceLabels
224222
case o.ProjectLabels != nil:
225223
glog.Infof("Router is only using routes in projects matching %s", o.ProjectLabels)
226-
factory.Namespaces = projectNames{projectclient, o.ProjectLabels}
224+
factory.ProjectLabels = o.ProjectLabels
227225
case len(factory.Namespace) > 0:
228226
glog.Infof("Router is only using resources in namespace %s", factory.Namespace)
229227
default:
@@ -232,42 +230,6 @@ func (o *RouterSelection) NewFactory(routeclient routeinternalclientset.Interfac
232230
return factory
233231
}
234232

235-
// projectNames returns the names of projects matching the label selector
236-
type projectNames struct {
237-
client projectclient.ProjectResourceInterface
238-
selector labels.Selector
239-
}
240-
241-
func (n projectNames) NamespaceNames() (sets.String, error) {
242-
all, err := n.client.List(metav1.ListOptions{LabelSelector: n.selector.String()})
243-
if err != nil {
244-
return nil, err
245-
}
246-
names := make(sets.String, len(all.Items))
247-
for i := range all.Items {
248-
names.Insert(all.Items[i].Name)
249-
}
250-
return names, nil
251-
}
252-
253-
// namespaceNames returns the names of namespaces matching the label selector
254-
type namespaceNames struct {
255-
client kcoreclient.NamespaceInterface
256-
selector labels.Selector
257-
}
258-
259-
func (n namespaceNames) NamespaceNames() (sets.String, error) {
260-
all, err := n.client.List(metav1.ListOptions{LabelSelector: n.selector.String()})
261-
if err != nil {
262-
return nil, err
263-
}
264-
names := make(sets.String, len(all.Items))
265-
for i := range all.Items {
266-
names.Insert(all.Items[i].Name)
267-
}
268-
return names, nil
269-
}
270-
271233
func envVarAsStrings(name, defaultValue, separator string) []string {
272234
strlist := []string{}
273235
if env := cmdutil.Env(name, defaultValue); env != "" {

pkg/router/controller/factory/factory.go

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@ import (
99
"github.com/golang/glog"
1010

1111
"k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/labels"
1213
"k8s.io/apimachinery/pkg/runtime"
1314
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
15+
"k8s.io/apimachinery/pkg/util/sets"
1416
utilwait "k8s.io/apimachinery/pkg/util/wait"
1517
"k8s.io/apimachinery/pkg/watch"
1618
kcache "k8s.io/client-go/tools/cache"
1719
kapi "k8s.io/kubernetes/pkg/api"
1820
"k8s.io/kubernetes/pkg/apis/extensions"
1921
kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
2022

23+
projectclient "github.com/openshift/origin/pkg/project/generated/internalclientset/typed/project/internalversion"
2124
routeapi "github.com/openshift/origin/pkg/route/apis/route"
2225
routeclientset "github.com/openshift/origin/pkg/route/generated/internalclientset"
2326
"github.com/openshift/origin/pkg/router"
@@ -29,23 +32,26 @@ import (
2932
// controller. It supports optional scoping on Namespace, Labels, and Fields of routes.
3033
// If Namespace is empty, it means "all namespaces".
3134
type RouterControllerFactory struct {
32-
KClient kclientset.Interface
33-
RClient routeclientset.Interface
35+
KClient kclientset.Interface
36+
RClient routeclientset.Interface
37+
ProjectClient projectclient.ProjectResourceInterface
3438

35-
Namespaces routercontroller.NamespaceLister
36-
ResyncInterval time.Duration
37-
Namespace string
38-
LabelSelector string
39-
FieldSelector string
39+
ResyncInterval time.Duration
40+
Namespace string
41+
LabelSelector string
42+
FieldSelector string
43+
NamespaceLabels labels.Selector
44+
ProjectLabels labels.Selector
4045

4146
informers map[reflect.Type]kcache.SharedIndexInformer
4247
}
4348

4449
// NewDefaultRouterControllerFactory initializes a default router controller factory.
45-
func NewDefaultRouterControllerFactory(rc routeclientset.Interface, kc kclientset.Interface) *RouterControllerFactory {
50+
func NewDefaultRouterControllerFactory(rc routeclientset.Interface, pc projectclient.ProjectResourceInterface, kc kclientset.Interface) *RouterControllerFactory {
4651
return &RouterControllerFactory{
4752
KClient: kc,
4853
RClient: rc,
54+
ProjectClient: pc,
4955
ResyncInterval: 10 * time.Minute,
5056

5157
Namespace: v1.NamespaceAll,
@@ -57,17 +63,23 @@ func NewDefaultRouterControllerFactory(rc routeclientset.Interface, kc kclientse
5763
// resources. It spawns child goroutines that cannot be terminated.
5864
func (f *RouterControllerFactory) Create(plugin router.Plugin, watchNodes, enableIngress bool) *routercontroller.RouterController {
5965
rc := &routercontroller.RouterController{
60-
Plugin: plugin,
61-
Namespaces: f.Namespaces,
62-
// check namespaces a bit more often than we resync events, so that we aren't always waiting
66+
Plugin: plugin,
67+
WatchNodes: watchNodes,
68+
EnableIngress: enableIngress,
69+
IngressTranslator: routercontroller.NewIngressTranslator(f.KClient.Core()),
70+
71+
NamespaceLabels: f.NamespaceLabels,
72+
FilteredNamespaceNames: make(sets.String),
73+
NamespaceRoutes: make(map[string]map[string]*routeapi.Route),
74+
NamespaceEndpoints: make(map[string]map[string]*kapi.Endpoints),
75+
76+
ProjectClient: f.ProjectClient,
77+
ProjectLabels: f.ProjectLabels,
78+
// Check projects a bit more often than we resync events, so that we aren't always waiting
6379
// the maximum interval for new items to come into the list
64-
// TODO: trigger a reflector resync after every namespace sync?
65-
NamespaceSyncInterval: f.ResyncInterval - 10*time.Second,
66-
NamespaceWaitInterval: 10 * time.Second,
67-
NamespaceRetries: 5,
68-
WatchNodes: watchNodes,
69-
EnableIngress: enableIngress,
70-
IngressTranslator: routercontroller.NewIngressTranslator(f.KClient.Core()),
80+
ProjectSyncInterval: f.ResyncInterval - 10*time.Second,
81+
ProjectWaitInterval: 10 * time.Second,
82+
ProjectRetries: 5,
7183
}
7284

7385
f.initInformers(rc)
@@ -77,13 +89,15 @@ func (f *RouterControllerFactory) Create(plugin router.Plugin, watchNodes, enabl
7789
}
7890

7991
func (f *RouterControllerFactory) initInformers(rc *routercontroller.RouterController) {
92+
if f.NamespaceLabels != nil {
93+
f.createNamespacesSharedInformer(rc)
94+
}
8095
f.createEndpointsSharedInformer(rc)
8196
f.createRoutesSharedInformer(rc)
8297

8398
if rc.WatchNodes {
8499
f.createNodesSharedInformer(rc)
85100
}
86-
87101
if rc.EnableIngress {
88102
f.createIngressesSharedInformer(rc)
89103
f.createSecretsSharedInformer(rc)
@@ -102,6 +116,9 @@ func (f *RouterControllerFactory) initInformers(rc *routercontroller.RouterContr
102116
}
103117

104118
func (f *RouterControllerFactory) registerInformerEventHandlers(rc *routercontroller.RouterController) {
119+
if f.NamespaceLabels != nil {
120+
f.registerSharedInformerEventHandlers(&kapi.Namespace{}, rc.HandleNamespace)
121+
}
105122
f.registerSharedInformerEventHandlers(&kapi.Endpoints{}, rc.HandleEndpoints)
106123
f.registerSharedInformerEventHandlers(&routeapi.Route{}, rc.HandleRoute)
107124

@@ -135,6 +152,17 @@ func (f *RouterControllerFactory) informerStoreList(obj runtime.Object) []interf
135152
// - Perform first router sync
136153
// - Register informer event handlers for new updates and resyncs
137154
func (f *RouterControllerFactory) processExistingItems(rc *routercontroller.RouterController) {
155+
if f.NamespaceLabels != nil {
156+
items := f.informerStoreList(&kapi.Namespace{})
157+
if len(items) == 0 {
158+
rc.UpdateNamespaces()
159+
} else {
160+
for _, item := range items {
161+
rc.HandleNamespace(watch.Added, item.(*kapi.Namespace))
162+
}
163+
}
164+
}
165+
138166
for _, item := range f.informerStoreList(&kapi.Endpoints{}) {
139167
rc.HandleEndpoints(watch.Added, item.(*kapi.Endpoints))
140168
}
@@ -255,6 +283,24 @@ func (f *RouterControllerFactory) createSecretsSharedInformer(rc *routercontroll
255283
f.informers[objType] = informer
256284
}
257285

286+
func (f *RouterControllerFactory) createNamespacesSharedInformer(rc *routercontroller.RouterController) {
287+
lw := &kcache.ListWatch{
288+
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
289+
options.LabelSelector = f.NamespaceLabels.String()
290+
return f.KClient.Core().Namespaces().List(options)
291+
},
292+
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
293+
options.LabelSelector = f.NamespaceLabels.String()
294+
return f.KClient.Core().Namespaces().Watch(options)
295+
},
296+
}
297+
ns := &kapi.Namespace{}
298+
objType := reflect.TypeOf(ns)
299+
indexers := kcache.Indexers{kcache.NamespaceIndex: kcache.MetaNamespaceIndexFunc}
300+
informer := kcache.NewSharedIndexInformer(lw, ns, f.ResyncInterval, indexers)
301+
f.informers[objType] = informer
302+
}
303+
258304
func (f *RouterControllerFactory) registerSharedInformerEventHandlers(obj runtime.Object,
259305
handleFunc func(watch.EventType, interface{})) {
260306
objType := reflect.TypeOf(obj)

0 commit comments

Comments
 (0)