Skip to content

Commit 99bf43f

Browse files
committed
Use a field name selected cache
The controller-runtime is missing filtering it's cache by labels or fields [1], this means that all the kubernetes-nmstate-handlers will read all the nodes and nodenetworkstates every period, clearly dies does not scale since kubernetes-nmstate-handler runs at as daemonset meaning that there is one handler running at every node so the bigger the cluster the bigger the problem. This change replace the default controller-runtime cache with an implementation that can be configured to use some field selectors depending on the resource, this way we can filter by "metadata.name" using the node name for "node" and "nodenetworkstate" so only one instance of them is feteched. [1] kubernetes-sigs/controller-runtime#244 Signed-off-by: Quique Llorente <[email protected]>
1 parent 66b9357 commit 99bf43f

File tree

12 files changed

+968
-17
lines changed

12 files changed

+968
-17
lines changed

controllers/node_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (r *NodeReconciler) SetupWithManager(mgr ctrl.Manager) error {
110110
// but we only want to watch create/delete for current node.
111111
onCreationForThisNode := predicate.Funcs{
112112
CreateFunc: func(createEvent event.CreateEvent) bool {
113-
return nmstate.EventIsForThisNode(createEvent.Meta)
113+
return true
114114
},
115115
DeleteFunc: func(event.DeleteEvent) bool {
116116
return false

controllers/nodenetworkconfigurationpolicy_controller.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ var (
7878
return false
7979
},
8080
UpdateFunc: func(updateEvent event.UpdateEvent) bool {
81-
labelsChanged := !reflect.DeepEqual(updateEvent.MetaOld.GetLabels(), updateEvent.MetaNew.GetLabels())
82-
return labelsChanged && nmstate.EventIsForThisNode(updateEvent.MetaNew)
81+
return !reflect.DeepEqual(updateEvent.MetaOld.GetLabels(), updateEvent.MetaNew.GetLabels())
8382
},
8483
GenericFunc: func(event.GenericEvent) bool {
8584
return false
@@ -101,8 +100,9 @@ func init() {
101100
// NodeNetworkConfigurationPolicyReconciler reconciles a NodeNetworkConfigurationPolicy object
102101
type NodeNetworkConfigurationPolicyReconciler struct {
103102
client.Client
104-
Log logr.Logger
105-
Scheme *runtime.Scheme
103+
APIReader client.Reader
104+
Log logr.Logger
105+
Scheme *runtime.Scheme
106106
}
107107

108108
func (r *NodeNetworkConfigurationPolicyReconciler) waitEnactmentCreated(enactmentKey types.NamespacedName) error {
@@ -230,7 +230,7 @@ func (r *NodeNetworkConfigurationPolicyReconciler) Reconcile(request ctrl.Reques
230230
// Policy conditions will be updated at the end so updating it
231231
// does not impact at applying state, it will increase just
232232
// reconcile time.
233-
defer policyconditions.Update(r.Client, request.NamespacedName)
233+
defer policyconditions.Update(r.Client, r.APIReader, request.NamespacedName)
234234

235235
policySelectors := selectors.NewFromPolicy(r.Client, *instance)
236236
unmatchingNodeLabels, err := policySelectors.UnmatchedNodeLabels(nodeName)

controllers/nodenetworkstate_controller.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ func (r *NodeNetworkStateReconciler) SetupWithManager(mgr ctrl.Manager) error {
9494
return false
9595
},
9696
DeleteFunc: func(deleteEvent event.DeleteEvent) bool {
97-
return nmstate.EventIsForThisNode(deleteEvent.Meta)
97+
return true
9898
},
9999
UpdateFunc: func(updateEvent event.UpdateEvent) bool {
100-
return nmstate.EventIsForThisNode(updateEvent.MetaNew) &&
101-
shouldForceRefresh(updateEvent)
100+
return shouldForceRefresh(updateEvent)
101+
102102
},
103103
GenericFunc: func(event.GenericEvent) bool {
104104
return false

main.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
nmstatev1alpha1 "github.com/nmstate/kubernetes-nmstate/api/v1alpha1"
4242
nmstatev1beta1 "github.com/nmstate/kubernetes-nmstate/api/v1beta1"
4343
"github.com/nmstate/kubernetes-nmstate/controllers"
44+
"github.com/nmstate/kubernetes-nmstate/pkg/cache"
4445
"github.com/nmstate/kubernetes-nmstate/pkg/environment"
4546
"github.com/nmstate/kubernetes-nmstate/pkg/webhook"
4647
)
@@ -93,6 +94,11 @@ func main() {
9394
if environment.IsWebhook() {
9495
ctrlOptions.LeaderElection = true
9596
ctrlOptions.LeaderElectionID = "5d2e944a.nmstate.io"
97+
} else if environment.IsHandler() {
98+
ctrlOptions.NewCache = cache.Builder(cache.Options{FieldSelectorByResource: map[string]string{
99+
"nodes": fmt.Sprintf("metadata.name=%s", environment.NodeName()),
100+
"nodenetworkstates": fmt.Sprintf("metadata.name=%s", environment.NodeName()),
101+
}})
96102
}
97103

98104
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrlOptions)
@@ -156,9 +162,10 @@ func main() {
156162
os.Exit(1)
157163
}
158164
if err = (&controllers.NodeNetworkConfigurationPolicyReconciler{
159-
Client: mgr.GetClient(),
160-
Log: ctrl.Log.WithName("controllers").WithName("NodeNetworkConfigurationPolicy"),
161-
Scheme: mgr.GetScheme(),
165+
Client: mgr.GetClient(),
166+
APIReader: mgr.GetAPIReader(),
167+
Log: ctrl.Log.WithName("controllers").WithName("NodeNetworkConfigurationPolicy"),
168+
Scheme: mgr.GetScheme(),
162169
}).SetupWithManager(mgr); err != nil {
163170
setupLog.Error(err, "unable to create NodeNetworkConfigurationPolicy controller", "controller", "NMState")
164171
os.Exit(1)

pkg/cache/cache.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cache
18+
19+
import (
20+
"fmt"
21+
"time"
22+
23+
"github.com/nmstate/kubernetes-nmstate/pkg/cache/internal"
24+
"k8s.io/client-go/kubernetes/scheme"
25+
"k8s.io/client-go/rest"
26+
ctrl "sigs.k8s.io/controller-runtime"
27+
crcache "sigs.k8s.io/controller-runtime/pkg/cache"
28+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
29+
)
30+
31+
var log = ctrl.Log.WithName("object-cache")
32+
33+
var defaultResyncTime = 10 * time.Hour
34+
35+
// Options are the optional arguments for creating a new InformersMap object
36+
type Options struct {
37+
crcache.Options
38+
FieldSelectorByResource map[string]string
39+
}
40+
41+
// New initializes and returns a new Cache.
42+
func New(config *rest.Config, opts Options) (crcache.Cache, error) {
43+
opts, err := defaultOpts(config, opts)
44+
if err != nil {
45+
return nil, err
46+
}
47+
im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace, opts.FieldSelectorByResource)
48+
return &informerCache{InformersMap: im}, nil
49+
}
50+
51+
func Builder(opts Options) crcache.NewCacheFunc {
52+
return func(config *rest.Config, cropts crcache.Options) (crcache.Cache, error) {
53+
opts.Options = cropts
54+
return New(config, opts)
55+
}
56+
}
57+
58+
func defaultOpts(config *rest.Config, opts Options) (Options, error) {
59+
// Use the default Kubernetes Scheme if unset
60+
if opts.Scheme == nil {
61+
opts.Scheme = scheme.Scheme
62+
}
63+
64+
// Construct a new Mapper if unset
65+
if opts.Mapper == nil {
66+
var err error
67+
opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config)
68+
if err != nil {
69+
log.WithName("setup").Error(err, "Failed to get API Group-Resources")
70+
return opts, fmt.Errorf("could not create RESTMapper from config")
71+
}
72+
}
73+
74+
// Default the resync period to 10 hours if unset
75+
if opts.Resync == nil {
76+
opts.Resync = &defaultResyncTime
77+
}
78+
return opts, nil
79+
}

pkg/cache/doc.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package cache provides object caches that act as caching client.Reader
18+
// instances and help drive Kubernetes-object-based event handlers.
19+
package cache

0 commit comments

Comments
 (0)