meta.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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 strategicpatch
  14. import (
  15. "errors"
  16. "fmt"
  17. "reflect"
  18. "strings"
  19. "k8s.io/apimachinery/pkg/util/mergepatch"
  20. forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
  21. openapi "k8s.io/kube-openapi/pkg/util/proto"
  22. "k8s.io/kube-openapi/pkg/validation/spec"
  23. )
  24. const patchMergeKey = "x-kubernetes-patch-merge-key"
  25. const patchStrategy = "x-kubernetes-patch-strategy"
  26. type PatchMeta struct {
  27. patchStrategies []string
  28. patchMergeKey string
  29. }
  30. func (pm *PatchMeta) GetPatchStrategies() []string {
  31. if pm.patchStrategies == nil {
  32. return []string{}
  33. }
  34. return pm.patchStrategies
  35. }
  36. func (pm *PatchMeta) SetPatchStrategies(ps []string) {
  37. pm.patchStrategies = ps
  38. }
  39. func (pm *PatchMeta) GetPatchMergeKey() string {
  40. return pm.patchMergeKey
  41. }
  42. func (pm *PatchMeta) SetPatchMergeKey(pmk string) {
  43. pm.patchMergeKey = pmk
  44. }
  45. type LookupPatchMeta interface {
  46. // LookupPatchMetadataForStruct gets subschema and the patch metadata (e.g. patch strategy and merge key) for map.
  47. LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error)
  48. // LookupPatchMetadataForSlice get subschema and the patch metadata for slice.
  49. LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error)
  50. // Get the type name of the field
  51. Name() string
  52. }
  53. type PatchMetaFromStruct struct {
  54. T reflect.Type
  55. }
  56. func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) {
  57. t, err := getTagStructType(dataStruct)
  58. return PatchMetaFromStruct{T: t}, err
  59. }
  60. var _ LookupPatchMeta = PatchMetaFromStruct{}
  61. func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
  62. fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key)
  63. if err != nil {
  64. return nil, PatchMeta{}, err
  65. }
  66. return PatchMetaFromStruct{T: fieldType},
  67. PatchMeta{
  68. patchStrategies: fieldPatchStrategies,
  69. patchMergeKey: fieldPatchMergeKey,
  70. }, nil
  71. }
  72. func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
  73. subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key)
  74. if err != nil {
  75. return nil, PatchMeta{}, err
  76. }
  77. elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct)
  78. t := elemPatchMetaFromStruct.T
  79. var elemType reflect.Type
  80. switch t.Kind() {
  81. // If t is an array or a slice, get the element type.
  82. // If element is still an array or a slice, return an error.
  83. // Otherwise, return element type.
  84. case reflect.Array, reflect.Slice:
  85. elemType = t.Elem()
  86. if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice {
  87. return nil, PatchMeta{}, errors.New("unexpected slice of slice")
  88. }
  89. // If t is an pointer, get the underlying element.
  90. // If the underlying element is neither an array nor a slice, the pointer is pointing to a slice,
  91. // e.g. https://github.com/kubernetes/kubernetes/blob/bc22e206c79282487ea0bf5696d5ccec7e839a76/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go#L2782-L2822
  92. // If the underlying element is either an array or a slice, return its element type.
  93. case reflect.Pointer:
  94. t = t.Elem()
  95. if t.Kind() == reflect.Array || t.Kind() == reflect.Slice {
  96. t = t.Elem()
  97. }
  98. elemType = t
  99. default:
  100. return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String())
  101. }
  102. return PatchMetaFromStruct{T: elemType}, patchMeta, nil
  103. }
  104. func (s PatchMetaFromStruct) Name() string {
  105. return s.T.Kind().String()
  106. }
  107. func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
  108. if dataStruct == nil {
  109. return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
  110. }
  111. t := reflect.TypeOf(dataStruct)
  112. // Get the underlying type for pointers
  113. if t.Kind() == reflect.Pointer {
  114. t = t.Elem()
  115. }
  116. if t.Kind() != reflect.Struct {
  117. return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
  118. }
  119. return t, nil
  120. }
  121. func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type {
  122. t, err := getTagStructType(dataStruct)
  123. if err != nil {
  124. panic(err)
  125. }
  126. return t
  127. }
  128. type PatchMetaFromOpenAPIV3 struct {
  129. // SchemaList is required to resolve OpenAPI V3 references
  130. SchemaList map[string]*spec.Schema
  131. Schema *spec.Schema
  132. }
  133. func (s PatchMetaFromOpenAPIV3) traverse(key string) (PatchMetaFromOpenAPIV3, error) {
  134. if s.Schema == nil {
  135. return PatchMetaFromOpenAPIV3{}, nil
  136. }
  137. if len(s.Schema.Properties) == 0 {
  138. return PatchMetaFromOpenAPIV3{}, fmt.Errorf("unable to find api field \"%s\"", key)
  139. }
  140. subschema, ok := s.Schema.Properties[key]
  141. if !ok {
  142. return PatchMetaFromOpenAPIV3{}, fmt.Errorf("unable to find api field \"%s\"", key)
  143. }
  144. return PatchMetaFromOpenAPIV3{SchemaList: s.SchemaList, Schema: &subschema}, nil
  145. }
  146. func resolve(l *PatchMetaFromOpenAPIV3) error {
  147. if len(l.Schema.AllOf) > 0 {
  148. l.Schema = &l.Schema.AllOf[0]
  149. }
  150. if refString := l.Schema.Ref.String(); refString != "" {
  151. str := strings.TrimPrefix(refString, "#/components/schemas/")
  152. sch, ok := l.SchemaList[str]
  153. if ok {
  154. l.Schema = sch
  155. } else {
  156. return fmt.Errorf("unable to resolve %s in OpenAPI V3", refString)
  157. }
  158. }
  159. return nil
  160. }
  161. func (s PatchMetaFromOpenAPIV3) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
  162. l, err := s.traverse(key)
  163. if err != nil {
  164. return l, PatchMeta{}, err
  165. }
  166. p := PatchMeta{}
  167. f, ok := l.Schema.Extensions[patchMergeKey]
  168. if ok {
  169. p.SetPatchMergeKey(f.(string))
  170. }
  171. g, ok := l.Schema.Extensions[patchStrategy]
  172. if ok {
  173. p.SetPatchStrategies(strings.Split(g.(string), ","))
  174. }
  175. err = resolve(&l)
  176. return l, p, err
  177. }
  178. func (s PatchMetaFromOpenAPIV3) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
  179. l, err := s.traverse(key)
  180. if err != nil {
  181. return l, PatchMeta{}, err
  182. }
  183. p := PatchMeta{}
  184. f, ok := l.Schema.Extensions[patchMergeKey]
  185. if ok {
  186. p.SetPatchMergeKey(f.(string))
  187. }
  188. g, ok := l.Schema.Extensions[patchStrategy]
  189. if ok {
  190. p.SetPatchStrategies(strings.Split(g.(string), ","))
  191. }
  192. if l.Schema.Items != nil {
  193. l.Schema = l.Schema.Items.Schema
  194. }
  195. err = resolve(&l)
  196. return l, p, err
  197. }
  198. func (s PatchMetaFromOpenAPIV3) Name() string {
  199. schema := s.Schema
  200. if len(schema.Type) > 0 {
  201. return strings.Join(schema.Type, "")
  202. }
  203. return "Struct"
  204. }
  205. type PatchMetaFromOpenAPI struct {
  206. Schema openapi.Schema
  207. }
  208. func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI {
  209. return PatchMetaFromOpenAPI{Schema: s}
  210. }
  211. var _ LookupPatchMeta = PatchMetaFromOpenAPI{}
  212. func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
  213. if s.Schema == nil {
  214. return &PatchMetaFromOpenAPI{}, PatchMeta{}, nil
  215. }
  216. kindItem := NewKindItem(key, s.Schema.GetPath())
  217. s.Schema.Accept(kindItem)
  218. err := kindItem.Error()
  219. if err != nil {
  220. return nil, PatchMeta{}, err
  221. }
  222. return PatchMetaFromOpenAPI{Schema: kindItem.subschema},
  223. kindItem.patchmeta, nil
  224. }
  225. func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
  226. if s.Schema == nil {
  227. return nil, PatchMeta{}, nil
  228. }
  229. sliceItem := NewSliceItem(key, s.Schema.GetPath())
  230. s.Schema.Accept(sliceItem)
  231. err := sliceItem.Error()
  232. if err != nil {
  233. return nil, PatchMeta{}, err
  234. }
  235. return PatchMetaFromOpenAPI{Schema: sliceItem.subschema},
  236. sliceItem.patchmeta, nil
  237. }
  238. func (s PatchMetaFromOpenAPI) Name() string {
  239. schema := s.Schema
  240. return schema.GetName()
  241. }