feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
471
vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go
generated
vendored
Normal file
471
vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go
generated
vendored
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
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 aggregator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/schemamutation"
|
||||
"k8s.io/kube-openapi/pkg/util"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
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{}
|
||||
walkOnAllReferences(func(ref *spec.Ref) {
|
||||
if refStr := ref.String(); refStr != "" && strings.HasPrefix(refStr, definitionPrefix) {
|
||||
usedDefinitions[refStr[len(definitionPrefix):]] = true
|
||||
}
|
||||
}, root)
|
||||
return usedDefinitions
|
||||
}
|
||||
|
||||
// FilterSpecByPaths removes unnecessary paths and definitions used by those paths.
|
||||
// i.e. if a Path removed by this function, all definitions used by it and not used
|
||||
// anywhere else will also be removed.
|
||||
func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) {
|
||||
*sp = *FilterSpecByPathsWithoutSideEffects(sp, keepPathPrefixes)
|
||||
}
|
||||
|
||||
// FilterSpecByPathsWithoutSideEffects removes unnecessary paths and definitions used by those paths.
|
||||
// i.e. if a Path removed by this function, all definitions used by it and not used
|
||||
// anywhere else will also be removed.
|
||||
// It does not modify the input, but the output shares data structures with the input.
|
||||
func FilterSpecByPathsWithoutSideEffects(sp *spec.Swagger, keepPathPrefixes []string) *spec.Swagger {
|
||||
if sp.Paths == nil {
|
||||
return sp
|
||||
}
|
||||
|
||||
// Walk all references to find all used definitions. This function
|
||||
// want to only deal with unused definitions resulted from filtering paths.
|
||||
// Thus a definition will be removed only if it has been used before but
|
||||
// it is unused because of a path prune.
|
||||
initialUsedDefinitions := usedDefinitionForSpec(sp)
|
||||
|
||||
// First remove unwanted paths
|
||||
prefixes := util.NewTrie(keepPathPrefixes)
|
||||
ret := *sp
|
||||
ret.Paths = &spec.Paths{
|
||||
VendorExtensible: sp.Paths.VendorExtensible,
|
||||
Paths: map[string]spec.PathItem{},
|
||||
}
|
||||
for path, pathItem := range sp.Paths.Paths {
|
||||
if !prefixes.HasPrefix(path) {
|
||||
continue
|
||||
}
|
||||
ret.Paths.Paths[path] = pathItem
|
||||
}
|
||||
|
||||
// Walk all references to find all definition references.
|
||||
usedDefinitions := usedDefinitionForSpec(&ret)
|
||||
|
||||
// Remove unused definitions
|
||||
ret.Definitions = spec.Definitions{}
|
||||
for k, v := range sp.Definitions {
|
||||
if usedDefinitions[k] || !initialUsedDefinitions[k] {
|
||||
ret.Definitions[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return &ret
|
||||
}
|
||||
|
||||
// renameDefinitions renames definition references, without mutating the input.
|
||||
// The output might share data structures with the input.
|
||||
func renameDefinitions(s *spec.Swagger, renames map[string]string) *spec.Swagger {
|
||||
refRenames := make(map[string]string, len(renames))
|
||||
foundOne := false
|
||||
for k, v := range renames {
|
||||
refRenames[definitionPrefix+k] = definitionPrefix + v
|
||||
if _, ok := s.Definitions[k]; ok {
|
||||
foundOne = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundOne {
|
||||
return s
|
||||
}
|
||||
|
||||
ret := &spec.Swagger{}
|
||||
*ret = *s
|
||||
|
||||
ret = schemamutation.ReplaceReferences(func(ref *spec.Ref) *spec.Ref {
|
||||
refName := ref.String()
|
||||
if newRef, found := refRenames[refName]; found {
|
||||
ret := spec.MustCreateRef(newRef)
|
||||
return &ret
|
||||
}
|
||||
return ref
|
||||
}, ret)
|
||||
|
||||
renamedDefinitions := make(spec.Definitions, len(ret.Definitions))
|
||||
for k, v := range ret.Definitions {
|
||||
if newRef, found := renames[k]; found {
|
||||
k = newRef
|
||||
}
|
||||
renamedDefinitions[k] = v
|
||||
}
|
||||
ret.Definitions = renamedDefinitions
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// renameParameters renames parameter references, without mutating the input.
|
||||
// The output might share data structures with the input.
|
||||
func renameParameters(s *spec.Swagger, renames map[string]string) *spec.Swagger {
|
||||
refRenames := make(map[string]string, len(renames))
|
||||
foundOne := false
|
||||
for k, v := range renames {
|
||||
refRenames[parameterPrefix+k] = parameterPrefix + v
|
||||
if _, ok := s.Parameters[k]; ok {
|
||||
foundOne = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundOne {
|
||||
return s
|
||||
}
|
||||
|
||||
ret := &spec.Swagger{}
|
||||
*ret = *s
|
||||
|
||||
ret = schemamutation.ReplaceReferences(func(ref *spec.Ref) *spec.Ref {
|
||||
refName := ref.String()
|
||||
if newRef, found := refRenames[refName]; found {
|
||||
ret := spec.MustCreateRef(newRef)
|
||||
return &ret
|
||||
}
|
||||
return ref
|
||||
}, ret)
|
||||
|
||||
renamed := make(map[string]spec.Parameter, len(ret.Parameters))
|
||||
for k, v := range ret.Parameters {
|
||||
if newRef, found := renames[k]; found {
|
||||
k = newRef
|
||||
}
|
||||
renamed[k] = v
|
||||
}
|
||||
ret.Parameters = renamed
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// MergeSpecsIgnorePathConflictRenamingDefinitionsAndParameters is the same as
|
||||
// MergeSpecs except it will ignore any path conflicts by keeping the paths of
|
||||
// destination. It will rename definition and parameter conflicts.
|
||||
func MergeSpecsIgnorePathConflictRenamingDefinitionsAndParameters(dest, source *spec.Swagger) error {
|
||||
return mergeSpecs(dest, source, true, true, true)
|
||||
}
|
||||
|
||||
// MergeSpecsIgnorePathConflictDeprecated is the same as MergeSpecs except it will ignore any path
|
||||
// conflicts by keeping the paths of destination. It will rename definition and
|
||||
// parameter conflicts.
|
||||
func MergeSpecsIgnorePathConflictDeprecated(dest, source *spec.Swagger) error {
|
||||
return mergeSpecs(dest, source, true, false, true)
|
||||
}
|
||||
|
||||
// MergeSpecsFailOnDefinitionConflict is different from MergeSpecs as it fails if there is
|
||||
// a definition or parameter conflict.
|
||||
func MergeSpecsFailOnDefinitionConflict(dest, source *spec.Swagger) error {
|
||||
return mergeSpecs(dest, source, false, false, false)
|
||||
}
|
||||
|
||||
// MergeSpecs copies paths, definitions and parameters from source to dest, rename
|
||||
// definitions if needed. It will fail on path conflicts.
|
||||
//
|
||||
// The destination is mutated, the source is not.
|
||||
func MergeSpecs(dest, source *spec.Swagger) error {
|
||||
return mergeSpecs(dest, source, true, true, false)
|
||||
}
|
||||
|
||||
// mergeSpecs merges source into dest while resolving conflicts.
|
||||
// The source is not mutated.
|
||||
func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, renameParameterConflicts, ignorePathConflicts bool) (err error) {
|
||||
// Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
|
||||
if source.Paths == nil {
|
||||
// When a source spec does not have any path, that means none of the definitions
|
||||
// are used thus we should not do anything
|
||||
return nil
|
||||
}
|
||||
if dest.Paths == nil {
|
||||
dest.Paths = &spec.Paths{}
|
||||
}
|
||||
if ignorePathConflicts {
|
||||
keepPaths := []string{}
|
||||
hasConflictingPath := false
|
||||
for k := range source.Paths.Paths {
|
||||
if _, found := dest.Paths.Paths[k]; !found {
|
||||
keepPaths = append(keepPaths, k)
|
||||
} else {
|
||||
hasConflictingPath = true
|
||||
}
|
||||
}
|
||||
if len(keepPaths) == 0 {
|
||||
// There is nothing to merge. All paths are conflicting.
|
||||
return nil
|
||||
}
|
||||
if hasConflictingPath {
|
||||
source = FilterSpecByPathsWithoutSideEffects(source, keepPaths)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for model conflicts and rename to make definitions conflict-free (modulo different GVKs)
|
||||
usedNames := map[string]bool{}
|
||||
for k := range dest.Definitions {
|
||||
usedNames[k] = true
|
||||
}
|
||||
renames := map[string]string{}
|
||||
DEFINITIONLOOP:
|
||||
for k, v := range source.Definitions {
|
||||
existing, found := dest.Definitions[k]
|
||||
if !found || deepEqualDefinitionsModuloGVKs(&existing, &v) {
|
||||
// skip for now, we copy them after the rename loop
|
||||
continue
|
||||
}
|
||||
|
||||
if !renameModelConflicts {
|
||||
return fmt.Errorf("model name conflict in merging OpenAPI spec: %s", k)
|
||||
}
|
||||
|
||||
// Reuse previously renamed model if one exists
|
||||
var newName string
|
||||
i := 1
|
||||
for found {
|
||||
i++
|
||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||
existing, found = dest.Definitions[newName]
|
||||
if found && deepEqualDefinitionsModuloGVKs(&existing, &v) {
|
||||
renames[k] = newName
|
||||
continue DEFINITIONLOOP
|
||||
}
|
||||
}
|
||||
|
||||
_, foundInSource := source.Definitions[newName]
|
||||
for usedNames[newName] || foundInSource {
|
||||
i++
|
||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||
_, foundInSource = source.Definitions[newName]
|
||||
}
|
||||
renames[k] = newName
|
||||
usedNames[newName] = true
|
||||
}
|
||||
source = renameDefinitions(source, renames)
|
||||
|
||||
// Check for parameter conflicts and rename to make parameters conflict-free
|
||||
usedNames = map[string]bool{}
|
||||
for k := range dest.Parameters {
|
||||
usedNames[k] = true
|
||||
}
|
||||
renames = map[string]string{}
|
||||
PARAMETERLOOP:
|
||||
for k, p := range source.Parameters {
|
||||
existing, found := dest.Parameters[k]
|
||||
if !found || reflect.DeepEqual(&existing, &p) {
|
||||
// skip for now, we copy them after the rename loop
|
||||
continue
|
||||
}
|
||||
|
||||
if !renameParameterConflicts {
|
||||
return fmt.Errorf("parameter name conflict in merging OpenAPI spec: %s", k)
|
||||
}
|
||||
|
||||
// Reuse previously renamed parameter if one exists
|
||||
var newName string
|
||||
i := 1
|
||||
for found {
|
||||
i++
|
||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||
existing, found = dest.Parameters[newName]
|
||||
if found && reflect.DeepEqual(&existing, &p) {
|
||||
renames[k] = newName
|
||||
continue PARAMETERLOOP
|
||||
}
|
||||
}
|
||||
|
||||
_, foundInSource := source.Parameters[newName]
|
||||
for usedNames[newName] || foundInSource {
|
||||
i++
|
||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||
_, foundInSource = source.Parameters[newName]
|
||||
}
|
||||
renames[k] = newName
|
||||
usedNames[newName] = true
|
||||
}
|
||||
source = renameParameters(source, renames)
|
||||
|
||||
// Now without conflict (modulo different GVKs), copy definitions to dest
|
||||
for k, v := range source.Definitions {
|
||||
if existing, found := dest.Definitions[k]; !found {
|
||||
if dest.Definitions == nil {
|
||||
dest.Definitions = make(spec.Definitions, len(source.Definitions))
|
||||
}
|
||||
dest.Definitions[k] = v
|
||||
} else if merged, changed, err := mergedGVKs(&existing, &v); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
existing.Extensions[gvkKey] = merged
|
||||
}
|
||||
}
|
||||
|
||||
// Now without conflict, copy parameters to dest
|
||||
for k, v := range source.Parameters {
|
||||
if _, found := dest.Parameters[k]; !found {
|
||||
if dest.Parameters == nil {
|
||||
dest.Parameters = make(map[string]spec.Parameter, len(source.Parameters))
|
||||
}
|
||||
dest.Parameters[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Check for path conflicts
|
||||
for k, v := range source.Paths.Paths {
|
||||
if _, found := dest.Paths.Paths[k]; found {
|
||||
return fmt.Errorf("unable to merge: duplicated path %s", k)
|
||||
}
|
||||
// PathItem may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
|
||||
if dest.Paths.Paths == nil {
|
||||
dest.Paths.Paths = map[string]spec.PathItem{}
|
||||
}
|
||||
dest.Paths.Paths[k] = v
|
||||
}
|
||||
|
||||
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))
|
||||
keys := make([]string, 0, 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"])
|
||||
keys = append(keys, k)
|
||||
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)
|
||||
keys = append(keys, k)
|
||||
changed = true
|
||||
}
|
||||
|
||||
if changed {
|
||||
sort.Sort(byKeys{ret, keys})
|
||||
}
|
||||
|
||||
return ret, changed, nil
|
||||
}
|
||||
|
||||
type byKeys struct {
|
||||
values []interface{}
|
||||
keys []string
|
||||
}
|
||||
|
||||
func (b byKeys) Len() int {
|
||||
return len(b.values)
|
||||
}
|
||||
|
||||
func (b byKeys) Less(i, j int) bool {
|
||||
return b.keys[i] < b.keys[j]
|
||||
}
|
||||
|
||||
func (b byKeys) Swap(i, j int) {
|
||||
b.values[i], b.values[j] = b.values[j], b.values[i]
|
||||
b.keys[i], b.keys[j] = b.keys[j], b.keys[i]
|
||||
}
|
||||
163
vendor/k8s.io/kube-openapi/pkg/aggregator/walker.go
generated
vendored
Normal file
163
vendor/k8s.io/kube-openapi/pkg/aggregator/walker.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
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 aggregator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
const (
|
||||
definitionPrefix = "#/definitions/"
|
||||
parameterPrefix = "#/parameters/"
|
||||
)
|
||||
|
||||
// Run a readonlyReferenceWalker method on all references of an OpenAPI spec
|
||||
type readonlyReferenceWalker struct {
|
||||
// walkRefCallback will be called on each reference. The input will never be nil.
|
||||
walkRefCallback func(ref *spec.Ref)
|
||||
|
||||
// The spec to walk through.
|
||||
root *spec.Swagger
|
||||
}
|
||||
|
||||
// walkOnAllReferences recursively walks on all references, while following references into definitions.
|
||||
// it calls walkRef on each found reference.
|
||||
func walkOnAllReferences(walkRef func(ref *spec.Ref), root *spec.Swagger) {
|
||||
alreadyVisited := map[string]bool{}
|
||||
|
||||
walker := &readonlyReferenceWalker{
|
||||
root: root,
|
||||
}
|
||||
walker.walkRefCallback = func(ref *spec.Ref) {
|
||||
walkRef(ref)
|
||||
|
||||
refStr := ref.String()
|
||||
if refStr == "" || !strings.HasPrefix(refStr, definitionPrefix) {
|
||||
return
|
||||
}
|
||||
defName := refStr[len(definitionPrefix):]
|
||||
|
||||
if _, found := root.Definitions[defName]; found && !alreadyVisited[refStr] {
|
||||
alreadyVisited[refStr] = true
|
||||
def := root.Definitions[defName]
|
||||
walker.walkSchema(&def)
|
||||
}
|
||||
}
|
||||
walker.Start()
|
||||
}
|
||||
|
||||
func (s *readonlyReferenceWalker) walkSchema(schema *spec.Schema) {
|
||||
if schema == nil {
|
||||
return
|
||||
}
|
||||
s.walkRefCallback(&schema.Ref)
|
||||
var v *spec.Schema
|
||||
if len(schema.Definitions)+len(schema.Properties)+len(schema.PatternProperties) > 0 {
|
||||
v = &spec.Schema{}
|
||||
}
|
||||
for k := range schema.Definitions {
|
||||
*v = schema.Definitions[k]
|
||||
s.walkSchema(v)
|
||||
}
|
||||
for k := range schema.Properties {
|
||||
*v = schema.Properties[k]
|
||||
s.walkSchema(v)
|
||||
}
|
||||
for k := range schema.PatternProperties {
|
||||
*v = schema.PatternProperties[k]
|
||||
s.walkSchema(v)
|
||||
}
|
||||
for i := range schema.AllOf {
|
||||
s.walkSchema(&schema.AllOf[i])
|
||||
}
|
||||
for i := range schema.AnyOf {
|
||||
s.walkSchema(&schema.AnyOf[i])
|
||||
}
|
||||
for i := range schema.OneOf {
|
||||
s.walkSchema(&schema.OneOf[i])
|
||||
}
|
||||
if schema.Not != nil {
|
||||
s.walkSchema(schema.Not)
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
s.walkSchema(schema.AdditionalProperties.Schema)
|
||||
}
|
||||
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
|
||||
s.walkSchema(schema.AdditionalItems.Schema)
|
||||
}
|
||||
if schema.Items != nil {
|
||||
if schema.Items.Schema != nil {
|
||||
s.walkSchema(schema.Items.Schema)
|
||||
}
|
||||
for i := range schema.Items.Schemas {
|
||||
s.walkSchema(&schema.Items.Schemas[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *readonlyReferenceWalker) walkParams(params []spec.Parameter) {
|
||||
if params == nil {
|
||||
return
|
||||
}
|
||||
for _, param := range params {
|
||||
s.walkRefCallback(¶m.Ref)
|
||||
s.walkSchema(param.Schema)
|
||||
if param.Items != nil {
|
||||
s.walkRefCallback(¶m.Items.Ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *readonlyReferenceWalker) walkResponse(resp *spec.Response) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
s.walkRefCallback(&resp.Ref)
|
||||
s.walkSchema(resp.Schema)
|
||||
}
|
||||
|
||||
func (s *readonlyReferenceWalker) walkOperation(op *spec.Operation) {
|
||||
if op == nil {
|
||||
return
|
||||
}
|
||||
s.walkParams(op.Parameters)
|
||||
if op.Responses == nil {
|
||||
return
|
||||
}
|
||||
s.walkResponse(op.Responses.Default)
|
||||
for _, r := range op.Responses.StatusCodeResponses {
|
||||
s.walkResponse(&r)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *readonlyReferenceWalker) Start() {
|
||||
if s.root.Paths == nil {
|
||||
return
|
||||
}
|
||||
for _, pathItem := range s.root.Paths.Paths {
|
||||
s.walkParams(pathItem.Parameters)
|
||||
s.walkOperation(pathItem.Delete)
|
||||
s.walkOperation(pathItem.Get)
|
||||
s.walkOperation(pathItem.Head)
|
||||
s.walkOperation(pathItem.Options)
|
||||
s.walkOperation(pathItem.Patch)
|
||||
s.walkOperation(pathItem.Post)
|
||||
s.walkOperation(pathItem.Put)
|
||||
}
|
||||
}
|
||||
2
vendor/k8s.io/kube-openapi/pkg/builder/openapi.go
generated
vendored
2
vendor/k8s.io/kube-openapi/pkg/builder/openapi.go
generated
vendored
@@ -152,7 +152,7 @@ func (o *openAPI) finalizeSwagger() (*spec.Swagger, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return o.swagger, nil
|
||||
return deduplicateParameters(o.swagger)
|
||||
}
|
||||
|
||||
func (o *openAPI) buildDefinitionRecursively(name string) error {
|
||||
|
||||
259
vendor/k8s.io/kube-openapi/pkg/builder/parameters.go
generated
vendored
Normal file
259
vendor/k8s.io/kube-openapi/pkg/builder/parameters.go
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
Copyright 2023 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 builder
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// deduplicateParameters finds parameters that are shared across multiple endpoints and replace them with
|
||||
// references to the shared parameters in order to avoid repetition.
|
||||
//
|
||||
// deduplicateParameters does not mutate the source.
|
||||
func deduplicateParameters(sp *spec.Swagger) (*spec.Swagger, error) {
|
||||
names, parameters, err := collectSharedParameters(sp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sp.Parameters != nil {
|
||||
return nil, fmt.Errorf("shared parameters already exist") // should not happen with the builder, but to be sure
|
||||
}
|
||||
|
||||
clone := *sp
|
||||
clone.Parameters = parameters
|
||||
return replaceSharedParameters(names, &clone)
|
||||
}
|
||||
|
||||
// collectSharedParameters finds parameters that show up for many endpoints. These
|
||||
// are basically all parameters with the exceptions of those where we know they are
|
||||
// endpoint specific, e.g. because they reference the schema of the kind, or have
|
||||
// the kind or resource name in the description.
|
||||
func collectSharedParameters(sp *spec.Swagger) (namesByJSON map[string]string, ret map[string]spec.Parameter, err error) {
|
||||
if sp == nil || sp.Paths == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
countsByJSON := map[string]int{}
|
||||
shared := map[string]spec.Parameter{}
|
||||
var keys []string
|
||||
|
||||
collect := func(p *spec.Parameter) error {
|
||||
if (p.In == "query" || p.In == "path") && p.Name == "name" {
|
||||
return nil // ignore name parameter as they are never shared with the Kind in the description
|
||||
}
|
||||
if p.In == "query" && p.Name == "fieldValidation" {
|
||||
return nil // keep fieldValidation parameter unshared because kubectl uses it (until 1.27) to detect server-side field validation support
|
||||
}
|
||||
if p.In == "query" && p.Name == "dryRun" {
|
||||
return nil // keep fieldValidation parameter unshared because kubectl uses it (until 1.26) to detect dry-run support
|
||||
}
|
||||
if p.Schema != nil && p.In == "body" && p.Name == "body" && !strings.HasPrefix(p.Schema.Ref.String(), "#/definitions/io.k8s.apimachinery") {
|
||||
return nil // ignore non-generic body parameters as they reference the custom schema of the kind
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k := string(bs)
|
||||
countsByJSON[k]++
|
||||
if count := countsByJSON[k]; count == 1 {
|
||||
shared[k] = *p
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, path := range sp.Paths.Paths {
|
||||
// per operation parameters
|
||||
for _, op := range operations(&path) {
|
||||
if op == nil {
|
||||
continue // shouldn't happen, but ignore if it does; tested through unit test
|
||||
}
|
||||
for _, p := range op.Parameters {
|
||||
if p.Ref.String() != "" {
|
||||
// shouldn't happen, but ignore if it does
|
||||
continue
|
||||
}
|
||||
if err := collect(&p); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// per path parameters
|
||||
for _, p := range path.Parameters {
|
||||
if p.Ref.String() != "" {
|
||||
continue // shouldn't happen, but ignore if it does
|
||||
}
|
||||
if err := collect(&p); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// name deterministically
|
||||
sort.Strings(keys)
|
||||
ret = map[string]spec.Parameter{}
|
||||
namesByJSON = map[string]string{}
|
||||
for _, k := range keys {
|
||||
name := shared[k].Name
|
||||
if name == "" {
|
||||
// this should never happen as the name is a required field. But if it does, let's be safe.
|
||||
name = "param"
|
||||
}
|
||||
name += "-" + base64Hash(k)
|
||||
i := 0
|
||||
for {
|
||||
if _, ok := ret[name]; !ok {
|
||||
ret[name] = shared[k]
|
||||
namesByJSON[k] = name
|
||||
break
|
||||
}
|
||||
i++ // only on hash conflict, unlikely with our few variants
|
||||
name = shared[k].Name + "-" + strconv.Itoa(i)
|
||||
}
|
||||
}
|
||||
|
||||
return namesByJSON, ret, nil
|
||||
}
|
||||
|
||||
func operations(path *spec.PathItem) []*spec.Operation {
|
||||
return []*spec.Operation{path.Get, path.Put, path.Post, path.Delete, path.Options, path.Head, path.Patch}
|
||||
}
|
||||
|
||||
func base64Hash(s string) string {
|
||||
hash := fnv.New64()
|
||||
hash.Write([]byte(s)) //nolint:errcheck
|
||||
return base64.URLEncoding.EncodeToString(hash.Sum(make([]byte, 0, 8))[:6]) // 8 characters
|
||||
}
|
||||
|
||||
func replaceSharedParameters(sharedParameterNamesByJSON map[string]string, sp *spec.Swagger) (*spec.Swagger, error) {
|
||||
if sp == nil || sp.Paths == nil {
|
||||
return sp, nil
|
||||
}
|
||||
|
||||
ret := sp
|
||||
|
||||
firstPathChange := true
|
||||
for k, path := range sp.Paths.Paths {
|
||||
pathChanged := false
|
||||
|
||||
// per operation parameters
|
||||
for _, op := range []**spec.Operation{&path.Get, &path.Put, &path.Post, &path.Delete, &path.Options, &path.Head, &path.Patch} {
|
||||
if *op == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
firstParamChange := true
|
||||
for i := range (*op).Parameters {
|
||||
p := (*op).Parameters[i]
|
||||
|
||||
if p.Ref.String() != "" {
|
||||
// shouldn't happen, but be idem-potent if it does
|
||||
continue
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if name, ok := sharedParameterNamesByJSON[string(bs)]; ok {
|
||||
if firstParamChange {
|
||||
orig := *op
|
||||
*op = &spec.Operation{}
|
||||
**op = *orig
|
||||
(*op).Parameters = make([]spec.Parameter, len(orig.Parameters))
|
||||
copy((*op).Parameters, orig.Parameters)
|
||||
firstParamChange = false
|
||||
}
|
||||
|
||||
(*op).Parameters[i] = spec.Parameter{
|
||||
Refable: spec.Refable{
|
||||
Ref: spec.MustCreateRef("#/parameters/" + name),
|
||||
},
|
||||
}
|
||||
pathChanged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// per path parameters
|
||||
firstParamChange := true
|
||||
for i := range path.Parameters {
|
||||
p := path.Parameters[i]
|
||||
|
||||
if p.Ref.String() != "" {
|
||||
// shouldn't happen, but be idem-potent if it does
|
||||
continue
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if name, ok := sharedParameterNamesByJSON[string(bs)]; ok {
|
||||
if firstParamChange {
|
||||
orig := path.Parameters
|
||||
path.Parameters = make([]spec.Parameter, len(orig))
|
||||
copy(path.Parameters, orig)
|
||||
firstParamChange = false
|
||||
}
|
||||
|
||||
path.Parameters[i] = spec.Parameter{
|
||||
Refable: spec.Refable{
|
||||
Ref: spec.MustCreateRef("#/parameters/" + name),
|
||||
},
|
||||
}
|
||||
pathChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
if pathChanged {
|
||||
if firstPathChange {
|
||||
clone := *sp
|
||||
ret = &clone
|
||||
|
||||
pathsClone := *ret.Paths
|
||||
ret.Paths = &pathsClone
|
||||
|
||||
ret.Paths.Paths = make(map[string]spec.PathItem, len(sp.Paths.Paths))
|
||||
for k, v := range sp.Paths.Paths {
|
||||
ret.Paths.Paths[k] = v
|
||||
}
|
||||
|
||||
firstPathChange = false
|
||||
}
|
||||
ret.Paths.Paths[k] = path
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
14
vendor/k8s.io/kube-openapi/pkg/builder3/openapi.go
generated
vendored
14
vendor/k8s.io/kube-openapi/pkg/builder3/openapi.go
generated
vendored
@@ -156,7 +156,9 @@ func (o *openAPI) buildRequestBody(parameters []common.Parameter, consumes []str
|
||||
}
|
||||
r := &spec3.RequestBody{
|
||||
RequestBodyProps: spec3.RequestBodyProps{
|
||||
Content: map[string]*spec3.MediaType{},
|
||||
Content: map[string]*spec3.MediaType{},
|
||||
Description: param.Description(),
|
||||
Required: param.Required(),
|
||||
},
|
||||
}
|
||||
for _, consume := range consumes {
|
||||
@@ -172,9 +174,9 @@ func (o *openAPI) buildRequestBody(parameters []common.Parameter, consumes []str
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func newOpenAPI(config *common.Config) openAPI {
|
||||
func newOpenAPI(config *common.OpenAPIV3Config) openAPI {
|
||||
o := openAPI{
|
||||
config: common.ConvertConfigToV3(config),
|
||||
config: config,
|
||||
spec: &spec3.OpenAPI{
|
||||
Version: "3.0.0",
|
||||
Info: config.Info,
|
||||
@@ -313,12 +315,12 @@ func (o *openAPI) buildOpenAPISpec(webServices []common.RouteContainer) error {
|
||||
// BuildOpenAPISpec builds OpenAPI v3 spec given a list of route containers and common.Config to customize it.
|
||||
//
|
||||
// Deprecated: BuildOpenAPISpecFromRoutes should be used instead.
|
||||
func BuildOpenAPISpec(webServices []*restful.WebService, config *common.Config) (*spec3.OpenAPI, error) {
|
||||
func BuildOpenAPISpec(webServices []*restful.WebService, config *common.OpenAPIV3Config) (*spec3.OpenAPI, error) {
|
||||
return BuildOpenAPISpecFromRoutes(restfuladapter.AdaptWebServices(webServices), config)
|
||||
}
|
||||
|
||||
// BuildOpenAPISpecFromRoutes builds OpenAPI v3 spec given a list of route containers and common.Config to customize it.
|
||||
func BuildOpenAPISpecFromRoutes(webServices []common.RouteContainer, config *common.Config) (*spec3.OpenAPI, error) {
|
||||
func BuildOpenAPISpecFromRoutes(webServices []common.RouteContainer, config *common.OpenAPIV3Config) (*spec3.OpenAPI, error) {
|
||||
a := newOpenAPI(config)
|
||||
err := a.buildOpenAPISpec(webServices)
|
||||
if err != nil {
|
||||
@@ -330,7 +332,7 @@ func BuildOpenAPISpecFromRoutes(webServices []common.RouteContainer, config *com
|
||||
// BuildOpenAPIDefinitionsForResource builds a partial OpenAPI spec given a sample object and common.Config to customize it.
|
||||
// BuildOpenAPIDefinitionsForResources returns the OpenAPI spec which includes the definitions for the
|
||||
// passed type names.
|
||||
func BuildOpenAPIDefinitionsForResources(config *common.Config, names ...string) (map[string]*spec.Schema, error) {
|
||||
func BuildOpenAPIDefinitionsForResources(config *common.OpenAPIV3Config, names ...string) (map[string]*spec.Schema, error) {
|
||||
o := newOpenAPI(config)
|
||||
// We can discard the return value of toSchema because all we care about is the side effect of calling it.
|
||||
// All the models created for this resource get added to o.swagger.Definitions
|
||||
|
||||
290
vendor/k8s.io/kube-openapi/pkg/cached/cache.go
generated
vendored
Normal file
290
vendor/k8s.io/kube-openapi/pkg/cached/cache.go
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
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 cached provides a cache mechanism based on etags to lazily
|
||||
// build, and/or cache results from expensive operation such that those
|
||||
// operations are not repeated unnecessarily. The operations can be
|
||||
// created as a tree, and replaced dynamically as needed.
|
||||
//
|
||||
// All the operations in this module are thread-safe.
|
||||
//
|
||||
// # Dependencies and types of caches
|
||||
//
|
||||
// This package uses a source/transform/sink model of caches to build
|
||||
// the dependency tree, and can be used as follows:
|
||||
// - [Func]: A source cache that recomputes the content every time.
|
||||
// - [Once]: A source cache that always produces the
|
||||
// same content, it is only called once.
|
||||
// - [Transform]: A cache that transforms data from one format to
|
||||
// another. It's only refreshed when the source changes.
|
||||
// - [Merge]: A cache that aggregates multiple caches in a map into one.
|
||||
// It's only refreshed when the source changes.
|
||||
// - [MergeList]: A cache that aggregates multiple caches in a list into one.
|
||||
// It's only refreshed when the source changes.
|
||||
// - [Atomic]: A cache adapter that atomically replaces the source with a new one.
|
||||
// - [LastSuccess]: A cache adapter that caches the last successful and returns
|
||||
// it if the next call fails. It extends [Atomic].
|
||||
//
|
||||
// # Etags
|
||||
//
|
||||
// Etags in this library is a cache version identifier. It doesn't
|
||||
// necessarily strictly match to the semantics of http `etags`, but are
|
||||
// somewhat inspired from it and function with the same principles.
|
||||
// Hashing the content is a good way to guarantee that your function is
|
||||
// never going to be called spuriously. In Kubernetes world, this could
|
||||
// be a `resourceVersion`, this can be an actual etag, a hash, a UUID
|
||||
// (if the cache always changes), or even a made-up string when the
|
||||
// content of the cache never changes.
|
||||
package cached
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Value is wrapping a value behind a getter for lazy evaluation.
|
||||
type Value[T any] interface {
|
||||
Get() (value T, etag string, err error)
|
||||
}
|
||||
|
||||
// Result is wrapping T and error into a struct for cases where a tuple is more
|
||||
// convenient or necessary in Golang.
|
||||
type Result[T any] struct {
|
||||
Value T
|
||||
Etag string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (r Result[T]) Get() (T, string, error) {
|
||||
return r.Value, r.Etag, r.Err
|
||||
}
|
||||
|
||||
// Func wraps a (thread-safe) function as a Value[T].
|
||||
func Func[T any](fn func() (T, string, error)) Value[T] {
|
||||
return valueFunc[T](fn)
|
||||
}
|
||||
|
||||
type valueFunc[T any] func() (T, string, error)
|
||||
|
||||
func (c valueFunc[T]) Get() (T, string, error) {
|
||||
return c()
|
||||
}
|
||||
|
||||
// Static returns constant values.
|
||||
func Static[T any](value T, etag string) Value[T] {
|
||||
return Result[T]{Value: value, Etag: etag}
|
||||
}
|
||||
|
||||
// Merge merges a of cached values. The merge function only gets called if any of
|
||||
// the dependency has changed.
|
||||
//
|
||||
// If any of the dependency returned an error before, or any of the
|
||||
// dependency returned an error this time, or if the mergeFn failed
|
||||
// before, then the function is run again.
|
||||
//
|
||||
// Note that this assumes there is no "partial" merge, the merge
|
||||
// function will remerge all the dependencies together everytime. Since
|
||||
// the list of dependencies is constant, there is no way to save some
|
||||
// partial merge information either.
|
||||
//
|
||||
// Also note that Golang map iteration is not stable. If the mergeFn
|
||||
// depends on the order iteration to be stable, it will need to
|
||||
// implement its own sorting or iteration order.
|
||||
func Merge[K comparable, T, V any](mergeFn func(results map[K]Result[T]) (V, string, error), caches map[K]Value[T]) Value[V] {
|
||||
list := make([]Value[T], 0, len(caches))
|
||||
|
||||
// map from index to key
|
||||
indexes := make(map[int]K, len(caches))
|
||||
i := 0
|
||||
for k := range caches {
|
||||
list = append(list, caches[k])
|
||||
indexes[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
return MergeList(func(results []Result[T]) (V, string, error) {
|
||||
if len(results) != len(indexes) {
|
||||
panic(fmt.Errorf("invalid result length %d, expected %d", len(results), len(indexes)))
|
||||
}
|
||||
m := make(map[K]Result[T], len(results))
|
||||
for i := range results {
|
||||
m[indexes[i]] = results[i]
|
||||
}
|
||||
return mergeFn(m)
|
||||
}, list)
|
||||
}
|
||||
|
||||
// MergeList merges a list of cached values. The function only gets called if
|
||||
// any of the dependency has changed.
|
||||
//
|
||||
// The benefit of ListMerger over the basic Merger is that caches are
|
||||
// stored in an ordered list so the order of the cache will be
|
||||
// preserved in the order of the results passed to the mergeFn.
|
||||
//
|
||||
// If any of the dependency returned an error before, or any of the
|
||||
// dependency returned an error this time, or if the mergeFn failed
|
||||
// before, then the function is reran.
|
||||
//
|
||||
// Note that this assumes there is no "partial" merge, the merge
|
||||
// function will remerge all the dependencies together everytime. Since
|
||||
// the list of dependencies is constant, there is no way to save some
|
||||
// partial merge information either.
|
||||
func MergeList[T, V any](mergeFn func(results []Result[T]) (V, string, error), delegates []Value[T]) Value[V] {
|
||||
return &listMerger[T, V]{
|
||||
mergeFn: mergeFn,
|
||||
delegates: delegates,
|
||||
}
|
||||
}
|
||||
|
||||
type listMerger[T, V any] struct {
|
||||
lock sync.Mutex
|
||||
mergeFn func([]Result[T]) (V, string, error)
|
||||
delegates []Value[T]
|
||||
cache []Result[T]
|
||||
result Result[V]
|
||||
}
|
||||
|
||||
func (c *listMerger[T, V]) prepareResultsLocked() []Result[T] {
|
||||
cacheResults := make([]Result[T], len(c.delegates))
|
||||
ch := make(chan struct {
|
||||
int
|
||||
Result[T]
|
||||
}, len(c.delegates))
|
||||
for i := range c.delegates {
|
||||
go func(index int) {
|
||||
value, etag, err := c.delegates[index].Get()
|
||||
ch <- struct {
|
||||
int
|
||||
Result[T]
|
||||
}{index, Result[T]{Value: value, Etag: etag, Err: err}}
|
||||
}(i)
|
||||
}
|
||||
for i := 0; i < len(c.delegates); i++ {
|
||||
res := <-ch
|
||||
cacheResults[res.int] = res.Result
|
||||
}
|
||||
return cacheResults
|
||||
}
|
||||
|
||||
func (c *listMerger[T, V]) needsRunningLocked(results []Result[T]) bool {
|
||||
if c.cache == nil {
|
||||
return true
|
||||
}
|
||||
if c.result.Err != nil {
|
||||
return true
|
||||
}
|
||||
if len(results) != len(c.cache) {
|
||||
panic(fmt.Errorf("invalid number of results: %v (expected %v)", len(results), len(c.cache)))
|
||||
}
|
||||
for i, oldResult := range c.cache {
|
||||
newResult := results[i]
|
||||
if newResult.Etag != oldResult.Etag || newResult.Err != nil || oldResult.Err != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *listMerger[T, V]) Get() (V, string, error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
cacheResults := c.prepareResultsLocked()
|
||||
if c.needsRunningLocked(cacheResults) {
|
||||
c.cache = cacheResults
|
||||
c.result.Value, c.result.Etag, c.result.Err = c.mergeFn(c.cache)
|
||||
}
|
||||
return c.result.Value, c.result.Etag, c.result.Err
|
||||
}
|
||||
|
||||
// Transform the result of another cached value. The transformFn will only be called
|
||||
// if the source has updated, otherwise, the result will be returned.
|
||||
//
|
||||
// If the dependency returned an error before, or it returns an error
|
||||
// this time, or if the transformerFn failed before, the function is
|
||||
// reran.
|
||||
func Transform[T, V any](transformerFn func(T, string, error) (V, string, error), source Value[T]) Value[V] {
|
||||
return MergeList(func(delegates []Result[T]) (V, string, error) {
|
||||
if len(delegates) != 1 {
|
||||
panic(fmt.Errorf("invalid cache for transformer cache: %v", delegates))
|
||||
}
|
||||
return transformerFn(delegates[0].Value, delegates[0].Etag, delegates[0].Err)
|
||||
}, []Value[T]{source})
|
||||
}
|
||||
|
||||
// Once calls Value[T].Get() lazily and only once, even in case of an error result.
|
||||
func Once[T any](d Value[T]) Value[T] {
|
||||
return &once[T]{
|
||||
data: d,
|
||||
}
|
||||
}
|
||||
|
||||
type once[T any] struct {
|
||||
once sync.Once
|
||||
data Value[T]
|
||||
result Result[T]
|
||||
}
|
||||
|
||||
func (c *once[T]) Get() (T, string, error) {
|
||||
c.once.Do(func() {
|
||||
c.result.Value, c.result.Etag, c.result.Err = c.data.Get()
|
||||
})
|
||||
return c.result.Value, c.result.Etag, c.result.Err
|
||||
}
|
||||
|
||||
// Replaceable extends the Value[T] interface with the ability to change the
|
||||
// underlying Value[T] after construction.
|
||||
type Replaceable[T any] interface {
|
||||
Value[T]
|
||||
Store(Value[T])
|
||||
}
|
||||
|
||||
// Atomic wraps a Value[T] as an atomic value that can be replaced. It implements
|
||||
// Replaceable[T].
|
||||
type Atomic[T any] struct {
|
||||
value atomic.Pointer[Value[T]]
|
||||
}
|
||||
|
||||
var _ Replaceable[[]byte] = &Atomic[[]byte]{}
|
||||
|
||||
func (x *Atomic[T]) Store(val Value[T]) { x.value.Store(&val) }
|
||||
func (x *Atomic[T]) Get() (T, string, error) { return (*x.value.Load()).Get() }
|
||||
|
||||
// LastSuccess calls Value[T].Get(), but hides errors by returning the last
|
||||
// success if there has been any.
|
||||
type LastSuccess[T any] struct {
|
||||
Atomic[T]
|
||||
success atomic.Pointer[Result[T]]
|
||||
}
|
||||
|
||||
var _ Replaceable[[]byte] = &LastSuccess[[]byte]{}
|
||||
|
||||
func (c *LastSuccess[T]) Get() (T, string, error) {
|
||||
success := c.success.Load()
|
||||
value, etag, err := c.Atomic.Get()
|
||||
if err == nil {
|
||||
if success == nil {
|
||||
c.success.CompareAndSwap(nil, &Result[T]{Value: value, Etag: etag, Err: err})
|
||||
}
|
||||
return value, etag, err
|
||||
}
|
||||
|
||||
if success != nil {
|
||||
return success.Value, success.Etag, success.Err
|
||||
}
|
||||
|
||||
return value, etag, err
|
||||
}
|
||||
38
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
38
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
@@ -22,7 +22,6 @@ import (
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/openapiconv"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
@@ -172,43 +171,6 @@ type OpenAPIV3Config struct {
|
||||
DefaultSecurity []map[string][]string
|
||||
}
|
||||
|
||||
// ConvertConfigToV3 converts a Config object to an OpenAPIV3Config object
|
||||
func ConvertConfigToV3(config *Config) *OpenAPIV3Config {
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v3Config := &OpenAPIV3Config{
|
||||
Info: config.Info,
|
||||
IgnorePrefixes: config.IgnorePrefixes,
|
||||
GetDefinitions: config.GetDefinitions,
|
||||
GetOperationIDAndTags: config.GetOperationIDAndTags,
|
||||
GetOperationIDAndTagsFromRoute: config.GetOperationIDAndTagsFromRoute,
|
||||
GetDefinitionName: config.GetDefinitionName,
|
||||
Definitions: config.Definitions,
|
||||
SecuritySchemes: make(spec3.SecuritySchemes),
|
||||
DefaultSecurity: config.DefaultSecurity,
|
||||
DefaultResponse: openapiconv.ConvertResponse(config.DefaultResponse, []string{"application/json"}),
|
||||
|
||||
CommonResponses: make(map[int]*spec3.Response),
|
||||
ResponseDefinitions: make(map[string]*spec3.Response),
|
||||
}
|
||||
|
||||
if config.SecurityDefinitions != nil {
|
||||
for s, securityScheme := range *config.SecurityDefinitions {
|
||||
v3Config.SecuritySchemes[s] = openapiconv.ConvertSecurityScheme(securityScheme)
|
||||
}
|
||||
}
|
||||
for k, commonResponse := range config.CommonResponses {
|
||||
v3Config.CommonResponses[k] = openapiconv.ConvertResponse(&commonResponse, []string{"application/json"})
|
||||
}
|
||||
|
||||
for k, responseDefinition := range config.ResponseDefinitions {
|
||||
v3Config.ResponseDefinitions[k] = openapiconv.ConvertResponse(&responseDefinition, []string{"application/json"})
|
||||
}
|
||||
return v3Config
|
||||
}
|
||||
|
||||
type typeInfo struct {
|
||||
name string
|
||||
format string
|
||||
|
||||
104
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
104
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
@@ -26,6 +26,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
defaultergen "k8s.io/gengo/examples/defaulter-gen/generators"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
@@ -120,7 +121,7 @@ func newOpenAPIGen(sanitizedName string, targetPackage string) generator.Generat
|
||||
DefaultGen: generator.DefaultGen{
|
||||
OptionalName: sanitizedName,
|
||||
},
|
||||
imports: generator.NewImportTracker(),
|
||||
imports: generator.NewImportTrackerForPackage(targetPackage),
|
||||
targetPackage: targetPackage,
|
||||
}
|
||||
}
|
||||
@@ -553,23 +554,83 @@ func (g openAPITypeWriter) validatePatchTags(m *types.Member, parent *types.Type
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultFromComments(comments []string) (interface{}, error) {
|
||||
tag, err := getSingleTagsValue(comments, tagDefault)
|
||||
func defaultFromComments(comments []string, commentPath string, t *types.Type) (interface{}, *types.Name, error) {
|
||||
var tag string
|
||||
|
||||
for {
|
||||
var err error
|
||||
tag, err = getSingleTagsValue(comments, tagDefault)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if t == nil || len(tag) > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
comments = t.CommentLines
|
||||
commentPath = t.Name.Package
|
||||
switch t.Kind {
|
||||
case types.Pointer:
|
||||
t = t.Elem
|
||||
case types.Alias:
|
||||
t = t.Underlying
|
||||
default:
|
||||
t = nil
|
||||
}
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
return nil, err
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
var i interface{}
|
||||
if err := json.Unmarshal([]byte(tag), &i); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal default: %v", err)
|
||||
if id, ok := defaultergen.ParseSymbolReference(tag, commentPath); ok {
|
||||
klog.Errorf("%v, %v", id, commentPath)
|
||||
return nil, &id, nil
|
||||
} else if err := json.Unmarshal([]byte(tag), &i); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal default: %v", err)
|
||||
}
|
||||
return i, nil, nil
|
||||
}
|
||||
|
||||
func implementsCustomUnmarshalling(t *types.Type) bool {
|
||||
switch t.Kind {
|
||||
case types.Pointer:
|
||||
unmarshaller, isUnmarshaller := t.Elem.Methods["UnmarshalJSON"]
|
||||
return isUnmarshaller && unmarshaller.Signature.Receiver.Kind == types.Pointer
|
||||
case types.Struct:
|
||||
_, isUnmarshaller := t.Methods["UnmarshalJSON"]
|
||||
return isUnmarshaller
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func mustEnforceDefault(t *types.Type, omitEmpty bool) (interface{}, error) {
|
||||
// Treat types with custom unmarshalling as a value
|
||||
// (Can be alias, struct, or pointer)
|
||||
if implementsCustomUnmarshalling(t) {
|
||||
// Since Go JSON deserializer always feeds `null` when present
|
||||
// to structs with custom UnmarshalJSON, the zero value for
|
||||
// these structs is also null.
|
||||
//
|
||||
// In general, Kubernetes API types with custom marshalling should
|
||||
// marshal their empty values to `null`.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch t.Kind {
|
||||
case types.Alias:
|
||||
return mustEnforceDefault(t.Underlying, omitEmpty)
|
||||
case types.Pointer, types.Map, types.Slice, types.Array, types.Interface:
|
||||
return nil, nil
|
||||
case types.Struct:
|
||||
if len(t.Members) == 1 && t.Members[0].Embedded {
|
||||
// Treat a struct with a single embedded member the same as an alias
|
||||
return mustEnforceDefault(t.Members[0].Type, omitEmpty)
|
||||
}
|
||||
|
||||
return map[string]interface{}{}, nil
|
||||
case types.Builtin:
|
||||
if !omitEmpty {
|
||||
@@ -585,9 +646,8 @@ func mustEnforceDefault(t *types.Type, omitEmpty bool) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateDefault(comments []string, t *types.Type, omitEmpty bool) error {
|
||||
t = resolveAliasAndEmbeddedType(t)
|
||||
def, err := defaultFromComments(comments)
|
||||
func (g openAPITypeWriter) generateDefault(comments []string, t *types.Type, omitEmpty bool, commentOwningType *types.Type) error {
|
||||
def, ref, err := defaultFromComments(comments, commentOwningType.Name.Package, t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -603,6 +663,8 @@ func (g openAPITypeWriter) generateDefault(comments []string, t *types.Type, omi
|
||||
}
|
||||
if def != nil {
|
||||
g.Do("Default: $.$,\n", fmt.Sprintf("%#v", def))
|
||||
} else if ref != nil {
|
||||
g.Do("Default: $.|raw$,\n", &types.Type{Name: *ref})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -676,7 +738,7 @@ func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type)
|
||||
return nil
|
||||
}
|
||||
omitEmpty := strings.Contains(reflect.StructTag(m.Tags).Get("json"), "omitempty")
|
||||
if err := g.generateDefault(m.CommentLines, m.Type, omitEmpty); err != nil {
|
||||
if err := g.generateDefault(m.CommentLines, m.Type, omitEmpty, parent); err != nil {
|
||||
return fmt.Errorf("failed to generate default in %v: %v: %v", parent, m.Name, err)
|
||||
}
|
||||
t := resolveAliasAndPtrType(m.Type)
|
||||
@@ -721,22 +783,6 @@ func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) {
|
||||
g.Do("Ref: ref(\"$.$\"),\n", t.Name.String())
|
||||
}
|
||||
|
||||
func resolveAliasAndEmbeddedType(t *types.Type) *types.Type {
|
||||
var prev *types.Type
|
||||
for prev != t {
|
||||
prev = t
|
||||
if t.Kind == types.Alias {
|
||||
t = t.Underlying
|
||||
}
|
||||
if t.Kind == types.Struct {
|
||||
if len(t.Members) == 1 && t.Members[0].Embedded {
|
||||
t = t.Members[0].Type
|
||||
}
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func resolveAliasAndPtrType(t *types.Type) *types.Type {
|
||||
var prev *types.Type
|
||||
for prev != t {
|
||||
@@ -762,7 +808,7 @@ func (g openAPITypeWriter) generateMapProperty(t *types.Type) error {
|
||||
|
||||
g.Do("Type: []string{\"object\"},\n", nil)
|
||||
g.Do("AdditionalProperties: &spec.SchemaOrBool{\nAllows: true,\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
|
||||
if err := g.generateDefault(t.Elem.CommentLines, t.Elem, false); err != nil {
|
||||
if err := g.generateDefault(t.Elem.CommentLines, t.Elem, false, t.Elem); err != nil {
|
||||
return err
|
||||
}
|
||||
typeString, format := openapi.OpenAPITypeFormat(elemType.String())
|
||||
@@ -795,7 +841,7 @@ func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error {
|
||||
elemType := resolveAliasAndPtrType(t.Elem)
|
||||
g.Do("Type: []string{\"array\"},\n", nil)
|
||||
g.Do("Items: &spec.SchemaOrArray{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
|
||||
if err := g.generateDefault(t.Elem.CommentLines, t.Elem, false); err != nil {
|
||||
if err := g.generateDefault(t.Elem.CommentLines, t.Elem, false, t.Elem); err != nil {
|
||||
return err
|
||||
}
|
||||
typeString, format := openapi.OpenAPITypeFormat(elemType.String())
|
||||
|
||||
142
vendor/k8s.io/kube-openapi/pkg/handler/handler.go
generated
vendored
142
vendor/k8s.io/kube-openapi/pkg/handler/handler.go
generated
vendored
@@ -22,19 +22,20 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/golang/protobuf/proto"
|
||||
openapi_v2 "github.com/google/gnostic/openapiv2"
|
||||
openapi_v2 "github.com/google/gnostic-models/openapiv2"
|
||||
"github.com/google/uuid"
|
||||
"github.com/munnerz/goautoneg"
|
||||
|
||||
klog "k8s.io/klog/v2"
|
||||
"k8s.io/kube-openapi/pkg/builder"
|
||||
"k8s.io/kube-openapi/pkg/cached"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/common/restfuladapter"
|
||||
"k8s.io/kube-openapi/pkg/internal/handler"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -51,81 +52,62 @@ func computeETag(data []byte) string {
|
||||
return fmt.Sprintf("%X", sha512.Sum512(data))
|
||||
}
|
||||
|
||||
type timedSpec struct {
|
||||
spec []byte
|
||||
lastModified time.Time
|
||||
}
|
||||
|
||||
// OpenAPIService is the service responsible for serving OpenAPI spec. It has
|
||||
// the ability to safely change the spec while serving it.
|
||||
type OpenAPIService struct {
|
||||
// rwMutex protects All members of this service.
|
||||
rwMutex sync.RWMutex
|
||||
|
||||
lastModified time.Time
|
||||
|
||||
jsonCache handler.HandlerCache
|
||||
protoCache handler.HandlerCache
|
||||
etagCache handler.HandlerCache
|
||||
specCache cached.LastSuccess[*spec.Swagger]
|
||||
jsonCache cached.Value[timedSpec]
|
||||
protoCache cached.Value[timedSpec]
|
||||
}
|
||||
|
||||
// NewOpenAPIService builds an OpenAPIService starting with the given spec.
|
||||
func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
|
||||
func NewOpenAPIService(swagger *spec.Swagger) *OpenAPIService {
|
||||
return NewOpenAPIServiceLazy(cached.Static(swagger, uuid.New().String()))
|
||||
}
|
||||
|
||||
// NewOpenAPIServiceLazy builds an OpenAPIService from lazy spec.
|
||||
func NewOpenAPIServiceLazy(swagger cached.Value[*spec.Swagger]) *OpenAPIService {
|
||||
o := &OpenAPIService{}
|
||||
if err := o.UpdateSpec(spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
o.UpdateSpecLazy(swagger)
|
||||
|
||||
func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time, error) {
|
||||
o.rwMutex.RLock()
|
||||
defer o.rwMutex.RUnlock()
|
||||
specBytes, err := o.jsonCache.Get()
|
||||
if err != nil {
|
||||
return nil, "", time.Time{}, err
|
||||
}
|
||||
etagBytes, err := o.etagCache.Get()
|
||||
if err != nil {
|
||||
return nil, "", time.Time{}, err
|
||||
}
|
||||
return specBytes, string(etagBytes), o.lastModified, nil
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time, error) {
|
||||
o.rwMutex.RLock()
|
||||
defer o.rwMutex.RUnlock()
|
||||
specPb, err := o.protoCache.Get()
|
||||
if err != nil {
|
||||
return nil, "", time.Time{}, err
|
||||
}
|
||||
etagBytes, err := o.etagCache.Get()
|
||||
if err != nil {
|
||||
return nil, "", time.Time{}, err
|
||||
}
|
||||
return specPb, string(etagBytes), o.lastModified, nil
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
|
||||
o.rwMutex.Lock()
|
||||
defer o.rwMutex.Unlock()
|
||||
o.jsonCache = o.jsonCache.New(func() ([]byte, error) {
|
||||
return openapiSpec.MarshalJSON()
|
||||
})
|
||||
o.protoCache = o.protoCache.New(func() ([]byte, error) {
|
||||
json, err := o.jsonCache.Get()
|
||||
o.jsonCache = cached.Transform[*spec.Swagger](func(spec *spec.Swagger, etag string, err error) (timedSpec, string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
return ToProtoBinary(json)
|
||||
})
|
||||
o.etagCache = o.etagCache.New(func() ([]byte, error) {
|
||||
json, err := o.jsonCache.Get()
|
||||
json, err := spec.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
return []byte(computeETag(json)), nil
|
||||
})
|
||||
o.lastModified = time.Now()
|
||||
return timedSpec{spec: json, lastModified: time.Now()}, computeETag(json), nil
|
||||
}, &o.specCache)
|
||||
o.protoCache = cached.Transform(func(ts timedSpec, etag string, err error) (timedSpec, string, error) {
|
||||
if err != nil {
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
proto, err := ToProtoBinary(ts.spec)
|
||||
if err != nil {
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
// We can re-use the same etag as json because of the Vary header.
|
||||
return timedSpec{spec: proto, lastModified: ts.lastModified}, etag, nil
|
||||
}, o.jsonCache)
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) UpdateSpec(swagger *spec.Swagger) error {
|
||||
o.UpdateSpecLazy(cached.Static(swagger, uuid.New().String()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) UpdateSpecLazy(swagger cached.Value[*spec.Swagger]) {
|
||||
o.specCache.Store(swagger)
|
||||
}
|
||||
|
||||
func ToProtoBinary(json []byte) ([]byte, error) {
|
||||
document, err := openapi_v2.ParseDocument(json)
|
||||
if err != nil {
|
||||
@@ -137,25 +119,23 @@ func ToProtoBinary(json []byte) ([]byte, error) {
|
||||
// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
|
||||
//
|
||||
// Deprecated: use OpenAPIService.RegisterOpenAPIVersionedService instead.
|
||||
func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
|
||||
o, err := NewOpenAPIService(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return o, o.RegisterOpenAPIVersionedService(servePath, handler)
|
||||
func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handler common.PathHandler) *OpenAPIService {
|
||||
o := NewOpenAPIService(spec)
|
||||
o.RegisterOpenAPIVersionedService(servePath, handler)
|
||||
return o
|
||||
}
|
||||
|
||||
// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
|
||||
func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handler common.PathHandler) error {
|
||||
func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handler common.PathHandler) {
|
||||
accepted := []struct {
|
||||
Type string
|
||||
SubType string
|
||||
ReturnedContentType string
|
||||
GetDataAndETag func() ([]byte, string, time.Time, error)
|
||||
GetDataAndEtag cached.Value[timedSpec]
|
||||
}{
|
||||
{"application", subTypeJSON, "application/" + subTypeJSON, o.getSwaggerBytes},
|
||||
{"application", subTypeProtobufDeprecated, "application/" + subTypeProtobuf, o.getSwaggerPbBytes},
|
||||
{"application", subTypeProtobuf, "application/" + subTypeProtobuf, o.getSwaggerPbBytes},
|
||||
{"application", subTypeJSON, "application/" + subTypeJSON, o.jsonCache},
|
||||
{"application", subTypeProtobufDeprecated, "application/" + subTypeProtobuf, o.protoCache},
|
||||
{"application", subTypeProtobuf, "application/" + subTypeProtobuf, o.protoCache},
|
||||
}
|
||||
|
||||
handler.Handle(servePath, gziphandler.GzipHandler(http.HandlerFunc(
|
||||
@@ -175,11 +155,11 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
|
||||
continue
|
||||
}
|
||||
// serve the first matching media type in the sorted clause list
|
||||
data, etag, lastModified, err := accepts.GetDataAndETag()
|
||||
ts, etag, err := accepts.GetDataAndEtag.Get()
|
||||
if err != nil {
|
||||
klog.Errorf("Error in OpenAPI handler: %s", err)
|
||||
// only return a 503 if we have no older cache data to serve
|
||||
if data == nil {
|
||||
if ts.spec == nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
@@ -190,7 +170,7 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
|
||||
// ETag must be enclosed in double quotes: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
|
||||
w.Header().Set("Etag", strconv.Quote(etag))
|
||||
// ServeContent will take care of caching using eTag.
|
||||
http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data))
|
||||
http.ServeContent(w, r, servePath, ts.lastModified, bytes.NewReader(ts.spec))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -199,8 +179,6 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
|
||||
return
|
||||
}),
|
||||
))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildAndRegisterOpenAPIVersionedService builds the spec and registers a handler to provide access to it.
|
||||
@@ -218,9 +196,7 @@ func BuildAndRegisterOpenAPIVersionedServiceFromRoutes(servePath string, routeCo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o, err := NewOpenAPIService(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return o, o.RegisterOpenAPIVersionedService(servePath, handler)
|
||||
o := NewOpenAPIService(spec)
|
||||
o.RegisterOpenAPIVersionedService(servePath, handler)
|
||||
return o, nil
|
||||
}
|
||||
|
||||
222
vendor/k8s.io/kube-openapi/pkg/handler3/handler.go
generated
vendored
222
vendor/k8s.io/kube-openapi/pkg/handler3/handler.go
generated
vendored
@@ -24,19 +24,20 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
openapi_v3 "github.com/google/gnostic/openapiv3"
|
||||
openapi_v3 "github.com/google/gnostic-models/openapiv3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/munnerz/goautoneg"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kube-openapi/pkg/cached"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/internal/handler"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,23 +59,63 @@ type OpenAPIV3DiscoveryGroupVersion struct {
|
||||
ServerRelativeURL string `json:"serverRelativeURL"`
|
||||
}
|
||||
|
||||
func ToV3ProtoBinary(json []byte) ([]byte, error) {
|
||||
document, err := openapi_v3.ParseDocument(json)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proto.Marshal(document)
|
||||
}
|
||||
|
||||
type timedSpec struct {
|
||||
spec []byte
|
||||
lastModified time.Time
|
||||
}
|
||||
|
||||
// This type is protected by the lock on OpenAPIService.
|
||||
type openAPIV3Group struct {
|
||||
specCache cached.LastSuccess[*spec3.OpenAPI]
|
||||
pbCache cached.Value[timedSpec]
|
||||
jsonCache cached.Value[timedSpec]
|
||||
}
|
||||
|
||||
func newOpenAPIV3Group() *openAPIV3Group {
|
||||
o := &openAPIV3Group{}
|
||||
o.jsonCache = cached.Transform[*spec3.OpenAPI](func(spec *spec3.OpenAPI, etag string, err error) (timedSpec, string, error) {
|
||||
if err != nil {
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
json, err := json.Marshal(spec)
|
||||
if err != nil {
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
return timedSpec{spec: json, lastModified: time.Now()}, computeETag(json), nil
|
||||
}, &o.specCache)
|
||||
o.pbCache = cached.Transform(func(ts timedSpec, etag string, err error) (timedSpec, string, error) {
|
||||
if err != nil {
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
proto, err := ToV3ProtoBinary(ts.spec)
|
||||
if err != nil {
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
return timedSpec{spec: proto, lastModified: ts.lastModified}, etag, nil
|
||||
}, o.jsonCache)
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *openAPIV3Group) UpdateSpec(openapi cached.Value[*spec3.OpenAPI]) {
|
||||
o.specCache.Store(openapi)
|
||||
}
|
||||
|
||||
// OpenAPIService is the service responsible for serving OpenAPI spec. It has
|
||||
// the ability to safely change the spec while serving it.
|
||||
type OpenAPIService struct {
|
||||
// rwMutex protects All members of this service.
|
||||
rwMutex sync.RWMutex
|
||||
lastModified time.Time
|
||||
v3Schema map[string]*OpenAPIV3Group
|
||||
}
|
||||
// Mutex protects the schema map.
|
||||
mutex sync.Mutex
|
||||
v3Schema map[string]*openAPIV3Group
|
||||
|
||||
type OpenAPIV3Group struct {
|
||||
rwMutex sync.RWMutex
|
||||
|
||||
lastModified time.Time
|
||||
|
||||
pbCache handler.HandlerCache
|
||||
jsonCache handler.HandlerCache
|
||||
etagCache handler.HandlerCache
|
||||
discoveryCache cached.LastSuccess[timedSpec]
|
||||
}
|
||||
|
||||
func computeETag(data []byte) string {
|
||||
@@ -93,94 +134,90 @@ func constructServerRelativeURL(gvString, etag string) string {
|
||||
}
|
||||
|
||||
// NewOpenAPIService builds an OpenAPIService starting with the given spec.
|
||||
func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
|
||||
func NewOpenAPIService() *OpenAPIService {
|
||||
o := &OpenAPIService{}
|
||||
o.v3Schema = make(map[string]*OpenAPIV3Group)
|
||||
return o, nil
|
||||
o.v3Schema = make(map[string]*openAPIV3Group)
|
||||
// We're not locked because we haven't shared the structure yet.
|
||||
o.discoveryCache.Store(o.buildDiscoveryCacheLocked())
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) getGroupBytes() ([]byte, error) {
|
||||
o.rwMutex.RLock()
|
||||
defer o.rwMutex.RUnlock()
|
||||
keys := make([]string, len(o.v3Schema))
|
||||
i := 0
|
||||
for k := range o.v3Schema {
|
||||
keys[i] = k
|
||||
i++
|
||||
func (o *OpenAPIService) buildDiscoveryCacheLocked() cached.Value[timedSpec] {
|
||||
caches := make(map[string]cached.Value[timedSpec], len(o.v3Schema))
|
||||
for gvName, group := range o.v3Schema {
|
||||
caches[gvName] = group.jsonCache
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
discovery := &OpenAPIV3Discovery{Paths: make(map[string]OpenAPIV3DiscoveryGroupVersion)}
|
||||
for gvString, groupVersion := range o.v3Schema {
|
||||
etagBytes, err := groupVersion.etagCache.Get()
|
||||
return cached.Merge(func(results map[string]cached.Result[timedSpec]) (timedSpec, string, error) {
|
||||
discovery := &OpenAPIV3Discovery{Paths: make(map[string]OpenAPIV3DiscoveryGroupVersion)}
|
||||
for gvName, result := range results {
|
||||
if result.Err != nil {
|
||||
return timedSpec{}, "", result.Err
|
||||
}
|
||||
discovery.Paths[gvName] = OpenAPIV3DiscoveryGroupVersion{
|
||||
ServerRelativeURL: constructServerRelativeURL(gvName, result.Etag),
|
||||
}
|
||||
}
|
||||
j, err := json.Marshal(discovery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return timedSpec{}, "", err
|
||||
}
|
||||
discovery.Paths[gvString] = OpenAPIV3DiscoveryGroupVersion{
|
||||
ServerRelativeURL: constructServerRelativeURL(gvString, string(etagBytes)),
|
||||
}
|
||||
}
|
||||
j, err := json.Marshal(discovery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return j, nil
|
||||
return timedSpec{spec: j, lastModified: time.Now()}, computeETag(j), nil
|
||||
}, caches)
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) getSingleGroupBytes(getType string, group string) ([]byte, string, time.Time, error) {
|
||||
o.rwMutex.RLock()
|
||||
defer o.rwMutex.RUnlock()
|
||||
o.mutex.Lock()
|
||||
defer o.mutex.Unlock()
|
||||
v, ok := o.v3Schema[group]
|
||||
if !ok {
|
||||
return nil, "", time.Now(), fmt.Errorf("Cannot find CRD group %s", group)
|
||||
}
|
||||
if getType == subTypeJSON {
|
||||
specBytes, err := v.jsonCache.Get()
|
||||
if err != nil {
|
||||
return nil, "", v.lastModified, err
|
||||
}
|
||||
etagBytes, err := v.etagCache.Get()
|
||||
return specBytes, string(etagBytes), v.lastModified, err
|
||||
} else if getType == subTypeProtobuf || getType == subTypeProtobufDeprecated {
|
||||
specPb, err := v.pbCache.Get()
|
||||
if err != nil {
|
||||
return nil, "", v.lastModified, err
|
||||
}
|
||||
etagBytes, err := v.etagCache.Get()
|
||||
return specPb, string(etagBytes), v.lastModified, err
|
||||
switch getType {
|
||||
case subTypeJSON:
|
||||
ts, etag, err := v.jsonCache.Get()
|
||||
return ts.spec, etag, ts.lastModified, err
|
||||
case subTypeProtobuf, subTypeProtobufDeprecated:
|
||||
ts, etag, err := v.pbCache.Get()
|
||||
return ts.spec, etag, ts.lastModified, err
|
||||
default:
|
||||
return nil, "", time.Now(), fmt.Errorf("Invalid accept clause %s", getType)
|
||||
}
|
||||
return nil, "", time.Now(), fmt.Errorf("Invalid accept clause %s", getType)
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) UpdateGroupVersion(group string, openapi *spec3.OpenAPI) (err error) {
|
||||
o.rwMutex.Lock()
|
||||
defer o.rwMutex.Unlock()
|
||||
|
||||
// UpdateGroupVersionLazy adds or updates an existing group with the new cached.
|
||||
func (o *OpenAPIService) UpdateGroupVersionLazy(group string, openapi cached.Value[*spec3.OpenAPI]) {
|
||||
o.mutex.Lock()
|
||||
defer o.mutex.Unlock()
|
||||
if _, ok := o.v3Schema[group]; !ok {
|
||||
o.v3Schema[group] = &OpenAPIV3Group{}
|
||||
o.v3Schema[group] = newOpenAPIV3Group()
|
||||
// Since there is a new item, we need to re-build the cache map.
|
||||
o.discoveryCache.Store(o.buildDiscoveryCacheLocked())
|
||||
}
|
||||
return o.v3Schema[group].UpdateSpec(openapi)
|
||||
o.v3Schema[group].UpdateSpec(openapi)
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) UpdateGroupVersion(group string, openapi *spec3.OpenAPI) {
|
||||
o.UpdateGroupVersionLazy(group, cached.Static(openapi, uuid.New().String()))
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) DeleteGroupVersion(group string) {
|
||||
o.rwMutex.Lock()
|
||||
defer o.rwMutex.Unlock()
|
||||
o.mutex.Lock()
|
||||
defer o.mutex.Unlock()
|
||||
delete(o.v3Schema, group)
|
||||
}
|
||||
|
||||
func ToV3ProtoBinary(json []byte) ([]byte, error) {
|
||||
document, err := openapi_v3.ParseDocument(json)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proto.Marshal(document)
|
||||
// Rebuild the merge cache map since the items have changed.
|
||||
o.discoveryCache.Store(o.buildDiscoveryCacheLocked())
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) HandleDiscovery(w http.ResponseWriter, r *http.Request) {
|
||||
data, _ := o.getGroupBytes()
|
||||
w.Header().Set("Etag", strconv.Quote(computeETag(data)))
|
||||
ts, etag, err := o.discoveryCache.Get()
|
||||
if err != nil {
|
||||
klog.Errorf("Error serving discovery: %s", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Etag", strconv.Quote(etag))
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
http.ServeContent(w, r, "/openapi/v3", time.Now(), bytes.NewReader(data))
|
||||
http.ServeContent(w, r, "/openapi/v3", ts.lastModified, bytes.NewReader(ts.spec))
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) HandleGroupVersion(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -256,30 +293,3 @@ func (o *OpenAPIService) RegisterOpenAPIV3VersionedService(servePath string, han
|
||||
handler.HandlePrefix(servePath+"/", http.HandlerFunc(o.HandleGroupVersion))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *OpenAPIV3Group) UpdateSpec(openapi *spec3.OpenAPI) (err error) {
|
||||
o.rwMutex.Lock()
|
||||
defer o.rwMutex.Unlock()
|
||||
|
||||
o.jsonCache = o.jsonCache.New(func() ([]byte, error) {
|
||||
return json.Marshal(openapi)
|
||||
})
|
||||
o.pbCache = o.pbCache.New(func() ([]byte, error) {
|
||||
json, err := o.jsonCache.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ToV3ProtoBinary(json)
|
||||
})
|
||||
// TODO: This forces a json marshal of corresponding group-versions.
|
||||
// We should look to replace this with a faster hashing mechanism.
|
||||
o.etagCache = o.etagCache.New(func() ([]byte, error) {
|
||||
json, err := o.jsonCache.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(computeETag(json)), nil
|
||||
})
|
||||
o.lastModified = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
3
vendor/k8s.io/kube-openapi/pkg/internal/flags.go
generated
vendored
3
vendor/k8s.io/kube-openapi/pkg/internal/flags.go
generated
vendored
@@ -18,7 +18,8 @@ package internal
|
||||
|
||||
// Used by tests to selectively disable experimental JSON unmarshaler
|
||||
var UseOptimizedJSONUnmarshaling bool = true
|
||||
var UseOptimizedJSONUnmarshalingV3 bool = false
|
||||
var UseOptimizedJSONUnmarshalingV3 bool = true
|
||||
|
||||
// Used by tests to selectively disable experimental JSON marshaler
|
||||
var UseOptimizedJSONMarshaling bool = true
|
||||
var UseOptimizedJSONMarshalingV3 bool = true
|
||||
|
||||
57
vendor/k8s.io/kube-openapi/pkg/internal/handler/handler_cache.go
generated
vendored
57
vendor/k8s.io/kube-openapi/pkg/internal/handler/handler_cache.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 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 handler
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// HandlerCache represents a lazy cache for generating a byte array
|
||||
// It is used to lazily marshal OpenAPI v2/v3 and lazily generate the ETag
|
||||
type HandlerCache struct {
|
||||
BuildCache func() ([]byte, error)
|
||||
once sync.Once
|
||||
bytes []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// Get either returns the cached value or calls BuildCache() once before caching and returning
|
||||
// its results. If BuildCache returns an error, the last valid value for the cache (from prior
|
||||
// calls to New()) is used instead if possible.
|
||||
func (c *HandlerCache) Get() ([]byte, error) {
|
||||
c.once.Do(func() {
|
||||
bytes, err := c.BuildCache()
|
||||
// if there is an error updating the cache, there can be situations where
|
||||
// c.bytes contains a valid value (carried over from the previous update)
|
||||
// but c.err is also not nil; the cache user is expected to check for this
|
||||
c.err = err
|
||||
if c.err == nil {
|
||||
// don't override previous spec if we had an error
|
||||
c.bytes = bytes
|
||||
}
|
||||
})
|
||||
return c.bytes, c.err
|
||||
}
|
||||
|
||||
// New creates a new HandlerCache for situations where a cache refresh is needed.
|
||||
// This function is not thread-safe and should not be called at the same time as Get().
|
||||
func (c *HandlerCache) New(cacheBuilder func() ([]byte, error)) HandlerCache {
|
||||
return HandlerCache{
|
||||
bytes: c.bytes,
|
||||
BuildCache: cacheBuilder,
|
||||
}
|
||||
}
|
||||
41
vendor/k8s.io/kube-openapi/pkg/spec3/encoding.go
generated
vendored
41
vendor/k8s.io/kube-openapi/pkg/spec3/encoding.go
generated
vendored
@@ -18,7 +18,10 @@ package spec3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -29,6 +32,9 @@ type Encoding struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Encoding as JSON
|
||||
func (e *Encoding) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(e)
|
||||
}
|
||||
b1, err := json.Marshal(e.EncodingProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -40,7 +46,20 @@ func (e *Encoding) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
}
|
||||
|
||||
func (e *Encoding) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
EncodingProps encodingPropsOmitZero `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Extensions = internal.SanitizeExtensions(e.Extensions)
|
||||
x.EncodingProps = encodingPropsOmitZero(e.EncodingProps)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (e *Encoding) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, e)
|
||||
}
|
||||
if err := json.Unmarshal(data, &e.EncodingProps); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,6 +69,20 @@ func (e *Encoding) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoding) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
EncodingProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
e.EncodingProps = x.EncodingProps
|
||||
return nil
|
||||
}
|
||||
|
||||
type EncodingProps struct {
|
||||
// Content Type for encoding a specific property
|
||||
ContentType string `json:"contentType,omitempty"`
|
||||
@@ -62,3 +95,11 @@ type EncodingProps struct {
|
||||
// AllowReserved determines whether the parameter value SHOULD allow reserved characters, as defined by RFC3986
|
||||
AllowReserved bool `json:"allowReserved,omitempty"`
|
||||
}
|
||||
|
||||
type encodingPropsOmitZero struct {
|
||||
ContentType string `json:"contentType,omitempty"`
|
||||
Headers map[string]*Header `json:"headers,omitempty"`
|
||||
Style string `json:"style,omitempty"`
|
||||
Explode bool `json:"explode,omitzero"`
|
||||
AllowReserved bool `json:"allowReserved,omitzero"`
|
||||
}
|
||||
|
||||
37
vendor/k8s.io/kube-openapi/pkg/spec3/example.go
generated
vendored
37
vendor/k8s.io/kube-openapi/pkg/spec3/example.go
generated
vendored
@@ -20,6 +20,9 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -33,6 +36,9 @@ type Example struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode RequestBody as JSON
|
||||
func (e *Example) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(e)
|
||||
}
|
||||
b1, err := json.Marshal(e.Refable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -47,8 +53,22 @@ func (e *Example) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
func (e *Example) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
ExampleProps `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Ref = e.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(e.Extensions)
|
||||
x.ExampleProps = e.ExampleProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (e *Example) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, e)
|
||||
}
|
||||
if err := json.Unmarshal(data, &e.Refable); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -61,6 +81,23 @@ func (e *Example) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Example) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
ExampleProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := internal.JSONRefFromMap(&e.Ref.Ref, x.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
e.ExampleProps = x.ExampleProps
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ExampleProps struct {
|
||||
// Summary holds a short description of the example
|
||||
Summary string `json:"summary,omitempty"`
|
||||
|
||||
32
vendor/k8s.io/kube-openapi/pkg/spec3/external_documentation.go
generated
vendored
32
vendor/k8s.io/kube-openapi/pkg/spec3/external_documentation.go
generated
vendored
@@ -18,7 +18,10 @@ package spec3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -36,6 +39,9 @@ type ExternalDocumentationProps struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Responses as JSON
|
||||
func (e *ExternalDocumentation) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(e)
|
||||
}
|
||||
b1, err := json.Marshal(e.ExternalDocumentationProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -47,7 +53,20 @@ func (e *ExternalDocumentation) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
}
|
||||
|
||||
func (e *ExternalDocumentation) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
ExternalDocumentationProps `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Extensions = internal.SanitizeExtensions(e.Extensions)
|
||||
x.ExternalDocumentationProps = e.ExternalDocumentationProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (e *ExternalDocumentation) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, e)
|
||||
}
|
||||
if err := json.Unmarshal(data, &e.ExternalDocumentationProps); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -56,3 +75,16 @@ func (e *ExternalDocumentation) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ExternalDocumentation) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
ExternalDocumentationProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
e.ExternalDocumentationProps = x.ExternalDocumentationProps
|
||||
return nil
|
||||
}
|
||||
|
||||
281
vendor/k8s.io/kube-openapi/pkg/spec3/fuzz.go
generated
vendored
Normal file
281
vendor/k8s.io/kube-openapi/pkg/spec3/fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
package spec3
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
fuzz "github.com/google/gofuzz"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// refChance is the chance that a particular component will use a $ref
|
||||
// instead of fuzzed. Expressed as a fraction 1/n, currently there is
|
||||
// a 1/3 chance that a ref will be used.
|
||||
const refChance = 3
|
||||
|
||||
const alphaNumChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
func randAlphanumString() string {
|
||||
arr := make([]string, rand.Intn(10)+5)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
arr[i] = string(alphaNumChars[rand.Intn(len(alphaNumChars))])
|
||||
}
|
||||
return strings.Join(arr, "")
|
||||
}
|
||||
|
||||
var OpenAPIV3FuzzFuncs []interface{} = []interface{}{
|
||||
func(s *string, c fuzz.Continue) {
|
||||
// All OpenAPI V3 map keys must follow the corresponding
|
||||
// regex. Note that this restricts the range for all other
|
||||
// string values as well.
|
||||
str := randAlphanumString()
|
||||
*s = str
|
||||
},
|
||||
func(o *OpenAPI, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(o)
|
||||
o.Version = "3.0.0"
|
||||
for i, val := range o.SecurityRequirement {
|
||||
if val == nil {
|
||||
o.SecurityRequirement[i] = make(map[string][]string)
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
if v == nil {
|
||||
val[k] = make([]string, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
func(r *interface{}, c fuzz.Continue) {
|
||||
switch c.Intn(3) {
|
||||
case 0:
|
||||
*r = nil
|
||||
case 1:
|
||||
n := c.RandString() + "x"
|
||||
*r = n
|
||||
case 2:
|
||||
n := c.Float64()
|
||||
*r = n
|
||||
}
|
||||
},
|
||||
func(v **spec.Info, c fuzz.Continue) {
|
||||
// Info is never nil
|
||||
*v = &spec.Info{}
|
||||
c.FuzzNoCustom(*v)
|
||||
(*v).Title = c.RandString() + "x"
|
||||
},
|
||||
func(v *Paths, c fuzz.Continue) {
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
num := c.Intn(5)
|
||||
if num > 0 {
|
||||
v.Paths = make(map[string]*Path)
|
||||
}
|
||||
for i := 0; i < num; i++ {
|
||||
val := Path{}
|
||||
c.Fuzz(&val)
|
||||
v.Paths["/"+c.RandString()] = &val
|
||||
}
|
||||
},
|
||||
func(v *SecurityScheme, c fuzz.Continue) {
|
||||
if c.Intn(refChance) == 0 {
|
||||
c.Fuzz(&v.Refable)
|
||||
return
|
||||
}
|
||||
switch c.Intn(4) {
|
||||
case 0:
|
||||
v.Type = "apiKey"
|
||||
v.Name = c.RandString() + "x"
|
||||
switch c.Intn(3) {
|
||||
case 0:
|
||||
v.In = "query"
|
||||
case 1:
|
||||
v.In = "header"
|
||||
case 2:
|
||||
v.In = "cookie"
|
||||
}
|
||||
case 1:
|
||||
v.Type = "http"
|
||||
case 2:
|
||||
v.Type = "oauth2"
|
||||
v.Flows = make(map[string]*OAuthFlow)
|
||||
flow := OAuthFlow{}
|
||||
flow.AuthorizationUrl = c.RandString() + "x"
|
||||
v.Flows["implicit"] = &flow
|
||||
flow.Scopes = make(map[string]string)
|
||||
flow.Scopes["foo"] = "bar"
|
||||
case 3:
|
||||
v.Type = "openIdConnect"
|
||||
v.OpenIdConnectUrl = "https://" + c.RandString()
|
||||
}
|
||||
v.Scheme = "basic"
|
||||
},
|
||||
func(v *spec.Ref, c fuzz.Continue) {
|
||||
switch c.Intn(7) {
|
||||
case 0:
|
||||
*v = spec.MustCreateRef("#/components/schemas/" + randAlphanumString())
|
||||
case 1:
|
||||
*v = spec.MustCreateRef("#/components/responses/" + randAlphanumString())
|
||||
case 2:
|
||||
*v = spec.MustCreateRef("#/components/headers/" + randAlphanumString())
|
||||
case 3:
|
||||
*v = spec.MustCreateRef("#/components/securitySchemes/" + randAlphanumString())
|
||||
case 5:
|
||||
*v = spec.MustCreateRef("#/components/parameters/" + randAlphanumString())
|
||||
case 6:
|
||||
*v = spec.MustCreateRef("#/components/requestBodies/" + randAlphanumString())
|
||||
}
|
||||
},
|
||||
func(v *Parameter, c fuzz.Continue) {
|
||||
if c.Intn(refChance) == 0 {
|
||||
c.Fuzz(&v.Refable)
|
||||
return
|
||||
}
|
||||
c.Fuzz(&v.ParameterProps)
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
|
||||
switch c.Intn(3) {
|
||||
case 0:
|
||||
// Header param
|
||||
v.In = "query"
|
||||
case 1:
|
||||
v.In = "header"
|
||||
case 2:
|
||||
v.In = "cookie"
|
||||
}
|
||||
},
|
||||
func(v *RequestBody, c fuzz.Continue) {
|
||||
if c.Intn(refChance) == 0 {
|
||||
c.Fuzz(&v.Refable)
|
||||
return
|
||||
}
|
||||
c.Fuzz(&v.RequestBodyProps)
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
},
|
||||
func(v *Header, c fuzz.Continue) {
|
||||
if c.Intn(refChance) == 0 {
|
||||
c.Fuzz(&v.Refable)
|
||||
return
|
||||
}
|
||||
c.Fuzz(&v.HeaderProps)
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
},
|
||||
func(v *ResponsesProps, c fuzz.Continue) {
|
||||
c.Fuzz(&v.Default)
|
||||
n := c.Intn(5)
|
||||
for i := 0; i < n; i++ {
|
||||
r2 := Response{}
|
||||
c.Fuzz(&r2)
|
||||
// HTTP Status code in 100-599 Range
|
||||
code := c.Intn(500) + 100
|
||||
v.StatusCodeResponses = make(map[int]*Response)
|
||||
v.StatusCodeResponses[code] = &r2
|
||||
}
|
||||
},
|
||||
func(v *Response, c fuzz.Continue) {
|
||||
if c.Intn(refChance) == 0 {
|
||||
c.Fuzz(&v.Refable)
|
||||
return
|
||||
}
|
||||
c.Fuzz(&v.ResponseProps)
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
},
|
||||
func(v *Operation, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(v)
|
||||
// Do not fuzz null values into the array.
|
||||
for i, val := range v.SecurityRequirement {
|
||||
if val == nil {
|
||||
v.SecurityRequirement[i] = make(map[string][]string)
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
if v == nil {
|
||||
val[k] = make([]string, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
func(v *spec.Extensions, c fuzz.Continue) {
|
||||
numChildren := c.Intn(5)
|
||||
for i := 0; i < numChildren; i++ {
|
||||
if *v == nil {
|
||||
*v = spec.Extensions{}
|
||||
}
|
||||
(*v)["x-"+c.RandString()] = c.RandString()
|
||||
}
|
||||
},
|
||||
func(v *spec.ExternalDocumentation, c fuzz.Continue) {
|
||||
c.Fuzz(&v.Description)
|
||||
v.URL = "https://" + randAlphanumString()
|
||||
},
|
||||
func(v *spec.SchemaURL, c fuzz.Continue) {
|
||||
*v = spec.SchemaURL("https://" + randAlphanumString())
|
||||
},
|
||||
func(v *spec.SchemaOrBool, c fuzz.Continue) {
|
||||
*v = spec.SchemaOrBool{}
|
||||
|
||||
if c.RandBool() {
|
||||
v.Allows = c.RandBool()
|
||||
} else {
|
||||
v.Schema = &spec.Schema{}
|
||||
v.Allows = true
|
||||
c.Fuzz(&v.Schema)
|
||||
}
|
||||
},
|
||||
func(v *spec.SchemaOrArray, c fuzz.Continue) {
|
||||
*v = spec.SchemaOrArray{}
|
||||
if c.RandBool() {
|
||||
schema := spec.Schema{}
|
||||
c.Fuzz(&schema)
|
||||
v.Schema = &schema
|
||||
} else {
|
||||
v.Schemas = []spec.Schema{}
|
||||
numChildren := c.Intn(5)
|
||||
for i := 0; i < numChildren; i++ {
|
||||
schema := spec.Schema{}
|
||||
c.Fuzz(&schema)
|
||||
v.Schemas = append(v.Schemas, schema)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
func(v *spec.SchemaOrStringArray, c fuzz.Continue) {
|
||||
if c.RandBool() {
|
||||
*v = spec.SchemaOrStringArray{}
|
||||
if c.RandBool() {
|
||||
c.Fuzz(&v.Property)
|
||||
} else {
|
||||
c.Fuzz(&v.Schema)
|
||||
}
|
||||
}
|
||||
},
|
||||
func(v *spec.Schema, c fuzz.Continue) {
|
||||
if c.Intn(refChance) == 0 {
|
||||
c.Fuzz(&v.Ref)
|
||||
return
|
||||
}
|
||||
if c.RandBool() {
|
||||
// file schema
|
||||
c.Fuzz(&v.Default)
|
||||
c.Fuzz(&v.Description)
|
||||
c.Fuzz(&v.Example)
|
||||
c.Fuzz(&v.ExternalDocs)
|
||||
|
||||
c.Fuzz(&v.Format)
|
||||
c.Fuzz(&v.ReadOnly)
|
||||
c.Fuzz(&v.Required)
|
||||
c.Fuzz(&v.Title)
|
||||
v.Type = spec.StringOrArray{"file"}
|
||||
|
||||
} else {
|
||||
// normal schema
|
||||
c.Fuzz(&v.SchemaProps)
|
||||
c.Fuzz(&v.SwaggerSchemaProps)
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
c.Fuzz(&v.ExtraProps)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
52
vendor/k8s.io/kube-openapi/pkg/spec3/header.go
generated
vendored
52
vendor/k8s.io/kube-openapi/pkg/spec3/header.go
generated
vendored
@@ -20,6 +20,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -34,6 +36,9 @@ type Header struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Header as JSON
|
||||
func (h *Header) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(h)
|
||||
}
|
||||
b1, err := json.Marshal(h.Refable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -49,7 +54,22 @@ func (h *Header) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
func (h *Header) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
HeaderProps headerPropsOmitZero `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Ref = h.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(h.Extensions)
|
||||
x.HeaderProps = headerPropsOmitZero(h.HeaderProps)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (h *Header) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, h)
|
||||
}
|
||||
if err := json.Unmarshal(data, &h.Refable); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,6 +83,22 @@ func (h *Header) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Header) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
HeaderProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := internal.JSONRefFromMap(&h.Ref.Ref, x.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
h.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
h.HeaderProps = x.HeaderProps
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderProps a struct that describes a header object
|
||||
type HeaderProps struct {
|
||||
// Description holds a brief description of the parameter
|
||||
@@ -88,3 +124,19 @@ type HeaderProps struct {
|
||||
// Examples of the header
|
||||
Examples map[string]*Example `json:"examples,omitempty"`
|
||||
}
|
||||
|
||||
// Marshaling structure only, always edit along with corresponding
|
||||
// struct (or compilation will fail).
|
||||
type headerPropsOmitZero struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Required bool `json:"required,omitzero"`
|
||||
Deprecated bool `json:"deprecated,omitzero"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitzero"`
|
||||
Style string `json:"style,omitempty"`
|
||||
Explode bool `json:"explode,omitzero"`
|
||||
AllowReserved bool `json:"allowReserved,omitzero"`
|
||||
Schema *spec.Schema `json:"schema,omitzero"`
|
||||
Content map[string]*MediaType `json:"content,omitempty"`
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
Examples map[string]*Example `json:"examples,omitempty"`
|
||||
}
|
||||
|
||||
40
vendor/k8s.io/kube-openapi/pkg/spec3/media_type.go
generated
vendored
40
vendor/k8s.io/kube-openapi/pkg/spec3/media_type.go
generated
vendored
@@ -18,7 +18,10 @@ package spec3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -32,6 +35,9 @@ type MediaType struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode MediaType as JSON
|
||||
func (m *MediaType) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(m)
|
||||
}
|
||||
b1, err := json.Marshal(m.MediaTypeProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -43,7 +49,20 @@ func (m *MediaType) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
}
|
||||
|
||||
func (e *MediaType) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
MediaTypeProps mediaTypePropsOmitZero `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Extensions = internal.SanitizeExtensions(e.Extensions)
|
||||
x.MediaTypeProps = mediaTypePropsOmitZero(e.MediaTypeProps)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (m *MediaType) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, m)
|
||||
}
|
||||
if err := json.Unmarshal(data, &m.MediaTypeProps); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -53,6 +72,20 @@ func (m *MediaType) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MediaType) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
MediaTypeProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
m.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
m.MediaTypeProps = x.MediaTypeProps
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MediaTypeProps a struct that allows you to specify content format, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#mediaTypeObject
|
||||
type MediaTypeProps struct {
|
||||
// Schema holds the schema defining the type used for the media type
|
||||
@@ -64,3 +97,10 @@ type MediaTypeProps struct {
|
||||
// A map between a property name and its encoding information. The key, being the property name, MUST exist in the schema as a property. The encoding object SHALL only apply to requestBody objects when the media type is multipart or application/x-www-form-urlencoded
|
||||
Encoding map[string]*Encoding `json:"encoding,omitempty"`
|
||||
}
|
||||
|
||||
type mediaTypePropsOmitZero struct {
|
||||
Schema *spec.Schema `json:"schema,omitzero"`
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
Examples map[string]*Example `json:"examples,omitempty"`
|
||||
Encoding map[string]*Encoding `json:"encoding,omitempty"`
|
||||
}
|
||||
|
||||
45
vendor/k8s.io/kube-openapi/pkg/spec3/operation.go
generated
vendored
45
vendor/k8s.io/kube-openapi/pkg/spec3/operation.go
generated
vendored
@@ -20,6 +20,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -33,6 +35,9 @@ type Operation struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Operation as JSON
|
||||
func (o *Operation) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(o)
|
||||
}
|
||||
b1, err := json.Marshal(o.OperationProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -44,14 +49,40 @@ func (o *Operation) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
}
|
||||
|
||||
func (o *Operation) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
OperationProps operationPropsOmitZero `json:",inline"`
|
||||
}
|
||||
x.Extensions = internal.SanitizeExtensions(o.Extensions)
|
||||
x.OperationProps = operationPropsOmitZero(o.OperationProps)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
// UnmarshalJSON hydrates this items instance with the data from JSON
|
||||
func (o *Operation) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, o)
|
||||
}
|
||||
if err := json.Unmarshal(data, &o.OperationProps); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, &o.VendorExtensible)
|
||||
}
|
||||
|
||||
func (o *Operation) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
OperationProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
o.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
o.OperationProps = x.OperationProps
|
||||
return nil
|
||||
}
|
||||
|
||||
// OperationProps describes a single API operation on a path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#operationObject
|
||||
type OperationProps struct {
|
||||
// Tags holds a list of tags for API documentation control
|
||||
@@ -77,3 +108,17 @@ type OperationProps struct {
|
||||
// Servers contains an alternative server array to service this operation
|
||||
Servers []*Server `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
type operationPropsOmitZero struct {
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitzero"`
|
||||
OperationId string `json:"operationId,omitempty"`
|
||||
Parameters []*Parameter `json:"parameters,omitempty"`
|
||||
RequestBody *RequestBody `json:"requestBody,omitzero"`
|
||||
Responses *Responses `json:"responses,omitzero"`
|
||||
Deprecated bool `json:"deprecated,omitzero"`
|
||||
SecurityRequirement []map[string][]string `json:"security,omitempty"`
|
||||
Servers []*Server `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
53
vendor/k8s.io/kube-openapi/pkg/spec3/parameter.go
generated
vendored
53
vendor/k8s.io/kube-openapi/pkg/spec3/parameter.go
generated
vendored
@@ -20,6 +20,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -34,6 +36,9 @@ type Parameter struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Parameter as JSON
|
||||
func (p *Parameter) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(p)
|
||||
}
|
||||
b1, err := json.Marshal(p.Refable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -49,7 +54,23 @@ func (p *Parameter) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
func (p *Parameter) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
ParameterProps parameterPropsOmitZero `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Ref = p.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(p.Extensions)
|
||||
x.ParameterProps = parameterPropsOmitZero(p.ParameterProps)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (p *Parameter) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, p)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &p.Refable); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,6 +84,22 @@ func (p *Parameter) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parameter) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
ParameterProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := internal.JSONRefFromMap(&p.Ref.Ref, x.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
p.ParameterProps = x.ParameterProps
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParameterProps a struct that describes a single operation parameter, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#parameterObject
|
||||
type ParameterProps struct {
|
||||
// Name holds the name of the parameter
|
||||
@@ -92,3 +129,19 @@ type ParameterProps struct {
|
||||
// Examples of the parameter's potential value. Each example SHOULD contain a value in the correct format as specified in the parameter encoding
|
||||
Examples map[string]*Example `json:"examples,omitempty"`
|
||||
}
|
||||
|
||||
type parameterPropsOmitZero struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
In string `json:"in,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Required bool `json:"required,omitzero"`
|
||||
Deprecated bool `json:"deprecated,omitzero"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitzero"`
|
||||
Style string `json:"style,omitempty"`
|
||||
Explode bool `json:"explode,omitzero"`
|
||||
AllowReserved bool `json:"allowReserved,omitzero"`
|
||||
Schema *spec.Schema `json:"schema,omitzero"`
|
||||
Content map[string]*MediaType `json:"content,omitempty"`
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
Examples map[string]*Example `json:"examples,omitempty"`
|
||||
}
|
||||
|
||||
127
vendor/k8s.io/kube-openapi/pkg/spec3/path.go
generated
vendored
127
vendor/k8s.io/kube-openapi/pkg/spec3/path.go
generated
vendored
@@ -18,9 +18,12 @@ package spec3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -32,19 +35,48 @@ type Paths struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Paths as JSON
|
||||
func (p *Paths) MarshalJSON() ([]byte, error) {
|
||||
b1, err := json.Marshal(p.Paths)
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(p)
|
||||
}
|
||||
b1, err := json.Marshal(p.VendorExtensible)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b2, err := json.Marshal(p.VendorExtensible)
|
||||
|
||||
pths := make(map[string]*Path)
|
||||
for k, v := range p.Paths {
|
||||
if strings.HasPrefix(k, "/") {
|
||||
pths[k] = v
|
||||
}
|
||||
}
|
||||
b2, err := json.Marshal(pths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
concated := swag.ConcatJSON(b1, b2)
|
||||
return concated, nil
|
||||
}
|
||||
|
||||
func (p *Paths) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
m := make(map[string]any, len(p.Extensions)+len(p.Paths))
|
||||
for k, v := range p.Extensions {
|
||||
if internal.IsExtensionKey(k) {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
for k, v := range p.Paths {
|
||||
if strings.HasPrefix(k, "/") {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return opts.MarshalNext(enc, m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON hydrates this items instance with the data from JSON
|
||||
func (p *Paths) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, p)
|
||||
}
|
||||
var res map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &res); err != nil {
|
||||
return err
|
||||
@@ -74,6 +106,59 @@ func (p *Paths) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Paths) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch k := tok.Kind(); k {
|
||||
case 'n':
|
||||
*p = Paths{}
|
||||
return nil
|
||||
case '{':
|
||||
for {
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tok.Kind() == '}' {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch k := tok.String(); {
|
||||
case internal.IsExtensionKey(k):
|
||||
var ext any
|
||||
if err := opts.UnmarshalNext(dec, &ext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Extensions == nil {
|
||||
p.Extensions = make(map[string]any)
|
||||
}
|
||||
p.Extensions[k] = ext
|
||||
case len(k) > 0 && k[0] == '/':
|
||||
pi := Path{}
|
||||
if err := opts.UnmarshalNext(dec, &pi); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Paths == nil {
|
||||
p.Paths = make(map[string]*Path)
|
||||
}
|
||||
p.Paths[k] = &pi
|
||||
default:
|
||||
_, err := dec.ReadValue() // skip value
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown JSON kind: %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
// Path describes the operations available on a single path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject
|
||||
//
|
||||
// Note that this struct is actually a thin wrapper around PathProps to make it referable and extensible
|
||||
@@ -85,6 +170,9 @@ type Path struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Path as JSON
|
||||
func (p *Path) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(p)
|
||||
}
|
||||
b1, err := json.Marshal(p.Refable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,7 +188,22 @@ func (p *Path) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
func (p *Path) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
spec.Extensions
|
||||
PathProps
|
||||
}
|
||||
x.Ref = p.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(p.Extensions)
|
||||
x.PathProps = p.PathProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (p *Path) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, p)
|
||||
}
|
||||
if err := json.Unmarshal(data, &p.Refable); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -113,6 +216,24 @@ func (p *Path) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Path) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
PathProps
|
||||
}
|
||||
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := internal.JSONRefFromMap(&p.Ref.Ref, x.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
p.PathProps = x.PathProps
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PathProps describes the operations available on a single path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject
|
||||
type PathProps struct {
|
||||
// Summary holds a summary for all operations in this path
|
||||
|
||||
42
vendor/k8s.io/kube-openapi/pkg/spec3/request_body.go
generated
vendored
42
vendor/k8s.io/kube-openapi/pkg/spec3/request_body.go
generated
vendored
@@ -20,6 +20,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -34,6 +36,9 @@ type RequestBody struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode RequestBody as JSON
|
||||
func (r *RequestBody) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(r)
|
||||
}
|
||||
b1, err := json.Marshal(r.Refable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -49,7 +54,22 @@ func (r *RequestBody) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
func (r *RequestBody) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
RequestBodyProps requestBodyPropsOmitZero `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Ref = r.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(r.Extensions)
|
||||
x.RequestBodyProps = requestBodyPropsOmitZero(r.RequestBodyProps)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (r *RequestBody) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, r)
|
||||
}
|
||||
if err := json.Unmarshal(data, &r.Refable); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -71,3 +91,25 @@ type RequestBodyProps struct {
|
||||
// Required determines if the request body is required in the request
|
||||
Required bool `json:"required,omitempty"`
|
||||
}
|
||||
|
||||
type requestBodyPropsOmitZero struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Content map[string]*MediaType `json:"content,omitempty"`
|
||||
Required bool `json:"required,omitzero"`
|
||||
}
|
||||
|
||||
func (r *RequestBody) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
RequestBodyProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := internal.JSONRefFromMap(&r.Ref.Ref, x.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
r.RequestBodyProps = x.RequestBodyProps
|
||||
return nil
|
||||
}
|
||||
|
||||
153
vendor/k8s.io/kube-openapi/pkg/spec3/response.go
generated
vendored
153
vendor/k8s.io/kube-openapi/pkg/spec3/response.go
generated
vendored
@@ -18,9 +18,12 @@ package spec3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -34,6 +37,9 @@ type Responses struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Responses as JSON
|
||||
func (r *Responses) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(r)
|
||||
}
|
||||
b1, err := json.Marshal(r.ResponsesProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -45,14 +51,35 @@ func (r *Responses) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
}
|
||||
|
||||
func (r Responses) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
type ArbitraryKeys map[string]interface{}
|
||||
var x struct {
|
||||
ArbitraryKeys
|
||||
Default *Response `json:"default,omitzero"`
|
||||
}
|
||||
x.ArbitraryKeys = make(map[string]any, len(r.Extensions)+len(r.StatusCodeResponses))
|
||||
for k, v := range r.Extensions {
|
||||
if internal.IsExtensionKey(k) {
|
||||
x.ArbitraryKeys[k] = v
|
||||
}
|
||||
}
|
||||
for k, v := range r.StatusCodeResponses {
|
||||
x.ArbitraryKeys[strconv.Itoa(k)] = v
|
||||
}
|
||||
x.Default = r.Default
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (r *Responses) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, r)
|
||||
}
|
||||
if err := json.Unmarshal(data, &r.ResponsesProps); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -78,6 +105,9 @@ func (r ResponsesProps) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON unmarshals responses from JSON
|
||||
func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, r)
|
||||
}
|
||||
var res map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &res); err != nil {
|
||||
return err
|
||||
@@ -106,6 +136,60 @@ func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Responses) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) (err error) {
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch k := tok.Kind(); k {
|
||||
case 'n':
|
||||
*r = Responses{}
|
||||
return nil
|
||||
case '{':
|
||||
for {
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tok.Kind() == '}' {
|
||||
return nil
|
||||
}
|
||||
switch k := tok.String(); {
|
||||
case internal.IsExtensionKey(k):
|
||||
var ext any
|
||||
if err := opts.UnmarshalNext(dec, &ext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.Extensions == nil {
|
||||
r.Extensions = make(map[string]any)
|
||||
}
|
||||
r.Extensions[k] = ext
|
||||
case k == "default":
|
||||
resp := Response{}
|
||||
if err := opts.UnmarshalNext(dec, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
r.ResponsesProps.Default = &resp
|
||||
default:
|
||||
if nk, err := strconv.Atoi(k); err == nil {
|
||||
resp := Response{}
|
||||
if err := opts.UnmarshalNext(dec, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.StatusCodeResponses == nil {
|
||||
r.StatusCodeResponses = map[int]*Response{}
|
||||
}
|
||||
r.StatusCodeResponses[nk] = &resp
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown JSON kind: %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
// Response describes a single response from an API Operation, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject
|
||||
//
|
||||
// Note that this struct is actually a thin wrapper around ResponseProps to make it referable and extensible
|
||||
@@ -117,6 +201,9 @@ type Response struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Response as JSON
|
||||
func (r *Response) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(r)
|
||||
}
|
||||
b1, err := json.Marshal(r.Refable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -132,7 +219,22 @@ func (r *Response) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
func (r Response) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
spec.Extensions
|
||||
ResponseProps `json:",inline"`
|
||||
}
|
||||
x.Ref = r.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(r.Extensions)
|
||||
x.ResponseProps = r.ResponseProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (r *Response) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, r)
|
||||
}
|
||||
if err := json.Unmarshal(data, &r.Refable); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -142,7 +244,22 @@ func (r *Response) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Response) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
ResponseProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := internal.JSONRefFromMap(&r.Ref.Ref, x.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
r.ResponseProps = x.ResponseProps
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -167,6 +284,9 @@ type Link struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Link as JSON
|
||||
func (r *Link) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(r)
|
||||
}
|
||||
b1, err := json.Marshal(r.Refable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -182,7 +302,22 @@ func (r *Link) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
func (r *Link) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
spec.Extensions
|
||||
LinkProps `json:",inline"`
|
||||
}
|
||||
x.Ref = r.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(r.Extensions)
|
||||
x.LinkProps = r.LinkProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (r *Link) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, r)
|
||||
}
|
||||
if err := json.Unmarshal(data, &r.Refable); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -196,6 +331,22 @@ func (r *Link) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Link) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
LinkProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := internal.JSONRefFromMap(&l.Ref.Ref, x.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
l.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
l.LinkProps = x.LinkProps
|
||||
return nil
|
||||
}
|
||||
|
||||
// LinkProps describes a single response from an API Operation, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject
|
||||
type LinkProps struct {
|
||||
// OperationId is the name of an existing, resolvable OAS operation
|
||||
|
||||
17
vendor/k8s.io/kube-openapi/pkg/spec3/security_scheme.go
generated
vendored
17
vendor/k8s.io/kube-openapi/pkg/spec3/security_scheme.go
generated
vendored
@@ -20,6 +20,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -32,6 +34,9 @@ type SecurityScheme struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode SecurityScheme as JSON
|
||||
func (s *SecurityScheme) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(s)
|
||||
}
|
||||
b1, err := json.Marshal(s.SecuritySchemeProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -47,6 +52,18 @@ func (s *SecurityScheme) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
func (s *SecurityScheme) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
SecuritySchemeProps `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Ref = s.Refable.Ref.String()
|
||||
x.Extensions = internal.SanitizeExtensions(s.Extensions)
|
||||
x.SecuritySchemeProps = s.SecuritySchemeProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
// UnmarshalJSON hydrates this items instance with the data from JSON
|
||||
func (s *SecurityScheme) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &s.SecuritySchemeProps); err != nil {
|
||||
|
||||
64
vendor/k8s.io/kube-openapi/pkg/spec3/server.go
generated
vendored
64
vendor/k8s.io/kube-openapi/pkg/spec3/server.go
generated
vendored
@@ -18,7 +18,10 @@ package spec3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -38,6 +41,9 @@ type ServerProps struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Responses as JSON
|
||||
func (s *Server) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(s)
|
||||
}
|
||||
b1, err := json.Marshal(s.ServerProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -49,7 +55,21 @@ func (s *Server) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
}
|
||||
|
||||
func (s *Server) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
ServerProps `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Extensions = internal.SanitizeExtensions(s.Extensions)
|
||||
x.ServerProps = s.ServerProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (s *Server) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, s)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &s.ServerProps); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,6 +79,20 @@ func (s *Server) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
ServerProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
s.ServerProps = x.ServerProps
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServerVariable struct {
|
||||
ServerVariableProps
|
||||
spec.VendorExtensible
|
||||
@@ -75,6 +109,9 @@ type ServerVariableProps struct {
|
||||
|
||||
// MarshalJSON is a custom marshal function that knows how to encode Responses as JSON
|
||||
func (s *ServerVariable) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(s)
|
||||
}
|
||||
b1, err := json.Marshal(s.ServerVariableProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -86,7 +123,20 @@ func (s *ServerVariable) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2), nil
|
||||
}
|
||||
|
||||
func (s *ServerVariable) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
var x struct {
|
||||
ServerVariableProps `json:",inline"`
|
||||
spec.Extensions
|
||||
}
|
||||
x.Extensions = internal.SanitizeExtensions(s.Extensions)
|
||||
x.ServerVariableProps = s.ServerVariableProps
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
func (s *ServerVariable) UnmarshalJSON(data []byte) error {
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, s)
|
||||
}
|
||||
if err := json.Unmarshal(data, &s.ServerVariableProps); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -95,3 +145,17 @@ func (s *ServerVariable) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServerVariable) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
|
||||
var x struct {
|
||||
spec.Extensions
|
||||
ServerVariableProps
|
||||
}
|
||||
if err := opts.UnmarshalNext(dec, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
s.ServerVariableProps = x.ServerVariableProps
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
38
vendor/k8s.io/kube-openapi/pkg/spec3/spec.go
generated
vendored
38
vendor/k8s.io/kube-openapi/pkg/spec3/spec.go
generated
vendored
@@ -17,6 +17,10 @@ limitations under the License.
|
||||
package spec3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/internal"
|
||||
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@@ -32,6 +36,40 @@ type OpenAPI struct {
|
||||
Servers []*Server `json:"servers,omitempty"`
|
||||
// Components hold various schemas for the specification
|
||||
Components *Components `json:"components,omitempty"`
|
||||
// SecurityRequirement holds a declaration of which security mechanisms can be used across the API
|
||||
SecurityRequirement []map[string][]string `json:"security,omitempty"`
|
||||
// ExternalDocs holds additional external documentation
|
||||
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"`
|
||||
}
|
||||
|
||||
func (o *OpenAPI) UnmarshalJSON(data []byte) error {
|
||||
type OpenAPIWithNoFunctions OpenAPI
|
||||
p := (*OpenAPIWithNoFunctions)(o)
|
||||
if internal.UseOptimizedJSONUnmarshalingV3 {
|
||||
return jsonv2.Unmarshal(data, &p)
|
||||
}
|
||||
return json.Unmarshal(data, &p)
|
||||
}
|
||||
|
||||
func (o *OpenAPI) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshalingV3 {
|
||||
return internal.DeterministicMarshal(o)
|
||||
}
|
||||
type OpenAPIWithNoFunctions OpenAPI
|
||||
p := (*OpenAPIWithNoFunctions)(o)
|
||||
return json.Marshal(&p)
|
||||
}
|
||||
|
||||
func (o *OpenAPI) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
type OpenAPIOmitZero struct {
|
||||
Version string `json:"openapi"`
|
||||
Info *spec.Info `json:"info"`
|
||||
Paths *Paths `json:"paths,omitzero"`
|
||||
Servers []*Server `json:"servers,omitempty"`
|
||||
Components *Components `json:"components,omitzero"`
|
||||
SecurityRequirement []map[string][]string `json:"security,omitempty"`
|
||||
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitzero"`
|
||||
}
|
||||
x := (*OpenAPIOmitZero)(o)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
2
vendor/k8s.io/kube-openapi/pkg/util/proto/document.go
generated
vendored
2
vendor/k8s.io/kube-openapi/pkg/util/proto/document.go
generated
vendored
@@ -21,7 +21,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
openapi_v2 "github.com/google/gnostic/openapiv2"
|
||||
openapi_v2 "github.com/google/gnostic-models/openapiv2"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
|
||||
2
vendor/k8s.io/kube-openapi/pkg/util/proto/document_v3.go
generated
vendored
2
vendor/k8s.io/kube-openapi/pkg/util/proto/document_v3.go
generated
vendored
@@ -21,7 +21,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
openapi_v3 "github.com/google/gnostic/openapiv3"
|
||||
openapi_v3 "github.com/google/gnostic-models/openapiv3"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
||||
2
vendor/k8s.io/kube-openapi/pkg/validation/errors/.gitignore
generated
vendored
Normal file
2
vendor/k8s.io/kube-openapi/pkg/validation/errors/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
secrets.yml
|
||||
coverage.out
|
||||
202
vendor/k8s.io/kube-openapi/pkg/validation/errors/LICENSE
generated
vendored
Normal file
202
vendor/k8s.io/kube-openapi/pkg/validation/errors/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
46
vendor/k8s.io/kube-openapi/pkg/validation/errors/api.go
generated
vendored
Normal file
46
vendor/k8s.io/kube-openapi/pkg/validation/errors/api.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Error represents a error interface all swagger framework errors implement
|
||||
type Error interface {
|
||||
error
|
||||
Code() int32
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
code int32
|
||||
message string
|
||||
}
|
||||
|
||||
func (a *apiError) Error() string {
|
||||
return a.message
|
||||
}
|
||||
|
||||
func (a *apiError) Code() int32 {
|
||||
return a.code
|
||||
}
|
||||
|
||||
// New creates a new API error with a code and a message
|
||||
func New(code int32, message string, args ...interface{}) Error {
|
||||
if len(args) > 0 {
|
||||
return &apiError{code, fmt.Sprintf(message, args...)}
|
||||
}
|
||||
return &apiError{code, message}
|
||||
}
|
||||
26
vendor/k8s.io/kube-openapi/pkg/validation/errors/doc.go
generated
vendored
Normal file
26
vendor/k8s.io/kube-openapi/pkg/validation/errors/doc.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 errors provides an Error interface and several concrete types
|
||||
implementing this interface to manage API errors and JSON-schema validation
|
||||
errors.
|
||||
|
||||
A middleware handler ServeError() is provided to serve the errors types
|
||||
it defines.
|
||||
|
||||
It is used throughout the various go-openapi toolkit libraries
|
||||
(https://github.com/go-openapi).
|
||||
*/
|
||||
package errors
|
||||
44
vendor/k8s.io/kube-openapi/pkg/validation/errors/headers.go
generated
vendored
Normal file
44
vendor/k8s.io/kube-openapi/pkg/validation/errors/headers.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 errors
|
||||
|
||||
// Validation represents a failure of a precondition
|
||||
type Validation struct {
|
||||
code int32
|
||||
Name string
|
||||
In string
|
||||
Value interface{}
|
||||
Valid interface{}
|
||||
message string
|
||||
Values []interface{}
|
||||
}
|
||||
|
||||
func (e *Validation) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
// Code the error code
|
||||
func (e *Validation) Code() int32 {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// ValidateName produces an error message name for an aliased property
|
||||
func (e *Validation) ValidateName(name string) *Validation {
|
||||
if e.Name == "" && name != "" {
|
||||
e.Name = name
|
||||
e.message = name + e.message
|
||||
}
|
||||
return e
|
||||
}
|
||||
573
vendor/k8s.io/kube-openapi/pkg/validation/errors/schema.go
generated
vendored
Normal file
573
vendor/k8s.io/kube-openapi/pkg/validation/errors/schema.go
generated
vendored
Normal file
@@ -0,0 +1,573 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
invalidType = "%s is an invalid type name"
|
||||
typeFail = "%s in %s must be of type %s"
|
||||
typeFailWithData = "%s in %s must be of type %s: %q"
|
||||
typeFailWithError = "%s in %s must be of type %s, because: %s"
|
||||
requiredFail = "%s in %s is required"
|
||||
tooLongMessage = "%s in %s should be at most %d chars long"
|
||||
tooShortMessage = "%s in %s should be at least %d chars long"
|
||||
patternFail = "%s in %s should match '%s'"
|
||||
enumFail = "%s in %s should be one of %v"
|
||||
multipleOfFail = "%s in %s should be a multiple of %v"
|
||||
maxIncFail = "%s in %s should be less than or equal to %v"
|
||||
maxExcFail = "%s in %s should be less than %v"
|
||||
minIncFail = "%s in %s should be greater than or equal to %v"
|
||||
minExcFail = "%s in %s should be greater than %v"
|
||||
uniqueFail = "%s in %s shouldn't contain duplicates"
|
||||
maxItemsFail = "%s in %s should have at most %d items"
|
||||
minItemsFail = "%s in %s should have at least %d items"
|
||||
typeFailNoIn = "%s must be of type %s"
|
||||
typeFailWithDataNoIn = "%s must be of type %s: %q"
|
||||
typeFailWithErrorNoIn = "%s must be of type %s, because: %s"
|
||||
requiredFailNoIn = "%s is required"
|
||||
tooLongMessageNoIn = "%s should be at most %d chars long"
|
||||
tooShortMessageNoIn = "%s should be at least %d chars long"
|
||||
patternFailNoIn = "%s should match '%s'"
|
||||
enumFailNoIn = "%s should be one of %v"
|
||||
multipleOfFailNoIn = "%s should be a multiple of %v"
|
||||
maxIncFailNoIn = "%s should be less than or equal to %v"
|
||||
maxExcFailNoIn = "%s should be less than %v"
|
||||
minIncFailNoIn = "%s should be greater than or equal to %v"
|
||||
minExcFailNoIn = "%s should be greater than %v"
|
||||
uniqueFailNoIn = "%s shouldn't contain duplicates"
|
||||
maxItemsFailNoIn = "%s should have at most %d items"
|
||||
minItemsFailNoIn = "%s should have at least %d items"
|
||||
noAdditionalItems = "%s in %s can't have additional items"
|
||||
noAdditionalItemsNoIn = "%s can't have additional items"
|
||||
tooFewProperties = "%s in %s should have at least %d properties"
|
||||
tooFewPropertiesNoIn = "%s should have at least %d properties"
|
||||
tooManyProperties = "%s in %s should have at most %d properties"
|
||||
tooManyPropertiesNoIn = "%s should have at most %d properties"
|
||||
unallowedProperty = "%s.%s in %s is a forbidden property"
|
||||
unallowedPropertyNoIn = "%s.%s is a forbidden property"
|
||||
failedAllPatternProps = "%s.%s in %s failed all pattern properties"
|
||||
failedAllPatternPropsNoIn = "%s.%s failed all pattern properties"
|
||||
multipleOfMustBePositive = "factor MultipleOf declared for %s must be positive: %v"
|
||||
)
|
||||
|
||||
// All code responses can be used to differentiate errors for different handling
|
||||
// by the consuming program
|
||||
const (
|
||||
// CompositeErrorCode remains 422 for backwards-compatibility
|
||||
// and to separate it from validation errors with cause
|
||||
CompositeErrorCode = 422
|
||||
// InvalidTypeCode is used for any subclass of invalid types
|
||||
InvalidTypeCode = 600 + iota
|
||||
RequiredFailCode
|
||||
TooLongFailCode
|
||||
TooShortFailCode
|
||||
PatternFailCode
|
||||
EnumFailCode
|
||||
MultipleOfFailCode
|
||||
MaxFailCode
|
||||
MinFailCode
|
||||
UniqueFailCode
|
||||
MaxItemsFailCode
|
||||
MinItemsFailCode
|
||||
NoAdditionalItemsCode
|
||||
TooFewPropertiesCode
|
||||
TooManyPropertiesCode
|
||||
UnallowedPropertyCode
|
||||
FailedAllPatternPropsCode
|
||||
MultipleOfMustBePositiveCode
|
||||
)
|
||||
|
||||
// CompositeError is an error that groups several errors together
|
||||
type CompositeError struct {
|
||||
Errors []error
|
||||
code int32
|
||||
message string
|
||||
}
|
||||
|
||||
// Code for this error
|
||||
func (c *CompositeError) Code() int32 {
|
||||
return c.code
|
||||
}
|
||||
|
||||
func (c *CompositeError) Error() string {
|
||||
if len(c.Errors) > 0 {
|
||||
msgs := []string{c.message + ":"}
|
||||
for _, e := range c.Errors {
|
||||
msgs = append(msgs, e.Error())
|
||||
}
|
||||
return strings.Join(msgs, "\n")
|
||||
}
|
||||
return c.message
|
||||
}
|
||||
|
||||
// CompositeValidationError an error to wrap a bunch of other errors
|
||||
func CompositeValidationError(errors ...error) *CompositeError {
|
||||
return &CompositeError{
|
||||
code: CompositeErrorCode,
|
||||
Errors: append([]error{}, errors...),
|
||||
message: "validation failure list",
|
||||
}
|
||||
}
|
||||
|
||||
// FailedAllPatternProperties an error for when the property doesn't match a pattern
|
||||
func FailedAllPatternProperties(name, in, key string) *Validation {
|
||||
msg := fmt.Sprintf(failedAllPatternProps, name, key, in)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(failedAllPatternPropsNoIn, name, key)
|
||||
}
|
||||
return &Validation{
|
||||
code: FailedAllPatternPropsCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: key,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// PropertyNotAllowed an error for when the property doesn't match a pattern
|
||||
func PropertyNotAllowed(name, in, key string) *Validation {
|
||||
msg := fmt.Sprintf(unallowedProperty, name, key, in)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(unallowedPropertyNoIn, name, key)
|
||||
}
|
||||
return &Validation{
|
||||
code: UnallowedPropertyCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: key,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// TooFewProperties an error for an object with too few properties
|
||||
func TooFewProperties(name, in string, minProperties, size int64) *Validation {
|
||||
msg := fmt.Sprintf(tooFewProperties, name, in, minProperties)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(tooFewPropertiesNoIn, name, minProperties)
|
||||
}
|
||||
return &Validation{
|
||||
code: TooFewPropertiesCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: size,
|
||||
Valid: minProperties,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// TooManyProperties an error for an object with too many properties
|
||||
func TooManyProperties(name, in string, maxProperties, size int64) *Validation {
|
||||
msg := fmt.Sprintf(tooManyProperties, name, in, maxProperties)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(tooManyPropertiesNoIn, name, maxProperties)
|
||||
}
|
||||
return &Validation{
|
||||
code: TooManyPropertiesCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: size,
|
||||
Valid: maxProperties,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// AdditionalItemsNotAllowed an error for invalid additional items
|
||||
func AdditionalItemsNotAllowed(name, in string) *Validation {
|
||||
msg := fmt.Sprintf(noAdditionalItems, name, in)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(noAdditionalItemsNoIn, name)
|
||||
}
|
||||
return &Validation{
|
||||
code: NoAdditionalItemsCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidCollectionFormat another flavor of invalid type error
|
||||
func InvalidCollectionFormat(name, in, format string) *Validation {
|
||||
return &Validation{
|
||||
code: InvalidTypeCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: format,
|
||||
message: fmt.Sprintf("the collection format %q is not supported for the %s param %q", format, in, name),
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidTypeName an error for when the type is invalid
|
||||
func InvalidTypeName(typeName string) *Validation {
|
||||
return &Validation{
|
||||
code: InvalidTypeCode,
|
||||
Value: typeName,
|
||||
message: fmt.Sprintf(invalidType, typeName),
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidType creates an error for when the type is invalid
|
||||
func InvalidType(name, in, typeName string, value interface{}) *Validation {
|
||||
var message string
|
||||
|
||||
if in != "" {
|
||||
switch value.(type) {
|
||||
case string:
|
||||
message = fmt.Sprintf(typeFailWithData, name, in, typeName, value)
|
||||
case error:
|
||||
message = fmt.Sprintf(typeFailWithError, name, in, typeName, value)
|
||||
default:
|
||||
message = fmt.Sprintf(typeFail, name, in, typeName)
|
||||
}
|
||||
} else {
|
||||
switch value.(type) {
|
||||
case string:
|
||||
message = fmt.Sprintf(typeFailWithDataNoIn, name, typeName, value)
|
||||
case error:
|
||||
message = fmt.Sprintf(typeFailWithErrorNoIn, name, typeName, value)
|
||||
default:
|
||||
message = fmt.Sprintf(typeFailNoIn, name, typeName)
|
||||
}
|
||||
}
|
||||
|
||||
return &Validation{
|
||||
code: InvalidTypeCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: message,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DuplicateItems error for when an array contains duplicates
|
||||
func DuplicateItems(name, in string) *Validation {
|
||||
msg := fmt.Sprintf(uniqueFail, name, in)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(uniqueFailNoIn, name)
|
||||
}
|
||||
return &Validation{
|
||||
code: UniqueFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// TooManyItems error for when an array contains too many items
|
||||
func TooManyItems(name, in string, max int64, value interface{}) *Validation {
|
||||
msg := fmt.Sprintf(maxItemsFail, name, in, max)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(maxItemsFailNoIn, name, max)
|
||||
}
|
||||
|
||||
return &Validation{
|
||||
code: MaxItemsFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
Valid: max,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// TooFewItems error for when an array contains too few items
|
||||
func TooFewItems(name, in string, min int64, value interface{}) *Validation {
|
||||
msg := fmt.Sprintf(minItemsFail, name, in, min)
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(minItemsFailNoIn, name, min)
|
||||
}
|
||||
return &Validation{
|
||||
code: MinItemsFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
Valid: min,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// ExceedsMaximumInt error for when maxinum validation fails
|
||||
func ExceedsMaximumInt(name, in string, max int64, exclusive bool, value interface{}) *Validation {
|
||||
var message string
|
||||
if in == "" {
|
||||
m := maxIncFailNoIn
|
||||
if exclusive {
|
||||
m = maxExcFailNoIn
|
||||
}
|
||||
message = fmt.Sprintf(m, name, max)
|
||||
} else {
|
||||
m := maxIncFail
|
||||
if exclusive {
|
||||
m = maxExcFail
|
||||
}
|
||||
message = fmt.Sprintf(m, name, in, max)
|
||||
}
|
||||
return &Validation{
|
||||
code: MaxFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// ExceedsMaximumUint error for when maxinum validation fails
|
||||
func ExceedsMaximumUint(name, in string, max uint64, exclusive bool, value interface{}) *Validation {
|
||||
var message string
|
||||
if in == "" {
|
||||
m := maxIncFailNoIn
|
||||
if exclusive {
|
||||
m = maxExcFailNoIn
|
||||
}
|
||||
message = fmt.Sprintf(m, name, max)
|
||||
} else {
|
||||
m := maxIncFail
|
||||
if exclusive {
|
||||
m = maxExcFail
|
||||
}
|
||||
message = fmt.Sprintf(m, name, in, max)
|
||||
}
|
||||
return &Validation{
|
||||
code: MaxFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// ExceedsMaximum error for when maxinum validation fails
|
||||
func ExceedsMaximum(name, in string, max float64, exclusive bool, value interface{}) *Validation {
|
||||
var message string
|
||||
if in == "" {
|
||||
m := maxIncFailNoIn
|
||||
if exclusive {
|
||||
m = maxExcFailNoIn
|
||||
}
|
||||
message = fmt.Sprintf(m, name, max)
|
||||
} else {
|
||||
m := maxIncFail
|
||||
if exclusive {
|
||||
m = maxExcFail
|
||||
}
|
||||
message = fmt.Sprintf(m, name, in, max)
|
||||
}
|
||||
return &Validation{
|
||||
code: MaxFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// ExceedsMinimumInt error for when maxinum validation fails
|
||||
func ExceedsMinimumInt(name, in string, min int64, exclusive bool, value interface{}) *Validation {
|
||||
var message string
|
||||
if in == "" {
|
||||
m := minIncFailNoIn
|
||||
if exclusive {
|
||||
m = minExcFailNoIn
|
||||
}
|
||||
message = fmt.Sprintf(m, name, min)
|
||||
} else {
|
||||
m := minIncFail
|
||||
if exclusive {
|
||||
m = minExcFail
|
||||
}
|
||||
message = fmt.Sprintf(m, name, in, min)
|
||||
}
|
||||
return &Validation{
|
||||
code: MinFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// ExceedsMinimumUint error for when maxinum validation fails
|
||||
func ExceedsMinimumUint(name, in string, min uint64, exclusive bool, value interface{}) *Validation {
|
||||
var message string
|
||||
if in == "" {
|
||||
m := minIncFailNoIn
|
||||
if exclusive {
|
||||
m = minExcFailNoIn
|
||||
}
|
||||
message = fmt.Sprintf(m, name, min)
|
||||
} else {
|
||||
m := minIncFail
|
||||
if exclusive {
|
||||
m = minExcFail
|
||||
}
|
||||
message = fmt.Sprintf(m, name, in, min)
|
||||
}
|
||||
return &Validation{
|
||||
code: MinFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// ExceedsMinimum error for when maxinum validation fails
|
||||
func ExceedsMinimum(name, in string, min float64, exclusive bool, value interface{}) *Validation {
|
||||
var message string
|
||||
if in == "" {
|
||||
m := minIncFailNoIn
|
||||
if exclusive {
|
||||
m = minExcFailNoIn
|
||||
}
|
||||
message = fmt.Sprintf(m, name, min)
|
||||
} else {
|
||||
m := minIncFail
|
||||
if exclusive {
|
||||
m = minExcFail
|
||||
}
|
||||
message = fmt.Sprintf(m, name, in, min)
|
||||
}
|
||||
return &Validation{
|
||||
code: MinFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// NotMultipleOf error for when multiple of validation fails
|
||||
func NotMultipleOf(name, in string, multiple, value interface{}) *Validation {
|
||||
var msg string
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(multipleOfFailNoIn, name, multiple)
|
||||
} else {
|
||||
msg = fmt.Sprintf(multipleOfFail, name, in, multiple)
|
||||
}
|
||||
return &Validation{
|
||||
code: MultipleOfFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// EnumFail error for when an enum validation fails
|
||||
func EnumFail(name, in string, value interface{}, values []interface{}) *Validation {
|
||||
var msg string
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(enumFailNoIn, name, values)
|
||||
} else {
|
||||
msg = fmt.Sprintf(enumFail, name, in, values)
|
||||
}
|
||||
|
||||
return &Validation{
|
||||
code: EnumFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
Values: values,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// Required error for when a value is missing
|
||||
func Required(name, in string) *Validation {
|
||||
var msg string
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(requiredFailNoIn, name)
|
||||
} else {
|
||||
msg = fmt.Sprintf(requiredFail, name, in)
|
||||
}
|
||||
return &Validation{
|
||||
code: RequiredFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// TooLong error for when a string is too long
|
||||
func TooLong(name, in string, max int64, value interface{}) *Validation {
|
||||
var msg string
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(tooLongMessageNoIn, name, max)
|
||||
} else {
|
||||
msg = fmt.Sprintf(tooLongMessage, name, in, max)
|
||||
}
|
||||
return &Validation{
|
||||
code: TooLongFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
Valid: max,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// TooShort error for when a string is too short
|
||||
func TooShort(name, in string, min int64, value interface{}) *Validation {
|
||||
var msg string
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(tooShortMessageNoIn, name, min)
|
||||
} else {
|
||||
msg = fmt.Sprintf(tooShortMessage, name, in, min)
|
||||
}
|
||||
|
||||
return &Validation{
|
||||
code: TooShortFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
Valid: min,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// FailedPattern error for when a string fails a regex pattern match
|
||||
// the pattern that is returned is the ECMA syntax version of the pattern not the golang version.
|
||||
func FailedPattern(name, in, pattern string, value interface{}) *Validation {
|
||||
var msg string
|
||||
if in == "" {
|
||||
msg = fmt.Sprintf(patternFailNoIn, name, pattern)
|
||||
} else {
|
||||
msg = fmt.Sprintf(patternFail, name, in, pattern)
|
||||
}
|
||||
|
||||
return &Validation{
|
||||
code: PatternFailCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: value,
|
||||
message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// MultipleOfMustBePositive error for when a
|
||||
// multipleOf factor is negative
|
||||
func MultipleOfMustBePositive(name, in string, factor interface{}) *Validation {
|
||||
return &Validation{
|
||||
code: MultipleOfMustBePositiveCode,
|
||||
Name: name,
|
||||
In: in,
|
||||
Value: factor,
|
||||
message: fmt.Sprintf(multipleOfMustBePositive, name, factor),
|
||||
}
|
||||
}
|
||||
502
vendor/k8s.io/kube-openapi/pkg/validation/spec/fuzz.go
generated
vendored
502
vendor/k8s.io/kube-openapi/pkg/validation/spec/fuzz.go
generated
vendored
@@ -1,502 +0,0 @@
|
||||
/*
|
||||
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 spec
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/jsonreference"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
fuzz "github.com/google/gofuzz"
|
||||
)
|
||||
|
||||
var SwaggerFuzzFuncs []interface{} = []interface{}{
|
||||
func(v *Responses, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(v)
|
||||
if v.Default != nil {
|
||||
// Check if we hit maxDepth and left an incomplete value
|
||||
if v.Default.Description == "" {
|
||||
v.Default = nil
|
||||
v.StatusCodeResponses = nil
|
||||
}
|
||||
}
|
||||
|
||||
// conversion has no way to discern empty statusCodeResponses from
|
||||
// nil, since "default" is always included in the map.
|
||||
// So avoid empty responses list
|
||||
if len(v.StatusCodeResponses) == 0 {
|
||||
v.StatusCodeResponses = nil
|
||||
}
|
||||
},
|
||||
func(v *Operation, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(v)
|
||||
|
||||
if v != nil {
|
||||
// force non-nil
|
||||
v.Responses = &Responses{}
|
||||
c.Fuzz(v.Responses)
|
||||
|
||||
v.Schemes = nil
|
||||
if c.RandBool() {
|
||||
v.Schemes = append(v.Schemes, "http")
|
||||
}
|
||||
|
||||
if c.RandBool() {
|
||||
v.Schemes = append(v.Schemes, "https")
|
||||
}
|
||||
|
||||
if c.RandBool() {
|
||||
v.Schemes = append(v.Schemes, "ws")
|
||||
}
|
||||
|
||||
if c.RandBool() {
|
||||
v.Schemes = append(v.Schemes, "wss")
|
||||
}
|
||||
|
||||
// Gnostic unconditionally makes security values non-null
|
||||
// So do not fuzz null values into the array.
|
||||
for i, val := range v.Security {
|
||||
if val == nil {
|
||||
v.Security[i] = make(map[string][]string)
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
if v == nil {
|
||||
val[k] = make([]string, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
func(v map[int]Response, c fuzz.Continue) {
|
||||
n := 0
|
||||
c.Fuzz(&n)
|
||||
if n == 0 {
|
||||
// Test that fuzzer is not at maxDepth so we do not
|
||||
// end up with empty elements
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent negative numbers
|
||||
num := c.Intn(4)
|
||||
for i := 0; i < num+2; i++ {
|
||||
val := Response{}
|
||||
c.Fuzz(&val)
|
||||
|
||||
val.Description = c.RandString() + "x"
|
||||
v[100*(i+1)+c.Intn(100)] = val
|
||||
}
|
||||
},
|
||||
func(v map[string]PathItem, c fuzz.Continue) {
|
||||
n := 0
|
||||
c.Fuzz(&n)
|
||||
if n == 0 {
|
||||
// Test that fuzzer is not at maxDepth so we do not
|
||||
// end up with empty elements
|
||||
return
|
||||
}
|
||||
|
||||
num := c.Intn(5)
|
||||
for i := 0; i < num+2; i++ {
|
||||
val := PathItem{}
|
||||
c.Fuzz(&val)
|
||||
|
||||
// Ref params are only allowed in certain locations, so
|
||||
// possibly add a few to PathItems
|
||||
numRefsToAdd := c.Intn(5)
|
||||
for i := 0; i < numRefsToAdd; i++ {
|
||||
theRef := Parameter{}
|
||||
c.Fuzz(&theRef.Refable)
|
||||
|
||||
val.Parameters = append(val.Parameters, theRef)
|
||||
}
|
||||
|
||||
v["/"+c.RandString()] = val
|
||||
}
|
||||
},
|
||||
func(v *SchemaOrArray, c fuzz.Continue) {
|
||||
*v = SchemaOrArray{}
|
||||
// gnostic parser just doesn't support more
|
||||
// than one Schema here
|
||||
v.Schema = &Schema{}
|
||||
c.Fuzz(&v.Schema)
|
||||
|
||||
},
|
||||
func(v *SchemaOrBool, c fuzz.Continue) {
|
||||
*v = SchemaOrBool{}
|
||||
|
||||
if c.RandBool() {
|
||||
v.Allows = c.RandBool()
|
||||
} else {
|
||||
v.Schema = &Schema{}
|
||||
v.Allows = true
|
||||
c.Fuzz(&v.Schema)
|
||||
}
|
||||
},
|
||||
func(v map[string]Response, c fuzz.Continue) {
|
||||
n := 0
|
||||
c.Fuzz(&n)
|
||||
if n == 0 {
|
||||
// Test that fuzzer is not at maxDepth so we do not
|
||||
// end up with empty elements
|
||||
return
|
||||
}
|
||||
|
||||
// Response definitions are not allowed to
|
||||
// be refs
|
||||
for i := 0; i < c.Intn(5)+1; i++ {
|
||||
resp := &Response{}
|
||||
|
||||
c.Fuzz(resp)
|
||||
resp.Ref = Ref{}
|
||||
resp.Description = c.RandString() + "x"
|
||||
|
||||
// Response refs are not vendor extensible by gnostic
|
||||
resp.VendorExtensible.Extensions = nil
|
||||
v[c.RandString()+"x"] = *resp
|
||||
}
|
||||
},
|
||||
func(v *Header, c fuzz.Continue) {
|
||||
if v != nil {
|
||||
c.FuzzNoCustom(v)
|
||||
|
||||
// descendant Items of Header may not be refs
|
||||
cur := v.Items
|
||||
for cur != nil {
|
||||
cur.Ref = Ref{}
|
||||
cur = cur.Items
|
||||
}
|
||||
}
|
||||
},
|
||||
func(v *Ref, c fuzz.Continue) {
|
||||
*v = Ref{}
|
||||
v.Ref, _ = jsonreference.New("http://asd.com/" + c.RandString())
|
||||
},
|
||||
func(v *Response, c fuzz.Continue) {
|
||||
*v = Response{}
|
||||
if c.RandBool() {
|
||||
v.Ref = Ref{}
|
||||
v.Ref.Ref, _ = jsonreference.New("http://asd.com/" + c.RandString())
|
||||
} else {
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
c.Fuzz(&v.Schema)
|
||||
c.Fuzz(&v.ResponseProps)
|
||||
|
||||
v.Headers = nil
|
||||
v.Ref = Ref{}
|
||||
|
||||
n := 0
|
||||
c.Fuzz(&n)
|
||||
if n != 0 {
|
||||
// Test that fuzzer is not at maxDepth so we do not
|
||||
// end up with empty elements
|
||||
num := c.Intn(4)
|
||||
for i := 0; i < num; i++ {
|
||||
if v.Headers == nil {
|
||||
v.Headers = make(map[string]Header)
|
||||
}
|
||||
hdr := Header{}
|
||||
c.Fuzz(&hdr)
|
||||
if hdr.Type == "" {
|
||||
// hit maxDepth, just abort trying to make haders
|
||||
v.Headers = nil
|
||||
break
|
||||
}
|
||||
v.Headers[c.RandString()+"x"] = hdr
|
||||
}
|
||||
} else {
|
||||
v.Headers = nil
|
||||
}
|
||||
}
|
||||
|
||||
v.Description = c.RandString() + "x"
|
||||
|
||||
// Gnostic parses empty as nil, so to keep avoid putting empty
|
||||
if len(v.Headers) == 0 {
|
||||
v.Headers = nil
|
||||
}
|
||||
},
|
||||
func(v **Info, c fuzz.Continue) {
|
||||
// Info is never nil
|
||||
*v = &Info{}
|
||||
c.FuzzNoCustom(*v)
|
||||
|
||||
(*v).Title = c.RandString() + "x"
|
||||
},
|
||||
func(v *Extensions, c fuzz.Continue) {
|
||||
// gnostic parser only picks up x- vendor extensions
|
||||
numChildren := c.Intn(5)
|
||||
for i := 0; i < numChildren; i++ {
|
||||
if *v == nil {
|
||||
*v = Extensions{}
|
||||
}
|
||||
(*v)["x-"+c.RandString()] = c.RandString()
|
||||
}
|
||||
},
|
||||
func(v *Swagger, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(v)
|
||||
|
||||
if v.Paths == nil {
|
||||
// Force paths non-nil since it does not have omitempty in json tag.
|
||||
// This means a perfect roundtrip (via json) is impossible,
|
||||
// since we can't tell the difference between empty/unspecified paths
|
||||
v.Paths = &Paths{}
|
||||
c.Fuzz(v.Paths)
|
||||
}
|
||||
|
||||
v.Swagger = "2.0"
|
||||
|
||||
// Gnostic support serializing ID at all
|
||||
// unavoidable data loss
|
||||
v.ID = ""
|
||||
|
||||
v.Schemes = nil
|
||||
if c.RandUint64()%2 == 1 {
|
||||
v.Schemes = append(v.Schemes, "http")
|
||||
}
|
||||
|
||||
if c.RandUint64()%2 == 1 {
|
||||
v.Schemes = append(v.Schemes, "https")
|
||||
}
|
||||
|
||||
if c.RandUint64()%2 == 1 {
|
||||
v.Schemes = append(v.Schemes, "ws")
|
||||
}
|
||||
|
||||
if c.RandUint64()%2 == 1 {
|
||||
v.Schemes = append(v.Schemes, "wss")
|
||||
}
|
||||
|
||||
// Gnostic unconditionally makes security values non-null
|
||||
// So do not fuzz null values into the array.
|
||||
for i, val := range v.Security {
|
||||
if val == nil {
|
||||
v.Security[i] = make(map[string][]string)
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
if v == nil {
|
||||
val[k] = make([]string, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
func(v *SecurityScheme, c fuzz.Continue) {
|
||||
v.Description = c.RandString() + "x"
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
|
||||
switch c.Intn(3) {
|
||||
case 0:
|
||||
v.Type = "basic"
|
||||
case 1:
|
||||
v.Type = "apiKey"
|
||||
switch c.Intn(2) {
|
||||
case 0:
|
||||
v.In = "header"
|
||||
case 1:
|
||||
v.In = "query"
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
v.Name = "x" + c.RandString()
|
||||
case 2:
|
||||
v.Type = "oauth2"
|
||||
|
||||
switch c.Intn(4) {
|
||||
case 0:
|
||||
v.Flow = "accessCode"
|
||||
v.TokenURL = "https://" + c.RandString()
|
||||
v.AuthorizationURL = "https://" + c.RandString()
|
||||
case 1:
|
||||
v.Flow = "application"
|
||||
v.TokenURL = "https://" + c.RandString()
|
||||
case 2:
|
||||
v.Flow = "implicit"
|
||||
v.AuthorizationURL = "https://" + c.RandString()
|
||||
case 3:
|
||||
v.Flow = "password"
|
||||
v.TokenURL = "https://" + c.RandString()
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
c.Fuzz(&v.Scopes)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
},
|
||||
func(v *interface{}, c fuzz.Continue) {
|
||||
*v = c.RandString() + "x"
|
||||
},
|
||||
func(v *string, c fuzz.Continue) {
|
||||
*v = c.RandString() + "x"
|
||||
},
|
||||
func(v *ExternalDocumentation, c fuzz.Continue) {
|
||||
v.Description = c.RandString() + "x"
|
||||
v.URL = c.RandString() + "x"
|
||||
},
|
||||
func(v *SimpleSchema, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(v)
|
||||
|
||||
switch c.Intn(5) {
|
||||
case 0:
|
||||
v.Type = "string"
|
||||
case 1:
|
||||
v.Type = "number"
|
||||
case 2:
|
||||
v.Type = "boolean"
|
||||
case 3:
|
||||
v.Type = "integer"
|
||||
case 4:
|
||||
v.Type = "array"
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
switch c.Intn(5) {
|
||||
case 0:
|
||||
v.CollectionFormat = "csv"
|
||||
case 1:
|
||||
v.CollectionFormat = "ssv"
|
||||
case 2:
|
||||
v.CollectionFormat = "tsv"
|
||||
case 3:
|
||||
v.CollectionFormat = "pipes"
|
||||
case 4:
|
||||
v.CollectionFormat = ""
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// None of the types which include SimpleSchema in our definitions
|
||||
// actually support "example" in the official spec
|
||||
v.Example = nil
|
||||
|
||||
// unsupported by openapi
|
||||
v.Nullable = false
|
||||
},
|
||||
func(v *int64, c fuzz.Continue) {
|
||||
c.Fuzz(v)
|
||||
|
||||
// Gnostic does not differentiate between 0 and non-specified
|
||||
// so avoid using 0 for fuzzer
|
||||
if *v == 0 {
|
||||
*v = 1
|
||||
}
|
||||
},
|
||||
func(v *float64, c fuzz.Continue) {
|
||||
c.Fuzz(v)
|
||||
|
||||
// Gnostic does not differentiate between 0 and non-specified
|
||||
// so avoid using 0 for fuzzer
|
||||
if *v == 0.0 {
|
||||
*v = 1.0
|
||||
}
|
||||
},
|
||||
func(v *Parameter, c fuzz.Continue) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
if c.RandBool() {
|
||||
// body param
|
||||
v.Description = c.RandString() + "x"
|
||||
v.Name = c.RandString() + "x"
|
||||
v.In = "body"
|
||||
c.Fuzz(&v.Description)
|
||||
c.Fuzz(&v.Required)
|
||||
|
||||
v.Schema = &Schema{}
|
||||
c.Fuzz(&v.Schema)
|
||||
|
||||
} else {
|
||||
c.Fuzz(&v.SimpleSchema)
|
||||
c.Fuzz(&v.CommonValidations)
|
||||
v.AllowEmptyValue = false
|
||||
v.Description = c.RandString() + "x"
|
||||
v.Name = c.RandString() + "x"
|
||||
|
||||
switch c.Intn(4) {
|
||||
case 0:
|
||||
// Header param
|
||||
v.In = "header"
|
||||
case 1:
|
||||
// Form data param
|
||||
v.In = "formData"
|
||||
v.AllowEmptyValue = c.RandBool()
|
||||
case 2:
|
||||
// Query param
|
||||
v.In = "query"
|
||||
v.AllowEmptyValue = c.RandBool()
|
||||
case 3:
|
||||
// Path param
|
||||
v.In = "path"
|
||||
v.Required = true
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// descendant Items of Parameter may not be refs
|
||||
cur := v.Items
|
||||
for cur != nil {
|
||||
cur.Ref = Ref{}
|
||||
cur = cur.Items
|
||||
}
|
||||
}
|
||||
},
|
||||
func(v *Schema, c fuzz.Continue) {
|
||||
if c.RandBool() {
|
||||
// file schema
|
||||
c.Fuzz(&v.Default)
|
||||
c.Fuzz(&v.Description)
|
||||
c.Fuzz(&v.Example)
|
||||
c.Fuzz(&v.ExternalDocs)
|
||||
|
||||
c.Fuzz(&v.Format)
|
||||
c.Fuzz(&v.ReadOnly)
|
||||
c.Fuzz(&v.Required)
|
||||
c.Fuzz(&v.Title)
|
||||
v.Type = StringOrArray{"file"}
|
||||
|
||||
} else {
|
||||
// normal schema
|
||||
c.Fuzz(&v.SchemaProps)
|
||||
c.Fuzz(&v.SwaggerSchemaProps)
|
||||
c.Fuzz(&v.VendorExtensible)
|
||||
// c.Fuzz(&v.ExtraProps)
|
||||
// ExtraProps will not roundtrip - gnostic throws out
|
||||
// unrecognized keys
|
||||
}
|
||||
|
||||
// Not supported by official openapi v2 spec
|
||||
// and stripped by k8s apiserver
|
||||
v.ID = ""
|
||||
v.AnyOf = nil
|
||||
v.OneOf = nil
|
||||
v.Not = nil
|
||||
v.Nullable = false
|
||||
v.AdditionalItems = nil
|
||||
v.Schema = ""
|
||||
v.PatternProperties = nil
|
||||
v.Definitions = nil
|
||||
v.Dependencies = nil
|
||||
},
|
||||
}
|
||||
|
||||
var SwaggerDiffOptions = []cmp.Option{
|
||||
// cmp.Diff panics on Ref since jsonreference.Ref uses unexported fields
|
||||
cmp.Comparer(func(a Ref, b Ref) bool {
|
||||
return a.String() == b.String()
|
||||
}),
|
||||
}
|
||||
2
vendor/k8s.io/kube-openapi/pkg/validation/spec/gnostic.go
generated
vendored
2
vendor/k8s.io/kube-openapi/pkg/validation/spec/gnostic.go
generated
vendored
@@ -21,7 +21,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/jsonreference"
|
||||
openapi_v2 "github.com/google/gnostic/openapiv2"
|
||||
openapi_v2 "github.com/google/gnostic-models/openapiv2"
|
||||
)
|
||||
|
||||
// Interfaces
|
||||
|
||||
4
vendor/k8s.io/kube-openapi/pkg/validation/spec/schema.go
generated
vendored
4
vendor/k8s.io/kube-openapi/pkg/validation/spec/schema.go
generated
vendored
@@ -523,6 +523,7 @@ func (s Schema) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder)
|
||||
ArbitraryKeys
|
||||
SchemaProps schemaPropsOmitZero `json:",inline"`
|
||||
SwaggerSchemaProps swaggerSchemaPropsOmitZero `json:",inline"`
|
||||
Schema string `json:"$schema,omitempty"`
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
}
|
||||
x.ArbitraryKeys = make(map[string]any, len(s.Extensions)+len(s.ExtraProps))
|
||||
@@ -537,6 +538,7 @@ func (s Schema) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder)
|
||||
x.SchemaProps = schemaPropsOmitZero(s.SchemaProps)
|
||||
x.SwaggerSchemaProps = swaggerSchemaPropsOmitZero(s.SwaggerSchemaProps)
|
||||
x.Ref = s.Ref.String()
|
||||
x.Schema = string(s.Schema)
|
||||
return opts.MarshalNext(enc, x)
|
||||
}
|
||||
|
||||
@@ -622,7 +624,7 @@ func (s *Schema) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Dec
|
||||
}
|
||||
|
||||
s.ExtraProps = x.Extensions.sanitizeWithExtra()
|
||||
s.VendorExtensible.Extensions = x.Extensions
|
||||
s.Extensions = internal.SanitizeExtensions(x.Extensions)
|
||||
s.SchemaProps = x.SchemaProps
|
||||
s.SwaggerSchemaProps = x.SwaggerSchemaProps
|
||||
return nil
|
||||
|
||||
20
vendor/k8s.io/kube-openapi/pkg/validation/spec/swagger.go
generated
vendored
20
vendor/k8s.io/kube-openapi/pkg/validation/spec/swagger.go
generated
vendored
@@ -164,15 +164,15 @@ func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
var nw SchemaOrBool
|
||||
if len(data) >= 4 {
|
||||
if data[0] == '{' {
|
||||
var sch Schema
|
||||
if err := json.Unmarshal(data, &sch); err != nil {
|
||||
return err
|
||||
}
|
||||
nw.Schema = &sch
|
||||
if len(data) > 0 && data[0] == '{' {
|
||||
var sch Schema
|
||||
if err := json.Unmarshal(data, &sch); err != nil {
|
||||
return err
|
||||
}
|
||||
nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e')
|
||||
nw.Schema = &sch
|
||||
nw.Allows = true
|
||||
} else {
|
||||
json.Unmarshal(data, &nw.Allows)
|
||||
}
|
||||
*s = nw
|
||||
return nil
|
||||
@@ -385,7 +385,7 @@ func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
|
||||
if internal.UseOptimizedJSONMarshaling {
|
||||
return internal.DeterministicMarshal(s)
|
||||
}
|
||||
if len(s.Schemas) > 0 {
|
||||
if s.Schemas != nil {
|
||||
return json.Marshal(s.Schemas)
|
||||
}
|
||||
return json.Marshal(s.Schema)
|
||||
@@ -393,7 +393,7 @@ func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// MarshalJSON converts this schema object or array into JSON structure
|
||||
func (s SchemaOrArray) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
|
||||
if len(s.Schemas) > 0 {
|
||||
if s.Schemas != nil {
|
||||
return opts.MarshalNext(enc, s.Schemas)
|
||||
}
|
||||
return opts.MarshalNext(enc, s.Schema)
|
||||
|
||||
2
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/.gitignore
generated
vendored
Normal file
2
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
secrets.yml
|
||||
coverage.out
|
||||
202
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/LICENSE
generated
vendored
Normal file
202
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
103
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/bson.go
generated
vendored
Normal file
103
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/bson.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 strfmt
|
||||
|
||||
import (
|
||||
bsonprim "k8s.io/kube-openapi/pkg/validation/strfmt/bson"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var id ObjectId
|
||||
// register this format in the default registry
|
||||
Default.Add("bsonobjectid", &id, IsBSONObjectID)
|
||||
}
|
||||
|
||||
// IsBSONObjectID returns true when the string is a valid BSON.ObjectId
|
||||
func IsBSONObjectID(str string) bool {
|
||||
_, err := bsonprim.ObjectIDFromHex(str)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ObjectId represents a BSON object ID (alias to go.mongodb.org/mongo-driver/bson/primitive.ObjectID)
|
||||
//
|
||||
// swagger:strfmt bsonobjectid
|
||||
type ObjectId bsonprim.ObjectID
|
||||
|
||||
// NewObjectId creates a ObjectId from a Hex String
|
||||
func NewObjectId(hex string) ObjectId {
|
||||
oid, err := bsonprim.ObjectIDFromHex(hex)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ObjectId(oid)
|
||||
}
|
||||
|
||||
// MarshalText turns this instance into text
|
||||
func (id ObjectId) MarshalText() ([]byte, error) {
|
||||
oid := bsonprim.ObjectID(id)
|
||||
if oid == bsonprim.NilObjectID {
|
||||
return nil, nil
|
||||
}
|
||||
return []byte(oid.Hex()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText hydrates this instance from text
|
||||
func (id *ObjectId) UnmarshalText(data []byte) error { // validation is performed later on
|
||||
if len(data) == 0 {
|
||||
*id = ObjectId(bsonprim.NilObjectID)
|
||||
return nil
|
||||
}
|
||||
oidstr := string(data)
|
||||
oid, err := bsonprim.ObjectIDFromHex(oidstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*id = ObjectId(oid)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (id ObjectId) String() string {
|
||||
return bsonprim.ObjectID(id).String()
|
||||
}
|
||||
|
||||
// MarshalJSON returns the ObjectId as JSON
|
||||
func (id ObjectId) MarshalJSON() ([]byte, error) {
|
||||
return bsonprim.ObjectID(id).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets the ObjectId from JSON
|
||||
func (id *ObjectId) UnmarshalJSON(data []byte) error {
|
||||
var obj bsonprim.ObjectID
|
||||
if err := obj.UnmarshalJSON(data); err != nil {
|
||||
return err
|
||||
}
|
||||
*id = ObjectId(obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto copies the receiver and writes its value into out.
|
||||
func (id *ObjectId) DeepCopyInto(out *ObjectId) {
|
||||
*out = *id
|
||||
}
|
||||
|
||||
// DeepCopy copies the receiver into a new ObjectId.
|
||||
func (id *ObjectId) DeepCopy() *ObjectId {
|
||||
if id == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ObjectId)
|
||||
id.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
122
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/bson/objectid.go
generated
vendored
Normal file
122
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/bson/objectid.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer
|
||||
// See THIRD-PARTY-NOTICES for original license terms.
|
||||
|
||||
package bson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrInvalidHex indicates that a hex string cannot be converted to an ObjectID.
|
||||
var ErrInvalidHex = errors.New("the provided hex string is not a valid ObjectID")
|
||||
|
||||
// ObjectID is the BSON ObjectID type.
|
||||
type ObjectID [12]byte
|
||||
|
||||
// NilObjectID is the zero value for ObjectID.
|
||||
var NilObjectID ObjectID
|
||||
|
||||
// Hex returns the hex encoding of the ObjectID as a string.
|
||||
func (id ObjectID) Hex() string {
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
func (id ObjectID) String() string {
|
||||
return fmt.Sprintf("ObjectID(%q)", id.Hex())
|
||||
}
|
||||
|
||||
// IsZero returns true if id is the empty ObjectID.
|
||||
func (id ObjectID) IsZero() bool {
|
||||
return bytes.Equal(id[:], NilObjectID[:])
|
||||
}
|
||||
|
||||
// ObjectIDFromHex creates a new ObjectID from a hex string. It returns an error if the hex string is not a
|
||||
// valid ObjectID.
|
||||
func ObjectIDFromHex(s string) (ObjectID, error) {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return NilObjectID, err
|
||||
}
|
||||
|
||||
if len(b) != 12 {
|
||||
return NilObjectID, ErrInvalidHex
|
||||
}
|
||||
|
||||
var oid [12]byte
|
||||
copy(oid[:], b[:])
|
||||
|
||||
return oid, nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the ObjectID as a string
|
||||
func (id ObjectID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(id.Hex())
|
||||
}
|
||||
|
||||
// UnmarshalJSON populates the byte slice with the ObjectID. If the byte slice is 24 bytes long, it
|
||||
// will be populated with the hex representation of the ObjectID. If the byte slice is twelve bytes
|
||||
// long, it will be populated with the BSON representation of the ObjectID. This method also accepts empty strings and
|
||||
// decodes them as NilObjectID. For any other inputs, an error will be returned.
|
||||
func (id *ObjectID) UnmarshalJSON(b []byte) error {
|
||||
// Ignore "null" to keep parity with the standard library. Decoding a JSON null into a non-pointer ObjectID field
|
||||
// will leave the field unchanged. For pointer values, encoding/json will set the pointer to nil and will not
|
||||
// enter the UnmarshalJSON hook.
|
||||
if string(b) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
switch len(b) {
|
||||
case 12:
|
||||
copy(id[:], b)
|
||||
default:
|
||||
// Extended JSON
|
||||
var res interface{}
|
||||
err := json.Unmarshal(b, &res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
str, ok := res.(string)
|
||||
if !ok {
|
||||
m, ok := res.(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("not an extended JSON ObjectID")
|
||||
}
|
||||
oid, ok := m["$oid"]
|
||||
if !ok {
|
||||
return errors.New("not an extended JSON ObjectID")
|
||||
}
|
||||
str, ok = oid.(string)
|
||||
if !ok {
|
||||
return errors.New("not an extended JSON ObjectID")
|
||||
}
|
||||
}
|
||||
|
||||
// An empty string is not a valid ObjectID, but we treat it as a special value that decodes as NilObjectID.
|
||||
if len(str) == 0 {
|
||||
copy(id[:], NilObjectID[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(str) != 24 {
|
||||
return fmt.Errorf("cannot unmarshal into an ObjectID, the length must be 24 but it is %d", len(str))
|
||||
}
|
||||
|
||||
_, err = hex.Decode(id[:], []byte(str))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
103
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/date.go
generated
vendored
Normal file
103
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/date.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 strfmt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
d := Date{}
|
||||
// register this format in the default registry
|
||||
Default.Add("date", &d, IsDate)
|
||||
}
|
||||
|
||||
// IsDate returns true when the string is a valid date
|
||||
func IsDate(str string) bool {
|
||||
_, err := time.Parse(RFC3339FullDate, str)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
const (
|
||||
// RFC3339FullDate represents a full-date as specified by RFC3339
|
||||
// See: http://goo.gl/xXOvVd
|
||||
RFC3339FullDate = "2006-01-02"
|
||||
)
|
||||
|
||||
// Date represents a date from the API
|
||||
//
|
||||
// swagger:strfmt date
|
||||
type Date time.Time
|
||||
|
||||
// String converts this date into a string
|
||||
func (d Date) String() string {
|
||||
return time.Time(d).Format(RFC3339FullDate)
|
||||
}
|
||||
|
||||
// UnmarshalText parses a text representation into a date type
|
||||
func (d *Date) UnmarshalText(text []byte) error {
|
||||
if len(text) == 0 {
|
||||
return nil
|
||||
}
|
||||
dd, err := time.Parse(RFC3339FullDate, string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Date(dd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText serializes this date type to string
|
||||
func (d Date) MarshalText() ([]byte, error) {
|
||||
return []byte(d.String()), nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the Date as JSON
|
||||
func (d Date) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(time.Time(d).Format(RFC3339FullDate))
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets the Date from JSON
|
||||
func (d *Date) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == jsonNull {
|
||||
return nil
|
||||
}
|
||||
var strdate string
|
||||
if err := json.Unmarshal(data, &strdate); err != nil {
|
||||
return err
|
||||
}
|
||||
tt, err := time.Parse(RFC3339FullDate, strdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Date(tt)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto copies the receiver and writes its value into out.
|
||||
func (d *Date) DeepCopyInto(out *Date) {
|
||||
*out = *d
|
||||
}
|
||||
|
||||
// DeepCopy copies the receiver into a new Date.
|
||||
func (d *Date) DeepCopy() *Date {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Date)
|
||||
d.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
1562
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/default.go
generated
vendored
Normal file
1562
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/default.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/doc.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 strfmt contains custom string formats
|
||||
//
|
||||
// TODO: add info on how to define and register a custom format
|
||||
package strfmt
|
||||
180
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/duration.go
generated
vendored
Normal file
180
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/duration.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 strfmt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
d := Duration(0)
|
||||
// register this format in the default registry
|
||||
Default.Add("duration", &d, IsDuration)
|
||||
}
|
||||
|
||||
var (
|
||||
timeUnits = [][]string{
|
||||
{"ns", "nano"},
|
||||
{"us", "µs", "micro"},
|
||||
{"ms", "milli"},
|
||||
{"s", "sec"},
|
||||
{"m", "min"},
|
||||
{"h", "hr", "hour"},
|
||||
{"d", "day"},
|
||||
{"w", "wk", "week"},
|
||||
}
|
||||
|
||||
timeMultiplier = map[string]time.Duration{
|
||||
"ns": time.Nanosecond,
|
||||
"us": time.Microsecond,
|
||||
"ms": time.Millisecond,
|
||||
"s": time.Second,
|
||||
"m": time.Minute,
|
||||
"h": time.Hour,
|
||||
"d": 24 * time.Hour,
|
||||
"w": 7 * 24 * time.Hour,
|
||||
}
|
||||
|
||||
durationMatcher = regexp.MustCompile(`((\d+)\s*([A-Za-zµ]+))`)
|
||||
)
|
||||
|
||||
// IsDuration returns true if the provided string is a valid duration
|
||||
func IsDuration(str string) bool {
|
||||
_, err := ParseDuration(str)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Duration represents a duration
|
||||
//
|
||||
// Duration stores a period of time as a nanosecond count, with the largest
|
||||
// repesentable duration being approximately 290 years.
|
||||
//
|
||||
// swagger:strfmt duration
|
||||
type Duration time.Duration
|
||||
|
||||
// MarshalText turns this instance into text
|
||||
func (d Duration) MarshalText() ([]byte, error) {
|
||||
return []byte(time.Duration(d).String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText hydrates this instance from text
|
||||
func (d *Duration) UnmarshalText(data []byte) error { // validation is performed later on
|
||||
dd, err := ParseDuration(string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Duration(dd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseDuration parses a duration from a string, compatible with scala duration syntax
|
||||
func ParseDuration(cand string) (time.Duration, error) {
|
||||
if dur, err := time.ParseDuration(cand); err == nil {
|
||||
return dur, nil
|
||||
}
|
||||
|
||||
var dur time.Duration
|
||||
ok := false
|
||||
for _, match := range durationMatcher.FindAllStringSubmatch(cand, -1) {
|
||||
|
||||
factor, err := strconv.Atoi(match[2]) // converts string to int
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
unit := strings.ToLower(strings.TrimSpace(match[3]))
|
||||
|
||||
for _, variants := range timeUnits {
|
||||
last := len(variants) - 1
|
||||
multiplier := timeMultiplier[variants[0]]
|
||||
|
||||
for i, variant := range variants {
|
||||
if (last == i && strings.HasPrefix(unit, variant)) || strings.EqualFold(variant, unit) {
|
||||
ok = true
|
||||
dur += time.Duration(factor) * multiplier
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
return dur, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unable to parse %s as duration", cand)
|
||||
}
|
||||
|
||||
// Scan reads a Duration value from database driver type.
|
||||
func (d *Duration) Scan(raw interface{}) error {
|
||||
switch v := raw.(type) {
|
||||
// TODO: case []byte: // ?
|
||||
case int64:
|
||||
*d = Duration(v)
|
||||
case float64:
|
||||
*d = Duration(int64(v))
|
||||
case nil:
|
||||
*d = Duration(0)
|
||||
default:
|
||||
return fmt.Errorf("cannot sql.Scan() strfmt.Duration from: %#v", v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String converts this duration to a string
|
||||
func (d Duration) String() string {
|
||||
return time.Duration(d).String()
|
||||
}
|
||||
|
||||
// MarshalJSON returns the Duration as JSON
|
||||
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(time.Duration(d).String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets the Duration from JSON
|
||||
func (d *Duration) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == jsonNull {
|
||||
return nil
|
||||
}
|
||||
|
||||
var dstr string
|
||||
if err := json.Unmarshal(data, &dstr); err != nil {
|
||||
return err
|
||||
}
|
||||
tt, err := ParseDuration(dstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Duration(tt)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto copies the receiver and writes its value into out.
|
||||
func (d *Duration) DeepCopyInto(out *Duration) {
|
||||
*out = *d
|
||||
}
|
||||
|
||||
// DeepCopy copies the receiver into a new Duration.
|
||||
func (d *Duration) DeepCopy() *Duration {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Duration)
|
||||
d.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
233
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/format.go
generated
vendored
Normal file
233
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/format.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 strfmt
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
)
|
||||
|
||||
// Default is the default formats registry
|
||||
var Default = NewSeededFormats(nil, nil)
|
||||
|
||||
// Validator represents a validator for a string format.
|
||||
type Validator func(string) bool
|
||||
|
||||
// Format represents a string format.
|
||||
//
|
||||
// All implementations of Format provide a string representation and text
|
||||
// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json).
|
||||
type Format interface {
|
||||
String() string
|
||||
encoding.TextMarshaler
|
||||
encoding.TextUnmarshaler
|
||||
}
|
||||
|
||||
// Registry is a registry of string formats, with a validation method.
|
||||
type Registry interface {
|
||||
Add(string, Format, Validator) bool
|
||||
DelByName(string) bool
|
||||
GetType(string) (reflect.Type, bool)
|
||||
ContainsName(string) bool
|
||||
Validates(string, string) bool
|
||||
Parse(string, string) (interface{}, error)
|
||||
}
|
||||
|
||||
type knownFormat struct {
|
||||
Name string
|
||||
OrigName string
|
||||
Type reflect.Type
|
||||
Validator Validator
|
||||
}
|
||||
|
||||
// NameNormalizer is a function that normalizes a format name.
|
||||
type NameNormalizer func(string) string
|
||||
|
||||
// DefaultNameNormalizer removes all dashes
|
||||
func DefaultNameNormalizer(name string) string {
|
||||
return strings.Replace(name, "-", "", -1)
|
||||
}
|
||||
|
||||
type defaultFormats struct {
|
||||
sync.Mutex
|
||||
data []knownFormat
|
||||
normalizeName NameNormalizer
|
||||
}
|
||||
|
||||
// NewFormats creates a new formats registry seeded with the values from the default
|
||||
func NewFormats() Registry {
|
||||
return NewSeededFormats(Default.(*defaultFormats).data, nil)
|
||||
}
|
||||
|
||||
// NewSeededFormats creates a new formats registry
|
||||
func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry {
|
||||
if normalizer == nil {
|
||||
normalizer = DefaultNameNormalizer
|
||||
}
|
||||
// copy here, don't modify original
|
||||
d := append([]knownFormat(nil), seeds...)
|
||||
return &defaultFormats{
|
||||
data: d,
|
||||
normalizeName: normalizer,
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a new format, return true if this was a new item instead of a replacement
|
||||
func (f *defaultFormats) Add(name string, strfmt Format, validator Validator) bool {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
nme := f.normalizeName(name)
|
||||
|
||||
tpe := reflect.TypeOf(strfmt)
|
||||
if tpe.Kind() == reflect.Ptr {
|
||||
tpe = tpe.Elem()
|
||||
}
|
||||
|
||||
for i := range f.data {
|
||||
v := &f.data[i]
|
||||
if v.Name == nme {
|
||||
v.Type = tpe
|
||||
v.Validator = validator
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// turns out it's new after all
|
||||
f.data = append(f.data, knownFormat{Name: nme, OrigName: name, Type: tpe, Validator: validator})
|
||||
return true
|
||||
}
|
||||
|
||||
// GetType gets the type for the specified name
|
||||
func (f *defaultFormats) GetType(name string) (reflect.Type, bool) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
nme := f.normalizeName(name)
|
||||
for _, v := range f.data {
|
||||
if v.Name == nme {
|
||||
return v.Type, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// DelByName removes the format by the specified name, returns true when an item was actually removed
|
||||
func (f *defaultFormats) DelByName(name string) bool {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
nme := f.normalizeName(name)
|
||||
|
||||
for i, v := range f.data {
|
||||
if v.Name == nme {
|
||||
f.data[i] = knownFormat{} // release
|
||||
f.data = append(f.data[:i], f.data[i+1:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DelByFormat removes the specified format, returns true when an item was actually removed
|
||||
func (f *defaultFormats) DelByFormat(strfmt Format) bool {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
tpe := reflect.TypeOf(strfmt)
|
||||
if tpe.Kind() == reflect.Ptr {
|
||||
tpe = tpe.Elem()
|
||||
}
|
||||
|
||||
for i, v := range f.data {
|
||||
if v.Type == tpe {
|
||||
f.data[i] = knownFormat{} // release
|
||||
f.data = append(f.data[:i], f.data[i+1:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsName returns true if this registry contains the specified name
|
||||
func (f *defaultFormats) ContainsName(name string) bool {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
nme := f.normalizeName(name)
|
||||
for _, v := range f.data {
|
||||
if v.Name == nme {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsFormat returns true if this registry contains the specified format
|
||||
func (f *defaultFormats) ContainsFormat(strfmt Format) bool {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
tpe := reflect.TypeOf(strfmt)
|
||||
if tpe.Kind() == reflect.Ptr {
|
||||
tpe = tpe.Elem()
|
||||
}
|
||||
|
||||
for _, v := range f.data {
|
||||
if v.Type == tpe {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Validates passed data against format.
|
||||
//
|
||||
// Note that the format name is automatically normalized, e.g. one may
|
||||
// use "date-time" to use the "datetime" format validator.
|
||||
func (f *defaultFormats) Validates(name, data string) bool {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
nme := f.normalizeName(name)
|
||||
for _, v := range f.data {
|
||||
if v.Name == nme {
|
||||
return v.Validator(data)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Parse a string into the appropriate format representation type.
|
||||
//
|
||||
// E.g. parsing a string a "date" will return a Date type.
|
||||
func (f *defaultFormats) Parse(name, data string) (interface{}, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
nme := f.normalizeName(name)
|
||||
for _, v := range f.data {
|
||||
if v.Name == nme {
|
||||
nw := reflect.New(v.Type).Interface()
|
||||
if dec, ok := nw.(encoding.TextUnmarshaler); ok {
|
||||
if err := dec.UnmarshalText([]byte(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nw, nil
|
||||
}
|
||||
return nil, errors.InvalidTypeName(name)
|
||||
}
|
||||
}
|
||||
return nil, errors.InvalidTypeName(name)
|
||||
}
|
||||
172
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/time.go
generated
vendored
Normal file
172
vendor/k8s.io/kube-openapi/pkg/validation/strfmt/time.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 strfmt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dt := DateTime{}
|
||||
Default.Add("datetime", &dt, IsDateTime)
|
||||
}
|
||||
|
||||
// IsDateTime returns true when the string is a valid date-time
|
||||
func IsDateTime(str string) bool {
|
||||
if len(str) < 4 {
|
||||
return false
|
||||
}
|
||||
s := strings.Split(strings.ToLower(str), "t")
|
||||
if len(s) < 2 || !IsDate(s[0]) {
|
||||
return false
|
||||
}
|
||||
|
||||
matches := rxDateTime.FindAllStringSubmatch(s[1], -1)
|
||||
if len(matches) == 0 || len(matches[0]) == 0 {
|
||||
return false
|
||||
}
|
||||
m := matches[0]
|
||||
res := m[1] <= "23" && m[2] <= "59" && m[3] <= "59"
|
||||
return res
|
||||
}
|
||||
|
||||
const (
|
||||
// RFC3339Millis represents a ISO8601 format to millis instead of to nanos
|
||||
RFC3339Millis = "2006-01-02T15:04:05.000Z07:00"
|
||||
// RFC3339Micro represents a ISO8601 format to micro instead of to nano
|
||||
RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
|
||||
// ISO8601LocalTime represents a ISO8601 format to ISO8601 in local time (no timezone)
|
||||
ISO8601LocalTime = "2006-01-02T15:04:05"
|
||||
// DateTimePattern pattern to match for the date-time format from http://tools.ietf.org/html/rfc3339#section-5.6
|
||||
DateTimePattern = `^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$`
|
||||
)
|
||||
|
||||
var (
|
||||
dateTimeFormats = []string{RFC3339Micro, RFC3339Millis, time.RFC3339, time.RFC3339Nano, ISO8601LocalTime}
|
||||
rxDateTime = regexp.MustCompile(DateTimePattern)
|
||||
// MarshalFormat sets the time resolution format used for marshaling time (set to milliseconds)
|
||||
MarshalFormat = RFC3339Millis
|
||||
)
|
||||
|
||||
// ParseDateTime parses a string that represents an ISO8601 time or a unix epoch
|
||||
func ParseDateTime(data string) (DateTime, error) {
|
||||
if data == "" {
|
||||
return NewDateTime(), nil
|
||||
}
|
||||
var lastError error
|
||||
for _, layout := range dateTimeFormats {
|
||||
dd, err := time.Parse(layout, data)
|
||||
if err != nil {
|
||||
lastError = err
|
||||
continue
|
||||
}
|
||||
return DateTime(dd), nil
|
||||
}
|
||||
return DateTime{}, lastError
|
||||
}
|
||||
|
||||
// DateTime is a time but it serializes to ISO8601 format with millis
|
||||
// It knows how to read 3 different variations of a RFC3339 date time.
|
||||
// Most APIs we encounter want either millisecond or second precision times.
|
||||
// This just tries to make it worry-free.
|
||||
//
|
||||
// swagger:strfmt date-time
|
||||
type DateTime time.Time
|
||||
|
||||
// NewDateTime is a representation of zero value for DateTime type
|
||||
func NewDateTime() DateTime {
|
||||
return DateTime(time.Unix(0, 0).UTC())
|
||||
}
|
||||
|
||||
// String converts this time to a string
|
||||
func (t DateTime) String() string {
|
||||
return time.Time(t).Format(MarshalFormat)
|
||||
}
|
||||
|
||||
// MarshalText implements the text marshaller interface
|
||||
func (t DateTime) MarshalText() ([]byte, error) {
|
||||
return []byte(t.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the text unmarshaller interface
|
||||
func (t *DateTime) UnmarshalText(text []byte) error {
|
||||
tt, err := ParseDateTime(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = tt
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan scans a DateTime value from database driver type.
|
||||
func (t *DateTime) Scan(raw interface{}) error {
|
||||
// TODO: case int64: and case float64: ?
|
||||
switch v := raw.(type) {
|
||||
case []byte:
|
||||
return t.UnmarshalText(v)
|
||||
case string:
|
||||
return t.UnmarshalText([]byte(v))
|
||||
case time.Time:
|
||||
*t = DateTime(v)
|
||||
case nil:
|
||||
*t = DateTime{}
|
||||
default:
|
||||
return fmt.Errorf("cannot sql.Scan() strfmt.DateTime from: %#v", v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the DateTime as JSON
|
||||
func (t DateTime) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(time.Time(t).Format(MarshalFormat))
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets the DateTime from JSON
|
||||
func (t *DateTime) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == jsonNull {
|
||||
return nil
|
||||
}
|
||||
|
||||
var tstr string
|
||||
if err := json.Unmarshal(data, &tstr); err != nil {
|
||||
return err
|
||||
}
|
||||
tt, err := ParseDateTime(tstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = tt
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto copies the receiver and writes its value into out.
|
||||
func (t *DateTime) DeepCopyInto(out *DateTime) {
|
||||
*out = *t
|
||||
}
|
||||
|
||||
// DeepCopy copies the receiver into a new DateTime.
|
||||
func (t *DateTime) DeepCopy() *DateTime {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DateTime)
|
||||
t.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
5
vendor/k8s.io/kube-openapi/pkg/validation/validate/.gitignore
generated
vendored
Normal file
5
vendor/k8s.io/kube-openapi/pkg/validation/validate/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
secrets.yml
|
||||
coverage.out
|
||||
*.cov
|
||||
*.out
|
||||
playground
|
||||
202
vendor/k8s.io/kube-openapi/pkg/validation/validate/LICENSE
generated
vendored
Normal file
202
vendor/k8s.io/kube-openapi/pkg/validation/validate/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
47
vendor/k8s.io/kube-openapi/pkg/validation/validate/debug.go
generated
vendored
Normal file
47
vendor/k8s.io/kube-openapi/pkg/validation/validate/debug.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
// Debug is true when the SWAGGER_DEBUG env var is not empty.
|
||||
// It enables a more verbose logging of validators.
|
||||
Debug = os.Getenv("SWAGGER_DEBUG") != ""
|
||||
// validateLogger is a debug logger for this package
|
||||
validateLogger *log.Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
debugOptions()
|
||||
}
|
||||
|
||||
func debugOptions() {
|
||||
validateLogger = log.New(os.Stdout, "validate:", log.LstdFlags)
|
||||
}
|
||||
|
||||
func debugLog(msg string, args ...interface{}) {
|
||||
// A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
|
||||
if Debug {
|
||||
_, file1, pos1, _ := runtime.Caller(1)
|
||||
validateLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
}
|
||||
22
vendor/k8s.io/kube-openapi/pkg/validation/validate/doc.go
generated
vendored
Normal file
22
vendor/k8s.io/kube-openapi/pkg/validation/validate/doc.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate provides methods to validate a swagger schema.
|
||||
|
||||
This package follows Swagger 2.0. specification (aka OpenAPI 2.0). Reference
|
||||
can be found here: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md.
|
||||
*/
|
||||
|
||||
package validate
|
||||
63
vendor/k8s.io/kube-openapi/pkg/validation/validate/formats.go
generated
vendored
Normal file
63
vendor/k8s.io/kube-openapi/pkg/validation/validate/formats.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
type formatValidator struct {
|
||||
Format string
|
||||
Path string
|
||||
In string
|
||||
KnownFormats strfmt.Registry
|
||||
}
|
||||
|
||||
func (f *formatValidator) SetPath(path string) {
|
||||
f.Path = path
|
||||
}
|
||||
|
||||
func (f *formatValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
doit := func() bool {
|
||||
if source == nil {
|
||||
return false
|
||||
}
|
||||
switch source := source.(type) {
|
||||
case *spec.Schema:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
}
|
||||
return false
|
||||
}
|
||||
r := doit()
|
||||
debugLog("format validator for %q applies %t for %T (kind: %v)\n", f.Path, r, source, kind)
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *formatValidator) Validate(val interface{}) *Result {
|
||||
result := new(Result)
|
||||
debugLog("validating \"%v\" against format: %s", val, f.Format)
|
||||
|
||||
if err := FormatOf(f.Path, f.In, f.Format, val.(string), f.KnownFormats); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
|
||||
if result.HasErrors() {
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
150
vendor/k8s.io/kube-openapi/pkg/validation/validate/helpers.go
generated
vendored
Normal file
150
vendor/k8s.io/kube-openapi/pkg/validation/validate/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
// TODO: define this as package validate/internal
|
||||
// This must be done while keeping CI intact with all tests and test coverage
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
swaggerExample = "example"
|
||||
swaggerExamples = "examples"
|
||||
)
|
||||
|
||||
const (
|
||||
objectType = "object"
|
||||
arrayType = "array"
|
||||
stringType = "string"
|
||||
integerType = "integer"
|
||||
numberType = "number"
|
||||
booleanType = "boolean"
|
||||
nullType = "null"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonProperties = "properties"
|
||||
jsonDefault = "default"
|
||||
)
|
||||
|
||||
const (
|
||||
stringFormatDate = "date"
|
||||
stringFormatDateTime = "date-time"
|
||||
stringFormatPassword = "password"
|
||||
stringFormatByte = "byte"
|
||||
stringFormatCreditCard = "creditcard"
|
||||
stringFormatDuration = "duration"
|
||||
stringFormatEmail = "email"
|
||||
stringFormatHexColor = "hexcolor"
|
||||
stringFormatHostname = "hostname"
|
||||
stringFormatIPv4 = "ipv4"
|
||||
stringFormatIPv6 = "ipv6"
|
||||
stringFormatISBN = "isbn"
|
||||
stringFormatISBN10 = "isbn10"
|
||||
stringFormatISBN13 = "isbn13"
|
||||
stringFormatMAC = "mac"
|
||||
stringFormatRGBColor = "rgbcolor"
|
||||
stringFormatSSN = "ssn"
|
||||
stringFormatURI = "uri"
|
||||
stringFormatUUID = "uuid"
|
||||
stringFormatUUID3 = "uuid3"
|
||||
stringFormatUUID4 = "uuid4"
|
||||
stringFormatUUID5 = "uuid5"
|
||||
|
||||
integerFormatInt32 = "int32"
|
||||
integerFormatInt64 = "int64"
|
||||
integerFormatUInt32 = "uint32"
|
||||
integerFormatUInt64 = "uint64"
|
||||
|
||||
numberFormatFloat32 = "float32"
|
||||
numberFormatFloat64 = "float64"
|
||||
numberFormatFloat = "float"
|
||||
numberFormatDouble = "double"
|
||||
)
|
||||
|
||||
// Helpers available at the package level
|
||||
var (
|
||||
valueHelp *valueHelper
|
||||
errorHelp *errorHelper
|
||||
)
|
||||
|
||||
type errorHelper struct {
|
||||
// A collection of unexported helpers for error construction
|
||||
}
|
||||
|
||||
func (h *errorHelper) sErr(err errors.Error) *Result {
|
||||
// Builds a Result from standard errors.Error
|
||||
return &Result{Errors: []error{err}}
|
||||
}
|
||||
|
||||
type valueHelper struct {
|
||||
// A collection of unexported helpers for value validation
|
||||
}
|
||||
|
||||
func (h *valueHelper) asInt64(val interface{}) int64 {
|
||||
// Number conversion function for int64, without error checking
|
||||
// (implements an implicit type upgrade).
|
||||
v := reflect.ValueOf(val)
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return int64(v.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return int64(v.Float())
|
||||
default:
|
||||
//panic("Non numeric value in asInt64()")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (h *valueHelper) asUint64(val interface{}) uint64 {
|
||||
// Number conversion function for uint64, without error checking
|
||||
// (implements an implicit type upgrade).
|
||||
v := reflect.ValueOf(val)
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return uint64(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return v.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return uint64(v.Float())
|
||||
default:
|
||||
//panic("Non numeric value in asUint64()")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Same for unsigned floats
|
||||
func (h *valueHelper) asFloat64(val interface{}) float64 {
|
||||
// Number conversion function for float64, without error checking
|
||||
// (implements an implicit type upgrade).
|
||||
v := reflect.ValueOf(val)
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float64(v.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float()
|
||||
default:
|
||||
//panic("Non numeric value in asFloat64()")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
180
vendor/k8s.io/kube-openapi/pkg/validation/validate/object_validator.go
generated
vendored
Normal file
180
vendor/k8s.io/kube-openapi/pkg/validation/validate/object_validator.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
type objectValidator struct {
|
||||
Path string
|
||||
In string
|
||||
MaxProperties *int64
|
||||
MinProperties *int64
|
||||
Required []string
|
||||
Properties map[string]spec.Schema
|
||||
AdditionalProperties *spec.SchemaOrBool
|
||||
PatternProperties map[string]spec.Schema
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func (o *objectValidator) SetPath(path string) {
|
||||
o.Path = path
|
||||
}
|
||||
|
||||
func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
// TODO: this should also work for structs
|
||||
// there is a problem in the type validator where it will be unhappy about null values
|
||||
// so that requires more testing
|
||||
r := reflect.TypeOf(source) == specSchemaType && (kind == reflect.Map || kind == reflect.Struct)
|
||||
debugLog("object validator for %q applies %t for %T (kind: %v)\n", o.Path, r, source, kind)
|
||||
return r
|
||||
}
|
||||
|
||||
func (o *objectValidator) Validate(data interface{}) *Result {
|
||||
val := data.(map[string]interface{})
|
||||
// TODO: guard against nil data
|
||||
numKeys := int64(len(val))
|
||||
|
||||
res := new(Result)
|
||||
|
||||
if o.MinProperties != nil && numKeys < *o.MinProperties {
|
||||
res.AddErrors(errors.TooFewProperties(o.Path, o.In, *o.MinProperties, numKeys))
|
||||
}
|
||||
if o.MaxProperties != nil && numKeys > *o.MaxProperties {
|
||||
res.AddErrors(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties, numKeys))
|
||||
}
|
||||
|
||||
// check validity of field names
|
||||
if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows {
|
||||
// Case: additionalProperties: false
|
||||
for k := range val {
|
||||
_, regularProperty := o.Properties[k]
|
||||
matched := false
|
||||
|
||||
for pk := range o.PatternProperties {
|
||||
if matches, _ := regexp.MatchString(pk, k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !regularProperty && !matched {
|
||||
// Special properties "$schema" and "id" are ignored
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Cases: no additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
|
||||
if !(regularProperty || matched || succeededOnce) {
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil {
|
||||
// AdditionalProperties as Schema
|
||||
res.Merge(o.Options.NewValidatorForField(key, o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value))
|
||||
} else if regularProperty && !(matched || succeededOnce) {
|
||||
// TODO: this is dead code since regularProperty=false here
|
||||
res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
}
|
||||
|
||||
createdFromDefaults := map[string]bool{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
for pName, pSchema := range o.Properties {
|
||||
rName := pName
|
||||
if o.Path != "" {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
if v, ok := val[pName]; ok {
|
||||
r := o.Options.NewValidatorForField(pName, &pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v)
|
||||
res.Merge(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
if len(o.Required) > 0 {
|
||||
for _, k := range o.Required {
|
||||
if _, ok := val[k]; !ok && !createdFromDefaults[k] {
|
||||
res.AddErrors(errors.Required(o.Path+"."+k, o.In))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check patternProperties
|
||||
// TODO: it looks like we have done that twice in many cases
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
matched, _ /*succeededOnce*/, patterns := o.validatePatternProperty(key, value, res)
|
||||
if !regularProperty && (matched /*|| succeededOnce*/) {
|
||||
for _, pName := range patterns {
|
||||
if v, ok := o.PatternProperties[pName]; ok {
|
||||
res.Merge(o.Options.NewValidatorForField(key, &v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// TODO: succeededOnce is not used anywhere
|
||||
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
var patterns []string
|
||||
|
||||
for k, schema := range o.PatternProperties {
|
||||
sch := schema
|
||||
if match, _ := regexp.MatchString(k, key); match {
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := o.Options.NewValidatorForField(key, &sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
}
|
||||
|
||||
// BUG(fredbi): can't get to here. Should remove dead code (commented out).
|
||||
|
||||
//if succeededOnce {
|
||||
// result.Inc()
|
||||
//}
|
||||
|
||||
return matched, succeededOnce, patterns
|
||||
}
|
||||
43
vendor/k8s.io/kube-openapi/pkg/validation/validate/options.go
generated
vendored
Normal file
43
vendor/k8s.io/kube-openapi/pkg/validation/validate/options.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import "sync"
|
||||
|
||||
// Opts specifies validation options for a SpecValidator.
|
||||
//
|
||||
// NOTE: other options might be needed, for example a go-swagger specific mode.
|
||||
type Opts struct {
|
||||
ContinueOnErrors bool // true: continue reporting errors, even if spec is invalid
|
||||
}
|
||||
|
||||
var (
|
||||
defaultOpts = Opts{ContinueOnErrors: false} // default is to stop validation on errors
|
||||
defaultOptsMutex = &sync.Mutex{}
|
||||
)
|
||||
|
||||
// SetContinueOnErrors sets global default behavior regarding spec validation errors reporting.
|
||||
//
|
||||
// For extended error reporting, you most likely want to set it to true.
|
||||
// For faster validation, it's better to give up early when a spec is detected as invalid: set it to false (this is the default).
|
||||
//
|
||||
// Setting this mode does NOT affect the validation status.
|
||||
//
|
||||
// NOTE: this method affects global defaults. It is not suitable for a concurrent usage.
|
||||
func SetContinueOnErrors(c bool) {
|
||||
defer defaultOptsMutex.Unlock()
|
||||
defaultOptsMutex.Lock()
|
||||
defaultOpts.ContinueOnErrors = c
|
||||
}
|
||||
207
vendor/k8s.io/kube-openapi/pkg/validation/validate/result.go
generated
vendored
Normal file
207
vendor/k8s.io/kube-openapi/pkg/validation/validate/result.go
generated
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
)
|
||||
|
||||
// Result represents a validation result set, composed of
|
||||
// errors and warnings.
|
||||
//
|
||||
// It is used to keep track of all detected errors and warnings during
|
||||
// the validation of a specification.
|
||||
//
|
||||
// Matchcount is used to determine
|
||||
// which errors are relevant in the case of AnyOf, OneOf
|
||||
// schema validation. Results from the validation branch
|
||||
// with most matches get eventually selected.
|
||||
//
|
||||
// TODO: keep path of key originating the error
|
||||
type Result struct {
|
||||
Errors []error
|
||||
Warnings []error
|
||||
MatchCount int
|
||||
}
|
||||
|
||||
// Merge merges this result with the other one(s), preserving match counts etc.
|
||||
func (r *Result) Merge(others ...*Result) *Result {
|
||||
for _, other := range others {
|
||||
if other != nil {
|
||||
r.AddErrors(other.Errors...)
|
||||
r.AddWarnings(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// MergeAsErrors merges this result with the other one(s), preserving match counts etc.
|
||||
//
|
||||
// Warnings from input are merged as Errors in the returned merged Result.
|
||||
func (r *Result) MergeAsErrors(others ...*Result) *Result {
|
||||
for _, other := range others {
|
||||
if other != nil {
|
||||
r.AddErrors(other.Errors...)
|
||||
r.AddErrors(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// MergeAsWarnings merges this result with the other one(s), preserving match counts etc.
|
||||
//
|
||||
// Errors from input are merged as Warnings in the returned merged Result.
|
||||
func (r *Result) MergeAsWarnings(others ...*Result) *Result {
|
||||
for _, other := range others {
|
||||
if other != nil {
|
||||
r.AddWarnings(other.Errors...)
|
||||
r.AddWarnings(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// AddErrors adds errors to this validation result (if not already reported).
|
||||
//
|
||||
// Since the same check may be passed several times while exploring the
|
||||
// spec structure (via $ref, ...) reported messages are kept
|
||||
// unique.
|
||||
func (r *Result) AddErrors(errors ...error) {
|
||||
for _, e := range errors {
|
||||
found := false
|
||||
if e != nil {
|
||||
for _, isReported := range r.Errors {
|
||||
if e.Error() == isReported.Error() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
r.Errors = append(r.Errors, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddWarnings adds warnings to this validation result (if not already reported).
|
||||
func (r *Result) AddWarnings(warnings ...error) {
|
||||
for _, e := range warnings {
|
||||
found := false
|
||||
if e != nil {
|
||||
for _, isReported := range r.Warnings {
|
||||
if e.Error() == isReported.Error() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
r.Warnings = append(r.Warnings, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Result) keepRelevantErrors() *Result {
|
||||
// TODO: this one is going to disapear...
|
||||
// keepRelevantErrors strips a result from standard errors and keeps
|
||||
// the ones which are supposedly more accurate.
|
||||
//
|
||||
// The original result remains unaffected (creates a new instance of Result).
|
||||
// This method is used to work around the "matchCount" filter which would otherwise
|
||||
// strip our result from some accurate error reporting from lower level validators.
|
||||
//
|
||||
// NOTE: this implementation with a placeholder (IMPORTANT!) is neither clean nor
|
||||
// very efficient. On the other hand, relying on go-openapi/errors to manipulate
|
||||
// codes would require to change a lot here. So, for the moment, let's go with
|
||||
// placeholders.
|
||||
strippedErrors := []error{}
|
||||
for _, e := range r.Errors {
|
||||
if strings.HasPrefix(e.Error(), "IMPORTANT!") {
|
||||
strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
}
|
||||
}
|
||||
strippedWarnings := []error{}
|
||||
for _, e := range r.Warnings {
|
||||
if strings.HasPrefix(e.Error(), "IMPORTANT!") {
|
||||
strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
}
|
||||
}
|
||||
strippedResult := new(Result)
|
||||
strippedResult.Errors = strippedErrors
|
||||
strippedResult.Warnings = strippedWarnings
|
||||
return strippedResult
|
||||
}
|
||||
|
||||
// IsValid returns true when this result is valid.
|
||||
//
|
||||
// Returns true on a nil *Result.
|
||||
func (r *Result) IsValid() bool {
|
||||
if r == nil {
|
||||
return true
|
||||
}
|
||||
return len(r.Errors) == 0
|
||||
}
|
||||
|
||||
// HasErrors returns true when this result is invalid.
|
||||
//
|
||||
// Returns false on a nil *Result.
|
||||
func (r *Result) HasErrors() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return !r.IsValid()
|
||||
}
|
||||
|
||||
// HasWarnings returns true when this result contains warnings.
|
||||
//
|
||||
// Returns false on a nil *Result.
|
||||
func (r *Result) HasWarnings() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return len(r.Warnings) > 0
|
||||
}
|
||||
|
||||
// HasErrorsOrWarnings returns true when this result contains
|
||||
// either errors or warnings.
|
||||
//
|
||||
// Returns false on a nil *Result.
|
||||
func (r *Result) HasErrorsOrWarnings() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return len(r.Errors) > 0 || len(r.Warnings) > 0
|
||||
}
|
||||
|
||||
// Inc increments the match count
|
||||
func (r *Result) Inc() {
|
||||
r.MatchCount++
|
||||
}
|
||||
|
||||
// AsError renders this result as an error interface
|
||||
//
|
||||
// TODO: reporting / pretty print with path ordered and indented
|
||||
func (r *Result) AsError() error {
|
||||
if r.IsValid() {
|
||||
return nil
|
||||
}
|
||||
return errors.CompositeValidationError(r.Errors...)
|
||||
}
|
||||
71
vendor/k8s.io/kube-openapi/pkg/validation/validate/rexp.go
generated
vendored
Normal file
71
vendor/k8s.io/kube-openapi/pkg/validation/validate/rexp.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
re "regexp"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Cache for compiled regular expressions
|
||||
var (
|
||||
cacheMutex = &sync.Mutex{}
|
||||
reDict = atomic.Value{} //map[string]*re.Regexp
|
||||
)
|
||||
|
||||
func compileRegexp(pattern string) (*re.Regexp, error) {
|
||||
if cache, ok := reDict.Load().(map[string]*re.Regexp); ok {
|
||||
if r := cache[pattern]; r != nil {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
r, err := re.Compile(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheRegexp(r)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func mustCompileRegexp(pattern string) *re.Regexp {
|
||||
if cache, ok := reDict.Load().(map[string]*re.Regexp); ok {
|
||||
if r := cache[pattern]; r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
r := re.MustCompile(pattern)
|
||||
cacheRegexp(r)
|
||||
return r
|
||||
}
|
||||
|
||||
func cacheRegexp(r *re.Regexp) {
|
||||
cacheMutex.Lock()
|
||||
defer cacheMutex.Unlock()
|
||||
|
||||
if cache, ok := reDict.Load().(map[string]*re.Regexp); !ok || cache[r.String()] == nil {
|
||||
newCache := map[string]*re.Regexp{
|
||||
r.String(): r,
|
||||
}
|
||||
|
||||
for k, v := range cache {
|
||||
newCache[k] = v
|
||||
}
|
||||
|
||||
reDict.Store(newCache)
|
||||
}
|
||||
}
|
||||
271
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema.go
generated
vendored
Normal file
271
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema.go
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
var (
|
||||
specSchemaType = reflect.TypeOf(&spec.Schema{})
|
||||
//specItemsType = reflect.TypeOf(&spec.Items{})
|
||||
)
|
||||
|
||||
// SchemaValidator validates data against a JSON schema
|
||||
type SchemaValidator struct {
|
||||
Path string
|
||||
in string
|
||||
Schema *spec.Schema
|
||||
validators []ValueValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// AgainstSchema validates the specified data against the provided schema, using a registry of supported formats.
|
||||
//
|
||||
// When no pre-parsed *spec.Schema structure is provided, it uses a JSON schema as default. See example.
|
||||
func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry, options ...Option) error {
|
||||
res := NewSchemaValidator(schema, nil, "", formats, options...).Validate(data)
|
||||
if res.HasErrors() {
|
||||
return errors.CompositeValidationError(res.Errors...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSchemaValidator creates a new schema validator.
|
||||
//
|
||||
// Panics if the provided schema is invalid.
|
||||
func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...Option) *SchemaValidator {
|
||||
if schema == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rootSchema == nil {
|
||||
rootSchema = schema
|
||||
}
|
||||
|
||||
if ref := schema.Ref.String(); ref != "" {
|
||||
panic(fmt.Sprintf("schema references not supported: %s", ref))
|
||||
}
|
||||
|
||||
s := SchemaValidator{
|
||||
Path: root,
|
||||
in: "body",
|
||||
Schema: schema,
|
||||
Root: rootSchema,
|
||||
KnownFormats: formats,
|
||||
Options: SchemaValidatorOptions{}}
|
||||
for _, o := range options {
|
||||
o(&s.Options)
|
||||
}
|
||||
|
||||
if s.Options.NewValidatorForIndex == nil {
|
||||
s.Options.NewValidatorForIndex = s.NewValidatorForIndex
|
||||
}
|
||||
if s.Options.NewValidatorForField == nil {
|
||||
s.Options.NewValidatorForField = s.NewValidatorForField
|
||||
}
|
||||
|
||||
s.validators = []ValueValidator{
|
||||
s.typeValidator(),
|
||||
s.schemaPropsValidator(),
|
||||
s.stringValidator(),
|
||||
s.formatValidator(),
|
||||
s.numberValidator(),
|
||||
s.sliceValidator(),
|
||||
s.commonValidator(),
|
||||
s.objectValidator(),
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) NewValidatorForField(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator {
|
||||
return NewSchemaValidator(schema, rootSchema, root, formats, opts...)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) NewValidatorForIndex(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator {
|
||||
return NewSchemaValidator(schema, rootSchema, root, formats, opts...)
|
||||
}
|
||||
|
||||
// SetPath sets the path for this schema validator
|
||||
func (s *SchemaValidator) SetPath(path string) {
|
||||
s.Path = path
|
||||
for _, v := range s.validators {
|
||||
v.SetPath(path)
|
||||
}
|
||||
}
|
||||
|
||||
// Applies returns true when this schema validator applies
|
||||
func (s *SchemaValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
_, ok := source.(*spec.Schema)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Validate validates the data against the schema
|
||||
func (s *SchemaValidator) Validate(data interface{}) *Result {
|
||||
result := new(Result)
|
||||
if s == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
result.Merge(s.validators[0].Validate(data)) // type validator
|
||||
result.Merge(s.validators[6].Validate(data)) // common validator
|
||||
return result
|
||||
}
|
||||
|
||||
tpe := reflect.TypeOf(data)
|
||||
kind := tpe.Kind()
|
||||
for kind == reflect.Ptr {
|
||||
tpe = tpe.Elem()
|
||||
kind = tpe.Kind()
|
||||
}
|
||||
d := data
|
||||
|
||||
if kind == reflect.Struct {
|
||||
// NOTE: since reflect retrieves the true nature of types
|
||||
// this means that all strfmt types passed here (e.g. strfmt.Datetime, etc..)
|
||||
// are converted here to strings, and structs are systematically converted
|
||||
// to map[string]interface{}.
|
||||
d = swag.ToDynamicJSON(data)
|
||||
}
|
||||
|
||||
// TODO: this part should be handed over to type validator
|
||||
// Handle special case of json.Number data (number marshalled as string)
|
||||
isnumber := s.Schema.Type.Contains(numberType) || s.Schema.Type.Contains(integerType)
|
||||
if num, ok := data.(json.Number); ok && isnumber {
|
||||
if s.Schema.Type.Contains(integerType) { // avoid lossy conversion
|
||||
in, erri := num.Int64()
|
||||
if erri != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
|
||||
result.Inc()
|
||||
return result
|
||||
}
|
||||
d = in
|
||||
} else {
|
||||
nf, errf := num.Float64()
|
||||
if errf != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, errf))
|
||||
result.Inc()
|
||||
return result
|
||||
}
|
||||
d = nf
|
||||
}
|
||||
|
||||
tpe = reflect.TypeOf(d)
|
||||
kind = tpe.Kind()
|
||||
}
|
||||
|
||||
for _, v := range s.validators {
|
||||
if !v.Applies(s.Schema, kind) {
|
||||
debugLog("%T does not apply for %v", v, kind)
|
||||
continue
|
||||
}
|
||||
|
||||
err := v.Validate(d)
|
||||
result.Merge(err)
|
||||
result.Inc()
|
||||
}
|
||||
result.Inc()
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) typeValidator() ValueValidator {
|
||||
return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path}
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) commonValidator() ValueValidator {
|
||||
return &basicCommonValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Enum: s.Schema.Enum,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) sliceValidator() ValueValidator {
|
||||
return &schemaSliceValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxItems: s.Schema.MaxItems,
|
||||
MinItems: s.Schema.MinItems,
|
||||
UniqueItems: s.Schema.UniqueItems,
|
||||
AdditionalItems: s.Schema.AdditionalItems,
|
||||
Items: s.Schema.Items,
|
||||
Root: s.Root,
|
||||
KnownFormats: s.KnownFormats,
|
||||
Options: s.Options,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) numberValidator() ValueValidator {
|
||||
return &numberValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Default: s.Schema.Default,
|
||||
MultipleOf: s.Schema.MultipleOf,
|
||||
Maximum: s.Schema.Maximum,
|
||||
ExclusiveMaximum: s.Schema.ExclusiveMaximum,
|
||||
Minimum: s.Schema.Minimum,
|
||||
ExclusiveMinimum: s.Schema.ExclusiveMinimum,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) stringValidator() ValueValidator {
|
||||
return &stringValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxLength: s.Schema.MaxLength,
|
||||
MinLength: s.Schema.MinLength,
|
||||
Pattern: s.Schema.Pattern,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) formatValidator() ValueValidator {
|
||||
return &formatValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Format: s.Schema.Format,
|
||||
KnownFormats: s.KnownFormats,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) schemaPropsValidator() ValueValidator {
|
||||
sch := s.Schema
|
||||
return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) objectValidator() ValueValidator {
|
||||
return &objectValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxProperties: s.Schema.MaxProperties,
|
||||
MinProperties: s.Schema.MinProperties,
|
||||
Required: s.Schema.Required,
|
||||
Properties: s.Schema.Properties,
|
||||
AdditionalProperties: s.Schema.AdditionalProperties,
|
||||
PatternProperties: s.Schema.PatternProperties,
|
||||
Root: s.Root,
|
||||
KnownFormats: s.KnownFormats,
|
||||
Options: s.Options,
|
||||
}
|
||||
}
|
||||
72
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema_messages.go
generated
vendored
Normal file
72
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema_messages.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
)
|
||||
|
||||
// Error messages related to schema validation and returned as results.
|
||||
const (
|
||||
// ArrayDoesNotAllowAdditionalItemsError when an additionalItems construct is not verified by the array values provided.
|
||||
//
|
||||
// TODO: should move to package go-openapi/errors
|
||||
ArrayDoesNotAllowAdditionalItemsError = "array doesn't allow for additional items"
|
||||
|
||||
// HasDependencyError indicates that a dependencies construct was not verified
|
||||
HasDependencyError = "%q has a dependency on %s"
|
||||
|
||||
// InvalidTypeConversionError indicates that a numerical conversion for the given type could not be carried on
|
||||
InvalidTypeConversionError = "invalid type conversion in %s: %v "
|
||||
|
||||
// MustValidateAtLeastOneSchemaError indicates that in a AnyOf construct, none of the schema constraints specified were verified
|
||||
MustValidateAtLeastOneSchemaError = "%q must validate at least one schema (anyOf)"
|
||||
|
||||
// MustValidateOnlyOneSchemaError indicates that in a OneOf construct, either none of the schema constraints specified were verified, or several were
|
||||
MustValidateOnlyOneSchemaError = "%q must validate one and only one schema (oneOf). %s"
|
||||
|
||||
// MustValidateAllSchemasError indicates that in a AllOf construct, at least one of the schema constraints specified were not verified
|
||||
//
|
||||
// TODO: punctuation in message
|
||||
MustValidateAllSchemasError = "%q must validate all the schemas (allOf)%s"
|
||||
|
||||
// MustNotValidateSchemaError indicates that in a Not construct, the schema constraint specified was verified
|
||||
MustNotValidateSchemaError = "%q must not validate the schema (not)"
|
||||
)
|
||||
|
||||
// Warning messages related to schema validation and returned as results
|
||||
const ()
|
||||
|
||||
func invalidTypeConversionMsg(path string, err error) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidTypeConversionError, path, err)
|
||||
}
|
||||
func mustValidateOnlyOneSchemaMsg(path, additionalMsg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustValidateOnlyOneSchemaError, path, additionalMsg)
|
||||
}
|
||||
func mustValidateAtLeastOneSchemaMsg(path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustValidateAtLeastOneSchemaError, path)
|
||||
}
|
||||
func mustValidateAllSchemasMsg(path, additionalMsg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustValidateAllSchemasError, path, additionalMsg)
|
||||
}
|
||||
func mustNotValidatechemaMsg(path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustNotValidateSchemaError, path)
|
||||
}
|
||||
func hasADependencyMsg(path, depkey string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, HasDependencyError, path, depkey)
|
||||
}
|
||||
func arrayDoesNotAllowAdditionalItemsMsg() errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ArrayDoesNotAllowAdditionalItemsError)
|
||||
}
|
||||
35
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema_option.go
generated
vendored
Normal file
35
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema_option.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
// SchemaValidatorOptions defines optional rules for schema validation
|
||||
type SchemaValidatorOptions struct {
|
||||
validationRulesEnabled bool
|
||||
NewValidatorForIndex func(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator
|
||||
NewValidatorForField func(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator
|
||||
}
|
||||
|
||||
// Option sets optional rules for schema validation
|
||||
type Option func(*SchemaValidatorOptions)
|
||||
|
||||
// Options returns current options
|
||||
func (svo SchemaValidatorOptions) Options() []Option {
|
||||
return []Option{}
|
||||
}
|
||||
252
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema_props.go
generated
vendored
Normal file
252
vendor/k8s.io/kube-openapi/pkg/validation/validate/schema_props.go
generated
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
type schemaPropsValidator struct {
|
||||
Path string
|
||||
In string
|
||||
AllOf []spec.Schema
|
||||
OneOf []spec.Schema
|
||||
AnyOf []spec.Schema
|
||||
Not *spec.Schema
|
||||
Dependencies spec.Dependencies
|
||||
anyOfValidators []SchemaValidator
|
||||
allOfValidators []SchemaValidator
|
||||
oneOfValidators []SchemaValidator
|
||||
notValidator *SchemaValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) SetPath(path string) {
|
||||
s.Path = path
|
||||
for _, v := range s.anyOfValidators {
|
||||
v.SetPath(path)
|
||||
}
|
||||
for _, v := range s.allOfValidators {
|
||||
v.SetPath(path)
|
||||
}
|
||||
for _, v := range s.oneOfValidators {
|
||||
v.SetPath(path)
|
||||
}
|
||||
if s.notValidator != nil {
|
||||
s.notValidator.SetPath(path)
|
||||
}
|
||||
}
|
||||
|
||||
func newSchemaPropsValidator(path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry, options ...Option) *schemaPropsValidator {
|
||||
var anyValidators []SchemaValidator
|
||||
for _, v := range anyOf {
|
||||
v := v
|
||||
anyValidators = append(anyValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
}
|
||||
var allValidators []SchemaValidator
|
||||
for _, v := range allOf {
|
||||
v := v
|
||||
allValidators = append(allValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
}
|
||||
var oneValidators []SchemaValidator
|
||||
for _, v := range oneOf {
|
||||
v := v
|
||||
oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
}
|
||||
|
||||
var notValidator *SchemaValidator
|
||||
if not != nil {
|
||||
notValidator = NewSchemaValidator(not, root, path, formats, options...)
|
||||
}
|
||||
|
||||
schOptions := &SchemaValidatorOptions{}
|
||||
for _, o := range options {
|
||||
o(schOptions)
|
||||
}
|
||||
return &schemaPropsValidator{
|
||||
Path: path,
|
||||
In: in,
|
||||
AllOf: allOf,
|
||||
OneOf: oneOf,
|
||||
AnyOf: anyOf,
|
||||
Not: not,
|
||||
Dependencies: deps,
|
||||
anyOfValidators: anyValidators,
|
||||
allOfValidators: allValidators,
|
||||
oneOfValidators: oneValidators,
|
||||
notValidator: notValidator,
|
||||
Root: root,
|
||||
KnownFormats: formats,
|
||||
Options: *schOptions,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
r := reflect.TypeOf(source) == specSchemaType
|
||||
debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) Validate(data interface{}) *Result {
|
||||
mainResult := new(Result)
|
||||
|
||||
// Intermediary error results
|
||||
|
||||
// IMPORTANT! messages from underlying validators
|
||||
keepResultAnyOf := new(Result)
|
||||
keepResultOneOf := new(Result)
|
||||
keepResultAllOf := new(Result)
|
||||
|
||||
// Validates at least one in anyOf schemas
|
||||
var firstSuccess *Result
|
||||
if len(s.anyOfValidators) > 0 {
|
||||
var bestFailures *Result
|
||||
succeededOnce := false
|
||||
for _, anyOfSchema := range s.anyOfValidators {
|
||||
result := anyOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAnyOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
bestFailures = nil
|
||||
succeededOnce = true
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
}
|
||||
keepResultAnyOf = new(Result)
|
||||
break
|
||||
}
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
|
||||
bestFailures = result
|
||||
}
|
||||
}
|
||||
|
||||
if !succeededOnce {
|
||||
mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path))
|
||||
}
|
||||
if bestFailures != nil {
|
||||
mainResult.Merge(bestFailures)
|
||||
} else if firstSuccess != nil {
|
||||
mainResult.Merge(firstSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
// Validates exactly one in oneOf schemas
|
||||
if len(s.oneOfValidators) > 0 {
|
||||
var bestFailures *Result
|
||||
var firstSuccess *Result
|
||||
validated := 0
|
||||
|
||||
for _, oneOfSchema := range s.oneOfValidators {
|
||||
result := oneOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultOneOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
bestFailures = nil
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
}
|
||||
keepResultOneOf = new(Result)
|
||||
continue
|
||||
}
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
|
||||
bestFailures = result
|
||||
}
|
||||
}
|
||||
|
||||
if validated != 1 {
|
||||
additionalMsg := ""
|
||||
if validated == 0 {
|
||||
additionalMsg = "Found none valid"
|
||||
} else {
|
||||
additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated)
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg))
|
||||
if bestFailures != nil {
|
||||
mainResult.Merge(bestFailures)
|
||||
}
|
||||
} else if firstSuccess != nil {
|
||||
mainResult.Merge(firstSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
// Validates all of allOf schemas
|
||||
if len(s.allOfValidators) > 0 {
|
||||
validated := 0
|
||||
|
||||
for _, allOfSchema := range s.allOfValidators {
|
||||
result := allOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAllOf.Merge(result.keepRelevantErrors())
|
||||
//keepResultAllOf.Merge(result)
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
}
|
||||
mainResult.Merge(result)
|
||||
}
|
||||
|
||||
if validated != len(s.allOfValidators) {
|
||||
additionalMsg := ""
|
||||
if validated == 0 {
|
||||
additionalMsg = ". None validated"
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg))
|
||||
}
|
||||
}
|
||||
|
||||
if s.notValidator != nil {
|
||||
result := s.notValidator.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
if result.IsValid() {
|
||||
mainResult.AddErrors(mustNotValidatechemaMsg(s.Path))
|
||||
}
|
||||
}
|
||||
|
||||
if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map {
|
||||
val := data.(map[string]interface{})
|
||||
for key := range val {
|
||||
if dep, ok := s.Dependencies[key]; ok {
|
||||
|
||||
if dep.Schema != nil {
|
||||
mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.Options()...).Validate(data))
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dep.Property) > 0 {
|
||||
for _, depKey := range dep.Property {
|
||||
if _, ok := val[depKey]; !ok {
|
||||
mainResult.AddErrors(hasADependencyMsg(s.Path, depKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mainResult.Inc()
|
||||
// In the end we retain best failures for schema validation
|
||||
// plus, if any, composite errors which may explain special cases (tagged as IMPORTANT!).
|
||||
return mainResult.Merge(keepResultAllOf, keepResultOneOf, keepResultAnyOf)
|
||||
}
|
||||
104
vendor/k8s.io/kube-openapi/pkg/validation/validate/slice_validator.go
generated
vendored
Normal file
104
vendor/k8s.io/kube-openapi/pkg/validation/validate/slice_validator.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
type schemaSliceValidator struct {
|
||||
Path string
|
||||
In string
|
||||
MaxItems *int64
|
||||
MinItems *int64
|
||||
UniqueItems bool
|
||||
AdditionalItems *spec.SchemaOrBool
|
||||
Items *spec.SchemaOrArray
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) SetPath(path string) {
|
||||
s.Path = path
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
_, ok := source.(*spec.Schema)
|
||||
r := ok && kind == reflect.Slice
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
||||
result := new(Result)
|
||||
if data == nil {
|
||||
return result
|
||||
}
|
||||
val := reflect.ValueOf(data)
|
||||
size := val.Len()
|
||||
|
||||
if s.Items != nil && s.Items.Schema != nil {
|
||||
for i := 0; i < size; i++ {
|
||||
validator := s.Options.NewValidatorForIndex(i, s.Items.Schema, s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...)
|
||||
value := val.Index(i)
|
||||
result.Merge(validator.Validate(value.Interface()))
|
||||
}
|
||||
}
|
||||
|
||||
itemsSize := 0
|
||||
if s.Items != nil && len(s.Items.Schemas) > 0 {
|
||||
itemsSize = len(s.Items.Schemas)
|
||||
for i := 0; i < itemsSize; i++ {
|
||||
validator := s.Options.NewValidatorForIndex(i, &s.Items.Schemas[i], s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...)
|
||||
if val.Len() <= i {
|
||||
break
|
||||
}
|
||||
result.Merge(validator.Validate(val.Index(i).Interface()))
|
||||
}
|
||||
}
|
||||
if s.AdditionalItems != nil && itemsSize < size {
|
||||
if s.Items != nil && len(s.Items.Schemas) > 0 && !s.AdditionalItems.Allows {
|
||||
result.AddErrors(arrayDoesNotAllowAdditionalItemsMsg())
|
||||
}
|
||||
if s.AdditionalItems.Schema != nil {
|
||||
for i := itemsSize; i < size-itemsSize+1; i++ {
|
||||
validator := s.Options.NewValidatorForIndex(i, s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...)
|
||||
result.Merge(validator.Validate(val.Index(i).Interface()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.MinItems != nil {
|
||||
if err := MinItems(s.Path, s.In, int64(size), *s.MinItems); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
}
|
||||
if s.MaxItems != nil {
|
||||
if err := MaxItems(s.Path, s.In, int64(size), *s.MaxItems); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
}
|
||||
if s.UniqueItems {
|
||||
if err := UniqueItems(s.Path, s.In, val.Interface()); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
}
|
||||
result.Inc()
|
||||
return result
|
||||
}
|
||||
173
vendor/k8s.io/kube-openapi/pkg/validation/validate/type.go
generated
vendored
Normal file
173
vendor/k8s.io/kube-openapi/pkg/validation/validate/type.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
type typeValidator struct {
|
||||
Type spec.StringOrArray
|
||||
Nullable bool
|
||||
Format string
|
||||
In string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
|
||||
// internal type to JSON type with swagger 2.0 format (with go-openapi/strfmt extensions),
|
||||
// see https://github.com/go-openapi/strfmt/blob/master/README.md
|
||||
// TODO: this switch really is some sort of reverse lookup for formats. It should be provided by strfmt.
|
||||
switch data.(type) {
|
||||
case []byte, strfmt.Base64, *strfmt.Base64:
|
||||
return stringType, stringFormatByte
|
||||
case strfmt.CreditCard, *strfmt.CreditCard:
|
||||
return stringType, stringFormatCreditCard
|
||||
case strfmt.Date, *strfmt.Date:
|
||||
return stringType, stringFormatDate
|
||||
case strfmt.DateTime, *strfmt.DateTime:
|
||||
return stringType, stringFormatDateTime
|
||||
case strfmt.Duration, *strfmt.Duration:
|
||||
return stringType, stringFormatDuration
|
||||
case strfmt.Email, *strfmt.Email:
|
||||
return stringType, stringFormatEmail
|
||||
case strfmt.HexColor, *strfmt.HexColor:
|
||||
return stringType, stringFormatHexColor
|
||||
case strfmt.Hostname, *strfmt.Hostname:
|
||||
return stringType, stringFormatHostname
|
||||
case strfmt.IPv4, *strfmt.IPv4:
|
||||
return stringType, stringFormatIPv4
|
||||
case strfmt.IPv6, *strfmt.IPv6:
|
||||
return stringType, stringFormatIPv6
|
||||
case strfmt.ISBN, *strfmt.ISBN:
|
||||
return stringType, stringFormatISBN
|
||||
case strfmt.ISBN10, *strfmt.ISBN10:
|
||||
return stringType, stringFormatISBN10
|
||||
case strfmt.ISBN13, *strfmt.ISBN13:
|
||||
return stringType, stringFormatISBN13
|
||||
case strfmt.MAC, *strfmt.MAC:
|
||||
return stringType, stringFormatMAC
|
||||
case strfmt.Password, *strfmt.Password:
|
||||
return stringType, stringFormatPassword
|
||||
case strfmt.RGBColor, *strfmt.RGBColor:
|
||||
return stringType, stringFormatRGBColor
|
||||
case strfmt.SSN, *strfmt.SSN:
|
||||
return stringType, stringFormatSSN
|
||||
case strfmt.URI, *strfmt.URI:
|
||||
return stringType, stringFormatURI
|
||||
case strfmt.UUID, *strfmt.UUID:
|
||||
return stringType, stringFormatUUID
|
||||
case strfmt.UUID3, *strfmt.UUID3:
|
||||
return stringType, stringFormatUUID3
|
||||
case strfmt.UUID4, *strfmt.UUID4:
|
||||
return stringType, stringFormatUUID4
|
||||
case strfmt.UUID5, *strfmt.UUID5:
|
||||
return stringType, stringFormatUUID5
|
||||
// TODO: missing binary (io.ReadCloser)
|
||||
// TODO: missing json.Number
|
||||
default:
|
||||
val := reflect.ValueOf(data)
|
||||
tpe := val.Type()
|
||||
switch tpe.Kind() {
|
||||
case reflect.Bool:
|
||||
return booleanType, ""
|
||||
case reflect.String:
|
||||
return stringType, ""
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
// NOTE: that is the spec. With go-openapi, is that not uint32 for unsigned integers?
|
||||
return integerType, integerFormatInt32
|
||||
case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64:
|
||||
return integerType, integerFormatInt64
|
||||
case reflect.Float32:
|
||||
// NOTE: is that not numberFormatFloat?
|
||||
return numberType, numberFormatFloat32
|
||||
case reflect.Float64:
|
||||
// NOTE: is that not "double"?
|
||||
return numberType, numberFormatFloat64
|
||||
// NOTE: go arrays (reflect.Array) are not supported (fixed length)
|
||||
case reflect.Slice:
|
||||
return arrayType, ""
|
||||
case reflect.Map, reflect.Struct:
|
||||
return objectType, ""
|
||||
case reflect.Interface:
|
||||
// What to do here?
|
||||
panic("dunno what to do here")
|
||||
case reflect.Ptr:
|
||||
return t.schemaInfoForType(reflect.Indirect(val).Interface())
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (t *typeValidator) SetPath(path string) {
|
||||
t.Path = path
|
||||
}
|
||||
|
||||
func (t *typeValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
// typeValidator applies to Schema, Parameter and Header objects
|
||||
stpe := reflect.TypeOf(source)
|
||||
r := (len(t.Type) > 0 || t.Format != "") && stpe == specSchemaType
|
||||
debugLog("type validator for %q applies %t for %T (kind: %v)\n", t.Path, r, source, kind)
|
||||
return r
|
||||
}
|
||||
|
||||
func (t *typeValidator) Validate(data interface{}) *Result {
|
||||
result := new(Result)
|
||||
result.Inc()
|
||||
if data == nil {
|
||||
// nil or zero value for the passed structure require Type: null
|
||||
if len(t.Type) > 0 && !t.Type.Contains(nullType) && !t.Nullable { // TODO: if a property is not required it also passes this
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), nullType))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// check if the type matches, should be used in every validator chain as first item
|
||||
val := reflect.Indirect(reflect.ValueOf(data))
|
||||
kind := val.Kind()
|
||||
|
||||
// infer schema type (JSON) and format from passed data type
|
||||
schType, format := t.schemaInfoForType(data)
|
||||
|
||||
debugLog("path: %s, schType: %s, format: %s, expType: %s, expFmt: %s, kind: %s", t.Path, schType, format, t.Type, t.Format, val.Kind().String())
|
||||
|
||||
// check numerical types
|
||||
// TODO: check unsigned ints
|
||||
// TODO: check json.Number (see schema.go)
|
||||
isLowerInt := t.Format == integerFormatInt64 && format == integerFormatInt32
|
||||
isLowerFloat := t.Format == numberFormatFloat64 && format == numberFormatFloat32
|
||||
isFloatInt := schType == numberType && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains(integerType)
|
||||
isIntFloat := schType == integerType && t.Type.Contains(numberType)
|
||||
|
||||
if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) {
|
||||
// TODO: test case
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format))
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(numberType) || t.Type.Contains(integerType)) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
|
||||
return result
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) {
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType))
|
||||
}
|
||||
return result
|
||||
}
|
||||
242
vendor/k8s.io/kube-openapi/pkg/validation/validate/validator.go
generated
vendored
Normal file
242
vendor/k8s.io/kube-openapi/pkg/validation/validate/validator.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// ValueValidator validates the values it applies to.
|
||||
type ValueValidator interface {
|
||||
// SetPath sets the exact path of the validator prior to calling Validate.
|
||||
// The exact path contains the map keys and array indices to locate the
|
||||
// value to be validated from the root data element.
|
||||
SetPath(path string)
|
||||
// Applies returns true if the validator applies to the valueKind
|
||||
// from source. Validate will be called if and only if Applies returns true.
|
||||
Applies(source interface{}, valueKind reflect.Kind) bool
|
||||
// Validate validates the value.
|
||||
Validate(value interface{}) *Result
|
||||
}
|
||||
|
||||
type basicCommonValidator struct {
|
||||
Path string
|
||||
In string
|
||||
Default interface{}
|
||||
Enum []interface{}
|
||||
}
|
||||
|
||||
func (b *basicCommonValidator) SetPath(path string) {
|
||||
b.Path = path
|
||||
}
|
||||
|
||||
func (b *basicCommonValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
switch source.(type) {
|
||||
case *spec.Schema:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *basicCommonValidator) Validate(data interface{}) (res *Result) {
|
||||
if len(b.Enum) > 0 {
|
||||
for _, enumValue := range b.Enum {
|
||||
actualType := reflect.TypeOf(enumValue)
|
||||
if actualType != nil { // Safeguard
|
||||
expectedValue := reflect.ValueOf(data)
|
||||
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
|
||||
if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type numberValidator struct {
|
||||
Path string
|
||||
In string
|
||||
Default interface{}
|
||||
MultipleOf *float64
|
||||
Maximum *float64
|
||||
ExclusiveMaximum bool
|
||||
Minimum *float64
|
||||
ExclusiveMinimum bool
|
||||
// Allows for more accurate behavior regarding integers
|
||||
Type string
|
||||
Format string
|
||||
}
|
||||
|
||||
func (n *numberValidator) SetPath(path string) {
|
||||
n.Path = path
|
||||
}
|
||||
|
||||
func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
switch source.(type) {
|
||||
case *spec.Schema:
|
||||
isInt := kind >= reflect.Int && kind <= reflect.Uint64
|
||||
isFloat := kind == reflect.Float32 || kind == reflect.Float64
|
||||
r := isInt || isFloat
|
||||
debugLog("schema props validator for %q applies %t for %T (kind: %v) isInt=%t, isFloat=%t\n", n.Path, r, source, kind, isInt, isFloat)
|
||||
return r
|
||||
}
|
||||
debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", n.Path, false, source, kind)
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate provides a validator for generic JSON numbers,
|
||||
//
|
||||
// By default, numbers are internally represented as float64.
|
||||
// Formats float, or float32 may alter this behavior by mapping to float32.
|
||||
// A special validation process is followed for integers, with optional "format":
|
||||
// this is an attempt to provide a validation with native types.
|
||||
//
|
||||
// NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled
|
||||
// as float64, loss of information remains possible (e.g. on very large integers).
|
||||
//
|
||||
// Since this value directly comes from the unmarshalling, it is not possible
|
||||
// at this stage of processing to check further and guarantee the correctness of such values.
|
||||
//
|
||||
// Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER)
|
||||
// would check we do not get such a loss.
|
||||
//
|
||||
// If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings().
|
||||
//
|
||||
// TODO: consider replacing boundary check errors by simple warnings.
|
||||
//
|
||||
// TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?)
|
||||
func (n *numberValidator) Validate(val interface{}) *Result {
|
||||
res := new(Result)
|
||||
|
||||
resMultiple := new(Result)
|
||||
resMinimum := new(Result)
|
||||
resMaximum := new(Result)
|
||||
|
||||
// Used only to attempt to validate constraint on value,
|
||||
// even though value or constraint specified do not match type and format
|
||||
data := valueHelp.asFloat64(val)
|
||||
|
||||
// Is the provided value within the range of the specified numeric type and format?
|
||||
res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
|
||||
|
||||
// nolint: dupl
|
||||
if n.MultipleOf != nil {
|
||||
// Is the constraint specifier within the range of the specific numeric type and format?
|
||||
resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path))
|
||||
if resMultiple.IsValid() {
|
||||
// Constraint validated with compatible types
|
||||
if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil {
|
||||
resMultiple.Merge(errorHelp.sErr(err))
|
||||
}
|
||||
} else {
|
||||
// Constraint nevertheless validated, converted as general number
|
||||
if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil {
|
||||
resMultiple.Merge(errorHelp.sErr(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: dupl
|
||||
if n.Maximum != nil {
|
||||
// Is the constraint specifier within the range of the specific numeric type and format?
|
||||
resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path))
|
||||
if resMaximum.IsValid() {
|
||||
// Constraint validated with compatible types
|
||||
if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil {
|
||||
resMaximum.Merge(errorHelp.sErr(err))
|
||||
}
|
||||
} else {
|
||||
// Constraint nevertheless validated, converted as general number
|
||||
if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil {
|
||||
resMaximum.Merge(errorHelp.sErr(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: dupl
|
||||
if n.Minimum != nil {
|
||||
// Is the constraint specifier within the range of the specific numeric type and format?
|
||||
resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path))
|
||||
if resMinimum.IsValid() {
|
||||
// Constraint validated with compatible types
|
||||
if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil {
|
||||
resMinimum.Merge(errorHelp.sErr(err))
|
||||
}
|
||||
} else {
|
||||
// Constraint nevertheless validated, converted as general number
|
||||
if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil {
|
||||
resMinimum.Merge(errorHelp.sErr(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
res.Merge(resMultiple, resMinimum, resMaximum)
|
||||
res.Inc()
|
||||
return res
|
||||
}
|
||||
|
||||
type stringValidator struct {
|
||||
MaxLength *int64
|
||||
MinLength *int64
|
||||
Pattern string
|
||||
Path string
|
||||
In string
|
||||
}
|
||||
|
||||
func (s *stringValidator) SetPath(path string) {
|
||||
s.Path = path
|
||||
}
|
||||
|
||||
func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
switch source.(type) {
|
||||
case *spec.Schema:
|
||||
r := kind == reflect.String
|
||||
debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
|
||||
return r
|
||||
}
|
||||
debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, false, source, kind)
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *stringValidator) Validate(val interface{}) *Result {
|
||||
data, ok := val.(string)
|
||||
if !ok {
|
||||
return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val))
|
||||
}
|
||||
|
||||
if s.MaxLength != nil {
|
||||
if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil {
|
||||
return errorHelp.sErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
if s.MinLength != nil {
|
||||
if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil {
|
||||
return errorHelp.sErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
if s.Pattern != "" {
|
||||
if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil {
|
||||
return errorHelp.sErr(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
386
vendor/k8s.io/kube-openapi/pkg/validation/validate/values.go
generated
vendored
Normal file
386
vendor/k8s.io/kube-openapi/pkg/validation/validate/values.go
generated
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"k8s.io/kube-openapi/pkg/validation/errors"
|
||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||
)
|
||||
|
||||
// Enum validates if the data is a member of the enum
|
||||
func Enum(path, in string, data interface{}, enum interface{}) *errors.Validation {
|
||||
val := reflect.ValueOf(enum)
|
||||
if val.Kind() != reflect.Slice {
|
||||
return nil
|
||||
}
|
||||
|
||||
var values []interface{}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
ele := val.Index(i)
|
||||
enumValue := ele.Interface()
|
||||
if data != nil {
|
||||
if reflect.DeepEqual(data, enumValue) {
|
||||
return nil
|
||||
}
|
||||
actualType := reflect.TypeOf(enumValue)
|
||||
if actualType == nil { // Safeguard. Frankly, I don't know how we may get a nil
|
||||
continue
|
||||
}
|
||||
expectedValue := reflect.ValueOf(data)
|
||||
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
|
||||
// Attempt comparison after type conversion
|
||||
if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
values = append(values, enumValue)
|
||||
}
|
||||
return errors.EnumFail(path, in, data, values)
|
||||
}
|
||||
|
||||
// MinItems validates that there are at least n items in a slice
|
||||
func MinItems(path, in string, size, min int64) *errors.Validation {
|
||||
if size < min {
|
||||
return errors.TooFewItems(path, in, min, size)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxItems validates that there are at most n items in a slice
|
||||
func MaxItems(path, in string, size, max int64) *errors.Validation {
|
||||
if size > max {
|
||||
return errors.TooManyItems(path, in, max, size)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UniqueItems validates that the provided slice has unique elements
|
||||
func UniqueItems(path, in string, data interface{}) *errors.Validation {
|
||||
val := reflect.ValueOf(data)
|
||||
if val.Kind() != reflect.Slice {
|
||||
return nil
|
||||
}
|
||||
var unique []interface{}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
v := val.Index(i).Interface()
|
||||
for _, u := range unique {
|
||||
if reflect.DeepEqual(v, u) {
|
||||
return errors.DuplicateItems(path, in)
|
||||
}
|
||||
}
|
||||
unique = append(unique, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinLength validates a string for minimum length
|
||||
func MinLength(path, in, data string, minLength int64) *errors.Validation {
|
||||
strLen := int64(utf8.RuneCount([]byte(data)))
|
||||
if strLen < minLength {
|
||||
return errors.TooShort(path, in, minLength, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxLength validates a string for maximum length
|
||||
func MaxLength(path, in, data string, maxLength int64) *errors.Validation {
|
||||
strLen := int64(utf8.RuneCount([]byte(data)))
|
||||
if strLen > maxLength {
|
||||
return errors.TooLong(path, in, maxLength, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Required validates an interface for requiredness
|
||||
func Required(path, in string, data interface{}) *errors.Validation {
|
||||
val := reflect.ValueOf(data)
|
||||
if val.IsValid() {
|
||||
if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) {
|
||||
return errors.Required(path, in)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return errors.Required(path, in)
|
||||
}
|
||||
|
||||
// Pattern validates a string against a regular expression
|
||||
func Pattern(path, in, data, pattern string) *errors.Validation {
|
||||
re, err := compileRegexp(pattern)
|
||||
if err != nil {
|
||||
return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error()), data)
|
||||
}
|
||||
if !re.MatchString(data) {
|
||||
return errors.FailedPattern(path, in, pattern, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaximumInt validates if a number is smaller than a given maximum
|
||||
func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data > max) || (exclusive && data >= max) {
|
||||
return errors.ExceedsMaximumInt(path, in, max, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaximumUint validates if a number is smaller than a given maximum
|
||||
func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data > max) || (exclusive && data >= max) {
|
||||
return errors.ExceedsMaximumUint(path, in, max, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Maximum validates if a number is smaller than a given maximum
|
||||
func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data > max) || (exclusive && data >= max) {
|
||||
return errors.ExceedsMaximum(path, in, max, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Minimum validates if a number is smaller than a given minimum
|
||||
func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data < min) || (exclusive && data <= min) {
|
||||
return errors.ExceedsMinimum(path, in, min, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinimumInt validates if a number is smaller than a given minimum
|
||||
func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data < min) || (exclusive && data <= min) {
|
||||
return errors.ExceedsMinimumInt(path, in, min, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinimumUint validates if a number is smaller than a given minimum
|
||||
func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data < min) || (exclusive && data <= min) {
|
||||
return errors.ExceedsMinimumUint(path, in, min, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultipleOf validates if the provided number is a multiple of the factor
|
||||
func MultipleOf(path, in string, data, factor float64) *errors.Validation {
|
||||
// multipleOf factor must be positive
|
||||
if factor <= 0 {
|
||||
return errors.MultipleOfMustBePositive(path, in, factor)
|
||||
}
|
||||
var mult float64
|
||||
if factor < 1 {
|
||||
mult = 1 / factor * data
|
||||
} else {
|
||||
mult = data / factor
|
||||
}
|
||||
if !swag.IsFloat64AJSONInteger(mult) {
|
||||
return errors.NotMultipleOf(path, in, factor, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultipleOfInt validates if the provided integer is a multiple of the factor
|
||||
func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation {
|
||||
// multipleOf factor must be positive
|
||||
if factor <= 0 {
|
||||
return errors.MultipleOfMustBePositive(path, in, factor)
|
||||
}
|
||||
mult := data / factor
|
||||
if mult*factor != data {
|
||||
return errors.NotMultipleOf(path, in, factor, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultipleOfUint validates if the provided unsigned integer is a multiple of the factor
|
||||
func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation {
|
||||
// multipleOf factor must be positive
|
||||
if factor == 0 {
|
||||
return errors.MultipleOfMustBePositive(path, in, factor)
|
||||
}
|
||||
mult := data / factor
|
||||
if mult*factor != data {
|
||||
return errors.NotMultipleOf(path, in, factor, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FormatOf validates if a string matches a format in the format registry
|
||||
func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation {
|
||||
if registry == nil {
|
||||
registry = strfmt.Default
|
||||
}
|
||||
if ok := registry.ContainsName(format); !ok {
|
||||
return errors.InvalidTypeName(format)
|
||||
}
|
||||
if ok := registry.Validates(format, data); !ok {
|
||||
return errors.InvalidType(path, in, format, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaximumNativeType provides native type constraint validation as a facade
|
||||
// to various numeric types versions of Maximum constraint check.
|
||||
//
|
||||
// Assumes that any possible loss conversion during conversion has been
|
||||
// checked beforehand.
|
||||
//
|
||||
// NOTE: currently, the max value is marshalled as a float64, no matter what,
|
||||
// which means there may be a loss during conversions (e.g. for very large integers)
|
||||
//
|
||||
// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
|
||||
func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation {
|
||||
kind := reflect.ValueOf(val).Type().Kind()
|
||||
switch kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
value := valueHelp.asInt64(val)
|
||||
return MaximumInt(path, in, value, int64(max), exclusive)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
value := valueHelp.asUint64(val)
|
||||
if max < 0 {
|
||||
return errors.ExceedsMaximum(path, in, max, exclusive, val)
|
||||
}
|
||||
return MaximumUint(path, in, value, uint64(max), exclusive)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fallthrough
|
||||
default:
|
||||
value := valueHelp.asFloat64(val)
|
||||
return Maximum(path, in, value, max, exclusive)
|
||||
}
|
||||
}
|
||||
|
||||
// MinimumNativeType provides native type constraint validation as a facade
|
||||
// to various numeric types versions of Minimum constraint check.
|
||||
//
|
||||
// Assumes that any possible loss conversion during conversion has been
|
||||
// checked beforehand.
|
||||
//
|
||||
// NOTE: currently, the min value is marshalled as a float64, no matter what,
|
||||
// which means there may be a loss during conversions (e.g. for very large integers)
|
||||
//
|
||||
// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
|
||||
func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation {
|
||||
kind := reflect.ValueOf(val).Type().Kind()
|
||||
switch kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
value := valueHelp.asInt64(val)
|
||||
return MinimumInt(path, in, value, int64(min), exclusive)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
value := valueHelp.asUint64(val)
|
||||
if min < 0 {
|
||||
return nil
|
||||
}
|
||||
return MinimumUint(path, in, value, uint64(min), exclusive)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fallthrough
|
||||
default:
|
||||
value := valueHelp.asFloat64(val)
|
||||
return Minimum(path, in, value, min, exclusive)
|
||||
}
|
||||
}
|
||||
|
||||
// MultipleOfNativeType provides native type constraint validation as a facade
|
||||
// to various numeric types version of MultipleOf constraint check.
|
||||
//
|
||||
// Assumes that any possible loss conversion during conversion has been
|
||||
// checked beforehand.
|
||||
//
|
||||
// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what,
|
||||
// which means there may be a loss during conversions (e.g. for very large integers)
|
||||
//
|
||||
// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
|
||||
func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation {
|
||||
kind := reflect.ValueOf(val).Type().Kind()
|
||||
switch kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
value := valueHelp.asInt64(val)
|
||||
return MultipleOfInt(path, in, value, int64(multipleOf))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
value := valueHelp.asUint64(val)
|
||||
return MultipleOfUint(path, in, value, uint64(multipleOf))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fallthrough
|
||||
default:
|
||||
value := valueHelp.asFloat64(val)
|
||||
return MultipleOf(path, in, value, multipleOf)
|
||||
}
|
||||
}
|
||||
|
||||
// IsValueValidAgainstRange checks that a numeric value is compatible with
|
||||
// the range defined by Type and Format, that is, may be converted without loss.
|
||||
//
|
||||
// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L
|
||||
func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error {
|
||||
kind := reflect.ValueOf(val).Type().Kind()
|
||||
|
||||
// What is the string representation of val
|
||||
stringRep := ""
|
||||
switch kind {
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
stringRep = swag.FormatUint64(valueHelp.asUint64(val))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
stringRep = swag.FormatInt64(valueHelp.asInt64(val))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
stringRep = swag.FormatFloat64(valueHelp.asFloat64(val))
|
||||
default:
|
||||
return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path)
|
||||
}
|
||||
|
||||
var errVal error
|
||||
|
||||
switch typeName {
|
||||
case integerType:
|
||||
switch format {
|
||||
case integerFormatInt32:
|
||||
_, errVal = swag.ConvertInt32(stringRep)
|
||||
case integerFormatUInt32:
|
||||
_, errVal = swag.ConvertUint32(stringRep)
|
||||
case integerFormatUInt64:
|
||||
_, errVal = swag.ConvertUint64(stringRep)
|
||||
case integerFormatInt64:
|
||||
fallthrough
|
||||
default:
|
||||
_, errVal = swag.ConvertInt64(stringRep)
|
||||
}
|
||||
case numberType:
|
||||
fallthrough
|
||||
default:
|
||||
switch format {
|
||||
case numberFormatFloat, numberFormatFloat32:
|
||||
_, errVal = swag.ConvertFloat32(stringRep)
|
||||
case numberFormatDouble, numberFormatFloat64:
|
||||
fallthrough
|
||||
default:
|
||||
// No check can be performed here since
|
||||
// no number beyond float64 is supported
|
||||
}
|
||||
}
|
||||
if errVal != nil { // We don't report the actual errVal from strconv
|
||||
if format != "" {
|
||||
errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path)
|
||||
} else {
|
||||
errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path)
|
||||
}
|
||||
}
|
||||
return errVal
|
||||
}
|
||||
Reference in New Issue
Block a user