util.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. "reflect"
  17. "strings"
  18. "k8s.io/apimachinery/pkg/runtime/schema"
  19. "k8s.io/kube-openapi/pkg/util/proto"
  20. "k8s.io/kubernetes/pkg/kubectl/apply"
  21. )
  22. // nilSafeLookup returns the value from the map if the map is non-nil
  23. func nilSafeLookup(key string, from map[string]interface{}) (interface{}, bool) {
  24. if from != nil {
  25. value, found := from[key]
  26. return value, found
  27. }
  28. // Not present
  29. return nil, false
  30. }
  31. // boundsSafeLookup returns the value from the slice if the slice is non-nil and
  32. // the index is in bounds.
  33. func boundsSafeLookup(index int, from []interface{}) (interface{}, bool) {
  34. if from != nil && len(from) > index {
  35. return from[index], true
  36. }
  37. return nil, false
  38. }
  39. // keysUnion returns a slice containing the union of the keys present in the arguments
  40. func keysUnion(maps ...map[string]interface{}) []string {
  41. keys := map[string]interface{}{}
  42. for _, m := range maps {
  43. for k := range m {
  44. keys[k] = nil
  45. }
  46. }
  47. result := []string{}
  48. for key := range keys {
  49. result = append(result, key)
  50. }
  51. return result
  52. }
  53. // max returns the argument with the highest value
  54. func max(values ...int) int {
  55. v := 0
  56. for _, i := range values {
  57. if i > v {
  58. v = i
  59. }
  60. }
  61. return v
  62. }
  63. // getType returns the type of the arguments. If the arguments don't have matching
  64. // types, getType returns an error. Nil types matching everything.
  65. func getType(args ...interface{}) (reflect.Type, error) {
  66. var last interface{}
  67. for _, next := range args {
  68. // Skip nil values
  69. if next == nil {
  70. continue
  71. }
  72. // Set the first non-nil value we find and continue
  73. if last == nil {
  74. last = next
  75. continue
  76. }
  77. // Verify the types of the values match
  78. if reflect.TypeOf(last).Kind() != reflect.TypeOf(next).Kind() {
  79. return nil, fmt.Errorf("missmatching non-nil types for the same field: %T %T", last, next)
  80. }
  81. }
  82. return reflect.TypeOf(last), nil
  83. }
  84. // getFieldMeta parses the metadata about the field from the openapi spec
  85. func getFieldMeta(s proto.Schema, name string) (apply.FieldMetaImpl, error) {
  86. m := apply.FieldMetaImpl{}
  87. if s != nil {
  88. ext := s.GetExtensions()
  89. if e, found := ext["x-kubernetes-patch-strategy"]; found {
  90. strategy, ok := e.(string)
  91. if !ok {
  92. return apply.FieldMetaImpl{}, fmt.Errorf("Expected string for x-kubernetes-patch-strategy by got %T", e)
  93. }
  94. // Take the first strategy if there are substrategies.
  95. // Sub strategies are copied to sub types in openapi.go
  96. strategies := strings.Split(strategy, ",")
  97. if len(strategies) > 2 {
  98. return apply.FieldMetaImpl{}, fmt.Errorf("Expected between 0 and 2 elements for x-kubernetes-patch-merge-strategy by got %v", strategies)
  99. }
  100. // For lists, choose the strategy for this type, not the subtype
  101. m.MergeType = strategies[0]
  102. }
  103. if k, found := ext["x-kubernetes-patch-merge-key"]; found {
  104. key, ok := k.(string)
  105. if !ok {
  106. return apply.FieldMetaImpl{}, fmt.Errorf("Expected string for x-kubernetes-patch-merge-key by got %T", k)
  107. }
  108. m.MergeKeys = apply.MergeKeys(strings.Split(key, ","))
  109. }
  110. }
  111. m.Name = name
  112. return m, nil
  113. }
  114. // getCommonGroupVersionKind verifies that the recorded, local and remote all share
  115. // the same GroupVersionKind and returns the value
  116. func getCommonGroupVersionKind(recorded, local, remote map[string]interface{}) (schema.GroupVersionKind, error) {
  117. recordedGVK, err := getGroupVersionKind(recorded)
  118. if err != nil {
  119. return schema.GroupVersionKind{}, err
  120. }
  121. localGVK, err := getGroupVersionKind(local)
  122. if err != nil {
  123. return schema.GroupVersionKind{}, err
  124. }
  125. remoteGVK, err := getGroupVersionKind(remote)
  126. if err != nil {
  127. return schema.GroupVersionKind{}, err
  128. }
  129. if !reflect.DeepEqual(recordedGVK, localGVK) || !reflect.DeepEqual(localGVK, remoteGVK) {
  130. return schema.GroupVersionKind{},
  131. fmt.Errorf("group version kinds do not match (recorded: %v local: %v remote: %v)",
  132. recordedGVK, localGVK, remoteGVK)
  133. }
  134. return recordedGVK, nil
  135. }
  136. // getGroupVersionKind returns the GroupVersionKind of the object
  137. func getGroupVersionKind(config map[string]interface{}) (schema.GroupVersionKind, error) {
  138. gvk := schema.GroupVersionKind{}
  139. if gv, found := config["apiVersion"]; found {
  140. casted, ok := gv.(string)
  141. if !ok {
  142. return gvk, fmt.Errorf("Expected string for apiVersion, found %T", gv)
  143. }
  144. s := strings.Split(casted, "/")
  145. if len(s) != 1 {
  146. gvk.Group = s[0]
  147. }
  148. gvk.Version = s[len(s)-1]
  149. } else {
  150. return gvk, fmt.Errorf("Missing apiVersion in Kind %v", config)
  151. }
  152. if k, found := config["kind"]; found {
  153. casted, ok := k.(string)
  154. if !ok {
  155. return gvk, fmt.Errorf("Expected string for kind, found %T", k)
  156. }
  157. gvk.Kind = casted
  158. } else {
  159. return gvk, fmt.Errorf("Missing kind in Kind %v", config)
  160. }
  161. return gvk, nil
  162. }