openapi.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package parse
  14. import (
  15. "fmt"
  16. "strings"
  17. "k8s.io/kube-openapi/pkg/util/proto"
  18. )
  19. // Contains functions for casting openapi interfaces to their underlying types
  20. // getSchemaType returns the string type of the schema - e.g. array, primitive, map, kind, reference
  21. func getSchemaType(schema proto.Schema) string {
  22. if schema == nil {
  23. return ""
  24. }
  25. visitor := &baseSchemaVisitor{}
  26. schema.Accept(visitor)
  27. return visitor.Kind
  28. }
  29. // getKind converts schema to an *proto.Kind object
  30. func getKind(schema proto.Schema) (*proto.Kind, error) {
  31. if schema == nil {
  32. return nil, nil
  33. }
  34. visitor := &kindSchemaVisitor{}
  35. schema.Accept(visitor)
  36. return visitor.Result, visitor.Err
  37. }
  38. // getArray converts schema to an *proto.Array object
  39. func getArray(schema proto.Schema) (*proto.Array, error) {
  40. if schema == nil {
  41. return nil, nil
  42. }
  43. visitor := &arraySchemaVisitor{}
  44. schema.Accept(visitor)
  45. return visitor.Result, visitor.Err
  46. }
  47. // getMap converts schema to an *proto.Map object
  48. func getMap(schema proto.Schema) (*proto.Map, error) {
  49. if schema == nil {
  50. return nil, nil
  51. }
  52. visitor := &mapSchemaVisitor{}
  53. schema.Accept(visitor)
  54. return visitor.Result, visitor.Err
  55. }
  56. // getPrimitive converts schema to an *proto.Primitive object
  57. func getPrimitive(schema proto.Schema) (*proto.Primitive, error) {
  58. if schema == nil {
  59. return nil, nil
  60. }
  61. visitor := &primitiveSchemaVisitor{}
  62. schema.Accept(visitor)
  63. return visitor.Result, visitor.Err
  64. }
  65. type baseSchemaVisitor struct {
  66. Err error
  67. Kind string
  68. }
  69. // VisitArray implements openapi
  70. func (v *baseSchemaVisitor) VisitArray(array *proto.Array) {
  71. v.Kind = "array"
  72. v.Err = fmt.Errorf("Array type not expected")
  73. }
  74. // MergeMap implements openapi
  75. func (v *baseSchemaVisitor) VisitMap(*proto.Map) {
  76. v.Kind = "map"
  77. v.Err = fmt.Errorf("Map type not expected")
  78. }
  79. // MergePrimitive implements openapi
  80. func (v *baseSchemaVisitor) VisitPrimitive(*proto.Primitive) {
  81. v.Kind = "primitive"
  82. v.Err = fmt.Errorf("Primitive type not expected")
  83. }
  84. // VisitKind implements openapi
  85. func (v *baseSchemaVisitor) VisitKind(*proto.Kind) {
  86. v.Kind = "kind"
  87. v.Err = fmt.Errorf("Kind type not expected")
  88. }
  89. // VisitReference implements openapi
  90. func (v *baseSchemaVisitor) VisitReference(reference proto.Reference) {
  91. v.Kind = "reference"
  92. v.Err = fmt.Errorf("Reference type not expected")
  93. }
  94. type kindSchemaVisitor struct {
  95. baseSchemaVisitor
  96. Result *proto.Kind
  97. }
  98. // VisitKind implements openapi
  99. func (v *kindSchemaVisitor) VisitKind(result *proto.Kind) {
  100. v.Result = result
  101. v.Kind = "kind"
  102. }
  103. // VisitReference implements openapi
  104. func (v *kindSchemaVisitor) VisitReference(reference proto.Reference) {
  105. reference.SubSchema().Accept(v)
  106. if v.Err == nil {
  107. v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
  108. }
  109. }
  110. func copyExtensions(field string, from, to map[string]interface{}) error {
  111. // Copy extensions from field to type for references
  112. for key, val := range from {
  113. if curr, found := to[key]; found {
  114. // Don't allow the same extension to be defined both on the field and on the type
  115. return fmt.Errorf("Cannot override value for extension %s on field %s from %v to %v",
  116. key, field, curr, val)
  117. }
  118. to[key] = val
  119. }
  120. return nil
  121. }
  122. type mapSchemaVisitor struct {
  123. baseSchemaVisitor
  124. Result *proto.Map
  125. }
  126. // MergeMap implements openapi
  127. func (v *mapSchemaVisitor) VisitMap(result *proto.Map) {
  128. v.Result = result
  129. v.Kind = "map"
  130. }
  131. // VisitReference implements openapi
  132. func (v *mapSchemaVisitor) VisitReference(reference proto.Reference) {
  133. reference.SubSchema().Accept(v)
  134. if v.Err == nil {
  135. v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
  136. }
  137. }
  138. type arraySchemaVisitor struct {
  139. baseSchemaVisitor
  140. Result *proto.Array
  141. }
  142. // VisitArray implements openapi
  143. func (v *arraySchemaVisitor) VisitArray(result *proto.Array) {
  144. v.Result = result
  145. v.Kind = "array"
  146. v.Err = copySubElementPatchStrategy(result.Path.String(), result.GetExtensions(), result.SubType.GetExtensions())
  147. }
  148. // copyPatchStrategy copies the strategies to subelements to the subtype
  149. // e.g. PodTemplate.Volumes is a []Volume with "x-kubernetes-patch-strategy": "merge,retainKeys"
  150. // the "retainKeys" strategy applies to merging Volumes, and must be copied to the sub element
  151. func copySubElementPatchStrategy(field string, from, to map[string]interface{}) error {
  152. // Check if the parent has a patch strategy extension
  153. if ext, found := from["x-kubernetes-patch-strategy"]; found {
  154. strategy, ok := ext.(string)
  155. if !ok {
  156. return fmt.Errorf("Expected string value for x-kubernetes-patch-strategy on %s, was %T",
  157. field, ext)
  158. }
  159. // Check of the parent patch strategy has a sub patch strategy, and if so copy to the sub type
  160. if strings.Contains(strategy, ",") {
  161. strategies := strings.Split(strategy, ",")
  162. if len(strategies) != 2 {
  163. // Only 1 sub strategy is supported
  164. return fmt.Errorf(
  165. "Expected between 0 and 2 elements for x-kubernetes-patch-merge-strategy by got %v",
  166. strategies)
  167. }
  168. to["x-kubernetes-patch-strategy"] = strategies[1]
  169. }
  170. }
  171. return nil
  172. }
  173. // MergePrimitive implements openapi
  174. func (v *arraySchemaVisitor) VisitReference(reference proto.Reference) {
  175. reference.SubSchema().Accept(v)
  176. if v.Err == nil {
  177. v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
  178. }
  179. }
  180. type primitiveSchemaVisitor struct {
  181. baseSchemaVisitor
  182. Result *proto.Primitive
  183. }
  184. // MergePrimitive implements openapi
  185. func (v *primitiveSchemaVisitor) VisitPrimitive(result *proto.Primitive) {
  186. v.Result = result
  187. v.Kind = "primitive"
  188. }
  189. // VisitReference implements openapi
  190. func (v *primitiveSchemaVisitor) VisitReference(reference proto.Reference) {
  191. reference.SubSchema().Accept(v)
  192. if v.Err == nil {
  193. v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
  194. }
  195. }