Skip to content

Commit 15e1a2e

Browse files
author
Shawn Hurley
committed
Adding two types of client for unstructured and typed
1 parent a43118e commit 15e1a2e

File tree

5 files changed

+310
-118
lines changed

5 files changed

+310
-118
lines changed

pkg/cache/cache_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ var _ = Describe("Informer Cache", func() {
521521
By("verifying the object is received on the channel")
522522
Eventually(out).Should(Receive(Equal(pod)))
523523
close(done)
524-
})
524+
}, 3)
525525

526526
It("should be able to index an object field then retrieve objects by that field", func() {
527527
By("creating the cache")
@@ -571,7 +571,7 @@ var _ = Describe("Informer Cache", func() {
571571
Expect(listObj.Items).Should(HaveLen(1))
572572
actual := listObj.Items[0]
573573
Expect(actual.GetName()).To(Equal("test-pod-3"))
574-
})
574+
}, 3)
575575
})
576576
})
577577
})

pkg/client/client.go

Lines changed: 43 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ import (
2222
"reflect"
2323

2424
"k8s.io/apimachinery/pkg/api/meta"
25+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2526
"k8s.io/apimachinery/pkg/runtime"
26-
"k8s.io/apimachinery/pkg/runtime/schema"
2727
"k8s.io/apimachinery/pkg/runtime/serializer"
28+
"k8s.io/client-go/dynamic"
2829
"k8s.io/client-go/kubernetes/scheme"
2930
"k8s.io/client-go/rest"
3031
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
@@ -59,16 +60,26 @@ func New(config *rest.Config, options Options) (Client, error) {
5960
}
6061
}
6162

63+
dynamicClient, err := dynamic.NewForConfig(config)
64+
if err != nil {
65+
return nil, err
66+
}
67+
6268
c := &client{
63-
cache: clientCache{
64-
config: config,
65-
scheme: options.Scheme,
66-
mapper: options.Mapper,
67-
codecs: serializer.NewCodecFactory(options.Scheme),
68-
resourceByType: make(map[reflect.Type]*resourceMeta),
69-
unstructuredResourceByGVK: make(map[schema.GroupVersionKind]*resourceMeta),
69+
typedClient: typedClient{
70+
cache: clientCache{
71+
config: config,
72+
scheme: options.Scheme,
73+
mapper: options.Mapper,
74+
codecs: serializer.NewCodecFactory(options.Scheme),
75+
resourceByType: make(map[reflect.Type]*resourceMeta),
76+
},
77+
paramCodec: runtime.NewParameterCodec(options.Scheme),
78+
},
79+
unstructuredClient: unstructuredClient{
80+
client: dynamicClient,
81+
restMapper: options.Mapper,
7082
},
71-
paramCodec: runtime.NewParameterCodec(options.Scheme),
7283
}
7384

7485
return c, nil
@@ -79,85 +90,53 @@ var _ Client = &client{}
7990
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
8091
// new clients at the time they are used, and caches the client.
8192
type client struct {
82-
cache clientCache
83-
paramCodec runtime.ParameterCodec
93+
typedClient typedClient
94+
unstructuredClient unstructuredClient
8495
}
8596

8697
// Create implements client.Client
8798
func (c *client) Create(ctx context.Context, obj runtime.Object) error {
88-
o, err := c.cache.getObjMeta(obj)
89-
if err != nil {
90-
return err
99+
_, ok := obj.(*unstructured.Unstructured)
100+
if ok {
101+
return c.unstructuredClient.Create(ctx, obj)
91102
}
92-
return o.Post().
93-
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
94-
Resource(o.resource()).
95-
Body(obj).
96-
Do().
97-
Into(obj)
103+
return c.typedClient.Create(ctx, obj)
98104
}
99105

100106
// Update implements client.Client
101107
func (c *client) Update(ctx context.Context, obj runtime.Object) error {
102-
o, err := c.cache.getObjMeta(obj)
103-
if err != nil {
104-
return err
108+
_, ok := obj.(*unstructured.Unstructured)
109+
if ok {
110+
return c.unstructuredClient.Update(ctx, obj)
105111
}
106-
return o.Put().
107-
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
108-
Resource(o.resource()).
109-
Name(o.GetName()).
110-
Body(obj).
111-
Do().
112-
Into(obj)
112+
return c.typedClient.Update(ctx, obj)
113113
}
114114

115115
// Delete implements client.Client
116116
func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error {
117-
o, err := c.cache.getObjMeta(obj)
118-
if err != nil {
119-
return err
117+
_, ok := obj.(*unstructured.Unstructured)
118+
if ok {
119+
return c.unstructuredClient.Delete(ctx, obj, opts...)
120120
}
121-
122-
deleteOpts := DeleteOptions{}
123-
return o.Delete().
124-
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
125-
Resource(o.resource()).
126-
Name(o.GetName()).
127-
Body(deleteOpts.ApplyOptions(opts).AsDeleteOptions()).
128-
Do().
129-
Error()
121+
return c.typedClient.Delete(ctx, obj, opts...)
130122
}
131123

132124
// Get implements client.Client
133125
func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
134-
r, err := c.cache.getResource(obj)
135-
if err != nil {
136-
return err
126+
_, ok := obj.(*unstructured.Unstructured)
127+
if ok {
128+
return c.unstructuredClient.Get(ctx, key, obj)
137129
}
138-
return r.Get().
139-
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
140-
Resource(r.resource()).
141-
Name(key.Name).Do().Into(obj)
130+
return c.typedClient.Get(ctx, key, obj)
142131
}
143132

144133
// List implements client.Client
145134
func (c *client) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
146-
r, err := c.cache.getResource(obj)
147-
if err != nil {
148-
return err
135+
_, ok := obj.(*unstructured.UnstructuredList)
136+
if ok {
137+
return c.unstructuredClient.List(ctx, opts, obj)
149138
}
150-
namespace := ""
151-
if opts != nil {
152-
namespace = opts.Namespace
153-
}
154-
return r.Get().
155-
NamespaceIfScoped(namespace, r.isNamespaced()).
156-
Resource(r.resource()).
157-
Body(obj).
158-
VersionedParams(opts.AsListOptions(), c.paramCodec).
159-
Do().
160-
Into(obj)
139+
return c.typedClient.List(ctx, opts, obj)
161140
}
162141

163142
// Status implements client.StatusClient
@@ -175,7 +154,7 @@ var _ StatusWriter = &statusWriter{}
175154

176155
// Update implements client.StatusWriter
177156
func (sw *statusWriter) Update(_ context.Context, obj runtime.Object) error {
178-
o, err := sw.client.cache.getObjMeta(obj)
157+
o, err := sw.client.typedClient.cache.getObjMeta(obj)
179158
if err != nil {
180159
return err
181160
}

pkg/client/client_cache.go

Lines changed: 11 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323

2424
"k8s.io/apimachinery/pkg/api/meta"
2525
"k8s.io/apimachinery/pkg/apis/meta/v1"
26-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2726
"k8s.io/apimachinery/pkg/runtime"
2827
"k8s.io/apimachinery/pkg/runtime/schema"
2928
"k8s.io/apimachinery/pkg/runtime/serializer"
@@ -45,18 +44,14 @@ type clientCache struct {
4544
// codecs are used to create a REST client for a gvk
4645
codecs serializer.CodecFactory
4746

48-
muByType sync.RWMutex
4947
// resourceByType caches type metadata
5048
resourceByType map[reflect.Type]*resourceMeta
51-
52-
muByGVK sync.RWMutex
53-
// resourceByGVK caches type metadata for unstructured
54-
unstructuredResourceByGVK map[schema.GroupVersionKind]*resourceMeta
49+
mu sync.RWMutex
5550
}
5651

5752
// newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
5853
// If the object is a list, the resource represents the item's type instead.
59-
func (c *clientCache) newResource(obj runtime.Object, isUnstructured bool) (*resourceMeta, error) {
54+
func (c *clientCache) newResource(obj runtime.Object) (*resourceMeta, error) {
6055
gvk, err := apiutil.GVKForObject(obj, c.scheme)
6156
if err != nil {
6257
return nil, err
@@ -67,12 +62,7 @@ func (c *clientCache) newResource(obj runtime.Object, isUnstructured bool) (*res
6762
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
6863
}
6964

70-
var client rest.Interface
71-
if isUnstructured {
72-
client, err = apiutil.RESTUnstructuredClientForGVK(gvk, c.config)
73-
} else {
74-
client, err = apiutil.RESTClientForGVK(gvk, c.config, c.codecs)
75-
}
65+
client, err := apiutil.RESTClientForGVK(gvk, c.config, c.codecs)
7666
if err != nil {
7767
return nil, err
7868
}
@@ -83,63 +73,32 @@ func (c *clientCache) newResource(obj runtime.Object, isUnstructured bool) (*res
8373
return &resourceMeta{Interface: client, mapping: mapping, gvk: gvk}, nil
8474
}
8575

86-
func (c *clientCache) getUnstructuredResourceByGVK(obj runtime.Object) (*resourceMeta, error) {
87-
// It's better to do creation work twice than to not let multiple
88-
// people make requests at once
89-
c.muByGVK.RLock()
90-
r, known := c.unstructuredResourceByGVK[obj.GetObjectKind().GroupVersionKind()]
91-
c.muByGVK.RUnlock()
92-
93-
if known {
94-
return r, nil
95-
}
96-
97-
// Initialize a new Client
98-
c.muByGVK.Lock()
99-
defer c.muByGVK.Unlock()
100-
r, err := c.newResource(obj, true)
101-
if err != nil {
102-
return nil, err
103-
}
104-
c.unstructuredResourceByGVK[obj.GetObjectKind().GroupVersionKind()] = r
105-
return r, err
106-
}
107-
108-
func (c *clientCache) getResourceByType(obj runtime.Object) (*resourceMeta, error) {
76+
// getResource returns the resource meta information for the given type of object.
77+
// If the object is a list, the resource represents the item's type instead.
78+
func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
10979
typ := reflect.TypeOf(obj)
11080

11181
// It's better to do creation work twice than to not let multiple
11282
// people make requests at once
113-
c.muByType.RLock()
83+
c.mu.RLock()
11484
r, known := c.resourceByType[typ]
115-
c.muByType.RUnlock()
85+
c.mu.RUnlock()
11686

11787
if known {
11888
return r, nil
11989
}
12090

12191
// Initialize a new Client
122-
c.muByType.Lock()
123-
defer c.muByType.Unlock()
124-
r, err := c.newResource(obj, false)
92+
c.mu.Lock()
93+
defer c.mu.Unlock()
94+
r, err := c.newResource(obj)
12595
if err != nil {
12696
return nil, err
12797
}
12898
c.resourceByType[typ] = r
12999
return r, err
130100
}
131101

132-
// getResource returns the resource meta information for the given type of object.
133-
// If the object is a list, the resource represents the item's type instead.
134-
func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
135-
_, isUnstructured := obj.(*unstructured.Unstructured)
136-
_, isUnstructuredList := obj.(*unstructured.UnstructuredList)
137-
if isUnstructured || isUnstructuredList {
138-
return c.getUnstructuredResourceByGVK(obj)
139-
}
140-
return c.getResourceByType(obj)
141-
}
142-
143102
// getObjMeta returns objMeta containing both type and object metadata and state
144103
func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
145104
r, err := c.getResource(obj)

0 commit comments

Comments
 (0)