Skip to content

Commit 8cdff9c

Browse files
authored
Merge pull request #27 from pwittrock/client
Client tests
2 parents 8e28c33 + 417fccf commit 8cdff9c

File tree

12 files changed

+735
-202
lines changed

12 files changed

+735
-202
lines changed

pkg/cache/cache.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ import (
3737

3838
var log = logf.KBLog.WithName("object-cache")
3939

40-
// objectCache is a ReadInterface
41-
var _ client.ReadInterface = &objectCache{}
40+
// objectCache is a Reader
41+
var _ client.Reader = &objectCache{}
4242

4343
// objectCache is a Kubernetes Object cache populated from Informers
4444
type objectCache struct {
@@ -47,7 +47,7 @@ type objectCache struct {
4747
informers *informers
4848
}
4949

50-
var _ client.ReadInterface = &objectCache{}
50+
var _ client.Reader = &objectCache{}
5151

5252
// addInformer adds an informer to the objectCache
5353
func (o *objectCache) addInformer(gvk schema.GroupVersionKind, c cache.SharedIndexInformer) {
@@ -106,7 +106,7 @@ func (o *objectCache) cacheFor(obj runtime.Object) (*singleObjectCache, error) {
106106
return cache, nil
107107
}
108108

109-
// Get implements populatingClient.ReadInterface
109+
// Get implements populatingClient.Reader
110110
func (o *objectCache) Get(ctx context.Context, key client.ObjectKey, out runtime.Object) error {
111111
// Make sure there is a Cache for this type
112112
if !o.has(out) {
@@ -124,7 +124,7 @@ func (o *objectCache) Get(ctx context.Context, key client.ObjectKey, out runtime
124124
return cache.Get(ctx, key, out)
125125
}
126126

127-
// List implements populatingClient.ReadInterface
127+
// List implements populatingClient.Reader
128128
func (o *objectCache) List(ctx context.Context, opts *client.ListOptions, out runtime.Object) error {
129129
itemsPtr, err := apimeta.GetItemsPtr(out)
130130
if err != nil {
@@ -148,10 +148,10 @@ func (o *objectCache) List(ctx context.Context, opts *client.ListOptions, out ru
148148
return cache.List(ctx, opts, out)
149149
}
150150

151-
// singleObjectCache is a ReadInterface
152-
var _ client.ReadInterface = &singleObjectCache{}
151+
// singleObjectCache is a Reader
152+
var _ client.Reader = &singleObjectCache{}
153153

154-
// singleObjectCache is a ReadInterface that retrieves objects
154+
// singleObjectCache is a Reader that retrieves objects
155155
// from a single local cache populated by a watch.
156156
type singleObjectCache struct {
157157
// Indexer is the underlying indexer wrapped by this cache.

pkg/cache/cache_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ var _ = Describe("Indexers", func() {
8787
})
8888

8989
Describe("populatingClient interface wrapper around an indexer", func() {
90-
var singleCache client.ReadInterface
90+
var singleCache client.Reader
9191

9292
BeforeEach(func() {
9393
var err error

pkg/cache/informer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import (
1919
"k8s.io/client-go/tools/cache"
2020
)
2121

22-
// Cache implements ReadInterface by reading objects from a cache populated by Informers
22+
// Cache implements Reader by reading objects from a cache populated by Informers
2323
type Cache interface {
24-
client.ReadInterface
24+
client.Reader
2525
Informers
2626
IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error
2727
}

pkg/client/client.go

Lines changed: 71 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
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+
117
package client
218

319
import (
420
"context"
21+
"fmt"
522
"reflect"
6-
"sync"
723

24+
"github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
825
"k8s.io/apimachinery/pkg/api/meta"
926
"k8s.io/apimachinery/pkg/runtime"
10-
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
27+
"k8s.io/apimachinery/pkg/runtime/serializer"
1128
"k8s.io/client-go/kubernetes/scheme"
1229
"k8s.io/client-go/rest"
13-
14-
"github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
15-
"k8s.io/apimachinery/pkg/apis/meta/v1"
1630
)
1731

1832
// Options are creation options for a Client
@@ -26,6 +40,10 @@ type Options struct {
2640

2741
// New returns a new Client using the provided config and Options.
2842
func New(config *rest.Config, options Options) (Client, error) {
43+
if config == nil {
44+
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
45+
}
46+
2947
// Init a scheme if none provided
3048
if options.Scheme == nil {
3149
options.Scheme = scheme.Scheme
@@ -40,161 +58,93 @@ func New(config *rest.Config, options Options) (Client, error) {
4058
}
4159
}
4260

43-
c := &populatingClient{
44-
config: config,
45-
scheme: options.Scheme,
46-
mapper: options.Mapper,
47-
codecs: serializer.NewCodecFactory(options.Scheme),
48-
paramCodec: runtime.NewParameterCodec(options.Scheme),
49-
clientsByType: make(map[reflect.Type]rest.Interface),
50-
resourcesByType: make(map[reflect.Type]string),
61+
c := &client{
62+
cache: clientCache{
63+
config: config,
64+
scheme: options.Scheme,
65+
mapper: options.Mapper,
66+
codecs: serializer.NewCodecFactory(options.Scheme),
67+
resourceByType: make(map[reflect.Type]*resourceMeta),
68+
},
69+
paramCodec: runtime.NewParameterCodec(options.Scheme),
5170
}
5271

5372
return c, nil
5473
}
5574

56-
var _ Client = &populatingClient{}
57-
58-
// populatingClient is an Client that reads and writes directly from/to an API server. It lazily initialized
59-
// new clients when they are used.
60-
type populatingClient struct {
61-
config *rest.Config
62-
scheme *runtime.Scheme
63-
mapper meta.RESTMapper
64-
65-
codecs serializer.CodecFactory
66-
paramCodec runtime.ParameterCodec
67-
clientsByType map[reflect.Type]rest.Interface
68-
resourcesByType map[reflect.Type]string
69-
mu sync.RWMutex
70-
}
71-
72-
// makeClient maps obj to a Kubernetes Resource and constructs a populatingClient for that Resource.
73-
func (c *populatingClient) makeClient(obj runtime.Object) (rest.Interface, string, error) {
74-
gvk, err := apiutil.GVKForObject(obj, c.scheme)
75-
if err != nil {
76-
return nil, "", err
77-
}
78-
client, err := apiutil.RESTClientForGVK(gvk, c.config, c.codecs)
79-
if err != nil {
80-
return nil, "", err
81-
}
82-
mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
83-
if err != nil {
84-
return nil, "", err
85-
}
86-
return client, mapping.Resource, nil
87-
}
88-
89-
// clientFor returns a raw rest.Client for the given object type.
90-
func (c *populatingClient) clientFor(obj runtime.Object) (rest.Interface, string, error) {
91-
typ := reflect.TypeOf(obj)
92-
93-
// It's better to do creation work twice than to not let multiple
94-
// people make requests at once
95-
c.mu.RLock()
96-
client, known := c.clientsByType[typ]
97-
resource, _ := c.resourcesByType[typ]
98-
c.mu.RUnlock()
99-
100-
// Initialize a new Client
101-
if !known {
102-
var err error
103-
client, resource, err = c.makeClient(obj)
104-
if err != nil {
105-
return nil, "", err
106-
}
107-
c.mu.Lock()
108-
defer c.mu.Unlock()
109-
c.clientsByType[typ] = client
110-
c.resourcesByType[typ] = resource
111-
}
112-
113-
return client, resource, nil
114-
}
75+
var _ Client = &client{}
11576

116-
func (c *populatingClient) metaAndClientFor(obj runtime.Object) (v1.Object, rest.Interface, string, error) {
117-
client, resource, err := c.clientFor(obj)
118-
if err != nil {
119-
return nil, nil, "", err
120-
}
121-
meta, err := meta.Accessor(obj)
122-
if err != nil {
123-
return nil, nil, "", err
124-
}
125-
return meta, client, resource, err
77+
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
78+
// new clients at the time they are used, and caches the client.
79+
type client struct {
80+
cache clientCache
81+
paramCodec runtime.ParameterCodec
12682
}
12783

128-
// Create implements Client
129-
func (c *populatingClient) Create(ctx context.Context, obj runtime.Object) error {
130-
meta, client, resource, err := c.metaAndClientFor(obj)
84+
// Create implements client.Client
85+
func (c *client) Create(ctx context.Context, obj runtime.Object) error {
86+
o, err := c.cache.getObjMeta(obj)
13187
if err != nil {
13288
return err
13389
}
134-
return client.Post().
135-
Namespace(meta.GetNamespace()).
136-
Resource(resource).
90+
return o.Post().
91+
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
92+
Resource(o.resource()).
13793
Body(obj).
13894
Do().
13995
Into(obj)
14096
}
14197

142-
// Update implements Client
143-
func (c *populatingClient) Update(ctx context.Context, obj runtime.Object) error {
144-
meta, client, resource, err := c.metaAndClientFor(obj)
98+
// Update implements client.Client
99+
func (c *client) Update(ctx context.Context, obj runtime.Object) error {
100+
o, err := c.cache.getObjMeta(obj)
145101
if err != nil {
146102
return err
147103
}
148-
return client.Put().
149-
Namespace(meta.GetNamespace()).
150-
Resource(resource).
151-
Name(meta.GetName()).
104+
return o.Put().
105+
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
106+
Resource(o.resource()).
107+
Name(o.GetName()).
152108
Body(obj).
153109
Do().
154110
Into(obj)
155111
}
156112

157-
// Delete implements Client
158-
func (c *populatingClient) Delete(ctx context.Context, obj runtime.Object) error {
159-
meta, client, resource, err := c.metaAndClientFor(obj)
113+
// Delete implements client.Client
114+
func (c *client) Delete(ctx context.Context, obj runtime.Object) error {
115+
o, err := c.cache.getObjMeta(obj)
160116
if err != nil {
161117
return err
162118
}
163-
return client.Delete().
164-
Namespace(meta.GetNamespace()).
165-
Resource(resource).
166-
Name(meta.GetName()).
119+
return o.Delete().
120+
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
121+
Resource(o.resource()).
122+
Name(o.GetName()).
167123
Do().
168124
Error()
169125
}
170126

171-
// Get implements Client
172-
func (c *populatingClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
173-
client, resource, err := c.clientFor(obj)
127+
// Get implements client.Client
128+
func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
129+
r, err := c.cache.getResource(obj)
174130
if err != nil {
175131
return err
176132
}
177-
return client.Get().
178-
Namespace(key.Namespace).
179-
Resource(resource).
180-
Name(key.Name).
181-
Do().
182-
Into(obj)
133+
return r.Get().
134+
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
135+
Resource(r.resource()).
136+
Name(key.Name).Do().Into(obj)
183137
}
184138

185-
// List implements Client
186-
func (c *populatingClient) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
187-
client, resource, err := c.clientFor(obj)
139+
// List implements client.Client
140+
func (c *client) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
141+
r, err := c.cache.getResource(obj)
188142
if err != nil {
189143
return err
190144
}
191-
ns := ""
192-
if opts != nil {
193-
ns = opts.Namespace
194-
}
195-
return client.Get().
196-
Namespace(ns).
197-
Resource(resource).
145+
return r.Get().
146+
NamespaceIfScoped(opts.Namespace, r.isNamespaced()).
147+
Resource(r.resource()).
198148
Body(obj).
199149
VersionedParams(opts.AsListOptions(), c.paramCodec).
200150
Do().

0 commit comments

Comments
 (0)