@@ -6,8 +6,10 @@ import (
6
6
"strings"
7
7
"time"
8
8
9
+ "github.com/pkg/errors"
9
10
log "github.com/sirupsen/logrus"
10
11
apixv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
12
+ apiErrors "k8s.io/apimachinery/pkg/api/errors"
11
13
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12
14
"k8s.io/apimachinery/pkg/runtime"
13
15
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -326,6 +328,21 @@ func getInClusterConfig() (config *rest.Config, defaultNs string, err error) {
326
328
// NOTE that fetching all preferred resources can give errors if there are non-working
327
329
// api controllers in cluster.
328
330
func (c * Client ) APIResourceList (apiVersion string ) (lists []* metav1.APIResourceList , err error ) {
331
+ lists , err = c .apiResourceList (apiVersion )
332
+ if err != nil {
333
+ if errors .Cause (err ).Error () == "not found" {
334
+ // *errors.errorString type is here, we can't check it another way
335
+ c .cachedDiscovery .Invalidate ()
336
+ return c .apiResourceList (apiVersion )
337
+ }
338
+
339
+ return nil , err
340
+ }
341
+
342
+ return lists , nil
343
+ }
344
+
345
+ func (c * Client ) apiResourceList (apiVersion string ) (lists []* metav1.APIResourceList , err error ) {
329
346
if apiVersion == "" {
330
347
// Get all preferred resources.
331
348
// Can return errors if api controllers are not available.
@@ -348,56 +365,51 @@ func (c *Client) APIResourceList(apiVersion string) (lists []*metav1.APIResource
348
365
349
366
list , err := c .discovery ().ServerResourcesForGroupVersion (gv .String ())
350
367
if err != nil {
351
- return nil , fmt .Errorf ("apiVersion '%s' has no supported resources in cluster: %v" , apiVersion , err )
368
+ // if not found, err has type *errors.errorString here
369
+ return nil , errors .Wrapf (err , "apiVersion '%s' has no supported resources in cluster" , apiVersion )
352
370
}
353
371
lists = []* metav1.APIResourceList {list }
354
372
}
355
373
356
- // TODO should it copy group and version into each resource?
357
-
358
- // TODO create debug command to output this from cli
359
- // Debug mode will list all available CRDs for apiVersion
360
- // for _, r := range list.APIResources {
361
- // log.Debugf("GVR: %30s %30s %30s", list.GroupVersion, r.Kind,
362
- // fmt.Sprintf("%+v", append([]string{r.Name}, r.ShortNames...)),
363
- // )
364
- // }
365
-
366
374
return
367
375
}
368
376
369
377
// APIResource fetches APIResource object from cluster that specifies the name of a resource and whether it is namespaced.
378
+ // if resource not found, we try to invalidate cache and
370
379
//
371
380
// NOTE that fetching with empty apiVersion can give errors if there are non-working
372
381
// api controllers in cluster.
373
- func (c * Client ) APIResource (apiVersion , kind string ) (res * metav1.APIResource , err error ) {
382
+ func (c * Client ) APIResource (apiVersion , kind string ) (* metav1.APIResource , error ) {
383
+ resource , err := c .apiResource (apiVersion , kind )
384
+ if err != nil {
385
+ if apiErrors .IsNotFound (err ) {
386
+ c .cachedDiscovery .Invalidate ()
387
+ resource , err = c .apiResource (apiVersion , kind )
388
+ } else {
389
+ return nil , fmt .Errorf ("apiVersion '%s', kind '%s' is not supported by cluster: %w" , apiVersion , kind , err )
390
+ }
391
+ }
392
+ if err != nil {
393
+ return nil , fmt .Errorf ("apiVersion '%s', kind '%s' is not supported by cluster: %w" , apiVersion , kind , err )
394
+ }
395
+
396
+ return resource , nil
397
+ }
398
+
399
+ func (c * Client ) apiResource (apiVersion , kind string ) (res * metav1.APIResource , err error ) {
374
400
lists , err := c .APIResourceList (apiVersion )
375
401
if err != nil && len (lists ) == 0 {
376
402
// apiVersion is defined and there is a ServerResourcesForGroupVersion error
377
403
return nil , err
378
404
}
379
405
380
406
resource := getApiResourceFromResourceLists (kind , lists )
381
- if resource != nil {
382
- return resource , nil
383
- }
384
-
385
- if c .cachedDiscovery != nil {
386
- c .cachedDiscovery .Invalidate ()
387
- }
388
-
389
- resource = getApiResourceFromResourceLists (kind , lists )
390
- if resource != nil {
391
- return resource , nil
407
+ if resource == nil {
408
+ gv , _ := schema .ParseGroupVersion (apiVersion )
409
+ return nil , apiErrors .NewNotFound (schema.GroupResource {Group : gv .Group , Resource : kind }, "" )
392
410
}
393
411
394
- // If resource is not found, append additional error, may be the custom API of the resource is not available.
395
- additionalErr := ""
396
- if err != nil {
397
- additionalErr = fmt .Sprintf (", additional error: %s" , err .Error ())
398
- }
399
- err = fmt .Errorf ("apiVersion '%s', kind '%s' is not supported by cluster%s" , apiVersion , kind , additionalErr )
400
- return nil , err
412
+ return resource , nil
401
413
}
402
414
403
415
// GroupVersionResource returns a GroupVersionResource object to use with dynamic informer.
@@ -424,6 +436,14 @@ func (c *Client) discovery() discovery.DiscoveryInterface {
424
436
return c .Discovery ()
425
437
}
426
438
439
+ // InvalidateDiscoveryCache allows you to invalidate cache manually, for example, when you are deploying CRD
440
+ // KubeClient tries to invalidate cache automatically when needed, but you can save a few resources to call this manually
441
+ func (c * Client ) InvalidateDiscoveryCache () {
442
+ if c .cachedDiscovery != nil {
443
+ c .cachedDiscovery .Invalidate ()
444
+ }
445
+ }
446
+
427
447
func equalLowerCasedToOneOf (term string , choices ... string ) bool {
428
448
if len (choices ) == 0 {
429
449
return false
0 commit comments