mirror of
https://github.com/rancher/steve.git
synced 2025-04-27 02:51:10 +00:00
420 lines
13 KiB
Go
420 lines
13 KiB
Go
package converter
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
openapiv2 "github.com/google/gnostic-models/openapiv2"
|
|
"github.com/rancher/apiserver/pkg/types"
|
|
wranglerSchema "github.com/rancher/wrangler/v3/pkg/schemas"
|
|
"github.com/stretchr/testify/assert"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/apimachinery/pkg/version"
|
|
"k8s.io/client-go/discovery"
|
|
"k8s.io/client-go/openapi"
|
|
restclient "k8s.io/client-go/rest"
|
|
)
|
|
|
|
func TestAddDiscovery(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
discoveryErr error
|
|
groups []schema.GroupVersion
|
|
groupVersionOverride bool
|
|
resources map[schema.GroupVersion][]metav1.APIResource
|
|
wantError bool
|
|
desiredSchema map[string]*types.APISchema
|
|
}{
|
|
{
|
|
name: "basic test case, one schema",
|
|
groups: []schema.GroupVersion{{Group: "TestGroup", Version: "v1"}},
|
|
resources: map[schema.GroupVersion][]metav1.APIResource{
|
|
{Group: "TestGroup", Version: "v1"}: {
|
|
{
|
|
|
|
Name: "testResources",
|
|
SingularName: "testResource",
|
|
Kind: "TestResource",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
},
|
|
wantError: false,
|
|
desiredSchema: map[string]*types.APISchema{
|
|
"testgroup.v1.testresource": {
|
|
Schema: &wranglerSchema.Schema{
|
|
ID: "testgroup.v1.testresource",
|
|
PluralName: "TestGroup.v1.testResources",
|
|
Attributes: map[string]interface{}{
|
|
"group": "TestGroup",
|
|
"version": "v1",
|
|
"kind": "TestResource",
|
|
"resource": "testResources",
|
|
"verbs": []string{"get"},
|
|
"namespaced": true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "discovery error but still got some information",
|
|
discoveryErr: &discovery.ErrGroupDiscoveryFailed{Groups: map[schema.GroupVersion]error{
|
|
schema.GroupVersion{
|
|
Group: "NotFound",
|
|
Version: "v1",
|
|
}: fmt.Errorf("group Not found"),
|
|
},
|
|
},
|
|
groups: []schema.GroupVersion{{Group: "TestGroup", Version: "v1"}},
|
|
resources: map[schema.GroupVersion][]metav1.APIResource{
|
|
{Group: "TestGroup", Version: "v1"}: {
|
|
{
|
|
|
|
Name: "testResources",
|
|
SingularName: "testResource",
|
|
Kind: "TestResource",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
},
|
|
wantError: false,
|
|
desiredSchema: map[string]*types.APISchema{
|
|
"testgroup.v1.testresource": {
|
|
Schema: &wranglerSchema.Schema{
|
|
ID: "testgroup.v1.testresource",
|
|
PluralName: "TestGroup.v1.testResources",
|
|
Attributes: map[string]interface{}{
|
|
"group": "TestGroup",
|
|
"version": "v1",
|
|
"kind": "TestResource",
|
|
"resource": "testResources",
|
|
"verbs": []string{"get"},
|
|
"namespaced": true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "discovery error, not partial",
|
|
discoveryErr: fmt.Errorf("cluster unavailable"),
|
|
groups: []schema.GroupVersion{{Group: "TestGroup", Version: "v1"}},
|
|
resources: map[schema.GroupVersion][]metav1.APIResource{
|
|
{Group: "TestGroup", Version: "v1"}: {
|
|
{
|
|
Name: "testResources",
|
|
SingularName: "testResource",
|
|
Kind: "TestResource",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
},
|
|
wantError: true,
|
|
desiredSchema: map[string]*types.APISchema{},
|
|
},
|
|
{
|
|
name: "bad group version",
|
|
groups: []schema.GroupVersion{{Group: "Invalid/Group", Version: "v2"}},
|
|
resources: map[schema.GroupVersion][]metav1.APIResource{
|
|
{Group: "Invalid/Group", Version: "v2"}: {
|
|
{
|
|
Name: "testResources",
|
|
SingularName: "testResource",
|
|
Kind: "TestResource",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
},
|
|
wantError: true,
|
|
desiredSchema: map[string]*types.APISchema{
|
|
"core..testresource": {
|
|
Schema: &wranglerSchema.Schema{
|
|
ID: "core..testresource",
|
|
PluralName: "core..testResources",
|
|
Attributes: map[string]interface{}{
|
|
"group": "",
|
|
"version": "",
|
|
"kind": "TestResource",
|
|
"resource": "testResources",
|
|
"verbs": []string{"get"},
|
|
"namespaced": true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "override groups and versions",
|
|
groups: []schema.GroupVersion{{Group: "autoscaling", Version: "v1"}, {Group: "extensions", Version: "v1"}},
|
|
groupVersionOverride: true,
|
|
resources: map[schema.GroupVersion][]metav1.APIResource{
|
|
{Group: "autoscaling", Version: "v1"}: {
|
|
{
|
|
Name: "testAutoscalings",
|
|
SingularName: "testAutoscaling",
|
|
Kind: "TestAutoscaling",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
{Group: "extensions", Version: "v1"}: {
|
|
{
|
|
Name: "testExtensions",
|
|
SingularName: "testExtension",
|
|
Kind: "TestExtension",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
},
|
|
wantError: false,
|
|
desiredSchema: map[string]*types.APISchema{
|
|
"autoscaling.v1.testautoscaling": {
|
|
Schema: &wranglerSchema.Schema{
|
|
ID: "autoscaling.v1.testautoscaling",
|
|
PluralName: "autoscaling.v1.testAutoscalings",
|
|
Attributes: map[string]interface{}{
|
|
"group": "autoscaling",
|
|
"version": "v1",
|
|
"kind": "TestAutoscaling",
|
|
"resource": "testAutoscalings",
|
|
"verbs": []string{"get"},
|
|
"namespaced": true,
|
|
"preferredVersion": "v2beta2",
|
|
},
|
|
},
|
|
},
|
|
"extensions.v1.testextension": {
|
|
Schema: &wranglerSchema.Schema{
|
|
ID: "extensions.v1.testextension",
|
|
PluralName: "extensions.v1.testExtensions",
|
|
Attributes: map[string]interface{}{
|
|
"group": "extensions",
|
|
"version": "v1",
|
|
"kind": "TestExtension",
|
|
"resource": "testExtensions",
|
|
"verbs": []string{"get"},
|
|
"namespaced": true,
|
|
"preferredGroup": "apps",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "eligible for override, but override version not found",
|
|
groups: []schema.GroupVersion{{Group: "autoscaling", Version: "v1"}},
|
|
groupVersionOverride: false,
|
|
resources: map[schema.GroupVersion][]metav1.APIResource{
|
|
{Group: "autoscaling", Version: "v1"}: {
|
|
{
|
|
Name: "testAutoscalings",
|
|
SingularName: "testAutoscaling",
|
|
Kind: "TestAutoscaling",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
},
|
|
wantError: false,
|
|
desiredSchema: map[string]*types.APISchema{
|
|
"autoscaling.v1.testautoscaling": {
|
|
Schema: &wranglerSchema.Schema{
|
|
ID: "autoscaling.v1.testautoscaling",
|
|
PluralName: "autoscaling.v1.testAutoscalings",
|
|
Attributes: map[string]interface{}{
|
|
"group": "autoscaling",
|
|
"version": "v1",
|
|
"kind": "TestAutoscaling",
|
|
"resource": "testAutoscalings",
|
|
"verbs": []string{"get"},
|
|
"namespaced": true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "skip resource with / silently",
|
|
groups: []schema.GroupVersion{{Group: "TestGroup", Version: "v1"}},
|
|
resources: map[schema.GroupVersion][]metav1.APIResource{
|
|
{Group: "TestGroup", Version: "v1"}: {
|
|
{
|
|
Name: "test/Resources",
|
|
SingularName: "test/Resource",
|
|
Kind: "Test/Resource",
|
|
Namespaced: true,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
{
|
|
Name: "testResources",
|
|
SingularName: "testResource",
|
|
Kind: "TestResource",
|
|
Namespaced: false,
|
|
Verbs: metav1.Verbs{"get"},
|
|
},
|
|
},
|
|
},
|
|
wantError: false,
|
|
desiredSchema: map[string]*types.APISchema{
|
|
"testgroup.v1.testresource": {
|
|
Schema: &wranglerSchema.Schema{
|
|
ID: "testgroup.v1.testresource",
|
|
PluralName: "TestGroup.v1.testResources",
|
|
Attributes: map[string]interface{}{
|
|
"group": "TestGroup",
|
|
"version": "v1",
|
|
"kind": "TestResource",
|
|
"resource": "testResources",
|
|
"verbs": []string{"get"},
|
|
"namespaced": false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
testDiscovery := fakeDiscovery{}
|
|
for _, gvr := range test.groups {
|
|
gvr := gvr
|
|
testDiscovery.AddGroup(gvr.Group, gvr.Version, test.groupVersionOverride)
|
|
}
|
|
for gvr, resourceSlice := range test.resources {
|
|
for _, resource := range resourceSlice {
|
|
resource := resource
|
|
testDiscovery.AddResource(gvr.Group, gvr.Version, resource)
|
|
}
|
|
}
|
|
testDiscovery.GroupResourcesErr = test.discoveryErr
|
|
schemas := map[string]*types.APISchema{}
|
|
err := addDiscovery(&testDiscovery, schemas)
|
|
if test.wantError {
|
|
assert.Error(t, err, "expected an error but did not get one")
|
|
} else {
|
|
assert.NoError(t, err, "got an error but did not expect one")
|
|
}
|
|
assert.Equal(t, test.desiredSchema, schemas, "schemas were not as expected")
|
|
})
|
|
}
|
|
}
|
|
|
|
type fakeDiscovery struct {
|
|
Groups []*metav1.APIGroup
|
|
Resources []*metav1.APIResourceList
|
|
Document *openapiv2.Document
|
|
GroupResourcesErr error
|
|
DocumentErr error
|
|
}
|
|
|
|
// ServerGroupsAndResources is the only method we actually need for the test - just returns what is on the struct
|
|
func (f *fakeDiscovery) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
|
|
return f.Groups, f.Resources, f.GroupResourcesErr
|
|
}
|
|
|
|
func (f *fakeDiscovery) AddGroup(groupName string, preferredVersion string, includeOverrideVersion bool) {
|
|
if f.Groups == nil {
|
|
f.Groups = []*metav1.APIGroup{}
|
|
}
|
|
groupVersion := fmt.Sprintf("%s/%s", groupName, preferredVersion)
|
|
found := -1
|
|
for i := range f.Groups {
|
|
if f.Groups[i].Name == groupName {
|
|
found = i
|
|
}
|
|
}
|
|
group := metav1.APIGroup{
|
|
Name: groupName,
|
|
PreferredVersion: metav1.GroupVersionForDiscovery{
|
|
GroupVersion: groupVersion,
|
|
Version: preferredVersion,
|
|
},
|
|
Versions: []metav1.GroupVersionForDiscovery{
|
|
{
|
|
GroupVersion: groupVersion,
|
|
Version: preferredVersion,
|
|
},
|
|
},
|
|
}
|
|
|
|
// if we should include override versions in list of versions, figure out if we have an override and add it
|
|
if includeOverrideVersion {
|
|
if override, ok := preferredVersionOverride[groupVersion]; ok {
|
|
group.Versions = append(group.Versions, metav1.GroupVersionForDiscovery{
|
|
GroupVersion: fmt.Sprintf("%s/%s", groupName, override),
|
|
Version: override,
|
|
})
|
|
}
|
|
}
|
|
if found >= 0 {
|
|
f.Groups[found] = &group
|
|
} else {
|
|
f.Groups = append(f.Groups, &group)
|
|
}
|
|
}
|
|
|
|
func (f *fakeDiscovery) AddResource(group, version string, resource metav1.APIResource) {
|
|
if f.Resources == nil {
|
|
f.Resources = []*metav1.APIResourceList{}
|
|
}
|
|
groupVersion := fmt.Sprintf("%s/%s", group, version)
|
|
found := -1
|
|
// first, find the APIResourceList for our group
|
|
for i := range f.Resources {
|
|
if f.Resources[i].GroupVersion == groupVersion {
|
|
found = i
|
|
}
|
|
}
|
|
|
|
if found >= 0 {
|
|
currentResourceList := f.Resources[found]
|
|
resourceFound := -1
|
|
// next, find the APIResource for our resource
|
|
for i := range currentResourceList.APIResources {
|
|
if currentResourceList.APIResources[i].Name == resource.Name {
|
|
resourceFound = i
|
|
}
|
|
}
|
|
if resourceFound >= 0 {
|
|
currentResourceList.APIResources[resourceFound] = resource
|
|
} else {
|
|
currentResourceList.APIResources = append(currentResourceList.APIResources, resource)
|
|
}
|
|
f.Resources[found] = currentResourceList
|
|
} else {
|
|
currentResourceList := &metav1.APIResourceList{
|
|
GroupVersion: groupVersion,
|
|
APIResources: []metav1.APIResource{resource},
|
|
}
|
|
f.Resources = append(f.Resources, currentResourceList)
|
|
}
|
|
}
|
|
|
|
// The rest of these methods are just here to conform to discovery.DiscoveryInterface
|
|
func (f *fakeDiscovery) RESTClient() restclient.Interface { return nil }
|
|
func (f *fakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) { return nil, nil }
|
|
func (f *fakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
|
|
return nil, nil
|
|
}
|
|
func (f *fakeDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
|
return nil, nil
|
|
}
|
|
func (f *fakeDiscovery) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
|
|
return nil, nil
|
|
}
|
|
func (f *fakeDiscovery) ServerVersion() (*version.Info, error) { return nil, nil }
|
|
func (f *fakeDiscovery) OpenAPISchema() (*openapiv2.Document, error) {
|
|
return f.Document, f.DocumentErr
|
|
}
|
|
func (f *fakeDiscovery) OpenAPIV3() openapi.Client { return nil }
|
|
func (f *fakeDiscovery) WithLegacy() discovery.DiscoveryInterface { return f }
|