|
7 | 7 | "log/slog" |
8 | 8 | "maps" |
9 | 9 | "os" |
| 10 | + "runtime/debug" |
10 | 11 | "sort" |
11 | 12 | "strconv" |
12 | 13 | "strings" |
@@ -68,6 +69,101 @@ func (d *DefaultNelmActions) ChartRender(ctx context.Context, opts action.ChartR |
68 | 69 | return action.ChartRender(ctx, opts) |
69 | 70 | } |
70 | 71 |
|
| 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 | + |
71 | 167 | func NewNelmClient(opts *CommonOptions, logger *log.Logger, labels map[string]string) *NelmClient { |
72 | 168 | nelmLog.Default = NewNelmLogger(logger) |
73 | 169 |
|
@@ -96,11 +192,16 @@ func NewNelmClient(opts *CommonOptions, logger *log.Logger, labels map[string]st |
96 | 192 |
|
97 | 193 | featgate.FeatCleanNullFields.Enable() |
98 | 194 |
|
| 195 | + nelmLogger := logger.With("operator.component", "nelm") |
| 196 | + |
99 | 197 | 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 | + }, |
104 | 205 | } |
105 | 206 | } |
106 | 207 |
|
|
0 commit comments