11package argocd
22
33import (
4+ "bytes"
45 "context"
56 "fmt"
67 "path/filepath"
@@ -21,7 +22,7 @@ import (
2122
2223 "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
2324 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
24- "gopkg.in/yaml.v2 "
25+ "gopkg.in/yaml.v3 "
2526)
2627
2728// Stores some statistics about the results of a run
@@ -418,6 +419,45 @@ func setAppImage(app *v1alpha1.Application, img *image.ContainerImage) error {
418419 return err
419420}
420421
422+ func marshalWithIndent (in interface {}, indent int ) (out []byte , err error ) {
423+ var b bytes.Buffer
424+ encoder := yaml .NewEncoder (& b )
425+ defer encoder .Close ()
426+ // note: yaml.v3 will only respect indents from 1 to 9 inclusive.
427+ encoder .SetIndent (indent )
428+ if err = encoder .Encode (in ); err != nil {
429+ return nil , err
430+ }
431+ if err = encoder .Close (); err != nil {
432+ return nil , err
433+ }
434+ return b .Bytes (), nil
435+ }
436+
437+ func guessIndent (root * yaml.Node ) int {
438+ node := root
439+ if root .Kind == yaml .DocumentNode {
440+ if len (node .Content ) == 0 {
441+ return 2
442+ }
443+ node = root .Content [0 ]
444+ }
445+ // anything other than a map at the root makes guessing difficult
446+ if node .Kind != yaml .MappingNode || len (node .Content ) == 0 {
447+ return 2
448+ }
449+ // first level map entries that are themselves mappings or sequences,
450+ // in block style, and are indented, allow guessing the preferred indent.
451+ for i , child := range node .Content {
452+ if i % 2 == 1 && child .Column > 1 && child .Column < 10 && child .Style != yaml .FlowStyle {
453+ if child .Kind == yaml .MappingNode || child .Kind == yaml .SequenceNode {
454+ return child .Column - 1
455+ }
456+ }
457+ }
458+ return 2
459+ }
460+
421461// marshalParamsOverride marshals the parameter overrides of a given application
422462// into YAML bytes
423463func marshalParamsOverride (app * v1alpha1.Application , originalData []byte ) ([]byte , error ) {
@@ -441,16 +481,16 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
441481 }
442482
443483 if len (originalData ) == 0 {
444- override , err = yaml . Marshal (newParams )
484+ override , err = marshalWithIndent (newParams , 2 )
445485 break
446486 }
447487 err = yaml .Unmarshal (originalData , & params )
448488 if err != nil {
449- override , err = yaml . Marshal (newParams )
489+ override , err = marshalWithIndent (newParams , 2 )
450490 break
451491 }
452492 mergeKustomizeOverride (& params , & newParams )
453- override , err = yaml . Marshal (params )
493+ override , err = marshalWithIndent (params , 2 )
454494 case ApplicationTypeHelm :
455495 if appSource .Helm == nil {
456496 return []byte {}, nil
@@ -459,11 +499,12 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
459499 if strings .HasPrefix (app .Annotations [common .WriteBackTargetAnnotation ], common .HelmPrefix ) {
460500 images := GetImagesAndAliasesFromApplication (app )
461501
462- helmNewValues := yaml.MapSlice {}
502+ helmNewValues := yaml.Node {}
463503 err = yaml .Unmarshal (originalData , & helmNewValues )
464504 if err != nil {
465505 return nil , err
466506 }
507+ indent := guessIndent (& helmNewValues )
467508
468509 for _ , c := range images {
469510 if c .ImageAlias == "" {
@@ -505,7 +546,7 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
505546 }
506547 }
507548
508- override , err = yaml . Marshal ( helmNewValues )
549+ override , err = marshalWithIndent ( & helmNewValues , indent )
509550 } else {
510551 var params helmOverride
511552 newParams := helmOverride {
@@ -518,16 +559,16 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
518559 log .WithContext ().AddField ("application" , app ).Debugf ("values: '%s'" , outputParams )
519560
520561 if len (originalData ) == 0 {
521- override , err = yaml . Marshal (newParams )
562+ override , err = marshalWithIndent (newParams , 2 )
522563 break
523564 }
524565 err = yaml .Unmarshal (originalData , & params )
525566 if err != nil {
526- override , err = yaml . Marshal (newParams )
567+ override , err = marshalWithIndent (newParams , 2 )
527568 break
528569 }
529570 mergeHelmOverride (& params , & newParams )
530- override , err = yaml . Marshal (params )
571+ override , err = marshalWithIndent (params , 2 )
531572 }
532573 default :
533574 err = fmt .Errorf ("unsupported application type" )
@@ -572,72 +613,98 @@ func mergeKustomizeOverride(t *kustomizeOverride, o *kustomizeOverride) {
572613 }
573614}
574615
575- // Check if a key exists in a MapSlice and return its index and value
576- func findHelmValuesKey (m yaml.MapSlice , key string ) (int , bool ) {
577- for i , item := range m {
578- if item .Key == key {
579- return i , true
616+ // Check if a key exists in a MappingNode and return the index of its value
617+ func findHelmValuesKey (m * yaml.Node , key string ) (int , bool ) {
618+ for i , item := range m . Content {
619+ if i % 2 == 0 && item .Value == key {
620+ return i + 1 , true
580621 }
581622 }
582623 return - 1 , false
583624}
584625
626+ func nodeKindString (k yaml.Kind ) string {
627+ return map [yaml.Kind ]string {
628+ yaml .DocumentNode : "DocumentNode" ,
629+ yaml .SequenceNode : "SequenceNode" ,
630+ yaml .MappingNode : "MappingNode" ,
631+ yaml .ScalarNode : "ScalarNode" ,
632+ yaml .AliasNode : "AliasNode" ,
633+ }[k ]
634+ }
635+
585636// set value of the parameter passed from the annotations.
586- func setHelmValue (currentValues * yaml.MapSlice , key string , value interface {}) error {
637+ func setHelmValue (currentValues * yaml.Node , key string , value interface {}) error {
638+ current := currentValues
639+
640+ // an unmarshalled document has a DocumentNode at the root, but
641+ // we navigate from a MappingNode.
642+ if current .Kind == yaml .DocumentNode {
643+ current = current .Content [0 ]
644+ }
645+
646+ if current .Kind != yaml .MappingNode {
647+ return fmt .Errorf ("unexpected type %s for root" , nodeKindString (current .Kind ))
648+ }
649+
587650 // Check if the full key exists
588- if idx , found := findHelmValuesKey (* currentValues , key ); found {
589- (* currentValues ) [idx ].Value = value
651+ if idx , found := findHelmValuesKey (current , key ); found {
652+ (* current ). Content [idx ].Value = value .( string )
590653 return nil
591654 }
592655
593656 var err error
594657 keys := strings .Split (key , "." )
595- current := currentValues
596- var parent * yaml.MapSlice
597- parentIdx := - 1
598658
599659 for i , k := range keys {
600- if idx , found := findHelmValuesKey (* current , k ); found {
660+ if idx , found := findHelmValuesKey (current , k ); found {
661+ // Navigate deeper into the map
662+ current = (* current ).Content [idx ]
663+ // unpack one level of alias; an alias of an alias is not supported
664+ if current .Kind == yaml .AliasNode {
665+ current = current .Alias
666+ }
601667 if i == len (keys )- 1 {
602668 // If we're at the final key, set the value and return
603- (* current )[idx ].Value = value
604- return nil
605- } else {
606- // Navigate deeper into the map
607- if nestedMap , ok := (* current )[idx ].Value .(yaml.MapSlice ); ok {
608- parent = current
609- parentIdx = idx
610- current = & nestedMap
669+ if current .Kind == yaml .ScalarNode {
670+ current .Value = value .(string )
671+ current .Tag = "!!str"
611672 } else {
612- return fmt .Errorf ("unexpected type %T for key %s" , ( * current )[ idx ]. Value , k )
673+ return fmt .Errorf ("unexpected type %s for key %s" , nodeKindString ( current . Kind ) , k )
613674 }
675+ return nil
676+ } else if current .Kind != yaml .MappingNode {
677+ return fmt .Errorf ("unexpected type %s for key %s" , nodeKindString (current .Kind ), k )
614678 }
615679 } else {
616- newCurrent := yaml.MapSlice {}
617- var newParent yaml.MapSlice
618-
619680 if i == len (keys )- 1 {
620- newParent = append (* current , yaml.MapItem {Key : k , Value : value })
621- } else {
622- newParent = append (* current , yaml.MapItem {Key : k , Value : newCurrent })
623- }
624-
625- if parent == nil {
626- * currentValues = newParent
681+ current .Content = append (current .Content ,
682+ & yaml.Node {
683+ Kind : yaml .ScalarNode ,
684+ Value : k ,
685+ Tag : "!!str" ,
686+ },
687+ & yaml.Node {
688+ Kind : yaml .ScalarNode ,
689+ Value : value .(string ),
690+ Tag : "!!str" ,
691+ },
692+ )
693+ return nil
627694 } else {
628- // if parentIdx has not been set (parent element is also new), set it to the last element
629- if parentIdx == - 1 {
630- parentIdx = len (* parent ) - 1
631- if parentIdx < 0 {
632- parentIdx = 0
633- }
634- }
635- (* parent )[parentIdx ].Value = newParent
695+ current .Content = append (current .Content ,
696+ & yaml.Node {
697+ Kind : yaml .ScalarNode ,
698+ Value : k ,
699+ Tag : "!!str" ,
700+ },
701+ & yaml.Node {
702+ Kind : yaml .MappingNode ,
703+ Content : []* yaml.Node {},
704+ },
705+ )
706+ current = current .Content [len (current .Content )- 1 ]
636707 }
637-
638- parent = & newParent
639- current = & newCurrent
640- parentIdx = - 1
641708 }
642709 }
643710
0 commit comments