1 Star 0 Fork 0

zhuchance / kubernetes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
openapi_aggregator.go 10.58 KB
一键复制 编辑 原始数据 按行查看 历史
mbohlool 提交于 2017-06-01 03:14 . Aggregate OpenAPI specs
/*
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 openapi
import (
"fmt"
"strings"
"github.com/go-openapi/spec"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apiserver/pkg/util/trie"
)
const (
DEFINITION_PREFIX = "#/definitions/"
)
var cloner = conversion.NewCloner()
// Run a walkRefCallback method on all references of an OpenAPI spec
type walkAllRefs struct {
// walkRefCallback will be called on each reference and the return value
// will replace that reference. This will allow the callers to change
// all/some references of an spec (e.g. useful in renaming definitions).
walkRefCallback func(ref spec.Ref) spec.Ref
// The spec to walk through.
root *spec.Swagger
}
func newWalkAllRefs(walkRef func(ref spec.Ref) spec.Ref, sp *spec.Swagger) *walkAllRefs {
return &walkAllRefs{
walkRefCallback: walkRef,
root: sp,
}
}
func (s *walkAllRefs) walkRef(ref spec.Ref) spec.Ref {
if ref.String() != "" {
refStr := ref.String()
// References that start with #/definitions/ has a definition
// inside the same spec file. If that is the case, walk through
// those definitions too.
// We do not support external references yet.
if strings.HasPrefix(refStr, DEFINITION_PREFIX) {
def := s.root.Definitions[refStr[len(DEFINITION_PREFIX):]]
s.walkSchema(&def)
}
}
return s.walkRefCallback(ref)
}
func (s *walkAllRefs) walkSchema(schema *spec.Schema) {
if schema == nil {
return
}
schema.Ref = s.walkRef(schema.Ref)
for _, v := range schema.Definitions {
s.walkSchema(&v)
}
for _, v := range schema.Properties {
s.walkSchema(&v)
}
for _, v := range schema.PatternProperties {
s.walkSchema(&v)
}
for _, v := range schema.AllOf {
s.walkSchema(&v)
}
for _, v := range schema.AnyOf {
s.walkSchema(&v)
}
for _, v := range schema.OneOf {
s.walkSchema(&v)
}
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 _, v := range schema.Items.Schemas {
s.walkSchema(&v)
}
}
}
func (s *walkAllRefs) walkParams(params []spec.Parameter) {
if params == nil {
return
}
for _, param := range params {
param.Ref = s.walkRef(param.Ref)
s.walkSchema(param.Schema)
if param.Items != nil {
param.Items.Ref = s.walkRef(param.Items.Ref)
}
}
}
func (s *walkAllRefs) walkResponse(resp *spec.Response) {
if resp == nil {
return
}
resp.Ref = s.walkRef(resp.Ref)
s.walkSchema(resp.Schema)
}
func (s *walkAllRefs) 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 *walkAllRefs) Start() {
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)
}
}
// FilterSpecByPaths remove unnecessary paths and unused definitions.
func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) {
// First remove unwanted paths
prefixes := trie.New(keepPathPrefixes)
orgPaths := sp.Paths
if orgPaths == nil {
return
}
sp.Paths = &spec.Paths{
VendorExtensible: orgPaths.VendorExtensible,
Paths: map[string]spec.PathItem{},
}
for path, pathItem := range orgPaths.Paths {
if !prefixes.HasPrefix(path) {
continue
}
sp.Paths.Paths[path] = pathItem
}
// Walk all references to find all definition references.
usedDefinitions := map[string]bool{}
newWalkAllRefs(func(ref spec.Ref) spec.Ref {
if ref.String() != "" {
refStr := ref.String()
if strings.HasPrefix(refStr, DEFINITION_PREFIX) {
usedDefinitions[refStr[len(DEFINITION_PREFIX):]] = true
}
}
return ref
}, sp).Start()
// Remove unused definitions
orgDefinitions := sp.Definitions
sp.Definitions = spec.Definitions{}
for k, v := range orgDefinitions {
if usedDefinitions[k] {
sp.Definitions[k] = v
}
}
}
func equalSchemaMap(s1, s2 map[string]spec.Schema) bool {
if len(s1) != len(s2) {
return false
}
for k, v := range s1 {
v2, found := s2[k]
if !found {
return false
}
if !EqualSchema(&v, &v2) {
return false
}
}
return true
}
func equalSchemaArray(s1, s2 []spec.Schema) bool {
if s1 == nil || s2 == nil {
return s1 == nil && s2 == nil
}
if len(s1) != len(s2) {
return false
}
for _, v1 := range s1 {
found := false
for _, v2 := range s2 {
if EqualSchema(&v1, &v2) {
found = true
break
}
}
if !found {
return false
}
}
for _, v2 := range s2 {
found := false
for _, v1 := range s1 {
if EqualSchema(&v1, &v2) {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func equalSchemaOrBool(s1, s2 *spec.SchemaOrBool) bool {
if s1 == nil || s2 == nil {
return s1 == s2
}
if s1.Allows != s2.Allows {
return false
}
if !EqualSchema(s1.Schema, s2.Schema) {
return false
}
return true
}
func equalSchemaOrArray(s1, s2 *spec.SchemaOrArray) bool {
if s1 == nil || s2 == nil {
return s1 == s2
}
if !EqualSchema(s1.Schema, s2.Schema) {
return false
}
if !equalSchemaArray(s1.Schemas, s2.Schemas) {
return false
}
return true
}
func equalStringArray(s1, s2 []string) bool {
if len(s1) != len(s2) {
return false
}
for _, v1 := range s1 {
found := false
for _, v2 := range s2 {
if v1 == v2 {
found = true
break
}
}
if !found {
return false
}
}
for _, v2 := range s2 {
found := false
for _, v1 := range s1 {
if v1 == v2 {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func equalFloatPointer(s1, s2 *float64) bool {
if s1 == nil || s2 == nil {
return s1 == s2
}
return *s1 == *s2
}
func equalIntPointer(s1, s2 *int64) bool {
if s1 == nil || s2 == nil {
return s1 == s2
}
return *s1 == *s2
}
// EqualSchema returns true if models have the same properties and references
// even if they have different documentation.
func EqualSchema(s1, s2 *spec.Schema) bool {
if s1 == nil || s2 == nil {
return s1 == s2
}
if s1.Ref.String() != s2.Ref.String() {
return false
}
if !equalSchemaMap(s1.Definitions, s2.Definitions) {
return false
}
if !equalSchemaMap(s1.Properties, s2.Properties) {
fmt.Println("Not equal props")
return false
}
if !equalSchemaMap(s1.PatternProperties, s2.PatternProperties) {
return false
}
if !equalSchemaArray(s1.AllOf, s2.AllOf) {
return false
}
if !equalSchemaArray(s1.AnyOf, s2.AnyOf) {
return false
}
if !equalSchemaArray(s1.OneOf, s2.OneOf) {
return false
}
if !EqualSchema(s1.Not, s2.Not) {
return false
}
if !equalSchemaOrBool(s1.AdditionalProperties, s2.AdditionalProperties) {
return false
}
if !equalSchemaOrBool(s1.AdditionalItems, s2.AdditionalItems) {
return false
}
if !equalSchemaOrArray(s1.Items, s2.Items) {
return false
}
if !equalStringArray(s1.Type, s2.Type) {
return false
}
if s1.Format != s2.Format {
return false
}
if !equalFloatPointer(s1.Minimum, s2.Minimum) {
return false
}
if !equalFloatPointer(s1.Maximum, s2.Maximum) {
return false
}
if s1.ExclusiveMaximum != s2.ExclusiveMaximum {
return false
}
if s1.ExclusiveMinimum != s2.ExclusiveMinimum {
return false
}
if !equalFloatPointer(s1.MultipleOf, s2.MultipleOf) {
return false
}
if !equalIntPointer(s1.MaxLength, s2.MaxLength) {
return false
}
if !equalIntPointer(s1.MinLength, s2.MinLength) {
return false
}
if !equalIntPointer(s1.MaxItems, s2.MaxItems) {
return false
}
if !equalIntPointer(s1.MinItems, s2.MinItems) {
return false
}
if s1.Pattern != s2.Pattern {
return false
}
if s1.UniqueItems != s2.UniqueItems {
return false
}
if !equalIntPointer(s1.MaxProperties, s2.MaxProperties) {
return false
}
if !equalIntPointer(s1.MinProperties, s2.MinProperties) {
return false
}
if !equalStringArray(s1.Required, s2.Required) {
return false
}
return len(s1.Enum) == 0 && len(s2.Enum) == 0 && len(s1.Dependencies) == 0 && len(s2.Dependencies) == 0
}
func renameDefinition(s *spec.Swagger, old, new string) {
old_ref := DEFINITION_PREFIX + old
new_ref := DEFINITION_PREFIX + new
newWalkAllRefs(func(ref spec.Ref) spec.Ref {
if ref.String() == old_ref {
return spec.MustCreateRef(new_ref)
}
return ref
}, s).Start()
s.Definitions[new] = s.Definitions[old]
delete(s.Definitions, old)
}
// Copy paths and definitions from source to dest, rename definitions if needed.
// dest will be mutated, and source will not be changed.
func MergeSpecs(dest, source *spec.Swagger) error {
source, err := CloneSpec(source)
if err != nil {
return err
}
for k, v := range source.Paths.Paths {
if _, found := dest.Paths.Paths[k]; found {
return fmt.Errorf("Unable to merge: Duplicated path %s", k)
}
dest.Paths.Paths[k] = v
}
usedNames := map[string]bool{}
for k := range dest.Definitions {
usedNames[k] = true
}
type Rename struct {
from, to string
}
renames := []Rename{}
for k, v := range source.Definitions {
v2, found := dest.Definitions[k]
if found || usedNames[k] {
if found && EqualSchema(&v, &v2) {
continue
}
i := 2
newName := fmt.Sprintf("%s_v%d", k, i)
for usedNames[newName] {
i += 1
newName = fmt.Sprintf("%s_v%d", k, i)
}
renames = append(renames, Rename{from: k, to: newName})
usedNames[newName] = true
} else {
usedNames[k] = true
}
}
for _, r := range renames {
renameDefinition(source, r.from, r.to)
}
for k, v := range source.Definitions {
if _, found := dest.Definitions[k]; !found {
dest.Definitions[k] = v
}
}
return nil
}
// Clone OpenAPI spec
func CloneSpec(source *spec.Swagger) (*spec.Swagger, error) {
if ret, err := cloner.DeepCopy(source); err != nil {
return nil, err
} else {
return ret.(*spec.Swagger), nil
}
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/meoom/kubernetes.git
git@gitee.com:meoom/kubernetes.git
meoom
kubernetes
kubernetes
v1.7.8

搜索帮助

344bd9b3 5694891 D2dac590 5694891