@@ -17,8 +17,12 @@ limitations under the License.
17
17
package kubernetes
18
18
19
19
import (
20
+ "sync"
21
+ "time"
22
+
20
23
"github.com/pkg/errors"
21
24
apiextnv1beta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
25
+ "k8s.io/client-go/discovery"
22
26
"k8s.io/client-go/kubernetes"
23
27
"k8s.io/client-go/rest"
24
28
"k8s.io/klog/v2"
@@ -47,13 +51,14 @@ type UtilityFuncs struct {
47
51
getAPIForAPIVersionAndResourceFn func (string , string ) * dynamicdiscovery.APIResource
48
52
}
49
53
50
- // Utility exposes instances needed to invoke kubernetes
51
- // api operations
54
+ // Utility provides options to invoke kubernetes APIs
52
55
type Utility struct {
53
56
* UtilityFuncs
54
57
Retry * Retryable
55
58
56
- apiDiscovery * dynamicdiscovery.APIResourceDiscovery
59
+ kubeConfig * rest.Config
60
+
61
+ apiResourceDiscovery * dynamicdiscovery.APIResourceDiscovery
57
62
58
63
// dynamic client to invoke kubernetes operations
59
64
// against kubernetes native as well as custom resources
@@ -79,53 +84,62 @@ type Utility struct {
79
84
err error
80
85
}
81
86
82
- func (u * Utility ) setTeardownFlag (config UtilityConfig ) {
83
- u .isTeardown = config .IsTeardown
84
- }
87
+ var doOnce sync.Once
85
88
86
- func ( u * Utility ) setAPIDiscovery ( config UtilityConfig ) {
87
- u . apiDiscovery = config . APIDiscovery
88
- }
89
+ // singleton instance of Utility to be used across the
90
+ // project to invoke Kubernetes operations
91
+ var singleton * Utility
89
92
90
- func (u * Utility ) setCRDClient (config UtilityConfig ) {
91
- if config .KubeConfig == nil {
92
- u .err = errors .Errorf (
93
- "Failed to set crd client: Nil kube config provided" ,
94
- )
95
- return
93
+ // Singleton returns a new or existing instance of Utility
94
+ func Singleton (config UtilityConfig ) (* Utility , error ) {
95
+ if singleton != nil {
96
+ return singleton , nil
96
97
}
97
- u .crdClient , u .err = apiextnv1beta1 .NewForConfig (
98
- config .KubeConfig ,
99
- )
98
+ var err error
99
+ doOnce .Do (func () {
100
+ singleton , err = NewUtility (config )
101
+ })
102
+ return singleton , err
100
103
}
101
104
102
- func (u * Utility ) setDynamicClientset (config UtilityConfig ) {
103
- u .dynamicClientset , u .err = dynamicclientset .New (
104
- config .KubeConfig ,
105
- config .APIDiscovery ,
106
- )
107
- }
108
-
109
- func (u * Utility ) setKubeClientset (config UtilityConfig ) {
110
- u .kubeClientset , u .err = kubernetes .NewForConfig (
111
- config .KubeConfig ,
112
- )
113
- }
114
-
115
- // NewUtility returns a new instance of Fixture
105
+ // NewUtility returns a new instance of Kubernetes utility
116
106
func NewUtility (config UtilityConfig ) (* Utility , error ) {
117
- // check retry
107
+ // initialize retry instance to default behaviour
118
108
var retry = NewRetry (RetryConfig {})
119
109
if config .Retry != nil {
110
+ // override from config if available
120
111
retry = config .Retry
121
112
}
113
+
114
+ // initialize utility instance
122
115
u := & Utility {
123
116
UtilityFuncs : & UtilityFuncs {},
124
117
Retry : retry ,
118
+ kubeConfig : config .KubeConfig ,
125
119
}
120
+
121
+ if u .kubeConfig == nil {
122
+ // Set kube config to in-cluster config
123
+ u .kubeConfig , u .err = rest .InClusterConfig ()
124
+ if u .err != nil {
125
+ return nil , errors .Wrapf (
126
+ u .err ,
127
+ "Failed to create k8s utility" ,
128
+ )
129
+ }
130
+ }
131
+
132
+ // setup options to mutate utility instance
133
+ // based on the provided config
134
+ //
135
+ // NOTE:
136
+ // Following order needs to be maintained
126
137
var setters = []func (UtilityConfig ){
138
+ // pre settings
127
139
u .setTeardownFlag ,
128
- u .setAPIDiscovery ,
140
+ u .setAPIResourceDiscoveryOrDefault ,
141
+
142
+ // post settings
129
143
u .setCRDClient ,
130
144
u .setDynamicClientset ,
131
145
u .setKubeClientset ,
@@ -139,11 +153,50 @@ func NewUtility(config UtilityConfig) (*Utility, error) {
139
153
return u , nil
140
154
}
141
155
142
- // Teardown deletes resources created through this instance
143
- func (u * Utility ) Teardown () {
144
- if ! u .isTeardown {
156
+ func (u * Utility ) setTeardownFlag (config UtilityConfig ) {
157
+ u .isTeardown = config .IsTeardown
158
+ }
159
+
160
+ func (u * Utility ) setAPIResourceDiscoveryOrDefault (config UtilityConfig ) {
161
+ u .apiResourceDiscovery = config .APIDiscovery
162
+ if u .apiResourceDiscovery != nil {
145
163
return
146
164
}
165
+ discoveryClient := discovery .NewDiscoveryClientForConfigOrDie (
166
+ u .kubeConfig ,
167
+ )
168
+ d := dynamicdiscovery .NewAPIResourceDiscoverer (discoveryClient )
169
+
170
+ // This needs to be started with appropriate refresh interval
171
+ // before being used. We set to 30 seconds discovery interval.
172
+ // In other words, if new CRDs are applied then it will take
173
+ // this interval for these new CRDs to be discovered.
174
+ d .Start (time .Duration (30 ) * time .Second )
175
+
176
+ u .apiResourceDiscovery = d
177
+ }
178
+
179
+ func (u * Utility ) setCRDClient (config UtilityConfig ) {
180
+ u .crdClient , u .err = apiextnv1beta1 .NewForConfig (
181
+ u .kubeConfig ,
182
+ )
183
+ }
184
+
185
+ func (u * Utility ) setDynamicClientset (config UtilityConfig ) {
186
+ u .dynamicClientset , u .err = dynamicclientset .New (
187
+ u .kubeConfig ,
188
+ u .apiResourceDiscovery , // this must be set previously
189
+ )
190
+ }
191
+
192
+ func (u * Utility ) setKubeClientset (config UtilityConfig ) {
193
+ u .kubeClientset , u .err = kubernetes .NewForConfig (
194
+ u .kubeConfig ,
195
+ )
196
+ }
197
+
198
+ // MustTeardown deletes resources created through this instance
199
+ func (u * Utility ) MustTeardown () {
147
200
// cleanup in descending order
148
201
for i := len (u .teardownFuncs ) - 1 ; i >= 0 ; i -- {
149
202
teardown := u .teardownFuncs [i ]
@@ -173,13 +226,31 @@ func (u *Utility) Teardown() {
173
226
}
174
227
}
175
228
176
- // AddToTeardown adds the given teardown function to
229
+ // Teardown optionally deletes resources created through this
230
+ // instance
231
+ func (u * Utility ) Teardown () {
232
+ if ! u .isTeardown {
233
+ return
234
+ }
235
+ u .MustTeardown ()
236
+ }
237
+
238
+ // MustAddToTeardown adds the given teardown function to
239
+ // the list of teardown functions
240
+ func (u * Utility ) MustAddToTeardown (teardown func () error ) {
241
+ if teardown == nil {
242
+ return
243
+ }
244
+ u .teardownFuncs = append (u .teardownFuncs , teardown )
245
+ }
246
+
247
+ // AddToTeardown optionally adds the given teardown function to
177
248
// the list of teardown functions
178
249
func (u * Utility ) AddToTeardown (teardown func () error ) {
179
250
if ! u .isTeardown {
180
251
return
181
252
}
182
- u .teardownFuncs = append ( u . teardownFuncs , teardown )
253
+ u .MustAddToTeardown ( teardown )
183
254
}
184
255
185
256
// GetClientForAPIVersionAndKind returns the dynamic client for the
@@ -198,32 +269,42 @@ func (u *Utility) GetClientForAPIVersionAndKind(
198
269
}
199
270
200
271
// GetClientForAPIVersionAndResource returns the dynamic client for the
201
- // given api version & resource
272
+ // given api version & resource name
202
273
func (u * Utility ) GetClientForAPIVersionAndResource (
203
274
apiversion string ,
204
- resource string ,
275
+ resourceName string ,
205
276
) (* clientset.ResourceClient , error ) {
206
277
if u .getClientForAPIVersionAndResourceFn != nil {
207
- return u .getClientForAPIVersionAndResourceFn (apiversion , resource )
278
+ return u .getClientForAPIVersionAndResourceFn (apiversion , resourceName )
208
279
}
209
280
return u .dynamicClientset .GetClientForAPIVersionAndResource (
210
281
apiversion ,
211
- resource ,
282
+ resourceName ,
212
283
)
213
284
}
214
285
215
- // GetAPIForAPIVersionAndResource returns the discovered api based
216
- // on the provided api version & resource
217
- func (u * Utility ) GetAPIForAPIVersionAndResource (
286
+ // GetAPIResourceForAPIVersionAndResourceName returns the
287
+ // discovered API resource based on the provided api version &
288
+ // resource name
289
+ func (u * Utility ) GetAPIResourceForAPIVersionAndResourceName (
218
290
apiversion string ,
219
- resource string ,
291
+ resourceName string ,
220
292
) * dynamicdiscovery.APIResource {
221
293
if u .getAPIForAPIVersionAndResourceFn != nil {
222
- return u .getAPIForAPIVersionAndResourceFn (apiversion , resource )
294
+ return u .getAPIForAPIVersionAndResourceFn (apiversion , resourceName )
223
295
}
224
- return u .apiDiscovery .
225
- GetAPIForAPIVersionAndResource (
226
- apiversion ,
227
- resource ,
228
- )
296
+ return u .apiResourceDiscovery .GetAPIForAPIVersionAndResource (
297
+ apiversion ,
298
+ resourceName ,
299
+ )
300
+ }
301
+
302
+ // GetAPIResourceDiscovery returns the api resource discovery instance
303
+ func (u * Utility ) GetAPIResourceDiscovery () * dynamicdiscovery.APIResourceDiscovery {
304
+ return u .apiResourceDiscovery
305
+ }
306
+
307
+ // GetKubeConfig returns the Kubernetes config instance
308
+ func (u * Utility ) GetKubeConfig () * rest.Config {
309
+ return u .kubeConfig
229
310
}
0 commit comments