Merge pull request #73345 from sttts/sttts-speedup-cache-miss-kubectl

discovery: speedup kubectl restmapper cache misses by a two-digit factor
pull/564/head
Kubernetes Prow Robot 2019-02-12 08:00:03 -08:00 committed by GitHub
commit 64ce2e598f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 356 additions and 83 deletions

View File

@ -948,10 +948,15 @@ func (_ *fakeServerResources) ServerResourcesForGroupVersion(groupVersion string
return nil, nil return nil, nil
} }
// Deprecated: use ServerGroupsAndResources instead.
func (_ *fakeServerResources) ServerResources() ([]*metav1.APIResourceList, error) { func (_ *fakeServerResources) ServerResources() ([]*metav1.APIResourceList, error) {
return nil, nil return nil, nil
} }
func (_ *fakeServerResources) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return nil, nil, nil
}
func (f *fakeServerResources) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (f *fakeServerResources) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
f.Lock.Lock() f.Lock.Lock()
defer f.Lock.Unlock() defer f.Lock.Unlock()

View File

@ -275,10 +275,15 @@ func (d *fakeCachedDiscoveryClient) Fresh() bool {
func (d *fakeCachedDiscoveryClient) Invalidate() { func (d *fakeCachedDiscoveryClient) Invalidate() {
} }
// Deprecated: use ServerGroupsAndResources instead.
func (d *fakeCachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) { func (d *fakeCachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
return []*metav1.APIResourceList{}, nil return []*metav1.APIResourceList{}, nil
} }
func (d *fakeCachedDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return []*metav1.APIGroup{}, []*metav1.APIResourceList{}, nil
}
// TestFactory extends cmdutil.Factory // TestFactory extends cmdutil.Factory
type TestFactory struct { type TestFactory struct {
cmdutil.Factory cmdutil.Factory

View File

@ -405,10 +405,16 @@ func (d *fakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*me
} }
// ServerResources returns the supported resources for all groups and versions. // ServerResources returns the supported resources for all groups and versions.
// Deprecated: use ServerGroupsAndResources instead.
func (d *fakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) { func (d *fakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) {
return nil, nil return nil, nil
} }
// ServerGroupsAndResources returns the supported groups and resources for all groups and versions.
func (d *fakeDiscovery) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return nil, nil, nil
}
// ServerPreferredResources returns the supported resources with the version preferred by the // ServerPreferredResources returns the supported resources with the version preferred by the
// server. // server.
func (d *fakeDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (d *fakeDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) {

View File

@ -118,10 +118,16 @@ func (d *memCacheClient) ServerResourcesForGroupVersion(groupVersion string) (*m
} }
// ServerResources returns the supported resources for all groups and versions. // ServerResources returns the supported resources for all groups and versions.
// Deprecated: use ServerGroupsAndResources instead.
func (d *memCacheClient) ServerResources() ([]*metav1.APIResourceList, error) { func (d *memCacheClient) ServerResources() ([]*metav1.APIResourceList, error) {
return discovery.ServerResources(d) return discovery.ServerResources(d)
} }
// ServerGroupsAndResources returns the groups and supported resources for all groups and versions.
func (d *memCacheClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return discovery.ServerGroupsAndResources(d)
}
func (d *memCacheClient) ServerGroups() (*metav1.APIGroupList, error) { func (d *memCacheClient) ServerGroups() (*metav1.APIGroupList, error) {
d.lock.Lock() d.lock.Lock()
defer d.lock.Unlock() defer d.lock.Unlock()

View File

@ -90,8 +90,15 @@ func (d *CachedDiscoveryClient) ServerResourcesForGroupVersion(groupVersion stri
} }
// ServerResources returns the supported resources for all groups and versions. // ServerResources returns the supported resources for all groups and versions.
// Deprecated: use ServerGroupsAndResources instead.
func (d *CachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) { func (d *CachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
return ServerResources(d) _, rs, err := ServerGroupsAndResources(d)
return rs, err
}
// ServerGroupsAndResources returns the supported groups and resources for all groups and versions.
func (d *CachedDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return ServerGroupsAndResources(d)
} }
// ServerGroups returns the supported groups, with information like supported versions and the // ServerGroups returns the supported groups, with information like supported versions and the

View File

@ -112,6 +112,10 @@ func (c *fakeDiscoveryClient) RESTClient() restclient.Interface {
func (c *fakeDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) { func (c *fakeDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
c.groupCalls = c.groupCalls + 1 c.groupCalls = c.groupCalls + 1
return c.serverGroups()
}
func (c *fakeDiscoveryClient) serverGroups() (*metav1.APIGroupList, error) {
return &metav1.APIGroupList{ return &metav1.APIGroupList{
Groups: []metav1.APIGroup{ Groups: []metav1.APIGroup{
{ {
@ -140,12 +144,26 @@ func (c *fakeDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string
return nil, errors.NewNotFound(schema.GroupResource{}, "") return nil, errors.NewNotFound(schema.GroupResource{}, "")
} }
// Deprecated: use ServerGroupsAndResources instead.
func (c *fakeDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) { func (c *fakeDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
_, rs, err := c.ServerGroupsAndResources()
return rs, err
}
func (c *fakeDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
c.resourceCalls = c.resourceCalls + 1 c.resourceCalls = c.resourceCalls + 1
if c.serverResourcesHandler != nil {
return c.serverResourcesHandler() gs, _ := c.serverGroups()
resultGroups := []*metav1.APIGroup{}
for i := range gs.Groups {
resultGroups = append(resultGroups, &gs.Groups[i])
} }
return []*metav1.APIResourceList{}, nil
if c.serverResourcesHandler != nil {
rs, err := c.serverResourcesHandler()
return resultGroups, rs, err
}
return resultGroups, []*metav1.APIResourceList{}, nil
} }
func (c *fakeDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (c *fakeDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {

View File

@ -88,12 +88,28 @@ type ServerResourcesInterface interface {
// ServerResourcesForGroupVersion returns the supported resources for a group and version. // ServerResourcesForGroupVersion returns the supported resources for a group and version.
ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error)
// ServerResources returns the supported resources for all groups and versions. // ServerResources returns the supported resources for all groups and versions.
//
// The returned resource list might be non-nil with partial results even in the case of
// non-nil error.
//
// Deprecated: use ServerGroupsAndResources instead.
ServerResources() ([]*metav1.APIResourceList, error) ServerResources() ([]*metav1.APIResourceList, error)
// ServerResources returns the supported groups and resources for all groups and versions.
//
// The returned group and resource lists might be non-nil with partial results even in the
// case of non-nil error.
ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)
// ServerPreferredResources returns the supported resources with the version preferred by the // ServerPreferredResources returns the supported resources with the version preferred by the
// server. // server.
//
// The returned group and resource lists might be non-nil with partial results even in the
// case of non-nil error.
ServerPreferredResources() ([]*metav1.APIResourceList, error) ServerPreferredResources() ([]*metav1.APIResourceList, error)
// ServerPreferredNamespacedResources returns the supported namespaced resources with the // ServerPreferredNamespacedResources returns the supported namespaced resources with the
// version preferred by the server. // version preferred by the server.
//
// The returned resource list might be non-nil with partial results even in the case of
// non-nil error.
ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error)
} }
@ -191,14 +207,18 @@ func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (r
return resources, nil return resources, nil
} }
// serverResources returns the supported resources for all groups and versions. // ServerResources returns the supported resources for all groups and versions.
func (d *DiscoveryClient) serverResources() ([]*metav1.APIResourceList, error) { // Deprecated: use ServerGroupsAndResources instead.
return ServerResources(d) func (d *DiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
_, rs, err := d.ServerGroupsAndResources()
return rs, err
} }
// ServerResources returns the supported resources for all groups and versions. // ServerGroupsAndResources returns the supported resources for all groups and versions.
func (d *DiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) { func (d *DiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return withRetries(defaultRetries, d.serverResources) return withRetries(defaultRetries, func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return ServerGroupsAndResources(d)
})
} }
// ErrGroupDiscoveryFailed is returned if one or more API groups fail to load. // ErrGroupDiscoveryFailed is returned if one or more API groups fail to load.
@ -224,23 +244,28 @@ func IsGroupDiscoveryFailedError(err error) bool {
return err != nil && ok return err != nil && ok
} }
// serverPreferredResources returns the supported resources with the version preferred by the server. // ServerResources uses the provided discovery interface to look up supported resources for all groups and versions.
func (d *DiscoveryClient) serverPreferredResources() ([]*metav1.APIResourceList, error) { // Deprecated: use ServerGroupsAndResources instead.
return ServerPreferredResources(d) func ServerResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) {
_, rs, err := ServerGroupsAndResources(d)
return rs, err
} }
// ServerResources uses the provided discovery interface to look up supported resources for all groups and versions. func ServerGroupsAndResources(d DiscoveryInterface) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
func ServerResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) { sgs, err := d.ServerGroups()
apiGroups, err := d.ServerGroups() if sgs == nil {
if err != nil { return nil, nil, err
return nil, err }
resultGroups := []*metav1.APIGroup{}
for i := range sgs.Groups {
resultGroups = append(resultGroups, &sgs.Groups[i])
} }
groupVersionResources, failedGroups := fetchGroupVersionResources(d, apiGroups) groupVersionResources, failedGroups := fetchGroupVersionResources(d, sgs)
// order results by group/version discovery order // order results by group/version discovery order
result := []*metav1.APIResourceList{} result := []*metav1.APIResourceList{}
for _, apiGroup := range apiGroups.Groups { for _, apiGroup := range sgs.Groups {
for _, version := range apiGroup.Versions { for _, version := range apiGroup.Versions {
gv := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version} gv := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version}
if resources, ok := groupVersionResources[gv]; ok { if resources, ok := groupVersionResources[gv]; ok {
@ -250,10 +275,10 @@ func ServerResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) {
} }
if len(failedGroups) == 0 { if len(failedGroups) == 0 {
return result, nil return resultGroups, result, nil
} }
return result, &ErrGroupDiscoveryFailed{Groups: failedGroups} return resultGroups, result, &ErrGroupDiscoveryFailed{Groups: failedGroups}
} }
// ServerPreferredResources uses the provided discovery interface to look up preferred resources // ServerPreferredResources uses the provided discovery interface to look up preferred resources
@ -317,7 +342,7 @@ func ServerPreferredResources(d DiscoveryInterface) ([]*metav1.APIResourceList,
return result, &ErrGroupDiscoveryFailed{Groups: failedGroups} return result, &ErrGroupDiscoveryFailed{Groups: failedGroups}
} }
// fetchServerResourcesForGroupVersions uses the discovery client to fetch the resources for the specified groups in parallel // fetchServerResourcesForGroupVersions uses the discovery client to fetch the resources for the specified groups in parallel.
func fetchGroupVersionResources(d DiscoveryInterface, apiGroups *metav1.APIGroupList) (map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error) { func fetchGroupVersionResources(d DiscoveryInterface, apiGroups *metav1.APIGroupList) (map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error) {
groupVersionResources := make(map[schema.GroupVersion]*metav1.APIResourceList) groupVersionResources := make(map[schema.GroupVersion]*metav1.APIResourceList)
failedGroups := make(map[schema.GroupVersion]error) failedGroups := make(map[schema.GroupVersion]error)
@ -341,7 +366,9 @@ func fetchGroupVersionResources(d DiscoveryInterface, apiGroups *metav1.APIGroup
if err != nil { if err != nil {
// TODO: maybe restrict this to NotFound errors // TODO: maybe restrict this to NotFound errors
failedGroups[groupVersion] = err failedGroups[groupVersion] = err
} else { }
if apiResourceList != nil {
// even in case of error, some fallback might have been returned
groupVersionResources[groupVersion] = apiResourceList groupVersionResources[groupVersion] = apiResourceList
} }
}() }()
@ -355,7 +382,11 @@ func fetchGroupVersionResources(d DiscoveryInterface, apiGroups *metav1.APIGroup
// ServerPreferredResources returns the supported resources with the version preferred by the // ServerPreferredResources returns the supported resources with the version preferred by the
// server. // server.
func (d *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (d *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return withRetries(defaultRetries, d.serverPreferredResources) _, rs, err := withRetries(defaultRetries, func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
rs, err := ServerPreferredResources(d)
return nil, rs, err
})
return rs, err
} }
// ServerPreferredNamespacedResources returns the supported namespaced resources with the // ServerPreferredNamespacedResources returns the supported namespaced resources with the
@ -410,19 +441,20 @@ func (d *DiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) {
} }
// withRetries retries the given recovery function in case the groups supported by the server change after ServerGroup() returns. // withRetries retries the given recovery function in case the groups supported by the server change after ServerGroup() returns.
func withRetries(maxRetries int, f func() ([]*metav1.APIResourceList, error)) ([]*metav1.APIResourceList, error) { func withRetries(maxRetries int, f func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
var result []*metav1.APIResourceList var result []*metav1.APIResourceList
var resultGroups []*metav1.APIGroup
var err error var err error
for i := 0; i < maxRetries; i++ { for i := 0; i < maxRetries; i++ {
result, err = f() resultGroups, result, err = f()
if err == nil { if err == nil {
return result, nil return resultGroups, result, nil
} }
if _, ok := err.(*ErrGroupDiscoveryFailed); !ok { if _, ok := err.(*ErrGroupDiscoveryFailed); !ok {
return nil, err return nil, nil, err
} }
} }
return result, err return resultGroups, result, err
} }
func setDiscoveryDefaults(config *restclient.Config) error { func setDiscoveryDefaults(config *restclient.Config) error {

View File

@ -53,13 +53,29 @@ func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*me
} }
// ServerResources returns the supported resources for all groups and versions. // ServerResources returns the supported resources for all groups and versions.
// Deprecated: use ServerGroupsAndResources instead.
func (c *FakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) { func (c *FakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) {
_, rs, err := c.ServerGroupsAndResources()
return rs, err
}
// ServerGroupsAndResources returns the supported groups and resources for all groups and versions.
func (c *FakeDiscovery) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
sgs, err := c.ServerGroups()
if err != nil {
return nil, nil, err
}
resultGroups := []*metav1.APIGroup{}
for i := range sgs.Groups {
resultGroups = append(resultGroups, &sgs.Groups[i])
}
action := testing.ActionImpl{ action := testing.ActionImpl{
Verb: "get", Verb: "get",
Resource: schema.GroupVersionResource{Resource: "resource"}, Resource: schema.GroupVersionResource{Resource: "resource"},
} }
c.Invokes(action, nil) c.Invokes(action, nil)
return c.Resources, nil return resultGroups, c.Resources, nil
} }
// ServerPreferredResources returns the supported resources with the version // ServerPreferredResources returns the supported resources with the version

View File

@ -36,6 +36,7 @@ go_test(
"//staging/src/k8s.io/client-go/discovery:go_default_library", "//staging/src/k8s.io/client-go/discovery:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/rest/fake:go_default_library", "//staging/src/k8s.io/client-go/rest/fake:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library",
], ],

View File

@ -145,27 +145,26 @@ func NewDiscoveryRESTMapper(groupResources []*APIGroupResources) meta.RESTMapper
// GetAPIGroupResources uses the provided discovery client to gather // GetAPIGroupResources uses the provided discovery client to gather
// discovery information and populate a slice of APIGroupResources. // discovery information and populate a slice of APIGroupResources.
func GetAPIGroupResources(cl discovery.DiscoveryInterface) ([]*APIGroupResources, error) { func GetAPIGroupResources(cl discovery.DiscoveryInterface) ([]*APIGroupResources, error) {
apiGroups, err := cl.ServerGroups() gs, rs, err := cl.ServerGroupsAndResources()
if err != nil { if rs == nil || gs == nil {
if apiGroups == nil || len(apiGroups.Groups) == 0 { return nil, err
return nil, err
}
// TODO track the errors and update callers to handle partial errors. // TODO track the errors and update callers to handle partial errors.
} }
rsm := map[string]*metav1.APIResourceList{}
for _, r := range rs {
rsm[r.GroupVersion] = r
}
var result []*APIGroupResources var result []*APIGroupResources
for _, group := range apiGroups.Groups { for _, group := range gs {
groupResources := &APIGroupResources{ groupResources := &APIGroupResources{
Group: group, Group: *group,
VersionedResources: make(map[string][]metav1.APIResource), VersionedResources: make(map[string][]metav1.APIResource),
} }
for _, version := range group.Versions { for _, version := range group.Versions {
resources, err := cl.ServerResourcesForGroupVersion(version.GroupVersion) resources, ok := rsm[version.GroupVersion]
if err != nil { if !ok {
// continue as best we can continue
// TODO track the errors and update callers to handle partial errors.
if resources == nil || len(resources.APIResources) == 0 {
continue
}
} }
groupResources.VersionedResources[version.Version] = resources.APIResources groupResources.VersionedResources[version.Version] = resources.APIResources
} }

View File

@ -17,9 +17,11 @@ limitations under the License.
package restmapper package restmapper
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
"github.com/davecgh/go-spew/spew"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
@ -283,10 +285,146 @@ func TestDeferredDiscoveryRESTMapper_CacheMiss(t *testing.T) {
assert.Equal(cdc.invalidateCalls, 2, "should HAVE called Invalidate() again after another cache-miss, but with fresh==false") assert.Equal(cdc.invalidateCalls, 2, "should HAVE called Invalidate() again after another cache-miss, but with fresh==false")
} }
func TestGetAPIGroupResources(t *testing.T) {
type Test struct {
name string
discovery DiscoveryInterface
expected []*APIGroupResources
expectedError error
}
for _, test := range []Test{
{"nil", &fakeFailingDiscovery{nil, nil, nil, nil}, nil, nil},
{"normal",
&fakeFailingDiscovery{
[]metav1.APIGroup{aGroup, bGroup}, nil,
map[string]*metav1.APIResourceList{"a/v1": &aResources, "b/v1": &bResources}, nil,
},
[]*APIGroupResources{
{aGroup, map[string][]metav1.APIResource{"v1": {aFoo}}},
{bGroup, map[string][]metav1.APIResource{"v1": {bBar}}},
}, nil,
},
{"groups failed, but has fallback with a only",
&fakeFailingDiscovery{
[]metav1.APIGroup{aGroup}, fmt.Errorf("error fetching groups"),
map[string]*metav1.APIResourceList{"a/v1": &aResources, "b/v1": &bResources}, nil,
},
[]*APIGroupResources{
{aGroup, map[string][]metav1.APIResource{"v1": {aFoo}}},
}, nil,
},
{"groups failed, but has no fallback",
&fakeFailingDiscovery{
nil, fmt.Errorf("error fetching groups"),
map[string]*metav1.APIResourceList{"a/v1": &aResources, "b/v1": &bResources}, nil,
},
nil, fmt.Errorf("error fetching groups"),
},
{"a failed, but has fallback",
&fakeFailingDiscovery{
[]metav1.APIGroup{aGroup, bGroup}, nil,
map[string]*metav1.APIResourceList{"a/v1": &aResources, "b/v1": &bResources}, map[string]error{"a/v1": fmt.Errorf("a failed")},
},
[]*APIGroupResources{
{aGroup, map[string][]metav1.APIResource{"v1": {aFoo}}},
{bGroup, map[string][]metav1.APIResource{"v1": {bBar}}},
}, nil, // TODO: do we want this?
},
{"a failed, but has no fallback",
&fakeFailingDiscovery{
[]metav1.APIGroup{aGroup, bGroup}, nil,
map[string]*metav1.APIResourceList{"b/v1": &bResources}, map[string]error{"a/v1": fmt.Errorf("a failed")},
},
[]*APIGroupResources{
{aGroup, map[string][]metav1.APIResource{}},
{bGroup, map[string][]metav1.APIResource{"v1": {bBar}}},
}, nil, // TODO: do we want this?
},
{"a and b failed, but have fallbacks",
&fakeFailingDiscovery{
[]metav1.APIGroup{aGroup, bGroup}, nil,
map[string]*metav1.APIResourceList{"a/v1": &aResources, "b/v1": &bResources}, // TODO: both fallbacks are ignored
map[string]error{"a/v1": fmt.Errorf("a failed"), "b/v1": fmt.Errorf("b failed")},
},
[]*APIGroupResources{
{aGroup, map[string][]metav1.APIResource{"v1": {aFoo}}},
{bGroup, map[string][]metav1.APIResource{"v1": {bBar}}},
}, nil, // TODO: do we want this?
},
} {
t.Run(test.name, func(t *testing.T) {
got, err := GetAPIGroupResources(test.discovery)
if err == nil && test.expectedError != nil {
t.Fatalf("expected error %q, but got none", test.expectedError)
} else if err != nil && test.expectedError == nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(test.expected, got) {
t.Errorf("unexpected result:\nexpected = %s\ngot = %s", spew.Sdump(test.expected), spew.Sdump(got))
}
})
}
}
var _ DiscoveryInterface = &fakeFailingDiscovery{}
type fakeFailingDiscovery struct {
groups []metav1.APIGroup
groupsErr error
resourcesForGroupVersion map[string]*metav1.APIResourceList
resourcesForGroupVersionErr map[string]error
}
func (*fakeFailingDiscovery) RESTClient() restclient.Interface {
return nil
}
func (d *fakeFailingDiscovery) ServerGroups() (*metav1.APIGroupList, error) {
if d.groups == nil && d.groupsErr != nil {
return nil, d.groupsErr
}
return &metav1.APIGroupList{Groups: d.groups}, d.groupsErr
}
func (d *fakeFailingDiscovery) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return ServerGroupsAndResources(d)
}
func (d *fakeFailingDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
if rs, found := d.resourcesForGroupVersion[groupVersion]; found {
return rs, d.resourcesForGroupVersionErr[groupVersion]
}
return nil, fmt.Errorf("not found")
}
func (d *fakeFailingDiscovery) ServerResources() ([]*metav1.APIResourceList, error) {
return ServerResources(d)
}
func (d *fakeFailingDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return ServerPreferredResources(d)
}
func (d *fakeFailingDiscovery) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return ServerPreferredNamespacedResources(d)
}
func (*fakeFailingDiscovery) ServerVersion() (*version.Info, error) {
return &version.Info{}, nil
}
func (*fakeFailingDiscovery) OpenAPISchema() (*openapi_v2.Document, error) {
panic("implement me")
}
type fakeCachedDiscoveryInterface struct { type fakeCachedDiscoveryInterface struct {
invalidateCalls int invalidateCalls int
fresh bool fresh bool
enabledA bool enabledGroupA bool
} }
var _ CachedDiscoveryInterface = &fakeCachedDiscoveryInterface{} var _ CachedDiscoveryInterface = &fakeCachedDiscoveryInterface{}
@ -298,7 +436,7 @@ func (c *fakeCachedDiscoveryInterface) Fresh() bool {
func (c *fakeCachedDiscoveryInterface) Invalidate() { func (c *fakeCachedDiscoveryInterface) Invalidate() {
c.invalidateCalls = c.invalidateCalls + 1 c.invalidateCalls = c.invalidateCalls + 1
c.fresh = true c.fresh = true
c.enabledA = true c.enabledGroupA = true
} }
func (c *fakeCachedDiscoveryInterface) RESTClient() restclient.Interface { func (c *fakeCachedDiscoveryInterface) RESTClient() restclient.Interface {
@ -306,55 +444,33 @@ func (c *fakeCachedDiscoveryInterface) RESTClient() restclient.Interface {
} }
func (c *fakeCachedDiscoveryInterface) ServerGroups() (*metav1.APIGroupList, error) { func (c *fakeCachedDiscoveryInterface) ServerGroups() (*metav1.APIGroupList, error) {
if c.enabledA { if c.enabledGroupA {
return &metav1.APIGroupList{ return &metav1.APIGroupList{
Groups: []metav1.APIGroup{ Groups: []metav1.APIGroup{aGroup},
{
Name: "a",
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: "a/v1",
Version: "v1",
},
},
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "a/v1",
Version: "v1",
},
},
},
}, nil }, nil
} }
return &metav1.APIGroupList{}, nil return &metav1.APIGroupList{}, nil
} }
func (c *fakeCachedDiscoveryInterface) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return ServerGroupsAndResources(c)
}
func (c *fakeCachedDiscoveryInterface) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) { func (c *fakeCachedDiscoveryInterface) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
if c.enabledA && groupVersion == "a/v1" { if c.enabledGroupA && groupVersion == "a/v1" {
return &metav1.APIResourceList{ return &aResources, nil
GroupVersion: "a/v1",
APIResources: []metav1.APIResource{
{
Name: "foo",
Kind: "Foo",
Namespaced: false,
},
},
}, nil
} }
return nil, errors.NewNotFound(schema.GroupResource{}, "") return nil, errors.NewNotFound(schema.GroupResource{}, "")
} }
// Deprecated: use ServerGroupsAndResources instead.
func (c *fakeCachedDiscoveryInterface) ServerResources() ([]*metav1.APIResourceList, error) { func (c *fakeCachedDiscoveryInterface) ServerResources() ([]*metav1.APIResourceList, error) {
if c.enabledA { return ServerResources(c)
av1, _ := c.ServerResourcesForGroupVersion("a/v1")
return []*metav1.APIResourceList{av1}, nil
}
return []*metav1.APIResourceList{}, nil
} }
func (c *fakeCachedDiscoveryInterface) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (c *fakeCachedDiscoveryInterface) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
if c.enabledA { if c.enabledGroupA {
return []*metav1.APIResourceList{ return []*metav1.APIResourceList{
{ {
GroupVersion: "a/v1", GroupVersion: "a/v1",
@ -382,3 +498,50 @@ func (c *fakeCachedDiscoveryInterface) ServerVersion() (*version.Info, error) {
func (c *fakeCachedDiscoveryInterface) OpenAPISchema() (*openapi_v2.Document, error) { func (c *fakeCachedDiscoveryInterface) OpenAPISchema() (*openapi_v2.Document, error) {
return &openapi_v2.Document{}, nil return &openapi_v2.Document{}, nil
} }
var (
aGroup = metav1.APIGroup{
Name: "a",
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: "a/v1",
Version: "v1",
},
},
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "a/v1",
Version: "v1",
},
}
bGroup = metav1.APIGroup{
Name: "b",
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: "b/v1",
Version: "v1",
},
},
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "b/v1",
Version: "v1",
},
}
aResources = metav1.APIResourceList{
GroupVersion: "a/v1",
APIResources: []metav1.APIResource{aFoo},
}
aFoo = metav1.APIResource{
Name: "foo",
Kind: "Foo",
Namespaced: false,
}
bResources = metav1.APIResourceList{
GroupVersion: "b/v1",
APIResources: []metav1.APIResource{bBar},
}
bBar = metav1.APIResource{
Name: "bar",
Kind: "Bar",
Namespaced: true,
}
)

View File

@ -265,11 +265,26 @@ func (c *fakeDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string
return nil, errors.NewNotFound(schema.GroupResource{}, "") return nil, errors.NewNotFound(schema.GroupResource{}, "")
} }
// Deprecated: use ServerGroupsAndResources instead.
func (c *fakeDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) { func (c *fakeDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
if c.serverResourcesHandler != nil { _, rs, err := c.ServerGroupsAndResources()
return c.serverResourcesHandler() return rs, err
}
func (c *fakeDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
sgs, err := c.ServerGroups()
if err != nil {
return nil, nil, err
} }
return []*metav1.APIResourceList{}, nil resultGroups := []*metav1.APIGroup{}
for i := range sgs.Groups {
resultGroups = append(resultGroups, &sgs.Groups[i])
}
if c.serverResourcesHandler != nil {
rs, err := c.serverResourcesHandler()
return resultGroups, rs, err
}
return resultGroups, []*metav1.APIResourceList{}, nil
} }
func (c *fakeDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (c *fakeDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {