Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
bc86aeba
Commit
bc86aeba
authored
May 07, 2019
by
Dr. Stefan Schimanski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
apiextensions: switch OpenAPI pubilshing to structural schema
parent
6acd9215
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
347 additions
and
126 deletions
+347
-126
builder.go
...apiextensions-apiserver/pkg/controller/openapi/builder.go
+20
-13
builder_test.go
...tensions-apiserver/pkg/controller/openapi/builder_test.go
+285
-26
conversion.go
...extensions-apiserver/pkg/controller/openapi/conversion.go
+41
-87
conversion_test.go
...sions-apiserver/pkg/controller/openapi/conversion_test.go
+0
-0
crd_test.go
test/integration/master/crd_test.go
+1
-0
No files found.
staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder.go
View file @
bc86aeba
...
@@ -26,6 +26,7 @@ import (
...
@@ -26,6 +26,7 @@ import (
"github.com/go-openapi/spec"
"github.com/go-openapi/spec"
v1
"k8s.io/api/autoscaling/v1"
v1
"k8s.io/api/autoscaling/v1"
structuralschema
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
metav1beta1
"k8s.io/apimachinery/pkg/apis/meta/v1beta1"
metav1beta1
"k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime"
...
@@ -58,22 +59,24 @@ var namer *openapi.DefinitionNamer
...
@@ -58,22 +59,24 @@ var namer *openapi.DefinitionNamer
// BuildSwagger builds swagger for the given crd in the given version
// BuildSwagger builds swagger for the given crd in the given version
func
BuildSwagger
(
crd
*
apiextensions
.
CustomResourceDefinition
,
version
string
)
(
*
spec
.
Swagger
,
error
)
{
func
BuildSwagger
(
crd
*
apiextensions
.
CustomResourceDefinition
,
version
string
)
(
*
spec
.
Swagger
,
error
)
{
var
schema
*
s
pec
.
Schema
var
schema
*
s
tructuralschema
.
Structural
s
,
err
:=
apiextensions
.
GetSchemaForVersion
(
crd
,
version
)
s
,
err
:=
apiextensions
.
GetSchemaForVersion
(
crd
,
version
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
if
s
!=
nil
&&
s
.
OpenAPIV3Schema
!=
nil
{
if
s
!=
nil
&&
s
.
OpenAPIV3Schema
!=
nil
{
schema
,
err
=
ConvertJSONSchemaPropsToOpenAPIv2Schema
(
s
.
OpenAPIV3Schema
)
ss
,
err
:=
structuralschema
.
NewStructural
(
s
.
OpenAPIV3Schema
)
if
err
!=
nil
{
if
err
==
nil
&&
len
(
structuralschema
.
ValidateStructural
(
ss
,
nil
))
==
0
{
return
nil
,
err
// skip non-structural schemas
schema
=
ss
.
Unfold
()
}
}
}
}
// TODO(roycaihw): remove the WebService templating below. The following logic
// TODO(roycaihw): remove the WebService templating below. The following logic
// comes from function registerResourceHandlers() in k8s.io/apiserver.
// comes from function registerResourceHandlers() in k8s.io/apiserver.
// Alternatives are either (ideally) refactoring registerResourceHandlers() to
// Alternatives are either (ideally) refactoring registerResourceHandlers() to
// reuse the code, or faking an APIInstaller for CR to feed to registerResourceHandlers().
// reuse the code, or faking an APIInstaller for CR to feed to registerResourceHandlers().
b
:=
newBuilder
(
crd
,
version
,
schema
)
b
:=
newBuilder
(
crd
,
version
,
schema
,
true
)
// Sample response types for building web service
// Sample response types for building web service
sample
:=
&
CRDCanonicalTypeNamer
{
sample
:=
&
CRDCanonicalTypeNamer
{
...
@@ -288,23 +291,27 @@ func (b *builder) buildRoute(root, path, action, verb string, sample interface{}
...
@@ -288,23 +291,27 @@ func (b *builder) buildRoute(root, path, action, verb string, sample interface{}
// buildKubeNative builds input schema with Kubernetes' native object meta, type meta and
// buildKubeNative builds input schema with Kubernetes' native object meta, type meta and
// extensions
// extensions
func
(
b
*
builder
)
buildKubeNative
(
schema
*
s
pec
.
Schema
)
*
spec
.
Schema
{
func
(
b
*
builder
)
buildKubeNative
(
schema
*
s
tructuralschema
.
Structural
,
v2
bool
)
(
ret
*
spec
.
Schema
)
{
// only add properties if we have a schema. Otherwise, kubectl would (wrongly) assume additionalProperties=false
// only add properties if we have a schema. Otherwise, kubectl would (wrongly) assume additionalProperties=false
// and forbid anything outside of apiVersion, kind and metadata. We have to fix kubectl to stop doing this, e.g. by
// 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.
// adding additionalProperties=true support to explicitly allow additional fields.
// TODO: fix kubectl to understand additionalProperties=true
// TODO: fix kubectl to understand additionalProperties=true
if
schema
==
nil
{
if
schema
==
nil
{
schema
=
&
spec
.
Schema
{
ret
=
&
spec
.
Schema
{
SchemaProps
:
spec
.
SchemaProps
{
Type
:
[]
string
{
"object"
}},
SchemaProps
:
spec
.
SchemaProps
{
Type
:
[]
string
{
"object"
}},
}
}
// no, we cannot add more properties here, not even TypeMeta/ObjectMeta because kubectl will complain about
// no, we cannot add more properties here, not even TypeMeta/ObjectMeta because kubectl will complain about
// unknown fields for anything else.
// unknown fields for anything else.
}
else
{
}
else
{
schema
.
SetProperty
(
"metadata"
,
*
spec
.
RefSchema
(
objectMetaSchemaRef
)
.
if
v2
{
schema
=
ToStructuralOpenAPIV2
(
schema
)
}
ret
=
schema
.
ToGoOpenAPI
()
ret
.
SetProperty
(
"metadata"
,
*
spec
.
RefSchema
(
objectMetaSchemaRef
)
.
WithDescription
(
swaggerPartialObjectMetadataDescriptions
[
"metadata"
]))
WithDescription
(
swaggerPartialObjectMetadataDescriptions
[
"metadata"
]))
addTypeMetaProperties
(
schema
)
addTypeMetaProperties
(
ret
)
}
}
schema
.
AddExtension
(
endpoints
.
ROUTE_META_GVK
,
[]
interface
{}{
ret
.
AddExtension
(
endpoints
.
ROUTE_META_GVK
,
[]
interface
{}{
map
[
string
]
interface
{}{
map
[
string
]
interface
{}{
"group"
:
b
.
group
,
"group"
:
b
.
group
,
"version"
:
b
.
version
,
"version"
:
b
.
version
,
...
@@ -312,7 +319,7 @@ func (b *builder) buildKubeNative(schema *spec.Schema) *spec.Schema {
...
@@ -312,7 +319,7 @@ func (b *builder) buildKubeNative(schema *spec.Schema) *spec.Schema {
},
},
})
})
return
schema
return
ret
}
}
// getDefinition gets definition for given Kubernetes type. This function is extracted from
// getDefinition gets definition for given Kubernetes type. This function is extracted from
...
@@ -391,7 +398,7 @@ func (b *builder) getOpenAPIConfig() *common.Config {
...
@@ -391,7 +398,7 @@ func (b *builder) getOpenAPIConfig() *common.Config {
}
}
}
}
func
newBuilder
(
crd
*
apiextensions
.
CustomResourceDefinition
,
version
string
,
schema
*
s
pec
.
Schema
)
*
builder
{
func
newBuilder
(
crd
*
apiextensions
.
CustomResourceDefinition
,
version
string
,
schema
*
s
tructuralschema
.
Structural
,
v2
bool
)
*
builder
{
b
:=
&
builder
{
b
:=
&
builder
{
schema
:
&
spec
.
Schema
{
schema
:
&
spec
.
Schema
{
SchemaProps
:
spec
.
SchemaProps
{
Type
:
[]
string
{
"object"
}},
SchemaProps
:
spec
.
SchemaProps
{
Type
:
[]
string
{
"object"
}},
...
@@ -410,7 +417,7 @@ func newBuilder(crd *apiextensions.CustomResourceDefinition, version string, sch
...
@@ -410,7 +417,7 @@ func newBuilder(crd *apiextensions.CustomResourceDefinition, version string, sch
}
}
// Pre-build schema with Kubernetes native properties
// Pre-build schema with Kubernetes native properties
b
.
schema
=
b
.
buildKubeNative
(
schema
)
b
.
schema
=
b
.
buildKubeNative
(
schema
,
v2
)
b
.
listSchema
=
b
.
buildListSchema
()
b
.
listSchema
=
b
.
buildListSchema
()
return
b
return
b
...
...
staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder_test.go
View file @
bc86aeba
This diff is collapsed.
Click to expand it.
staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/conversion.go
View file @
bc86aeba
...
@@ -17,106 +17,60 @@ limitations under the License.
...
@@ -17,106 +17,60 @@ limitations under the License.
package
openapi
package
openapi
import
(
import
(
"strings"
structuralschema
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
"github.com/go-openapi/spec"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
)
)
//
ConvertJSONSchemaPropsToOpenAPIv2Schema converts our internal OpenAPI v3 schema
//
ToStructuralOpenAPIV2 converts our internal OpenAPI v3 structural schema to
//
(*apiextensions.JSONSchemaProps) to an OpenAPI v2 schema (*spec.Schema)
.
//
to a v2 compatible schema
.
func
ConvertJSONSchemaPropsToOpenAPIv2Schema
(
in
*
apiextensions
.
JSONSchemaProps
)
(
*
spec
.
Schema
,
error
)
{
func
ToStructuralOpenAPIV2
(
in
*
structuralschema
.
Structural
)
*
structuralschema
.
Structural
{
if
in
==
nil
{
if
in
==
nil
{
return
nil
,
nil
return
nil
}
}
// dirty hack to temporarily set the type at the root. See continuation at the func bottom.
out
:=
in
.
DeepCopy
()
// TODO: remove for Kubernetes 1.15
oldRootType
:=
in
.
Type
if
len
(
in
.
Type
)
==
0
{
in
.
Type
=
"object"
}
// Remove unsupported fields in OpenAPI v2 recursively
// Remove unsupported fields in OpenAPI v2 recursively
out
:=
new
(
spec
.
Schema
)
mapper
:=
structuralschema
.
Visitor
{
validation
.
ConvertJSONSchemaPropsWithPostProcess
(
in
,
out
,
func
(
p
*
spec
.
Schema
)
error
{
Structural
:
func
(
s
*
structuralschema
.
Structural
)
bool
{
p
.
OneOf
=
nil
changed
:=
false
// TODO(roycaihw): preserve cases where we only have one subtree in AnyOf, same for OneOf
if
s
.
ValueValidation
!=
nil
{
p
.
AnyOf
=
nil
if
s
.
ValueValidation
.
AllOf
!=
nil
{
p
.
Not
=
nil
s
.
ValueValidation
.
AllOf
=
nil
changed
=
true
// TODO: drop everything below in 1.15 when we have passed one version skew towards kube-openapi in <1.14, which rejects valid openapi schemata
}
if
s
.
ValueValidation
.
OneOf
!=
nil
{
if
p
.
Ref
.
String
()
!=
""
{
s
.
ValueValidation
.
OneOf
=
nil
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R95
changed
=
true
p
.
Properties
=
nil
}
if
s
.
ValueValidation
.
AnyOf
!=
nil
{
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R99
s
.
ValueValidation
.
AnyOf
=
nil
p
.
Type
=
nil
changed
=
true
}
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R104
if
s
.
ValueValidation
.
Not
!=
nil
{
if
!
strings
.
HasPrefix
(
p
.
Ref
.
String
(),
"#/definitions/"
)
{
s
.
ValueValidation
.
Not
=
nil
p
.
Ref
=
spec
.
Ref
{}
changed
=
true
}
}
switch
{
case
len
(
p
.
Type
)
==
2
&&
(
p
.
Type
[
0
]
==
"null"
||
p
.
Type
[
1
]
==
"null"
)
:
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-ce77fea74b9dd098045004410023e0c3R219
p
.
Type
=
nil
case
len
(
p
.
Type
)
==
1
:
switch
p
.
Type
[
0
]
{
case
"null"
:
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-ce77fea74b9dd098045004410023e0c3R219
p
.
Type
=
nil
case
"array"
:
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R183
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R184
if
p
.
Items
==
nil
||
(
p
.
Items
.
Schema
==
nil
&&
len
(
p
.
Items
.
Schemas
)
!=
1
)
{
p
.
Type
=
nil
p
.
Items
=
nil
}
}
}
}
case
len
(
p
.
Type
)
>
1
:
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R272
// We also set Properties to null to enforce parseArbitrary at https://github.com/kubernetes/kube-openapi/blob/814a8073653e40e0e324205d093770d4e7bb811f/pkg/util/proto/document.go#L247
p
.
Type
=
nil
p
.
Properties
=
nil
default
:
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R248
p
.
Properties
=
nil
}
// normalize items
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-ce77fea74b9dd098045004410023e0c3R219
if
p
.
Items
!=
nil
&&
len
(
p
.
Items
.
Schemas
)
==
1
{
if
s
.
Nullable
{
p
.
Items
=
&
spec
.
SchemaOrArray
{
Schema
:
&
p
.
Items
.
Schemas
[
0
]}
s
.
Type
=
""
}
s
.
Nullable
=
false
// general fixups not supported by gnostic
// untyped values break if items or properties are set in kubectl
p
.
ID
=
""
// https://github.com/kubernetes/kube-openapi/pull/143/files#diff-62afddb578e9db18fb32ffb6b7802d92R183
p
.
Schema
=
""
s
.
Items
=
nil
p
.
Definitions
=
nil
s
.
Properties
=
nil
p
.
AdditionalItems
=
nil
p
.
Dependencies
=
nil
p
.
PatternProperties
=
nil
if
p
.
ExternalDocs
!=
nil
&&
len
(
p
.
ExternalDocs
.
URL
)
==
0
{
p
.
ExternalDocs
=
nil
}
if
p
.
Items
!=
nil
&&
p
.
Items
.
Schemas
!=
nil
{
p
.
Items
=
nil
}
return
nil
changed
=
true
})
}
// restore root level type in input, and remove it in output if we had added it
return
changed
// TODO: remove with Kubernetes 1.15
},
in
.
Type
=
oldRootType
// we drop all junctors above, and hence, never reach nested value validations
if
len
(
oldRootType
)
==
0
{
NestedValueValidation
:
nil
,
out
.
Type
=
nil
}
}
mapper
.
Visit
(
out
)
return
out
,
nil
return
out
}
}
staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/conversion_test.go
View file @
bc86aeba
This diff is collapsed.
Click to expand it.
test/integration/master/crd_test.go
View file @
bc86aeba
...
@@ -160,6 +160,7 @@ func TestCRDOpenAPI(t *testing.T) {
...
@@ -160,6 +160,7 @@ func TestCRDOpenAPI(t *testing.T) {
},
},
Validation
:
&
apiextensionsv1beta1
.
CustomResourceValidation
{
Validation
:
&
apiextensionsv1beta1
.
CustomResourceValidation
{
OpenAPIV3Schema
:
&
apiextensionsv1beta1
.
JSONSchemaProps
{
OpenAPIV3Schema
:
&
apiextensionsv1beta1
.
JSONSchemaProps
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensionsv1beta1
.
JSONSchemaProps
{
Properties
:
map
[
string
]
apiextensionsv1beta1
.
JSONSchemaProps
{
"foo"
:
{
Type
:
"string"
},
"foo"
:
{
Type
:
"string"
},
},
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment