Skip to content

Commit 9bfc3e0

Browse files
notandyfwiesel
authored andcommitted
Use zapEncoder for removing stacktrace of reconcilerrors, fixups
This PR modifies the Code of #161 to improve following points: 1. no need for extra error-log since instead of dropping Reconcile Errors, we format them nicely with the Encoder. 2. Function (like rewritten `setErrorCondition`) should not return the errors the've been invoked with - but only return errors if they fail. Also, it's an uneeded roundtrip to return the same error that has been passed by the caller. 3. Introduce `utils.LifecycleEnabledPredicate`, a predicate that will filter event's for hypervisors with LifecycleEnabled == True.
1 parent dde2817 commit 9bfc3e0

File tree

4 files changed

+73
-56
lines changed

4 files changed

+73
-56
lines changed

cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func main() {
9696
opts := ctrlzap.Options{
9797
Development: true,
9898
TimeEncoder: zapcore.ISO8601TimeEncoder,
99-
ZapOpts: []zap.Option{zap.WrapCore(logger.WrapCore)},
99+
Encoder: logger.NewSanitzeReconcileErrorEncoder(zapcore.EncoderConfig{}),
100100
StacktraceLevel: zap.DPanicLevel,
101101
}
102102
opts.BindFlags(flag.CommandLine)

internal/controller/aggregates_controller.go

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737

3838
kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
3939
"github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/openstack"
40+
"github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/utils"
4041
)
4142

4243
const (
@@ -56,27 +57,24 @@ type AggregatesController struct {
5657
// +kubebuilder:rbac:groups=kvm.cloud.sap,resources=hypervisors/status,verbs=get;list;watch;create;update;patch;delete
5758

5859
func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
59-
log := logger.FromContext(ctx).WithName(req.Name)
60-
ctx = logger.IntoContext(ctx, log)
61-
60+
log := logger.FromContext(ctx)
6261
hv := &kvmv1.Hypervisor{}
6362
if err := ac.Get(ctx, req.NamespacedName, hv); err != nil {
6463
return ctrl.Result{}, k8sclient.IgnoreNotFound(err)
6564
}
6665

67-
// apply traits only when lifecycle management is enabled
68-
if !hv.Spec.LifecycleEnabled {
69-
return ctrl.Result{}, nil
70-
}
71-
7266
if slices.Equal(hv.Spec.Aggregates, hv.Status.Aggregates) {
7367
// Nothing to be done
7468
return ctrl.Result{}, nil
7569
}
7670

7771
aggs, err := aggregatesByName(ctx, ac.computeClient)
7872
if err != nil {
79-
return ctrl.Result{}, ac.trackError(ctx, hv, "failed fetching aggregates", err)
73+
err = fmt.Errorf("failed listing aggregates: %w", err)
74+
if err2 := ac.setErrorCondition(ctx, hv, err.Error()); err2 != nil {
75+
return ctrl.Result{}, errors.Join(err, err2)
76+
}
77+
return ctrl.Result{}, err
8078
}
8179

8280
toAdd := Difference(hv.Status.Aggregates, hv.Spec.Aggregates)
@@ -91,8 +89,7 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
9189
if len(toAdd) > 0 {
9290
log.Info("Adding", "aggregates", toAdd)
9391
for item := range slices.Values(toAdd) {
94-
err = addToAggregate(ctx, ac.computeClient, aggs, hv.Name, item, "")
95-
if err != nil {
92+
if err = addToAggregate(ctx, ac.computeClient, aggs, hv.Name, item, ""); err != nil {
9693
errs = append(errs, err)
9794
}
9895
}
@@ -101,15 +98,18 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
10198
if len(toRemove) > 0 {
10299
log.Info("Removing", "aggregates", toRemove)
103100
for item := range slices.Values(toRemove) {
104-
err = removeFromAggregate(ctx, ac.computeClient, aggs, hv.Name, item)
105-
if err != nil {
101+
if err = removeFromAggregate(ctx, ac.computeClient, aggs, hv.Name, item); err != nil {
106102
errs = append(errs, err)
107103
}
108104
}
109105
}
110106

111107
if errs != nil {
112-
return ctrl.Result{}, ac.trackError(ctx, hv, "failed updating aggregates", errs...)
108+
err = fmt.Errorf("encountered errors during aggregate update: %w", errors.Join(errs...))
109+
if err2 := ac.setErrorCondition(ctx, hv, err.Error()); err2 != nil {
110+
return ctrl.Result{}, errors.Join(err, err2)
111+
}
112+
return ctrl.Result{}, err
113113
}
114114

115115
hv.Status.Aggregates = hv.Spec.Aggregates
@@ -122,29 +122,22 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
122122
return ctrl.Result{}, ac.Status().Update(ctx, hv)
123123
}
124124

125-
func (ac *AggregatesController) trackError(ctx context.Context, hv *kvmv1.Hypervisor, msg string, errs ...error) error {
126-
err := errors.Join(errs...)
127-
if err == nil {
128-
return nil
129-
}
130-
125+
// setErrorCondition sets the error condition on the Hypervisor status, returns error if update fails
126+
func (ac *AggregatesController) setErrorCondition(ctx context.Context, hv *kvmv1.Hypervisor, msg string) error {
131127
condition := metav1.Condition{
132128
Type: ConditionTypeAggregatesUpdated,
133129
Status: metav1.ConditionFalse,
134130
Reason: ConditionAggregatesFailed,
135-
Message: err.Error(),
131+
Message: msg,
136132
}
137133

138134
if meta.SetStatusCondition(&hv.Status.Conditions, condition) {
139-
if err2 := ac.Status().Update(ctx, hv); err2 != nil {
140-
return errors.Join(err, err2)
135+
if err := ac.Status().Update(ctx, hv); err != nil {
136+
return err
141137
}
142-
logger.FromContext(ctx).
143-
WithCallDepth(1). // Where did we call trackError() from?
144-
Error(err, msg)
145138
}
146139

147-
return err
140+
return nil
148141
}
149142

150143
// SetupWithManager sets up the controller with the Manager.
@@ -160,7 +153,8 @@ func (ac *AggregatesController) SetupWithManager(mgr ctrl.Manager) error {
160153

161154
return ctrl.NewControllerManagedBy(mgr).
162155
Named(AggregatesControllerName).
163-
For(&kvmv1.Hypervisor{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
156+
For(&kvmv1.Hypervisor{}, builder.WithPredicates(utils.LifecycleEnabledPredicate)).
157+
WithEventFilter(predicate.GenerationChangedPredicate{}).
164158
Complete(ac)
165159
}
166160

internal/logger/logger.go

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,41 +18,30 @@ limitations under the License.
1818
package logger
1919

2020
import (
21+
"go.uber.org/zap/buffer"
2122
"go.uber.org/zap/zapcore"
2223
)
2324

24-
type wrapCore struct {
25-
core zapcore.Core
25+
func NewSanitzeReconcileErrorEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder {
26+
return &SanitzeReconcileErrorEncoder{zapcore.NewConsoleEncoder(cfg), cfg}
2627
}
2728

28-
func WrapCore(core zapcore.Core) zapcore.Core {
29-
return wrapCore{core}
29+
type SanitzeReconcileErrorEncoder struct {
30+
zapcore.Encoder
31+
cfg zapcore.EncoderConfig
3032
}
3133

32-
// Check implements zapcore.Core.
33-
func (w wrapCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry {
34-
if entry.Message == "Reconciler error" {
35-
entry.Level = -2
34+
func (e *SanitzeReconcileErrorEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
35+
if entry.Message == "Reconcile error" {
36+
// Downgrade the log level to debug to avoid log spam
37+
entry.Level = zapcore.WarnLevel
38+
entry.Stack = ""
3639
}
37-
return w.core.Check(entry, checkedEntry)
40+
return e.Encoder.EncodeEntry(entry, fields)
3841
}
3942

40-
// Enabled implements zapcore.Core.
41-
func (w wrapCore) Enabled(level zapcore.Level) bool {
42-
return w.core.Enabled(level)
43-
}
44-
45-
// Sync implements zapcore.Core.
46-
func (w wrapCore) Sync() error {
47-
return w.core.Sync()
48-
}
49-
50-
// With implements zapcore.Core.
51-
func (w wrapCore) With(fields []zapcore.Field) zapcore.Core {
52-
return wrapCore{w.core.With(fields)}
53-
}
54-
55-
// Write implements zapcore.Core.
56-
func (w wrapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
57-
return w.core.Write(entry, fields)
43+
func (e *SanitzeReconcileErrorEncoder) Clone() zapcore.Encoder {
44+
return &SanitzeReconcileErrorEncoder{
45+
Encoder: e.Encoder.Clone(),
46+
}
5847
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
SPDX-FileCopyrightText: Copyright 2025 SAP SE or an SAP affiliate company and cobaltcore-dev contributors
3+
SPDX-License-Identifier: Apache-2.0
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package utils
19+
20+
import (
21+
"sigs.k8s.io/controller-runtime/pkg/client"
22+
"sigs.k8s.io/controller-runtime/pkg/predicate"
23+
24+
kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
25+
)
26+
27+
var (
28+
LifecycleEnabledPredicate = predicate.NewPredicateFuncs(func(object client.Object) bool {
29+
if hv, ok := object.(*kvmv1.Hypervisor); ok {
30+
return hv.Spec.LifecycleEnabled
31+
}
32+
return true
33+
})
34+
)

0 commit comments

Comments
 (0)