Skip to content

Commit 317a7c9

Browse files
authored
Implement calculate diffs and rendering diffs of k8s (#5674)
* Implement the diff list rendering. Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Add test for DiffListResult.Render Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> --------- Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
1 parent 20577c0 commit 317a7c9

File tree

2 files changed

+699
-0
lines changed

2 files changed

+699
-0
lines changed

pkg/app/pipedv1/plugin/kubernetes/provider/diff.go

+144
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
package provider
1616

1717
import (
18+
"fmt"
19+
"sort"
20+
"strings"
21+
1822
"go.uber.org/zap"
1923
v1 "k8s.io/api/core/v1"
2024
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -81,3 +85,143 @@ func normalizeNewSecret(old, new *unstructured.Unstructured) (*unstructured.Unst
8185

8286
return &unstructured.Unstructured{Object: newO}, nil
8387
}
88+
89+
type DiffListResult struct {
90+
Adds []Manifest
91+
Deletes []Manifest
92+
Changes []DiffListChange
93+
}
94+
95+
type DiffListChange struct {
96+
Old Manifest
97+
New Manifest
98+
Diff *diff.Result
99+
}
100+
101+
func (r *DiffListResult) NoChanges() bool {
102+
return len(r.Adds)+len(r.Deletes)+len(r.Changes) == 0
103+
}
104+
105+
func (r *DiffListResult) TotalOutOfSync() int {
106+
return len(r.Adds) + len(r.Deletes) + len(r.Changes)
107+
}
108+
109+
func DiffList(liveManifests, desiredManifests []Manifest, logger *zap.Logger, opts ...diff.Option) (*DiffListResult, error) {
110+
adds, deletes, newChanges, oldChanges := groupManifests(liveManifests, desiredManifests)
111+
result := &DiffListResult{
112+
Adds: adds,
113+
Deletes: deletes,
114+
Changes: make([]DiffListChange, 0, len(newChanges)),
115+
}
116+
117+
for i := 0; i < len(newChanges); i++ {
118+
diffResult, err := Diff(oldChanges[i], newChanges[i], logger, opts...)
119+
if err != nil {
120+
logger.Error("Failed to diff manifests", zap.Error(err))
121+
continue
122+
}
123+
if !diffResult.HasDiff() {
124+
continue
125+
}
126+
result.Changes = append(result.Changes, DiffListChange{
127+
Old: oldChanges[i],
128+
New: newChanges[i],
129+
Diff: diffResult,
130+
})
131+
}
132+
133+
return result, nil
134+
}
135+
136+
func groupManifests(olds, news []Manifest) (adds, deletes, newChanges, oldChanges []Manifest) {
137+
// Sort the manifests before comparing.
138+
sort.Slice(news, func(i, j int) bool {
139+
return news[i].Key().String() < news[j].Key().String()
140+
})
141+
sort.Slice(olds, func(i, j int) bool {
142+
return olds[i].Key().String() < olds[j].Key().String()
143+
})
144+
145+
var n, o int
146+
for {
147+
if n >= len(news) || o >= len(olds) {
148+
break
149+
}
150+
if news[n].Key().String() == olds[o].Key().String() {
151+
newChanges = append(newChanges, news[n])
152+
oldChanges = append(oldChanges, olds[o])
153+
n++
154+
o++
155+
continue
156+
}
157+
// Has in news but not in olds so this should be a added one.
158+
if news[n].Key().String() < olds[o].Key().String() {
159+
adds = append(adds, news[n])
160+
n++
161+
continue
162+
}
163+
// Has in olds but not in news so this should be an deleted one.
164+
deletes = append(deletes, olds[o])
165+
o++
166+
}
167+
168+
if len(news) > n {
169+
adds = append(adds, news[n:]...)
170+
}
171+
if len(olds) > o {
172+
deletes = append(deletes, olds[o:]...)
173+
}
174+
return adds, deletes, newChanges, oldChanges
175+
}
176+
177+
type DiffRenderOptions struct {
178+
MaskSecret bool
179+
MaskConfigMap bool
180+
// Maximum number of changed manifests should be shown.
181+
// Zero means rendering all.
182+
MaxChangedManifests int
183+
}
184+
185+
func (r *DiffListResult) Render(opt DiffRenderOptions) string {
186+
var b strings.Builder
187+
index := 0
188+
for _, delete := range r.Deletes {
189+
index++
190+
b.WriteString(fmt.Sprintf("- %d. %s\n\n", index, delete.Key().ReadableString()))
191+
}
192+
for _, add := range r.Adds {
193+
index++
194+
b.WriteString(fmt.Sprintf("+ %d. %s\n\n", index, add.Key().ReadableString()))
195+
}
196+
197+
maxPrintDiffs := len(r.Changes)
198+
if opt.MaxChangedManifests != 0 && opt.MaxChangedManifests < maxPrintDiffs {
199+
maxPrintDiffs = opt.MaxChangedManifests
200+
}
201+
202+
for _, change := range r.Changes[:maxPrintDiffs] {
203+
key := change.Old.Key()
204+
opts := []diff.RenderOption{
205+
diff.WithLeftPadding(1),
206+
}
207+
208+
if opt.MaskSecret && change.Old.IsSecret() {
209+
opts = append(opts, diff.WithMaskPath("data"))
210+
} else if opt.MaskConfigMap && change.Old.IsConfigMap() {
211+
opts = append(opts, diff.WithMaskPath("data"))
212+
}
213+
renderer := diff.NewRenderer(opts...)
214+
215+
index++
216+
b.WriteString(fmt.Sprintf("# %d. %s\n\n", index, key.ReadableString()))
217+
218+
b.WriteString(renderer.Render(change.Diff.Nodes()))
219+
b.WriteString("\n")
220+
}
221+
222+
if maxPrintDiffs < len(r.Changes) {
223+
b.WriteString(fmt.Sprintf("... (omitted %d other changed manifests)\n", len(r.Changes)-maxPrintDiffs))
224+
}
225+
226+
return b.String()
227+
}

0 commit comments

Comments
 (0)