Skip to content

Commit dde2817

Browse files
committed
Aggregates: Use GenerationChangedPredicate and join errors
Prior to this change, we would have to exit the reconcile function on each status update, and continue there. Now with the filter, we only retry on either an error or RequeueAfter. Joining the errors allows us to complete at least some of the changes, and not always abort on the first. Return kubernetes errors directly, and return a RequeueAfter on an error from Openstack for now, until the logging situation has been cleared.
1 parent 9232c4e commit dde2817

File tree

1 file changed

+42
-22
lines changed

1 file changed

+42
-22
lines changed

internal/controller/aggregates_controller.go

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@ package controller
1919

2020
import (
2121
"context"
22+
"errors"
2223
"fmt"
2324
"slices"
2425

2526
"k8s.io/apimachinery/pkg/api/meta"
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"k8s.io/apimachinery/pkg/runtime"
2829
ctrl "sigs.k8s.io/controller-runtime"
30+
"sigs.k8s.io/controller-runtime/pkg/builder"
2931
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
3032
logger "sigs.k8s.io/controller-runtime/pkg/log"
33+
"sigs.k8s.io/controller-runtime/pkg/predicate"
3134

3235
"github.com/gophercloud/gophercloud/v2"
3336
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates"
@@ -73,30 +76,24 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
7376

7477
aggs, err := aggregatesByName(ctx, ac.computeClient)
7578
if err != nil {
76-
meta.SetStatusCondition(&hv.Status.Conditions, metav1.Condition{
77-
Type: ConditionTypeAggregatesUpdated,
78-
Status: metav1.ConditionFalse,
79-
Reason: ConditionAggregatesFailed,
80-
Message: err.Error(),
81-
})
82-
return ctrl.Result{}, ac.Status().Update(ctx, hv)
79+
return ctrl.Result{}, ac.trackError(ctx, hv, "failed fetching aggregates", err)
8380
}
8481

8582
toAdd := Difference(hv.Status.Aggregates, hv.Spec.Aggregates)
8683
toRemove := Difference(hv.Spec.Aggregates, hv.Status.Aggregates)
8784

85+
// We need to add first the host to the aggregates, because if we first drop
86+
// an aggregate with a filter criterion and then add a new one, we leave the host
87+
// open for period of time. Still, this may fail due to a conflict of aggregates
88+
// with different availability zones, so we collect all the errors and return them
89+
// so it hopefully will converge eventually.
90+
var errs []error
8891
if len(toAdd) > 0 {
8992
log.Info("Adding", "aggregates", toAdd)
9093
for item := range slices.Values(toAdd) {
9194
err = addToAggregate(ctx, ac.computeClient, aggs, hv.Name, item, "")
9295
if err != nil {
93-
meta.SetStatusCondition(&hv.Status.Conditions, metav1.Condition{
94-
Type: ConditionTypeAggregatesUpdated,
95-
Status: metav1.ConditionFalse,
96-
Reason: ConditionAggregatesFailed,
97-
Message: err.Error(),
98-
})
99-
return ctrl.Result{}, ac.Status().Update(ctx, hv)
96+
errs = append(errs, err)
10097
}
10198
}
10299
}
@@ -106,17 +103,15 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
106103
for item := range slices.Values(toRemove) {
107104
err = removeFromAggregate(ctx, ac.computeClient, aggs, hv.Name, item)
108105
if err != nil {
109-
meta.SetStatusCondition(&hv.Status.Conditions, metav1.Condition{
110-
Type: ConditionTypeAggregatesUpdated,
111-
Status: metav1.ConditionFalse,
112-
Reason: ConditionAggregatesFailed,
113-
Message: err.Error(),
114-
})
115-
return ctrl.Result{}, ac.Status().Update(ctx, hv)
106+
errs = append(errs, err)
116107
}
117108
}
118109
}
119110

111+
if errs != nil {
112+
return ctrl.Result{}, ac.trackError(ctx, hv, "failed updating aggregates", errs...)
113+
}
114+
120115
hv.Status.Aggregates = hv.Spec.Aggregates
121116
meta.SetStatusCondition(&hv.Status.Conditions, metav1.Condition{
122117
Type: ConditionTypeAggregatesUpdated,
@@ -127,6 +122,31 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
127122
return ctrl.Result{}, ac.Status().Update(ctx, hv)
128123
}
129124

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+
131+
condition := metav1.Condition{
132+
Type: ConditionTypeAggregatesUpdated,
133+
Status: metav1.ConditionFalse,
134+
Reason: ConditionAggregatesFailed,
135+
Message: err.Error(),
136+
}
137+
138+
if meta.SetStatusCondition(&hv.Status.Conditions, condition) {
139+
if err2 := ac.Status().Update(ctx, hv); err2 != nil {
140+
return errors.Join(err, err2)
141+
}
142+
logger.FromContext(ctx).
143+
WithCallDepth(1). // Where did we call trackError() from?
144+
Error(err, msg)
145+
}
146+
147+
return err
148+
}
149+
130150
// SetupWithManager sets up the controller with the Manager.
131151
func (ac *AggregatesController) SetupWithManager(mgr ctrl.Manager) error {
132152
ctx := context.Background()
@@ -140,7 +160,7 @@ func (ac *AggregatesController) SetupWithManager(mgr ctrl.Manager) error {
140160

141161
return ctrl.NewControllerManagedBy(mgr).
142162
Named(AggregatesControllerName).
143-
For(&kvmv1.Hypervisor{}).
163+
For(&kvmv1.Hypervisor{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
144164
Complete(ac)
145165
}
146166

0 commit comments

Comments
 (0)