Skip to content

Commit 207f0c7

Browse files
committed
Wrap Refs with AllOf
1 parent 9f9c01d commit 207f0c7

File tree

3 files changed

+111
-7
lines changed

3 files changed

+111
-7
lines changed

pkg/builder3/openapi.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"net/http"
23+
"reflect"
2324
"strings"
2425

2526
restful "github.com/emicklei/go-restful"
2627

2728
"k8s.io/kube-openapi/pkg/common"
2829
"k8s.io/kube-openapi/pkg/common/restfuladapter"
30+
"k8s.io/kube-openapi/pkg/schemamutation"
2931
"k8s.io/kube-openapi/pkg/spec3"
3032
"k8s.io/kube-openapi/pkg/util"
3133
"k8s.io/kube-openapi/pkg/validation/spec"
@@ -396,6 +398,7 @@ func (o *openAPI) buildDefinitionRecursively(name string) error {
396398
}
397399
// delete the embedded v2 schema if exists, otherwise no-op
398400
delete(schema.VendorExtensible.Extensions, common.ExtensionV2Schema)
401+
schema = wrapRefs(schema)
399402
o.spec.Components.Schemas[uniqueName] = schema
400403
for _, v := range item.Dependencies {
401404
if err := o.buildDefinitionRecursively(v); err != nil {
@@ -436,3 +439,30 @@ func (o *openAPI) toSchema(name string) (_ *spec.Schema, err error) {
436439
}, nil
437440
}
438441
}
442+
443+
// wrapRefs wraps OpenAPI V3 Schema refs that contain sibling elements.
444+
// AllOf is used to wrap the Ref to prevent references from having sibling elements
445+
// Please see https://github.com/kubernetes/kubernetes/issues/106387#issuecomment-967640388
446+
func wrapRefs(schema *spec.Schema) *spec.Schema {
447+
walker := schemamutation.Walker{
448+
SchemaCallback: func(schema *spec.Schema) *spec.Schema {
449+
orig := schema
450+
clone := func() {
451+
if orig == schema {
452+
schema = new(spec.Schema)
453+
*schema = *orig
454+
}
455+
}
456+
if schema.Ref.String() != "" && !reflect.DeepEqual(*schema, spec.Schema{SchemaProps: spec.SchemaProps{Ref: schema.Ref}}) {
457+
clone()
458+
refSchema := new(spec.Schema)
459+
refSchema.Ref = schema.Ref
460+
schema.Ref = spec.Ref{}
461+
schema.AllOf = []spec.Schema{*refSchema}
462+
}
463+
return schema
464+
},
465+
RefCallback: schemamutation.RefCallbackNoop,
466+
}
467+
return walker.WalkSchema(schema)
468+
}

pkg/builder3/openapi_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,28 @@ func (_ TestInput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
8989
},
9090
},
9191
},
92+
"reference-extension": {
93+
VendorExtensible: spec.VendorExtensible{
94+
Extensions: map[string]interface{}{"extension": "value"},
95+
},
96+
SchemaProps: spec.SchemaProps{
97+
Ref: spec.MustCreateRef("/components/schemas/builder3.TestOutput"),
98+
},
99+
},
100+
"reference-nullable": {
101+
SchemaProps: spec.SchemaProps{
102+
Ref: spec.MustCreateRef("/components/schemas/builder3.TestOutput"),
103+
Nullable: true,
104+
},
105+
},
106+
"reference-default": {
107+
SchemaProps: spec.SchemaProps{
108+
Ref: spec.MustCreateRef("/components/schemas/builder3.TestOutput"),
109+
Default: map[string]interface{}{},
110+
},
111+
},
112+
113+
92114
}
93115
schema.Extensions = spec.Extensions{"x-test": "test"}
94116
def := openapi.EmbedOpenAPIDefinitionIntoV2Extension(openapi.OpenAPIDefinition{
@@ -338,6 +360,38 @@ func getTestInputDefinition() *spec.Schema {
338360
},
339361
},
340362
},
363+
"reference-extension": {
364+
VendorExtensible: spec.VendorExtensible{
365+
Extensions: map[string]interface{}{"extension": "value"},
366+
},
367+
SchemaProps: spec.SchemaProps{
368+
AllOf: []spec.Schema{{
369+
SchemaProps: spec.SchemaProps{
370+
Ref: spec.MustCreateRef("/components/schemas/builder3.TestOutput"),
371+
},
372+
}},
373+
},
374+
},
375+
"reference-nullable": {
376+
SchemaProps: spec.SchemaProps{
377+
Nullable: true,
378+
AllOf: []spec.Schema{{
379+
SchemaProps: spec.SchemaProps{
380+
Ref: spec.MustCreateRef("/components/schemas/builder3.TestOutput"),
381+
},
382+
}},
383+
},
384+
},
385+
"reference-default": {
386+
SchemaProps: spec.SchemaProps{
387+
AllOf: []spec.Schema{{
388+
SchemaProps: spec.SchemaProps{
389+
Ref: spec.MustCreateRef("/components/schemas/builder3.TestOutput"),
390+
},
391+
}},
392+
Default: map[string]interface{}{},
393+
},
394+
},
341395
},
342396
},
343397
VendorExtensible: spec.VendorExtensible{

test/integration/testdata/golden.v3.json

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -514,14 +514,22 @@
514514
},
515515
"OtherSub": {
516516
"default": {},
517-
"$ref": "#/components/schemas/defaults.SubStruct"
517+
"allOf": [
518+
{
519+
"$ref": "#/components/schemas/defaults.SubStruct"
520+
}
521+
]
518522
},
519523
"Sub": {
520524
"default": {
521525
"i": 5,
522526
"s": "foo"
523527
},
524-
"$ref": "#/components/schemas/defaults.SubStruct"
528+
"allOf": [
529+
{
530+
"$ref": "#/components/schemas/defaults.SubStruct"
531+
}
532+
]
525533
}
526534
}
527535
},
@@ -688,7 +696,11 @@
688696
"type": "array",
689697
"items": {
690698
"default": {},
691-
"$ref": "#/components/schemas/listtype.Item"
699+
"allOf": [
700+
{
701+
"$ref": "#/components/schemas/listtype.Item"
702+
}
703+
]
692704
},
693705
"x-kubernetes-list-map-keys": [
694706
"port"
@@ -754,8 +766,12 @@
754766
"properties": {
755767
"Field": {
756768
"default": {},
757-
"x-kubernetes-map-type": "atomic",
758-
"$ref": "#/components/schemas/structtype.ContainedStruct"
769+
"allOf": [
770+
{
771+
"$ref": "#/components/schemas/structtype.ContainedStruct"
772+
}
773+
],
774+
"x-kubernetes-map-type": "atomic"
759775
},
760776
"OtherField": {
761777
"type": "integer",
@@ -790,8 +806,12 @@
790806
"properties": {
791807
"Field": {
792808
"default": {},
793-
"x-kubernetes-map-type": "granular",
794-
"$ref": "#/components/schemas/structtype.ContainedStruct"
809+
"allOf": [
810+
{
811+
"$ref": "#/components/schemas/structtype.ContainedStruct"
812+
}
813+
],
814+
"x-kubernetes-map-type": "granular"
795815
},
796816
"OtherField": {
797817
"type": "integer",

0 commit comments

Comments
 (0)