Skip to content

Commit c0594d2

Browse files
committed
apiextensions: builder for OpenAPI v3
1 parent e710fb7 commit c0594d2

File tree

6 files changed

+110
-6
lines changed

6 files changed

+110
-6
lines changed

staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ go_library(
66
"complete.go",
77
"convert.go",
88
"goopenapi.go",
9+
"skeleton.go",
910
"structural.go",
1011
"unfold.go",
1112
"validation.go",
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package schema
18+
19+
// StripDefaults returns a copy without defaults.
20+
func (s *Structural) StripDefaults() *Structural {
21+
s = s.DeepCopy()
22+
v := Visitor{
23+
Structural: func(s *Structural) bool {
24+
changed := false
25+
if s.Default.Object != nil {
26+
s.Default.Object = nil
27+
changed = true
28+
}
29+
return changed
30+
},
31+
}
32+
v.Visit(s)
33+
return s
34+
}
35+
36+
// StripValueValidations returns a copy without value validations.
37+
func (s *Structural) StripValueValidations() *Structural {
38+
s = s.DeepCopy()
39+
v := Visitor{
40+
Structural: func(s *Structural) bool {
41+
changed := false
42+
if s.ValueValidation != nil {
43+
s.ValueValidation = nil
44+
changed = true
45+
}
46+
return changed
47+
},
48+
}
49+
v.Visit(s)
50+
return s
51+
}

staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder/builder.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,20 @@ var definitions map[string]common.OpenAPIDefinition
6565
var buildDefinitions sync.Once
6666
var namer *openapi.DefinitionNamer
6767

68+
// Options contains builder options.
69+
type Options struct {
70+
// Convert to OpenAPI v2.
71+
V2 bool
72+
73+
// Strip defaults.
74+
StripDefaults bool
75+
76+
// Strip value validation.
77+
StripValueValidation bool
78+
}
79+
6880
// BuildSwagger builds swagger for the given crd in the given version
69-
func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string) (*spec.Swagger, error) {
81+
func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string, opts Options) (*spec.Swagger, error) {
7082
var schema *structuralschema.Structural
7183
s, err := apiextensions.GetSchemaForVersion(crd, version)
7284
if err != nil {
@@ -76,7 +88,17 @@ func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string) (
7688
if s != nil && s.OpenAPIV3Schema != nil {
7789
if !validation.SchemaHasInvalidTypes(s.OpenAPIV3Schema) {
7890
if ss, err := structuralschema.NewStructural(s.OpenAPIV3Schema); err == nil {
79-
schema = ss.Unfold()
91+
// skip non-structural schemas
92+
schema = ss
93+
94+
if opts.StripDefaults {
95+
schema = schema.StripDefaults()
96+
}
97+
if opts.StripValueValidation {
98+
schema = schema.StripValueValidations()
99+
}
100+
101+
schema = schema.Unfold()
80102
}
81103
}
82104
}
@@ -85,7 +107,7 @@ func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string) (
85107
// comes from function registerResourceHandlers() in k8s.io/apiserver.
86108
// Alternatives are either (ideally) refactoring registerResourceHandlers() to
87109
// reuse the code, or faking an APIInstaller for CR to feed to registerResourceHandlers().
88-
b := newBuilder(crd, version, schema, true)
110+
b := newBuilder(crd, version, schema, opts.V2)
89111

90112
// Sample response types for building web service
91113
sample := &CRDCanonicalTypeNamer{

staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder/builder_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ func TestCRDRouteParameterBuilder(t *testing.T) {
485485
},
486486
},
487487
}
488-
swagger, err := BuildSwagger(testNamespacedCRD, testCRDVersion)
488+
swagger, err := BuildSwagger(testNamespacedCRD, testCRDVersion, Options{V2: true, StripDefaults: true})
489489
require.NoError(t, err)
490490
require.Equal(t, len(testCase.paths), len(swagger.Paths.Paths), testCase.scope)
491491
for path, expected := range testCase.paths {
@@ -557,21 +557,49 @@ func TestBuildSwagger(t *testing.T) {
557557
name string
558558
schema string
559559
wantedSchema string
560+
opts Options
560561
}{
561562
{
562563
"nil",
563564
"",
564565
`{"type":"object","x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
566+
Options{V2: true, StripDefaults: true},
565567
},
566568
{
567569
"with properties",
568570
`{"type":"object","properties":{"spec":{"type":"object"},"status":{"type":"object"}}}`,
569571
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"spec":{"type":"object"},"status":{"type":"object"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
572+
Options{V2: true, StripDefaults: true},
570573
},
571574
{
572575
"with invalid-typed properties",
573576
`{"type":"object","properties":{"spec":{"type":"bug"},"status":{"type":"object"}}}`,
574577
`{"type":"object","x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
578+
Options{V2: true, StripDefaults: true},
579+
},
580+
{
581+
"with stripped defaults",
582+
`{"type":"object","properties":{"foo":{"type":"string","default":"bar"}}}`,
583+
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"foo":{"type":"string"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
584+
Options{V2: true, StripDefaults: true},
585+
},
586+
{
587+
"with stripped defaults",
588+
`{"type":"object","properties":{"foo":{"type":"string","default":"bar"}}}`,
589+
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"foo":{"type":"string"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
590+
Options{V2: true, StripDefaults: true},
591+
},
592+
{
593+
"v2",
594+
`{"type":"object","properties":{"foo":{"type":"string","oneOf":[{"pattern":"a"},{"pattern":"b"}]}}}`,
595+
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"foo":{"type":"string"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
596+
Options{V2: true, StripDefaults: true},
597+
},
598+
{
599+
"v3",
600+
`{"type":"object","properties":{"foo":{"type":"string","oneOf":[{"pattern":"a"},{"pattern":"b"}]}}}`,
601+
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"foo":{"type":"string","oneOf":[{"pattern":"a"},{"pattern":"b"}]}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
602+
Options{V2: false, StripDefaults: true},
575603
},
576604
}
577605
for _, tt := range tests {
@@ -603,7 +631,7 @@ func TestBuildSwagger(t *testing.T) {
603631
Scope: apiextensions.NamespaceScoped,
604632
Validation: validation,
605633
},
606-
}, "v1")
634+
}, "v1", tt.opts)
607635
if err != nil {
608636
t.Fatal(err)
609637
}

staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func buildVersionSpecs(crd *apiextensions.CustomResourceDefinition, oldSpecs map
193193
if !v.Served {
194194
continue
195195
}
196-
spec, err := builder.BuildSwagger(crd, v.Name)
196+
spec, err := builder.BuildSwagger(crd, v.Name, builder.Options{V2: true, StripDefaults: true})
197197
if err != nil {
198198
return nil, false, err
199199
}

vendor/modules.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,8 @@ k8s.io/apiextensions-apiserver/pkg/controller/establish
11631163
k8s.io/apiextensions-apiserver/pkg/controller/finalizer
11641164
k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema
11651165
k8s.io/apiextensions-apiserver/pkg/controller/openapi
1166+
k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder
1167+
k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2
11661168
k8s.io/apiextensions-apiserver/pkg/controller/status
11671169
k8s.io/apiextensions-apiserver/pkg/crdserverscheme
11681170
k8s.io/apiextensions-apiserver/pkg/features

0 commit comments

Comments
 (0)