list_element.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. "k8s.io/kube-openapi/pkg/util/proto"
  17. "k8s.io/kubernetes/pkg/kubectl/apply"
  18. )
  19. // Contains the heavy lifting for finding tuples of matching elements in lists based on the merge key
  20. // and then uses the canonical order derived from the orders in the recorded, local and remote lists.
  21. // replaceListElement builds a ListElement for a listItem.
  22. // Uses the "merge" strategy to identify "same" elements across lists by a "merge key"
  23. func (v ElementBuildingVisitor) mergeListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
  24. subtype := getSchemaType(item.Array.SubType)
  25. switch subtype {
  26. case "primitive":
  27. return v.doPrimitiveList(meta, item)
  28. case "map", "kind", "reference":
  29. return v.doMapList(meta, item)
  30. default:
  31. return nil, fmt.Errorf("Cannot merge lists with subtype %s", subtype)
  32. }
  33. }
  34. // doPrimitiveList merges 3 lists of primitives together
  35. // tries to maintain ordering
  36. func (v ElementBuildingVisitor) doPrimitiveList(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
  37. result := &apply.ListElement{
  38. FieldMetaImpl: apply.FieldMetaImpl{
  39. MergeType: apply.MergeStrategy,
  40. Name: item.Name,
  41. },
  42. ListElementData: item.ListElementData,
  43. Values: []apply.Element{},
  44. }
  45. // Use locally defined order, then add remote, then add recorded.
  46. orderedKeys := &apply.CombinedPrimitiveSlice{}
  47. // Locally defined items come first and retain their order
  48. // as defined locally
  49. for _, l := range item.GetLocalList() {
  50. orderedKeys.UpsertLocal(l)
  51. }
  52. // Mixin remote values, adding any that are not present locally
  53. for _, l := range item.GetRemoteList() {
  54. orderedKeys.UpsertRemote(l)
  55. }
  56. // Mixin recorded values, adding any that are not present locally
  57. // or remotely
  58. for _, l := range item.GetRecordedList() {
  59. orderedKeys.UpsertRecorded(l)
  60. }
  61. for i, l := range orderedKeys.Items {
  62. var s proto.Schema
  63. if item.Array != nil && item.Array.SubType != nil {
  64. s = item.Array.SubType
  65. }
  66. subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
  67. if err != nil {
  68. return nil, err
  69. }
  70. // Convert the Item to an Element
  71. newelem, err := subitem.CreateElement(v)
  72. if err != nil {
  73. return nil, err
  74. }
  75. // Append the element to the list
  76. result.Values = append(result.Values, newelem)
  77. }
  78. return result, nil
  79. }
  80. // doMapList merges 3 lists of maps together by collating their values.
  81. // tries to retain ordering
  82. func (v ElementBuildingVisitor) doMapList(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
  83. key := meta.GetFieldMergeKeys()
  84. result := &apply.ListElement{
  85. FieldMetaImpl: apply.FieldMetaImpl{
  86. MergeType: apply.MergeStrategy,
  87. MergeKeys: key,
  88. Name: item.Name,
  89. },
  90. ListElementData: item.ListElementData,
  91. Values: []apply.Element{},
  92. }
  93. // Use locally defined order, then add remote, then add recorded.
  94. orderedKeys := &apply.CombinedMapSlice{}
  95. // Locally defined items come first and retain their order
  96. // as defined locally
  97. for _, l := range item.GetLocalList() {
  98. orderedKeys.UpsertLocal(key, l)
  99. }
  100. // Mixin remote values, adding any that are not present locally
  101. for _, l := range item.GetRemoteList() {
  102. orderedKeys.UpsertRemote(key, l)
  103. }
  104. // Mixin recorded values, adding any that are not present locally
  105. // or remotely
  106. for _, l := range item.GetRecordedList() {
  107. orderedKeys.UpsertRecorded(key, l)
  108. }
  109. for i, l := range orderedKeys.Items {
  110. var s proto.Schema
  111. if item.Array != nil && item.Array.SubType != nil {
  112. s = item.Array.SubType
  113. }
  114. subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
  115. if err != nil {
  116. return nil, err
  117. }
  118. // Build the element fully
  119. newelem, err := subitem.CreateElement(v)
  120. if err != nil {
  121. return nil, err
  122. }
  123. // Append the element to the list
  124. result.Values = append(result.Values, newelem)
  125. }
  126. return result, nil
  127. }
  128. // replaceListElement builds a new ListElement from a listItem
  129. // Uses the "replace" strategy and identify "same" elements across lists by their index
  130. func (v ElementBuildingVisitor) replaceListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
  131. meta.Name = item.Name
  132. result := &apply.ListElement{
  133. FieldMetaImpl: meta,
  134. ListElementData: item.ListElementData,
  135. Values: []apply.Element{},
  136. }
  137. // Use the max length to iterate over the slices
  138. for i := 0; i < max(len(item.GetRecordedList()), len(item.GetLocalList()), len(item.GetRemoteList())); i++ {
  139. // Lookup the item from each list
  140. data := apply.RawElementData{}
  141. if recorded, recordedSet := boundsSafeLookup(i, item.GetRecordedList()); recordedSet {
  142. data.SetRecorded(recorded)
  143. }
  144. if local, localSet := boundsSafeLookup(i, item.GetLocalList()); localSet {
  145. data.SetLocal(local)
  146. }
  147. if remote, remoteSet := boundsSafeLookup(i, item.GetRemoteList()); remoteSet {
  148. data.SetRemote(remote)
  149. }
  150. // Create the Item
  151. var s proto.Schema
  152. if item.Array != nil && item.Array.SubType != nil {
  153. s = item.Array.SubType
  154. }
  155. subitem, err := v.getItem(s, fmt.Sprintf("%d", i), data)
  156. if err != nil {
  157. return nil, err
  158. }
  159. // Build the element
  160. newelem, err := subitem.CreateElement(v)
  161. if err != nil {
  162. return nil, err
  163. }
  164. // Append the element to the list
  165. result.Values = append(result.Values, newelem)
  166. }
  167. return result, nil
  168. }