build source
This commit is contained in:
commit
ee1fec43ed
4171 changed files with 1351288 additions and 0 deletions
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
|
||||
19
vendor/k8s.io/kube-openapi/pkg/util/proto/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kube-openapi/pkg/util/proto/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Copyright 2017 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 proto is a collection of libraries for parsing and indexing the type definitions.
|
||||
// The openapi spec contains the object model definitions and extensions metadata.
|
||||
package proto
|
||||
362
vendor/k8s.io/kube-openapi/pkg/util/proto/document.go
generated
vendored
Normal file
362
vendor/k8s.io/kube-openapi/pkg/util/proto/document.go
generated
vendored
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
Copyright 2017 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 proto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
openapi_v2 "github.com/google/gnostic-models/openapiv2"
|
||||
yaml "go.yaml.in/yaml/v2"
|
||||
)
|
||||
|
||||
func newSchemaError(path *Path, format string, a ...interface{}) error {
|
||||
err := fmt.Sprintf(format, a...)
|
||||
if path.Len() == 0 {
|
||||
return fmt.Errorf("SchemaError: %v", err)
|
||||
}
|
||||
return fmt.Errorf("SchemaError(%v): %v", path, err)
|
||||
}
|
||||
|
||||
// VendorExtensionToMap converts openapi VendorExtension to a map.
|
||||
func VendorExtensionToMap(e []*openapi_v2.NamedAny) map[string]interface{} {
|
||||
values := map[string]interface{}{}
|
||||
|
||||
for _, na := range e {
|
||||
if na.GetName() == "" || na.GetValue() == nil {
|
||||
continue
|
||||
}
|
||||
if na.GetValue().GetYaml() == "" {
|
||||
continue
|
||||
}
|
||||
var value interface{}
|
||||
err := yaml.Unmarshal([]byte(na.GetValue().GetYaml()), &value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
values[na.GetName()] = value
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
// Definitions is an implementation of `Models`. It looks for
|
||||
// models in an openapi Schema.
|
||||
type Definitions struct {
|
||||
models map[string]Schema
|
||||
}
|
||||
|
||||
var _ Models = &Definitions{}
|
||||
|
||||
// NewOpenAPIData creates a new `Models` out of the openapi document.
|
||||
func NewOpenAPIData(doc *openapi_v2.Document) (Models, error) {
|
||||
definitions := Definitions{
|
||||
models: map[string]Schema{},
|
||||
}
|
||||
|
||||
// Save the list of all models first. This will allow us to
|
||||
// validate that we don't have any dangling reference.
|
||||
for _, namedSchema := range doc.GetDefinitions().GetAdditionalProperties() {
|
||||
definitions.models[namedSchema.GetName()] = nil
|
||||
}
|
||||
|
||||
// Now, parse each model. We can validate that references exists.
|
||||
for _, namedSchema := range doc.GetDefinitions().GetAdditionalProperties() {
|
||||
path := NewPath(namedSchema.GetName())
|
||||
schema, err := definitions.ParseSchema(namedSchema.GetValue(), &path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
definitions.models[namedSchema.GetName()] = schema
|
||||
}
|
||||
|
||||
return &definitions, nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
reference := strings.TrimPrefix(s.GetXRef(), "#/definitions/")
|
||||
if _, ok := d.models[reference]; !ok {
|
||||
return nil, newSchemaError(path, "unknown model in reference: %q", reference)
|
||||
}
|
||||
base, err := d.parseBaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Ref{
|
||||
BaseSchema: base,
|
||||
reference: reference,
|
||||
definitions: d,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseDefault(def *openapi_v2.Any) (interface{}, error) {
|
||||
if def == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var i interface{}
|
||||
if err := yaml.Unmarshal([]byte(def.Yaml), &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseBaseSchema(s *openapi_v2.Schema, path *Path) (BaseSchema, error) {
|
||||
def, err := parseDefault(s.GetDefault())
|
||||
if err != nil {
|
||||
return BaseSchema{}, err
|
||||
}
|
||||
return BaseSchema{
|
||||
Description: s.GetDescription(),
|
||||
Default: def,
|
||||
Extensions: VendorExtensionToMap(s.GetVendorExtension()),
|
||||
Path: *path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// We believe the schema is a map, verify and return a new schema
|
||||
func (d *Definitions) parseMap(s *openapi_v2.Schema, path *Path) (Schema, error) {
|
||||
if len(s.GetType().GetValue()) != 0 && s.GetType().GetValue()[0] != object {
|
||||
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 {
|
||||
base, err := d.parseBaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub = &Arbitrary{
|
||||
BaseSchema: base,
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
sub, err = d.ParseSchema(s.GetAdditionalProperties().GetSchema(), path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
base, err := d.parseBaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Map{
|
||||
BaseSchema: base,
|
||||
SubType: sub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parsePrimitive(s *openapi_v2.Schema, path *Path) (Schema, error) {
|
||||
var t string
|
||||
if len(s.GetType().GetValue()) > 1 {
|
||||
return nil, newSchemaError(path, "primitive can't have more than 1 type")
|
||||
}
|
||||
if len(s.GetType().GetValue()) == 1 {
|
||||
t = s.GetType().GetValue()[0]
|
||||
}
|
||||
switch t {
|
||||
case String: // do nothing
|
||||
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)
|
||||
}
|
||||
base, err := d.parseBaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Primitive{
|
||||
BaseSchema: base,
|
||||
Type: t,
|
||||
Format: s.GetFormat(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseArray(s *openapi_v2.Schema, path *Path) (Schema, error) {
|
||||
if len(s.GetType().GetValue()) != 1 {
|
||||
return nil, newSchemaError(path, "array should have exactly one type")
|
||||
}
|
||||
if s.GetType().GetValue()[0] != array {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base, err := d.parseBaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Array{
|
||||
BaseSchema: base,
|
||||
SubType: sub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseKind(s *openapi_v2.Schema, path *Path) (Schema, error) {
|
||||
if len(s.GetType().GetValue()) != 0 && s.GetType().GetValue()[0] != object {
|
||||
return nil, newSchemaError(path, "invalid object type")
|
||||
}
|
||||
if s.GetProperties() == nil {
|
||||
return nil, newSchemaError(path, "object doesn't have properties")
|
||||
}
|
||||
|
||||
fields := map[string]Schema{}
|
||||
fieldOrder := []string{}
|
||||
|
||||
for _, namedSchema := range s.GetProperties().GetAdditionalProperties() {
|
||||
var err error
|
||||
name := namedSchema.GetName()
|
||||
path := path.FieldPath(name)
|
||||
fields[name], err = d.ParseSchema(namedSchema.GetValue(), &path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldOrder = append(fieldOrder, name)
|
||||
}
|
||||
|
||||
base, err := d.parseBaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Kind{
|
||||
BaseSchema: base,
|
||||
RequiredFields: s.GetRequired(),
|
||||
Fields: fields,
|
||||
FieldOrder: fieldOrder,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseArbitrary(s *openapi_v2.Schema, path *Path) (Schema, error) {
|
||||
base, err := d.parseBaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Arbitrary{
|
||||
BaseSchema: base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseSchema creates a walkable Schema from an openapi schema. While
|
||||
// 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()
|
||||
switch len(objectTypes) {
|
||||
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(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:
|
||||
t := objectTypes[0]
|
||||
switch t {
|
||||
case object:
|
||||
if s.GetProperties() != nil {
|
||||
return d.parseKind(s, path)
|
||||
} else {
|
||||
return d.parseMap(s, path)
|
||||
}
|
||||
case array:
|
||||
return d.parseArray(s, path)
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// LookupModel is public through the interface of Models. It
|
||||
// returns a visitable schema from the given model name.
|
||||
func (d *Definitions) LookupModel(model string) Schema {
|
||||
return d.models[model]
|
||||
}
|
||||
|
||||
func (d *Definitions) ListModels() []string {
|
||||
models := []string{}
|
||||
|
||||
for model := range d.models {
|
||||
models = append(models, model)
|
||||
}
|
||||
|
||||
sort.Strings(models)
|
||||
return models
|
||||
}
|
||||
|
||||
type Ref struct {
|
||||
BaseSchema
|
||||
|
||||
reference string
|
||||
definitions *Definitions
|
||||
}
|
||||
|
||||
var _ Reference = &Ref{}
|
||||
|
||||
func (r *Ref) Reference() string {
|
||||
return r.reference
|
||||
}
|
||||
|
||||
func (r *Ref) SubSchema() Schema {
|
||||
return r.definitions.models[r.reference]
|
||||
}
|
||||
|
||||
func (r *Ref) Accept(v SchemaVisitor) {
|
||||
v.VisitReference(r)
|
||||
}
|
||||
|
||||
func (r *Ref) GetName() string {
|
||||
return fmt.Sprintf("Reference to %q", r.reference)
|
||||
}
|
||||
324
vendor/k8s.io/kube-openapi/pkg/util/proto/document_v3.go
generated
vendored
Normal file
324
vendor/k8s.io/kube-openapi/pkg/util/proto/document_v3.go
generated
vendored
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
Copyright 2022 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 proto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
openapi_v3 "github.com/google/gnostic-models/openapiv3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
// Temporary parse implementation to be used until gnostic->kube-openapi conversion
|
||||
// is possible.
|
||||
func NewOpenAPIV3Data(doc *openapi_v3.Document) (Models, error) {
|
||||
definitions := Definitions{
|
||||
models: map[string]Schema{},
|
||||
}
|
||||
|
||||
schemas := doc.GetComponents().GetSchemas()
|
||||
if schemas == nil {
|
||||
return &definitions, nil
|
||||
}
|
||||
|
||||
// Save the list of all models first. This will allow us to
|
||||
// validate that we don't have any dangling reference.
|
||||
for _, namedSchema := range schemas.GetAdditionalProperties() {
|
||||
definitions.models[namedSchema.GetName()] = nil
|
||||
}
|
||||
|
||||
// Now, parse each model. We can validate that references exists.
|
||||
for _, namedSchema := range schemas.GetAdditionalProperties() {
|
||||
path := NewPath(namedSchema.GetName())
|
||||
val := namedSchema.GetValue()
|
||||
|
||||
if val == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if schema, err := definitions.ParseV3SchemaOrReference(namedSchema.GetValue(), &path); err != nil {
|
||||
return nil, err
|
||||
} else if schema != nil {
|
||||
// Schema may be nil if we hit incompleteness in the conversion,
|
||||
// but not a fatal error
|
||||
definitions.models[namedSchema.GetName()] = schema
|
||||
}
|
||||
}
|
||||
|
||||
return &definitions, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) ParseV3SchemaReference(s *openapi_v3.Reference, path *Path) (Schema, error) {
|
||||
base := &BaseSchema{
|
||||
Description: s.Description,
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(s.GetXRef(), "#/components/schemas") {
|
||||
// Only resolve references to components/schemas. We may add support
|
||||
// later for other in-spec paths, but otherwise treat unrecognized
|
||||
// refs as arbitrary/unknown values.
|
||||
return &Arbitrary{
|
||||
BaseSchema: *base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
reference := strings.TrimPrefix(s.GetXRef(), "#/components/schemas/")
|
||||
if _, ok := d.models[reference]; !ok {
|
||||
return nil, newSchemaError(path, "unknown model in reference: %q", reference)
|
||||
}
|
||||
|
||||
return &Ref{
|
||||
BaseSchema: BaseSchema{
|
||||
Description: s.Description,
|
||||
},
|
||||
reference: reference,
|
||||
definitions: d,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) ParseV3SchemaOrReference(s *openapi_v3.SchemaOrReference, path *Path) (Schema, error) {
|
||||
var schema Schema
|
||||
var err error
|
||||
|
||||
switch v := s.GetOneof().(type) {
|
||||
case *openapi_v3.SchemaOrReference_Reference:
|
||||
// Any references stored in #!/components/... are bound to refer
|
||||
// to external documents. This API does not support such a
|
||||
// feature.
|
||||
//
|
||||
// In the weird case that this is a reference to a schema that is
|
||||
// not external, we attempt to parse anyway
|
||||
schema, err = d.ParseV3SchemaReference(v.Reference, path)
|
||||
case *openapi_v3.SchemaOrReference_Schema:
|
||||
schema, err = d.ParseSchemaV3(v.Schema, path)
|
||||
default:
|
||||
panic("unexpected type")
|
||||
}
|
||||
|
||||
return schema, err
|
||||
}
|
||||
|
||||
// ParseSchema creates a walkable Schema from an openapi v3 schema. While
|
||||
// this function is public, it doesn't leak through the interface.
|
||||
func (d *Definitions) ParseSchemaV3(s *openapi_v3.Schema, path *Path) (Schema, error) {
|
||||
switch s.GetType() {
|
||||
case object:
|
||||
for _, extension := range s.GetSpecificationExtension() {
|
||||
if extension.Name == "x-kubernetes-group-version-kind" {
|
||||
// Objects with x-kubernetes-group-version-kind are always top
|
||||
// level types.
|
||||
return d.parseV3Kind(s, path)
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.GetProperties().GetAdditionalProperties()) > 0 {
|
||||
return d.parseV3Kind(s, path)
|
||||
}
|
||||
return d.parseV3Map(s, path)
|
||||
case array:
|
||||
return d.parseV3Array(s, path)
|
||||
case String, Number, Integer, Boolean:
|
||||
return d.parseV3Primitive(s, path)
|
||||
default:
|
||||
return d.parseV3Arbitrary(s, path)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Definitions) parseV3Kind(s *openapi_v3.Schema, path *Path) (Schema, error) {
|
||||
if s.GetType() != object {
|
||||
return nil, newSchemaError(path, "invalid object type")
|
||||
} else if s.GetProperties() == nil {
|
||||
return nil, newSchemaError(path, "object doesn't have properties")
|
||||
}
|
||||
|
||||
fields := map[string]Schema{}
|
||||
fieldOrder := []string{}
|
||||
|
||||
for _, namedSchema := range s.GetProperties().GetAdditionalProperties() {
|
||||
var err error
|
||||
name := namedSchema.GetName()
|
||||
path := path.FieldPath(name)
|
||||
fields[name], err = d.ParseV3SchemaOrReference(namedSchema.GetValue(), &path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldOrder = append(fieldOrder, name)
|
||||
}
|
||||
|
||||
base, err := d.parseV3BaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Kind{
|
||||
BaseSchema: *base,
|
||||
RequiredFields: s.GetRequired(),
|
||||
Fields: fields,
|
||||
FieldOrder: fieldOrder,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseV3Arbitrary(s *openapi_v3.Schema, path *Path) (Schema, error) {
|
||||
base, err := d.parseV3BaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Arbitrary{
|
||||
BaseSchema: *base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseV3Primitive(s *openapi_v3.Schema, path *Path) (Schema, error) {
|
||||
switch s.GetType() {
|
||||
case String: // do nothing
|
||||
case Number: // do nothing
|
||||
case Integer: // do nothing
|
||||
case Boolean: // do nothing
|
||||
default:
|
||||
// Unsupported primitive type. Treat as arbitrary type
|
||||
return d.parseV3Arbitrary(s, path)
|
||||
}
|
||||
|
||||
base, err := d.parseV3BaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Primitive{
|
||||
BaseSchema: *base,
|
||||
Type: s.GetType(),
|
||||
Format: s.GetFormat(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseV3Array(s *openapi_v3.Schema, path *Path) (Schema, error) {
|
||||
if s.GetType() != array {
|
||||
return nil, newSchemaError(path, `array should have type "array"`)
|
||||
} else if len(s.GetItems().GetSchemaOrReference()) != 1 {
|
||||
// This array can have multiple types in it (or no types at all)
|
||||
// This is not supported by this conversion.
|
||||
// Just return an arbitrary type
|
||||
return d.parseV3Arbitrary(s, path)
|
||||
}
|
||||
|
||||
sub, err := d.ParseV3SchemaOrReference(s.GetItems().GetSchemaOrReference()[0], path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base, err := d.parseV3BaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Array{
|
||||
BaseSchema: *base,
|
||||
SubType: sub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// We believe the schema is a map, verify and return a new schema
|
||||
func (d *Definitions) parseV3Map(s *openapi_v3.Schema, path *Path) (Schema, error) {
|
||||
if s.GetType() != object {
|
||||
return nil, newSchemaError(path, "invalid object type")
|
||||
}
|
||||
var sub Schema
|
||||
|
||||
switch p := s.GetAdditionalProperties().GetOneof().(type) {
|
||||
case *openapi_v3.AdditionalPropertiesItem_Boolean:
|
||||
// What does this boolean even mean?
|
||||
base, err := d.parseV3BaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub = &Arbitrary{
|
||||
BaseSchema: *base,
|
||||
}
|
||||
case *openapi_v3.AdditionalPropertiesItem_SchemaOrReference:
|
||||
if schema, err := d.ParseV3SchemaOrReference(p.SchemaOrReference, path); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
sub = schema
|
||||
}
|
||||
case nil:
|
||||
// no subtype?
|
||||
sub = &Arbitrary{}
|
||||
default:
|
||||
panic("unrecognized type " + reflect.TypeOf(p).Name())
|
||||
}
|
||||
|
||||
base, err := d.parseV3BaseSchema(s, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Map{
|
||||
BaseSchema: *base,
|
||||
SubType: sub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseV3Interface(def *yaml.Node) (interface{}, error) {
|
||||
if def == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var i interface{}
|
||||
if err := def.Decode(&i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (d *Definitions) parseV3BaseSchema(s *openapi_v3.Schema, path *Path) (*BaseSchema, error) {
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("cannot initialize BaseSchema from nil")
|
||||
}
|
||||
|
||||
def, err := parseV3Interface(s.GetDefault().ToRawInfo())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BaseSchema{
|
||||
Description: s.GetDescription(),
|
||||
Default: def,
|
||||
Extensions: SpecificationExtensionToMap(s.GetSpecificationExtension()),
|
||||
Path: *path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SpecificationExtensionToMap(e []*openapi_v3.NamedAny) map[string]interface{} {
|
||||
values := map[string]interface{}{}
|
||||
|
||||
for _, na := range e {
|
||||
if na.GetName() == "" || na.GetValue() == nil {
|
||||
continue
|
||||
}
|
||||
if na.GetValue().GetYaml() == "" {
|
||||
continue
|
||||
}
|
||||
var value interface{}
|
||||
err := yaml.Unmarshal([]byte(na.GetValue().GetYaml()), &value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
values[na.GetName()] = value
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
285
vendor/k8s.io/kube-openapi/pkg/util/proto/openapi.go
generated
vendored
Normal file
285
vendor/k8s.io/kube-openapi/pkg/util/proto/openapi.go
generated
vendored
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
Copyright 2017 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 proto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Defines openapi types.
|
||||
const (
|
||||
Integer = "integer"
|
||||
Number = "number"
|
||||
String = "string"
|
||||
Boolean = "boolean"
|
||||
|
||||
// These types are private as they should never leak, and are
|
||||
// represented by actual structs.
|
||||
array = "array"
|
||||
object = "object"
|
||||
)
|
||||
|
||||
// Models interface describe a model provider. They can give you the
|
||||
// schema for a specific model.
|
||||
type Models interface {
|
||||
LookupModel(string) Schema
|
||||
ListModels() []string
|
||||
}
|
||||
|
||||
// SchemaVisitor is an interface that you need to implement if you want
|
||||
// to "visit" an openapi schema. A dispatch on the Schema type will call
|
||||
// the appropriate function based on its actual type:
|
||||
// - Array is a list of one and only one given subtype
|
||||
// - Map is a map of string to one and only one given subtype
|
||||
// - Primitive can be string, integer, number and boolean.
|
||||
// - Kind is an object with specific fields mapping to specific types.
|
||||
// - Reference is a link to another definition.
|
||||
type SchemaVisitor interface {
|
||||
VisitArray(*Array)
|
||||
VisitMap(*Map)
|
||||
VisitPrimitive(*Primitive)
|
||||
VisitKind(*Kind)
|
||||
VisitReference(Reference)
|
||||
}
|
||||
|
||||
// SchemaVisitorArbitrary is an additional visitor interface which handles
|
||||
// arbitrary types. For backwards compatibility, it's a separate interface
|
||||
// which is checked for at runtime.
|
||||
type SchemaVisitorArbitrary interface {
|
||||
SchemaVisitor
|
||||
VisitArbitrary(*Arbitrary)
|
||||
}
|
||||
|
||||
// Schema is the base definition of an openapi type.
|
||||
type Schema interface {
|
||||
// Giving a visitor here will let you visit the actual type.
|
||||
Accept(SchemaVisitor)
|
||||
|
||||
// Pretty print the name of the type.
|
||||
GetName() string
|
||||
// Describes how to access this field.
|
||||
GetPath() *Path
|
||||
// Describes the field.
|
||||
GetDescription() string
|
||||
// Default for that schema.
|
||||
GetDefault() interface{}
|
||||
// Returns type extensions.
|
||||
GetExtensions() map[string]interface{}
|
||||
}
|
||||
|
||||
// Path helps us keep track of type paths
|
||||
type Path struct {
|
||||
parent *Path
|
||||
key string
|
||||
}
|
||||
|
||||
func NewPath(key string) Path {
|
||||
return Path{key: key}
|
||||
}
|
||||
|
||||
func (p *Path) Get() []string {
|
||||
if p == nil {
|
||||
return []string{}
|
||||
}
|
||||
if p.key == "" {
|
||||
return p.parent.Get()
|
||||
}
|
||||
return append(p.parent.Get(), p.key)
|
||||
}
|
||||
|
||||
func (p *Path) Len() int {
|
||||
return len(p.Get())
|
||||
}
|
||||
|
||||
func (p *Path) String() string {
|
||||
return strings.Join(p.Get(), "")
|
||||
}
|
||||
|
||||
// ArrayPath appends an array index and creates a new path
|
||||
func (p *Path) ArrayPath(i int) Path {
|
||||
return Path{
|
||||
parent: p,
|
||||
key: fmt.Sprintf("[%d]", i),
|
||||
}
|
||||
}
|
||||
|
||||
// FieldPath appends a field name and creates a new path
|
||||
func (p *Path) FieldPath(field string) Path {
|
||||
return Path{
|
||||
parent: p,
|
||||
key: fmt.Sprintf(".%s", field),
|
||||
}
|
||||
}
|
||||
|
||||
// BaseSchema holds data used by each types of schema.
|
||||
type BaseSchema struct {
|
||||
Description string
|
||||
Extensions map[string]interface{}
|
||||
Default interface{}
|
||||
|
||||
Path Path
|
||||
}
|
||||
|
||||
func (b *BaseSchema) GetDescription() string {
|
||||
return b.Description
|
||||
}
|
||||
|
||||
func (b *BaseSchema) GetExtensions() map[string]interface{} {
|
||||
return b.Extensions
|
||||
}
|
||||
|
||||
func (b *BaseSchema) GetDefault() interface{} {
|
||||
return b.Default
|
||||
}
|
||||
|
||||
func (b *BaseSchema) GetPath() *Path {
|
||||
return &b.Path
|
||||
}
|
||||
|
||||
// Array must have all its element of the same `SubType`.
|
||||
type Array struct {
|
||||
BaseSchema
|
||||
|
||||
SubType Schema
|
||||
}
|
||||
|
||||
var _ Schema = &Array{}
|
||||
|
||||
func (a *Array) Accept(v SchemaVisitor) {
|
||||
v.VisitArray(a)
|
||||
}
|
||||
|
||||
func (a *Array) GetName() string {
|
||||
return fmt.Sprintf("Array of %s", a.SubType.GetName())
|
||||
}
|
||||
|
||||
// Kind is a complex object. It can have multiple different
|
||||
// subtypes for each field, as defined in the `Fields` field. Mandatory
|
||||
// fields are listed in `RequiredFields`. The key of the object is
|
||||
// always of type `string`.
|
||||
type Kind struct {
|
||||
BaseSchema
|
||||
|
||||
// Lists names of required fields.
|
||||
RequiredFields []string
|
||||
// Maps field names to types.
|
||||
Fields map[string]Schema
|
||||
// FieldOrder reports the canonical order for the fields.
|
||||
FieldOrder []string
|
||||
}
|
||||
|
||||
var _ Schema = &Kind{}
|
||||
|
||||
func (k *Kind) Accept(v SchemaVisitor) {
|
||||
v.VisitKind(k)
|
||||
}
|
||||
|
||||
func (k *Kind) GetName() string {
|
||||
properties := []string{}
|
||||
for key := range k.Fields {
|
||||
properties = append(properties, key)
|
||||
}
|
||||
return fmt.Sprintf("Kind(%v)", properties)
|
||||
}
|
||||
|
||||
// IsRequired returns true if `field` is a required field for this type.
|
||||
func (k *Kind) IsRequired(field string) bool {
|
||||
for _, f := range k.RequiredFields {
|
||||
if f == field {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Keys returns a alphabetically sorted list of keys.
|
||||
func (k *Kind) Keys() []string {
|
||||
keys := make([]string, 0)
|
||||
for key := range k.Fields {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
// Map is an object who values must all be of the same `SubType`.
|
||||
// The key of the object is always of type `string`.
|
||||
type Map struct {
|
||||
BaseSchema
|
||||
|
||||
SubType Schema
|
||||
}
|
||||
|
||||
var _ Schema = &Map{}
|
||||
|
||||
func (m *Map) Accept(v SchemaVisitor) {
|
||||
v.VisitMap(m)
|
||||
}
|
||||
|
||||
func (m *Map) GetName() string {
|
||||
return fmt.Sprintf("Map of %s", m.SubType.GetName())
|
||||
}
|
||||
|
||||
// Primitive is a literal. There can be multiple types of primitives,
|
||||
// and this subtype can be visited through the `subType` field.
|
||||
type Primitive struct {
|
||||
BaseSchema
|
||||
|
||||
// Type of a primitive must be one of: integer, number, string, boolean.
|
||||
Type string
|
||||
Format string
|
||||
}
|
||||
|
||||
var _ Schema = &Primitive{}
|
||||
|
||||
func (p *Primitive) Accept(v SchemaVisitor) {
|
||||
v.VisitPrimitive(p)
|
||||
}
|
||||
|
||||
func (p *Primitive) GetName() string {
|
||||
if p.Format == "" {
|
||||
return p.Type
|
||||
}
|
||||
return fmt.Sprintf("%s (%s)", p.Type, p.Format)
|
||||
}
|
||||
|
||||
// Arbitrary is a value of any type (primitive, object or array)
|
||||
type Arbitrary struct {
|
||||
BaseSchema
|
||||
}
|
||||
|
||||
var _ Schema = &Arbitrary{}
|
||||
|
||||
func (a *Arbitrary) Accept(v SchemaVisitor) {
|
||||
if visitor, ok := v.(SchemaVisitorArbitrary); ok {
|
||||
visitor.VisitArbitrary(a)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Arbitrary) GetName() string {
|
||||
return "Arbitrary value (primitive, object or array)"
|
||||
}
|
||||
|
||||
// Reference implementation depends on the type of document.
|
||||
type Reference interface {
|
||||
Schema
|
||||
|
||||
Reference() string
|
||||
SubSchema() Schema
|
||||
}
|
||||
79
vendor/k8s.io/kube-openapi/pkg/util/trie.go
generated
vendored
Normal file
79
vendor/k8s.io/kube-openapi/pkg/util/trie.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
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 util
|
||||
|
||||
// A simple trie implementation with Add and HasPrefix methods only.
|
||||
type Trie struct {
|
||||
children map[byte]*Trie
|
||||
wordTail bool
|
||||
word string
|
||||
}
|
||||
|
||||
// NewTrie creates a Trie and add all strings in the provided list to it.
|
||||
func NewTrie(list []string) Trie {
|
||||
ret := Trie{
|
||||
children: make(map[byte]*Trie),
|
||||
wordTail: false,
|
||||
}
|
||||
for _, v := range list {
|
||||
ret.Add(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Add adds a word to this trie
|
||||
func (t *Trie) Add(v string) {
|
||||
root := t
|
||||
for _, b := range []byte(v) {
|
||||
child, exists := root.children[b]
|
||||
if !exists {
|
||||
child = &Trie{
|
||||
children: make(map[byte]*Trie),
|
||||
wordTail: false,
|
||||
}
|
||||
root.children[b] = child
|
||||
}
|
||||
root = child
|
||||
}
|
||||
root.wordTail = true
|
||||
root.word = v
|
||||
}
|
||||
|
||||
// HasPrefix returns true of v has any of the prefixes stored in this trie.
|
||||
func (t *Trie) HasPrefix(v string) bool {
|
||||
_, has := t.GetPrefix(v)
|
||||
return has
|
||||
}
|
||||
|
||||
// GetPrefix is like HasPrefix but return the prefix in case of match or empty string otherwise.
|
||||
func (t *Trie) GetPrefix(v string) (string, bool) {
|
||||
root := t
|
||||
if root.wordTail {
|
||||
return root.word, true
|
||||
}
|
||||
for _, b := range []byte(v) {
|
||||
child, exists := root.children[b]
|
||||
if !exists {
|
||||
return "", false
|
||||
}
|
||||
if child.wordTail {
|
||||
return child.word, true
|
||||
}
|
||||
root = child
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
126
vendor/k8s.io/kube-openapi/pkg/util/util.go
generated
vendored
Normal file
126
vendor/k8s.io/kube-openapi/pkg/util/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
Copyright 2017 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 util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// [DEPRECATED] ToCanonicalName converts Golang package/type canonical name into REST friendly OpenAPI name.
|
||||
// This method is deprecated because it has a misleading name. Please use ToRESTFriendlyName
|
||||
// instead
|
||||
//
|
||||
// NOTE: actually the "canonical name" in this method should be named "REST friendly OpenAPI name",
|
||||
// which is different from "canonical name" defined in GetCanonicalTypeName. The "canonical name" defined
|
||||
// in GetCanonicalTypeName means Go type names with full package path.
|
||||
//
|
||||
// Examples of REST friendly OpenAPI name:
|
||||
//
|
||||
// Input: k8s.io/api/core/v1.Pod
|
||||
// Output: io.k8s.api.core.v1.Pod
|
||||
//
|
||||
// Input: k8s.io/api/core/v1
|
||||
// Output: io.k8s.api.core.v1
|
||||
//
|
||||
// Input: csi.storage.k8s.io/v1alpha1.CSINodeInfo
|
||||
// Output: io.k8s.storage.csi.v1alpha1.CSINodeInfo
|
||||
func ToCanonicalName(name string) string {
|
||||
return ToRESTFriendlyName(name)
|
||||
}
|
||||
|
||||
// ToRESTFriendlyName converts Golang package/type canonical name into REST friendly OpenAPI name.
|
||||
//
|
||||
// Examples of REST friendly OpenAPI name:
|
||||
//
|
||||
// Input: k8s.io/api/core/v1.Pod
|
||||
// Output: io.k8s.api.core.v1.Pod
|
||||
//
|
||||
// Input: k8s.io/api/core/v1
|
||||
// Output: io.k8s.api.core.v1
|
||||
//
|
||||
// Input: csi.storage.k8s.io/v1alpha1.CSINodeInfo
|
||||
// Output: io.k8s.storage.csi.v1alpha1.CSINodeInfo
|
||||
func ToRESTFriendlyName(name string) string {
|
||||
nameParts := strings.Split(name, "/")
|
||||
// Reverse first part. e.g., io.k8s... instead of k8s.io...
|
||||
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
|
||||
parts := strings.Split(nameParts[0], ".")
|
||||
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
|
||||
parts[i], parts[j] = parts[j], parts[i]
|
||||
}
|
||||
nameParts[0] = strings.Join(parts, ".")
|
||||
}
|
||||
return strings.Join(nameParts, ".")
|
||||
}
|
||||
|
||||
// OpenAPICanonicalTypeNamer is an interface for models without Go type to seed model name.
|
||||
//
|
||||
// OpenAPI canonical names are Go type names with full package path, for uniquely indentifying
|
||||
// a model / Go type. If a Go type is vendored from another package, only the path after "/vendor/"
|
||||
// should be used. For custom resource definition (CRD), the canonical name is expected to be
|
||||
//
|
||||
// group/version.kind
|
||||
//
|
||||
// Examples of canonical name:
|
||||
//
|
||||
// Go type: k8s.io/kubernetes/pkg/apis/core.Pod
|
||||
// CRD: csi.storage.k8s.io/v1alpha1.CSINodeInfo
|
||||
//
|
||||
// Example for vendored Go type:
|
||||
//
|
||||
// Original full path: k8s.io/kubernetes/vendor/k8s.io/api/core/v1.Pod
|
||||
// Canonical name: k8s.io/api/core/v1.Pod
|
||||
//
|
||||
// Original full path: vendor/k8s.io/api/core/v1.Pod
|
||||
// Canonical name: k8s.io/api/core/v1.Pod
|
||||
type OpenAPICanonicalTypeNamer interface {
|
||||
OpenAPICanonicalTypeName() string
|
||||
}
|
||||
|
||||
// OpenAPIModelNamer is an interface Go types may implement to provide an OpenAPI model name.
|
||||
//
|
||||
// This takes precedence over OpenAPICanonicalTypeNamer, and should be used when a Go type has a model
|
||||
// name that differs from its canonical type name as determined by Go package name reflection.
|
||||
type OpenAPIModelNamer interface {
|
||||
OpenAPIModelName() string
|
||||
}
|
||||
|
||||
// GetCanonicalTypeName will find the canonical type name of a sample object, removing
|
||||
// the "vendor" part of the path
|
||||
func GetCanonicalTypeName(model interface{}) string {
|
||||
switch namer := model.(type) {
|
||||
case OpenAPIModelNamer:
|
||||
return namer.OpenAPIModelName()
|
||||
case OpenAPICanonicalTypeNamer:
|
||||
return namer.OpenAPICanonicalTypeName()
|
||||
}
|
||||
t := reflect.TypeOf(model)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.PkgPath() == "" {
|
||||
return t.Name()
|
||||
}
|
||||
path := t.PkgPath()
|
||||
if strings.Contains(path, "/vendor/") {
|
||||
path = path[strings.Index(path, "/vendor/")+len("/vendor/"):]
|
||||
} else if strings.HasPrefix(path, "vendor/") {
|
||||
path = strings.TrimPrefix(path, "vendor/")
|
||||
}
|
||||
return path + "." + t.Name()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue