mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-30 21:30:16 +00:00 
			
		
		
		
	Update Godeps to use kube-openapi
This commit is contained in:
		
							
								
								
									
										24
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @@ -3032,6 +3032,30 @@ | |||||||
| 			"Comment": "v1.2.0-beta.1", | 			"Comment": "v1.2.0-beta.1", | ||||||
| 			"Rev": "c2ac40f1adf8c42a79badddb2a2acd673cae3bcb" | 			"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", | 			"ImportPath": "k8s.io/utils/exec", | ||||||
| 			"Rev": "9fdc871a36f37980dd85f96d576b20d564cc0784" | 			"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 | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user