merge_visitor.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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 strategy
  14. import (
  15. "fmt"
  16. "k8s.io/kubernetes/pkg/kubectl/apply"
  17. )
  18. func createMergeStrategy(options Options, strategic *delegatingStrategy) mergeStrategy {
  19. return mergeStrategy{
  20. strategic,
  21. options,
  22. }
  23. }
  24. // mergeStrategy merges the values in an Element into a single Result
  25. type mergeStrategy struct {
  26. strategic *delegatingStrategy
  27. options Options
  28. }
  29. // MergeList merges the lists in a ListElement into a single Result
  30. func (v mergeStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
  31. // No merge logic if adding or deleting a field
  32. if result, done := v.doAddOrDelete(e); done {
  33. return result, nil
  34. }
  35. // Detect conflict in ListElement
  36. if err := v.doConflictDetect(e); err != nil {
  37. return apply.Result{}, err
  38. }
  39. // Merge each item in the list and append it to the list
  40. merged := []interface{}{}
  41. for _, value := range e.Values {
  42. // Recursively merge the list element before adding the value to the list
  43. m, err := value.Merge(v.strategic)
  44. if err != nil {
  45. return apply.Result{}, err
  46. }
  47. switch m.Operation {
  48. case apply.SET:
  49. // Keep the list item value
  50. merged = append(merged, m.MergedResult)
  51. case apply.DROP:
  52. // Drop the list item value
  53. default:
  54. panic(fmt.Errorf("Unexpected result operation type %+v", m))
  55. }
  56. }
  57. if len(merged) == 0 {
  58. // If the list is empty, return a nil entry
  59. return apply.Result{Operation: apply.SET, MergedResult: nil}, nil
  60. }
  61. // Return the merged list, and tell the caller to keep it
  62. return apply.Result{Operation: apply.SET, MergedResult: merged}, nil
  63. }
  64. // MergeMap merges the maps in a MapElement into a single Result
  65. func (v mergeStrategy) MergeMap(e apply.MapElement) (apply.Result, error) {
  66. // No merge logic if adding or deleting a field
  67. if result, done := v.doAddOrDelete(e); done {
  68. return result, nil
  69. }
  70. // Detect conflict in MapElement
  71. if err := v.doConflictDetect(e); err != nil {
  72. return apply.Result{}, err
  73. }
  74. return v.doMergeMap(e.GetValues())
  75. }
  76. // MergeMap merges the type instances in a TypeElement into a single Result
  77. func (v mergeStrategy) MergeType(e apply.TypeElement) (apply.Result, error) {
  78. // No merge logic if adding or deleting a field
  79. if result, done := v.doAddOrDelete(e); done {
  80. return result, nil
  81. }
  82. // Detect conflict in TypeElement
  83. if err := v.doConflictDetect(e); err != nil {
  84. return apply.Result{}, err
  85. }
  86. return v.doMergeMap(e.GetValues())
  87. }
  88. // do merges a recorded, local and remote map into a new object
  89. func (v mergeStrategy) doMergeMap(e map[string]apply.Element) (apply.Result, error) {
  90. // Merge each item in the list
  91. merged := map[string]interface{}{}
  92. for key, value := range e {
  93. // Recursively merge the map element before adding the value to the map
  94. result, err := value.Merge(v.strategic)
  95. if err != nil {
  96. return apply.Result{}, err
  97. }
  98. switch result.Operation {
  99. case apply.SET:
  100. // Keep the map item value
  101. merged[key] = result.MergedResult
  102. case apply.DROP:
  103. // Drop the map item value
  104. default:
  105. panic(fmt.Errorf("Unexpected result operation type %+v", result))
  106. }
  107. }
  108. // Return the merged map, and tell the caller to keep it
  109. if len(merged) == 0 {
  110. // Special case the empty map to set the field value to nil, but keep the field key
  111. // This is how the tests expect the structures to look when parsed from yaml
  112. return apply.Result{Operation: apply.SET, MergedResult: nil}, nil
  113. }
  114. return apply.Result{Operation: apply.SET, MergedResult: merged}, nil
  115. }
  116. func (v mergeStrategy) doAddOrDelete(e apply.Element) (apply.Result, bool) {
  117. if apply.IsAdd(e) {
  118. return apply.Result{Operation: apply.SET, MergedResult: e.GetLocal()}, true
  119. }
  120. // Delete the List
  121. if apply.IsDrop(e) {
  122. return apply.Result{Operation: apply.DROP}, true
  123. }
  124. return apply.Result{}, false
  125. }
  126. // MergePrimitive returns and error. Primitive elements can't be merged, only replaced.
  127. func (v mergeStrategy) MergePrimitive(diff apply.PrimitiveElement) (apply.Result, error) {
  128. return apply.Result{}, fmt.Errorf("Cannot merge primitive element %v", diff.Name)
  129. }
  130. // MergeEmpty returns an empty result
  131. func (v mergeStrategy) MergeEmpty(diff apply.EmptyElement) (apply.Result, error) {
  132. return apply.Result{Operation: apply.SET}, nil
  133. }
  134. // doConflictDetect returns error if element has conflict
  135. func (v mergeStrategy) doConflictDetect(e apply.Element) error {
  136. return v.strategic.doConflictDetect(e)
  137. }
  138. var _ apply.Strategy = &mergeStrategy{}