Skip to content

Commit 34cf975

Browse files
committed
cleanup client/config
1 parent d791801 commit 34cf975

File tree

3 files changed

+179
-219
lines changed

3 files changed

+179
-219
lines changed

pkg/client/cache.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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 client
18+
19+
import (
20+
"reflect"
21+
"sync"
22+
23+
"github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
24+
"k8s.io/apimachinery/pkg/api/meta"
25+
"k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/apimachinery/pkg/runtime/schema"
28+
"k8s.io/apimachinery/pkg/runtime/serializer"
29+
"k8s.io/client-go/rest"
30+
)
31+
32+
// resourceMeta contains data about a resourceMeta
33+
type resourceMeta struct {
34+
// client is the rest client used to talk to the apiserver
35+
rest.Interface
36+
// gvk is the GroupVersionKind of the resourceMeta
37+
gvk schema.GroupVersionKind
38+
// mapping is the rest mapping
39+
mapping *meta.RESTMapping
40+
}
41+
42+
func (r *resourceMeta) isNamespaced() bool {
43+
if r.mapping.Scope.Name() == meta.RESTScopeNameRoot {
44+
return false
45+
}
46+
return true
47+
}
48+
49+
func (r *resourceMeta) resource() string {
50+
return r.mapping.Resource
51+
}
52+
53+
type objMeta struct {
54+
*resourceMeta
55+
v1.Object
56+
}
57+
58+
// clientCache caches rest clients for Kubernetes types.
59+
type clientCache struct {
60+
// config is the rest.Config to talk to an apiserver
61+
config *rest.Config
62+
// scheme maps go structs to GroupVersionKinds
63+
scheme *runtime.Scheme
64+
// mapper maps GroupVersionKinds to Resources
65+
mapper meta.RESTMapper
66+
67+
codecs serializer.CodecFactory
68+
resourceByType map[reflect.Type]*resourceMeta
69+
mu sync.RWMutex
70+
}
71+
72+
// newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
73+
func (c *clientCache) newResource(obj runtime.Object) (*resourceMeta, 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 &resourceMeta{Interface: client, mapping: mapping, gvk: gvk}, nil
87+
}
88+
89+
// getResource returns a raw rest.Client for the given object type.
90+
func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, 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+
r, known := c.resourceByType[typ]
97+
c.mu.RUnlock()
98+
99+
if known {
100+
return r, nil
101+
}
102+
103+
// Initialize a new Client
104+
c.mu.Lock()
105+
defer c.mu.Unlock()
106+
r, err := c.newResource(obj)
107+
if err != nil {
108+
return nil, err
109+
}
110+
c.resourceByType[typ] = r
111+
return r, err
112+
}
113+
114+
func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
115+
r, err := c.getResource(obj)
116+
if err != nil {
117+
return nil, err
118+
}
119+
m, err := meta.Accessor(obj)
120+
if err != nil {
121+
return nil, err
122+
}
123+
return &objMeta{resourceMeta: r, Object: m}, err
124+
}

pkg/client/client.go

Lines changed: 43 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@ import (
2020
"context"
2121
"fmt"
2222
"reflect"
23-
"sync"
2423

2524
"github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
2625
"k8s.io/apimachinery/pkg/api/meta"
27-
"k8s.io/apimachinery/pkg/apis/meta/v1"
2826
"k8s.io/apimachinery/pkg/runtime"
2927
"k8s.io/apimachinery/pkg/runtime/serializer"
3028
"k8s.io/client-go/kubernetes/scheme"
@@ -60,209 +58,93 @@ func New(config *rest.Config, options Options) (Client, error) {
6058
}
6159
}
6260

63-
c := &populatingClient{
64-
config: config,
65-
scheme: options.Scheme,
66-
mapper: options.Mapper,
67-
codecs: serializer.NewCodecFactory(options.Scheme),
68-
paramCodec: runtime.NewParameterCodec(options.Scheme),
69-
clientsByType: make(map[reflect.Type]rest.Interface),
70-
resourcesByType: make(map[reflect.Type]string),
71-
mappingByType: make(map[reflect.Type]*meta.RESTMapping),
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),
7270
}
7371

7472
return c, nil
7573
}
7674

77-
var _ Client = &populatingClient{}
75+
var _ Client = &client{}
7876

79-
// populatingClient is an Client that reads and writes directly from/to an API server. It lazily initialized
80-
// new clients when they are used.
81-
type populatingClient struct {
82-
config *rest.Config
83-
scheme *runtime.Scheme
84-
mapper meta.RESTMapper
85-
86-
codecs serializer.CodecFactory
87-
paramCodec runtime.ParameterCodec
88-
clientsByType map[reflect.Type]rest.Interface
89-
mappingByType map[reflect.Type]*meta.RESTMapping
90-
resourcesByType map[reflect.Type]string
91-
mu sync.RWMutex
92-
}
93-
94-
// makeClient maps obj to a Kubernetes Resource and constructs a populatingClient for that Resource.
95-
func (c *populatingClient) makeClient(obj runtime.Object) (rest.Interface, *meta.RESTMapping, string, error) {
96-
gvk, err := apiutil.GVKForObject(obj, c.scheme)
97-
if err != nil {
98-
return nil, nil, "", err
99-
}
100-
client, err := apiutil.RESTClientForGVK(gvk, c.config, c.codecs)
101-
if err != nil {
102-
return nil, nil, "", err
103-
}
104-
mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
105-
if err != nil {
106-
return nil, nil, "", err
107-
}
108-
return client, mapping, mapping.Resource, nil
109-
}
110-
111-
// clientFor returns a raw rest.Client for the given object type.
112-
func (c *populatingClient) clientFor(obj runtime.Object) (rest.Interface, *meta.RESTMapping, string, error) {
113-
typ := reflect.TypeOf(obj)
114-
115-
// It's better to do creation work twice than to not let multiple
116-
// people make requests at once
117-
c.mu.RLock()
118-
client, known := c.clientsByType[typ]
119-
resource, _ := c.resourcesByType[typ]
120-
mapping, _ := c.mappingByType[typ]
121-
c.mu.RUnlock()
122-
123-
// Initialize a new Client
124-
if !known {
125-
var err error
126-
client, mapping, resource, err = c.makeClient(obj)
127-
if err != nil {
128-
return nil, mapping, "", err
129-
}
130-
c.mu.Lock()
131-
defer c.mu.Unlock()
132-
c.clientsByType[typ] = client
133-
c.resourcesByType[typ] = resource
134-
c.mappingByType[typ] = mapping
135-
}
136-
137-
return client, mapping, resource, nil
138-
}
139-
140-
func (c *populatingClient) metaAndClientFor(obj runtime.Object) (v1.Object, rest.Interface, *meta.RESTMapping, string, error) {
141-
client, mapping, resource, err := c.clientFor(obj)
142-
if err != nil {
143-
return nil, nil, nil, "", err
144-
}
145-
146-
meta, err := meta.Accessor(obj)
147-
if err != nil {
148-
return nil, nil, nil, "", err
149-
}
150-
return meta, client, mapping, resource, err
151-
}
152-
153-
func isNamespaced(mapping *meta.RESTMapping) bool {
154-
if mapping.Scope.Name() == meta.RESTScopeNameRoot {
155-
return false
156-
}
157-
return true
77+
// client is an Client that reads and writes directly from/to an API server. It lazily initialized
78+
// new clients when they are used by creating and caching a new rest client for the type.
79+
type client struct {
80+
cache clientCache
81+
paramCodec runtime.ParameterCodec
15882
}
15983

16084
// Create implements Client
161-
func (c *populatingClient) Create(ctx context.Context, obj runtime.Object) error {
162-
meta, client, mapping, resource, err := c.metaAndClientFor(obj)
85+
func (c *client) Create(ctx context.Context, obj runtime.Object) error {
86+
o, err := c.cache.getObjMeta(obj)
16387
if err != nil {
16488
return err
16589
}
166-
167-
if isNamespaced(mapping) {
168-
return client.Post().
169-
Namespace(meta.GetNamespace()).
170-
Resource(resource).
171-
Body(obj).
172-
Do().
173-
Into(obj)
174-
}
175-
176-
return client.Post().
177-
Resource(resource).
90+
return o.Post().
91+
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
92+
Resource(o.resource()).
17893
Body(obj).
17994
Do().
18095
Into(obj)
18196
}
18297

18398
// Update implements Client
184-
func (c *populatingClient) Update(ctx context.Context, obj runtime.Object) error {
185-
meta, client, mapping, resource, err := c.metaAndClientFor(obj)
99+
func (c *client) Update(ctx context.Context, obj runtime.Object) error {
100+
o, err := c.cache.getObjMeta(obj)
186101
if err != nil {
187102
return err
188103
}
189-
if isNamespaced(mapping) {
190-
return client.Put().
191-
Namespace(meta.GetNamespace()).
192-
Resource(resource).
193-
Name(meta.GetName()).
194-
Body(obj).
195-
Do().
196-
Into(obj)
197-
}
198-
199-
return client.Put().
200-
Resource(resource).
201-
Name(meta.GetName()).
104+
return o.Put().
105+
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
106+
Resource(o.resource()).
107+
Name(o.GetName()).
202108
Body(obj).
203109
Do().
204110
Into(obj)
205111
}
206112

207113
// Delete implements Client
208-
func (c *populatingClient) Delete(ctx context.Context, obj runtime.Object) error {
209-
meta, client, mapping, resource, err := c.metaAndClientFor(obj)
114+
func (c *client) Delete(ctx context.Context, obj runtime.Object) error {
115+
o, err := c.cache.getObjMeta(obj)
210116
if err != nil {
211117
return err
212118
}
213-
if isNamespaced(mapping) {
214-
return client.Delete().
215-
Namespace(meta.GetNamespace()).
216-
Resource(resource).
217-
Name(meta.GetName()).
218-
Do().
219-
Error()
220-
}
221-
return client.Delete().
222-
Resource(resource).
223-
Name(meta.GetName()).
119+
return o.Delete().
120+
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
121+
Resource(o.resource()).
122+
Name(o.GetName()).
224123
Do().
225124
Error()
226125
}
227126

228127
// Get implements Client
229-
func (c *populatingClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
230-
_, client, mapping, resource, err := c.metaAndClientFor(obj)
128+
func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
129+
r, err := c.cache.getResource(obj)
231130
if err != nil {
232131
return err
233132
}
234-
if isNamespaced(mapping) {
235-
return client.Get().
236-
Namespace(key.Namespace).
237-
Resource(resource).
238-
Name(key.Name).
239-
Do().
240-
Into(obj)
241-
}
242-
return client.Get().
243-
Resource(resource).
244-
Name(key.Name).
245-
Do().
246-
Into(obj)
133+
return r.Get().
134+
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
135+
Resource(r.resource()).
136+
Name(key.Name).Do().Into(obj)
247137
}
248138

249139
// List implements Client
250-
func (c *populatingClient) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
251-
_, client, mapping, resource, err := c.metaAndClientFor(obj)
140+
func (c *client) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
141+
r, err := c.cache.getResource(obj)
252142
if err != nil {
253143
return err
254144
}
255-
if isNamespaced(mapping) && opts != nil && len(opts.Namespace) > 0 {
256-
return client.Get().
257-
Namespace(opts.Namespace).
258-
Resource(resource).
259-
Body(obj).
260-
VersionedParams(opts.AsListOptions(), c.paramCodec).
261-
Do().
262-
Into(obj)
263-
}
264-
return client.Get().
265-
Resource(resource).
145+
return r.Get().
146+
NamespaceIfScoped(opts.Namespace, r.isNamespaced()).
147+
Resource(r.resource()).
266148
Body(obj).
267149
VersionedParams(opts.AsListOptions(), c.paramCodec).
268150
Do().

0 commit comments

Comments
 (0)