stop using util.ToRESTFriendlyName in favor of declared model names

Signed-off-by: Joe Betz <jpbetz@google.com>
This commit is contained in:
Joe Betz
2025-09-10 15:52:59 -04:00
parent 0dd2056045
commit 3fc59ee9a7
4 changed files with 46 additions and 27 deletions

View File

@@ -22,7 +22,6 @@ import (
"os"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/util"
"k8s.io/kube-openapi/pkg/validation/spec"
"k8s.io/kubernetes/pkg/generated/openapi"
)
@@ -38,7 +37,7 @@ func main() {
func output() error {
refFunc := func(name string) spec.Ref {
return spec.MustCreateRef(fmt.Sprintf("#/definitions/%s", util.ToRESTFriendlyName(name)))
return spec.MustCreateRef(fmt.Sprintf("#/definitions/%s", name))
}
defs := openapi.GetOpenAPIDefinitions(refFunc)
schemaDefs := make(map[string]spec.Schema, len(defs))
@@ -50,12 +49,12 @@ func output() error {
// the type.
if schema, ok := v.Schema.Extensions[common.ExtensionV2Schema]; ok {
if v2Schema, isOpenAPISchema := schema.(spec.Schema); isOpenAPISchema {
schemaDefs[util.ToRESTFriendlyName(k)] = v2Schema
schemaDefs[k] = v2Schema
continue
}
}
schemaDefs[util.ToRESTFriendlyName(k)] = v.Schema
schemaDefs[k] = v.Schema
}
data, err := json.Marshal(&spec.Swagger{
SwaggerProps: spec.SwaggerProps{

View File

@@ -19,6 +19,7 @@ package builder
import (
"fmt"
"net/http"
"slices"
"strings"
"sync"
@@ -26,6 +27,7 @@ import (
"golang.org/x/text/cases"
"golang.org/x/text/language"
v1 "k8s.io/api/autoscaling/v1"
apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers"
apiextensionsinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
@@ -57,8 +59,8 @@ const (
objectMetaSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
listMetaSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta"
typeMetaType = "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta"
objectMetaType = "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"
typeMetaType = "io.k8s.apimachinery.pkg.apis.meta.v1.TypeMeta"
objectMetaType = "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
definitionPrefix = "#/definitions/"
v3DefinitionPrefix = "#/components/schemas/"
@@ -225,7 +227,7 @@ type CRDCanonicalTypeNamer struct {
// OpenAPICanonicalTypeName returns canonical type name for given CRD
func (c *CRDCanonicalTypeNamer) OpenAPICanonicalTypeName() string {
return fmt.Sprintf("%s/%s.%s", c.group, c.version, c.kind)
return gvkToModelName(c.group, c.version, c.kind)
}
// builder contains validation schema and basic naming information for a CRD in
@@ -383,7 +385,11 @@ func (b *builder) buildKubeNative(crd *apiextensionsv1.CustomResourceDefinition,
// and forbid anything outside of apiVersion, kind and metadata. We have to fix kubectl to stop doing this, e.g. by
// adding additionalProperties=true support to explicitly allow additional fields.
// TODO: fix kubectl to understand additionalProperties=true
if schema == nil || (opts.V2 && (schema.XPreserveUnknownFields || crdPreserveUnknownFields)) {
if schema == nil {
ret = &spec.Schema{
SchemaProps: spec.SchemaProps{Type: []string{"object"}},
}
} else if opts.V2 && (schema.XPreserveUnknownFields || crdPreserveUnknownFields) {
ret = &spec.Schema{
SchemaProps: spec.SchemaProps{Type: []string{"object"}},
}
@@ -497,7 +503,7 @@ func addTypeMetaProperties(s *spec.Schema, v2 bool) {
// buildListSchema builds the list kind schema for the CRD
func (b *builder) buildListSchema(crd *apiextensionsv1.CustomResourceDefinition, opts Options) *spec.Schema {
name := definitionPrefix + util.ToRESTFriendlyName(fmt.Sprintf("%s/%s/%s", b.group, b.version, b.kind))
name := definitionPrefix + gvkToModelName(b.group, b.version, b.kind)
doc := fmt.Sprintf("List of %s. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md", b.plural)
s := new(spec.Schema).
Typed("object", "").
@@ -522,6 +528,13 @@ func (b *builder) buildListSchema(crd *apiextensionsv1.CustomResourceDefinition,
return s
}
func gvkToModelName(g, v, k string) string {
groupParts := strings.Split(g, ".")
slices.Reverse(groupParts)
g = strings.Join(groupParts, ".")
return fmt.Sprintf("%s.%s.%s", g, v, k)
}
// getOpenAPIConfig builds config which wires up generated definitions for kube-openapi to consume
func (b *builder) getOpenAPIConfig() *common.Config {
return &common.Config{
@@ -546,11 +559,11 @@ func (b *builder) getOpenAPIConfig() *common.Config {
},
GetDefinitions: func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
def := utilopenapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(generatedopenapi.GetOpenAPIDefinitions)(ref)
def[fmt.Sprintf("%s/%s.%s", b.group, b.version, b.kind)] = common.OpenAPIDefinition{
def[gvkToModelName(b.group, b.version, b.kind)] = common.OpenAPIDefinition{
Schema: *b.schema,
Dependencies: []string{objectMetaType},
}
def[fmt.Sprintf("%s/%s.%s", b.group, b.version, b.listKind)] = common.OpenAPIDefinition{
def[gvkToModelName(b.group, b.version, b.listKind)] = common.OpenAPIDefinition{
Schema: *b.listSchema,
}
return def
@@ -580,11 +593,11 @@ func (b *builder) getOpenAPIV3Config() *common.OpenAPIV3Config {
},
GetDefinitions: func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
def := utilopenapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(generatedopenapi.GetOpenAPIDefinitions)(ref)
def[fmt.Sprintf("%s/%s.%s", b.group, b.version, b.kind)] = common.OpenAPIDefinition{
def[gvkToModelName(b.group, b.version, b.kind)] = common.OpenAPIDefinition{
Schema: *b.schema,
Dependencies: []string{objectMetaType},
}
def[fmt.Sprintf("%s/%s.%s", b.group, b.version, b.listKind)] = common.OpenAPIDefinition{
def[gvkToModelName(b.group, b.version, b.listKind)] = common.OpenAPIDefinition{
Schema: *b.listSchema,
}
return def

View File

@@ -29,6 +29,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kube-openapi/pkg/util"
)
// Scheme defines methods for serializing and deserializing API objects, a type
@@ -752,6 +753,9 @@ var internalPackages = []string{"k8s.io/apimachinery/pkg/runtime/scheme.go"}
// The OpenAPI definition name is the canonical name of the type, with the group and version removed.
// For example, the OpenAPI definition name of Pod is `io.k8s.api.core.v1.Pod`.
//
// This respects the util.OpenAPIModelNamer interface and will return the name returned by
// OpenAPIModelName() if it is defined on the type.
//
// A known type that is registered as an unstructured.Unstructured type is treated as a custom resource and
// which has an OpenAPI definition name of the form `<reversed-group>.<version.<kind>`.
// For example, the OpenAPI definition name of `group: stable.example.com, version: v1, kind: Pod` is
@@ -764,6 +768,12 @@ func (s *Scheme) ToOpenAPIDefinitionName(groupVersionKind schema.GroupVersionKin
if err != nil {
return "", err
}
// Use a namer if provided
if namer, ok := example.(util.OpenAPIModelNamer); ok {
return namer.OpenAPIModelName(), nil
}
if _, ok := example.(Unstructured); ok {
if groupVersionKind.Group == "" || groupVersionKind.Kind == "" {
return "", fmt.Errorf("unable to convert GroupVersionKind with empty fields to unstructured type to an OpenAPI definition name: %v", groupVersionKind)

View File

@@ -19,7 +19,6 @@ package openapi
import (
"bytes"
"fmt"
"reflect"
"sort"
"strings"
"unicode"
@@ -29,6 +28,7 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
"k8s.io/kube-openapi/pkg/util"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -133,31 +133,28 @@ func gvkConvert(gvk schema.GroupVersionKind) v1.GroupVersionKind {
}
}
func typeName(t reflect.Type) string {
path := t.PkgPath()
if strings.Contains(path, "/vendor/") {
path = path[strings.Index(path, "/vendor/")+len("/vendor/"):]
}
return fmt.Sprintf("%s.%s", path, t.Name())
}
// NewDefinitionNamer constructs a new DefinitionNamer to be used to customize OpenAPI spec.
func NewDefinitionNamer(schemes ...*runtime.Scheme) *DefinitionNamer {
ret := &DefinitionNamer{
typeGroupVersionKinds: map[string]groupVersionKinds{},
}
for _, s := range schemes {
for gvk, rtype := range s.AllKnownTypes() {
for gvk := range s.AllKnownTypes() {
newGVK := gvkConvert(gvk)
exists := false
for _, existingGVK := range ret.typeGroupVersionKinds[typeName(rtype)] {
name, err := s.ToOpenAPIDefinitionName(gvk)
if err != nil {
klog.Fatalf("failed to get OpenAPI definition name for %v: %v", gvk, err)
continue
}
for _, existingGVK := range ret.typeGroupVersionKinds[name] {
if newGVK == existingGVK {
exists = true
break
}
}
if !exists {
ret.typeGroupVersionKinds[typeName(rtype)] = append(ret.typeGroupVersionKinds[typeName(rtype)], newGVK)
ret.typeGroupVersionKinds[name] = append(ret.typeGroupVersionKinds[name], newGVK)
}
}
}
@@ -170,9 +167,9 @@ func NewDefinitionNamer(schemes ...*runtime.Scheme) *DefinitionNamer {
// GetDefinitionName returns the name and tags for a given definition
func (d *DefinitionNamer) GetDefinitionName(name string) (string, spec.Extensions) {
if groupVersionKinds, ok := d.typeGroupVersionKinds[name]; ok {
return util.ToRESTFriendlyName(name), spec.Extensions{
return name, spec.Extensions{
extensionGVK: groupVersionKinds.JSON(),
}
}
return util.ToRESTFriendlyName(name), nil
return name, nil
}