Run update-vendor.sh
This commit is contained in:
3
vendor/k8s.io/kube-openapi/cmd/openapi-gen/args/args.go
generated
vendored
3
vendor/k8s.io/kube-openapi/cmd/openapi-gen/args/args.go
generated
vendored
@@ -18,6 +18,7 @@ package args
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/gengo/args"
|
||||
@@ -38,6 +39,8 @@ func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
|
||||
// WithoutDefaultFlagParsing() disables implicit addition of command line flags and parsing,
|
||||
// which allows registering custom arguments afterwards
|
||||
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
||||
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), "k8s.io/kube-openapi/boilerplate/boilerplate.go.txt")
|
||||
|
||||
customArgs := &CustomArgs{}
|
||||
genericArgs.CustomArgs = customArgs
|
||||
|
||||
|
113
vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go
generated
vendored
113
vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go
generated
vendored
@@ -26,6 +26,8 @@ import (
|
||||
"k8s.io/kube-openapi/pkg/util"
|
||||
)
|
||||
|
||||
const gvkKey = "x-kubernetes-group-version-kind"
|
||||
|
||||
// usedDefinitionForSpec returns a map with all used definitions in the provided spec as keys and true as values.
|
||||
func usedDefinitionForSpec(root *spec.Swagger) map[string]bool {
|
||||
usedDefinitions := map[string]bool{}
|
||||
@@ -152,7 +154,7 @@ func MergeSpecs(dest, source *spec.Swagger) error {
|
||||
return mergeSpecs(dest, source, true, false)
|
||||
}
|
||||
|
||||
// mergeSpecs merged source into dest while resolving conflicts.
|
||||
// mergeSpecs merges source into dest while resolving conflicts.
|
||||
// The source is not mutated.
|
||||
func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConflicts bool) (err error) {
|
||||
// Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
|
||||
@@ -186,7 +188,7 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf
|
||||
conflicts := false
|
||||
for k, v := range source.Definitions {
|
||||
v2, found := dest.Definitions[k]
|
||||
if found && !reflect.DeepEqual(v, v2) {
|
||||
if found && !deepEqualDefinitionsModuloGVKs(&v2, &v) {
|
||||
if !renameModelConflicts {
|
||||
return fmt.Errorf("model name conflict in merging OpenAPI spec: %s", k)
|
||||
}
|
||||
@@ -207,7 +209,12 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf
|
||||
if usedNames[k] {
|
||||
v2, found := dest.Definitions[k]
|
||||
// Reuse model if they are exactly the same.
|
||||
if found && reflect.DeepEqual(v, v2) {
|
||||
if found && deepEqualDefinitionsModuloGVKs(&v2, &v) {
|
||||
if gvks, found, err := mergedGVKs(&v2, &v); err != nil {
|
||||
return err
|
||||
} else if found {
|
||||
v2.Extensions[gvkKey] = gvks
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -218,8 +225,13 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf
|
||||
i++
|
||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||
v2, found = dest.Definitions[newName]
|
||||
if found && reflect.DeepEqual(v, v2) {
|
||||
if found && deepEqualDefinitionsModuloGVKs(&v2, &v) {
|
||||
renames[k] = newName
|
||||
if gvks, found, err := mergedGVKs(&v2, &v); err != nil {
|
||||
return err
|
||||
} else if found {
|
||||
v2.Extensions[gvkKey] = gvks
|
||||
}
|
||||
continue OUTERLOOP
|
||||
}
|
||||
}
|
||||
@@ -257,3 +269,96 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deepEqualDefinitionsModuloGVKs compares s1 and s2, but ignores the x-kubernetes-group-version-kind extension.
|
||||
func deepEqualDefinitionsModuloGVKs(s1, s2 *spec.Schema) bool {
|
||||
if s1 == nil {
|
||||
return s2 == nil
|
||||
} else if s2 == nil {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(s1.Extensions, s2.Extensions) {
|
||||
for k, v := range s1.Extensions {
|
||||
if k == gvkKey {
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(v, s2.Extensions[k]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
len1 := len(s1.Extensions)
|
||||
len2 := len(s2.Extensions)
|
||||
if _, found := s1.Extensions[gvkKey]; found {
|
||||
len1--
|
||||
}
|
||||
if _, found := s2.Extensions[gvkKey]; found {
|
||||
len2--
|
||||
}
|
||||
if len1 != len2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if s1.Extensions != nil {
|
||||
shallowCopy := *s1
|
||||
s1 = &shallowCopy
|
||||
s1.Extensions = nil
|
||||
}
|
||||
if s2.Extensions != nil {
|
||||
shallowCopy := *s2
|
||||
s2 = &shallowCopy
|
||||
s2.Extensions = nil
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(s1, s2)
|
||||
}
|
||||
|
||||
// mergedGVKs merges the x-kubernetes-group-version-kind slices and returns the result, and whether
|
||||
// s1's x-kubernetes-group-version-kind slice was changed at all.
|
||||
func mergedGVKs(s1, s2 *spec.Schema) (interface{}, bool, error) {
|
||||
gvk1, found1 := s1.Extensions[gvkKey]
|
||||
gvk2, found2 := s2.Extensions[gvkKey]
|
||||
|
||||
if !found1 {
|
||||
return gvk2, found2, nil
|
||||
}
|
||||
if !found2 {
|
||||
return gvk1, false, nil
|
||||
}
|
||||
|
||||
slice1, ok := gvk1.([]interface{})
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("expected slice of GroupVersionKinds, got: %+v", slice1)
|
||||
}
|
||||
slice2, ok := gvk2.([]interface{})
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("expected slice of GroupVersionKinds, got: %+v", slice2)
|
||||
}
|
||||
|
||||
ret := make([]interface{}, len(slice1), len(slice1)+len(slice2))
|
||||
copy(ret, slice1)
|
||||
seen := make(map[string]bool, len(slice1))
|
||||
for _, x := range slice1 {
|
||||
gvk, ok := x.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf(`expected {"group": <group>, "kind": <kind>, "version": <version>}, got: %#v`, x)
|
||||
}
|
||||
k := fmt.Sprintf("%s/%s.%s", gvk["group"], gvk["version"], gvk["kind"])
|
||||
seen[k] = true
|
||||
}
|
||||
changed := false
|
||||
for _, x := range slice2 {
|
||||
gvk, ok := x.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf(`expected {"group": <group>, "kind": <kind>, "version": <version>}, got: %#v`, x)
|
||||
}
|
||||
k := fmt.Sprintf("%s/%s.%s", gvk["group"], gvk["version"], gvk["kind"])
|
||||
if seen[k] {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, x)
|
||||
changed = true
|
||||
}
|
||||
|
||||
return ret, changed, nil
|
||||
}
|
||||
|
1
vendor/k8s.io/kube-openapi/pkg/generators/BUILD
generated
vendored
1
vendor/k8s.io/kube-openapi/pkg/generators/BUILD
generated
vendored
@@ -7,6 +7,7 @@ go_library(
|
||||
"config.go",
|
||||
"extension.go",
|
||||
"openapi.go",
|
||||
"union.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/kube-openapi/pkg/generators",
|
||||
importpath = "k8s.io/kube-openapi/pkg/generators",
|
||||
|
1
vendor/k8s.io/kube-openapi/pkg/generators/api_linter.go
generated
vendored
1
vendor/k8s.io/kube-openapi/pkg/generators/api_linter.go
generated
vendored
@@ -139,6 +139,7 @@ func newAPILinter() *apiLinter {
|
||||
rules: []APIRule{
|
||||
&rules.NamesMatch{},
|
||||
&rules.OmitEmptyMatchCase{},
|
||||
&rules.ListTypeMissing{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
24
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
24
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
@@ -397,11 +397,18 @@ func (g openAPITypeWriter) generateStructExtensions(t *types.Type) error {
|
||||
// Initially, we will only log struct extension errors.
|
||||
if len(errors) > 0 {
|
||||
for _, e := range errors {
|
||||
klog.V(2).Infof("[%s]: %s\n", t.String(), e)
|
||||
klog.Errorf("[%s]: %s\n", t.String(), e)
|
||||
}
|
||||
}
|
||||
unions, errors := parseUnions(t)
|
||||
if len(errors) > 0 {
|
||||
for _, e := range errors {
|
||||
klog.Errorf("[%s]: %s\n", t.String(), e)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(seans3): Validate struct extensions here.
|
||||
g.emitExtensions(extensions)
|
||||
g.emitExtensions(extensions, unions)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -416,13 +423,13 @@ func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *typ
|
||||
klog.V(2).Infof("%s %s\n", errorPrefix, e)
|
||||
}
|
||||
}
|
||||
g.emitExtensions(extensions)
|
||||
g.emitExtensions(extensions, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) emitExtensions(extensions []extension) {
|
||||
func (g openAPITypeWriter) emitExtensions(extensions []extension, unions []union) {
|
||||
// If any extensions exist, then emit code to create them.
|
||||
if len(extensions) == 0 {
|
||||
if len(extensions) == 0 && len(unions) == 0 {
|
||||
return
|
||||
}
|
||||
g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
|
||||
@@ -438,6 +445,13 @@ func (g openAPITypeWriter) emitExtensions(extensions []extension) {
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
}
|
||||
if len(unions) > 0 {
|
||||
g.Do("\"x-kubernetes-unions\": []interface{}{\n", nil)
|
||||
for _, u := range unions {
|
||||
u.emit(g)
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
g.Do("},\n},\n", nil)
|
||||
}
|
||||
|
||||
|
1
vendor/k8s.io/kube-openapi/pkg/generators/rules/BUILD
generated
vendored
1
vendor/k8s.io/kube-openapi/pkg/generators/rules/BUILD
generated
vendored
@@ -4,6 +4,7 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"idl_tag.go",
|
||||
"names_match.go",
|
||||
"omitempty_match_case.go",
|
||||
],
|
||||
|
36
vendor/k8s.io/kube-openapi/pkg/generators/rules/idl_tag.go
generated
vendored
Normal file
36
vendor/k8s.io/kube-openapi/pkg/generators/rules/idl_tag.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
const ListTypeIDLTag = "listType"
|
||||
|
||||
// ListTypeMissing implements APIRule interface.
|
||||
// A list type is required for inlined list.
|
||||
type ListTypeMissing struct{}
|
||||
|
||||
// Name returns the name of APIRule
|
||||
func (l *ListTypeMissing) Name() string {
|
||||
return "list_type_missing"
|
||||
}
|
||||
|
||||
// Validate evaluates API rule on type t and returns a list of field names in
|
||||
// the type that violate the rule. Empty field name [""] implies the entire
|
||||
// type violates the rule.
|
||||
func (l *ListTypeMissing) Validate(t *types.Type) ([]string, error) {
|
||||
fields := make([]string, 0)
|
||||
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
for _, m := range t.Members {
|
||||
if m.Type.Kind == types.Slice && types.ExtractCommentTags("+", m.CommentLines)[ListTypeIDLTag] == nil {
|
||||
fields = append(fields, m.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields, nil
|
||||
|
||||
}
|
207
vendor/k8s.io/kube-openapi/pkg/generators/union.go
generated
vendored
Normal file
207
vendor/k8s.io/kube-openapi/pkg/generators/union.go
generated
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
const tagUnionMember = "union"
|
||||
const tagUnionDeprecated = "unionDeprecated"
|
||||
const tagUnionDiscriminator = "unionDiscriminator"
|
||||
|
||||
type union struct {
|
||||
discriminator string
|
||||
fieldsToDiscriminated map[string]string
|
||||
}
|
||||
|
||||
// emit prints the union, can be called on a nil union (emits nothing)
|
||||
func (u *union) emit(g openAPITypeWriter) {
|
||||
if u == nil {
|
||||
return
|
||||
}
|
||||
g.Do("map[string]interface{}{\n", nil)
|
||||
if u.discriminator != "" {
|
||||
g.Do("\"discriminator\": \"$.$\",\n", u.discriminator)
|
||||
}
|
||||
g.Do("\"fields-to-discriminateBy\": map[string]interface{}{\n", nil)
|
||||
keys := []string{}
|
||||
for field := range u.fieldsToDiscriminated {
|
||||
keys = append(keys, field)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, field := range keys {
|
||||
g.Do("\"$.$\": ", field)
|
||||
g.Do("\"$.$\",\n", u.fieldsToDiscriminated[field])
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
|
||||
// Sets the discriminator if it's not set yet, otherwise return an error
|
||||
func (u *union) setDiscriminator(value string) []error {
|
||||
errors := []error{}
|
||||
if u.discriminator != "" {
|
||||
errors = append(errors, fmt.Errorf("at least two discriminators found: %v and %v", value, u.discriminator))
|
||||
}
|
||||
u.discriminator = value
|
||||
return errors
|
||||
}
|
||||
|
||||
// Add a new member to the union
|
||||
func (u *union) addMember(jsonName, variableName string) {
|
||||
if _, ok := u.fieldsToDiscriminated[jsonName]; ok {
|
||||
panic(fmt.Errorf("same field (%v) found multiple times", jsonName))
|
||||
}
|
||||
u.fieldsToDiscriminated[jsonName] = variableName
|
||||
}
|
||||
|
||||
// Makes sure that the union is valid, specifically looking for re-used discriminated
|
||||
func (u *union) isValid() []error {
|
||||
errors := []error{}
|
||||
// Case 1: discriminator but no fields
|
||||
if u.discriminator != "" && len(u.fieldsToDiscriminated) == 0 {
|
||||
errors = append(errors, fmt.Errorf("discriminator set with no fields in union"))
|
||||
}
|
||||
// Case 2: two fields have the same discriminated value
|
||||
discriminated := map[string]struct{}{}
|
||||
for _, d := range u.fieldsToDiscriminated {
|
||||
if _, ok := discriminated[d]; ok {
|
||||
errors = append(errors, fmt.Errorf("discriminated value is used twice: %v", d))
|
||||
}
|
||||
discriminated[d] = struct{}{}
|
||||
}
|
||||
// Case 3: a field is both discriminator AND part of the union
|
||||
if u.discriminator != "" {
|
||||
if _, ok := u.fieldsToDiscriminated[u.discriminator]; ok {
|
||||
errors = append(errors, fmt.Errorf("%v can't be both discriminator and part of the union", u.discriminator))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
// Find unions either directly on the members (or inlined members, not
|
||||
// going across types) or on the type itself, or on embedded types.
|
||||
func parseUnions(t *types.Type) ([]union, []error) {
|
||||
errors := []error{}
|
||||
unions := []union{}
|
||||
su, err := parseUnionStruct(t)
|
||||
if su != nil {
|
||||
unions = append(unions, *su)
|
||||
}
|
||||
errors = append(errors, err...)
|
||||
eu, err := parseEmbeddedUnion(t)
|
||||
unions = append(unions, eu...)
|
||||
errors = append(errors, err...)
|
||||
mu, err := parseUnionMembers(t)
|
||||
if mu != nil {
|
||||
unions = append(unions, *mu)
|
||||
}
|
||||
errors = append(errors, err...)
|
||||
return unions, errors
|
||||
}
|
||||
|
||||
// Find unions in embedded types, unions shouldn't go across types.
|
||||
func parseEmbeddedUnion(t *types.Type) ([]union, []error) {
|
||||
errors := []error{}
|
||||
unions := []union{}
|
||||
for _, m := range t.Members {
|
||||
if hasOpenAPITagValue(m.CommentLines, tagValueFalse) {
|
||||
continue
|
||||
}
|
||||
if !shouldInlineMembers(&m) {
|
||||
continue
|
||||
}
|
||||
u, err := parseUnions(m.Type)
|
||||
unions = append(unions, u...)
|
||||
errors = append(errors, err...)
|
||||
}
|
||||
return unions, errors
|
||||
}
|
||||
|
||||
// Look for union tag on a struct, and then include all the fields
|
||||
// (except the discriminator if there is one). The struct shouldn't have
|
||||
// embedded types.
|
||||
func parseUnionStruct(t *types.Type) (*union, []error) {
|
||||
errors := []error{}
|
||||
if types.ExtractCommentTags("+", t.CommentLines)[tagUnionMember] == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
u := &union{fieldsToDiscriminated: map[string]string{}}
|
||||
|
||||
for _, m := range t.Members {
|
||||
jsonName := getReferableName(&m)
|
||||
if jsonName == "" {
|
||||
continue
|
||||
}
|
||||
if shouldInlineMembers(&m) {
|
||||
errors = append(errors, fmt.Errorf("union structures can't have embedded fields: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
errors = append(errors, fmt.Errorf("union struct can't have unionDeprecated members: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
errors = append(errors, u.setDiscriminator(jsonName)...)
|
||||
} else {
|
||||
if !hasOptionalTag(&m) {
|
||||
errors = append(errors, fmt.Errorf("union members must be optional: %v.%v", t.Name, m.Name))
|
||||
}
|
||||
u.addMember(jsonName, m.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return u, errors
|
||||
}
|
||||
|
||||
// Find unions specifically on members.
|
||||
func parseUnionMembers(t *types.Type) (*union, []error) {
|
||||
errors := []error{}
|
||||
u := &union{fieldsToDiscriminated: map[string]string{}}
|
||||
|
||||
for _, m := range t.Members {
|
||||
jsonName := getReferableName(&m)
|
||||
if jsonName == "" {
|
||||
continue
|
||||
}
|
||||
if shouldInlineMembers(&m) {
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
errors = append(errors, u.setDiscriminator(jsonName)...)
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionMember] != nil {
|
||||
errors = append(errors, fmt.Errorf("union tag is not accepted on struct members: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
if !hasOptionalTag(&m) {
|
||||
errors = append(errors, fmt.Errorf("union members must be optional: %v.%v", t.Name, m.Name))
|
||||
}
|
||||
u.addMember(jsonName, m.Name)
|
||||
}
|
||||
}
|
||||
if len(u.fieldsToDiscriminated) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return u, append(errors, u.isValid()...)
|
||||
}
|
163
vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go
generated
vendored
163
vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go
generated
vendored
@@ -20,6 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
@@ -36,7 +37,7 @@ func ToSchema(models proto.Models) (*schema.Schema, error) {
|
||||
if err := c.convertAll(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.addCommonTypes()
|
||||
return c.output, nil
|
||||
}
|
||||
|
||||
@@ -95,14 +96,37 @@ func (c *convert) insertTypeDef(name string, model proto.Schema) {
|
||||
c.output.Types = append(c.output.Types, def)
|
||||
}
|
||||
|
||||
func (c *convert) addCommonTypes() {
|
||||
c.output.Types = append(c.output.Types, untypedDef)
|
||||
}
|
||||
|
||||
var untypedName string = "__untyped_atomic_"
|
||||
|
||||
var untypedDef schema.TypeDef = schema.TypeDef{
|
||||
Name: untypedName,
|
||||
Atom: schema.Atom{
|
||||
Scalar: ptr(schema.Scalar("untyped")),
|
||||
List: &schema.List{
|
||||
ElementType: schema.TypeRef{
|
||||
NamedType: &untypedName,
|
||||
},
|
||||
ElementRelationship: schema.Atomic,
|
||||
},
|
||||
Map: &schema.Map{
|
||||
ElementType: schema.TypeRef{
|
||||
NamedType: &untypedName,
|
||||
},
|
||||
ElementRelationship: schema.Atomic,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (c *convert) makeRef(model proto.Schema) schema.TypeRef {
|
||||
var tr schema.TypeRef
|
||||
if r, ok := model.(*proto.Ref); ok {
|
||||
if r.Reference() == "io.k8s.apimachinery.pkg.runtime.RawExtension" {
|
||||
return schema.TypeRef{
|
||||
Inlined: schema.Atom{
|
||||
Untyped: &schema.Untyped{},
|
||||
},
|
||||
NamedType: &untypedName,
|
||||
}
|
||||
}
|
||||
// reference a named type
|
||||
@@ -116,24 +140,136 @@ func (c *convert) makeRef(model proto.Schema) schema.TypeRef {
|
||||
|
||||
if tr == (schema.TypeRef{}) {
|
||||
// emit warning?
|
||||
tr.Inlined.Untyped = &schema.Untyped{}
|
||||
tr.NamedType = &untypedName
|
||||
}
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
func makeUnions(extensions map[string]interface{}) ([]schema.Union, error) {
|
||||
schemaUnions := []schema.Union{}
|
||||
if iunions, ok := extensions["x-kubernetes-unions"]; ok {
|
||||
unions, ok := iunions.([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`"x-kubernetes-unions" should be a list, got %#v`, unions)
|
||||
}
|
||||
for _, iunion := range unions {
|
||||
union, ok := iunion.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`"x-kubernetes-unions" items should be a map of string to unions, got %#v`, iunion)
|
||||
}
|
||||
unionMap := map[string]interface{}{}
|
||||
for k, v := range union {
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`"x-kubernetes-unions" has non-string key: %#v`, k)
|
||||
}
|
||||
unionMap[key] = v
|
||||
}
|
||||
schemaUnion, err := makeUnion(unionMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaUnions = append(schemaUnions, schemaUnion)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we have no overlap between unions
|
||||
fs := map[string]struct{}{}
|
||||
for _, u := range schemaUnions {
|
||||
if u.Discriminator != nil {
|
||||
if _, ok := fs[*u.Discriminator]; ok {
|
||||
return nil, fmt.Errorf("%v field appears multiple times in unions", *u.Discriminator)
|
||||
}
|
||||
fs[*u.Discriminator] = struct{}{}
|
||||
}
|
||||
for _, f := range u.Fields {
|
||||
if _, ok := fs[f.FieldName]; ok {
|
||||
return nil, fmt.Errorf("%v field appears multiple times in unions", f.FieldName)
|
||||
}
|
||||
fs[f.FieldName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return schemaUnions, nil
|
||||
}
|
||||
|
||||
func makeUnion(extensions map[string]interface{}) (schema.Union, error) {
|
||||
union := schema.Union{
|
||||
Fields: []schema.UnionField{},
|
||||
}
|
||||
|
||||
if idiscriminator, ok := extensions["discriminator"]; ok {
|
||||
discriminator, ok := idiscriminator.(string)
|
||||
if !ok {
|
||||
return schema.Union{}, fmt.Errorf(`"discriminator" must be a string, got: %#v`, idiscriminator)
|
||||
}
|
||||
union.Discriminator = &discriminator
|
||||
}
|
||||
|
||||
if ifields, ok := extensions["fields-to-discriminateBy"]; ok {
|
||||
fields, ok := ifields.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return schema.Union{}, fmt.Errorf(`"fields-to-discriminateBy" must be a map[string]string, got: %#v`, ifields)
|
||||
}
|
||||
// Needs sorted keys by field.
|
||||
keys := []string{}
|
||||
for ifield := range fields {
|
||||
field, ok := ifield.(string)
|
||||
if !ok {
|
||||
return schema.Union{}, fmt.Errorf(`"fields-to-discriminateBy": field must be a string, got: %#v`, ifield)
|
||||
}
|
||||
keys = append(keys, field)
|
||||
|
||||
}
|
||||
sort.Strings(keys)
|
||||
reverseMap := map[string]struct{}{}
|
||||
for _, field := range keys {
|
||||
value := fields[field]
|
||||
discriminated, ok := value.(string)
|
||||
if !ok {
|
||||
return schema.Union{}, fmt.Errorf(`"fields-to-discriminateBy"/%v: value must be a string, got: %#v`, field, value)
|
||||
}
|
||||
union.Fields = append(union.Fields, schema.UnionField{
|
||||
FieldName: field,
|
||||
DiscriminatorValue: discriminated,
|
||||
})
|
||||
|
||||
// Check that we don't have the same discriminateBy multiple times.
|
||||
if _, ok := reverseMap[discriminated]; ok {
|
||||
return schema.Union{}, fmt.Errorf("Multiple fields have the same discriminated name: %v", discriminated)
|
||||
}
|
||||
reverseMap[discriminated] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if union.Discriminator != nil && len(union.Fields) == 0 {
|
||||
return schema.Union{}, fmt.Errorf("discriminator set to %v, but no fields in union", *union.Discriminator)
|
||||
}
|
||||
return union, nil
|
||||
}
|
||||
|
||||
func (c *convert) VisitKind(k *proto.Kind) {
|
||||
a := c.top()
|
||||
a.Struct = &schema.Struct{}
|
||||
a.Map = &schema.Map{}
|
||||
for _, name := range k.FieldOrder {
|
||||
member := k.Fields[name]
|
||||
tr := c.makeRef(member)
|
||||
a.Struct.Fields = append(a.Struct.Fields, schema.StructField{
|
||||
a.Map.Fields = append(a.Map.Fields, schema.StructField{
|
||||
Name: name,
|
||||
Type: tr,
|
||||
})
|
||||
}
|
||||
|
||||
unions, err := makeUnions(k.GetExtensions())
|
||||
if err != nil {
|
||||
c.reportError(err.Error())
|
||||
return
|
||||
}
|
||||
// TODO: We should check that the fields and discriminator
|
||||
// specified in the union are actual fields in the struct.
|
||||
a.Map.Unions = unions
|
||||
|
||||
// TODO: Get element relationship when we start adding it to the spec.
|
||||
}
|
||||
|
||||
@@ -211,9 +347,10 @@ func (c *convert) VisitMap(m *proto.Map) {
|
||||
// spec.
|
||||
}
|
||||
|
||||
func ptr(s schema.Scalar) *schema.Scalar { return &s }
|
||||
|
||||
func (c *convert) VisitPrimitive(p *proto.Primitive) {
|
||||
a := c.top()
|
||||
ptr := func(s schema.Scalar) *schema.Scalar { return &s }
|
||||
switch p.Type {
|
||||
case proto.Integer:
|
||||
a.Scalar = ptr(schema.Numeric)
|
||||
@@ -227,21 +364,21 @@ func (c *convert) VisitPrimitive(p *proto.Primitive) {
|
||||
// byte really means []byte and is encoded as a string.
|
||||
a.Scalar = ptr(schema.String)
|
||||
case "int-or-string":
|
||||
a.Untyped = &schema.Untyped{}
|
||||
a.Scalar = ptr(schema.Scalar("untyped"))
|
||||
case "date-time":
|
||||
a.Untyped = &schema.Untyped{}
|
||||
a.Scalar = ptr(schema.Scalar("untyped"))
|
||||
default:
|
||||
a.Untyped = &schema.Untyped{}
|
||||
a.Scalar = ptr(schema.Scalar("untyped"))
|
||||
}
|
||||
case proto.Boolean:
|
||||
a.Scalar = ptr(schema.Boolean)
|
||||
default:
|
||||
a.Untyped = &schema.Untyped{}
|
||||
a.Scalar = ptr(schema.Scalar("untyped"))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *convert) VisitArbitrary(a *proto.Arbitrary) {
|
||||
c.top().Untyped = &schema.Untyped{}
|
||||
*c.top() = untypedDef.Atom
|
||||
}
|
||||
|
||||
func (c *convert) VisitReference(proto.Reference) {
|
||||
|
2
vendor/k8s.io/kube-openapi/pkg/util/proto/OWNERS
generated
vendored
Normal file
2
vendor/k8s.io/kube-openapi/pkg/util/proto/OWNERS
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
approvers:
|
||||
- apelisse
|
17
vendor/k8s.io/kube-openapi/pkg/util/proto/document.go
generated
vendored
17
vendor/k8s.io/kube-openapi/pkg/util/proto/document.go
generated
vendored
@@ -92,13 +92,16 @@ func NewOpenAPIData(doc *openapi_v2.Document) (Models, error) {
|
||||
// We believe the schema is a reference, verify that and returns a new
|
||||
// Schema
|
||||
func (d *Definitions) parseReference(s *openapi_v2.Schema, path *Path) (Schema, error) {
|
||||
// TODO(wrong): a schema with a $ref can have properties. We can ignore them (would be incomplete), but we cannot return an error.
|
||||
if len(s.GetProperties().GetAdditionalProperties()) > 0 {
|
||||
return nil, newSchemaError(path, "unallowed embedded type definition")
|
||||
}
|
||||
// TODO(wrong): a schema with a $ref can have a type. We can ignore it (would be incomplete), but we cannot return an error.
|
||||
if len(s.GetType().GetValue()) > 0 {
|
||||
return nil, newSchemaError(path, "definition reference can't have a type")
|
||||
}
|
||||
|
||||
// TODO(wrong): $refs outside of the definitions are completely valid. We can ignore them (would be incomplete), but we cannot return an error.
|
||||
if !strings.HasPrefix(s.GetXRef(), "#/definitions/") {
|
||||
return nil, newSchemaError(path, "unallowed reference to non-definition %q", s.GetXRef())
|
||||
}
|
||||
@@ -127,6 +130,7 @@ func (d *Definitions) parseMap(s *openapi_v2.Schema, path *Path) (Schema, error)
|
||||
return nil, newSchemaError(path, "invalid object type")
|
||||
}
|
||||
var sub Schema
|
||||
// TODO(incomplete): this misses the boolean case as AdditionalProperties is a bool+schema sum type.
|
||||
if s.GetAdditionalProperties().GetSchema() == nil {
|
||||
sub = &Arbitrary{
|
||||
BaseSchema: d.parseBaseSchema(s, path),
|
||||
@@ -157,6 +161,7 @@ func (d *Definitions) parsePrimitive(s *openapi_v2.Schema, path *Path) (Schema,
|
||||
case Number: // do nothing
|
||||
case Integer: // do nothing
|
||||
case Boolean: // do nothing
|
||||
// TODO(wrong): this misses "null". Would skip the null case (would be incomplete), but we cannot return an error.
|
||||
default:
|
||||
return nil, newSchemaError(path, "Unknown primitive type: %q", t)
|
||||
}
|
||||
@@ -175,6 +180,8 @@ func (d *Definitions) parseArray(s *openapi_v2.Schema, path *Path) (Schema, erro
|
||||
return nil, newSchemaError(path, `array should have type "array"`)
|
||||
}
|
||||
if len(s.GetItems().GetSchema()) != 1 {
|
||||
// TODO(wrong): Items can have multiple elements. We can ignore Items then (would be incomplete), but we cannot return an error.
|
||||
// TODO(wrong): "type: array" witohut any items at all is completely valid.
|
||||
return nil, newSchemaError(path, "array should have exactly one sub-item")
|
||||
}
|
||||
sub, err := d.ParseSchema(s.GetItems().GetSchema()[0], path)
|
||||
@@ -227,6 +234,8 @@ func (d *Definitions) parseArbitrary(s *openapi_v2.Schema, path *Path) (Schema,
|
||||
// this function is public, it doesn't leak through the interface.
|
||||
func (d *Definitions) ParseSchema(s *openapi_v2.Schema, path *Path) (Schema, error) {
|
||||
if s.GetXRef() != "" {
|
||||
// TODO(incomplete): ignoring the rest of s is wrong. As long as there are no conflict, everything from s must be considered
|
||||
// Reference: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#path-item-object
|
||||
return d.parseReference(s, path)
|
||||
}
|
||||
objectTypes := s.GetType().GetValue()
|
||||
@@ -234,11 +243,15 @@ func (d *Definitions) ParseSchema(s *openapi_v2.Schema, path *Path) (Schema, err
|
||||
case 0:
|
||||
// in the OpenAPI schema served by older k8s versions, object definitions created from structs did not include
|
||||
// the type:object property (they only included the "properties" property), so we need to handle this case
|
||||
// TODO: validate that we ever published empty, non-nil properties. JSON roundtripping nils them.
|
||||
if s.GetProperties() != nil {
|
||||
// TODO(wrong): when verifying a non-object later against this, it will be rejected as invalid type.
|
||||
// TODO(CRD validation schema publishing): we have to filter properties (empty or not) if type=object is not given
|
||||
return d.parseKind(s, path)
|
||||
} else {
|
||||
// Definition has no type and no properties. Treat it as an arbitrary value
|
||||
// TODO: what if it has additionalProperties or patternProperties?
|
||||
// TODO(incomplete): what if it has additionalProperties=false or patternProperties?
|
||||
// ANSWER: parseArbitrary is less strict than it has to be with patternProperties (which is ignored). So this is correct (of course not complete).
|
||||
return d.parseArbitrary(s, path)
|
||||
}
|
||||
case 1:
|
||||
@@ -256,6 +269,8 @@ func (d *Definitions) ParseSchema(s *openapi_v2.Schema, path *Path) (Schema, err
|
||||
return d.parsePrimitive(s, path)
|
||||
default:
|
||||
// the OpenAPI generator never generates (nor it ever did in the past) OpenAPI type definitions with multiple types
|
||||
// TODO(wrong): this is rejecting a completely valid OpenAPI spec
|
||||
// TODO(CRD validation schema publishing): filter these out
|
||||
return nil, newSchemaError(path, "definitions with multiple types aren't supported")
|
||||
}
|
||||
}
|
||||
|
1
vendor/k8s.io/kube-openapi/pkg/util/proto/validation/types.go
generated
vendored
1
vendor/k8s.io/kube-openapi/pkg/util/proto/validation/types.go
generated
vendored
@@ -216,6 +216,7 @@ func (item *primitiveItem) VisitPrimitive(schema *proto.Primitive) {
|
||||
case proto.String:
|
||||
return
|
||||
}
|
||||
// TODO(wrong): this misses "null"
|
||||
|
||||
item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: schema.Type, Actual: item.Kind})
|
||||
}
|
||||
|
Reference in New Issue
Block a user