aggregator.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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 aggregator
  14. import (
  15. "fmt"
  16. "reflect"
  17. "sort"
  18. "strings"
  19. "github.com/go-openapi/spec"
  20. "k8s.io/kube-openapi/pkg/util"
  21. )
  22. const gvkKey = "x-kubernetes-group-version-kind"
  23. // usedDefinitionForSpec returns a map with all used definitions in the provided spec as keys and true as values.
  24. func usedDefinitionForSpec(root *spec.Swagger) map[string]bool {
  25. usedDefinitions := map[string]bool{}
  26. walkOnAllReferences(func(ref *spec.Ref) {
  27. if refStr := ref.String(); refStr != "" && strings.HasPrefix(refStr, definitionPrefix) {
  28. usedDefinitions[refStr[len(definitionPrefix):]] = true
  29. }
  30. }, root)
  31. return usedDefinitions
  32. }
  33. // FilterSpecByPaths removes unnecessary paths and definitions used by those paths.
  34. // i.e. if a Path removed by this function, all definitions used by it and not used
  35. // anywhere else will also be removed.
  36. func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) {
  37. *sp = *FilterSpecByPathsWithoutSideEffects(sp, keepPathPrefixes)
  38. }
  39. // FilterSpecByPathsWithoutSideEffects removes unnecessary paths and definitions used by those paths.
  40. // i.e. if a Path removed by this function, all definitions used by it and not used
  41. // anywhere else will also be removed.
  42. // It does not modify the input, but the output shares data structures with the input.
  43. func FilterSpecByPathsWithoutSideEffects(sp *spec.Swagger, keepPathPrefixes []string) *spec.Swagger {
  44. if sp.Paths == nil {
  45. return sp
  46. }
  47. // Walk all references to find all used definitions. This function
  48. // want to only deal with unused definitions resulted from filtering paths.
  49. // Thus a definition will be removed only if it has been used before but
  50. // it is unused because of a path prune.
  51. initialUsedDefinitions := usedDefinitionForSpec(sp)
  52. // First remove unwanted paths
  53. prefixes := util.NewTrie(keepPathPrefixes)
  54. ret := *sp
  55. ret.Paths = &spec.Paths{
  56. VendorExtensible: sp.Paths.VendorExtensible,
  57. Paths: map[string]spec.PathItem{},
  58. }
  59. for path, pathItem := range sp.Paths.Paths {
  60. if !prefixes.HasPrefix(path) {
  61. continue
  62. }
  63. ret.Paths.Paths[path] = pathItem
  64. }
  65. // Walk all references to find all definition references.
  66. usedDefinitions := usedDefinitionForSpec(&ret)
  67. // Remove unused definitions
  68. ret.Definitions = spec.Definitions{}
  69. for k, v := range sp.Definitions {
  70. if usedDefinitions[k] || !initialUsedDefinitions[k] {
  71. ret.Definitions[k] = v
  72. }
  73. }
  74. return &ret
  75. }
  76. type rename struct {
  77. from, to string
  78. }
  79. // renameDefinition renames references, without mutating the input.
  80. // The output might share data structures with the input.
  81. func renameDefinition(s *spec.Swagger, renames map[string]string) *spec.Swagger {
  82. refRenames := make(map[string]string, len(renames))
  83. foundOne := false
  84. for k, v := range renames {
  85. refRenames[definitionPrefix+k] = definitionPrefix + v
  86. if _, ok := s.Definitions[k]; ok {
  87. foundOne = true
  88. }
  89. }
  90. if !foundOne {
  91. return s
  92. }
  93. ret := &spec.Swagger{}
  94. *ret = *s
  95. ret = replaceReferences(func(ref *spec.Ref) *spec.Ref {
  96. refName := ref.String()
  97. if newRef, found := refRenames[refName]; found {
  98. ret := spec.MustCreateRef(newRef)
  99. return &ret
  100. }
  101. return ref
  102. }, ret)
  103. renamedDefinitions := make(spec.Definitions, len(ret.Definitions))
  104. for k, v := range ret.Definitions {
  105. if newRef, found := renames[k]; found {
  106. k = newRef
  107. }
  108. renamedDefinitions[k] = v
  109. }
  110. ret.Definitions = renamedDefinitions
  111. return ret
  112. }
  113. // MergeSpecsIgnorePathConflict is the same as MergeSpecs except it will ignore any path
  114. // conflicts by keeping the paths of destination. It will rename definition conflicts.
  115. // The source is not mutated.
  116. func MergeSpecsIgnorePathConflict(dest, source *spec.Swagger) error {
  117. return mergeSpecs(dest, source, true, true)
  118. }
  119. // MergeSpecsFailOnDefinitionConflict is differ from MergeSpecs as it fails if there is
  120. // a definition conflict.
  121. // The source is not mutated.
  122. func MergeSpecsFailOnDefinitionConflict(dest, source *spec.Swagger) error {
  123. return mergeSpecs(dest, source, false, false)
  124. }
  125. // MergeSpecs copies paths and definitions from source to dest, rename definitions if needed.
  126. // dest will be mutated, and source will not be changed. It will fail on path conflicts.
  127. // The source is not mutated.
  128. func MergeSpecs(dest, source *spec.Swagger) error {
  129. return mergeSpecs(dest, source, true, false)
  130. }
  131. // mergeSpecs merges source into dest while resolving conflicts.
  132. // The source is not mutated.
  133. func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConflicts bool) (err error) {
  134. // Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
  135. if source.Paths == nil {
  136. // When a source spec does not have any path, that means none of the definitions
  137. // are used thus we should not do anything
  138. return nil
  139. }
  140. if dest.Paths == nil {
  141. dest.Paths = &spec.Paths{}
  142. }
  143. if ignorePathConflicts {
  144. keepPaths := []string{}
  145. hasConflictingPath := false
  146. for k := range source.Paths.Paths {
  147. if _, found := dest.Paths.Paths[k]; !found {
  148. keepPaths = append(keepPaths, k)
  149. } else {
  150. hasConflictingPath = true
  151. }
  152. }
  153. if len(keepPaths) == 0 {
  154. // There is nothing to merge. All paths are conflicting.
  155. return nil
  156. }
  157. if hasConflictingPath {
  158. source = FilterSpecByPathsWithoutSideEffects(source, keepPaths)
  159. }
  160. }
  161. // Check for model conflicts and rename to make definitions conflict-free (modulo different GVKs)
  162. usedNames := map[string]bool{}
  163. for k := range dest.Definitions {
  164. usedNames[k] = true
  165. }
  166. renames := map[string]string{}
  167. DEFINITIONLOOP:
  168. for k, v := range source.Definitions {
  169. existing, found := dest.Definitions[k]
  170. if !found || deepEqualDefinitionsModuloGVKs(&existing, &v) {
  171. // skip for now, we copy them after the rename loop
  172. continue
  173. }
  174. if !renameModelConflicts {
  175. return fmt.Errorf("model name conflict in merging OpenAPI spec: %s", k)
  176. }
  177. // Reuse previously renamed model if one exists
  178. var newName string
  179. i := 1
  180. for found {
  181. i++
  182. newName = fmt.Sprintf("%s_v%d", k, i)
  183. existing, found = dest.Definitions[newName]
  184. if found && deepEqualDefinitionsModuloGVKs(&existing, &v) {
  185. renames[k] = newName
  186. continue DEFINITIONLOOP
  187. }
  188. }
  189. _, foundInSource := source.Definitions[newName]
  190. for usedNames[newName] || foundInSource {
  191. i++
  192. newName = fmt.Sprintf("%s_v%d", k, i)
  193. _, foundInSource = source.Definitions[newName]
  194. }
  195. renames[k] = newName
  196. usedNames[newName] = true
  197. }
  198. source = renameDefinition(source, renames)
  199. // now without conflict (modulo different GVKs), copy definitions to dest
  200. for k, v := range source.Definitions {
  201. if existing, found := dest.Definitions[k]; !found {
  202. if dest.Definitions == nil {
  203. dest.Definitions = spec.Definitions{}
  204. }
  205. dest.Definitions[k] = v
  206. } else if merged, changed, err := mergedGVKs(&existing, &v); err != nil {
  207. return err
  208. } else if changed {
  209. existing.Extensions[gvkKey] = merged
  210. }
  211. }
  212. // Check for path conflicts
  213. for k, v := range source.Paths.Paths {
  214. if _, found := dest.Paths.Paths[k]; found {
  215. return fmt.Errorf("unable to merge: duplicated path %s", k)
  216. }
  217. // PathItem may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
  218. if dest.Paths.Paths == nil {
  219. dest.Paths.Paths = map[string]spec.PathItem{}
  220. }
  221. dest.Paths.Paths[k] = v
  222. }
  223. return nil
  224. }
  225. // deepEqualDefinitionsModuloGVKs compares s1 and s2, but ignores the x-kubernetes-group-version-kind extension.
  226. func deepEqualDefinitionsModuloGVKs(s1, s2 *spec.Schema) bool {
  227. if s1 == nil {
  228. return s2 == nil
  229. } else if s2 == nil {
  230. return false
  231. }
  232. if !reflect.DeepEqual(s1.Extensions, s2.Extensions) {
  233. for k, v := range s1.Extensions {
  234. if k == gvkKey {
  235. continue
  236. }
  237. if !reflect.DeepEqual(v, s2.Extensions[k]) {
  238. return false
  239. }
  240. }
  241. len1 := len(s1.Extensions)
  242. len2 := len(s2.Extensions)
  243. if _, found := s1.Extensions[gvkKey]; found {
  244. len1--
  245. }
  246. if _, found := s2.Extensions[gvkKey]; found {
  247. len2--
  248. }
  249. if len1 != len2 {
  250. return false
  251. }
  252. if s1.Extensions != nil {
  253. shallowCopy := *s1
  254. s1 = &shallowCopy
  255. s1.Extensions = nil
  256. }
  257. if s2.Extensions != nil {
  258. shallowCopy := *s2
  259. s2 = &shallowCopy
  260. s2.Extensions = nil
  261. }
  262. }
  263. return reflect.DeepEqual(s1, s2)
  264. }
  265. // mergedGVKs merges the x-kubernetes-group-version-kind slices and returns the result, and whether
  266. // s1's x-kubernetes-group-version-kind slice was changed at all.
  267. func mergedGVKs(s1, s2 *spec.Schema) (interface{}, bool, error) {
  268. gvk1, found1 := s1.Extensions[gvkKey]
  269. gvk2, found2 := s2.Extensions[gvkKey]
  270. if !found1 {
  271. return gvk2, found2, nil
  272. }
  273. if !found2 {
  274. return gvk1, false, nil
  275. }
  276. slice1, ok := gvk1.([]interface{})
  277. if !ok {
  278. return nil, false, fmt.Errorf("expected slice of GroupVersionKinds, got: %+v", slice1)
  279. }
  280. slice2, ok := gvk2.([]interface{})
  281. if !ok {
  282. return nil, false, fmt.Errorf("expected slice of GroupVersionKinds, got: %+v", slice2)
  283. }
  284. ret := make([]interface{}, len(slice1), len(slice1)+len(slice2))
  285. keys := make([]string, 0, len(slice1)+len(slice2))
  286. copy(ret, slice1)
  287. seen := make(map[string]bool, len(slice1))
  288. for _, x := range slice1 {
  289. gvk, ok := x.(map[string]interface{})
  290. if !ok {
  291. return nil, false, fmt.Errorf(`expected {"group": <group>, "kind": <kind>, "version": <version>}, got: %#v`, x)
  292. }
  293. k := fmt.Sprintf("%s/%s.%s", gvk["group"], gvk["version"], gvk["kind"])
  294. keys = append(keys, k)
  295. seen[k] = true
  296. }
  297. changed := false
  298. for _, x := range slice2 {
  299. gvk, ok := x.(map[string]interface{})
  300. if !ok {
  301. return nil, false, fmt.Errorf(`expected {"group": <group>, "kind": <kind>, "version": <version>}, got: %#v`, x)
  302. }
  303. k := fmt.Sprintf("%s/%s.%s", gvk["group"], gvk["version"], gvk["kind"])
  304. if seen[k] {
  305. continue
  306. }
  307. ret = append(ret, x)
  308. keys = append(keys, k)
  309. changed = true
  310. }
  311. if changed {
  312. sort.Sort(byKeys{ret, keys})
  313. }
  314. return ret, changed, nil
  315. }
  316. type byKeys struct {
  317. values []interface{}
  318. keys []string
  319. }
  320. func (b byKeys) Len() int {
  321. return len(b.values)
  322. }
  323. func (b byKeys) Less(i, j int) bool {
  324. return b.keys[i] < b.keys[j]
  325. }
  326. func (b byKeys) Swap(i, j int) {
  327. b.values[i], b.values[j] = b.values[j], b.values[i]
  328. b.keys[i], b.keys[j] = b.keys[j], b.keys[i]
  329. }