4
4
"context"
5
5
"fmt"
6
6
"path/filepath"
7
+ "regexp"
7
8
"strings"
8
9
"sync"
9
10
"text/template"
@@ -21,7 +22,11 @@ import (
21
22
22
23
"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
23
24
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
24
- "gopkg.in/yaml.v2"
25
+
26
+ "github.com/goccy/go-yaml"
27
+ "github.com/goccy/go-yaml/ast"
28
+ "github.com/goccy/go-yaml/parser"
29
+ "github.com/goccy/go-yaml/token"
25
30
)
26
31
27
32
// Stores some statistics about the results of a run
@@ -459,8 +464,7 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
459
464
if strings .HasPrefix (app .Annotations [common .WriteBackTargetAnnotation ], common .HelmPrefix ) {
460
465
images := GetImagesAndAliasesFromApplication (app )
461
466
462
- helmNewValues := yaml.MapSlice {}
463
- err = yaml .Unmarshal (originalData , & helmNewValues )
467
+ helmNewValues , err := parser .ParseBytes (originalData , parser .ParseComments )
464
468
if err != nil {
465
469
return nil , err
466
470
}
@@ -488,7 +492,7 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
488
492
if helmParamVersion == nil {
489
493
return nil , fmt .Errorf ("%s parameter not found" , helmAnnotationParamVersion )
490
494
}
491
- err = setHelmValue (& helmNewValues , helmAnnotationParamVersion , helmParamVersion .Value )
495
+ err = setHelmValue (helmNewValues , helmAnnotationParamVersion , helmParamVersion .Value )
492
496
if err != nil {
493
497
return nil , fmt .Errorf ("failed to set image parameter version value: %v" , err )
494
498
}
@@ -499,13 +503,13 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
499
503
return nil , fmt .Errorf ("%s parameter not found" , helmAnnotationParamName )
500
504
}
501
505
502
- err = setHelmValue (& helmNewValues , helmAnnotationParamName , helmParamName .Value )
506
+ err = setHelmValue (helmNewValues , helmAnnotationParamName , helmParamName .Value )
503
507
if err != nil {
504
508
return nil , fmt .Errorf ("failed to set image parameter name value: %v" , err )
505
509
}
506
510
}
507
511
508
- override , err = yaml . Marshal (helmNewValues )
512
+ override = [] byte (helmNewValues . String () )
509
513
} else {
510
514
var params helmOverride
511
515
newParams := helmOverride {
@@ -561,76 +565,113 @@ func mergeKustomizeOverride(t *kustomizeOverride, o *kustomizeOverride) {
561
565
}
562
566
}
563
567
564
- // Check if a key exists in a MapSlice and return its index and value
565
- func findHelmValuesKey (m yaml.MapSlice , key string ) (int , bool ) {
566
- for i , item := range m {
567
- if item .Key == key {
568
- return i , true
568
+ func splitKeys (input string ) []string {
569
+ // Regular expression to match quoted and unquoted segments
570
+ re := regexp .MustCompile (`'([^']*)'|"([^"]*)"|([^.".]+)` )
571
+ matches := re .FindAllStringSubmatch (input , - 1 )
572
+
573
+ var result []string
574
+ for _ , match := range matches {
575
+ if match [1 ] != "" { // Single-quoted string
576
+ result = append (result , match [1 ])
577
+ } else if match [2 ] != "" { // Double-quoted string
578
+ result = append (result , match [2 ])
579
+ } else if match [3 ] != "" { // Unquoted segment
580
+ result = append (result , match [3 ])
569
581
}
570
582
}
571
- return - 1 , false
583
+
584
+ return result
572
585
}
573
586
574
587
// set value of the parameter passed from the annotations.
575
- func setHelmValue (currentValues * yaml.MapSlice , key string , value interface {}) error {
576
- // Check if the full key exists
577
- if idx , found := findHelmValuesKey (* currentValues , key ); found {
578
- (* currentValues )[idx ].Value = value
579
- return nil
588
+ func setHelmValue (file * ast.File , keyPath , value string ) error {
589
+ path := splitKeys (keyPath )
590
+ if len (path ) == 0 {
591
+ return fmt .Errorf ("empty key provided" )
580
592
}
581
593
582
- var err error
583
- keys := strings .Split (key , "." )
584
- current := currentValues
585
- var parent * yaml.MapSlice
586
- parentIdx := - 1
587
-
588
- for i , k := range keys {
589
- if idx , found := findHelmValuesKey (* current , k ); found {
590
- if i == len (keys )- 1 {
591
- // If we're at the final key, set the value and return
592
- (* current )[idx ].Value = value
593
- return nil
594
- } else {
595
- // Navigate deeper into the map
596
- if nestedMap , ok := (* current )[idx ].Value .(yaml.MapSlice ); ok {
597
- parent = current
598
- parentIdx = idx
599
- current = & nestedMap
600
- } else {
601
- return fmt .Errorf ("unexpected type %T for key %s" , (* current )[idx ].Value , k )
594
+ mapping , ok := file .Docs [0 ].Body .(* ast.MappingNode )
595
+ if ! ok {
596
+ tk := token .New ("$" , "$" , & token.Position {})
597
+ mapping = ast .Mapping (tk , false )
598
+ file .Docs [0 ].Body = mapping
599
+ }
600
+
601
+ // Traverse the path
602
+ var lastNode * ast.MappingValueNode
603
+ for index , key := range path {
604
+ found := false
605
+ var currentNode * ast.MappingValueNode
606
+
607
+ for _ , v := range mapping .Values {
608
+ if v .Key .GetToken ().Value == key {
609
+ currentNode = v
610
+ if index == len (path )- 1 {
611
+ lastNode = currentNode
612
+ found = true
613
+ break
614
+ }
615
+ // Move deeper into the structure
616
+ if nextMapping , ok := v .Value .(* ast.MappingNode ); ok {
617
+ mapping = nextMapping
618
+ found = true
619
+ break
602
620
}
603
621
}
604
- } else {
605
- newCurrent := yaml.MapSlice {}
606
- var newParent yaml.MapSlice
622
+ }
607
623
608
- if i == len (keys )- 1 {
609
- newParent = append (* current , yaml.MapItem {Key : k , Value : value })
610
- } else {
611
- newParent = append (* current , yaml.MapItem {Key : k , Value : newCurrent })
612
- }
624
+ // If key does not exist, create it
625
+ if ! found {
626
+ // Create a token with proper position (assuming default line/column)
627
+ keyToken := token .New (key , key , & token.Position {Column : index * 2 + 1 })
628
+ newKey := ast .String (keyToken ) // Create key node
629
+ mappingToken := token .New (key , key , & token.Position {})
630
+ newMapping := ast .Mapping (mappingToken , false ) // Create empty mapping
613
631
614
- if parent == nil {
615
- * currentValues = newParent
616
- } else {
617
- // if parentIdx has not been set (parent element is also new), set it to the last element
618
- if parentIdx == - 1 {
619
- parentIdx = len (* parent ) - 1
620
- if parentIdx < 0 {
621
- parentIdx = 0
622
- }
632
+ if currentNode != nil {
633
+ comment := currentNode .Value .GetComment ()
634
+ currentNode .Value = newMapping
635
+ err := currentNode .Value .SetComment (comment )
636
+ if err != nil {
637
+ return err
623
638
}
624
- (* parent )[parentIdx ].Value = newParent
639
+ lastNode = currentNode
640
+ } else {
641
+ // Add the new mapping to the parent mapping
642
+ lastNode = ast .MappingValue (mappingToken , newKey , newMapping )
643
+ mapping .Values = append (mapping .Values , lastNode )
625
644
}
626
-
627
- parent = & newParent
628
- current = & newCurrent
629
- parentIdx = - 1
645
+ mapping = newMapping
630
646
}
631
647
}
632
648
633
- return err
649
+ if lastNode == nil {
650
+ return fmt .Errorf ("key not found" )
651
+ }
652
+
653
+ var valueToken * token.Token
654
+ if token .IsNeedQuoted (value ) {
655
+ valueToken = token .SingleQuote (value , value , & token.Position {})
656
+ } else {
657
+ valueToken = token .New (value , value , & token.Position {})
658
+ }
659
+ newValue := ast .String (valueToken )
660
+ comment := lastNode .Value .GetComment ()
661
+ if comment == nil {
662
+ comment = lastNode .Key .GetComment ()
663
+ }
664
+ lastNode .Value = newValue
665
+ err := lastNode .Key .SetComment (nil )
666
+ if err != nil {
667
+ return err
668
+ }
669
+ err = lastNode .Value .SetComment (comment )
670
+ if err != nil {
671
+ return err
672
+ }
673
+
674
+ return nil
634
675
}
635
676
636
677
func getWriteBackConfig (app * v1alpha1.Application , kubeClient * kube.ImageUpdaterKubernetesClient , argoClient ArgoCD ) (* WriteBackConfig , error ) {
0 commit comments