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
23
"sync"
7
24
25
+ "github.com/kubernetes-sigs/controller-runtime/pkg/client/apiutil"
8
26
"k8s.io/apimachinery/pkg/api/meta"
27
+ "k8s.io/apimachinery/pkg/apis/meta/v1"
9
28
"k8s.io/apimachinery/pkg/runtime"
10
- serializer "k8s.io/apimachinery/pkg/runtime/serializer"
29
+ "k8s.io/apimachinery/pkg/runtime/serializer"
11
30
"k8s.io/client-go/kubernetes/scheme"
12
31
"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
32
)
17
33
18
34
// Options are creation options for a Client
@@ -26,6 +42,10 @@ type Options struct {
26
42
27
43
// New returns a new Client using the provided config and Options.
28
44
func New (config * rest.Config , options Options ) (Client , error ) {
45
+ if config == nil {
46
+ return nil , fmt .Errorf ("must provide non-nil rest.Config to client.New" )
47
+ }
48
+
29
49
// Init a scheme if none provided
30
50
if options .Scheme == nil {
31
51
options .Scheme = scheme .Scheme
@@ -48,6 +68,7 @@ func New(config *rest.Config, options Options) (Client, error) {
48
68
paramCodec : runtime .NewParameterCodec (options .Scheme ),
49
69
clientsByType : make (map [reflect.Type ]rest.Interface ),
50
70
resourcesByType : make (map [reflect.Type ]string ),
71
+ mappingByType : make (map [reflect.Type ]* meta.RESTMapping ),
51
72
}
52
73
53
74
return c , nil
@@ -65,74 +86,94 @@ type populatingClient struct {
65
86
codecs serializer.CodecFactory
66
87
paramCodec runtime.ParameterCodec
67
88
clientsByType map [reflect.Type ]rest.Interface
89
+ mappingByType map [reflect.Type ]* meta.RESTMapping
68
90
resourcesByType map [reflect.Type ]string
69
91
mu sync.RWMutex
70
92
}
71
93
72
94
// 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 ) {
95
+ func (c * populatingClient ) makeClient (obj runtime.Object ) (rest.Interface , * meta. RESTMapping , string , error ) {
74
96
gvk , err := apiutil .GVKForObject (obj , c .scheme )
75
97
if err != nil {
76
- return nil , "" , err
98
+ return nil , nil , "" , err
77
99
}
78
100
client , err := apiutil .RESTClientForGVK (gvk , c .config , c .codecs )
79
101
if err != nil {
80
- return nil , "" , err
102
+ return nil , nil , "" , err
81
103
}
82
104
mapping , err := c .mapper .RESTMapping (gvk .GroupKind (), gvk .Version )
83
105
if err != nil {
84
- return nil , "" , err
106
+ return nil , nil , "" , err
85
107
}
86
- return client , mapping .Resource , nil
108
+ return client , mapping , mapping .Resource , nil
87
109
}
88
110
89
111
// clientFor returns a raw rest.Client for the given object type.
90
- func (c * populatingClient ) clientFor (obj runtime.Object ) (rest.Interface , string , error ) {
112
+ func (c * populatingClient ) clientFor (obj runtime.Object ) (rest.Interface , * meta. RESTMapping , string , error ) {
91
113
typ := reflect .TypeOf (obj )
92
114
93
115
// It's better to do creation work twice than to not let multiple
94
116
// people make requests at once
95
117
c .mu .RLock ()
96
118
client , known := c .clientsByType [typ ]
97
119
resource , _ := c .resourcesByType [typ ]
120
+ mapping , _ := c .mappingByType [typ ]
98
121
c .mu .RUnlock ()
99
122
100
123
// Initialize a new Client
101
124
if ! known {
102
125
var err error
103
- client , resource , err = c .makeClient (obj )
126
+ client , mapping , resource , err = c .makeClient (obj )
104
127
if err != nil {
105
- return nil , "" , err
128
+ return nil , mapping , "" , err
106
129
}
107
130
c .mu .Lock ()
108
131
defer c .mu .Unlock ()
109
132
c .clientsByType [typ ] = client
110
133
c .resourcesByType [typ ] = resource
134
+ c .mappingByType [typ ] = mapping
111
135
}
112
136
113
- return client , resource , nil
137
+ return client , mapping , resource , nil
114
138
}
115
139
116
- func (c * populatingClient ) metaAndClientFor (obj runtime.Object ) (v1.Object , rest.Interface , string , error ) {
117
- client , resource , err := c .clientFor (obj )
140
+ func (c * populatingClient ) metaAndClientFor (obj runtime.Object ) (v1.Object , rest.Interface , * meta. RESTMapping , string , error ) {
141
+ client , mapping , resource , err := c .clientFor (obj )
118
142
if err != nil {
119
- return nil , nil , "" , err
143
+ return nil , nil , nil , "" , err
120
144
}
145
+
121
146
meta , err := meta .Accessor (obj )
122
147
if err != nil {
123
- return nil , nil , "" , err
148
+ return nil , nil , nil , "" , err
124
149
}
125
- return meta , client , resource , err
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
126
158
}
127
159
128
160
// Create implements Client
129
161
func (c * populatingClient ) Create (ctx context.Context , obj runtime.Object ) error {
130
- meta , client , resource , err := c .metaAndClientFor (obj )
162
+ meta , client , mapping , resource , err := c .metaAndClientFor (obj )
131
163
if err != nil {
132
164
return err
133
165
}
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
+
134
176
return client .Post ().
135
- Namespace (meta .GetNamespace ()).
136
177
Resource (resource ).
137
178
Body (obj ).
138
179
Do ().
@@ -141,12 +182,21 @@ func (c *populatingClient) Create(ctx context.Context, obj runtime.Object) error
141
182
142
183
// Update implements Client
143
184
func (c * populatingClient ) Update (ctx context.Context , obj runtime.Object ) error {
144
- meta , client , resource , err := c .metaAndClientFor (obj )
185
+ meta , client , mapping , resource , err := c .metaAndClientFor (obj )
145
186
if err != nil {
146
187
return err
147
188
}
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
+
148
199
return client .Put ().
149
- Namespace (meta .GetNamespace ()).
150
200
Resource (resource ).
151
201
Name (meta .GetName ()).
152
202
Body (obj ).
@@ -156,12 +206,19 @@ func (c *populatingClient) Update(ctx context.Context, obj runtime.Object) error
156
206
157
207
// Delete implements Client
158
208
func (c * populatingClient ) Delete (ctx context.Context , obj runtime.Object ) error {
159
- meta , client , resource , err := c .metaAndClientFor (obj )
209
+ meta , client , mapping , resource , err := c .metaAndClientFor (obj )
160
210
if err != nil {
161
211
return err
162
212
}
213
+ if isNamespaced (mapping ) {
214
+ return client .Delete ().
215
+ Namespace (meta .GetNamespace ()).
216
+ Resource (resource ).
217
+ Name (meta .GetName ()).
218
+ Do ().
219
+ Error ()
220
+ }
163
221
return client .Delete ().
164
- Namespace (meta .GetNamespace ()).
165
222
Resource (resource ).
166
223
Name (meta .GetName ()).
167
224
Do ().
@@ -170,12 +227,19 @@ func (c *populatingClient) Delete(ctx context.Context, obj runtime.Object) error
170
227
171
228
// Get implements Client
172
229
func (c * populatingClient ) Get (ctx context.Context , key ObjectKey , obj runtime.Object ) error {
173
- client , resource , err := c .clientFor (obj )
230
+ _ , client , mapping , resource , err := c .metaAndClientFor (obj )
174
231
if err != nil {
175
232
return err
176
233
}
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
+ }
177
242
return client .Get ().
178
- Namespace (key .Namespace ).
179
243
Resource (resource ).
180
244
Name (key .Name ).
181
245
Do ().
@@ -184,16 +248,20 @@ func (c *populatingClient) Get(ctx context.Context, key ObjectKey, obj runtime.O
184
248
185
249
// List implements Client
186
250
func (c * populatingClient ) List (ctx context.Context , opts * ListOptions , obj runtime.Object ) error {
187
- client , resource , err := c .clientFor (obj )
251
+ _ , client , mapping , resource , err := c .metaAndClientFor (obj )
188
252
if err != nil {
189
253
return err
190
254
}
191
- ns := ""
192
- if opts != nil {
193
- ns = opts .Namespace
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 )
194
263
}
195
264
return client .Get ().
196
- Namespace (ns ).
197
265
Resource (resource ).
198
266
Body (obj ).
199
267
VersionedParams (opts .AsListOptions (), c .paramCodec ).
0 commit comments