Skip to content

Commit fd11270

Browse files
committed
contextstore
1 parent c59901f commit fd11270

3 files changed

Lines changed: 96 additions & 86 deletions

File tree

pkg/konnector/controllers/cluster/serviceexport/serviceexport_controller.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535

3636
"github.com/kube-bind/kube-bind/pkg/committer"
3737
"github.com/kube-bind/kube-bind/pkg/indexers"
38+
"github.com/kube-bind/kube-bind/pkg/konnector/controllers/contextstore"
3839
"github.com/kube-bind/kube-bind/pkg/konnector/controllers/dynamic"
3940
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
4041
bindclient "github.com/kube-bind/kube-bind/sdk/client/clientset/versioned"
@@ -94,7 +95,7 @@ func NewController(
9495
consumerConfig: consumerConfig,
9596
providerConfig: providerConfig,
9697

97-
syncContext: map[string]syncContext{},
98+
syncStore: contextstore.New(),
9899

99100
getServiceBinding: func(name string) (*kubebindv1alpha2.APIServiceBinding, error) {
100101
return serviceBindingInformer.Lister().Get(name)
@@ -287,27 +288,27 @@ func (c *controller) processNextWorkItem(ctx context.Context) bool {
287288
}
288289

289290
func (c *controller) process(ctx context.Context, key string) error {
290-
ns, name, err := cache.SplitMetaNamespaceKey(key)
291+
namespace, name, err := cache.SplitMetaNamespaceKey(key)
291292
if err != nil {
292293
runtime.HandleError(err)
293294
return nil // we cannot do anything
294295
}
295296

296297
logger := klog.FromContext(ctx)
297298

298-
obj, err := c.serviceExportLister.APIServiceExports(ns).Get(name)
299+
obj, err := c.serviceExportLister.APIServiceExports(namespace).Get(name)
299300
if err != nil && !errors.IsNotFound(err) {
300301
return err
301302
} else if errors.IsNotFound(err) {
302303
logger.Error(err, "APIServiceExport disappeared")
303-
return c.reconcile(ctx, name, nil)
304+
return c.reconcile(ctx, namespace, name, nil)
304305
}
305306

306307
old := obj
307308
obj = obj.DeepCopy()
308309

309310
var errs []error
310-
if err := c.reconcile(ctx, name, obj); err != nil {
311+
if err := c.reconcile(ctx, namespace, name, obj); err != nil {
311312
errs = append(errs, err)
312313
}
313314

pkg/konnector/controllers/cluster/serviceexport/serviceexport_reconcile.go

Lines changed: 42 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"maps"
2323
"slices"
2424
"strings"
25-
"sync"
2625
"time"
2726

2827
corev1 "k8s.io/api/core/v1"
@@ -41,6 +40,7 @@ import (
4140
"github.com/kube-bind/kube-bind/pkg/konnector/controllers/cluster/serviceexport/multinsinformer"
4241
"github.com/kube-bind/kube-bind/pkg/konnector/controllers/cluster/serviceexport/spec"
4342
"github.com/kube-bind/kube-bind/pkg/konnector/controllers/cluster/serviceexport/status"
43+
"github.com/kube-bind/kube-bind/pkg/konnector/controllers/contextstore"
4444
"github.com/kube-bind/kube-bind/pkg/konnector/controllers/dynamic"
4545
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
4646
conditionsapi "github.com/kube-bind/kube-bind/sdk/apis/third_party/conditions/apis/conditions/v1alpha1"
@@ -56,8 +56,7 @@ type reconciler struct {
5656

5757
consumerConfig, providerConfig *rest.Config
5858

59-
lock sync.Mutex
60-
syncContext map[string]syncContext // by CRD name
59+
syncStore contextstore.Store // by APIServiceExport name. This includes same ctx for resourc claims and crds.
6160

6261
getCRD func(name string) (*apiextensionsv1.CustomResourceDefinition, error)
6362
getServiceBinding func(name string) (*kubebindv1alpha2.APIServiceBinding, error)
@@ -66,15 +65,10 @@ type reconciler struct {
6665
updateRemoteBoundSchema func(ctx context.Context, boundSchema *kubebindv1alpha2.BoundSchema) error
6766
}
6867

69-
type syncContext struct {
70-
generation int64
71-
cancel func()
72-
}
73-
74-
func (r *reconciler) reconcile(ctx context.Context, name string, export *kubebindv1alpha2.APIServiceExport) error {
68+
func (r *reconciler) reconcile(ctx context.Context, namespace, name string, export *kubebindv1alpha2.APIServiceExport) error {
7569
errs := []error{}
7670

77-
if err := r.ensureControllers(ctx, name, export); err != nil {
71+
if err := r.ensureControllers(ctx, namespace, name, export); err != nil {
7872
errs = append(errs, err)
7973
}
8074

@@ -90,22 +84,17 @@ func (r *reconciler) reconcile(ctx context.Context, name string, export *kubebin
9084
return utilerrors.NewAggregate(errs)
9185
}
9286

93-
func (r *reconciler) ensureControllers(ctx context.Context, name string, export *kubebindv1alpha2.APIServiceExport) error {
87+
func (r *reconciler) ensureControllers(ctx context.Context, namespace, name string, export *kubebindv1alpha2.APIServiceExport) error {
9488
logger := klog.FromContext(ctx)
89+
exportKey := contextstore.Key(namespace + "." + name) // Key for the export
9590

9691
if export == nil {
97-
// stop dangling syncers on delete
98-
r.lock.Lock()
99-
defer r.lock.Unlock()
100-
10192
// Clean up any controllers associated with this export
102-
for key, c := range r.syncContext {
103-
if strings.HasSuffix(key, "."+name) {
104-
logger.V(1).Info("Stopping APIServiceExport sync", "key", key, "reason", "APIServiceExport deleted")
105-
c.cancel()
106-
delete(r.syncContext, key)
107-
}
93+
deleted := r.syncStore.BulkDeletePrefixed(exportKey)
94+
for _, k := range deleted {
95+
logger.V(1).Info("Stopping APIServiceExport sync", "key", k.Key(), "reason", "NoAPIServiceExport")
10896
}
97+
10998
return nil
11099
}
111100

@@ -116,14 +105,9 @@ func (r *reconciler) ensureControllers(ctx context.Context, name string, export
116105
}
117106
if binding == nil {
118107
// Stop all controllers for this export
119-
r.lock.Lock()
120-
defer r.lock.Unlock()
121-
for key, c := range r.syncContext {
122-
if strings.HasSuffix(key, "."+export.Name) {
123-
logger.V(1).Info("Stopping APIServiceExport sync", "key", key, "reason", "NoAPIServiceBinding")
124-
c.cancel()
125-
delete(r.syncContext, key)
126-
}
108+
deleted := r.syncStore.BulkDeletePrefixed(exportKey)
109+
for _, k := range deleted {
110+
logger.V(1).Info("Stopping APIServiceExport sync", "key", k.Key(), "reason", "NoAPIServiceBinding")
127111
}
128112
return nil
129113
}
@@ -139,15 +123,12 @@ func (r *reconciler) ensureControllers(ctx context.Context, name string, export
139123
schema, err := r.getRemoteBoundSchema(ctx, name)
140124
if err != nil {
141125
if errors.IsNotFound(err) {
142-
// Stop the controller for this schema if it exists
143-
r.lock.Lock()
144-
key := name + "." + export.Name
145-
if c, found := r.syncContext[key]; found {
146-
logger.V(1).Info("Stopping APIServiceExport resource sync", "key", key, "reason", "BoundSchema not found")
147-
c.cancel()
148-
delete(r.syncContext, key)
126+
// Stop the controller for this schema if it does not exists.
127+
key := contextstore.NewKey(namespace, name, name)
128+
deleted := r.syncStore.BulkDeletePrefixed(key)
129+
for _, k := range deleted {
130+
logger.V(1).Info("Stopping APIServiceExport sync", "key", k.Key(), "reason", "BoundSchema not found")
149131
}
150-
r.lock.Unlock()
151132
continue
152133
}
153134
errs = append(errs, err)
@@ -159,50 +140,45 @@ func (r *reconciler) ensureControllers(ctx context.Context, name string, export
159140
errs = append(errs, err)
160141
}
161142

162-
processedSchemas[name] = true
143+
processedSchemas[name] = true // This is only schemas names (suffix)
163144
isClusterScoped = schema.Spec.Scope == apiextensionsv1.ClusterScoped || schema.Spec.InformerScope == kubebindv1alpha2.ClusterScope
164145
}
165146

166147
// Ensure controller for permission claims
167-
if err := r.ensureControllersForPermissionClaims(ctx, binding, isClusterScoped); err != nil {
148+
if err := r.ensureControllersForPermissionClaims(ctx, export, binding, isClusterScoped); err != nil {
168149
errs = append(errs, err)
169150
}
170151

171-
// Stop controllers for schemas that are no longer referenced
172-
r.lock.Lock()
173-
for key, c := range r.syncContext {
174-
parts := strings.Split(key, ".")
175-
if len(parts) == 2 && parts[1] == export.Name {
176-
schemaName := parts[0]
177-
if !processedSchemas[schemaName] {
178-
logger.V(1).Info("Stopping APIServiceExport resource sync", "key", key, "reason", "Schema no longer referenced")
179-
c.cancel()
180-
delete(r.syncContext, key)
181-
}
152+
// Stop controllers for schemas that are no longer referenced.
153+
// This will be `exportNamespace.exportName.<schemaName>`
154+
contexts := r.syncStore.ListPrefixed(exportKey)
155+
for _, c := range contexts {
156+
schemaName := strings.TrimPrefix(string(c.Key()), string(exportKey)+".") // schemaName will be processed schema names.
157+
if !processedSchemas[schemaName] {
158+
logger.V(1).Info("Stopping APIServiceExport resource sync", "key", c.Key(), "reason", "Schema no longer referenced")
159+
r.syncStore.Delete(c.Key())
182160
}
161+
183162
}
184-
r.lock.Unlock()
185163

186164
return utilerrors.NewAggregate(errs)
187165
}
188166

189167
func (r *reconciler) ensureControllerForSchema(ctx context.Context, export *kubebindv1alpha2.APIServiceExport, schema *kubebindv1alpha2.BoundSchema) error {
190168
logger := klog.FromContext(ctx)
191-
key := schema.Name + "." + export.Name
169+
key := contextstore.NewKey(export.Namespace, export.Name, schema.Name)
192170

193-
r.lock.Lock()
194-
c, found := r.syncContext[key]
171+
c, found := r.syncStore.Get(key)
195172
if found {
196-
if c.generation == export.Generation {
197-
r.lock.Unlock()
173+
if c.Generation == export.Generation {
198174
return nil // all as expected
199175
}
200176

201177
logger.V(1).Info("Stopping APIServiceExport resource sync", "key", key, "reason", "GenerationChanged", "generation", schema.Generation)
202-
c.cancel()
203-
delete(r.syncContext, key)
178+
r.syncStore.Delete(key)
204179
}
205-
r.lock.Unlock()
180+
181+
// At this point we dont have a controller for this schema.
206182

207183
// start a new syncer
208184
var syncVersion string
@@ -307,26 +283,24 @@ func (r *reconciler) ensureControllerForSchema(ctx context.Context, export *kube
307283
go statusCtrl.Start(ctxWithCancel, 1)
308284
}()
309285

310-
r.lock.Lock()
311-
defer r.lock.Unlock()
312-
if c, found := r.syncContext[key]; found {
313-
c.cancel()
314-
}
315-
r.syncContext[key] = syncContext{
316-
generation: schema.Generation,
317-
cancel: cancel,
318-
}
286+
r.syncStore.Set(key, contextstore.SyncContext{
287+
Generation: schema.Generation,
288+
Cancel: cancel,
289+
})
319290

320291
return nil
321292
}
322293

323294
func (r *reconciler) ensureControllersForPermissionClaims(
324295
ctx context.Context,
296+
export *kubebindv1alpha2.APIServiceExport,
325297
binding *kubebindv1alpha2.APIServiceBinding,
326298
isClusterScoped bool, // schema.Spec.Scope == apiextensionsv1.ClusterScoped || schema.Spec.InformerScope == kubebindv1alpha2.ClusterScope
327299
) error {
328300
logger := klog.FromContext(ctx)
329301

302+
//ctxWithCancel, cancel := context.WithCancel(ctx)
303+
330304
dynamicProviderClient := dynamicclient.NewForConfigOrDie(r.providerConfig)
331305
dynamicConsumerClient := dynamicclient.NewForConfigOrDie(r.consumerConfig)
332306

pkg/konnector/controllers/contextstore/contextstore.go

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ package contextstore
2121
// Context are stored at the APIServiceExport level.
2222

2323
import (
24+
"strings"
2425
"sync"
25-
26-
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
2726
)
2827

2928
type Key string
@@ -32,14 +31,16 @@ func (k Key) String() string {
3231
return string(k)
3332
}
3433

35-
func NewKey(export kubebindv1alpha2.APIServiceExport) Key {
36-
return Key(export.Namespace + "/" + export.Name)
34+
func NewKey(exportNamespace, exportName string, suffix ...string) Key {
35+
return Key(exportNamespace + "." + exportName + "." + strings.Join(suffix, "."))
3736
}
3837

3938
type Store interface {
4039
Get(key Key) (SyncContext, bool)
40+
ListPrefixed(prefix Key) []SyncContext
4141
Set(key Key, value SyncContext)
4242
Delete(key Key)
43+
BulkDeletePrefixed(prefix Key) []SyncContext
4344
}
4445

4546
type contextStore struct {
@@ -48,8 +49,9 @@ type contextStore struct {
4849
}
4950

5051
type SyncContext struct {
51-
generation int64
52-
cancel func()
52+
key Key // reference key for the context for logging
53+
Generation int64
54+
Cancel func()
5355
}
5456

5557
func New() Store {
@@ -58,12 +60,8 @@ func New() Store {
5860
}
5961
}
6062

61-
func (c *SyncContext) Generation() int64 {
62-
return c.generation
63-
}
64-
65-
func (c *SyncContext) Cancel() {
66-
c.cancel()
63+
func (c *SyncContext) Key() Key {
64+
return c.key
6765
}
6866

6967
func (c *contextStore) Get(key Key) (SyncContext, bool) {
@@ -73,9 +71,23 @@ func (c *contextStore) Get(key Key) (SyncContext, bool) {
7371
return val, ok
7472
}
7573

74+
func (c *contextStore) ListPrefixed(prefix Key) []SyncContext {
75+
c.lock.Lock()
76+
defer c.lock.Unlock()
77+
78+
var results []SyncContext
79+
for k, v := range c.store {
80+
if strings.HasPrefix(k.String(), prefix.String()) {
81+
results = append(results, v)
82+
}
83+
}
84+
return results
85+
}
86+
7687
func (c *contextStore) Set(key Key, value SyncContext) {
7788
c.lock.Lock()
7889
defer c.lock.Unlock()
90+
value.key = key
7991
c.store[key] = value
8092
}
8193

@@ -84,7 +96,30 @@ func (c *contextStore) Delete(key Key) {
8496
defer c.lock.Unlock()
8597
ctx, ok := c.store[key]
8698
if ok {
87-
ctx.cancel()
99+
ctx.Cancel()
88100
}
89101
delete(c.store, key)
90102
}
103+
104+
func (c *contextStore) BulkDeletePrefixed(prefix Key) []SyncContext {
105+
c.lock.Lock()
106+
defer c.lock.Unlock()
107+
108+
var deleted []SyncContext
109+
var keysToDelete []Key
110+
111+
for k, v := range c.store {
112+
if strings.HasPrefix(k.String(), prefix.String()) {
113+
keysToDelete = append(keysToDelete, k)
114+
deleted = append(deleted, v)
115+
}
116+
}
117+
118+
for _, k := range keysToDelete {
119+
ctx := c.store[k]
120+
ctx.Cancel()
121+
delete(c.store, k)
122+
}
123+
124+
return deleted
125+
}

0 commit comments

Comments
 (0)