mirror of
https://github.com/rancher/steve.git
synced 2025-09-04 08:55:55 +00:00
Adding changes from code review
This commit is contained in:
@@ -4,10 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
openapi_v2 "github.com/google/gnostic-models/openapiv2"
|
|
||||||
"github.com/rancher/wrangler/v3/pkg/yaml"
|
"github.com/rancher/wrangler/v3/pkg/yaml"
|
||||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/kube-openapi/pkg/util/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -65,6 +63,26 @@ spec:
|
|||||||
nullable: true
|
nullable: true
|
||||||
served: true
|
served: true
|
||||||
storage: true
|
storage: true
|
||||||
|
---
|
||||||
|
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
|
||||||
|
preserveUnkownFields: true
|
||||||
|
versions:
|
||||||
|
- name: v2
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -348,6 +366,35 @@ definitions:
|
|||||||
- group: "management.cattle.io"
|
- group: "management.cattle.io"
|
||||||
version: "v2"
|
version: "v2"
|
||||||
kind: "Nullable"
|
kind: "Nullable"
|
||||||
|
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.cattle.management.NotAKind:
|
io.cattle.management.NotAKind:
|
||||||
type: "string"
|
type: "string"
|
||||||
description: "Some string which isn't a kind"
|
description: "Some string which isn't a kind"
|
||||||
@@ -400,114 +447,3 @@ definitions:
|
|||||||
kind: "ConfigMap"
|
kind: "ConfigMap"
|
||||||
version: "v1"
|
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
|
|
||||||
}
|
|
||||||
|
@@ -225,8 +225,6 @@ func listGVKModels(models proto.Models, groups *metav1.APIGroupList, crdCache wa
|
|||||||
}
|
}
|
||||||
if version.Schema != nil {
|
if version.Schema != nil {
|
||||||
gvkToCRD[gvk] = version.Schema.OpenAPIV3Schema
|
gvkToCRD[gvk] = version.Schema.OpenAPIV3Schema
|
||||||
} else {
|
|
||||||
gvkToCRD[gvk] = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ func TestRefresh(t *testing.T) {
|
|||||||
require.NotNil(t, userAttributesV2)
|
require.NotNil(t, userAttributesV2)
|
||||||
|
|
||||||
nullableV2 := getJSONSchema(crds, "nullable.management.cattle.io", "v2")
|
nullableV2 := getJSONSchema(crds, "nullable.management.cattle.io", "v2")
|
||||||
require.NotNil(t, userAttributesV2)
|
require.NotNil(t, nullableV2)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -85,6 +85,11 @@ func TestRefresh(t *testing.T) {
|
|||||||
Schema: defaultModels.LookupModel("io.cattle.management.v2.Nullable"),
|
Schema: defaultModels.LookupModel("io.cattle.management.v2.Nullable"),
|
||||||
CRD: nullableV2,
|
CRD: nullableV2,
|
||||||
},
|
},
|
||||||
|
"management.cattle.io.schemaless": {
|
||||||
|
ModelName: "io.cattle.management.v2.Schemaless",
|
||||||
|
Schema: defaultModels.LookupModel("io.cattle.management.v2.Schemaless"),
|
||||||
|
CRD: nil,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -147,6 +152,11 @@ func TestRefresh(t *testing.T) {
|
|||||||
Schema: defaultModels.LookupModel("io.cattle.management.v2.Nullable"),
|
Schema: defaultModels.LookupModel("io.cattle.management.v2.Nullable"),
|
||||||
CRD: nullableV2,
|
CRD: nullableV2,
|
||||||
},
|
},
|
||||||
|
"management.cattle.io.schemaless": {
|
||||||
|
ModelName: "io.cattle.management.v2.Schemaless",
|
||||||
|
Schema: defaultModels.LookupModel("io.cattle.management.v2.Schemaless"),
|
||||||
|
CRD: nil,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -196,59 +206,6 @@ 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) {
|
func Test_byID(t *testing.T) {
|
||||||
discoveryClient, err := buildDefaultDiscovery()
|
discoveryClient, err := buildDefaultDiscovery()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -788,65 +745,6 @@ func buildDefaultDiscovery() (*fakeDiscovery, error) {
|
|||||||
}, nil
|
}, 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 {
|
type fakeDiscovery struct {
|
||||||
Groups *metav1.APIGroupList
|
Groups *metav1.APIGroupList
|
||||||
Document *openapi_v2.Document
|
Document *openapi_v2.Document
|
||||||
|
Reference in New Issue
Block a user