1
2
3
4 package kioutil
5
6 import (
7 "fmt"
8 "path"
9 "sort"
10 "strconv"
11 "strings"
12
13 "sigs.k8s.io/kustomize/kyaml/errors"
14 "sigs.k8s.io/kustomize/kyaml/yaml"
15 )
16
17 type AnnotationKey = string
18
19 const (
20
21
22 internalPrefix string = "internal.config.kubernetes.io/"
23
24
25 IndexAnnotation AnnotationKey = internalPrefix + "index"
26
27
28 PathAnnotation AnnotationKey = internalPrefix + "path"
29
30
31 SeqIndentAnnotation AnnotationKey = internalPrefix + "seqindent"
32
33
34 IdAnnotation AnnotationKey = internalPrefix + "id"
35
36
37 LegacyIndexAnnotation AnnotationKey = "config.kubernetes.io/index"
38
39
40 LegacyPathAnnotation AnnotationKey = "config.kubernetes.io/path"
41
42
43 LegacyIdAnnotation = "config.k8s.io/id"
44
45
46
47
48 InternalAnnotationsMigrationResourceIDAnnotation = internalPrefix + "annotations-migration-resource-id"
49 )
50
51 func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
52 rm, _ := rn.GetMeta()
53 annotations := rm.Annotations
54 path, found := annotations[PathAnnotation]
55 if !found {
56 path = annotations[LegacyPathAnnotation]
57 }
58 index, found := annotations[IndexAnnotation]
59 if !found {
60 index = annotations[LegacyIndexAnnotation]
61 }
62 return path, index, nil
63 }
64
65 func GetIdAnnotation(rn *yaml.RNode) string {
66 rm, _ := rn.GetMeta()
67 annotations := rm.Annotations
68 id, found := annotations[IdAnnotation]
69 if !found {
70 id = annotations[LegacyIdAnnotation]
71 }
72 return id
73 }
74
75 func CopyLegacyAnnotations(rn *yaml.RNode) error {
76 meta, err := rn.GetMeta()
77 if err != nil {
78 if err == yaml.ErrMissingMetadata {
79
80 return nil
81 }
82 return err
83 }
84 if err := copyAnnotations(meta, rn, LegacyPathAnnotation, PathAnnotation); err != nil {
85 return err
86 }
87 if err := copyAnnotations(meta, rn, LegacyIndexAnnotation, IndexAnnotation); err != nil {
88 return err
89 }
90 if err := copyAnnotations(meta, rn, LegacyIdAnnotation, IdAnnotation); err != nil {
91 return err
92 }
93 return nil
94 }
95
96 func copyAnnotations(meta yaml.ResourceMeta, rn *yaml.RNode, legacyKey string, newKey string) error {
97 newValue := meta.Annotations[newKey]
98 legacyValue := meta.Annotations[legacyKey]
99 if newValue != "" {
100 if legacyValue == "" {
101 if err := rn.PipeE(yaml.SetAnnotation(legacyKey, newValue)); err != nil {
102 return err
103 }
104 }
105 } else {
106 if legacyValue != "" {
107 if err := rn.PipeE(yaml.SetAnnotation(newKey, legacyValue)); err != nil {
108 return err
109 }
110 }
111 }
112 return nil
113 }
114
115
116 func ErrorIfMissingAnnotation(nodes []*yaml.RNode, keys ...AnnotationKey) error {
117 for _, key := range keys {
118 for _, node := range nodes {
119 val, err := node.Pipe(yaml.GetAnnotation(key))
120 if err != nil {
121 return errors.Wrap(err)
122 }
123 if val == nil {
124 return errors.Errorf("missing annotation %s", key)
125 }
126 }
127 }
128 return nil
129 }
130
131
132
133 func CreatePathAnnotationValue(dir string, m yaml.ResourceMeta) string {
134 filename := fmt.Sprintf("%s_%s.yaml", strings.ToLower(m.Kind), m.Name)
135 return path.Join(dir, m.Namespace, filename)
136 }
137
138
139
140 func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
141 counts := map[string]int{}
142
143
144 for i := range nodes {
145 if err := CopyLegacyAnnotations(nodes[i]); err != nil {
146 return err
147 }
148 m, err := nodes[i].GetMeta()
149 if err != nil {
150 return err
151 }
152
153
154 if p, found := m.Annotations[PathAnnotation]; found {
155
156 if i, found := m.Annotations[IndexAnnotation]; found {
157 index, _ := strconv.Atoi(i)
158 if index > counts[p] {
159 counts[p] = index
160 }
161 }
162
163
164 continue
165 }
166
167
168 path := CreatePathAnnotationValue(dir, m)
169 if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
170 return err
171 }
172 if err := nodes[i].PipeE(yaml.SetAnnotation(LegacyPathAnnotation, path)); err != nil {
173 return err
174 }
175 }
176
177
178 for i := range nodes {
179 m, err := nodes[i].GetMeta()
180 if err != nil {
181 return err
182 }
183
184 if _, found := m.Annotations[IndexAnnotation]; found {
185 continue
186 }
187
188 p := m.Annotations[PathAnnotation]
189
190
191 c := counts[p]
192 counts[p] = c + 1
193 if err := nodes[i].PipeE(
194 yaml.SetAnnotation(IndexAnnotation, fmt.Sprintf("%d", c))); err != nil {
195 return err
196 }
197 if err := nodes[i].PipeE(
198 yaml.SetAnnotation(LegacyIndexAnnotation, fmt.Sprintf("%d", c))); err != nil {
199 return err
200 }
201 }
202 return nil
203 }
204
205
206
207 func DefaultPathAnnotation(dir string, nodes []*yaml.RNode) error {
208
209 for i := range nodes {
210 if err := CopyLegacyAnnotations(nodes[i]); err != nil {
211 return err
212 }
213 m, err := nodes[i].GetMeta()
214 if err != nil {
215 return err
216 }
217
218 if _, found := m.Annotations[PathAnnotation]; found {
219
220 continue
221 }
222
223
224 path := CreatePathAnnotationValue(dir, m)
225 if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
226 return err
227 }
228 if err := nodes[i].PipeE(yaml.SetAnnotation(LegacyPathAnnotation, path)); err != nil {
229 return err
230 }
231 }
232 return nil
233 }
234
235
236 func Map(nodes []*yaml.RNode, fn func(*yaml.RNode) (*yaml.RNode, error)) ([]*yaml.RNode, error) {
237 var returnNodes []*yaml.RNode
238 for i := range nodes {
239 n, err := fn(nodes[i])
240 if err != nil {
241 return nil, errors.Wrap(err)
242 }
243 if n != nil {
244 returnNodes = append(returnNodes, n)
245 }
246 }
247 return returnNodes, nil
248 }
249
250 func MapMeta(nodes []*yaml.RNode, fn func(*yaml.RNode, yaml.ResourceMeta) (*yaml.RNode, error)) (
251 []*yaml.RNode, error) {
252 var returnNodes []*yaml.RNode
253 for i := range nodes {
254 meta, err := nodes[i].GetMeta()
255 if err != nil {
256 return nil, errors.Wrap(err)
257 }
258 n, err := fn(nodes[i], meta)
259 if err != nil {
260 return nil, errors.Wrap(err)
261 }
262 if n != nil {
263 returnNodes = append(returnNodes, n)
264 }
265 }
266 return returnNodes, nil
267 }
268
269
270
271
272 func SortNodes(nodes []*yaml.RNode) error {
273 var err error
274
275 sort.SliceStable(nodes, func(i, j int) bool {
276 if err != nil {
277 return false
278 }
279 if err := CopyLegacyAnnotations(nodes[i]); err != nil {
280 return false
281 }
282 if err := CopyLegacyAnnotations(nodes[j]); err != nil {
283 return false
284 }
285 var iMeta, jMeta yaml.ResourceMeta
286 if iMeta, _ = nodes[i].GetMeta(); err != nil {
287 return false
288 }
289 if jMeta, _ = nodes[j].GetMeta(); err != nil {
290 return false
291 }
292
293 iValue := iMeta.Annotations[PathAnnotation]
294 jValue := jMeta.Annotations[PathAnnotation]
295 if iValue != jValue {
296 return iValue < jValue
297 }
298
299 iValue = iMeta.Annotations[IndexAnnotation]
300 jValue = jMeta.Annotations[IndexAnnotation]
301
302
303 if iValue == jValue {
304 return false
305 }
306 if iValue == "" {
307 return true
308 }
309 if jValue == "" {
310 return false
311 }
312
313
314 var iIndex, jIndex int
315 iIndex, err = strconv.Atoi(iValue)
316 if err != nil {
317 err = fmt.Errorf("unable to parse config.kubernetes.io/index %s :%v", iValue, err)
318 return false
319 }
320 jIndex, err = strconv.Atoi(jValue)
321 if err != nil {
322 err = fmt.Errorf("unable to parse config.kubernetes.io/index %s :%v", jValue, err)
323 return false
324 }
325 if iIndex != jIndex {
326 return iIndex < jIndex
327 }
328
329
330 return false
331 })
332 return errors.Wrap(err)
333 }
334
335
336
337
338 func CopyInternalAnnotations(src *yaml.RNode, dst *yaml.RNode, exclusions ...AnnotationKey) error {
339 srcAnnotations := GetInternalAnnotations(src)
340 for k, v := range srcAnnotations {
341 if stringSliceContains(exclusions, k) {
342 continue
343 }
344 if err := dst.PipeE(yaml.SetAnnotation(k, v)); err != nil {
345 return err
346 }
347 }
348 return nil
349 }
350
351
352
353
354 func ConfirmInternalAnnotationUnchanged(r1 *yaml.RNode, r2 *yaml.RNode, exclusions ...AnnotationKey) error {
355 r1Annotations := GetInternalAnnotations(r1)
356 r2Annotations := GetInternalAnnotations(r2)
357
358
359 diffAnnos := make(map[string]bool)
360
361 for k, v1 := range r1Annotations {
362 if stringSliceContains(exclusions, k) {
363 continue
364 }
365 if v2, ok := r2Annotations[k]; !ok || v1 != v2 {
366 diffAnnos[k] = true
367 }
368 }
369
370 for k, v2 := range r2Annotations {
371 if stringSliceContains(exclusions, k) {
372 continue
373 }
374 if v1, ok := r1Annotations[k]; !ok || v2 != v1 {
375 diffAnnos[k] = true
376 }
377 }
378
379 if len(diffAnnos) > 0 {
380 keys := make([]string, 0, len(diffAnnos))
381 for k := range diffAnnos {
382 keys = append(keys, k)
383 }
384 sort.Strings(keys)
385
386 errorString := "internal annotations differ: "
387 for _, key := range keys {
388 errorString = errorString + key + ", "
389 }
390 return errors.Errorf(errorString[0 : len(errorString)-2])
391 }
392
393 return nil
394 }
395
396
397
398
399
400 func GetInternalAnnotations(rn *yaml.RNode) map[string]string {
401 meta, _ := rn.GetMeta()
402 annotations := meta.Annotations
403 result := make(map[string]string)
404 for k, v := range annotations {
405 if strings.HasPrefix(k, internalPrefix) || k == LegacyPathAnnotation || k == LegacyIndexAnnotation || k == LegacyIdAnnotation {
406 result[k] = v
407 }
408 }
409 return result
410 }
411
412
413 func stringSliceContains(slice []string, str string) bool {
414 for _, s := range slice {
415 if s == str {
416 return true
417 }
418 }
419 return false
420 }
421
View as plain text