@@ -28,6 +28,7 @@ import (
28
28
"github.com/GoogleContainerTools/kpt/internal/types"
29
29
"github.com/GoogleContainerTools/kpt/internal/util/git"
30
30
kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
31
+ "sigs.k8s.io/kustomize/kyaml/sets"
31
32
"sigs.k8s.io/kustomize/kyaml/yaml"
32
33
"sigs.k8s.io/kustomize/kyaml/yaml/merge3"
33
34
)
@@ -223,7 +224,15 @@ func UpdateUpstreamLockFromGit(path string, spec *git.RepoSpec) error {
223
224
return nil
224
225
}
225
226
227
+ // merge merges the Kptfiles from various sources and updates localKf with output
228
+ // please refer to https://github.com/GoogleContainerTools/kpt/blob/main/docs/design-docs/03-pipeline-merge.md
229
+ // for related design
226
230
func merge (localKf , updatedKf , originalKf * kptfilev1.KptFile ) error {
231
+ shouldAddSyntheticMergeName := shouldAddFnKey (localKf , updatedKf , originalKf )
232
+ if shouldAddSyntheticMergeName {
233
+ addNameForMerge (localKf , updatedKf , originalKf )
234
+ }
235
+
227
236
localBytes , err := yaml .Marshal (localKf )
228
237
if err != nil {
229
238
return err
@@ -239,7 +248,7 @@ func merge(localKf, updatedKf, originalKf *kptfilev1.KptFile) error {
239
248
return err
240
249
}
241
250
242
- mergedBytes , err := merge3 .MergeStrings (string (localBytes ), string (originalBytes ), string (updatedBytes ), false )
251
+ mergedBytes , err := merge3 .MergeStrings (string (localBytes ), string (originalBytes ), string (updatedBytes ), true )
243
252
if err != nil {
244
253
return err
245
254
}
@@ -250,6 +259,10 @@ func merge(localKf, updatedKf, originalKf *kptfilev1.KptFile) error {
250
259
return err
251
260
}
252
261
262
+ if shouldAddSyntheticMergeName {
263
+ removeFnKey (localKf , updatedKf , originalKf , & mergedKf )
264
+ }
265
+
253
266
// Copy the merged content into the local Kptfile struct. We don't copy
254
267
// name, namespace, Upstream or UpstreamLock, since we don't want those
255
268
// merged.
@@ -261,6 +274,100 @@ func merge(localKf, updatedKf, originalKf *kptfilev1.KptFile) error {
261
274
return nil
262
275
}
263
276
277
+ // shouldAddFnKey returns true iff all the functions from all sources
278
+ // doesn't have name field set and there are no duplicate function declarations,
279
+ // it means the user is unaware of name field, and we use image name or exec field
280
+ // value as mergeKey instead of name in such cases
281
+ func shouldAddFnKey (kfs ... * kptfilev1.KptFile ) bool {
282
+ for _ , kf := range kfs {
283
+ if kf == nil || kf .Pipeline == nil {
284
+ continue
285
+ }
286
+ if ! shouldAddFnKeyUtil (kf .Pipeline .Mutators ) || ! shouldAddFnKeyUtil (kf .Pipeline .Validators ) {
287
+ return false
288
+ }
289
+ }
290
+ return true
291
+ }
292
+
293
+ // shouldAddFnKeyUtil returns true iff all the functions from input list
294
+ // doesn't have name field set and there are no duplicate function declarations,
295
+ // it means the user is unaware of name field, and we use image name or exec field
296
+ // value as mergeKey instead of name in such cases
297
+ func shouldAddFnKeyUtil (fns []kptfilev1.Function ) bool {
298
+ keySet := sets.String {}
299
+ for _ , fn := range fns {
300
+ if fn .Name != "" {
301
+ return false
302
+ }
303
+ var key string
304
+ if fn .Exec != "" {
305
+ key = fn .Exec
306
+ } else {
307
+ key = strings .Split (fn .Image , ":" )[0 ]
308
+ }
309
+ if keySet .Has (key ) {
310
+ return false
311
+ }
312
+ keySet .Insert (key )
313
+ }
314
+ return true
315
+ }
316
+
317
+ // addNameForMerge adds name field for all the functions if empty
318
+ // name is primarily used as merge-key
319
+ func addNameForMerge (kfs ... * kptfilev1.KptFile ) {
320
+ for _ , kf := range kfs {
321
+ if kf == nil || kf .Pipeline == nil {
322
+ continue
323
+ }
324
+ for i , mutator := range kf .Pipeline .Mutators {
325
+ kf .Pipeline .Mutators [i ] = addName (mutator )
326
+ }
327
+ for i , validator := range kf .Pipeline .Validators {
328
+ kf .Pipeline .Validators [i ] = addName (validator )
329
+ }
330
+ }
331
+ }
332
+
333
+ // addName adds name field to the input function if empty
334
+ // name is nothing but image name in this case as we use it as fall back mergeKey
335
+ func addName (fn kptfilev1.Function ) kptfilev1.Function {
336
+ if fn .Name != "" {
337
+ return fn
338
+ }
339
+ var key string
340
+ if fn .Exec != "" {
341
+ key = fn .Exec
342
+ } else {
343
+ parts := strings .Split (fn .Image , ":" )
344
+ if len (parts ) > 0 {
345
+ key = parts [0 ]
346
+ }
347
+ }
348
+ fn .Name = fmt .Sprintf ("_kpt-merge_%s" , key )
349
+ return fn
350
+ }
351
+
352
+ // removeFnKey remove the synthesized function name field before writing
353
+ func removeFnKey (kfs ... * kptfilev1.KptFile ) {
354
+ for _ , kf := range kfs {
355
+ if kf == nil || kf .Pipeline == nil {
356
+ continue
357
+ }
358
+ for i := range kf .Pipeline .Mutators {
359
+ if strings .HasPrefix (kf .Pipeline .Mutators [i ].Name , "_kpt-merge_" ) {
360
+ kf .Pipeline .Mutators [i ].Name = ""
361
+ }
362
+ }
363
+ for i := range kf .Pipeline .Validators {
364
+ if strings .HasPrefix (kf .Pipeline .Validators [i ].Name , "_kpt-merge_" ) {
365
+ kf .Pipeline .Validators [i ].Name = ""
366
+ }
367
+ }
368
+ }
369
+ }
370
+
264
371
func updateUpstreamAndUpstreamLock (localKf , updatedKf * kptfilev1.KptFile ) {
265
372
if updatedKf .Upstream != nil {
266
373
localKf .Upstream = updatedKf .Upstream
0 commit comments