Skip to content

Commit 4181556

Browse files
authored
Merge pull request kubernetes#81480 from sttts/sttts-openapi-builder-v3
apiextensions: builder for v3 schemas
2 parents 2af52db + c0594d2 commit 4181556

File tree

12 files changed

+242
-69
lines changed

12 files changed

+242
-69
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+
}
Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,25 @@
1-
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
22

33
go_library(
44
name = "go_default_library",
5-
srcs = [
6-
"aggregator.go",
7-
"builder.go",
8-
"controller.go",
9-
"conversion.go",
10-
],
5+
srcs = ["controller.go"],
116
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/controller/openapi",
127
importpath = "k8s.io/apiextensions-apiserver/pkg/controller/openapi",
138
visibility = ["//visibility:public"],
149
deps = [
15-
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
1610
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
17-
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation:go_default_library",
18-
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
1911
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library",
2012
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:go_default_library",
21-
"//staging/src/k8s.io/apiextensions-apiserver/pkg/generated/openapi:go_default_library",
13+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder:go_default_library",
2214
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
23-
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
24-
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
2515
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
26-
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
27-
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
2816
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
29-
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
3017
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
31-
"//staging/src/k8s.io/apiserver/pkg/endpoints:go_default_library",
32-
"//staging/src/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
33-
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
34-
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
3518
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
3619
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
37-
"//vendor/github.com/emicklei/go-restful:go_default_library",
3820
"//vendor/github.com/go-openapi/spec:go_default_library",
3921
"//vendor/k8s.io/klog:go_default_library",
40-
"//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library",
41-
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
4222
"//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library",
43-
"//vendor/k8s.io/kube-openapi/pkg/util:go_default_library",
44-
],
45-
)
46-
47-
go_test(
48-
name = "go_default_test",
49-
srcs = [
50-
"builder_test.go",
51-
"conversion_test.go",
52-
],
53-
embed = [":go_default_library"],
54-
deps = [
55-
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
56-
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
57-
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
58-
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
59-
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
60-
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
61-
"//staging/src/k8s.io/apiserver/pkg/endpoints:go_default_library",
62-
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
63-
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
64-
"//vendor/github.com/go-openapi/spec:go_default_library",
65-
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
66-
"//vendor/github.com/google/gofuzz:go_default_library",
67-
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
68-
"//vendor/github.com/googleapis/gnostic/compiler:go_default_library",
69-
"//vendor/github.com/stretchr/testify/assert:go_default_library",
70-
"//vendor/github.com/stretchr/testify/require:go_default_library",
71-
"//vendor/gopkg.in/yaml.v2:go_default_library",
72-
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
7323
],
7424
)
7525

@@ -82,7 +32,11 @@ filegroup(
8232

8333
filegroup(
8434
name = "all-srcs",
85-
srcs = [":package-srcs"],
35+
srcs = [
36+
":package-srcs",
37+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder:all-srcs",
38+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2:all-srcs",
39+
],
8640
tags = ["automanaged"],
8741
visibility = ["//visibility:public"],
8842
)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = [
6+
"builder.go",
7+
"merge.go",
8+
],
9+
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder",
10+
importpath = "k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder",
11+
visibility = ["//visibility:public"],
12+
deps = [
13+
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
14+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
15+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation:go_default_library",
16+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
17+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2:go_default_library",
18+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/generated/openapi:go_default_library",
19+
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
20+
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
21+
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
22+
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
23+
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
24+
"//staging/src/k8s.io/apiserver/pkg/endpoints:go_default_library",
25+
"//staging/src/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
26+
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
27+
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
28+
"//vendor/github.com/emicklei/go-restful:go_default_library",
29+
"//vendor/github.com/go-openapi/spec:go_default_library",
30+
"//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library",
31+
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
32+
"//vendor/k8s.io/kube-openapi/pkg/util:go_default_library",
33+
],
34+
)
35+
36+
go_test(
37+
name = "go_default_test",
38+
srcs = ["builder_test.go"],
39+
embed = [":go_default_library"],
40+
deps = [
41+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
42+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
43+
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
44+
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
45+
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
46+
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
47+
"//staging/src/k8s.io/apiserver/pkg/endpoints:go_default_library",
48+
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
49+
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
50+
"//vendor/github.com/go-openapi/spec:go_default_library",
51+
"//vendor/github.com/stretchr/testify/assert:go_default_library",
52+
"//vendor/github.com/stretchr/testify/require:go_default_library",
53+
],
54+
)
55+
56+
filegroup(
57+
name = "package-srcs",
58+
srcs = glob(["**"]),
59+
tags = ["automanaged"],
60+
visibility = ["//visibility:private"],
61+
)
62+
63+
filegroup(
64+
name = "all-srcs",
65+
srcs = [":package-srcs"],
66+
tags = ["automanaged"],
67+
visibility = ["//visibility:public"],
68+
)

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

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package openapi
17+
package builder
1818

1919
import (
2020
"fmt"
@@ -29,6 +29,7 @@ import (
2929
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
3030
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
3131
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
32+
openapiv2 "k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2"
3233
generatedopenapi "k8s.io/apiextensions-apiserver/pkg/generated/openapi"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
@@ -64,8 +65,20 @@ var definitions map[string]common.OpenAPIDefinition
6465
var buildDefinitions sync.Once
6566
var namer *openapi.DefinitionNamer
6667

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+
6780
// BuildSwagger builds swagger for the given crd in the given version
68-
func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string) (*spec.Swagger, error) {
81+
func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string, opts Options) (*spec.Swagger, error) {
6982
var schema *structuralschema.Structural
7083
s, err := apiextensions.GetSchemaForVersion(crd, version)
7184
if err != nil {
@@ -75,7 +88,17 @@ func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string) (
7588
if s != nil && s.OpenAPIV3Schema != nil {
7689
if !validation.SchemaHasInvalidTypes(s.OpenAPIV3Schema) {
7790
if ss, err := structuralschema.NewStructural(s.OpenAPIV3Schema); err == nil {
78-
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()
79102
}
80103
}
81104
}
@@ -84,7 +107,7 @@ func BuildSwagger(crd *apiextensions.CustomResourceDefinition, version string) (
84107
// comes from function registerResourceHandlers() in k8s.io/apiserver.
85108
// Alternatives are either (ideally) refactoring registerResourceHandlers() to
86109
// reuse the code, or faking an APIInstaller for CR to feed to registerResourceHandlers().
87-
b := newBuilder(crd, version, schema, true)
110+
b := newBuilder(crd, version, schema, opts.V2)
88111

89112
// Sample response types for building web service
90113
sample := &CRDCanonicalTypeNamer{
@@ -324,7 +347,7 @@ func (b *builder) buildKubeNative(schema *structuralschema.Structural, v2 bool)
324347
// unknown fields for anything else.
325348
} else {
326349
if v2 {
327-
schema = ToStructuralOpenAPIV2(schema)
350+
schema = openapiv2.ToStructuralOpenAPIV2(schema)
328351
}
329352
ret = schema.ToGoOpenAPI()
330353
ret.SetProperty("metadata", *spec.RefSchema(objectMetaSchemaRef).

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

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package openapi
17+
package builder
1818

1919
import (
2020
"reflect"
@@ -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/aggregator.go renamed to staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder/merge.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package openapi
17+
package builder
1818

1919
import (
2020
"github.com/go-openapi/spec"
2121
)
2222

23-
// mergeSpecs aggregates all OpenAPI specs, reusing the metadata of the first, static spec as the basis.
24-
func mergeSpecs(staticSpec *spec.Swagger, crdSpecs ...*spec.Swagger) *spec.Swagger {
23+
// MergeSpecs aggregates all OpenAPI specs, reusing the metadata of the first, static spec as the basis.
24+
// Later paths and definitions override earlier ones. None of the input is mutated, but input
25+
// and output share data structures.
26+
func MergeSpecs(staticSpec *spec.Swagger, crdSpecs ...*spec.Swagger) *spec.Swagger {
2527
// create shallow copy of staticSpec, but replace paths and definitions because we modify them.
2628
specToReturn := *staticSpec
2729
if staticSpec.Definitions != nil {

0 commit comments

Comments
 (0)