Skip to content

Commit 42599a8

Browse files
authored
Merge pull request #9 from kcp-dev/start-informers
🐛 Start informers for objects that they haven't been started for yet
2 parents 5cf666d + e66c5ab commit 42599a8

File tree

6 files changed

+54
-18
lines changed

6 files changed

+54
-18
lines changed

examples/apiexport/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func main() {
121121
return reconcile.Result{}, fmt.Errorf("failed to get configmap: %w", err)
122122
}
123123

124-
log.Info("Reconciling configmap", "name", s.Name, "uuid", s.UID)
124+
log.Info("Reconciling ConfigMap", "name", s.Name, "uuid", s.UID)
125125

126126
return reconcile.Result{}, nil
127127
},

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20240817110845-a9eb9752bfeb
88
github.com/kcp-dev/kcp/sdk v0.26.1
99
github.com/kcp-dev/logicalcluster/v3 v3.0.5
10-
github.com/multicluster-runtime/multicluster-runtime v0.20.0-alpha.3
10+
github.com/multicluster-runtime/multicluster-runtime v0.20.0-alpha.5
1111
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace
1212
github.com/stretchr/testify v1.9.0
1313
golang.org/x/sync v0.10.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
8787
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
8888
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
8989
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
90-
github.com/multicluster-runtime/multicluster-runtime v0.20.0-alpha.3 h1:GLVW6WCrHbuyrSOcMpXUd/svnW32YLRMJPwRwAiR8zI=
91-
github.com/multicluster-runtime/multicluster-runtime v0.20.0-alpha.3/go.mod h1:6ZuT8VoTSr8nYyToyhnhAepyZoHAaCQzzabzbSFwAKc=
90+
github.com/multicluster-runtime/multicluster-runtime v0.20.0-alpha.5 h1:aoDDYIbqFXbRfWVbQEl5l5KktEVWntuwD2gpPMEIr4A=
91+
github.com/multicluster-runtime/multicluster-runtime v0.20.0-alpha.5/go.mod h1:6ZuT8VoTSr8nYyToyhnhAepyZoHAaCQzzabzbSFwAKc=
9292
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
9393
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
9494
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=

virtualworkspace/cache.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,10 @@ func (c *scopedCache) IndexField(ctx context.Context, obj client.Object, field s
5252

5353
// Get returns a single object from the cache.
5454
func (c *scopedCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
55-
inf, gvk, scope, found, err := c.base.getSharedInformer(obj)
55+
inf, gvk, scope, err := c.base.getSharedInformer(obj)
5656
if err != nil {
5757
return fmt.Errorf("failed to get informer for %T %s: %w", obj, obj.GetObjectKind().GroupVersionKind(), err)
5858
}
59-
if !found {
60-
return fmt.Errorf("no informer found for %T %s", obj, obj.GetObjectKind().GroupVersionKind())
61-
}
6259

6360
cr := cacheReader{
6461
indexer: inf.GetIndexer(),
@@ -73,13 +70,10 @@ func (c *scopedCache) Get(ctx context.Context, key client.ObjectKey, obj client.
7370

7471
// List returns a list of objects from the cache.
7572
func (c *scopedCache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
76-
inf, gvk, scope, found, err := c.base.getSharedInformer(list)
73+
inf, gvk, scope, err := c.base.getSharedInformer(list)
7774
if err != nil {
7875
return fmt.Errorf("failed to get informer for %T %s: %w", list, list.GetObjectKind().GroupVersionKind(), err)
7976
}
80-
if !found {
81-
return fmt.Errorf("no informer found for %T %s", list, list.GetObjectKind().GroupVersionKind())
82-
}
8377

8478
cr := cacheReader{
8579
indexer: inf.GetIndexer(),

virtualworkspace/provider.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func (p *Provider) Run(ctx context.Context, mgr mcmanager.Manager) error {
109109
if err != nil {
110110
return fmt.Errorf("failed to get logical cluster informer: %w", err)
111111
}
112-
shInf, _, _, _, err := p.cache.getSharedInformer(p.object)
112+
shInf, _, _, err := p.cache.getSharedInformer(p.object)
113113
if err != nil {
114114
return fmt.Errorf("failed to get shared informer: %w", err)
115115
}
@@ -227,3 +227,8 @@ func (p *Provider) Get(_ context.Context, name string) (cluster.Cluster, error)
227227
func (p *Provider) GetWildcard() cache.Cache {
228228
return p.cache
229229
}
230+
231+
// IndexField indexes the given object by the given field on all engaged clusters, current and future.
232+
func (p *Provider) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
233+
return p.cache.IndexField(ctx, obj, field, extractValue)
234+
}

virtualworkspace/wildcard.go

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import (
4343
// WildcardCache is a cache that operates on a /clusters/* endpoint.
4444
type WildcardCache interface {
4545
cache.Cache
46-
getSharedInformer(obj runtime.Object) (k8scache.SharedIndexInformer, schema.GroupVersionKind, apimeta.RESTScopeName, bool, error)
46+
getSharedInformer(obj runtime.Object) (k8scache.SharedIndexInformer, schema.GroupVersionKind, apimeta.RESTScopeName, error)
4747
}
4848

4949
// NewWildcardCache returns a cache.Cache that handles multi-cluster watches
@@ -80,6 +80,8 @@ func NewWildcardCache(config *rest.Config, opts cache.Options) (WildcardCache, e
8080
Unstructured: make(map[schema.GroupVersionKind]k8scache.SharedIndexInformer),
8181
Metadata: make(map[schema.GroupVersionKind]k8scache.SharedIndexInformer),
8282
},
83+
84+
readerFailOnMissingInformer: opts.ReaderFailOnMissingInformer,
8385
}
8486

8587
opts.NewInformer = func(watcher k8scache.ListerWatcher, obj runtime.Object, duration time.Duration, indexers k8scache.Indexers) k8scache.SharedIndexInformer {
@@ -121,25 +123,60 @@ type wildcardCache struct {
121123
scheme *runtime.Scheme
122124
mapper apimeta.RESTMapper
123125
tracker informerTracker
126+
127+
readerFailOnMissingInformer bool
124128
}
125129

126-
func (c *wildcardCache) getSharedInformer(obj runtime.Object) (k8scache.SharedIndexInformer, schema.GroupVersionKind, apimeta.RESTScopeName, bool, error) {
130+
func (c *wildcardCache) getSharedInformer(obj runtime.Object) (k8scache.SharedIndexInformer, schema.GroupVersionKind, apimeta.RESTScopeName, error) {
127131
gvk, err := apiutil.GVKForObject(obj, c.scheme)
128132
if err != nil {
129-
return nil, gvk, "", false, err
133+
return nil, gvk, "", fmt.Errorf("failed to get GVK for object: %w", err)
130134
}
131135

136+
// We need the non-list GVK, so chop off the "List" from the end of the kind.
137+
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
138+
132139
mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
133140
if err != nil {
134-
return nil, gvk, "", false, err
141+
return nil, gvk, "", fmt.Errorf("failed to get REST mapping: %w", err)
135142
}
136143

137144
infs := c.tracker.informersByType(obj)
138145
c.tracker.lock.RLock()
139146
inf, ok := infs[gvk]
140147
c.tracker.lock.RUnlock()
141148

142-
return inf, gvk, mapping.Scope.Name(), ok, nil
149+
// we need to create a new informer here.
150+
if !ok {
151+
// we have been instructed to fail if the informer is missing.
152+
if c.readerFailOnMissingInformer {
153+
return nil, gvk, "", &cache.ErrResourceNotCached{}
154+
}
155+
156+
// Let's generate a new object from the chopped GVK, since the original obj might be of *List type.
157+
o, err := c.scheme.New(gvk)
158+
if err != nil {
159+
return nil, gvk, "", fmt.Errorf("failed to create object for GVK: %w", err)
160+
}
161+
162+
// Call GetInformer, but we don't care about the output. We just need to make sure that our NewInformer
163+
// func has been called, which registers the new informer in our tracker.
164+
if _, err := c.Cache.GetInformer(context.TODO(), o.(client.Object)); err != nil {
165+
return nil, gvk, "", fmt.Errorf("failed to create informer: %w", err)
166+
}
167+
168+
// Now we should be able to find the informer.
169+
infs := c.tracker.informersByType(obj)
170+
c.tracker.lock.RLock()
171+
inf, ok = infs[gvk]
172+
c.tracker.lock.RUnlock()
173+
174+
if !ok {
175+
return nil, gvk, "", fmt.Errorf("failed to find newly started informer for %v", gvk)
176+
}
177+
}
178+
179+
return inf, gvk, mapping.Scope.Name(), nil
143180
}
144181

145182
// IndexField adds an index for the given object kind.

0 commit comments

Comments
 (0)