From 08be221081b238566b8137f9a72177195f82f4a6 Mon Sep 17 00:00:00 2001 From: Hannes Gustafsson Date: Tue, 3 Sep 2024 09:47:04 +0100 Subject: [PATCH] Improve application diff This changes the application diff to render a YAML diff with a number of context lines instead of relying on the cmp library previously used which would render a diff of Go objects. --- go.mod | 4 +- internal/pkg/argocd/argocd.go | 21 +- internal/pkg/argocd/argocd_test.go | 84 ++++++ internal/pkg/argocd/diff/README.md | 5 + internal/pkg/argocd/diff/diff.go | 260 ++++++++++++++++++ internal/pkg/argocd/diff/diff_test.go | 46 ++++ internal/pkg/argocd/diff/testdata/allnew.txt | 13 + internal/pkg/argocd/diff/testdata/allold.txt | 13 + internal/pkg/argocd/diff/testdata/basic.txt | 35 +++ internal/pkg/argocd/diff/testdata/dups.txt | 40 +++ internal/pkg/argocd/diff/testdata/end.txt | 38 +++ internal/pkg/argocd/diff/testdata/eof.txt | 9 + internal/pkg/argocd/diff/testdata/eof1.txt | 18 ++ internal/pkg/argocd/diff/testdata/eof2.txt | 18 ++ internal/pkg/argocd/diff/testdata/long.txt | 62 +++++ internal/pkg/argocd/diff/testdata/same.txt | 5 + internal/pkg/argocd/diff/testdata/start.txt | 34 +++ internal/pkg/argocd/diff/testdata/triv.txt | 40 +++ .../TestDiffLiveVsTargetObject/1.live | 38 +++ .../TestDiffLiveVsTargetObject/1.target | 38 +++ .../TestDiffLiveVsTargetObject/1.want | 16 ++ .../pkg/argocd/testdata/TestRenderDiff.live | 54 ++++ .../pkg/argocd/testdata/TestRenderDiff.md | 22 ++ .../pkg/argocd/testdata/TestRenderDiff.target | 54 ++++ 24 files changed, 961 insertions(+), 6 deletions(-) create mode 100644 internal/pkg/argocd/diff/README.md create mode 100644 internal/pkg/argocd/diff/diff.go create mode 100644 internal/pkg/argocd/diff/diff_test.go create mode 100644 internal/pkg/argocd/diff/testdata/allnew.txt create mode 100644 internal/pkg/argocd/diff/testdata/allold.txt create mode 100644 internal/pkg/argocd/diff/testdata/basic.txt create mode 100644 internal/pkg/argocd/diff/testdata/dups.txt create mode 100644 internal/pkg/argocd/diff/testdata/end.txt create mode 100644 internal/pkg/argocd/diff/testdata/eof.txt create mode 100644 internal/pkg/argocd/diff/testdata/eof1.txt create mode 100644 internal/pkg/argocd/diff/testdata/eof2.txt create mode 100644 internal/pkg/argocd/diff/testdata/long.txt create mode 100644 internal/pkg/argocd/diff/testdata/same.txt create mode 100644 internal/pkg/argocd/diff/testdata/start.txt create mode 100644 internal/pkg/argocd/diff/testdata/triv.txt create mode 100644 internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.live create mode 100644 internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.target create mode 100644 internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.want create mode 100644 internal/pkg/argocd/testdata/TestRenderDiff.live create mode 100644 internal/pkg/argocd/testdata/TestRenderDiff.md create mode 100644 internal/pkg/argocd/testdata/TestRenderDiff.target diff --git a/go.mod b/go.mod index db765467..42008b77 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 github.com/go-test/deep v1.1.0 github.com/golang/mock v1.6.0 - github.com/google/go-cmp v0.6.0 github.com/google/go-github/v62 v62.0.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hexops/gotextdiff v1.0.3 @@ -26,6 +25,7 @@ require ( github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/oauth2 v0.19.0 + golang.org/x/tools v0.21.0 google.golang.org/grpc v1.63.2 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.26.11 @@ -90,6 +90,7 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.7.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v56 v56.0.0 // indirect github.com/google/go-github/v60 v60.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect @@ -175,7 +176,6 @@ require ( golang.org/x/term v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect diff --git a/internal/pkg/argocd/argocd.go b/internal/pkg/argocd/argocd.go index db798428..d4b689fb 100644 --- a/internal/pkg/argocd/argocd.go +++ b/internal/pkg/argocd/argocd.go @@ -24,11 +24,15 @@ import ( argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff" "github.com/argoproj/argo-cd/v2/util/argo/normalizers" "github.com/argoproj/gitops-engine/pkg/sync/hook" - "github.com/google/go-cmp/cmp" log "github.com/sirupsen/logrus" + "github.com/wayfair-incubator/telefonistka/internal/pkg/argocd/diff" + yaml2 "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +// ctxLines is the number of context lines used in application diffs. +const ctxLines = 10 + type argoCdClients struct { app application.ApplicationServiceClient project projectpkg.ProjectServiceClient @@ -145,10 +149,19 @@ func generateArgocdAppDiff(ctx context.Context, keepDiffData bool, app *argoappv return foundDiffs, diffElements, nil } -// Should return output that is compatible with github markdown diff highlighting format +// diffLiveVsTargetObject returns the diff of live and target in a format that +// is compatible with Github markdown diff highlighting. func diffLiveVsTargetObject(live, target *unstructured.Unstructured) (string, error) { - patch := cmp.Diff(live, target) - return patch, nil + a, err := yaml2.Marshal(live) + if err != nil { + return "", err + } + b, err := yaml2.Marshal(target) + if err != nil { + return "", err + } + patch := diff.Diff(ctxLines, "live", a, "target", b) + return string(patch), nil } func getEnv(key, fallback string) string { diff --git a/internal/pkg/argocd/argocd_test.go b/internal/pkg/argocd/argocd_test.go index 3bda870c..a8fd5cff 100644 --- a/internal/pkg/argocd/argocd_test.go +++ b/internal/pkg/argocd/argocd_test.go @@ -1,15 +1,99 @@ package argocd import ( + "bytes" "context" + "os" + "strings" "testing" + "text/template" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/golang/mock/gomock" "github.com/wayfair-incubator/telefonistka/internal/pkg/mocks" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +func readLiveTarget(t *testing.T) (live, target *unstructured.Unstructured, expected string) { + t.Helper() + live = readManifest(t, "testdata/"+t.Name()+".live") + target = readManifest(t, "testdata/"+t.Name()+".target") + expected = readFileString(t, "testdata/"+t.Name()+".want") + return live, target, expected +} + +func readFileString(t *testing.T, path string) string { + b, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + return string(b) +} + +func readManifest(t *testing.T, path string) *unstructured.Unstructured { + t.Helper() + + s := readFileString(t, path) + obj, err := argoappv1.UnmarshalToUnstructured(s) + if err != nil { + t.Fatalf("unmarshal %v: %v", path, err) + } + return obj +} + +func TestDiffLiveVsTargetObject(t *testing.T) { + tests := []struct { + name string + }{ + {"1"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + live, target, want := readLiveTarget(t) + got, err := diffLiveVsTargetObject(live, target) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if got != want { + t.Errorf("got %q, want %q", got, want) + } + }) + } +} + +func TestRenderDiff(t *testing.T) { + live := readManifest(t, "testdata/TestRenderDiff.live") + target := readManifest(t, "testdata/TestRenderDiff.target") + want := readFileString(t, "testdata/TestRenderDiff.md") + data, err := diffLiveVsTargetObject(live, target) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + // backticks are tricky https://github.com/golang/go/issues/24475 + r := strings.NewReplacer("¬", "`") + tmpl := r.Replace("¬¬¬diff\n{{.}}¬¬¬\n") + + rendered := renderTemplate(t, tmpl, data) + + if got, want := rendered.String(), want; got != want { + t.Errorf("got %q, want %q", got, want) + } + +} + +func renderTemplate(t *testing.T, tpl string, data any) *bytes.Buffer { + buf := bytes.NewBuffer(nil) + tmpl := template.New("") + tmpl = template.Must(tmpl.Parse(tpl)) + if err := tmpl.Execute(buf, data); err != nil { + t.Fatalf("unexpected error: %v", err) + } + return buf +} + func TestFindArgocdAppBySHA1Label(t *testing.T) { // Here the filtering is done on the ArgoCD server side, so we are just testing the function returns a app t.Parallel() diff --git a/internal/pkg/argocd/diff/README.md b/internal/pkg/argocd/diff/README.md new file mode 100644 index 00000000..8d73bb26 --- /dev/null +++ b/internal/pkg/argocd/diff/README.md @@ -0,0 +1,5 @@ +This package has been pulled from the internal +[diff](https://github.com/golang/go/tree/master/src/internal/diff) package. + +Minor changes were done to allow a custom number of context lines, import a +public `txtar` package and adhere to the local linter settings. diff --git a/internal/pkg/argocd/diff/diff.go b/internal/pkg/argocd/diff/diff.go new file mode 100644 index 00000000..a7a77ec8 --- /dev/null +++ b/internal/pkg/argocd/diff/diff.go @@ -0,0 +1,260 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file.package argocd + +package diff + +import ( + "bytes" + "fmt" + "sort" + "strings" +) + +// A pair is a pair of values tracked for both the x and y side of a diff. +// It is typically a pair of line indexes. +type pair struct{ x, y int } + +// Diff returns an anchored diff of the two texts old and new +// in the “unified diff” format. If old and new are identical, +// Diff returns a nil slice (no output). +// +// Unix diff implementations typically look for a diff with +// the smallest number of lines inserted and removed, +// which can in the worst case take time quadratic in the +// number of lines in the texts. As a result, many implementations +// either can be made to run for a long time or cut off the search +// after a predetermined amount of work. +// +// In contrast, this implementation looks for a diff with the +// smallest number of “unique” lines inserted and removed, +// where unique means a line that appears just once in both old and new. +// We call this an “anchored diff” because the unique lines anchor +// the chosen matching regions. An anchored diff is usually clearer +// than a standard diff, because the algorithm does not try to +// reuse unrelated blank lines or closing braces. +// The algorithm also guarantees to run in O(n log n) time +// instead of the standard O(n²) time. +// +// Some systems call this approach a “patience diff,” named for +// the “patience sorting” algorithm, itself named for a solitaire card game. +// We avoid that name for two reasons. First, the name has been used +// for a few different variants of the algorithm, so it is imprecise. +// Second, the name is frequently interpreted as meaning that you have +// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm, +// when in fact the algorithm is faster than the standard one. +func Diff(C int, oldName string, old []byte, newName string, new []byte) []byte { + if bytes.Equal(old, new) { + return nil + } + x := lines(old) + y := lines(new) + + // Print diff header. + var out bytes.Buffer + fmt.Fprintf(&out, "diff %s %s\n", oldName, newName) + fmt.Fprintf(&out, "--- %s\n", oldName) + fmt.Fprintf(&out, "+++ %s\n", newName) + + // Loop over matches to consider, + // expanding each match to include surrounding lines, + // and then printing diff chunks. + // To avoid setup/teardown cases outside the loop, + // tgs returns a leading {0,0} and trailing {len(x), len(y)} pair + // in the sequence of matches. + var ( + done pair // printed up to x[:done.x] and y[:done.y] + chunk pair // start lines of current chunk + count pair // number of lines from each side in current chunk + ctext []string // lines for current chunk + ) + for _, m := range tgs(x, y) { + if m.x < done.x { + // Already handled scanning forward from earlier match. + continue + } + + // Expand matching lines as far as possible, + // establishing that x[start.x:end.x] == y[start.y:end.y]. + // Note that on the first (or last) iteration we may (or definitely do) + // have an empty match: start.x==end.x and start.y==end.y. + start := m + for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] { + start.x-- + start.y-- + } + end := m + for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] { + end.x++ + end.y++ + } + + // Emit the mismatched lines before start into this chunk. + // (No effect on first sentinel iteration, when start = {0,0}.) + for _, s := range x[done.x:start.x] { + ctext = append(ctext, "-"+s) + count.x++ + } + for _, s := range y[done.y:start.y] { + ctext = append(ctext, "+"+s) + count.y++ + } + + // If we're not at EOF and have too few common lines, + // the chunk includes all the common lines and continues. + if (end.x < len(x) || end.y < len(y)) && + (end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) { + for _, s := range x[start.x:end.x] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = end + continue + } + + // End chunk with common lines for context. + if len(ctext) > 0 { + n := end.x - start.x + if n > C { + n = C + } + for _, s := range x[start.x : start.x+n] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = pair{start.x + n, start.y + n} + + // Format and emit chunk. + // Convert line numbers to 1-indexed. + // Special case: empty file shows up as 0,0 not 1,0. + if count.x > 0 { + chunk.x++ + } + if count.y > 0 { + chunk.y++ + } + fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y) + for _, s := range ctext { + out.WriteString(s) + } + count.x = 0 + count.y = 0 + ctext = ctext[:0] + } + + // If we reached EOF, we're done. + if end.x >= len(x) && end.y >= len(y) { + break + } + + // Otherwise start a new chunk. + chunk = pair{end.x - C, end.y - C} + for _, s := range x[chunk.x:end.x] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = end + } + + return out.Bytes() +} + +// lines returns the lines in the file x, including newlines. +// If the file does not end in a newline, one is supplied +// along with a warning about the missing newline. +func lines(x []byte) []string { + l := strings.SplitAfter(string(x), "\n") + if l[len(l)-1] == "" { + l = l[:len(l)-1] + } else { + // Treat last line as having a message about the missing newline attached, + // using the same text as BSD/GNU diff (including the leading backslash). + l[len(l)-1] += "\n\\ No newline at end of file\n" + } + return l +} + +// tgs returns the pairs of indexes of the longest common subsequence +// of unique lines in x and y, where a unique line is one that appears +// once in x and once in y. +// +// The longest common subsequence algorithm is as described in +// Thomas G. Szymanski, “A Special Case of the Maximal Common +// Subsequence Problem,” Princeton TR #170 (January 1975), +// available at https://research.swtch.com/tgs170.pdf. +func tgs(x, y []string) []pair { + // Count the number of times each string appears in a and b. + // We only care about 0, 1, many, counted as 0, -1, -2 + // for the x side and 0, -4, -8 for the y side. + // Using negative numbers now lets us distinguish positive line numbers later. + m := make(map[string]int) + for _, s := range x { + if c := m[s]; c > -2 { + m[s] = c - 1 + } + } + for _, s := range y { + if c := m[s]; c > -8 { + m[s] = c - 4 + } + } + + // Now unique strings can be identified by m[s] = -1+-4. + // + // Gather the indexes of those strings in x and y, building: + // xi[i] = increasing indexes of unique strings in x. + // yi[i] = increasing indexes of unique strings in y. + // inv[i] = index j such that x[xi[i]] = y[yi[j]]. + var xi, yi, inv []int + for i, s := range y { + if m[s] == -1+-4 { + m[s] = len(yi) + yi = append(yi, i) + } + } + for i, s := range x { + if j, ok := m[s]; ok && j >= 0 { + xi = append(xi, i) + inv = append(inv, j) + } + } + + // Apply Algorithm A from Szymanski's paper. + // In those terms, A = J = inv and B = [0, n). + // We add sentinel pairs {0,0}, and {len(x),len(y)} + // to the returned sequence, to help the processing loop. + J := inv + n := len(xi) + T := make([]int, n) + L := make([]int, n) + for i := range T { + T[i] = n + 1 + } + for i := 0; i < n; i++ { + k := sort.Search(n, func(k int) bool { + return T[k] >= J[i] + }) + T[k] = J[i] + L[i] = k + 1 + } + k := 0 + for _, v := range L { + if k < v { + k = v + } + } + seq := make([]pair, 2+k) + seq[1+k] = pair{len(x), len(y)} // sentinel at end + lastj := n + for i := n - 1; i >= 0; i-- { + if L[i] == k && J[i] < lastj { + seq[k] = pair{xi[i], yi[J[i]]} + k-- + } + } + seq[0] = pair{0, 0} // sentinel at start + return seq +} diff --git a/internal/pkg/argocd/diff/diff_test.go b/internal/pkg/argocd/diff/diff_test.go new file mode 100644 index 00000000..59a03d29 --- /dev/null +++ b/internal/pkg/argocd/diff/diff_test.go @@ -0,0 +1,46 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package diff + +import ( + "bytes" + "path/filepath" + "testing" + + "golang.org/x/tools/txtar" +) + +func clean(text []byte) []byte { + text = bytes.ReplaceAll(text, []byte("$\n"), []byte("\n")) + text = bytes.TrimSuffix(text, []byte("^D\n")) + return text +} + +func TestDiff(t *testing.T) { + t.Parallel() + files, _ := filepath.Glob("testdata/*.txt") + if len(files) == 0 { + t.Fatalf("no testdata") + } + + for _, file := range files { + t.Run(filepath.Base(file), func(t *testing.T) { + t.Parallel() + a, err := txtar.ParseFile(file) + if err != nil { + t.Fatal(err) + } + if len(a.Files) != 3 || a.Files[2].Name != "diff" { + t.Fatalf("%s: want three files, third named \"diff\"", file) + } + diffs := Diff(3, a.Files[0].Name, clean(a.Files[0].Data), a.Files[1].Name, clean(a.Files[1].Data)) + want := clean(a.Files[2].Data) + if !bytes.Equal(diffs, want) { + t.Fatalf("%s: have:\n%s\nwant:\n%s\n%s", file, + diffs, want, Diff(3, "have", diffs, "want", want)) + } + }) + } +} diff --git a/internal/pkg/argocd/diff/testdata/allnew.txt b/internal/pkg/argocd/diff/testdata/allnew.txt new file mode 100644 index 00000000..88756492 --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/allnew.txt @@ -0,0 +1,13 @@ +-- old -- +-- new -- +a +b +c +-- diff -- +diff old new +--- old ++++ new +@@ -0,0 +1,3 @@ ++a ++b ++c diff --git a/internal/pkg/argocd/diff/testdata/allold.txt b/internal/pkg/argocd/diff/testdata/allold.txt new file mode 100644 index 00000000..bcc9ac0e --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/allold.txt @@ -0,0 +1,13 @@ +-- old -- +a +b +c +-- new -- +-- diff -- +diff old new +--- old ++++ new +@@ -1,3 +0,0 @@ +-a +-b +-c diff --git a/internal/pkg/argocd/diff/testdata/basic.txt b/internal/pkg/argocd/diff/testdata/basic.txt new file mode 100644 index 00000000..d2565b5d --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/basic.txt @@ -0,0 +1,35 @@ +Example from Hunt and McIlroy, “An Algorithm for Differential File Comparison.” +https://www.cs.dartmouth.edu/~doug/diff.pdf + +-- old -- +a +b +c +d +e +f +g +-- new -- +w +a +b +x +y +z +e +-- diff -- +diff old new +--- old ++++ new +@@ -1,7 +1,7 @@ ++w + a + b +-c +-d ++x ++y ++z + e +-f +-g diff --git a/internal/pkg/argocd/diff/testdata/dups.txt b/internal/pkg/argocd/diff/testdata/dups.txt new file mode 100644 index 00000000..d10524d0 --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/dups.txt @@ -0,0 +1,40 @@ +-- old -- +a + +b + +c + +d + +e + +f +-- new -- +a + +B + +C + +d + +e + +f +-- diff -- +diff old new +--- old ++++ new +@@ -1,8 +1,8 @@ + a + $ +-b +- +-c ++B ++ ++C + $ + d + $ diff --git a/internal/pkg/argocd/diff/testdata/end.txt b/internal/pkg/argocd/diff/testdata/end.txt new file mode 100644 index 00000000..158637c1 --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/end.txt @@ -0,0 +1,38 @@ +-- old -- +1 +2 +3 +4 +5 +6 +7 +eight +nine +ten +eleven +-- new -- +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +-- diff -- +diff old new +--- old ++++ new +@@ -5,7 +5,6 @@ + 5 + 6 + 7 +-eight +-nine +-ten +-eleven ++8 ++9 ++10 diff --git a/internal/pkg/argocd/diff/testdata/eof.txt b/internal/pkg/argocd/diff/testdata/eof.txt new file mode 100644 index 00000000..5dc145c4 --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/eof.txt @@ -0,0 +1,9 @@ +-- old -- +a +b +c^D +-- new -- +a +b +c^D +-- diff -- diff --git a/internal/pkg/argocd/diff/testdata/eof1.txt b/internal/pkg/argocd/diff/testdata/eof1.txt new file mode 100644 index 00000000..1ebf621e --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/eof1.txt @@ -0,0 +1,18 @@ +-- old -- +a +b +c +-- new -- +a +b +c^D +-- diff -- +diff old new +--- old ++++ new +@@ -1,3 +1,3 @@ + a + b +-c ++c +\ No newline at end of file diff --git a/internal/pkg/argocd/diff/testdata/eof2.txt b/internal/pkg/argocd/diff/testdata/eof2.txt new file mode 100644 index 00000000..047705e6 --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/eof2.txt @@ -0,0 +1,18 @@ +-- old -- +a +b +c^D +-- new -- +a +b +c +-- diff -- +diff old new +--- old ++++ new +@@ -1,3 +1,3 @@ + a + b +-c +\ No newline at end of file ++c diff --git a/internal/pkg/argocd/diff/testdata/long.txt b/internal/pkg/argocd/diff/testdata/long.txt new file mode 100644 index 00000000..3fc99f71 --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/long.txt @@ -0,0 +1,62 @@ +-- old -- +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +14½ +15 +16 +17 +18 +19 +20 +-- new -- +1 +2 +3 +4 +5 +6 +8 +9 +10 +11 +12 +13 +14 +17 +18 +19 +20 +-- diff -- +diff old new +--- old ++++ new +@@ -4,7 +4,6 @@ + 4 + 5 + 6 +-7 + 8 + 9 + 10 +@@ -12,9 +11,6 @@ + 12 + 13 + 14 +-14½ +-15 +-16 + 17 + 18 + 19 diff --git a/internal/pkg/argocd/diff/testdata/same.txt b/internal/pkg/argocd/diff/testdata/same.txt new file mode 100644 index 00000000..86b1100d --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/same.txt @@ -0,0 +1,5 @@ +-- old -- +hello world +-- new -- +hello world +-- diff -- diff --git a/internal/pkg/argocd/diff/testdata/start.txt b/internal/pkg/argocd/diff/testdata/start.txt new file mode 100644 index 00000000..217b2fdc --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/start.txt @@ -0,0 +1,34 @@ +-- old -- +e +pi +4 +5 +6 +7 +8 +9 +10 +-- new -- +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +-- diff -- +diff old new +--- old ++++ new +@@ -1,5 +1,6 @@ +-e +-pi ++1 ++2 ++3 + 4 + 5 + 6 diff --git a/internal/pkg/argocd/diff/testdata/triv.txt b/internal/pkg/argocd/diff/testdata/triv.txt new file mode 100644 index 00000000..ab5759fc --- /dev/null +++ b/internal/pkg/argocd/diff/testdata/triv.txt @@ -0,0 +1,40 @@ +Another example from Hunt and McIlroy, +“An Algorithm for Differential File Comparison.” +https://www.cs.dartmouth.edu/~doug/diff.pdf + +Anchored diff gives up on finding anything, +since there are no unique lines. + +-- old -- +a +b +c +a +b +b +a +-- new -- +c +a +b +a +b +c +-- diff -- +diff old new +--- old ++++ new +@@ -1,7 +1,6 @@ +-a +-b +-c +-a +-b +-b +-a ++c ++a ++b ++a ++b ++c diff --git a/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.live b/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.live new file mode 100644 index 00000000..73ad30cf --- /dev/null +++ b/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.live @@ -0,0 +1,38 @@ +{ + "apiVersion": "commercetools.io/v1alpha1", + "kind": "Bar", + "metadata": { + "generation": 46, + "labels": { + "argocd.argoproj.io/instance": "foobar-plg-gcp-eu-west1-v1" + }, + "managedFields": [ + { + "apiVersion": "commercetools.io/v1alpha1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:labels": { + "f:argocd.argoproj.io/instance": {} + } + }, + "f:spec": { + "f:deploymentName": {}, + "f:replicas": {} + } + }, + "manager": "argocd-controller", + "operation": "Apply", + "time": "2024-08-30T15:08:52Z" + } + ], + "name": "example-baz-bar", + "namespace": "gitops-demo-ns2", + "resourceVersion": "2168525640", + "uid": "f845fd72-d6d9-48f2-b0f2-2def6807deb8" + }, + "spec": { + "deploymentName": "example-baz-bar", + "replicas": 63 + } +} diff --git a/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.target b/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.target new file mode 100644 index 00000000..86fbedcd --- /dev/null +++ b/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.target @@ -0,0 +1,38 @@ +{ + "apiVersion": "commercetools.io/v1alpha1", + "kind": "Bar", + "metadata": { + "generation": 46, + "labels": { + "argocd.argoproj.io/instance": "foobar-plg-gcp-eu-west1-v1" + }, + "managedFields": [ + { + "apiVersion": "commercetools.io/v1alpha1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:labels": { + "f:argocd.argoproj.io/instance": {} + } + }, + "f:spec": { + "f:deploymentName": {}, + "f:replicas": {} + } + }, + "manager": "argocd-controller", + "operation": "Apply", + "time": "2024-08-30T15:08:52Z" + } + ], + "name": "example-baz-bar", + "namespace": "gitops-demo-ns2", + "resourceVersion": "2168525640", + "uid": "f845fd72-d6d9-48f2-b0f2-2def6807deb8" + }, + "spec": { + "deploymentName": "example-baz-bar", + "replicas": 42 + } +} diff --git a/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.want b/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.want new file mode 100644 index 00000000..924c3ac8 --- /dev/null +++ b/internal/pkg/argocd/testdata/TestDiffLiveVsTargetObject/1.want @@ -0,0 +1,16 @@ +diff live target +--- live ++++ target +@@ -17,11 +17,11 @@ + f:replicas: {} + manager: argocd-controller + operation: Apply + time: "2024-08-30T15:08:52Z" + name: example-baz-bar + namespace: gitops-demo-ns2 + resourceVersion: "2168525640" + uid: f845fd72-d6d9-48f2-b0f2-2def6807deb8 + spec: + deploymentName: example-baz-bar +- replicas: 63 ++ replicas: 42 diff --git a/internal/pkg/argocd/testdata/TestRenderDiff.live b/internal/pkg/argocd/testdata/TestRenderDiff.live new file mode 100644 index 00000000..59c35c98 --- /dev/null +++ b/internal/pkg/argocd/testdata/TestRenderDiff.live @@ -0,0 +1,54 @@ +{ + "apiVersion": "commercetools.io/v1alpha1", + "kind": "Bar", + "rbacBindings": [ + { + "clusterRoleBindings": [ + { + "clusterRole": "view" + } + ], + "name": "security-audit-viewer-vault", + "subjects": [ + { + "kind": "Group", + "name": "vault:some-team@domain.tld" + } + ] + } + ], + "metadata": { + "generation": 46, + "labels": { + "argocd.argoproj.io/instance": "foobar-plg-gcp-eu-west1-v1" + }, + "managedFields": [ + { + "apiVersion": "commercetools.io/v1alpha1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:labels": { + "f:argocd.argoproj.io/instance": {} + } + }, + "f:spec": { + "f:deploymentName": {}, + "f:replicas": {} + } + }, + "manager": "argocd-controller", + "operation": "Apply", + "time": "2024-08-30T15:08:52Z" + } + ], + "name": "example-baz-bar", + "namespace": "gitops-demo-ns2", + "resourceVersion": "2168525640", + "uid": "f845fd72-d6d9-48f2-b0f2-2def6807deb8" + }, + "spec": { + "deploymentName": "example-baz-bar", + "replicas": 63 + } +} diff --git a/internal/pkg/argocd/testdata/TestRenderDiff.md b/internal/pkg/argocd/testdata/TestRenderDiff.md new file mode 100644 index 00000000..30373eec --- /dev/null +++ b/internal/pkg/argocd/testdata/TestRenderDiff.md @@ -0,0 +1,22 @@ +```diff +diff live target +--- live ++++ target +@@ -21,14 +21,14 @@ + name: example-baz-bar + namespace: gitops-demo-ns2 + resourceVersion: "2168525640" + uid: f845fd72-d6d9-48f2-b0f2-2def6807deb8 + rbacBindings: + - clusterRoleBindings: + - clusterRole: view + name: security-audit-viewer-vault + subjects: + - kind: Group +- name: vault:some-team@domain.tld ++ name: vault:some-team-name@domain.tld + spec: + deploymentName: example-baz-bar +- replicas: 63 ++ replicas: 42 +``` diff --git a/internal/pkg/argocd/testdata/TestRenderDiff.target b/internal/pkg/argocd/testdata/TestRenderDiff.target new file mode 100644 index 00000000..b5a9ff30 --- /dev/null +++ b/internal/pkg/argocd/testdata/TestRenderDiff.target @@ -0,0 +1,54 @@ +{ + "apiVersion": "commercetools.io/v1alpha1", + "kind": "Bar", + "rbacBindings": [ + { + "clusterRoleBindings": [ + { + "clusterRole": "view" + } + ], + "name": "security-audit-viewer-vault", + "subjects": [ + { + "kind": "Group", + "name": "vault:some-team-name@domain.tld" + } + ] + } + ], + "metadata": { + "generation": 46, + "labels": { + "argocd.argoproj.io/instance": "foobar-plg-gcp-eu-west1-v1" + }, + "managedFields": [ + { + "apiVersion": "commercetools.io/v1alpha1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:labels": { + "f:argocd.argoproj.io/instance": {} + } + }, + "f:spec": { + "f:deploymentName": {}, + "f:replicas": {} + } + }, + "manager": "argocd-controller", + "operation": "Apply", + "time": "2024-08-30T15:08:52Z" + } + ], + "name": "example-baz-bar", + "namespace": "gitops-demo-ns2", + "resourceVersion": "2168525640", + "uid": "f845fd72-d6d9-48f2-b0f2-2def6807deb8" + }, + "spec": { + "deploymentName": "example-baz-bar", + "replicas": 42 + } +}