Skip to content

Commit a44cbb3

Browse files
[nelm] Implement SafeNelmActions for panic recovery (#707)
Signed-off-by: Roman Berezkin <[email protected]>
1 parent b995f12 commit a44cbb3

File tree

1 file changed

+105
-4
lines changed

1 file changed

+105
-4
lines changed

pkg/helm/nelm/nelm.go

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"log/slog"
88
"maps"
99
"os"
10+
"runtime/debug"
1011
"sort"
1112
"strconv"
1213
"strings"
@@ -68,6 +69,101 @@ func (d *DefaultNelmActions) ChartRender(ctx context.Context, opts action.ChartR
6869
return action.ChartRender(ctx, opts)
6970
}
7071

72+
// SafeNelmActions wraps NelmActions and provides panic recovery for all action calls.
73+
type SafeNelmActions struct {
74+
wrapped NelmActions
75+
logger *log.Logger
76+
}
77+
78+
//nolint:nonamedreturns // named returns required for defer/recover to modify return values
79+
func (s *SafeNelmActions) ReleaseGet(ctx context.Context, name, namespace string, opts action.ReleaseGetOptions) (result *action.ReleaseGetResultV1, err error) {
80+
defer func() {
81+
if r := recover(); r != nil {
82+
s.logger.Error("panic in ReleaseGet",
83+
slog.Any("panic", r),
84+
slog.String("release", name),
85+
slog.String("namespace", namespace),
86+
slog.Int("revision", opts.Revision),
87+
slog.String("stack", string(debug.Stack())),
88+
)
89+
err = fmt.Errorf("panic in ReleaseGet: %v", r)
90+
}
91+
}()
92+
return s.wrapped.ReleaseGet(ctx, name, namespace, opts)
93+
}
94+
95+
func (s *SafeNelmActions) ReleaseInstall(ctx context.Context, name, namespace string, opts action.ReleaseInstallOptions) (err error) {
96+
defer func() {
97+
if r := recover(); r != nil {
98+
s.logger.Error("panic in ReleaseInstall",
99+
slog.Any("panic", r),
100+
slog.String("release", name),
101+
slog.String("namespace", namespace),
102+
slog.String("chart", opts.Chart),
103+
slog.String("default_chart_name", opts.DefaultChartName),
104+
slog.Bool("force_adoption", opts.ForceAdoption),
105+
slog.Bool("auto_rollback", opts.AutoRollback),
106+
slog.Int("values_files_count", len(opts.ValuesFiles)),
107+
slog.Int("extra_labels_count", len(opts.ExtraLabels)),
108+
slog.String("stack", string(debug.Stack())),
109+
)
110+
err = fmt.Errorf("panic in ReleaseInstall: %v", r)
111+
}
112+
}()
113+
return s.wrapped.ReleaseInstall(ctx, name, namespace, opts)
114+
}
115+
116+
func (s *SafeNelmActions) ReleaseUninstall(ctx context.Context, name, namespace string, opts action.ReleaseUninstallOptions) (err error) {
117+
defer func() {
118+
if r := recover(); r != nil {
119+
s.logger.Error("panic in ReleaseUninstall",
120+
slog.Any("panic", r),
121+
slog.String("release", name),
122+
slog.String("namespace", namespace),
123+
slog.String("delete_propagation", opts.DefaultDeletePropagation),
124+
slog.Bool("delete_release_namespace", opts.DeleteReleaseNamespace),
125+
slog.String("stack", string(debug.Stack())),
126+
)
127+
err = fmt.Errorf("panic in ReleaseUninstall: %v", r)
128+
}
129+
}()
130+
return s.wrapped.ReleaseUninstall(ctx, name, namespace, opts)
131+
}
132+
133+
//nolint:nonamedreturns // named returns required for defer/recover to modify return values
134+
func (s *SafeNelmActions) ReleaseList(ctx context.Context, opts action.ReleaseListOptions) (result *action.ReleaseListResultV1, err error) {
135+
defer func() {
136+
if r := recover(); r != nil {
137+
s.logger.Error("panic in ReleaseList",
138+
slog.Any("panic", r),
139+
slog.String("namespace", opts.ReleaseNamespace),
140+
slog.String("stack", string(debug.Stack())),
141+
)
142+
err = fmt.Errorf("panic in ReleaseList: %v", r)
143+
}
144+
}()
145+
return s.wrapped.ReleaseList(ctx, opts)
146+
}
147+
148+
//nolint:nonamedreturns // named returns required for defer/recover to modify return values
149+
func (s *SafeNelmActions) ChartRender(ctx context.Context, opts action.ChartRenderOptions) (result *action.ChartRenderResultV2, err error) {
150+
defer func() {
151+
if r := recover(); r != nil {
152+
s.logger.Error("panic in ChartRender",
153+
slog.Any("panic", r),
154+
slog.String("chart", opts.Chart),
155+
slog.String("release", opts.ReleaseName),
156+
slog.String("namespace", opts.ReleaseNamespace),
157+
slog.Bool("remote", opts.Remote),
158+
slog.Int("values_files_count", len(opts.ValuesFiles)),
159+
slog.String("stack", string(debug.Stack())),
160+
)
161+
err = fmt.Errorf("panic in ChartRender: %v", r)
162+
}
163+
}()
164+
return s.wrapped.ChartRender(ctx, opts)
165+
}
166+
71167
func NewNelmClient(opts *CommonOptions, logger *log.Logger, labels map[string]string) *NelmClient {
72168
nelmLog.Default = NewNelmLogger(logger)
73169

@@ -96,11 +192,16 @@ func NewNelmClient(opts *CommonOptions, logger *log.Logger, labels map[string]st
96192

97193
featgate.FeatCleanNullFields.Enable()
98194

195+
nelmLogger := logger.With("operator.component", "nelm")
196+
99197
return &NelmClient{
100-
logger: logger.With("operator.component", "nelm"),
101-
labels: clientLabels,
102-
opts: opts,
103-
actions: &DefaultNelmActions{},
198+
logger: nelmLogger,
199+
labels: clientLabels,
200+
opts: opts,
201+
actions: &SafeNelmActions{
202+
wrapped: &DefaultNelmActions{},
203+
logger: nelmLogger,
204+
},
104205
}
105206
}
106207

0 commit comments

Comments
 (0)