Skip to content

Commit 3ea135c

Browse files
author
Evsyukov Denis
authored
feat: remove generation from diff output (#608)
Signed-off-by: Evsyukov Denis <[email protected]>
1 parent 74df397 commit 3ea135c

File tree

2 files changed

+133
-19
lines changed

2 files changed

+133
-19
lines changed

pkg/addon-operator/debug_server.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,42 @@ import (
1515
apierrors "k8s.io/apimachinery/pkg/api/errors"
1616
"k8s.io/cli-runtime/pkg/genericiooptions"
1717
"k8s.io/cli-runtime/pkg/resource"
18-
"k8s.io/kubectl/pkg/cmd/diff"
1918
"k8s.io/kubectl/pkg/scheme"
2019
"k8s.io/utils/exec"
2120

21+
"github.com/flant/addon-operator/pkg/addon-operator/diff"
2222
"github.com/flant/addon-operator/pkg/app"
2323
"github.com/flant/addon-operator/pkg/module_manager/models/modules"
2424
"github.com/flant/addon-operator/pkg/utils"
2525
"github.com/flant/shell-operator/pkg/debug"
2626
"github.com/flant/shell-operator/pkg/hook/types"
2727
)
2828

29-
const filedManagerName = "kubectl-client-side-apply"
29+
const fieldManagerName = "kubectl-client-side-apply"
3030

3131
func (op *AddonOperator) RegisterDebugGlobalRoutes(dbgSrv *debug.Server) {
32-
dbgSrv.RegisterHandler(http.MethodGet, "/global/list.{format:(json|yaml)}", func(_ *http.Request) (interface{}, error) {
33-
return map[string]interface{}{
32+
dbgSrv.RegisterHandler(http.MethodGet, "/global/list.{format:(json|yaml)}", func(_ *http.Request) (any, error) {
33+
return map[string]any{
3434
"globalHooks": op.ModuleManager.GetGlobalHooksNames(),
3535
}, nil
3636
})
3737

38-
dbgSrv.RegisterHandler(http.MethodGet, "/global/values.{format:(json|yaml)}", func(_ *http.Request) (interface{}, error) {
38+
dbgSrv.RegisterHandler(http.MethodGet, "/global/values.{format:(json|yaml)}", func(_ *http.Request) (any, error) {
3939
return op.ModuleManager.GetGlobal().GetValues(false), nil
4040
})
4141

42-
dbgSrv.RegisterHandler(http.MethodGet, "/global/config.{format:(json|yaml)}", func(_ *http.Request) (interface{}, error) {
42+
dbgSrv.RegisterHandler(http.MethodGet, "/global/config.{format:(json|yaml)}", func(_ *http.Request) (any, error) {
4343
return op.ModuleManager.GetGlobal().GetConfigValues(false), nil
4444
})
4545

46-
dbgSrv.RegisterHandler(http.MethodGet, "/global/patches.{format:(json|yaml)}", func(_ *http.Request) (interface{}, error) {
46+
dbgSrv.RegisterHandler(http.MethodGet, "/global/patches.{format:(json|yaml)}", func(_ *http.Request) (any, error) {
4747
return op.ModuleManager.GetGlobal().GetValuesPatches(), nil
4848
})
4949

5050
dbgSrv.RegisterHandler(http.MethodGet, "/global/snapshots.{format:(json|yaml)}",
51-
func(_ *http.Request) (interface{}, error) {
51+
func(_ *http.Request) (any, error) {
5252
kubeHookNames := op.ModuleManager.GetGlobalHooksInOrder(types.OnKubernetesEvent)
53-
snapshots := make(map[string]interface{})
53+
snapshots := make(map[string]any)
5454
for _, hName := range kubeHookNames {
5555
h := op.ModuleManager.GetGlobalHook(hName)
5656
snapshots[hName] = h.GetHookController().SnapshotsDump()
@@ -98,13 +98,13 @@ func (op *AddonOperator) RegisterDebugGraphRoutes(dbgSrv *debug.Server) {
9898
}
9999

100100
func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
101-
dbgSrv.RegisterHandler(http.MethodGet, "/module/list.{format:(json|yaml|text)}", func(_ *http.Request) (interface{}, error) {
101+
dbgSrv.RegisterHandler(http.MethodGet, "/module/list.{format:(json|yaml|text)}", func(_ *http.Request) (any, error) {
102102
mods := op.ModuleManager.GetEnabledModuleNames()
103103
sort.Strings(mods)
104104
return map[string][]string{"enabledModules": mods}, nil
105105
})
106106

107-
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/{type:(config|values)}.{format:(json|yaml)}", func(r *http.Request) (interface{}, error) {
107+
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/{type:(config|values)}.{format:(json|yaml)}", func(r *http.Request) (any, error) {
108108
modName := chi.URLParam(r, "name")
109109
valType := chi.URLParam(r, "type")
110110

@@ -147,7 +147,7 @@ func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
147147
return "no values", nil
148148
})
149149

150-
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/render", func(r *http.Request) (interface{}, error) {
150+
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/render", func(r *http.Request) (any, error) {
151151
modName := chi.URLParam(r, "name")
152152
debugMode, err := strconv.ParseBool(r.URL.Query().Get("debug"))
153153
if err != nil {
@@ -263,7 +263,7 @@ func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
263263
ServerSideApply: true,
264264
ForceConflicts: true,
265265
IOStreams: diffProgram.IOStreams,
266-
FieldManager: filedManagerName,
266+
FieldManager: fieldManagerName,
267267
}
268268

269269
err = differ.Diff(obj, printer, false)
@@ -289,7 +289,7 @@ func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
289289
return "No diff found", nil
290290
})
291291

292-
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/patches.json", func(r *http.Request) (interface{}, error) {
292+
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/patches.json", func(r *http.Request) (any, error) {
293293
modName := chi.URLParam(r, "name")
294294

295295
m := op.ModuleManager.GetModule(modName)
@@ -300,8 +300,8 @@ func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
300300
return m.GetValuesPatches(), nil
301301
})
302302

303-
dbgSrv.RegisterHandler(http.MethodGet, "/module/resource-monitor.{format:(json|yaml)}", func(_ *http.Request) (interface{}, error) {
304-
dump := map[string]interface{}{}
303+
dbgSrv.RegisterHandler(http.MethodGet, "/module/resource-monitor.{format:(json|yaml)}", func(_ *http.Request) (any, error) {
304+
dump := map[string]any{}
305305

306306
for _, moduleName := range op.ModuleManager.GetEnabledModuleNames() {
307307
if !op.HelmResourcesManager.HasMonitor(moduleName) {
@@ -316,7 +316,7 @@ func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
316316
return dump, nil
317317
})
318318

319-
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/snapshots.{format:(json|yaml)}", func(r *http.Request) (interface{}, error) {
319+
dbgSrv.RegisterHandler(http.MethodGet, "/module/{name}/snapshots.{format:(json|yaml)}", func(r *http.Request) (any, error) {
320320
modName := chi.URLParam(r, "name")
321321

322322
m := op.ModuleManager.GetModule(modName)
@@ -325,7 +325,7 @@ func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
325325
}
326326

327327
mHooks := m.GetHooks()
328-
snapshots := make(map[string]interface{})
328+
snapshots := make(map[string]any)
329329
for _, h := range mHooks {
330330
snapshots[h.GetName()] = h.GetHookController().SnapshotsDump()
331331
}
@@ -335,7 +335,7 @@ func (op *AddonOperator) RegisterDebugModuleRoutes(dbgSrv *debug.Server) {
335335
}
336336

337337
func (op *AddonOperator) RegisterDiscoveryRoute(dbgSrv *debug.Server) {
338-
dbgSrv.RegisterHandler(http.MethodGet, "/discovery", func(_ *http.Request) (interface{}, error) {
338+
dbgSrv.RegisterHandler(http.MethodGet, "/discovery", func(_ *http.Request) (any, error) {
339339
buf := bytes.NewBuffer(nil)
340340
walkFn := func(
341341
method string,

pkg/addon-operator/diff/diff.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package diff
2+
3+
import (
4+
"io"
5+
6+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
7+
"k8s.io/apimachinery/pkg/runtime"
8+
kubectldiff "k8s.io/kubectl/pkg/cmd/diff"
9+
"sigs.k8s.io/yaml"
10+
)
11+
12+
// Re-export types from kubectl diff package
13+
type (
14+
DiffProgram = kubectldiff.DiffProgram
15+
InfoObject = kubectldiff.InfoObject
16+
Object = kubectldiff.Object
17+
)
18+
19+
// Differ wraps the kubectl Differ with custom printer support
20+
type Differ struct {
21+
*kubectldiff.Differ
22+
}
23+
24+
// NewDiffer creates a new Differ instance
25+
func NewDiffer(from, to string) (*Differ, error) {
26+
d, err := kubectldiff.NewDiffer(from, to)
27+
if err != nil {
28+
return nil, err
29+
}
30+
return &Differ{Differ: d}, nil
31+
}
32+
33+
// Diff performs diff with custom printer that removes metadata.generation
34+
func (d *Differ) Diff(obj Object, printer Printer, showManagedFields bool) error {
35+
// Use standard kubectl printer, but modify the object first
36+
kubectlPrinter := kubectldiff.Printer{}
37+
38+
// Create a wrapper object that pre-processes the Live and Merged methods
39+
objWrapper := &objectWrapper{
40+
Object: obj,
41+
printer: printer,
42+
}
43+
44+
return d.Differ.Diff(objWrapper, kubectlPrinter, showManagedFields)
45+
}
46+
47+
// objectWrapper wraps an Object to preprocess objects before printing
48+
type objectWrapper struct {
49+
Object
50+
printer Printer
51+
}
52+
53+
func (ow *objectWrapper) Live() runtime.Object {
54+
obj := ow.Object.Live()
55+
return ow.preprocessObject(obj)
56+
}
57+
58+
func (ow *objectWrapper) Merged() (runtime.Object, error) {
59+
obj, err := ow.Object.Merged()
60+
if err != nil {
61+
return nil, err
62+
}
63+
return ow.preprocessObject(obj), nil
64+
}
65+
66+
func (ow *objectWrapper) preprocessObject(obj runtime.Object) runtime.Object {
67+
if obj == nil {
68+
return obj
69+
}
70+
71+
// Remove metadata.generation field if present
72+
if u, err := toUnstructured(obj); err == nil && u != nil {
73+
unstructured.RemoveNestedField(u.Object, "metadata", "generation")
74+
return u
75+
}
76+
77+
return obj
78+
}
79+
80+
// Printer is used to print an object with custom metadata.generation handling
81+
type Printer struct{}
82+
83+
// Print the object inside the writer w.
84+
// This differs from the standard kubectl diff Printer by removing metadata.generation field
85+
func (p *Printer) Print(obj runtime.Object, w io.Writer) error {
86+
if obj == nil {
87+
return nil
88+
}
89+
90+
// Remove metadata.generation field if present
91+
if u, err := toUnstructured(obj); err == nil && u != nil {
92+
unstructured.RemoveNestedField(u.Object, "metadata", "generation")
93+
obj = u
94+
}
95+
96+
data, err := yaml.Marshal(obj)
97+
if err != nil {
98+
return err
99+
}
100+
_, err = w.Write(data)
101+
return err
102+
}
103+
104+
// toUnstructured converts a runtime.Object into an unstructured.Unstructured object.
105+
func toUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {
106+
if unstr, ok := obj.(*unstructured.Unstructured); ok {
107+
return unstr, nil
108+
}
109+
data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
110+
if err != nil {
111+
return nil, err
112+
}
113+
return &unstructured.Unstructured{Object: data}, nil
114+
}

0 commit comments

Comments
 (0)