Skip to content

Commit 0d0bfe9

Browse files
author
Phillip Wittrock
authored
Merge pull request kubernetes-sigs#14 from pwittrock/client
Refactor informer and client packages into cache and client packages
2 parents ea2cb08 + d556144 commit 0d0bfe9

22 files changed

+671
-483
lines changed

example/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func main() {
4545

4646
// Setup a new controller to Reconcile ReplicaSets
4747
c, err := manager.NewController(
48-
controller.Args{Name: "foo-controller", MaxConcurrentReconciles: 1},
48+
controller.Options{Name: "foo-controller", MaxConcurrentReconciles: 1},
4949
&reconcileReplicaSet{client: manager.GetClient()},
5050
)
5151
if err != nil {
@@ -75,7 +75,7 @@ func main() {
7575

7676
// reconcileReplicaSet reconciles ReplicaSets
7777
type reconcileReplicaSet struct {
78-
client client.Interface
78+
client client.Client
7979
}
8080

8181
// Implement reconcile.Reconcile so the controller can reconcile objects

pkg/client/cache.go renamed to pkg/cache/cache.go

Lines changed: 36 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
package client
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
218

319
import (
420
"context"
@@ -14,58 +30,25 @@ import (
1430
"k8s.io/apimachinery/pkg/selection"
1531
"k8s.io/client-go/tools/cache"
1632

17-
"github.com/kubernetes-sigs/controller-runtime/pkg/internal/informer"
33+
"github.com/kubernetes-sigs/controller-runtime/pkg/client"
1834
logf "github.com/kubernetes-sigs/controller-runtime/pkg/runtime/log"
1935
)
2036

2137
var log = logf.KBLog.WithName("object-cache")
2238

2339
// objectCache is a ReadInterface
24-
var _ ReadInterface = &objectCache{}
40+
var _ client.ReadInterface = &objectCache{}
2541

2642
// objectCache is a Kubernetes Object cache populated from Informers
2743
type objectCache struct {
2844
cachesByType map[reflect.Type]*singleObjectCache
2945
scheme *runtime.Scheme
3046
}
3147

32-
var _ Cache = &objectCache{}
33-
34-
// Cache implements ReadInterface by reading objects from a cache populated by Informers
35-
type Cache interface {
36-
ReadInterface
37-
informer.Callback
38-
}
39-
40-
// NewObjectCache returns a new objectCache populated from informers
41-
func NewObjectCache(
42-
informers map[schema.GroupVersionKind]cache.SharedIndexInformer,
43-
scheme *runtime.Scheme) Cache {
44-
res := &objectCache{
45-
cachesByType: make(map[reflect.Type]*singleObjectCache),
46-
scheme: scheme,
47-
}
48-
res.AddInformers(informers)
49-
return res
50-
}
51-
52-
// AddInformers adds new informers to the objectCache
53-
func (o *objectCache) AddInformers(informers map[schema.GroupVersionKind]cache.SharedIndexInformer) {
54-
if informers == nil {
55-
return
56-
}
57-
for gvk, informer := range informers {
58-
o.AddInformer(gvk, informer)
59-
}
60-
}
61-
62-
// Call implements the informer.Callback so that the cache can be populate with new Informers as they are added
63-
func (o *objectCache) Call(gvk schema.GroupVersionKind, c cache.SharedIndexInformer) {
64-
o.AddInformer(gvk, c)
65-
}
48+
var _ client.ReadInterface = &objectCache{}
6649

67-
// AddInformer adds an informer to the objectCache
68-
func (o *objectCache) AddInformer(gvk schema.GroupVersionKind, c cache.SharedIndexInformer) {
50+
// addInformer adds an informer to the objectCache
51+
func (o *objectCache) addInformer(gvk schema.GroupVersionKind, c cache.SharedIndexInformer) {
6952
obj, err := o.scheme.New(gvk)
7053
if err != nil {
7154
log.Error(err, "could not register informer in objectCache for GVK", "GroupVersionKind", gvk)
@@ -91,17 +74,17 @@ func (o *objectCache) cacheFor(obj runtime.Object) (*singleObjectCache, bool) {
9174
return cache, isKnown
9275
}
9376

94-
// Get implements client.ReadInterface
95-
func (o *objectCache) Get(ctx context.Context, key ObjectKey, out runtime.Object) error {
77+
// Get implements populatingClient.ReadInterface
78+
func (o *objectCache) Get(ctx context.Context, key client.ObjectKey, out runtime.Object) error {
9679
cache, isKnown := o.cacheFor(out)
9780
if !isKnown {
9881
return fmt.Errorf("no cache for objects of type %T, must have asked for an watch/informer first", out)
9982
}
10083
return cache.Get(ctx, key, out)
10184
}
10285

103-
// List implements client.ReadInterface
104-
func (o *objectCache) List(ctx context.Context, opts *ListOptions, out runtime.Object) error {
86+
// List implements populatingClient.ReadInterface
87+
func (o *objectCache) List(ctx context.Context, opts *client.ListOptions, out runtime.Object) error {
10588
itemsPtr, err := apimeta.GetItemsPtr(out)
10689
if err != nil {
10790
return nil
@@ -116,7 +99,7 @@ func (o *objectCache) List(ctx context.Context, opts *ListOptions, out runtime.O
11699
}
117100

118101
// singleObjectCache is a ReadInterface
119-
var _ ReadInterface = &singleObjectCache{}
102+
var _ client.ReadInterface = &singleObjectCache{}
120103

121104
// singleObjectCache is a ReadInterface that retrieves objects
122105
// from a single local cache populated by a watch.
@@ -127,8 +110,8 @@ type singleObjectCache struct {
127110
GroupVersionKind schema.GroupVersionKind
128111
}
129112

130-
// Get implements client.Interface
131-
func (c *singleObjectCache) Get(_ context.Context, key ObjectKey, out runtime.Object) error {
113+
// Get implements populatingClient.Client
114+
func (c *singleObjectCache) Get(_ context.Context, key client.ObjectKey, out runtime.Object) error {
132115
storeKey := objectKeyToStoreKey(key)
133116
obj, exists, err := c.Indexer.GetByKey(storeKey)
134117
if err != nil {
@@ -160,8 +143,8 @@ func (c *singleObjectCache) Get(_ context.Context, key ObjectKey, out runtime.Ob
160143
return nil
161144
}
162145

163-
// List implements client.Interface
164-
func (c *singleObjectCache) List(ctx context.Context, opts *ListOptions, out runtime.Object) error {
146+
// List implements populatingClient.Client
147+
func (c *singleObjectCache) List(ctx context.Context, opts *client.ListOptions, out runtime.Object) error {
165148
var objs []interface{}
166149
var err error
167150

@@ -219,30 +202,25 @@ func (c *singleObjectCache) getListItems(objs []interface{}, labelSel labels.Sel
219202
}
220203

221204
// TODO: Make an interface with this function that has an Informers as an object on the struct
222-
// that automatically calls InformerFor and passes in the Indexer into indexByField
205+
// that automatically calls GetInformer and passes in the Indexer into indexByField
223206

224207
// noNamespaceNamespace is used as the "namespace" when we want to list across all namespaces
225208
const allNamespacesNamespace = "__all_namespaces"
226209

227-
// InformerFieldIndexer provides an in-memory index of Object fields
228-
type InformerFieldIndexer struct {
229-
Informers informer.Informers
230-
}
231-
232210
// IndexField adds an indexer to the underlying cache, using extraction function to get
233211
// value(s) from the given field. This index can then be used by passing a field selector
234212
// to List. For one-to-one compatibility with "normal" field selectors, only return one value.
235213
// The values may be anything. They will automatically be prefixed with the namespace of the
236214
// given object, if present. The objects passed are guaranteed to be objects of the correct type.
237-
func (i *InformerFieldIndexer) IndexField(obj runtime.Object, field string, extractValue IndexerFunc) error {
238-
informer, err := i.Informers.InformerFor(obj)
215+
func (i *informers) IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error {
216+
informer, err := i.GetInformer(obj)
239217
if err != nil {
240218
return err
241219
}
242220
return indexByField(informer.GetIndexer(), field, extractValue)
243221
}
244222

245-
func indexByField(indexer cache.Indexer, field string, extractor IndexerFunc) error {
223+
func indexByField(indexer cache.Indexer, field string, extractor client.IndexerFunc) error {
246224
indexFunc := func(objRaw interface{}) ([]string, error) {
247225
// TODO(directxman12): check if this is the correct type?
248226
obj, isObj := objRaw.(runtime.Object)
@@ -300,7 +278,7 @@ func keyToNamespacedKey(ns string, baseKey string) string {
300278
// It's akin to MetaNamespaceKeyFunc. It's separate from
301279
// String to allow keeping the key format easily in sync with
302280
// MetaNamespaceKeyFunc.
303-
func objectKeyToStoreKey(k ObjectKey) string {
281+
func objectKeyToStoreKey(k client.ObjectKey) string {
304282
if k.Namespace == "" {
305283
return k.Name
306284
}
@@ -319,11 +297,3 @@ func requiresExactMatch(sel fields.Selector) (field, val string, required bool)
319297
}
320298
return req.Field, req.Value, true
321299
}
322-
323-
// SplitReaderWriter forms an interface Interface by composing separate
324-
// read and write interfaces. This way, you can have an Interface that
325-
// reads from a cache and writes to the API server.
326-
type SplitReaderWriter struct {
327-
ReadInterface
328-
WriteInterface
329-
}

pkg/client/cache_test.go renamed to pkg/cache/cache_test.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package client
1+
package cache
22

33
import (
44
"context"
@@ -13,14 +13,15 @@ import (
1313

1414
"reflect"
1515

16+
"github.com/kubernetes-sigs/controller-runtime/pkg/client"
1617
"k8s.io/client-go/kubernetes/scheme"
1718
)
1819

1920
var _ = Describe("Indexers", func() {
2021
three := int64(3)
21-
knownPodKey := ObjectKey{Name: "some-pod", Namespace: "some-ns"}
22-
knownPod3Key := ObjectKey{Name: "some-pod", Namespace: "some-other-ns"}
23-
knownVolumeKey := ObjectKey{Name: "some-vol", Namespace: "some-ns"}
22+
knownPodKey := client.ObjectKey{Name: "some-pod", Namespace: "some-ns"}
23+
knownPod3Key := client.ObjectKey{Name: "some-pod", Namespace: "some-other-ns"}
24+
knownVolumeKey := client.ObjectKey{Name: "some-vol", Namespace: "some-ns"}
2425
knownPod := &kapi.Pod{
2526
ObjectMeta: metav1.ObjectMeta{
2627
Name: knownPodKey.Name,
@@ -85,8 +86,8 @@ var _ = Describe("Indexers", func() {
8586
multiCache.registerCache(&kapi.PersistentVolume{}, kapi.SchemeGroupVersion.WithKind("PersistentVolume"), volumeIndexer)
8687
})
8788

88-
Describe("client interface wrapper around an indexer", func() {
89-
var singleCache ReadInterface
89+
Describe("populatingClient interface wrapper around an indexer", func() {
90+
var singleCache client.ReadInterface
9091

9192
BeforeEach(func() {
9293
var ok bool
@@ -101,12 +102,12 @@ var _ = Describe("Indexers", func() {
101102
})
102103

103104
It("should error out for missing objects", func() {
104-
Expect(singleCache.Get(context.TODO(), ObjectKey{Name: "unknown-pod"}, &kapi.Pod{})).To(HaveOccurred())
105+
Expect(singleCache.Get(context.TODO(), client.ObjectKey{Name: "unknown-pod"}, &kapi.Pod{})).To(HaveOccurred())
105106
})
106107

107108
It("should be able to list objects by namespace", func() {
108109
out := kapi.PodList{}
109-
Expect(singleCache.List(context.TODO(), InNamespace(knownPodKey.Namespace), &out)).NotTo(HaveOccurred())
110+
Expect(singleCache.List(context.TODO(), client.InNamespace(knownPodKey.Namespace), &out)).NotTo(HaveOccurred())
110111
Expect(out.Items).To(ConsistOf(*knownPod, *knownPod2))
111112
})
112113

@@ -125,23 +126,23 @@ var _ = Describe("Indexers", func() {
125126

126127
It("should support filtering by labels", func() {
127128
out := kapi.PodList{}
128-
Expect(singleCache.List(context.TODO(), InNamespace(knownPodKey.Namespace).MatchingLabels(map[string]string{"somelbl": "someval"}), &out)).NotTo(HaveOccurred())
129+
Expect(singleCache.List(context.TODO(), client.InNamespace(knownPodKey.Namespace).MatchingLabels(map[string]string{"somelbl": "someval"}), &out)).NotTo(HaveOccurred())
129130
Expect(out.Items).To(ConsistOf(*knownPod2))
130131
})
131132

132133
It("should support filtering by a single field=value specification, if previously indexed", func() {
133134
By("listing by field selector in a namespace")
134135
out := kapi.PodList{}
135-
Expect(singleCache.List(context.TODO(), InNamespace(knownPodKey.Namespace).MatchingField("spec.restartPolicy", "Always"), &out)).NotTo(HaveOccurred())
136+
Expect(singleCache.List(context.TODO(), client.InNamespace(knownPodKey.Namespace).MatchingField("spec.restartPolicy", "Always"), &out)).NotTo(HaveOccurred())
136137
Expect(out.Items).To(ConsistOf(*knownPod2))
137138

138139
By("listing by field selector across all namespaces")
139-
Expect(singleCache.List(context.TODO(), MatchingField("spec.restartPolicy", "Never"), &out)).NotTo(HaveOccurred())
140+
Expect(singleCache.List(context.TODO(), client.MatchingField("spec.restartPolicy", "Never"), &out)).NotTo(HaveOccurred())
140141
Expect(out.Items).To(ConsistOf(*knownPod, *knownPod3))
141142
})
142143
})
143144

144-
Describe("client interface wrapper around multiple indexers", func() {
145+
Describe("populatingClient interface wrapper around multiple indexers", func() {
145146
It("should be able to fetch any known object by key and type", func() {
146147
outPod := kapi.Pod{}
147148
Expect(multiCache.Get(context.TODO(), knownPodKey, &outPod)).NotTo(HaveOccurred())

0 commit comments

Comments
 (0)