@@ -19,15 +19,18 @@ package controller
1919
2020import (
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"
@@ -72,31 +75,25 @@ func (ac *AggregatesController) Reconcile(ctx context.Context, req ctrl.Request)
7275 }
7376
7477 aggs , err := aggregatesByName (ctx , ac .computeClient )
75- 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 )
78+ if err := ac .trackError (ctx , hv , "failed fetching aggregates" , err ); err != nil {
79+ return ctrl.Result {}, 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 err := ac .trackError (ctx , hv , "failed updating aggregates" , errs ... ); err != nil {
112+ return ctrl.Result {}, err
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.
131151func (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