mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-20 10:20:51 +00:00
Update Godeps to use kube-openapi
This commit is contained in:
parent
56fd5853b3
commit
3bde9a1e8c
24
Godeps/Godeps.json
generated
24
Godeps/Godeps.json
generated
@ -3032,6 +3032,30 @@
|
||||
"Comment": "v1.2.0-beta.1",
|
||||
"Rev": "c2ac40f1adf8c42a79badddb2a2acd673cae3bcb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/aggregator",
|
||||
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/builder",
|
||||
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/common",
|
||||
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/generators",
|
||||
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/handler",
|
||||
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util",
|
||||
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/utils/exec",
|
||||
"Rev": "9fdc871a36f37980dd85f96d576b20d564cc0784"
|
||||
|
1260
Godeps/LICENSES
generated
1260
Godeps/LICENSES
generated
File diff suppressed because it is too large
Load Diff
202
vendor/k8s.io/kube-openapi/LICENSE
generated
vendored
Normal file
202
vendor/k8s.io/kube-openapi/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.
|
276
vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go
generated
vendored
Normal file
276
vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go
generated
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
definitionPrefix = "#/definitions/"
|
||||
)
|
||||
|
||||
// Run a walkRefCallback method on all references of an OpenAPI spec
|
||||
type referenceWalker 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
|
||||
|
||||
// Keep track of visited references
|
||||
alreadyVisited map[string]bool
|
||||
}
|
||||
|
||||
func walkOnAllReferences(walkRef func(ref spec.Ref) spec.Ref, sp *spec.Swagger) {
|
||||
walker := &referenceWalker{walkRefCallback: walkRef, root: sp, alreadyVisited: map[string]bool{}}
|
||||
walker.Start()
|
||||
}
|
||||
|
||||
func (s *referenceWalker) walkRef(ref spec.Ref) spec.Ref {
|
||||
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 !s.alreadyVisited[refStr] && strings.HasPrefix(refStr, definitionPrefix) {
|
||||
s.alreadyVisited[refStr] = true
|
||||
def := s.root.Definitions[refStr[len(definitionPrefix):]]
|
||||
s.walkSchema(&def)
|
||||
}
|
||||
return s.walkRefCallback(ref)
|
||||
}
|
||||
|
||||
func (s *referenceWalker) 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 *referenceWalker) 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 *referenceWalker) walkResponse(resp *spec.Response) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
resp.Ref = s.walkRef(resp.Ref)
|
||||
s.walkSchema(resp.Schema)
|
||||
}
|
||||
|
||||
func (s *referenceWalker) 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 *referenceWalker) 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 := util.NewTrie(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{}
|
||||
|
||||
walkOnAllReferences(func(ref spec.Ref) spec.Ref {
|
||||
if ref.String() != "" {
|
||||
refStr := ref.String()
|
||||
if strings.HasPrefix(refStr, definitionPrefix) {
|
||||
usedDefinitions[refStr[len(definitionPrefix):]] = true
|
||||
}
|
||||
}
|
||||
return ref
|
||||
}, sp)
|
||||
|
||||
// Remove unused definitions
|
||||
orgDefinitions := sp.Definitions
|
||||
sp.Definitions = spec.Definitions{}
|
||||
for k, v := range orgDefinitions {
|
||||
if usedDefinitions[k] {
|
||||
sp.Definitions[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renameDefinition(s *spec.Swagger, old, new string) {
|
||||
old_ref := definitionPrefix + old
|
||||
new_ref := definitionPrefix + new
|
||||
walkOnAllReferences(func(ref spec.Ref) spec.Ref {
|
||||
if ref.String() == old_ref {
|
||||
return spec.MustCreateRef(new_ref)
|
||||
}
|
||||
return ref
|
||||
}, s)
|
||||
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 {
|
||||
sourceCopy, err := CloneSpec(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range sourceCopy.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 sourceCopy.Definitions {
|
||||
if usedNames[k] {
|
||||
v2, found := dest.Definitions[k]
|
||||
// Reuse model iff they are exactly the same.
|
||||
if found && reflect.DeepEqual(v, v2) {
|
||||
continue
|
||||
}
|
||||
i := 2
|
||||
newName := fmt.Sprintf("%s_v%d", k, i)
|
||||
_, foundInSource := sourceCopy.Definitions[newName]
|
||||
for usedNames[newName] || foundInSource {
|
||||
i += 1
|
||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||
_, foundInSource = sourceCopy.Definitions[newName]
|
||||
}
|
||||
renames = append(renames, Rename{from: k, to: newName})
|
||||
usedNames[newName] = true
|
||||
}
|
||||
}
|
||||
for _, r := range renames {
|
||||
renameDefinition(sourceCopy, r.from, r.to)
|
||||
}
|
||||
for k, v := range sourceCopy.Definitions {
|
||||
if _, found := dest.Definitions[k]; !found {
|
||||
dest.Definitions[k] = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clone OpenAPI spec
|
||||
func CloneSpec(source *spec.Swagger) (*spec.Swagger, error) {
|
||||
// TODO(mehdy): Find a faster way to clone an spec
|
||||
bytes, err := json.Marshal(source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ret spec.Swagger
|
||||
err = json.Unmarshal(bytes, &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
20
vendor/k8s.io/kube-openapi/pkg/builder/doc.go
generated
vendored
Normal file
20
vendor/k8s.io/kube-openapi/pkg/builder/doc.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package builder contains code to generate OpenAPI discovery spec (which
|
||||
// initial version of it also known as Swagger 2.0).
|
||||
// For more details: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
|
||||
package builder
|
424
vendor/k8s.io/kube-openapi/pkg/builder/openapi.go
generated
vendored
Normal file
424
vendor/k8s.io/kube-openapi/pkg/builder/openapi.go
generated
vendored
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
restful "github.com/emicklei/go-restful"
|
||||
"github.com/go-openapi/spec"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
OpenAPIVersion = "2.0"
|
||||
// TODO: Make this configurable.
|
||||
extensionPrefix = "x-kubernetes-"
|
||||
)
|
||||
|
||||
type openAPI struct {
|
||||
config *common.Config
|
||||
swagger *spec.Swagger
|
||||
protocolList []string
|
||||
definitions map[string]common.OpenAPIDefinition
|
||||
}
|
||||
|
||||
// BuildOpenAPISpec builds OpenAPI spec given a list of webservices (containing routes) and common.Config to customize it.
|
||||
func BuildOpenAPISpec(webServices []*restful.WebService, config *common.Config) (*spec.Swagger, error) {
|
||||
o := openAPI{
|
||||
config: config,
|
||||
swagger: &spec.Swagger{
|
||||
SwaggerProps: spec.SwaggerProps{
|
||||
Swagger: OpenAPIVersion,
|
||||
Definitions: spec.Definitions{},
|
||||
Paths: &spec.Paths{Paths: map[string]spec.PathItem{}},
|
||||
Info: config.Info,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := o.init(webServices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return o.swagger, nil
|
||||
}
|
||||
|
||||
func (o *openAPI) init(webServices []*restful.WebService) error {
|
||||
if o.config.GetOperationIDAndTags == nil {
|
||||
o.config.GetOperationIDAndTags = func(r *restful.Route) (string, []string, error) {
|
||||
return r.Operation, nil, nil
|
||||
}
|
||||
}
|
||||
if o.config.GetDefinitionName == nil {
|
||||
o.config.GetDefinitionName = func(name string) (string, spec.Extensions) {
|
||||
return name[strings.LastIndex(name, "/")+1:], nil
|
||||
}
|
||||
}
|
||||
o.definitions = o.config.GetDefinitions(func(name string) spec.Ref {
|
||||
defName, _ := o.config.GetDefinitionName(name)
|
||||
return spec.MustCreateRef("#/definitions/" + common.EscapeJsonPointer(defName))
|
||||
})
|
||||
if o.config.CommonResponses == nil {
|
||||
o.config.CommonResponses = map[int]spec.Response{}
|
||||
}
|
||||
err := o.buildPaths(webServices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if o.config.SecurityDefinitions != nil {
|
||||
o.swagger.SecurityDefinitions = *o.config.SecurityDefinitions
|
||||
o.swagger.Security = o.config.DefaultSecurity
|
||||
}
|
||||
if o.config.PostProcessSpec != nil {
|
||||
o.swagger, err = o.config.PostProcessSpec(o.swagger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCanonicalizeTypeName(t reflect.Type) string {
|
||||
if t.PkgPath() == "" {
|
||||
return t.Name()
|
||||
}
|
||||
path := t.PkgPath()
|
||||
if strings.Contains(path, "/vendor/") {
|
||||
path = path[strings.Index(path, "/vendor/")+len("/vendor/"):]
|
||||
}
|
||||
return path + "." + t.Name()
|
||||
}
|
||||
|
||||
func (o *openAPI) buildDefinitionRecursively(name string) error {
|
||||
uniqueName, extensions := o.config.GetDefinitionName(name)
|
||||
if _, ok := o.swagger.Definitions[uniqueName]; ok {
|
||||
return nil
|
||||
}
|
||||
if item, ok := o.definitions[name]; ok {
|
||||
schema := spec.Schema{
|
||||
VendorExtensible: item.Schema.VendorExtensible,
|
||||
SchemaProps: item.Schema.SchemaProps,
|
||||
SwaggerSchemaProps: item.Schema.SwaggerSchemaProps,
|
||||
}
|
||||
if extensions != nil {
|
||||
if schema.Extensions == nil {
|
||||
schema.Extensions = spec.Extensions{}
|
||||
}
|
||||
for k, v := range extensions {
|
||||
schema.Extensions[k] = v
|
||||
}
|
||||
}
|
||||
o.swagger.Definitions[uniqueName] = schema
|
||||
for _, v := range item.Dependencies {
|
||||
if err := o.buildDefinitionRecursively(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("cannot find model definition for %v. If you added a new type, you may need to add +k8s:openapi-gen=true to the package or type and run code-gen again", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildDefinitionForType build a definition for a given type and return a referable name to it's definition.
|
||||
// This is the main function that keep track of definitions used in this spec and is depend on code generated
|
||||
// by k8s.io/kubernetes/cmd/libs/go2idl/openapi-gen.
|
||||
func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) {
|
||||
t := reflect.TypeOf(sample)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
name := getCanonicalizeTypeName(t)
|
||||
if err := o.buildDefinitionRecursively(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defName, _ := o.config.GetDefinitionName(name)
|
||||
return "#/definitions/" + common.EscapeJsonPointer(defName), nil
|
||||
}
|
||||
|
||||
// buildPaths builds OpenAPI paths using go-restful's web services.
|
||||
func (o *openAPI) buildPaths(webServices []*restful.WebService) error {
|
||||
pathsToIgnore := util.NewTrie(o.config.IgnorePrefixes)
|
||||
duplicateOpId := make(map[string]string)
|
||||
for _, w := range webServices {
|
||||
rootPath := w.RootPath()
|
||||
if pathsToIgnore.HasPrefix(rootPath) {
|
||||
continue
|
||||
}
|
||||
commonParams, err := o.buildParameters(w.PathParameters())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for path, routes := range groupRoutesByPath(w.Routes()) {
|
||||
// go-swagger has special variable definition {$NAME:*} that can only be
|
||||
// used at the end of the path and it is not recognized by OpenAPI.
|
||||
if strings.HasSuffix(path, ":*}") {
|
||||
path = path[:len(path)-3] + "}"
|
||||
}
|
||||
if pathsToIgnore.HasPrefix(path) {
|
||||
continue
|
||||
}
|
||||
// Aggregating common parameters make API spec (and generated clients) simpler
|
||||
inPathCommonParamsMap, err := o.findCommonParameters(routes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pathItem, exists := o.swagger.Paths.Paths[path]
|
||||
if exists {
|
||||
return fmt.Errorf("duplicate webservice route has been found for path: %v", path)
|
||||
}
|
||||
pathItem = spec.PathItem{
|
||||
PathItemProps: spec.PathItemProps{
|
||||
Parameters: make([]spec.Parameter, 0),
|
||||
},
|
||||
}
|
||||
// add web services's parameters as well as any parameters appears in all ops, as common parameters
|
||||
pathItem.Parameters = append(pathItem.Parameters, commonParams...)
|
||||
for _, p := range inPathCommonParamsMap {
|
||||
pathItem.Parameters = append(pathItem.Parameters, p)
|
||||
}
|
||||
sortParameters(pathItem.Parameters)
|
||||
for _, route := range routes {
|
||||
op, err := o.buildOperations(route, inPathCommonParamsMap)
|
||||
sortParameters(op.Parameters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dpath, exists := duplicateOpId[op.ID]
|
||||
if exists {
|
||||
return fmt.Errorf("duplicate Operation ID %v for path %v and %v", op.ID, dpath, path)
|
||||
} else {
|
||||
duplicateOpId[op.ID] = path
|
||||
}
|
||||
switch strings.ToUpper(route.Method) {
|
||||
case "GET":
|
||||
pathItem.Get = op
|
||||
case "POST":
|
||||
pathItem.Post = op
|
||||
case "HEAD":
|
||||
pathItem.Head = op
|
||||
case "PUT":
|
||||
pathItem.Put = op
|
||||
case "DELETE":
|
||||
pathItem.Delete = op
|
||||
case "OPTIONS":
|
||||
pathItem.Options = op
|
||||
case "PATCH":
|
||||
pathItem.Patch = op
|
||||
}
|
||||
}
|
||||
o.swagger.Paths.Paths[path] = pathItem
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildOperations builds operations for each webservice path
|
||||
func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map[interface{}]spec.Parameter) (ret *spec.Operation, err error) {
|
||||
ret = &spec.Operation{
|
||||
OperationProps: spec.OperationProps{
|
||||
Description: route.Doc,
|
||||
Consumes: route.Consumes,
|
||||
Produces: route.Produces,
|
||||
Schemes: o.config.ProtocolList,
|
||||
Responses: &spec.Responses{
|
||||
ResponsesProps: spec.ResponsesProps{
|
||||
StatusCodeResponses: make(map[int]spec.Response),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range route.Metadata {
|
||||
if strings.HasPrefix(k, extensionPrefix) {
|
||||
if ret.Extensions == nil {
|
||||
ret.Extensions = spec.Extensions{}
|
||||
}
|
||||
ret.Extensions.Add(k, v)
|
||||
}
|
||||
}
|
||||
if ret.ID, ret.Tags, err = o.config.GetOperationIDAndTags(&route); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Build responses
|
||||
for _, resp := range route.ResponseErrors {
|
||||
ret.Responses.StatusCodeResponses[resp.Code], err = o.buildResponse(resp.Model, resp.Message)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
// If there is no response but a write sample, assume that write sample is an http.StatusOK response.
|
||||
if len(ret.Responses.StatusCodeResponses) == 0 && route.WriteSample != nil {
|
||||
ret.Responses.StatusCodeResponses[http.StatusOK], err = o.buildResponse(route.WriteSample, "OK")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
for code, resp := range o.config.CommonResponses {
|
||||
if _, exists := ret.Responses.StatusCodeResponses[code]; !exists {
|
||||
ret.Responses.StatusCodeResponses[code] = resp
|
||||
}
|
||||
}
|
||||
// If there is still no response, use default response provided.
|
||||
if len(ret.Responses.StatusCodeResponses) == 0 {
|
||||
ret.Responses.Default = o.config.DefaultResponse
|
||||
}
|
||||
|
||||
// Build non-common Parameters
|
||||
ret.Parameters = make([]spec.Parameter, 0)
|
||||
for _, param := range route.ParameterDocs {
|
||||
if _, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)]; !isCommon {
|
||||
openAPIParam, err := o.buildParameter(param.Data(), route.ReadSample)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret.Parameters = append(ret.Parameters, openAPIParam)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (o *openAPI) buildResponse(model interface{}, description string) (spec.Response, error) {
|
||||
schema, err := o.toSchema(model)
|
||||
if err != nil {
|
||||
return spec.Response{}, err
|
||||
}
|
||||
return spec.Response{
|
||||
ResponseProps: spec.ResponseProps{
|
||||
Description: description,
|
||||
Schema: schema,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}]spec.Parameter, error) {
|
||||
commonParamsMap := make(map[interface{}]spec.Parameter, 0)
|
||||
paramOpsCountByName := make(map[interface{}]int, 0)
|
||||
paramNameKindToDataMap := make(map[interface{}]restful.ParameterData, 0)
|
||||
for _, route := range routes {
|
||||
routeParamDuplicateMap := make(map[interface{}]bool)
|
||||
s := ""
|
||||
for _, param := range route.ParameterDocs {
|
||||
m, _ := json.Marshal(param.Data())
|
||||
s += string(m) + "\n"
|
||||
key := mapKeyFromParam(param)
|
||||
if routeParamDuplicateMap[key] {
|
||||
msg, _ := json.Marshal(route.ParameterDocs)
|
||||
return commonParamsMap, fmt.Errorf("duplicate parameter %v for route %v, %v", param.Data().Name, string(msg), s)
|
||||
}
|
||||
routeParamDuplicateMap[key] = true
|
||||
paramOpsCountByName[key]++
|
||||
paramNameKindToDataMap[key] = param.Data()
|
||||
}
|
||||
}
|
||||
for key, count := range paramOpsCountByName {
|
||||
paramData := paramNameKindToDataMap[key]
|
||||
if count == len(routes) && paramData.Kind != restful.BodyParameterKind {
|
||||
openAPIParam, err := o.buildParameter(paramData, nil)
|
||||
if err != nil {
|
||||
return commonParamsMap, err
|
||||
}
|
||||
commonParamsMap[key] = openAPIParam
|
||||
}
|
||||
}
|
||||
return commonParamsMap, nil
|
||||
}
|
||||
|
||||
func (o *openAPI) toSchema(model interface{}) (_ *spec.Schema, err error) {
|
||||
if openAPIType, openAPIFormat := common.GetOpenAPITypeFormat(getCanonicalizeTypeName(reflect.TypeOf(model))); openAPIType != "" {
|
||||
return &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{openAPIType},
|
||||
Format: openAPIFormat,
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
ref, err := o.buildDefinitionForType(model)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: spec.MustCreateRef(ref),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *openAPI) buildParameter(restParam restful.ParameterData, bodySample interface{}) (ret spec.Parameter, err error) {
|
||||
ret = spec.Parameter{
|
||||
ParamProps: spec.ParamProps{
|
||||
Name: restParam.Name,
|
||||
Description: restParam.Description,
|
||||
Required: restParam.Required,
|
||||
},
|
||||
}
|
||||
switch restParam.Kind {
|
||||
case restful.BodyParameterKind:
|
||||
if bodySample != nil {
|
||||
ret.In = "body"
|
||||
ret.Schema, err = o.toSchema(bodySample)
|
||||
return ret, err
|
||||
} else {
|
||||
// There is not enough information in the body parameter to build the definition.
|
||||
// Body parameter has a data type that is a short name but we need full package name
|
||||
// of the type to create a definition.
|
||||
return ret, fmt.Errorf("restful body parameters are not supported: %v", restParam.DataType)
|
||||
}
|
||||
case restful.PathParameterKind:
|
||||
ret.In = "path"
|
||||
if !restParam.Required {
|
||||
return ret, fmt.Errorf("path parameters should be marked at required for parameter %v", restParam)
|
||||
}
|
||||
case restful.QueryParameterKind:
|
||||
ret.In = "query"
|
||||
case restful.HeaderParameterKind:
|
||||
ret.In = "header"
|
||||
case restful.FormParameterKind:
|
||||
ret.In = "formData"
|
||||
default:
|
||||
return ret, fmt.Errorf("unknown restful operation kind : %v", restParam.Kind)
|
||||
}
|
||||
openAPIType, openAPIFormat := common.GetOpenAPITypeFormat(restParam.DataType)
|
||||
if openAPIType == "" {
|
||||
return ret, fmt.Errorf("non-body Restful parameter type should be a simple type, but got : %v", restParam.DataType)
|
||||
}
|
||||
ret.Type = openAPIType
|
||||
ret.Format = openAPIFormat
|
||||
ret.UniqueItems = !restParam.AllowMultiple
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (o *openAPI) buildParameters(restParam []*restful.Parameter) (ret []spec.Parameter, err error) {
|
||||
ret = make([]spec.Parameter, len(restParam))
|
||||
for i, v := range restParam {
|
||||
ret[i], err = o.buildParameter(v.Data(), nil)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
61
vendor/k8s.io/kube-openapi/pkg/builder/util.go
generated
vendored
Normal file
61
vendor/k8s.io/kube-openapi/pkg/builder/util.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
type parameters []spec.Parameter
|
||||
|
||||
func (s parameters) Len() int { return len(s) }
|
||||
func (s parameters) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// byNameIn used in sorting parameters by Name and In fields.
|
||||
type byNameIn struct {
|
||||
parameters
|
||||
}
|
||||
|
||||
func (s byNameIn) Less(i, j int) bool {
|
||||
return s.parameters[i].Name < s.parameters[j].Name || (s.parameters[i].Name == s.parameters[j].Name && s.parameters[i].In < s.parameters[j].In)
|
||||
}
|
||||
|
||||
// SortParameters sorts parameters by Name and In fields.
|
||||
func sortParameters(p []spec.Parameter) {
|
||||
sort.Sort(byNameIn{p})
|
||||
}
|
||||
|
||||
func groupRoutesByPath(routes []restful.Route) map[string][]restful.Route {
|
||||
pathToRoutes := make(map[string][]restful.Route)
|
||||
for _, r := range routes {
|
||||
pathToRoutes[r.Path] = append(pathToRoutes[r.Path], r)
|
||||
}
|
||||
return pathToRoutes
|
||||
}
|
||||
|
||||
func mapKeyFromParam(param *restful.Parameter) interface{} {
|
||||
return struct {
|
||||
Name string
|
||||
Kind int
|
||||
}{
|
||||
Name: param.Data().Name,
|
||||
Kind: param.Data().Kind,
|
||||
}
|
||||
}
|
167
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
Normal file
167
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
|
||||
type OpenAPIDefinition struct {
|
||||
Schema spec.Schema
|
||||
Dependencies []string
|
||||
}
|
||||
|
||||
type ReferenceCallback func(path string) spec.Ref
|
||||
|
||||
// OpenAPIDefinitions is collection of all definitions.
|
||||
type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition
|
||||
|
||||
// OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface,
|
||||
// the definition returned by it will be used, otherwise the auto-generated definitions will be used. See
|
||||
// GetOpenAPITypeFormat for more information about trade-offs of using this interface or GetOpenAPITypeFormat method when
|
||||
// possible.
|
||||
type OpenAPIDefinitionGetter interface {
|
||||
OpenAPIDefinition() *OpenAPIDefinition
|
||||
}
|
||||
|
||||
type PathHandler interface {
|
||||
Handle(path string, handler http.Handler)
|
||||
}
|
||||
|
||||
// Config is set of configuration for openAPI spec generation.
|
||||
type Config struct {
|
||||
// List of supported protocols such as https, http, etc.
|
||||
ProtocolList []string
|
||||
|
||||
// Info is general information about the API.
|
||||
Info *spec.Info
|
||||
|
||||
// DefaultResponse will be used if an operation does not have any responses listed. It
|
||||
// will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
|
||||
DefaultResponse *spec.Response
|
||||
|
||||
// CommonResponses will be added as a response to all operation specs. This is a good place to add common
|
||||
// responses such as authorization failed.
|
||||
CommonResponses map[int]spec.Response
|
||||
|
||||
// List of webservice's path prefixes to ignore
|
||||
IgnorePrefixes []string
|
||||
|
||||
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
|
||||
// or any of the models will result in spec generation failure.
|
||||
GetDefinitions GetOpenAPIDefinitions
|
||||
|
||||
// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
|
||||
GetOperationIDAndTags func(r *restful.Route) (string, []string, error)
|
||||
|
||||
// GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
|
||||
// It is an optional function to customize model names.
|
||||
GetDefinitionName func(name string) (string, spec.Extensions)
|
||||
|
||||
// PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving.
|
||||
PostProcessSpec func(*spec.Swagger) (*spec.Swagger, error)
|
||||
|
||||
// SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config
|
||||
// is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses.
|
||||
SecurityDefinitions *spec.SecurityDefinitions
|
||||
|
||||
// DefaultSecurity for all operations. This will pass as spec.SwaggerProps.Security to OpenAPI.
|
||||
// For most cases, this will be list of acceptable definitions in SecurityDefinitions.
|
||||
DefaultSecurity []map[string][]string
|
||||
}
|
||||
|
||||
// This function is a reference for converting go (or any custom type) to a simple open API type,format pair. There are
|
||||
// two ways to customize spec for a type. If you add it here, a type will be converted to a simple type and the type
|
||||
// comment (the comment that is added before type definition) will be lost. The spec will still have the property
|
||||
// comment. The second way is to implement OpenAPIDefinitionGetter interface. That function can customize the spec (so
|
||||
// the spec does not need to be simple type,format) or can even return a simple type,format (e.g. IntOrString). For simple
|
||||
// type formats, the benefit of adding OpenAPIDefinitionGetter interface is to keep both type and property documentation.
|
||||
// Example:
|
||||
// type Sample struct {
|
||||
// ...
|
||||
// // port of the server
|
||||
// port IntOrString
|
||||
// ...
|
||||
// }
|
||||
// // IntOrString documentation...
|
||||
// type IntOrString { ... }
|
||||
//
|
||||
// Adding IntOrString to this function:
|
||||
// "port" : {
|
||||
// format: "string",
|
||||
// type: "int-or-string",
|
||||
// Description: "port of the server"
|
||||
// }
|
||||
//
|
||||
// Implement OpenAPIDefinitionGetter for IntOrString:
|
||||
//
|
||||
// "port" : {
|
||||
// $Ref: "#/definitions/IntOrString"
|
||||
// Description: "port of the server"
|
||||
// }
|
||||
// ...
|
||||
// definitions:
|
||||
// {
|
||||
// "IntOrString": {
|
||||
// format: "string",
|
||||
// type: "int-or-string",
|
||||
// Description: "IntOrString documentation..." // new
|
||||
// }
|
||||
// }
|
||||
//
|
||||
func GetOpenAPITypeFormat(typeName string) (string, string) {
|
||||
schemaTypeFormatMap := map[string][]string{
|
||||
"uint": {"integer", "int32"},
|
||||
"uint8": {"integer", "byte"},
|
||||
"uint16": {"integer", "int32"},
|
||||
"uint32": {"integer", "int64"},
|
||||
"uint64": {"integer", "int64"},
|
||||
"int": {"integer", "int32"},
|
||||
"int8": {"integer", "byte"},
|
||||
"int16": {"integer", "int32"},
|
||||
"int32": {"integer", "int32"},
|
||||
"int64": {"integer", "int64"},
|
||||
"byte": {"integer", "byte"},
|
||||
"float64": {"number", "double"},
|
||||
"float32": {"number", "float"},
|
||||
"bool": {"boolean", ""},
|
||||
"time.Time": {"string", "date-time"},
|
||||
"string": {"string", ""},
|
||||
"integer": {"integer", ""},
|
||||
"number": {"number", ""},
|
||||
"boolean": {"boolean", ""},
|
||||
"[]byte": {"string", "byte"}, // base64 encoded characters
|
||||
"interface{}": {"object", ""},
|
||||
}
|
||||
mapped, ok := schemaTypeFormatMap[typeName]
|
||||
if !ok {
|
||||
return "", ""
|
||||
}
|
||||
return mapped[0], mapped[1]
|
||||
}
|
||||
|
||||
func EscapeJsonPointer(p string) string {
|
||||
// Escaping reference name using rfc6901
|
||||
p = strings.Replace(p, "~", "~0", -1)
|
||||
p = strings.Replace(p, "/", "~1", -1)
|
||||
return p
|
||||
}
|
19
vendor/k8s.io/kube-openapi/pkg/common/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kube-openapi/pkg/common/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// package common holds shared code and types between open API code
|
||||
// generator and spec generator.
|
||||
package common
|
15
vendor/k8s.io/kube-openapi/pkg/generators/README
generated
vendored
Normal file
15
vendor/k8s.io/kube-openapi/pkg/generators/README
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Generate OpenAPI definitions
|
||||
|
||||
- To generate definition for a specific type or package add "+k8s:openapi-gen=true" tag to the type/package comment lines.
|
||||
- To exclude a type or a member from a tagged package/type, add "+k8s:openapi-gen=false" tag to the comment lines.
|
||||
|
||||
# OpenAPI Extensions
|
||||
OpenAPI spec can have extensions on types. To define one or more extensions on a type or its member
|
||||
add `+k8s:openapi-gen=x-kubernetes-$NAME:`$VALUE`` to the comment lines before type/member. A type/member can
|
||||
have multiple extensions. The rest of the line in the comment will be used as $VALUE so there is no need to
|
||||
escape or quote the value string. Extensions can be used to pass more information to client generators or
|
||||
documentation generators. For example a type might have a friendly name to be displayed in documentation or
|
||||
being used in a client's fluent interface.
|
||||
|
||||
|
||||
TODO(mehdy): Make k8s:openapi-gen a parameter to the generator now that OpenAPI has its own repo.
|
612
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
Normal file
612
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
Normal file
@ -0,0 +1,612 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
openapi "k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// This is the comment tag that carries parameters for open API generation.
|
||||
const tagName = "k8s:openapi-gen"
|
||||
const tagOptional = "optional"
|
||||
|
||||
// Known values for the tag.
|
||||
const (
|
||||
tagValueTrue = "true"
|
||||
tagValueFalse = "false"
|
||||
tagExtensionPrefix = "x-kubernetes-"
|
||||
tagPatchStrategy = "patchStrategy"
|
||||
tagPatchMergeKey = "patchMergeKey"
|
||||
patchStrategyExtensionName = "patch-strategy"
|
||||
patchMergeKeyExtensionName = "patch-merge-key"
|
||||
)
|
||||
|
||||
func getOpenAPITagValue(comments []string) []string {
|
||||
return types.ExtractCommentTags("+", comments)[tagName]
|
||||
}
|
||||
|
||||
func getSingleTagsValue(comments []string, tag string) (string, error) {
|
||||
tags, ok := types.ExtractCommentTags("+", comments)[tag]
|
||||
if !ok || len(tags) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
if len(tags) > 1 {
|
||||
return "", fmt.Errorf("multiple values are not allowed for tag %s", tag)
|
||||
}
|
||||
return tags[0], nil
|
||||
}
|
||||
|
||||
func hasOpenAPITagValue(comments []string, value string) bool {
|
||||
tagValues := getOpenAPITagValue(comments)
|
||||
for _, val := range tagValues {
|
||||
if val == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasOptionalTag returns true if the member has +optional in its comments or
|
||||
// omitempty in its json tags.
|
||||
func hasOptionalTag(m *types.Member) bool {
|
||||
hasOptionalCommentTag := types.ExtractCommentTags(
|
||||
"+", m.CommentLines)[tagOptional] != nil
|
||||
hasOptionalJsonTag := strings.Contains(
|
||||
reflect.StructTag(m.Tags).Get("json"), "omitempty")
|
||||
return hasOptionalCommentTag || hasOptionalJsonTag
|
||||
}
|
||||
|
||||
type identityNamer struct{}
|
||||
|
||||
func (_ identityNamer) Name(t *types.Type) string {
|
||||
return t.Name.String()
|
||||
}
|
||||
|
||||
var _ namer.Namer = identityNamer{}
|
||||
|
||||
// NameSystems returns the name system used by the generators in this package.
|
||||
func NameSystems() namer.NameSystems {
|
||||
return namer.NameSystems{
|
||||
"raw": namer.NewRawNamer("", nil),
|
||||
"sorting_namer": identityNamer{},
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultNameSystem returns the default name system for ordering the types to be
|
||||
// processed by the generators in this package.
|
||||
func DefaultNameSystem() string {
|
||||
return "sorting_namer"
|
||||
}
|
||||
|
||||
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
||||
boilerplate, err := arguments.LoadGoBoilerplate()
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed loading boilerplate: %v", err)
|
||||
}
|
||||
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
|
||||
header = append(header, []byte(
|
||||
`
|
||||
// This file was autogenerated by openapi-gen. Do not edit it manually!
|
||||
|
||||
`)...)
|
||||
|
||||
if err := context.AddDir(arguments.OutputPackagePath); err != nil {
|
||||
glog.Fatalf("Failed to load output package: %v", err)
|
||||
}
|
||||
pkg := context.Universe[arguments.OutputPackagePath]
|
||||
if pkg == nil {
|
||||
glog.Fatalf("Got nil output package: %v", err)
|
||||
}
|
||||
return generator.Packages{
|
||||
&generator.DefaultPackage{
|
||||
PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0],
|
||||
PackagePath: pkg.Path,
|
||||
HeaderText: header,
|
||||
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||
return []generator.Generator{NewOpenAPIGen(arguments.OutputFileBaseName, pkg, context)}
|
||||
},
|
||||
FilterFunc: func(c *generator.Context, t *types.Type) bool {
|
||||
// There is a conflict between this codegen and codecgen, we should avoid types generated for codecgen
|
||||
if strings.HasPrefix(t.Name.Name, "codecSelfer") {
|
||||
return false
|
||||
}
|
||||
pkg := context.Universe.Package(t.Name.Package)
|
||||
if hasOpenAPITagValue(pkg.Comments, tagValueTrue) {
|
||||
return !hasOpenAPITagValue(t.CommentLines, tagValueFalse)
|
||||
}
|
||||
if hasOpenAPITagValue(t.CommentLines, tagValueTrue) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
specPackagePath = "github.com/go-openapi/spec"
|
||||
openAPICommonPackagePath = "k8s.io/kube-openapi/pkg/common"
|
||||
)
|
||||
|
||||
// openApiGen produces a file with auto-generated OpenAPI functions.
|
||||
type openAPIGen struct {
|
||||
generator.DefaultGen
|
||||
// TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
|
||||
targetPackage *types.Package
|
||||
imports namer.ImportTracker
|
||||
context *generator.Context
|
||||
}
|
||||
|
||||
func NewOpenAPIGen(sanitizedName string, targetPackage *types.Package, context *generator.Context) generator.Generator {
|
||||
return &openAPIGen{
|
||||
DefaultGen: generator.DefaultGen{
|
||||
OptionalName: sanitizedName,
|
||||
},
|
||||
imports: generator.NewImportTracker(),
|
||||
targetPackage: targetPackage,
|
||||
context: context,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems {
|
||||
// Have the raw namer for this file track what it imports.
|
||||
return namer.NameSystems{
|
||||
"raw": namer.NewRawNamer(g.targetPackage.Path, g.imports),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Filter(c *generator.Context, t *types.Type) bool {
|
||||
// There is a conflict between this codegen and codecgen, we should avoid types generated for codecgen
|
||||
if strings.HasPrefix(t.Name.Name, "codecSelfer") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *openAPIGen) isOtherPackage(pkg string) bool {
|
||||
if pkg == g.targetPackage.Path {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(pkg, "\""+g.targetPackage.Path+"\"") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Imports(c *generator.Context) []string {
|
||||
importLines := []string{}
|
||||
for _, singleImport := range g.imports.ImportLines() {
|
||||
importLines = append(importLines, singleImport)
|
||||
}
|
||||
return importLines
|
||||
}
|
||||
|
||||
func argsFromType(t *types.Type) generator.Args {
|
||||
return generator.Args{
|
||||
"type": t,
|
||||
"ReferenceCallback": types.Ref(openAPICommonPackagePath, "ReferenceCallback"),
|
||||
"OpenAPIDefinition": types.Ref(openAPICommonPackagePath, "OpenAPIDefinition"),
|
||||
"SpecSchemaType": types.Ref(specPackagePath, "Schema"),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
sw.Do("func GetOpenAPIDefinitions(ref $.ReferenceCallback|raw$) map[string]$.OpenAPIDefinition|raw$ {\n", argsFromType(nil))
|
||||
sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil))
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Finalize(c *generator.Context, w io.Writer) error {
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
sw.Do("}\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
|
||||
glog.V(5).Infof("generating for type %v", t)
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
err := newOpenAPITypeWriter(sw).generate(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
func getJsonTags(m *types.Member) []string {
|
||||
jsonTag := reflect.StructTag(m.Tags).Get("json")
|
||||
if jsonTag == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(jsonTag, ",")
|
||||
}
|
||||
|
||||
func getPatchTags(m *types.Member) (string, string) {
|
||||
return reflect.StructTag(m.Tags).Get(tagPatchMergeKey), reflect.StructTag(m.Tags).Get(tagPatchStrategy)
|
||||
}
|
||||
|
||||
func getReferableName(m *types.Member) string {
|
||||
jsonTags := getJsonTags(m)
|
||||
if len(jsonTags) > 0 {
|
||||
if jsonTags[0] == "-" {
|
||||
return ""
|
||||
} else {
|
||||
return jsonTags[0]
|
||||
}
|
||||
} else {
|
||||
return m.Name
|
||||
}
|
||||
}
|
||||
|
||||
func shouldInlineMembers(m *types.Member) bool {
|
||||
jsonTags := getJsonTags(m)
|
||||
return len(jsonTags) > 1 && jsonTags[1] == "inline"
|
||||
}
|
||||
|
||||
type openAPITypeWriter struct {
|
||||
*generator.SnippetWriter
|
||||
refTypes map[string]*types.Type
|
||||
GetDefinitionInterface *types.Type
|
||||
}
|
||||
|
||||
func newOpenAPITypeWriter(sw *generator.SnippetWriter) openAPITypeWriter {
|
||||
return openAPITypeWriter{
|
||||
SnippetWriter: sw,
|
||||
refTypes: map[string]*types.Type{},
|
||||
}
|
||||
}
|
||||
|
||||
func hasOpenAPIDefinitionMethod(t *types.Type) bool {
|
||||
for mn, mt := range t.Methods {
|
||||
if mn != "OpenAPIDefinition" {
|
||||
continue
|
||||
}
|
||||
if len(mt.Signature.Parameters) != 0 || len(mt.Signature.Results) != 1 {
|
||||
return false
|
||||
}
|
||||
r := mt.Signature.Results[0]
|
||||
if r.Name.Name != "OpenAPIDefinition" || r.Name.Package != openAPICommonPackagePath {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// typeShortName returns short package name (e.g. the name x appears in package x definition) dot type name.
|
||||
func typeShortName(t *types.Type) string {
|
||||
return filepath.Base(t.Name.Package) + "." + t.Name.Name
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([]string, error) {
|
||||
var err error
|
||||
for _, m := range t.Members {
|
||||
if hasOpenAPITagValue(m.CommentLines, tagValueFalse) {
|
||||
continue
|
||||
}
|
||||
if shouldInlineMembers(&m) {
|
||||
required, err = g.generateMembers(m.Type, required)
|
||||
if err != nil {
|
||||
return required, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
name := getReferableName(&m)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if !hasOptionalTag(&m) {
|
||||
required = append(required, name)
|
||||
}
|
||||
if err = g.generateProperty(&m, t); err != nil {
|
||||
return required, err
|
||||
}
|
||||
}
|
||||
return required, nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
// Only generate for struct type and ignore the rest
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
args := argsFromType(t)
|
||||
g.Do("\"$.$\": ", t.Name)
|
||||
if hasOpenAPIDefinitionMethod(t) {
|
||||
g.Do("$.type|raw${}.OpenAPIDefinition(),\n", args)
|
||||
return nil
|
||||
}
|
||||
g.Do("{\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args)
|
||||
required, err := g.generateMembers(t, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if len(required) > 0 {
|
||||
g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\""))
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if err := g.generateExtensions(t.CommentLines); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
g.Do("Dependencies: []string{\n", args)
|
||||
// Map order is undefined, sort them or we may get a different file generated each time.
|
||||
keys := []string{}
|
||||
for k := range g.refTypes {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
v := g.refTypes[k]
|
||||
if t, _ := openapi.GetOpenAPITypeFormat(v.String()); t != "" {
|
||||
// This is a known type, we do not need a reference to it
|
||||
// Will eliminate special case of time.Time
|
||||
continue
|
||||
}
|
||||
g.Do("\"$.$\",", k)
|
||||
}
|
||||
g.Do("},\n},\n", nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateExtensions(CommentLines []string) error {
|
||||
tagValues := getOpenAPITagValue(CommentLines)
|
||||
type NameValue struct {
|
||||
Name, Value string
|
||||
}
|
||||
extensions := []NameValue{}
|
||||
for _, val := range tagValues {
|
||||
if strings.HasPrefix(val, tagExtensionPrefix) {
|
||||
parts := strings.SplitN(val, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid extension value: %v", val)
|
||||
}
|
||||
extensions = append(extensions, NameValue{parts[0], parts[1]})
|
||||
}
|
||||
}
|
||||
patchMergeKeyTag, err := getSingleTagsValue(CommentLines, tagPatchMergeKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(patchMergeKeyTag) > 0 {
|
||||
extensions = append(extensions, NameValue{tagExtensionPrefix + patchMergeKeyExtensionName, patchMergeKeyTag})
|
||||
}
|
||||
patchStrategyTag, err := getSingleTagsValue(CommentLines, tagPatchStrategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(patchStrategyTag) > 0 {
|
||||
extensions = append(extensions, NameValue{tagExtensionPrefix + patchStrategyExtensionName, patchStrategyTag})
|
||||
}
|
||||
if len(extensions) == 0 {
|
||||
return nil
|
||||
}
|
||||
g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
|
||||
for _, extension := range extensions {
|
||||
g.Do("\"$.$\": ", extension.Name)
|
||||
g.Do("\"$.$\",\n", extension.Value)
|
||||
}
|
||||
g.Do("},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(#44005): Move this validation outside of this generator (probably to policy verifier)
|
||||
func (g openAPITypeWriter) validatePatchTags(m *types.Member, parent *types.Type) error {
|
||||
patchMergeKeyStructTag, patchStrategyStructTag := getPatchTags(m)
|
||||
patchMergeKeyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchMergeKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patchStrategyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchStrategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if patchMergeKeyStructTag != patchMergeKeyCommentTag {
|
||||
return fmt.Errorf("patchMergeKey in comment and struct tags should match for member (%s) of (%s)",
|
||||
m.Name, parent.Name.String())
|
||||
}
|
||||
if patchStrategyStructTag != patchStrategyCommentTag {
|
||||
return fmt.Errorf("patchStrategy in comment and struct tags should match for member (%s) of (%s)",
|
||||
m.Name, parent.Name.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateDescription(CommentLines []string) {
|
||||
var buffer bytes.Buffer
|
||||
delPrevChar := func() {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range CommentLines {
|
||||
// Ignore all lines after ---
|
||||
if line == "---" {
|
||||
break
|
||||
}
|
||||
line = strings.TrimRight(line, " ")
|
||||
leading := strings.TrimLeft(line, " ")
|
||||
switch {
|
||||
case len(line) == 0: // Keep paragraphs
|
||||
delPrevChar()
|
||||
buffer.WriteString("\n\n")
|
||||
case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
|
||||
case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
|
||||
default:
|
||||
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
|
||||
delPrevChar()
|
||||
line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
|
||||
} else {
|
||||
line += " "
|
||||
}
|
||||
buffer.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
postDoc := strings.TrimRight(buffer.String(), "\n")
|
||||
postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
|
||||
postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
|
||||
postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
|
||||
postDoc = strings.Replace(postDoc, "\t", "\\t", -1)
|
||||
postDoc = strings.Trim(postDoc, " ")
|
||||
if postDoc != "" {
|
||||
g.Do("Description: \"$.$\",\n", postDoc)
|
||||
}
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type) error {
|
||||
name := getReferableName(m)
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
if err := g.validatePatchTags(m, parent); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("\"$.$\": {\n", name)
|
||||
if err := g.generateExtensions(m.CommentLines); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("SchemaProps: spec.SchemaProps{\n", nil)
|
||||
g.generateDescription(m.CommentLines)
|
||||
jsonTags := getJsonTags(m)
|
||||
if len(jsonTags) > 1 && jsonTags[1] == "string" {
|
||||
g.generateSimpleProperty("string", "")
|
||||
g.Do("},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
t := resolveAliasAndPtrType(m.Type)
|
||||
// If we can get a openAPI type and format for this type, we consider it to be simple property
|
||||
typeString, format := openapi.GetOpenAPITypeFormat(t.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
g.Do("},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
switch t.Kind {
|
||||
case types.Builtin:
|
||||
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", t)
|
||||
case types.Map:
|
||||
if err := g.generateMapProperty(t); err != nil {
|
||||
return err
|
||||
}
|
||||
case types.Slice, types.Array:
|
||||
if err := g.generateSliceProperty(t); err != nil {
|
||||
return err
|
||||
}
|
||||
case types.Struct, types.Interface:
|
||||
g.generateReferenceProperty(t)
|
||||
default:
|
||||
return fmt.Errorf("cannot generate spec for type %v", t)
|
||||
}
|
||||
g.Do("},\n},\n", nil)
|
||||
return g.Error()
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateSimpleProperty(typeString, format string) {
|
||||
g.Do("Type: []string{\"$.$\"},\n", typeString)
|
||||
g.Do("Format: \"$.$\",\n", format)
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) {
|
||||
g.refTypes[t.Name.String()] = t
|
||||
g.Do("Ref: ref(\"$.$\"),\n", t.Name.String())
|
||||
}
|
||||
|
||||
func resolveAliasAndPtrType(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.Pointer {
|
||||
t = t.Elem
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateMapProperty(t *types.Type) error {
|
||||
keyType := resolveAliasAndPtrType(t.Key)
|
||||
elemType := resolveAliasAndPtrType(t.Elem)
|
||||
|
||||
// According to OpenAPI examples, only map from string is supported
|
||||
if keyType.Name.Name != "string" {
|
||||
return fmt.Errorf("map with non-string keys are not supported by OpenAPI in %v", t)
|
||||
}
|
||||
g.Do("Type: []string{\"object\"},\n", nil)
|
||||
g.Do("AdditionalProperties: &spec.SchemaOrBool{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
|
||||
typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
switch elemType.Kind {
|
||||
case types.Builtin:
|
||||
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
|
||||
case types.Struct:
|
||||
g.generateReferenceProperty(elemType)
|
||||
case types.Slice, types.Array:
|
||||
g.generateSliceProperty(elemType)
|
||||
default:
|
||||
return fmt.Errorf("map Element kind %v is not supported in %v", elemType.Kind, t.Name)
|
||||
}
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
switch elemType.Kind {
|
||||
case types.Builtin:
|
||||
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
|
||||
case types.Struct:
|
||||
g.generateReferenceProperty(elemType)
|
||||
default:
|
||||
return fmt.Errorf("slice Element kind %v is not supported in %v", elemType.Kind, t)
|
||||
}
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
204
vendor/k8s.io/kube-openapi/pkg/handler/handler.go
generated
vendored
Normal file
204
vendor/k8s.io/kube-openapi/pkg/handler/handler.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
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 handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha512"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v2"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/googleapis/gnostic/OpenAPIv2"
|
||||
"github.com/googleapis/gnostic/compiler"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/builder"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonExt = ".json"
|
||||
|
||||
mimeJson = "application/json"
|
||||
// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
|
||||
mimePb = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
|
||||
mimePbGz = "application/x-gzip"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
orgSpec *spec.Swagger
|
||||
lastModified time.Time
|
||||
|
||||
specBytes []byte
|
||||
specPb []byte
|
||||
specPbGz []byte
|
||||
|
||||
specBytesETag string
|
||||
specPbETag string
|
||||
specPbGzETag string
|
||||
}
|
||||
|
||||
func init() {
|
||||
mime.AddExtensionType(".json", mimeJson)
|
||||
mime.AddExtensionType(".pb-v1", mimePb)
|
||||
mime.AddExtensionType(".gz", mimePbGz)
|
||||
}
|
||||
|
||||
func computeETag(data []byte) string {
|
||||
return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
|
||||
}
|
||||
|
||||
// BuildAndRegisterOpenAPIService builds the spec and registers a handler to provides access to it.
|
||||
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIService.
|
||||
func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
|
||||
spec, err := builder.BuildOpenAPISpec(webServices, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RegisterOpenAPIService(spec, servePath, handler)
|
||||
}
|
||||
|
||||
// RegisterOpenAPIService registers a handler to provides access to provided swagger spec.
|
||||
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
|
||||
// json file and will also serve .pb and .gz files.
|
||||
func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
|
||||
if !strings.HasSuffix(servePath, jsonExt) {
|
||||
return nil, fmt.Errorf("serving path must end with \"%s\"", jsonExt)
|
||||
}
|
||||
|
||||
servePathBase := strings.TrimSuffix(servePath, jsonExt)
|
||||
|
||||
o := OpenAPIService{}
|
||||
if err := o.UpdateSpec(openapiSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type fileInfo struct {
|
||||
ext string
|
||||
getDataAndETag func() ([]byte, string, time.Time)
|
||||
}
|
||||
|
||||
files := []fileInfo{
|
||||
{".json", o.getSwaggerBytes},
|
||||
{"-2.0.0.json", o.getSwaggerBytes},
|
||||
{"-2.0.0.pb-v1", o.getSwaggerPbBytes},
|
||||
{"-2.0.0.pb-v1.gz", o.getSwaggerPbGzBytes},
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
path := servePathBase + file.ext
|
||||
getDataAndETag := file.getDataAndETag
|
||||
handler.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
data, etag, lastModified := getDataAndETag()
|
||||
w.Header().Set("Etag", etag)
|
||||
|
||||
// ServeContent will take care of caching using eTag.
|
||||
http.ServeContent(w, r, path, lastModified, bytes.NewReader(data))
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
return &o, nil
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) {
|
||||
o.rwMutex.RLock()
|
||||
defer o.rwMutex.RUnlock()
|
||||
return o.specBytes, o.specBytesETag, o.lastModified
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) {
|
||||
o.rwMutex.RLock()
|
||||
defer o.rwMutex.RUnlock()
|
||||
return o.specPb, o.specPbETag, o.lastModified
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) {
|
||||
o.rwMutex.RLock()
|
||||
defer o.rwMutex.RUnlock()
|
||||
return o.specPbGz, o.specPbGzETag, o.lastModified
|
||||
}
|
||||
|
||||
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
|
||||
orgSpec := openapiSpec
|
||||
specBytes, err := json.MarshalIndent(openapiSpec, " ", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
specPb, err := toProtoBinary(specBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
specPbGz := toGzip(specPb)
|
||||
|
||||
specBytesETag := computeETag(specBytes)
|
||||
specPbETag := computeETag(specPb)
|
||||
specPbGzETag := computeETag(specPbGz)
|
||||
|
||||
lastModified := time.Now()
|
||||
|
||||
o.rwMutex.Lock()
|
||||
defer o.rwMutex.Unlock()
|
||||
|
||||
o.orgSpec = orgSpec
|
||||
o.specBytes = specBytes
|
||||
o.specPb = specPb
|
||||
o.specPbGz = specPbGz
|
||||
o.specBytesETag = specBytesETag
|
||||
o.specPbETag = specPbETag
|
||||
o.specPbGzETag = specPbGzETag
|
||||
o.lastModified = lastModified
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toProtoBinary(spec []byte) ([]byte, error) {
|
||||
var info yaml.MapSlice
|
||||
err := yaml.Unmarshal(spec, &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
document, err := openapi_v2.NewDocument(info, compiler.NewContext("$root", nil))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proto.Marshal(document)
|
||||
}
|
||||
|
||||
func toGzip(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
zw := gzip.NewWriter(&buf)
|
||||
zw.Write(data)
|
||||
zw.Close()
|
||||
return buf.Bytes()
|
||||
}
|
79
vendor/k8s.io/kube-openapi/pkg/util/trie.go
generated
vendored
Normal file
79
vendor/k8s.io/kube-openapi/pkg/util/trie.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
// A simple trie implementation with Add and HasPrefix methods only.
|
||||
type Trie struct {
|
||||
children map[byte]*Trie
|
||||
wordTail bool
|
||||
word string
|
||||
}
|
||||
|
||||
// NewTrie creates a Trie and add all strings in the provided list to it.
|
||||
func NewTrie(list []string) Trie {
|
||||
ret := Trie{
|
||||
children: make(map[byte]*Trie),
|
||||
wordTail: false,
|
||||
}
|
||||
for _, v := range list {
|
||||
ret.Add(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Add adds a word to this trie
|
||||
func (t *Trie) Add(v string) {
|
||||
root := t
|
||||
for _, b := range []byte(v) {
|
||||
child, exists := root.children[b]
|
||||
if !exists {
|
||||
child = &Trie{
|
||||
children: make(map[byte]*Trie),
|
||||
wordTail: false,
|
||||
}
|
||||
root.children[b] = child
|
||||
}
|
||||
root = child
|
||||
}
|
||||
root.wordTail = true
|
||||
root.word = v
|
||||
}
|
||||
|
||||
// HasPrefix returns true of v has any of the prefixes stored in this trie.
|
||||
func (t *Trie) HasPrefix(v string) bool {
|
||||
_, has := t.GetPrefix(v)
|
||||
return has
|
||||
}
|
||||
|
||||
// GetPrefix is like HasPrefix but return the prefix in case of match or empty string otherwise.
|
||||
func (t *Trie) GetPrefix(v string) (string, bool) {
|
||||
root := t
|
||||
if root.wordTail {
|
||||
return root.word, true
|
||||
}
|
||||
for _, b := range []byte(v) {
|
||||
child, exists := root.children[b]
|
||||
if !exists {
|
||||
return "", false
|
||||
}
|
||||
if child.wordTail {
|
||||
return child.word, true
|
||||
}
|
||||
root = child
|
||||
}
|
||||
return "", false
|
||||
}
|
Loading…
Reference in New Issue
Block a user