element.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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 apply
  14. import (
  15. "fmt"
  16. )
  17. // Element contains the record, local, and remote value for a field in an object
  18. // and metadata about the field read from openapi.
  19. // Calling Merge on an element will apply the passed in strategy to Element -
  20. // e.g. either replacing the whole element with the local copy or merging each
  21. // of the recorded, local and remote fields of the element.
  22. type Element interface {
  23. // FieldMeta specifies which merge strategy to use for this element
  24. FieldMeta
  25. // Merge merges the recorded, local and remote values in the element using the Strategy
  26. // provided as an argument. Calls the type specific method on the Strategy - following the
  27. // "Accept" method from the "Visitor" pattern.
  28. // e.g. Merge on a ListElement will call Strategy.MergeList(self)
  29. // Returns the Result of the merged elements
  30. Merge(Strategy) (Result, error)
  31. // HasRecorded returns true if the field was explicitly
  32. // present in the recorded source. This is to differentiate between
  33. // undefined and set to null
  34. HasRecorded() bool
  35. // GetRecorded returns the field value from the recorded source of the object
  36. GetRecorded() interface{}
  37. // HasLocal returns true if the field was explicitly
  38. // present in the local source. This is to differentiate between
  39. // undefined and set to null
  40. HasLocal() bool
  41. // GetLocal returns the field value from the local source of the object
  42. GetLocal() interface{}
  43. // HasRemote returns true if the field was explicitly
  44. // present in the remote source. This is to differentiate between
  45. // undefined and set to null
  46. HasRemote() bool
  47. // GetRemote returns the field value from the remote source of the object
  48. GetRemote() interface{}
  49. }
  50. // FieldMeta defines the strategy used to apply a Patch for an element
  51. type FieldMeta interface {
  52. // GetFieldMergeType returns the type of merge strategy to use for this field
  53. // maybe "merge", "replace" or "retainkeys"
  54. // TODO: There maybe multiple strategies, so this may need to be a slice, map, or struct
  55. // Address this in a follow up in the PR to introduce retainkeys strategy
  56. GetFieldMergeType() string
  57. // GetFieldMergeKeys returns the merge key to use when the MergeType is "merge" and underlying type is a list
  58. GetFieldMergeKeys() MergeKeys
  59. // GetFieldType returns the openapi field type - e.g. primitive, array, map, type, reference
  60. GetFieldType() string
  61. }
  62. // FieldMetaImpl implements FieldMeta
  63. type FieldMetaImpl struct {
  64. // MergeType is the type of merge strategy to use for this field
  65. // maybe "merge", "replace" or "retainkeys"
  66. MergeType string
  67. // MergeKeys are the merge keys to use when the MergeType is "merge" and underlying type is a list
  68. MergeKeys MergeKeys
  69. // Type is the openapi type of the field - "list", "primitive", "map"
  70. Type string
  71. // Name contains name of the field
  72. Name string
  73. }
  74. // GetFieldMergeType implements FieldMeta.GetFieldMergeType
  75. func (s FieldMetaImpl) GetFieldMergeType() string {
  76. return s.MergeType
  77. }
  78. // GetFieldMergeKeys implements FieldMeta.GetFieldMergeKeys
  79. func (s FieldMetaImpl) GetFieldMergeKeys() MergeKeys {
  80. return s.MergeKeys
  81. }
  82. // GetFieldType implements FieldMeta.GetFieldType
  83. func (s FieldMetaImpl) GetFieldType() string {
  84. return s.Type
  85. }
  86. // MergeKeyValue records the value of the mergekey for an item in a list
  87. type MergeKeyValue map[string]string
  88. // Equal returns true if the MergeKeyValues share the same value,
  89. // representing the same item in a list
  90. func (v MergeKeyValue) Equal(o MergeKeyValue) bool {
  91. if len(v) != len(o) {
  92. return false
  93. }
  94. for key, v1 := range v {
  95. if v2, found := o[key]; !found || v1 != v2 {
  96. return false
  97. }
  98. }
  99. return true
  100. }
  101. // MergeKeys is the set of fields on an object that uniquely identify
  102. // and is used when merging lists to identify the "same" object
  103. // independent of the ordering of the objects
  104. type MergeKeys []string
  105. // GetMergeKeyValue parses the MergeKeyValue from an item in a list
  106. func (mk MergeKeys) GetMergeKeyValue(i interface{}) (MergeKeyValue, error) {
  107. result := MergeKeyValue{}
  108. if len(mk) <= 0 {
  109. return result, fmt.Errorf("merge key must have at least 1 value to merge")
  110. }
  111. m, ok := i.(map[string]interface{})
  112. if !ok {
  113. return result, fmt.Errorf("cannot use mergekey %v for primitive item in list %v", mk, i)
  114. }
  115. for _, field := range mk {
  116. if value, found := m[field]; !found {
  117. result[field] = ""
  118. } else {
  119. result[field] = fmt.Sprintf("%v", value)
  120. }
  121. }
  122. return result, nil
  123. }
  124. // CombinedPrimitiveSlice implements a slice of primitives
  125. type CombinedPrimitiveSlice struct {
  126. Items []*PrimitiveListItem
  127. }
  128. // PrimitiveListItem represents a single value in a slice of primitives
  129. type PrimitiveListItem struct {
  130. // Value is the value of the primitive, should match recorded, local and remote
  131. Value interface{}
  132. RawElementData
  133. }
  134. // Contains returns true if the slice contains the l
  135. func (s *CombinedPrimitiveSlice) lookup(l interface{}) *PrimitiveListItem {
  136. val := fmt.Sprintf("%v", l)
  137. for _, i := range s.Items {
  138. if fmt.Sprintf("%v", i.Value) == val {
  139. return i
  140. }
  141. }
  142. return nil
  143. }
  144. func (s *CombinedPrimitiveSlice) upsert(l interface{}) *PrimitiveListItem {
  145. // Return the item if it exists
  146. if item := s.lookup(l); item != nil {
  147. return item
  148. }
  149. // Otherwise create a new item and append to the list
  150. item := &PrimitiveListItem{
  151. Value: l,
  152. }
  153. s.Items = append(s.Items, item)
  154. return item
  155. }
  156. // UpsertRecorded adds l to the slice. If there is already a value of l in the
  157. // slice for either the local or remote, set on that value as the recorded value
  158. // Otherwise append a new item to the list with the recorded value.
  159. func (s *CombinedPrimitiveSlice) UpsertRecorded(l interface{}) {
  160. v := s.upsert(l)
  161. v.recorded = l
  162. v.recordedSet = true
  163. }
  164. // UpsertLocal adds l to the slice. If there is already a value of l in the
  165. // slice for either the recorded or remote, set on that value as the local value
  166. // Otherwise append a new item to the list with the local value.
  167. func (s *CombinedPrimitiveSlice) UpsertLocal(l interface{}) {
  168. v := s.upsert(l)
  169. v.local = l
  170. v.localSet = true
  171. }
  172. // UpsertRemote adds l to the slice. If there is already a value of l in the
  173. // slice for either the local or recorded, set on that value as the remote value
  174. // Otherwise append a new item to the list with the remote value.
  175. func (s *CombinedPrimitiveSlice) UpsertRemote(l interface{}) {
  176. v := s.upsert(l)
  177. v.remote = l
  178. v.remoteSet = true
  179. }
  180. // ListItem represents a single value in a slice of maps or types
  181. type ListItem struct {
  182. // KeyValue is the merge key value of the item
  183. KeyValue MergeKeyValue
  184. // RawElementData contains the field values
  185. RawElementData
  186. }
  187. // CombinedMapSlice is a slice of maps or types with merge keys
  188. type CombinedMapSlice struct {
  189. Items []*ListItem
  190. }
  191. // Lookup returns the ListItem matching the merge key, or nil if not found.
  192. func (s *CombinedMapSlice) lookup(v MergeKeyValue) *ListItem {
  193. for _, i := range s.Items {
  194. if i.KeyValue.Equal(v) {
  195. return i
  196. }
  197. }
  198. return nil
  199. }
  200. func (s *CombinedMapSlice) upsert(key MergeKeys, l interface{}) (*ListItem, error) {
  201. // Get the identity of the item
  202. val, err := key.GetMergeKeyValue(l)
  203. if err != nil {
  204. return nil, err
  205. }
  206. // Return the item if it exists
  207. if item := s.lookup(val); item != nil {
  208. return item, nil
  209. }
  210. // Otherwise create a new item and append to the list
  211. item := &ListItem{
  212. KeyValue: val,
  213. }
  214. s.Items = append(s.Items, item)
  215. return item, nil
  216. }
  217. // UpsertRecorded adds l to the slice. If there is already a value of l sharing
  218. // l's merge key in the slice for either the local or remote, set l the recorded value
  219. // Otherwise append a new item to the list with the recorded value.
  220. func (s *CombinedMapSlice) UpsertRecorded(key MergeKeys, l interface{}) error {
  221. item, err := s.upsert(key, l)
  222. if err != nil {
  223. return err
  224. }
  225. item.SetRecorded(l)
  226. return nil
  227. }
  228. // UpsertLocal adds l to the slice. If there is already a value of l sharing
  229. // l's merge key in the slice for either the recorded or remote, set l the local value
  230. // Otherwise append a new item to the list with the local value.
  231. func (s *CombinedMapSlice) UpsertLocal(key MergeKeys, l interface{}) error {
  232. item, err := s.upsert(key, l)
  233. if err != nil {
  234. return err
  235. }
  236. item.SetLocal(l)
  237. return nil
  238. }
  239. // UpsertRemote adds l to the slice. If there is already a value of l sharing
  240. // l's merge key in the slice for either the recorded or local, set l the remote value
  241. // Otherwise append a new item to the list with the remote value.
  242. func (s *CombinedMapSlice) UpsertRemote(key MergeKeys, l interface{}) error {
  243. item, err := s.upsert(key, l)
  244. if err != nil {
  245. return err
  246. }
  247. item.SetRemote(l)
  248. return nil
  249. }
  250. // IsDrop returns true if the field represented by e should be dropped from the merged object
  251. func IsDrop(e Element) bool {
  252. // Specified in the last value recorded value and since deleted from the local
  253. removed := e.HasRecorded() && !e.HasLocal()
  254. // Specified locally and explicitly set to null
  255. setToNil := e.HasLocal() && e.GetLocal() == nil
  256. return removed || setToNil
  257. }
  258. // IsAdd returns true if the field represented by e should have the local value directly
  259. // added to the merged object instead of merging the recorded, local and remote values
  260. func IsAdd(e Element) bool {
  261. // If it isn't already present in the remote value and is present in the local value
  262. return e.HasLocal() && !e.HasRemote()
  263. }
  264. // NewRawElementData returns a new RawElementData, setting IsSet to true for
  265. // non-nil values, and leaving IsSet false for nil values.
  266. // Note: use this only when you want a nil-value to be considered "unspecified"
  267. // (ignore) and not "unset" (deleted).
  268. func NewRawElementData(recorded, local, remote interface{}) RawElementData {
  269. data := RawElementData{}
  270. if recorded != nil {
  271. data.SetRecorded(recorded)
  272. }
  273. if local != nil {
  274. data.SetLocal(local)
  275. }
  276. if remote != nil {
  277. data.SetRemote(remote)
  278. }
  279. return data
  280. }
  281. // RawElementData contains the raw recorded, local and remote data
  282. // and metadata about whethere or not each was set
  283. type RawElementData struct {
  284. HasElementData
  285. recorded interface{}
  286. local interface{}
  287. remote interface{}
  288. }
  289. // SetRecorded sets the recorded value
  290. func (b *RawElementData) SetRecorded(value interface{}) {
  291. b.recorded = value
  292. b.recordedSet = true
  293. }
  294. // SetLocal sets the local value
  295. func (b *RawElementData) SetLocal(value interface{}) {
  296. b.local = value
  297. b.localSet = true
  298. }
  299. // SetRemote sets the remote value
  300. func (b *RawElementData) SetRemote(value interface{}) {
  301. b.remote = value
  302. b.remoteSet = true
  303. }
  304. // GetRecorded implements Element.GetRecorded
  305. func (b RawElementData) GetRecorded() interface{} {
  306. // https://golang.org/doc/faq#nil_error
  307. if b.recorded == nil {
  308. return nil
  309. }
  310. return b.recorded
  311. }
  312. // GetLocal implements Element.GetLocal
  313. func (b RawElementData) GetLocal() interface{} {
  314. // https://golang.org/doc/faq#nil_error
  315. if b.local == nil {
  316. return nil
  317. }
  318. return b.local
  319. }
  320. // GetRemote implements Element.GetRemote
  321. func (b RawElementData) GetRemote() interface{} {
  322. // https://golang.org/doc/faq#nil_error
  323. if b.remote == nil {
  324. return nil
  325. }
  326. return b.remote
  327. }
  328. // HasElementData contains whether a field was set in the recorded, local and remote sources
  329. type HasElementData struct {
  330. recordedSet bool
  331. localSet bool
  332. remoteSet bool
  333. }
  334. // HasRecorded implements Element.HasRecorded
  335. func (e HasElementData) HasRecorded() bool {
  336. return e.recordedSet
  337. }
  338. // HasLocal implements Element.HasLocal
  339. func (e HasElementData) HasLocal() bool {
  340. return e.localSet
  341. }
  342. // HasRemote implements Element.HasRemote
  343. func (e HasElementData) HasRemote() bool {
  344. return e.remoteSet
  345. }
  346. // ConflictDetector defines the capability to detect conflict. An element can examine remote/recorded value to detect conflict.
  347. type ConflictDetector interface {
  348. HasConflict() error
  349. }