Skip to content

Commit c4f7873

Browse files
committed
usc: Add the MCP selector cache
1 parent 429ba19 commit c4f7873

File tree

3 files changed

+298
-95
lines changed

3 files changed

+298
-95
lines changed

pkg/updatestatus/controlplaneinformer_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ import (
66
"testing"
77
"time"
88

9-
kerrors "k8s.io/apimachinery/pkg/api/errors"
10-
119
"github.com/google/go-cmp/cmp"
1210
"github.com/google/go-cmp/cmp/cmpopts"
1311
"gopkg.in/yaml.v3"
1412
"k8s.io/utils/ptr"
1513

1614
appsv1 "k8s.io/api/apps/v1"
1715
corev1 "k8s.io/api/core/v1"
16+
kerrors "k8s.io/apimachinery/pkg/api/errors"
1817
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1918
"k8s.io/apimachinery/pkg/runtime"
2019
fakekubeclient "k8s.io/client-go/kubernetes/fake"
@@ -662,6 +661,12 @@ func newTestSyncContext(queueKey string) factory.SyncContext {
662661
return testSyncContext{
663662
queueKey: queueKey,
664663
eventRecorder: events.NewInMemoryRecorder("test", clocktesting.NewFakePassiveClock(time.Now())),
664+
queue: workqueue.NewTypedRateLimitingQueueWithConfig(
665+
workqueue.DefaultTypedControllerRateLimiter[any](),
666+
workqueue.TypedRateLimitingQueueConfig[any]{
667+
Name: "test",
668+
},
669+
),
665670
}
666671
}
667672

pkg/updatestatus/nodeinformer.go

Lines changed: 98 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ type nodeInformerController struct {
4040
// sendInsight should be called to send produced insights to the update status controller
4141
sendInsight sendInsightFn
4242

43+
// machineConfigPoolSelectorCache caches the label selectors converted from the node selectors of the machine config pools by their names.
44+
machineConfigPoolSelectorCache machineConfigPoolSelectorCache
45+
4346
// machineConfigVersionCache caches machine config versions
4447
// The cache stores the name of MC as the key and the release image version as its value which is retrieved from the annotation of the MC.
4548
machineConfigVersionCache machineConfigVersionCache
@@ -106,13 +109,19 @@ func (c *nodeInformerController) sync(ctx context.Context, syncCtx factory.SyncC
106109
return err
107110
}
108111

109-
pools, err := c.machineConfigPools.List(labels.Everything())
110-
if err != nil {
111-
return err
112+
mcpName := c.machineConfigPoolSelectorCache.whichMCP(labels.Set(node.Labels))
113+
if mcpName == "" {
114+
return fmt.Errorf("failed to determine which machine config pool the node %s belongs to", node.Name)
112115
}
113-
mcp, err := whichMCP(node, pools)
116+
klog.V(4).Infof("Node %s belongs to machine config pool %s", node.Name, mcpName)
117+
118+
mcp, err := c.machineConfigPools.Get(mcpName)
114119
if err != nil {
115-
return fmt.Errorf("failed to determine which machine config pool the node belongs to: %w", err)
120+
if kerrors.IsNotFound(err) {
121+
// it will be another event if a MCP is deleted
122+
return nil
123+
}
124+
return err
116125
}
117126

118127
var mostRecentVersionInCVHistory string
@@ -152,7 +161,23 @@ func (c *nodeInformerController) sync(ctx context.Context, syncCtx factory.SyncC
152161
}
153162
return nil
154163
case machineConfigPoolKindName:
155-
return c.reconcileAllNodes(syncCtx.Queue())
164+
machineConfigPool, err := c.machineConfigPools.Get(name)
165+
if err != nil && !kerrors.IsNotFound(err) {
166+
return err
167+
}
168+
if kerrors.IsNotFound(err) {
169+
// The pool was deleted
170+
if changed := c.machineConfigPoolSelectorCache.forget(name); changed {
171+
klog.V(2).Infof("Reconciling all nodes as machine config pool %q is deleted", name)
172+
return c.reconcileAllNodes(syncCtx.Queue())
173+
}
174+
return nil
175+
}
176+
if changed := c.machineConfigPoolSelectorCache.ingest(machineConfigPool); changed {
177+
klog.V(2).Infof("Reconciling all nodes as machine config pool %q is refreshed", name)
178+
return c.reconcileAllNodes(syncCtx.Queue())
179+
}
180+
return nil
156181
default:
157182
return fmt.Errorf("invalid queue key %s with unexpected type %s", queueKey, t)
158183
}
@@ -211,6 +236,73 @@ func (c *machineConfigVersionCache) match(config string) (string, bool) {
211236
return v, ok
212237
}
213238

239+
type machineConfigPoolSelectorCache struct {
240+
cache map[string]labels.Selector
241+
lock sync.Mutex
242+
}
243+
244+
func (c *machineConfigPoolSelectorCache) whichMCP(labels labels.Labels) string {
245+
c.lock.Lock()
246+
defer c.lock.Unlock()
247+
248+
if v, ok := c.cache[mco.MachineConfigPoolMaster]; ok && v.Matches(labels) {
249+
return mco.MachineConfigPoolMaster
250+
}
251+
252+
for k, v := range c.cache {
253+
if k == mco.MachineConfigPoolMaster || k == mco.MachineConfigPoolWorker {
254+
continue
255+
}
256+
if v.Matches(labels) {
257+
return k
258+
}
259+
}
260+
261+
if v, ok := c.cache[mco.MachineConfigPoolWorker]; ok && v.Matches(labels) {
262+
return mco.MachineConfigPoolWorker
263+
}
264+
265+
return ""
266+
}
267+
268+
func (c *machineConfigPoolSelectorCache) ingest(pool *machineconfigv1.MachineConfigPool) bool {
269+
c.lock.Lock()
270+
defer c.lock.Unlock()
271+
272+
v, ok := c.cache[pool.Name]
273+
s, err := metav1.LabelSelectorAsSelector(pool.Spec.NodeSelector)
274+
if err != nil {
275+
klog.Errorf("Failed to convert to a label selector from the node selector of MachineConfigPool %s : %v", pool.Name, err)
276+
if ok {
277+
delete(c.cache, pool.Name)
278+
klog.V(4).Infof("Deleted MachineConfigPool %s from the cache", pool.Name)
279+
return true
280+
} else {
281+
return false
282+
}
283+
}
284+
if !ok || v.String() != s.String() {
285+
if c.cache == nil {
286+
c.cache = make(map[string]labels.Selector)
287+
}
288+
c.cache[pool.Name] = s
289+
klog.V(4).Infof("Cached MachineConfigPool %s with selector %s", pool.Name, s.String())
290+
return true
291+
}
292+
return false
293+
}
294+
295+
func (c *machineConfigPoolSelectorCache) forget(mcpName string) bool {
296+
c.lock.Lock()
297+
defer c.lock.Unlock()
298+
if _, ok := c.cache[mcpName]; ok {
299+
delete(c.cache, mcpName)
300+
klog.V(4).Infof("Deleted MachineConfigPool %s from the cache", mcpName)
301+
return true
302+
}
303+
return false
304+
}
305+
214306
func (c *nodeInformerController) reconcileAllNodes(queue workqueue.TypedRateLimitingInterface[any]) error {
215307
nodes, err := c.nodes.List(labels.Everything())
216308
if err != nil {
@@ -235,41 +327,6 @@ func makeInsightMsgForNode(nodeInsight *updatestatus.NodeStatusInsight, acquired
235327
return makeWorkerPoolsInsightMsg(insight, nodesInformerName)
236328
}
237329

238-
func whichMCP(node *corev1.Node, pools []*machineconfigv1.MachineConfigPool) (*machineconfigv1.MachineConfigPool, error) {
239-
var masterSelector labels.Selector
240-
var workerSelector labels.Selector
241-
customSelectors := map[string]labels.Selector{}
242-
poolsMap := make(map[string]*machineconfigv1.MachineConfigPool, len(pools))
243-
for _, pool := range pools {
244-
poolsMap[pool.Name] = pool
245-
s, err := metav1.LabelSelectorAsSelector(pool.Spec.NodeSelector)
246-
if err != nil {
247-
return nil, fmt.Errorf("failed to get label selector from the pool %s: %w", pool.Name, err)
248-
}
249-
switch pool.Name {
250-
case mco.MachineConfigPoolMaster:
251-
masterSelector = s
252-
case mco.MachineConfigPoolWorker:
253-
workerSelector = s
254-
default:
255-
customSelectors[pool.Name] = s
256-
}
257-
}
258-
259-
if masterSelector != nil && masterSelector.Matches(labels.Set(node.Labels)) {
260-
return poolsMap[mco.MachineConfigPoolMaster], nil
261-
}
262-
for name, selector := range customSelectors {
263-
if selector.Matches(labels.Set(node.Labels)) {
264-
return poolsMap[name], nil
265-
}
266-
}
267-
if workerSelector != nil && workerSelector.Matches(labels.Set(node.Labels)) {
268-
return poolsMap[mco.MachineConfigPoolWorker], nil
269-
}
270-
return nil, fmt.Errorf("failed to find a matching node selector from %d machine config pools", len(pools))
271-
}
272-
273330
func isNodeDegraded(node *corev1.Node) bool {
274331
// Inspired by: https://github.com/openshift/machine-config-operator/blob/master/pkg/controller/node/status.go
275332
if node.Annotations == nil {

0 commit comments

Comments
 (0)