1
2
3
4
5
6 package kio
7
8 import (
9 "fmt"
10 "strconv"
11
12 "sigs.k8s.io/kustomize/kyaml/errors"
13 "sigs.k8s.io/kustomize/kyaml/kio/kioutil"
14 "sigs.k8s.io/kustomize/kyaml/yaml"
15 )
16
17
18 type Reader interface {
19 Read() ([]*yaml.RNode, error)
20 }
21
22
23
24
25 type ResourceNodeSlice []*yaml.RNode
26
27 var _ Reader = ResourceNodeSlice{}
28
29 func (o ResourceNodeSlice) Read() ([]*yaml.RNode, error) {
30 return o, nil
31 }
32
33
34 type Writer interface {
35 Write([]*yaml.RNode) error
36 }
37
38
39 type WriterFunc func([]*yaml.RNode) error
40
41 func (fn WriterFunc) Write(o []*yaml.RNode) error {
42 return fn(o)
43 }
44
45
46 type ReaderWriter interface {
47 Reader
48 Writer
49 }
50
51
52
53
54
55
56 type Filter interface {
57 Filter([]*yaml.RNode) ([]*yaml.RNode, error)
58 }
59
60
61
62 type TrackableFilter interface {
63 Filter
64 WithMutationTracker(func(key, value, tag string, node *yaml.RNode))
65 }
66
67
68 type FilterFunc func([]*yaml.RNode) ([]*yaml.RNode, error)
69
70 func (fn FilterFunc) Filter(o []*yaml.RNode) ([]*yaml.RNode, error) {
71 return fn(o)
72 }
73
74
75
76
77
78 type Pipeline struct {
79
80 Inputs []Reader `yaml:"inputs,omitempty"`
81
82
83
84
85 Filters []Filter `yaml:"filters,omitempty"`
86
87
88 Outputs []Writer `yaml:"outputs,omitempty"`
89
90
91
92
93
94
95
96
97
98 ContinueOnEmptyResult bool `yaml:"continueOnEmptyResult,omitempty"`
99 }
100
101
102
103 func (p Pipeline) Execute() error {
104 return p.ExecuteWithCallback(nil)
105 }
106
107
108 type PipelineExecuteCallbackFunc = func(op Filter)
109
110
111
112 func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) error {
113 var result []*yaml.RNode
114
115
116 for _, i := range p.Inputs {
117 nodes, err := i.Read()
118 if err != nil {
119 return errors.Wrap(err)
120 }
121 result = append(result, nodes...)
122 }
123
124
125 for i := range p.Filters {
126
127
128 nodeAnnos, err := PreprocessResourcesForInternalAnnotationMigration(result)
129 if err != nil {
130 return err
131 }
132
133 op := p.Filters[i]
134 if callback != nil {
135 callback(op)
136 }
137 result, err = op.Filter(result)
138
139
140
141 if len(result) == 0 && !p.ContinueOnEmptyResult || err != nil {
142 return errors.Wrap(err)
143 }
144
145
146
147 err = ReconcileInternalAnnotations(result, nodeAnnos)
148 if err != nil {
149 return err
150 }
151 }
152
153
154 for _, o := range p.Outputs {
155 if err := o.Write(result); err != nil {
156 return errors.Wrap(err)
157 }
158 }
159 return nil
160 }
161
162
163 func FilterAll(filter yaml.Filter) Filter {
164 return FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
165 for i := range nodes {
166 _, err := filter.Filter(nodes[i])
167 if err != nil {
168 return nil, errors.Wrap(err)
169 }
170 }
171 return nodes, nil
172 })
173 }
174
175
176
177
178
179
180 func PreprocessResourcesForInternalAnnotationMigration(result []*yaml.RNode) (map[string]map[string]string, error) {
181 idToAnnosMap := make(map[string]map[string]string)
182 for i := range result {
183 idStr := strconv.Itoa(i)
184 err := result[i].PipeE(yaml.SetAnnotation(kioutil.InternalAnnotationsMigrationResourceIDAnnotation, idStr))
185 if err != nil {
186 return nil, err
187 }
188 idToAnnosMap[idStr] = kioutil.GetInternalAnnotations(result[i])
189 if err = kioutil.CopyLegacyAnnotations(result[i]); err != nil {
190 return nil, err
191 }
192 meta, _ := result[i].GetMeta()
193 if err = checkMismatchedAnnos(meta.Annotations); err != nil {
194 return nil, err
195 }
196 }
197 return idToAnnosMap, nil
198 }
199
200 func checkMismatchedAnnos(annotations map[string]string) error {
201 path := annotations[kioutil.PathAnnotation]
202 index := annotations[kioutil.IndexAnnotation]
203 id := annotations[kioutil.IdAnnotation]
204
205 legacyPath := annotations[kioutil.LegacyPathAnnotation]
206 legacyIndex := annotations[kioutil.LegacyIndexAnnotation]
207 legacyId := annotations[kioutil.LegacyIdAnnotation]
208
209
210
211 if path != "" && legacyPath != "" && path != legacyPath {
212 return fmt.Errorf("resource input to function has mismatched legacy and internal path annotations")
213 }
214 if index != "" && legacyIndex != "" && index != legacyIndex {
215 return fmt.Errorf("resource input to function has mismatched legacy and internal index annotations")
216 }
217 if id != "" && legacyId != "" && id != legacyId {
218 return fmt.Errorf("resource input to function has mismatched legacy and internal id annotations")
219 }
220 return nil
221 }
222
223 type nodeAnnotations struct {
224 path string
225 index string
226 id string
227 }
228
229
230
231
232 func ReconcileInternalAnnotations(result []*yaml.RNode, nodeAnnosMap map[string]map[string]string) error {
233 useInternal, useLegacy, err := determineAnnotationsFormat(nodeAnnosMap)
234 if err != nil {
235 return err
236 }
237
238 for i := range result {
239
240 err = missingInternalOrLegacyAnnotations(result[i])
241 if err != nil {
242 return err
243 }
244
245
246
247 err = checkAnnotationsAltered(result[i], nodeAnnosMap)
248 if err != nil {
249 return err
250 }
251
252
253
254 err = formatInternalAnnotations(result[i], useInternal, useLegacy)
255 if err != nil {
256 return err
257 }
258
259 meta, _ := result[i].GetMeta()
260 err = checkMismatchedAnnos(meta.Annotations)
261 if err != nil {
262 return err
263 }
264
265 if _, err = result[i].Pipe(yaml.ClearAnnotation(kioutil.InternalAnnotationsMigrationResourceIDAnnotation)); err != nil {
266 return err
267 }
268 }
269 return nil
270 }
271
272
273 func determineAnnotationsFormat(nodeAnnosMap map[string]map[string]string) (bool, bool, error) {
274 var useInternal, useLegacy bool
275 var err error
276
277 if len(nodeAnnosMap) == 0 {
278 return true, true, nil
279 }
280
281 var internal, legacy *bool
282 for _, annos := range nodeAnnosMap {
283 _, foundPath := annos[kioutil.PathAnnotation]
284 _, foundIndex := annos[kioutil.IndexAnnotation]
285 _, foundId := annos[kioutil.IdAnnotation]
286 _, foundLegacyPath := annos[kioutil.LegacyPathAnnotation]
287 _, foundLegacyIndex := annos[kioutil.LegacyIndexAnnotation]
288 _, foundLegacyId := annos[kioutil.LegacyIdAnnotation]
289
290 if !(foundPath || foundIndex || foundId || foundLegacyPath || foundLegacyIndex || foundLegacyId) {
291 continue
292 }
293
294 foundOneOf := foundPath || foundIndex || foundId
295 if internal == nil {
296 f := foundOneOf
297 internal = &f
298 }
299 if (foundOneOf && !*internal) || (!foundOneOf && *internal) {
300 err = fmt.Errorf("the annotation formatting in the input resources is not consistent")
301 return useInternal, useLegacy, err
302 }
303
304 foundOneOf = foundLegacyPath || foundLegacyIndex || foundLegacyId
305 if legacy == nil {
306 f := foundOneOf
307 legacy = &f
308 }
309 if (foundOneOf && !*legacy) || (!foundOneOf && *legacy) {
310 err = fmt.Errorf("the annotation formatting in the input resources is not consistent")
311 return useInternal, useLegacy, err
312 }
313 }
314 if internal != nil {
315 useInternal = *internal
316 }
317 if legacy != nil {
318 useLegacy = *legacy
319 }
320 return useInternal, useLegacy, err
321 }
322
323 func missingInternalOrLegacyAnnotations(rn *yaml.RNode) error {
324 if err := missingInternalOrLegacyAnnotation(rn, kioutil.PathAnnotation, kioutil.LegacyPathAnnotation); err != nil {
325 return err
326 }
327 if err := missingInternalOrLegacyAnnotation(rn, kioutil.IndexAnnotation, kioutil.LegacyIndexAnnotation); err != nil {
328 return err
329 }
330 if err := missingInternalOrLegacyAnnotation(rn, kioutil.IdAnnotation, kioutil.LegacyIdAnnotation); err != nil {
331 return err
332 }
333 return nil
334 }
335
336 func missingInternalOrLegacyAnnotation(rn *yaml.RNode, newKey string, legacyKey string) error {
337 meta, _ := rn.GetMeta()
338 annotations := meta.Annotations
339 value := annotations[newKey]
340 legacyValue := annotations[legacyKey]
341
342 if value == "" && legacyValue == "" {
343
344 return nil
345 }
346
347 if value == "" {
348
349 if err := rn.PipeE(yaml.SetAnnotation(newKey, legacyValue)); err != nil {
350 return err
351 }
352 } else if legacyValue == "" {
353
354 if err := rn.PipeE(yaml.SetAnnotation(legacyKey, value)); err != nil {
355 return err
356 }
357 }
358 return nil
359 }
360
361 func checkAnnotationsAltered(rn *yaml.RNode, nodeAnnosMap map[string]map[string]string) error {
362 meta, _ := rn.GetMeta()
363 annotations := meta.Annotations
364
365 internal := nodeAnnotations{
366 path: annotations[kioutil.PathAnnotation],
367 index: annotations[kioutil.IndexAnnotation],
368 id: annotations[kioutil.IdAnnotation],
369 }
370
371
372 legacy := nodeAnnotations{
373 path: annotations[kioutil.LegacyPathAnnotation],
374 index: annotations[kioutil.LegacyIndexAnnotation],
375 id: annotations[kioutil.LegacyIdAnnotation],
376 }
377
378 rid := annotations[kioutil.InternalAnnotationsMigrationResourceIDAnnotation]
379 originalAnnotations, found := nodeAnnosMap[rid]
380 if !found {
381 return nil
382 }
383 originalPath, found := originalAnnotations[kioutil.PathAnnotation]
384 if !found {
385 originalPath = originalAnnotations[kioutil.LegacyPathAnnotation]
386 }
387 if originalPath != "" {
388 switch {
389 case originalPath != internal.path && originalPath != legacy.path && internal.path != legacy.path:
390 return fmt.Errorf("resource input to function has mismatched legacy and internal path annotations")
391 case originalPath != internal.path:
392 if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, internal.path)); err != nil {
393 return err
394 }
395 case originalPath != legacy.path:
396 if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.PathAnnotation, legacy.path)); err != nil {
397 return err
398 }
399 }
400 }
401
402 originalIndex, found := originalAnnotations[kioutil.IndexAnnotation]
403 if !found {
404 originalIndex = originalAnnotations[kioutil.LegacyIndexAnnotation]
405 }
406 if originalIndex != "" {
407 switch {
408 case originalIndex != internal.index && originalIndex != legacy.index && internal.index != legacy.index:
409 return fmt.Errorf("resource input to function has mismatched legacy and internal index annotations")
410 case originalIndex != internal.index:
411 if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.LegacyIndexAnnotation, internal.index)); err != nil {
412 return err
413 }
414 case originalIndex != legacy.index:
415 if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.IndexAnnotation, legacy.index)); err != nil {
416 return err
417 }
418 }
419 }
420 return nil
421 }
422
423 func formatInternalAnnotations(rn *yaml.RNode, useInternal, useLegacy bool) error {
424 if !useInternal {
425 if err := rn.PipeE(yaml.ClearAnnotation(kioutil.IdAnnotation)); err != nil {
426 return err
427 }
428 if err := rn.PipeE(yaml.ClearAnnotation(kioutil.PathAnnotation)); err != nil {
429 return err
430 }
431 if err := rn.PipeE(yaml.ClearAnnotation(kioutil.IndexAnnotation)); err != nil {
432 return err
433 }
434 }
435 if !useLegacy {
436 if err := rn.PipeE(yaml.ClearAnnotation(kioutil.LegacyIdAnnotation)); err != nil {
437 return err
438 }
439 if err := rn.PipeE(yaml.ClearAnnotation(kioutil.LegacyPathAnnotation)); err != nil {
440 return err
441 }
442 if err := rn.PipeE(yaml.ClearAnnotation(kioutil.LegacyIndexAnnotation)); err != nil {
443 return err
444 }
445 }
446 return nil
447 }
448
View as plain text