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
+
1
17
package client
2
18
3
19
import (
4
20
"context"
21
+ "fmt"
5
22
"reflect"
6
- "sync"
7
23
24
+ "github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
8
25
"k8s.io/apimachinery/pkg/api/meta"
9
26
"k8s.io/apimachinery/pkg/runtime"
10
- serializer "k8s.io/apimachinery/pkg/runtime/serializer"
27
+ "k8s.io/apimachinery/pkg/runtime/serializer"
11
28
"k8s.io/client-go/kubernetes/scheme"
12
29
"k8s.io/client-go/rest"
13
-
14
- "github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
15
- "k8s.io/apimachinery/pkg/apis/meta/v1"
16
30
)
17
31
18
32
// Options are creation options for a Client
@@ -26,6 +40,10 @@ type Options struct {
26
40
27
41
// New returns a new Client using the provided config and Options.
28
42
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
+
29
47
// Init a scheme if none provided
30
48
if options .Scheme == nil {
31
49
options .Scheme = scheme .Scheme
@@ -40,161 +58,93 @@ func New(config *rest.Config, options Options) (Client, error) {
40
58
}
41
59
}
42
60
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 ),
51
70
}
52
71
53
72
return c , nil
54
73
}
55
74
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 {}
115
76
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
126
82
}
127
83
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 )
131
87
if err != nil {
132
88
return err
133
89
}
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 () ).
137
93
Body (obj ).
138
94
Do ().
139
95
Into (obj )
140
96
}
141
97
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 )
145
101
if err != nil {
146
102
return err
147
103
}
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 ()).
152
108
Body (obj ).
153
109
Do ().
154
110
Into (obj )
155
111
}
156
112
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 )
160
116
if err != nil {
161
117
return err
162
118
}
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 ()).
167
123
Do ().
168
124
Error ()
169
125
}
170
126
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 )
174
130
if err != nil {
175
131
return err
176
132
}
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 )
183
137
}
184
138
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 )
188
142
if err != nil {
189
143
return err
190
144
}
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 ()).
198
148
Body (obj ).
199
149
VersionedParams (opts .AsListOptions (), c .paramCodec ).
200
150
Do ().
0 commit comments