From c23b34226b65facdfa5495f605982b17f3cc5ae1 Mon Sep 17 00:00:00 2001 From: Romain Caire Date: Wed, 20 Mar 2024 18:04:25 +0100 Subject: [PATCH 1/3] Allow write-back to git helm (fix overwrite code) Signed-off-by: Romain Caire --- pkg/argocd/argocd.go | 9 +++++++++ pkg/argocd/update.go | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/pkg/argocd/argocd.go b/pkg/argocd/argocd.go index 4584dbe0..e2868b69 100644 --- a/pkg/argocd/argocd.go +++ b/pkg/argocd/argocd.go @@ -503,6 +503,12 @@ func GetImagesFromApplication(app *v1alpha1.Application) image.ContainerImageLis return images } +func GetImagesFromImageList(app *v1alpha1.Application) image.ContainerImageList { + images := make(image.ContainerImageList, 0) + images = append(images, *parseImageList(app.Annotations)...) + return images +} + // GetApplicationTypeByName first retrieves application with given appName and // returns its application type func GetApplicationTypeByName(client ArgoCD, appName string) (ApplicationType, error) { @@ -552,6 +558,9 @@ func getApplicationSourceType(app *v1alpha1.Application) v1alpha1.ApplicationSou if st, set := app.Annotations[common.WriteBackTargetAnnotation]; set && strings.HasPrefix(st, common.KustomizationPrefix) { return v1alpha1.ApplicationSourceTypeKustomize + } else if st, set := app.Annotations[common.WriteBackTargetAnnotation]; set && + strings.HasPrefix(st, common.HelmPrefix) { + return v1alpha1.ApplicationSourceTypeHelm } if app.Spec.HasMultipleSources() { diff --git a/pkg/argocd/update.go b/pkg/argocd/update.go index 855bbdd0..aba999b4 100644 --- a/pkg/argocd/update.go +++ b/pkg/argocd/update.go @@ -416,15 +416,15 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by } if strings.HasPrefix(app.Annotations[common.WriteBackTargetAnnotation], common.HelmPrefix) { - images := GetImagesFromApplication(app) + images := GetImagesFromImageList(app) for _, c := range images { - helmAnnotationParamName, helmAnnotationParamVersion := getHelmParamNamesFromAnnotation(app.Annotations, c.ImageName) + helmAnnotationParamName, helmAnnotationParamVersion := getHelmParamNamesFromAnnotation(app.Annotations, c.ImageAlias) if helmAnnotationParamName == "" { - return nil, fmt.Errorf("could not find an image-name annotation for image %s", c.ImageName) + return nil, fmt.Errorf("could not find an image-name annotation for image %s", c.ImageAlias) } if helmAnnotationParamVersion == "" { - return nil, fmt.Errorf("could not find an image-tag annotation for image %s", c.ImageName) + return nil, fmt.Errorf("could not find an image-tag annotation for image %s", c.ImageAlias) } helmParamName := getHelmParam(appSource.Helm.Parameters, helmAnnotationParamName) @@ -437,11 +437,16 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by return nil, fmt.Errorf("%s parameter not found", helmAnnotationParamVersion) } - // Build string with YAML format to merge with originalData values - helmValues := fmt.Sprintf("%s: %s\n%s: %s", helmAnnotationParamName, helmParamName.Value, helmAnnotationParamVersion, helmParamVersion.Value) - var mergedParams *conflate.Conflate - mergedParams, err = conflate.FromData(originalData, []byte(helmValues)) + mergedParams, err = conflate.FromData(originalData) + if err != nil { + return nil, err + } + + err = mergedParams.AddGo( + dotToObj(helmAnnotationParamName, helmParamName.Value), + dotToObj(helmAnnotationParamVersion, helmParamVersion.Value), + ) if err != nil { return nil, err } @@ -481,6 +486,24 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by return override, nil } +func dotToObj(key string, value string) map[string]interface{} { + keys := strings.Split(key, ".") + obj := make(map[string]interface{}) + + current := obj + for i, k := range keys { + if i == len(keys)-1 { + current[k] = value + } else { + currentMap := make(map[string]interface{}) + current[k] = currentMap + current = currentMap + } + } + + return obj +} + func mergeHelmOverride(t *helmOverride, o *helmOverride) { for _, param := range o.Helm.Parameters { idx := slices.IndexFunc(t.Helm.Parameters, func(tp v1alpha1.HelmParameter) bool { return tp.Name == param.Name }) From 021a55482ff477ce5f2b366ffd132e1d8b86f8ee Mon Sep 17 00:00:00 2001 From: Romain Caire Date: Wed, 20 Mar 2024 18:53:53 +0100 Subject: [PATCH 2/3] Fix tests Signed-off-by: Romain Caire --- pkg/argocd/update.go | 11 ++++++++--- pkg/argocd/update_test.go | 20 ++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/pkg/argocd/update.go b/pkg/argocd/update.go index aba999b4..816f3fd0 100644 --- a/pkg/argocd/update.go +++ b/pkg/argocd/update.go @@ -419,12 +419,17 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by images := GetImagesFromImageList(app) for _, c := range images { - helmAnnotationParamName, helmAnnotationParamVersion := getHelmParamNamesFromAnnotation(app.Annotations, c.ImageAlias) + imageName := c.ImageAlias + if c.ImageAlias == "" { + imageName = c.ImageName + } + + helmAnnotationParamName, helmAnnotationParamVersion := getHelmParamNamesFromAnnotation(app.Annotations, imageName) if helmAnnotationParamName == "" { - return nil, fmt.Errorf("could not find an image-name annotation for image %s", c.ImageAlias) + return nil, fmt.Errorf("could not find an image-name annotation for image %s", imageName) } if helmAnnotationParamVersion == "" { - return nil, fmt.Errorf("could not find an image-tag annotation for image %s", c.ImageAlias) + return nil, fmt.Errorf("could not find an image-tag annotation for image %s", imageName) } helmParamName := getHelmParam(appSource.Helm.Parameters, helmAnnotationParamName) diff --git a/pkg/argocd/update_test.go b/pkg/argocd/update_test.go index 1d1246fa..199b4436 100644 --- a/pkg/argocd/update_test.go +++ b/pkg/argocd/update_test.go @@ -1320,8 +1320,9 @@ helm: t.Run("Valid Helm source with Helm values file", func(t *testing.T) { expected := ` -image.name: nginx -image.tag: v1.0.0 +image: + name: nginx + tag: v1.0.0 replicas: 1 ` app := v1alpha1.Application{ @@ -1366,8 +1367,9 @@ replicas: 1 } originalData := []byte(` -image.name: nginx -image.tag: v0.0.0 +image: + name: nginx + tag: v0.0.0 replicas: 1 `) yaml, err := marshalParamsOverride(&app, originalData) @@ -1512,7 +1514,10 @@ replicas: 1 }, } - originalData := []byte(`random content`) + originalData := []byte(` +random: + content: hello +`) _, err := marshalParamsOverride(&app, originalData) assert.Error(t, err) assert.Equal(t, "wrongimage.name parameter not found", err.Error()) @@ -1560,7 +1565,10 @@ replicas: 1 }, } - originalData := []byte(`random content`) + originalData := []byte(` +random: + content: hello +`) _, err := marshalParamsOverride(&app, originalData) assert.Error(t, err) assert.Equal(t, "wrongimage.tag parameter not found", err.Error()) From 62c519777de99d13f4c61ccf0f9b0a0acf030dcc Mon Sep 17 00:00:00 2001 From: Romain Caire Date: Wed, 20 Mar 2024 23:46:54 +0100 Subject: [PATCH 3/3] Add DotToObj tests Signed-off-by: Romain Caire --- pkg/argocd/update.go | 6 +++++- pkg/argocd/update_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/pkg/argocd/update.go b/pkg/argocd/update.go index 816f3fd0..143ff6db 100644 --- a/pkg/argocd/update.go +++ b/pkg/argocd/update.go @@ -492,8 +492,12 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by } func dotToObj(key string, value string) map[string]interface{} { - keys := strings.Split(key, ".") obj := make(map[string]interface{}) + if key == "" { + return obj + } + + keys := strings.Split(key, ".") current := obj for i, k := range keys { diff --git a/pkg/argocd/update_test.go b/pkg/argocd/update_test.go index 199b4436..9e3d5084 100644 --- a/pkg/argocd/update_test.go +++ b/pkg/argocd/update_test.go @@ -1652,6 +1652,28 @@ random: }) } +func Test_DotToObj(t *testing.T) { + t.Run("no value", func(t *testing.T) { + obj := dotToObj("", "val") + assert.Equal(t, map[string]interface{}{}, obj) + }) + + t.Run("single value", func(t *testing.T) { + obj := dotToObj("a", "val") + assert.Equal(t, map[string]interface{}{"a": "val"}, obj) + }) + + t.Run("multiple values", func(t *testing.T) { + obj := dotToObj("a.b.c", "val") + assert.Equal(t, map[string]interface{}{"a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": "val", + }, + }, + }, obj) + }) +} + func Test_GetWriteBackConfig(t *testing.T) { t.Run("Valid write-back config - git", func(t *testing.T) { app := v1alpha1.Application{