@@ -22,15 +22,16 @@ import (
2222 "github.com/pkg/errors"
2323 "github.com/sirupsen/logrus"
2424 "github.com/spf13/viper"
25+ "k8s.io/api/core/v1"
2526 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2627 "k8s.io/apimachinery/pkg/runtime"
2728 "k8s.io/apimachinery/pkg/runtime/schema"
2829 "k8s.io/apimachinery/pkg/util/sets"
2930 "k8s.io/cli-runtime/pkg/genericclioptions"
3031 "k8s.io/cli-runtime/pkg/genericclioptions/resource"
31- authv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
3232 "sort"
3333 "strings"
34+ "sync"
3435)
3536
3637// groupResource contains the APIGroup and APIResource
@@ -39,53 +40,26 @@ type groupResource struct {
3940 APIResource metav1.APIResource
4041}
4142
42- // TODO rework client, so that it does not fail without cluster admin rights
4343func GetAllServerResources (flags * genericclioptions.ConfigFlags ) (runtime.Object , error ) {
4444 useCache := viper .GetBool (constants .FlagUseCache )
4545 scope := viper .GetString (constants .FlagScope )
4646
47- grs , err := FetchAvailableGroupResources (useCache , scope , flags )
47+ grs , err := fetchAvailableGroupResources (useCache , scope , flags )
4848 if err != nil {
4949 return nil , errors .Wrap (err , "fetch available group resources" )
5050 }
5151
52- resources := extractRelevantResourceNames (grs , viper .GetStringSlice (constants .FlagExclude ))
52+ resources := extractRelevantResources (grs , viper .GetStringSlice (constants .FlagExclude ))
5353
54- restConfig , _ := flags .ToRESTConfig ()
55- authClient , _ := authv1 .NewForConfig (restConfig )
56- allowed , err := CheckResourceAccessPar (authClient , resources )
57- if err != nil {
58- return nil , errors .Wrap (err , "check resource access" )
54+ response , err := fetchResourcesBulk (flags , resources ... )
55+ if err == nil {
56+ return response , nil
5957 }
6058
61- allowedResourceTypes := ToResourceTypes (allowed )
62- logrus .Debugf ("Resources to fetch: %s" , allowedResourceTypes )
63-
64- request := resource .NewBuilder (flags ).
65- Unstructured ().
66- SelectAllParam (true ).
67- ResourceTypes (allowedResourceTypes ... ).
68- Latest ()
69-
70- if ns := viper .GetString (constants .FlagNamespace ); ns != "" {
71- logrus .Debugf ("Restricting to namespace %s" , ns )
72- request .NamespaceParam (ns )
73- } else {
74- request .AllNamespaces (true )
75- }
76-
77- response := request .Do ()
78-
79- if infos , err := response .Infos (); err != nil {
80- return nil , errors .Wrap (err , "request resources" )
81- } else if len (infos ) == 0 {
82- return nil , fmt .Errorf ("No resources found" )
83- }
84-
85- return response .Object ()
59+ return fetchResourcesIncremental (flags , resources ... )
8660}
8761
88- func FetchAvailableGroupResources (cache bool , scope string , flags * genericclioptions.ConfigFlags ) ([]groupResource , error ) {
62+ func fetchAvailableGroupResources (cache bool , scope string , flags * genericclioptions.ConfigFlags ) ([]groupResource , error ) {
8963 client , err := flags .ToDiscoveryClient ()
9064 if err != nil {
9165 return nil , errors .Wrap (err , "discovery client" )
@@ -105,7 +79,7 @@ func FetchAvailableGroupResources(cache bool, scope string, flags *genericcliopt
10579 return nil , errors .Wrap (err , "get preferred resources" )
10680 }
10781
108- grs := []groupResource {}
82+ var grs []groupResource
10983 for _ , list := range resources {
11084 if len (list .APIResources ) == 0 {
11185 continue
@@ -138,11 +112,11 @@ func FetchAvailableGroupResources(cache bool, scope string, flags *genericcliopt
138112 return grs , nil
139113}
140114
141- func extractRelevantResourceNames (grs []groupResource , exclusions []string ) []groupResource {
115+ func extractRelevantResources (grs []groupResource , exclusions []string ) []groupResource {
142116 sort .Stable (sortableGroupResource (grs ))
143117 forbidden := sets .NewString (exclusions ... )
144118
145- result := []groupResource {}
119+ var result []groupResource
146120 for _ , r := range grs {
147121 name := r .fullName ()
148122 resourceIds := r .APIResource .ShortNames
@@ -157,6 +131,71 @@ func extractRelevantResourceNames(grs []groupResource, exclusions []string) []gr
157131 return result
158132}
159133
134+ // Fetches all objects in bulk. This is much faster than incrementally but may fail due to missing rights
135+ func fetchResourcesBulk (flags * genericclioptions.ConfigFlags , resourceTypes ... groupResource ) (runtime.Object , error ) {
136+ resourceNames := ToResourceTypes (resourceTypes )
137+ logrus .Debugf ("Resources to fetch: %s" , resourceNames )
138+
139+ request := resource .NewBuilder (flags ).
140+ Unstructured ().
141+ SelectAllParam (true ).
142+ ResourceTypes (resourceNames ... ).
143+ Latest ()
144+ if ns := viper .GetString (constants .FlagNamespace ); ns != "" {
145+ request .NamespaceParam (ns )
146+ } else {
147+ request .AllNamespaces (true )
148+ }
149+
150+ return request .Do ().Object ()
151+ }
152+
153+ // Fetches all objects of the given resources one-by-one. This can be used as a fallback when fetchResourcesBulk fails.
154+ func fetchResourcesIncremental (flags * genericclioptions.ConfigFlags , rs ... groupResource ) (runtime.Object , error ) {
155+ logrus .Debug ("Fetch resources incrementally" )
156+ group := sync.WaitGroup {}
157+
158+ objsChan := make (chan runtime.Object )
159+ for _ , r := range rs {
160+ r := r
161+ group .Add (1 )
162+ go func (sendObj chan <- runtime.Object ) {
163+ defer group .Done ()
164+ if o , e := fetchResourcesBulk (flags , r ); e != nil {
165+ logrus .Warnf ("Cannot fetch: %s" , e )
166+ } else {
167+ sendObj <- o
168+ }
169+ }(objsChan )
170+ }
171+
172+ var objs []runtime.Object
173+ go func (recvObj <- chan runtime.Object ) {
174+ for o := range recvObj {
175+ objs = append (objs , o )
176+ }
177+ }(objsChan )
178+
179+ logrus .Debug ("Wait for resource requests" )
180+ group .Wait ()
181+ logrus .Debug ("Resource requests all done" )
182+ close (objsChan )
183+
184+ if len (objs ) == 0 {
185+ return nil , fmt .Errorf ("Not authorized to list any resources. Try to narrow the scope with --namespace." )
186+ }
187+
188+ return toV1List (objs ), nil
189+ }
190+
191+ func toV1List (objects []runtime.Object ) runtime.Object {
192+ var raw []runtime.RawExtension
193+ for _ , o := range objects {
194+ raw = append (raw , runtime.RawExtension {Object : o })
195+ }
196+ return & v1.List {Items : raw }
197+ }
198+
160199func getResourceScope (scope string ) (skipCluster , skipNamespace bool , err error ) {
161200 switch scope {
162201 case "" :
@@ -174,6 +213,7 @@ func getResourceScope(scope string) (skipCluster, skipNamespace bool, err error)
174213 return
175214}
176215
216+ // Extracts the full name including APIGroup, e.g. 'deployment.apps'
177217func (g groupResource ) fullName () string {
178218 if g .APIGroup == "" {
179219 return g .APIResource .Name
@@ -184,7 +224,7 @@ func (g groupResource) fullName() string {
184224type sortableGroupResource []groupResource
185225
186226func ToResourceTypes (in []groupResource ) []string {
187- result := []string {}
227+ var result []string
188228 for _ , r := range in {
189229 result = append (result , r .fullName ())
190230 }
0 commit comments