1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-07 18:31:13 +00:00

Add unit tests for schemaless CRDs

This commit is contained in:
Eric Promislow
2024-07-29 16:31:39 -07:00
committed by Michael Bolot
parent 72384a606d
commit 8ce0b83be7
2 changed files with 225 additions and 0 deletions

View File

@@ -4,8 +4,10 @@ import (
"bytes"
"fmt"
openapi_v2 "github.com/google/gnostic-models/openapiv2"
"github.com/rancher/wrangler/v3/pkg/yaml"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/kube-openapi/pkg/util/proto"
)
var (
@@ -398,3 +400,114 @@ definitions:
kind: "ConfigMap"
version: "v1"
`
var (
rawSchemalessCRDs = `
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: schemaless.management.cattle.io
spec:
conversion:
strategy: None
group: management.cattle.io
names:
kind: Schemaless
listKind: SchemalessList
plural: schemalese
singular: schemaless
scope: Cluster
versions:
- name: v2
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
rkeConfig:
type: object
nullable: true
x-kubernetes-preserve-unknown-fields: true
served: true
storage: true
`
rawSchemalessModels = `
swagger: "2.0"
info:
title: "Test openapi spec"
version: "v1.0.0"
paths:
/apis/management.cattle.io/v3/globalroles:
get:
description: "get a global role"
responses:
200:
description: "OK"
definitions:
io.cattle.management.v2.schemaless:
description: "this kind has no schema"
type: "object"
properties:
apiVersion:
description: "The APIVersion of this resource"
type: "string"
kind:
description: "The kind"
type: "string"
metadata:
description: "The metadata"
$ref: "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
spec:
description: "The spec for the resource"
type: "object"
required:
- "name"
properties:
name:
description: "The name of the resource"
type: "string"
notRequired:
description: "Some field that isn't required"
type: "boolean"
x-kubernetes-group-version-kind:
- group: "management.cattle.io"
version: "v2"
kind: "Schemaless"
io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta:
description: "Object Metadata"
properties:
annotations:
description: "annotations of the resource"
type: "object"
additionalProperties:
type: "string"
name:
description: "name of the resource"
type: "string"
`
)
func getSchemalessCRDs() ([]*apiextv1.CustomResourceDefinition, error) {
crds, err := yaml.UnmarshalWithJSONDecoder[*apiextv1.CustomResourceDefinition](bytes.NewBuffer([]byte(rawSchemalessCRDs)))
if err != nil {
return nil, fmt.Errorf("unmarshaling raw CRDs: %w", err)
}
for _, crd := range crds {
for _, crdVersion := range crd.Spec.Versions {
crdVersion.Schema = nil
}
}
return crds, err
}
func getSchemalessModels() (proto.Models, error) {
doc, err := openapi_v2.ParseDocument([]byte(rawSchemalessModels))
if err != nil {
return nil, fmt.Errorf("unmarshaling raw models: %w", err)
}
models, err := proto.NewOpenAPIData(doc)
return models, err
}

View File

@@ -196,6 +196,59 @@ func TestRefresh(t *testing.T) {
}
}
func TestRefreshSchemalessCRDs(t *testing.T) {
schemalessModels, err := getSchemalessModels()
require.NoError(t, err)
crds, err := getSchemalessCRDs()
require.NoError(t, err)
for _, crd := range crds {
for _, crdVersion := range crd.Spec.Versions {
crdVersion.Schema = nil
}
}
test := struct {
name string
nilGroups bool
wantModels proto.Models
wantGVKModels map[string]gvkModel
}{
name: "problem - missing schema",
wantModels: schemalessModels,
wantGVKModels: map[string]gvkModel{
"management.cattle.io.schemaless": {
ModelName: "io.cattle.management.v2.schemaless",
},
},
}
t.Run(test.name, func(t *testing.T) {
client, err := buildDefaultServerlessDiscovery()
require.Nil(t, err)
baseSchemas := types.EmptyAPISchemas()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
crdCache := fake.NewMockNonNamespacedCacheInterface[*apiextv1.CustomResourceDefinition](ctrl)
crdCache.EXPECT().List(labels.Everything()).Return(crds, nil).AnyTimes()
handler := NewSchemaDefinitionHandler(baseSchemas, crdCache, client)
err = handler.Refresh()
require.NoError(t, err)
handler.lock.RLock()
defer handler.lock.RUnlock()
require.Equal(t, test.wantModels, handler.models)
// Just test the model names, because the schema and crd will be null in the input
require.Equal(t, len(test.wantGVKModels), len(handler.gvkModels))
for k, v := range test.wantGVKModels {
require.Equal(t, v.ModelName, handler.gvkModels[k].ModelName)
}
})
}
func Test_byID(t *testing.T) {
discoveryClient, err := buildDefaultDiscovery()
require.NoError(t, err)
@@ -735,6 +788,65 @@ func buildDefaultDiscovery() (*fakeDiscovery, error) {
}, nil
}
func buildDefaultServerlessDiscovery() (*fakeDiscovery, error) {
document, err := openapi_v2.ParseDocument([]byte(rawSchemalessModels))
if err != nil {
return nil, fmt.Errorf("unable to parse rawSchemalessModels: %w", err)
}
groups := []metav1.APIGroup{
// The core groups (eg: Pods, ConfigMaps, etc)
{
Name: "",
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "v1",
Version: "v1",
},
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: "v1",
Version: "v1",
},
},
},
{
Name: "management.cattle.io",
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "management.cattle.io/v2",
Version: "v2",
},
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: "management.cattle.io/v1",
Version: "v1",
},
{
GroupVersion: "management.cattle.io/v2",
Version: "v2",
},
},
},
{
Name: "noversion.cattle.io",
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: "noversion.cattle.io/v1",
Version: "v1",
},
{
GroupVersion: "noversion.cattle.io/v2",
Version: "v2",
},
},
},
}
return &fakeDiscovery{
Groups: &metav1.APIGroupList{
Groups: groups,
},
Document: document,
}, nil
}
type fakeDiscovery struct {
Groups *metav1.APIGroupList
Document *openapi_v2.Document