Skip to content

Commit

Permalink
Preserve field order when updating kustomization.yaml file
Browse files Browse the repository at this point in the history
This commit causes argocd-image-updater to preserve the order of fields
in the kustomization.yaml file when updating the image(s). Doing so will
minimize git diffs in the case that other tools also edit the
kustomization.yaml file.

Fixes #502
  • Loading branch information
jtdoepke committed Jan 25, 2024
1 parent 7d93c7a commit 3b8a879
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 2 deletions.
34 changes: 32 additions & 2 deletions pkg/argocd/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/order"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"

"github.com/argoproj-labs/argocd-image-updater/pkg/image"
Expand Down Expand Up @@ -314,14 +315,43 @@ func writeKustomization(app *v1alpha1.Application, wbc *WriteBackConfig, gitC gi
if err != nil {
return err, false
}
err = kyaml.UpdateFile(filterFunc, kustFile)
if err != nil {

if err = updateKustomizeFile(filterFunc, kustFile); err != nil {
return err, false
}

return nil, false
}

// updateKustomizeFile reads the kustomization file at path, applies the filter to it, and writes the result back
// to the file. This is the same behavior as kyaml.UpdateFile, but it preserves the original order
// of YAML fields to minimize git diffs.
func updateKustomizeFile(filter kyaml.Filter, path string) error {
// Read the yaml
y, err := kyaml.ReadFile(path)
if err != nil {
return err
}

// Update the yaml
yCpy := y.Copy()
if err := yCpy.PipeE(filter); err != nil {
return err
}

// Preserve the original order of fields
if err := order.SyncOrder(y, yCpy); err != nil {
return err
}

// Write the yaml
if err := kyaml.WriteFile(yCpy, path); err != nil {
return err
}

return nil
}

func imagesFilter(images v1alpha1.KustomizeImages) (kyaml.Filter, error) {
var overrides []kyaml.Filter
for _, img := range images {
Expand Down
72 changes: 72 additions & 0 deletions pkg/argocd/git_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package argocd

import (
"os"
"testing"
"text/template"
"time"
Expand Down Expand Up @@ -215,3 +216,74 @@ images:
})
}
}

func Test_updateKustomizeFile(t *testing.T) {
makeTmpKustomization := func(t *testing.T, content []byte) string {
f, err := os.CreateTemp("", "kustomization-*.yaml")
if err != nil {
t.Fatal(err)
}
_, err = f.Write(content)
if err != nil {
t.Fatal(err)
}
f.Close()
t.Cleanup(func() {
os.Remove(f.Name())
})
return f.Name()
}

filter, err := imagesFilter(v1alpha1.KustomizeImages{"foo@sha23456"})
if err != nil {
t.Fatal(err)
}

tests := []struct {
name string
content []byte
wantContent []byte
filter kyaml.Filter
wantErr bool
}{
{
name: "sorted",
content: []byte(`images:
- digest: sha12345
name: foo
`),
wantContent: []byte(`images:
- digest: sha23456
name: foo
`),
filter: filter,
},
{
name: "not-sorted",
content: []byte(`images:
- name: foo
digest: sha12345
`),
wantContent: []byte(`images:
- name: foo
digest: sha23456
`),
filter: filter,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
path := makeTmpKustomization(t, tt.content)
err := updateKustomizeFile(tt.filter, path)
if tt.wantErr {
assert.Error(t, err)
} else {
got, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, string(tt.wantContent), string(got))
}
})
}
}

0 comments on commit 3b8a879

Please sign in to comment.