1
2
3
4 package walk
5
6 import (
7 "strings"
8
9 "github.com/go-errors/errors"
10 "sigs.k8s.io/kustomize/kyaml/openapi"
11 "sigs.k8s.io/kustomize/kyaml/sets"
12 "sigs.k8s.io/kustomize/kyaml/yaml"
13 )
14
15
16
17
18
19 func appendListNode(dst, src *yaml.RNode, keys []string) (*yaml.RNode, error) {
20 var err error
21 for _, elem := range src.Content() {
22
23
24 if keys[0] == "" {
25 _, err = dst.Pipe(yaml.ElementSetter{
26 Element: elem,
27 Keys: []string{""},
28 Values: []string{elem.Value},
29 })
30 if err != nil {
31 return nil, err
32 }
33 continue
34 }
35
36
37
38 v := []string{}
39 for _, key := range keys {
40 tmpNode := yaml.NewRNode(elem)
41 valueNode, err := tmpNode.Pipe(yaml.Get(key))
42 if err != nil {
43 return nil, err
44 }
45 if valueNode.IsNil() {
46
47 err = dst.PipeE(yaml.Append(elem))
48 if err != nil {
49 return nil, err
50 }
51 continue
52 }
53 v = append(v, valueNode.YNode().Value)
54 }
55
56
57
58
59 if len(keys) > 1 {
60 _, err = dst.Pipe(yaml.ElementSetter{
61 Keys: keys,
62 Values: v,
63 })
64 if err != nil {
65 return nil, err
66 }
67 }
68
69
70
71
72 _, err = dst.Pipe(yaml.ElementSetter{
73 Element: elem,
74 Keys: keys,
75 Values: v,
76 })
77 if err != nil {
78 return nil, err
79 }
80 }
81 return dst, nil
82 }
83
84
85
86 func validateKeys(valuesList [][]string, values []string, keys []string) ([]string, []string) {
87 validKeys := make([]string, 0)
88 validValues := make([]string, 0)
89 validKeySet := sets.String{}
90 for _, values := range valuesList {
91 for i, v := range values {
92 if v != "" {
93 validKeySet.Insert(keys[i])
94 }
95 }
96 }
97 if validKeySet.Len() == 0 {
98 return keys, values
99 }
100 for _, k := range keys {
101 if validKeySet.Has(k) {
102 validKeys = append(validKeys, k)
103 }
104 }
105 for i, v := range values {
106 if v != "" || validKeySet.Has(keys[i]) {
107 validValues = append(validValues, v)
108 }
109 }
110 return validKeys, validValues
111 }
112
113
114
115
116 func mergeValues(valuesList [][]string) [][]string {
117 for i, values1 := range valuesList {
118 for j, values2 := range valuesList {
119 if matched, values := match(values1, values2); matched {
120 valuesList[i] = values
121 valuesList[j] = values
122 }
123 }
124 }
125 return valuesList
126 }
127
128
129
130 func match(values1 []string, values2 []string) (bool, []string) {
131 if len(values1) != len(values2) {
132 return false, nil
133 }
134 var commonElement bool
135 var res []string
136 for i := range values1 {
137 if values1[i] == values2[i] {
138 commonElement = true
139 res = append(res, values1[i])
140 continue
141 }
142 if values1[i] != "" && values2[i] != "" {
143 return false, nil
144 }
145 if values1[i] != "" {
146 res = append(res, values1[i])
147 } else {
148 res = append(res, values2[i])
149 }
150 }
151 return commonElement, res
152 }
153
154
155 func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []string, dest *yaml.RNode) (*yaml.RNode, error) {
156
157 itemsToBeAdded := yaml.NewListRNode()
158 var schema *openapi.ResourceSchema
159 if l.Schema != nil {
160 schema = l.Schema.Elements()
161 }
162 if len(keys) > 1 {
163 valuesList = mergeValues(valuesList)
164 }
165
166
167
168
169
170
171
172
173
174 var validKeys []string
175 var validValues []string
176 for _, values := range valuesList {
177 if len(values) == 0 {
178 continue
179 }
180
181 validKeys, validValues = validateKeys(valuesList, values, keys)
182 val, err := Walker{
183 VisitKeysAsScalars: l.VisitKeysAsScalars,
184 InferAssociativeLists: l.InferAssociativeLists,
185 Visitor: l,
186 Schema: schema,
187 Sources: l.elementValueList(validKeys, validValues),
188 MergeOptions: l.MergeOptions,
189 }.Walk()
190 if err != nil {
191 return nil, err
192 }
193
194 exit := false
195 for i, key := range validKeys {
196
197 if yaml.IsMissingOrNull(val) || yaml.IsEmptyMap(val) {
198 _, err = dest.Pipe(yaml.ElementSetter{
199 Keys: validKeys,
200 Values: validValues,
201 })
202 if err != nil {
203 return nil, err
204 }
205 exit = true
206 } else if val.Field(key) == nil && validValues[i] != "" {
207
208 _, err = val.Pipe(yaml.SetField(key, yaml.NewScalarRNode(validValues[i])))
209 if err != nil {
210 return nil, err
211 }
212 }
213 }
214 if exit {
215 continue
216 }
217
218
219
220
221 _, err = itemsToBeAdded.Pipe(yaml.ElementSetter{
222 Element: val.YNode(),
223 Keys: validKeys,
224 Values: validValues,
225 })
226 if err != nil {
227 return nil, err
228 }
229 }
230
231 var err error
232 if len(valuesList) > 0 {
233 if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
234
235
236 dest, err = appendListNode(itemsToBeAdded, dest, validKeys)
237 } else {
238
239 dest, err = appendListNode(dest, itemsToBeAdded, validKeys)
240 }
241 }
242
243 if err != nil {
244 return nil, err
245 }
246
247 if yaml.IsMissingOrNull(dest) {
248 return nil, nil
249 }
250 return dest, nil
251 }
252
253 func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
254
255 dest, err := l.Sources.setDestNode(l.VisitList(l.Sources, l.Schema, AssociativeList))
256 if dest == nil || err != nil {
257 return nil, err
258 }
259
260
261 var strategy string
262 var keys []string
263 if l.Schema != nil {
264 strategy, keys = l.Schema.PatchStrategyAndKeyList()
265 }
266 if strategy == "" && len(keys) == 0 {
267
268 key, err := l.elementKey()
269 if err != nil {
270 return nil, err
271 }
272 if key != "" {
273 keys = append(keys, key)
274 }
275 }
276
277
278 values := l.elementValues(keys)
279 if len(values) != 0 || len(keys) > 0 {
280 return l.setAssociativeSequenceElements(values, keys, dest)
281 }
282
283
284 return l.setAssociativeSequenceElements(l.elementPrimitiveValues(), []string{""}, dest)
285 }
286
287
288 func (l Walker) elementKey() (string, error) {
289 var key string
290 for i := range l.Sources {
291 if l.Sources[i] != nil && len(l.Sources[i].Content()) > 0 {
292 newKey := l.Sources[i].GetAssociativeKey()
293 if key != "" && key != newKey {
294 return "", errors.Errorf(
295 "conflicting merge keys [%s,%s] for field %s",
296 key, newKey, strings.Join(l.Path, "."))
297 }
298 key = newKey
299 }
300 }
301 if key == "" {
302 return "", errors.Errorf("no merge key found for field %s",
303 strings.Join(l.Path, "."))
304 }
305 return key, nil
306 }
307
308
309
310
311
312 func (l Walker) elementValues(keys []string) [][]string {
313
314 var returnValues [][]string
315 var seen sets.StringList
316
317
318
319 beginIdx := 0
320 if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
321 beginIdx = 1
322 }
323 for i := range l.Sources {
324 src := l.Sources[(i+beginIdx)%len(l.Sources)]
325 if src == nil {
326 continue
327 }
328
329
330
331 values, _ := src.ElementValuesList(keys)
332 for _, s := range values {
333 if len(s) == 0 || seen.Has(s) {
334 continue
335 }
336 returnValues = append(returnValues, s)
337 seen = seen.Insert(s)
338 }
339 }
340 return returnValues
341 }
342
343
344 func (l Walker) elementPrimitiveValues() [][]string {
345
346 var returnValues [][]string
347 seen := sets.String{}
348
349
350 beginIdx := 0
351 if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
352 beginIdx = 1
353 }
354 for i := range l.Sources {
355 src := l.Sources[(i+beginIdx)%len(l.Sources)]
356 if src == nil {
357 continue
358 }
359
360
361
362 for _, item := range src.YNode().Content {
363 if seen.Has(item.Value) {
364 continue
365 }
366 returnValues = append(returnValues, []string{item.Value})
367 seen.Insert(item.Value)
368 }
369 }
370 return returnValues
371 }
372
373
374 func (l Walker) elementValueList(keys []string, values []string) []*yaml.RNode {
375 keys, values = validateKeys([][]string{values}, values, keys)
376 var fields []*yaml.RNode
377 for i := range l.Sources {
378 if l.Sources[i] == nil {
379 fields = append(fields, nil)
380 continue
381 }
382 fields = append(fields, l.Sources[i].ElementList(keys, values))
383 }
384 return fields
385 }
386
View as plain text