update.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. Copyright 2018 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 merge
  14. import (
  15. "fmt"
  16. "sigs.k8s.io/structured-merge-diff/v3/fieldpath"
  17. "sigs.k8s.io/structured-merge-diff/v3/typed"
  18. )
  19. // Converter is an interface to the conversion logic. The converter
  20. // needs to be able to convert objects from one version to another.
  21. type Converter interface {
  22. Convert(object *typed.TypedValue, version fieldpath.APIVersion) (*typed.TypedValue, error)
  23. IsMissingVersionError(error) bool
  24. }
  25. // Updater is the object used to compute updated FieldSets and also
  26. // merge the object on Apply.
  27. type Updater struct {
  28. Converter Converter
  29. enableUnions bool
  30. }
  31. // EnableUnionFeature turns on union handling. It is disabled by default until the
  32. // feature is complete.
  33. func (s *Updater) EnableUnionFeature() {
  34. s.enableUnions = true
  35. }
  36. func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, *typed.Comparison, error) {
  37. conflicts := fieldpath.ManagedFields{}
  38. removed := fieldpath.ManagedFields{}
  39. compare, err := oldObject.Compare(newObject)
  40. if err != nil {
  41. return nil, nil, fmt.Errorf("failed to compare objects: %v", err)
  42. }
  43. versions := map[fieldpath.APIVersion]*typed.Comparison{
  44. version: compare,
  45. }
  46. for manager, managerSet := range managers {
  47. if manager == workflow {
  48. continue
  49. }
  50. compare, ok := versions[managerSet.APIVersion()]
  51. if !ok {
  52. var err error
  53. versionedOldObject, err := s.Converter.Convert(oldObject, managerSet.APIVersion())
  54. if err != nil {
  55. if s.Converter.IsMissingVersionError(err) {
  56. delete(managers, manager)
  57. continue
  58. }
  59. return nil, nil, fmt.Errorf("failed to convert old object: %v", err)
  60. }
  61. versionedNewObject, err := s.Converter.Convert(newObject, managerSet.APIVersion())
  62. if err != nil {
  63. if s.Converter.IsMissingVersionError(err) {
  64. delete(managers, manager)
  65. continue
  66. }
  67. return nil, nil, fmt.Errorf("failed to convert new object: %v", err)
  68. }
  69. compare, err = versionedOldObject.Compare(versionedNewObject)
  70. if err != nil {
  71. return nil, nil, fmt.Errorf("failed to compare objects: %v", err)
  72. }
  73. versions[managerSet.APIVersion()] = compare
  74. }
  75. conflictSet := managerSet.Set().Intersection(compare.Modified.Union(compare.Added))
  76. if !conflictSet.Empty() {
  77. conflicts[manager] = fieldpath.NewVersionedSet(conflictSet, managerSet.APIVersion(), false)
  78. }
  79. if !compare.Removed.Empty() {
  80. removed[manager] = fieldpath.NewVersionedSet(compare.Removed, managerSet.APIVersion(), false)
  81. }
  82. }
  83. if !force && len(conflicts) != 0 {
  84. return nil, nil, ConflictsFromManagers(conflicts)
  85. }
  86. for manager, conflictSet := range conflicts {
  87. managers[manager] = fieldpath.NewVersionedSet(managers[manager].Set().Difference(conflictSet.Set()), managers[manager].APIVersion(), managers[manager].Applied())
  88. }
  89. for manager, removedSet := range removed {
  90. managers[manager] = fieldpath.NewVersionedSet(managers[manager].Set().Difference(removedSet.Set()), managers[manager].APIVersion(), managers[manager].Applied())
  91. }
  92. for manager := range managers {
  93. if managers[manager].Set().Empty() {
  94. delete(managers, manager)
  95. }
  96. }
  97. return managers, compare, nil
  98. }
  99. // Update is the method you should call once you've merged your final
  100. // object on CREATE/UPDATE/PATCH verbs. newObject must be the object
  101. // that you intend to persist (after applying the patch if this is for a
  102. // PATCH call), and liveObject must be the original object (empty if
  103. // this is a CREATE call).
  104. func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string) (*typed.TypedValue, fieldpath.ManagedFields, error) {
  105. var err error
  106. if s.enableUnions {
  107. newObject, err = liveObject.NormalizeUnions(newObject)
  108. if err != nil {
  109. return nil, fieldpath.ManagedFields{}, err
  110. }
  111. }
  112. managers = shallowCopyManagers(managers)
  113. managers, compare, err := s.update(liveObject, newObject, version, managers, manager, true)
  114. if err != nil {
  115. return nil, fieldpath.ManagedFields{}, err
  116. }
  117. if _, ok := managers[manager]; !ok {
  118. managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false)
  119. }
  120. managers[manager] = fieldpath.NewVersionedSet(
  121. managers[manager].Set().Union(compare.Modified).Union(compare.Added).Difference(compare.Removed),
  122. version,
  123. false,
  124. )
  125. if managers[manager].Set().Empty() {
  126. delete(managers, manager)
  127. }
  128. return newObject, managers, nil
  129. }
  130. // Apply should be called when Apply is run, given the current object as
  131. // well as the configuration that is applied. This will merge the object
  132. // and return it.
  133. func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string, force bool) (*typed.TypedValue, fieldpath.ManagedFields, error) {
  134. managers = shallowCopyManagers(managers)
  135. var err error
  136. if s.enableUnions {
  137. configObject, err = configObject.NormalizeUnionsApply(configObject)
  138. if err != nil {
  139. return nil, fieldpath.ManagedFields{}, err
  140. }
  141. }
  142. newObject, err := liveObject.Merge(configObject)
  143. if err != nil {
  144. return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to merge config: %v", err)
  145. }
  146. if s.enableUnions {
  147. newObject, err = configObject.NormalizeUnionsApply(newObject)
  148. if err != nil {
  149. return nil, fieldpath.ManagedFields{}, err
  150. }
  151. }
  152. lastSet := managers[manager]
  153. set, err := configObject.ToFieldSet()
  154. if err != nil {
  155. return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err)
  156. }
  157. managers[manager] = fieldpath.NewVersionedSet(set, version, true)
  158. newObject, err = s.prune(newObject, managers, manager, lastSet)
  159. if err != nil {
  160. return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to prune fields: %v", err)
  161. }
  162. managers, _, err = s.update(liveObject, newObject, version, managers, manager, force)
  163. if err != nil {
  164. return nil, fieldpath.ManagedFields{}, err
  165. }
  166. return newObject, managers, nil
  167. }
  168. func shallowCopyManagers(managers fieldpath.ManagedFields) fieldpath.ManagedFields {
  169. newManagers := fieldpath.ManagedFields{}
  170. for manager, set := range managers {
  171. newManagers[manager] = set
  172. }
  173. return newManagers
  174. }
  175. // prune will remove a list or map item, iff:
  176. // * applyingManager applied it last time
  177. // * applyingManager didn't apply it this time
  178. // * no other applier claims to manage it
  179. func (s *Updater) prune(merged *typed.TypedValue, managers fieldpath.ManagedFields, applyingManager string, lastSet fieldpath.VersionedSet) (*typed.TypedValue, error) {
  180. if lastSet == nil || lastSet.Set().Empty() {
  181. return merged, nil
  182. }
  183. convertedMerged, err := s.Converter.Convert(merged, lastSet.APIVersion())
  184. if err != nil {
  185. if s.Converter.IsMissingVersionError(err) {
  186. return merged, nil
  187. }
  188. return nil, fmt.Errorf("failed to convert merged object to last applied version: %v", err)
  189. }
  190. pruned := convertedMerged.RemoveItems(lastSet.Set())
  191. pruned, err = s.addBackOwnedItems(convertedMerged, pruned, managers, applyingManager)
  192. if err != nil {
  193. return nil, fmt.Errorf("failed add back owned items: %v", err)
  194. }
  195. pruned, err = s.addBackDanglingItems(convertedMerged, pruned, lastSet)
  196. if err != nil {
  197. return nil, fmt.Errorf("failed add back dangling items: %v", err)
  198. }
  199. return s.Converter.Convert(pruned, managers[applyingManager].APIVersion())
  200. }
  201. // addBackOwnedItems adds back any list and map items that were removed by prune,
  202. // but other appliers (or the current applier's new config) claim to own.
  203. func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, managedFields fieldpath.ManagedFields, applyingManager string) (*typed.TypedValue, error) {
  204. var err error
  205. managedAtVersion := map[fieldpath.APIVersion]*fieldpath.Set{}
  206. for _, managerSet := range managedFields {
  207. if managerSet.Applied() {
  208. if _, ok := managedAtVersion[managerSet.APIVersion()]; !ok {
  209. managedAtVersion[managerSet.APIVersion()] = fieldpath.NewSet()
  210. }
  211. managedAtVersion[managerSet.APIVersion()] = managedAtVersion[managerSet.APIVersion()].Union(managerSet.Set())
  212. }
  213. }
  214. for version, managed := range managedAtVersion {
  215. merged, err = s.Converter.Convert(merged, version)
  216. if err != nil {
  217. if s.Converter.IsMissingVersionError(err) {
  218. continue
  219. }
  220. return nil, fmt.Errorf("failed to convert merged object at version %v: %v", version, err)
  221. }
  222. pruned, err = s.Converter.Convert(pruned, version)
  223. if err != nil {
  224. if s.Converter.IsMissingVersionError(err) {
  225. continue
  226. }
  227. return nil, fmt.Errorf("failed to convert pruned object at version %v: %v", version, err)
  228. }
  229. mergedSet, err := merged.ToFieldSet()
  230. if err != nil {
  231. return nil, fmt.Errorf("failed to create field set from merged object at version %v: %v", version, err)
  232. }
  233. prunedSet, err := pruned.ToFieldSet()
  234. if err != nil {
  235. return nil, fmt.Errorf("failed to create field set from pruned object at version %v: %v", version, err)
  236. }
  237. pruned = merged.RemoveItems(mergedSet.Difference(prunedSet.Union(managed)))
  238. }
  239. return pruned, nil
  240. }
  241. // addBackDanglingItems makes sure that the only items removed by prune are items that were
  242. // previously owned by the currently applying manager. This will add back unowned items and items
  243. // which are owned by Updaters that shouldn't be removed.
  244. func (s *Updater) addBackDanglingItems(merged, pruned *typed.TypedValue, lastSet fieldpath.VersionedSet) (*typed.TypedValue, error) {
  245. convertedPruned, err := s.Converter.Convert(pruned, lastSet.APIVersion())
  246. if err != nil {
  247. if s.Converter.IsMissingVersionError(err) {
  248. return merged, nil
  249. }
  250. return nil, fmt.Errorf("failed to convert pruned object to last applied version: %v", err)
  251. }
  252. prunedSet, err := convertedPruned.ToFieldSet()
  253. if err != nil {
  254. return nil, fmt.Errorf("failed to create field set from pruned object in last applied version: %v", err)
  255. }
  256. mergedSet, err := merged.ToFieldSet()
  257. if err != nil {
  258. return nil, fmt.Errorf("failed to create field set from merged object in last applied version: %v", err)
  259. }
  260. return merged.RemoveItems(mergedSet.Difference(prunedSet).Intersection(lastSet.Set())), nil
  261. }