Skip to content

Commit

Permalink
Use a field name selected cache
Browse files Browse the repository at this point in the history
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 <ellorent@redhat.com>
  • Loading branch information
qinqon committed Feb 1, 2021
1 parent e0daac1 commit 99ab801
Show file tree
Hide file tree
Showing 12 changed files with 968 additions and 17 deletions.
2 changes: 1 addition & 1 deletion controllers/node_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (r *NodeReconciler) SetupWithManager(mgr ctrl.Manager) error {
// but we only want to watch create/delete for current node.
onCreationForThisNode := predicate.Funcs{
CreateFunc: func(createEvent event.CreateEvent) bool {
return nmstate.EventIsForThisNode(createEvent.Meta)
return true
},
DeleteFunc: func(event.DeleteEvent) bool {
return false
Expand Down
10 changes: 5 additions & 5 deletions controllers/nodenetworkconfigurationpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ var (
return false
},
UpdateFunc: func(updateEvent event.UpdateEvent) bool {
labelsChanged := !reflect.DeepEqual(updateEvent.MetaOld.GetLabels(), updateEvent.MetaNew.GetLabels())
return labelsChanged && nmstate.EventIsForThisNode(updateEvent.MetaNew)
return !reflect.DeepEqual(updateEvent.MetaOld.GetLabels(), updateEvent.MetaNew.GetLabels())
},
GenericFunc: func(event.GenericEvent) bool {
return false
Expand All @@ -101,8 +100,9 @@ func init() {
// NodeNetworkConfigurationPolicyReconciler reconciles a NodeNetworkConfigurationPolicy object
type NodeNetworkConfigurationPolicyReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
APIReader client.Reader
Log logr.Logger
Scheme *runtime.Scheme
}

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

policySelectors := selectors.NewFromPolicy(r.Client, *instance)
unmatchingNodeLabels, err := policySelectors.UnmatchedNodeLabels(nodeName)
Expand Down
6 changes: 3 additions & 3 deletions controllers/nodenetworkstate_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ func (r *NodeNetworkStateReconciler) SetupWithManager(mgr ctrl.Manager) error {
return false
},
DeleteFunc: func(deleteEvent event.DeleteEvent) bool {
return nmstate.EventIsForThisNode(deleteEvent.Meta)
return true
},
UpdateFunc: func(updateEvent event.UpdateEvent) bool {
return nmstate.EventIsForThisNode(updateEvent.MetaNew) &&
shouldForceRefresh(updateEvent)
return shouldForceRefresh(updateEvent)

},
GenericFunc: func(event.GenericEvent) bool {
return false
Expand Down
13 changes: 10 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
nmstatev1alpha1 "github.com/nmstate/kubernetes-nmstate/api/v1alpha1"
nmstatev1beta1 "github.com/nmstate/kubernetes-nmstate/api/v1beta1"
"github.com/nmstate/kubernetes-nmstate/controllers"
"github.com/nmstate/kubernetes-nmstate/pkg/cache"
"github.com/nmstate/kubernetes-nmstate/pkg/environment"
"github.com/nmstate/kubernetes-nmstate/pkg/webhook"
)
Expand Down Expand Up @@ -93,6 +94,11 @@ func main() {
if environment.IsWebhook() {
ctrlOptions.LeaderElection = true
ctrlOptions.LeaderElectionID = "5d2e944a.nmstate.io"
} else if environment.IsHandler() {
ctrlOptions.NewCache = cache.Builder(cache.Options{FieldSelectorByResource: map[string]string{
"nodes": fmt.Sprintf("metadata.name=%s", environment.NodeName()),
"nodenetworkstates": fmt.Sprintf("metadata.name=%s", environment.NodeName()),
}})
}

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrlOptions)
Expand Down Expand Up @@ -151,9 +157,10 @@ func main() {
os.Exit(1)
}
if err = (&controllers.NodeNetworkConfigurationPolicyReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("NodeNetworkConfigurationPolicy"),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
APIReader: mgr.GetAPIReader(),
Log: ctrl.Log.WithName("controllers").WithName("NodeNetworkConfigurationPolicy"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create NodeNetworkConfigurationPolicy controller", "controller", "NMState")
os.Exit(1)
Expand Down
79 changes: 79 additions & 0 deletions pkg/cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cache

import (
"fmt"
"time"

"github.com/nmstate/kubernetes-nmstate/pkg/cache/internal"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
crcache "sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)

var log = ctrl.Log.WithName("object-cache")

var defaultResyncTime = 10 * time.Hour

// Options are the optional arguments for creating a new InformersMap object
type Options struct {
crcache.Options
FieldSelectorByResource map[string]string
}

// New initializes and returns a new Cache.
func New(config *rest.Config, opts Options) (crcache.Cache, error) {
opts, err := defaultOpts(config, opts)
if err != nil {
return nil, err
}
im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace, opts.FieldSelectorByResource)
return &informerCache{InformersMap: im}, nil
}

func Builder(opts Options) crcache.NewCacheFunc {
return func(config *rest.Config, cropts crcache.Options) (crcache.Cache, error) {
opts.Options = cropts
return New(config, opts)
}
}

func defaultOpts(config *rest.Config, opts Options) (Options, error) {
// Use the default Kubernetes Scheme if unset
if opts.Scheme == nil {
opts.Scheme = scheme.Scheme
}

// Construct a new Mapper if unset
if opts.Mapper == nil {
var err error
opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config)
if err != nil {
log.WithName("setup").Error(err, "Failed to get API Group-Resources")
return opts, fmt.Errorf("could not create RESTMapper from config")
}
}

// Default the resync period to 10 hours if unset
if opts.Resync == nil {
opts.Resync = &defaultResyncTime
}
return opts, nil
}
19 changes: 19 additions & 0 deletions pkg/cache/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package cache provides object caches that act as caching client.Reader
// instances and help drive Kubernetes-object-based event handlers.
package cache

0 comments on commit 99ab801

Please sign in to comment.