@@ -14,10 +14,15 @@ import (
1414 "k8s.io/apimachinery/pkg/runtime"
1515 kerrors "k8s.io/apimachinery/pkg/util/errors"
1616 "k8s.io/client-go/tools/record"
17+ "k8s.io/klog/v2"
1718 ctrl "sigs.k8s.io/controller-runtime"
19+ "sigs.k8s.io/controller-runtime/pkg/builder"
1820 "sigs.k8s.io/controller-runtime/pkg/client"
1921 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
22+ "sigs.k8s.io/controller-runtime/pkg/event"
23+ "sigs.k8s.io/controller-runtime/pkg/handler"
2024 "sigs.k8s.io/controller-runtime/pkg/predicate"
25+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
2126
2227 "github.com/ironcore-dev/network-operator/api/core/v1alpha1"
2328 "github.com/ironcore-dev/network-operator/internal/conditions"
@@ -169,8 +174,10 @@ func (r *RoutingPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Reques
169174 return ctrl.Result {}, nil
170175}
171176
177+ var routingPolicyPrefixSetRefKey = ".spec.statements[].conditions.matchPrefixSet.prefixSetRef.name"
178+
172179// SetupWithManager sets up the controller with the Manager.
173- func (r * RoutingPolicyReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
180+ func (r * RoutingPolicyReconciler ) SetupWithManager (ctx context. Context , mgr ctrl.Manager ) error {
174181 labelSelector := metav1.LabelSelector {}
175182 if r .WatchFilterValue != "" {
176183 labelSelector .MatchLabels = map [string ]string {v1alpha1 .WatchLabel : r .WatchFilterValue }
@@ -181,10 +188,37 @@ func (r *RoutingPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
181188 return fmt .Errorf ("failed to create label selector predicate: %w" , err )
182189 }
183190
191+ if err := mgr .GetFieldIndexer ().IndexField (ctx , & v1alpha1.RoutingPolicy {}, routingPolicyPrefixSetRefKey , func (obj client.Object ) []string {
192+ rp := obj .(* v1alpha1.RoutingPolicy )
193+ var names []string
194+ for _ , stmt := range rp .Spec .Statements {
195+ if stmt .Conditions != nil && stmt .Conditions .MatchPrefixSet != nil {
196+ names = append (names , stmt .Conditions .MatchPrefixSet .PrefixSetRef .Name )
197+ }
198+ }
199+ return names
200+ }); err != nil {
201+ return err
202+ }
203+
184204 return ctrl .NewControllerManagedBy (mgr ).
185205 For (& v1alpha1.RoutingPolicy {}).
186206 Named ("routingpolicy" ).
187207 WithEventFilter (filter ).
208+ // Watches enqueues RoutingPolicies for updates in referenced PrefixSet resources.
209+ // Only triggers on create and delete events since PrefixSet names are immutable.
210+ Watches (
211+ & v1alpha1.PrefixSet {},
212+ handler .EnqueueRequestsFromMapFunc (r .prefixSetToRoutingPolicy ),
213+ builder .WithPredicates (predicate.Funcs {
214+ UpdateFunc : func (e event.UpdateEvent ) bool {
215+ return false
216+ },
217+ GenericFunc : func (e event.GenericEvent ) bool {
218+ return false
219+ },
220+ }),
221+ ).
188222 Complete (r )
189223}
190224
@@ -211,6 +245,11 @@ func (r *RoutingPolicyReconciler) reconcile(ctx context.Context, s *routingPolic
211245 }
212246 }
213247
248+ statements , err := r .reconcileStatements (ctx , s )
249+ if err != nil {
250+ return err
251+ }
252+
214253 if err := s .Provider .Connect (ctx , s .Connection ); err != nil {
215254 return fmt .Errorf ("failed to connect to provider: %w" , err )
216255 }
@@ -221,8 +260,9 @@ func (r *RoutingPolicyReconciler) reconcile(ctx context.Context, s *routingPolic
221260 }()
222261
223262 // Ensure the RoutingPolicy is realized on the provider.
224- err := s .Provider .EnsureRoutingPolicy (ctx , & provider.RoutingPolicyRequest {
225- RoutingPolicy : s .RoutingPolicy ,
263+ err = s .Provider .EnsureRoutingPolicy (ctx , & provider.EnsureRoutingPolicyRequest {
264+ Name : s .RoutingPolicy .Spec .Name ,
265+ Statements : statements ,
226266 ProviderConfig : s .ProviderConfig ,
227267 })
228268
@@ -234,6 +274,66 @@ func (r *RoutingPolicyReconciler) reconcile(ctx context.Context, s *routingPolic
234274 return err
235275}
236276
277+ func (r * RoutingPolicyReconciler ) reconcileStatements (ctx context.Context , s * routingPolicyScope ) ([]provider.PolicyStatement , error ) {
278+ statements := make ([]provider.PolicyStatement , 0 , len (s .RoutingPolicy .Spec .Statements ))
279+
280+ for _ , stmt := range s .RoutingPolicy .Spec .Statements {
281+ var conditions []provider.PolicyCondition
282+ switch {
283+ case stmt .Conditions != nil && stmt .Conditions .MatchPrefixSet != nil :
284+ prefixSet , err := r .reconcilePrefixSet (ctx , s , stmt .Conditions .MatchPrefixSet )
285+ if err != nil {
286+ return nil , err
287+ }
288+ conditions = append (conditions , provider.MatchPrefixSetCondition {
289+ PrefixSet : prefixSet ,
290+ })
291+ }
292+
293+ statements = append (statements , provider.PolicyStatement {
294+ Sequence : stmt .Sequence ,
295+ Conditions : conditions ,
296+ Actions : stmt .Actions ,
297+ })
298+ }
299+
300+ return statements , nil
301+ }
302+
303+ // reconcilePrefixSet ensures that the referenced PrefixSet exists and belongs to the same device as the RoutingPolicy.
304+ func (r * RoutingPolicyReconciler ) reconcilePrefixSet (ctx context.Context , s * routingPolicyScope , c * v1alpha1.PrefixSetMatchCondition ) (* v1alpha1.PrefixSet , error ) {
305+ key := client.ObjectKey {
306+ Name : c .PrefixSetRef .Name ,
307+ Namespace : s .RoutingPolicy .Namespace ,
308+ }
309+
310+ prefixSet := new (v1alpha1.PrefixSet )
311+ if err := r .Get (ctx , key , prefixSet ); err != nil {
312+ if apierrors .IsNotFound (err ) {
313+ conditions .Set (s .RoutingPolicy , metav1.Condition {
314+ Type : v1alpha1 .ReadyCondition ,
315+ Status : metav1 .ConditionFalse ,
316+ Reason : v1alpha1 .PrefixSetNotFoundReason ,
317+ Message : fmt .Sprintf ("referenced PrefixSet %q not found" , key ),
318+ })
319+ return nil , reconcile .TerminalError (fmt .Errorf ("referenced PrefixSet %q not found" , key ))
320+ }
321+ return nil , fmt .Errorf ("failed to get referenced PrefixSet %q: %w" , key , err )
322+ }
323+
324+ if prefixSet .Spec .DeviceRef .Name != s .Device .Name {
325+ conditions .Set (s .RoutingPolicy , metav1.Condition {
326+ Type : v1alpha1 .ReadyCondition ,
327+ Status : metav1 .ConditionFalse ,
328+ Reason : v1alpha1 .CrossDeviceReferenceReason ,
329+ Message : fmt .Sprintf ("referenced PrefixSet %q does not belong to device %q" , prefixSet .Name , s .Device .Name ),
330+ })
331+ return nil , reconcile .TerminalError (fmt .Errorf ("referenced PrefixSet %q does not belong to device %q" , prefixSet .Name , s .Device .Name ))
332+ }
333+
334+ return prefixSet , nil
335+ }
336+
237337func (r * RoutingPolicyReconciler ) finalize (ctx context.Context , s * routingPolicyScope ) (reterr error ) {
238338 if err := s .Provider .Connect (ctx , s .Connection ); err != nil {
239339 return fmt .Errorf ("failed to connect to provider: %w" , err )
@@ -244,8 +344,42 @@ func (r *RoutingPolicyReconciler) finalize(ctx context.Context, s *routingPolicy
244344 }
245345 }()
246346
247- return s .Provider .DeleteRoutingPolicy (ctx , & provider.RoutingPolicyRequest {
248- RoutingPolicy : s .RoutingPolicy ,
249- ProviderConfig : s .ProviderConfig ,
347+ return s .Provider .DeleteRoutingPolicy (ctx , & provider.DeleteRoutingPolicyRequest {
348+ Name : s .RoutingPolicy .Spec .Name ,
250349 })
251350}
351+
352+ // prefixSetToRoutingPolicy is a [handler.MapFunc] to be used to enqueue requests for reconciliation
353+ // for RoutingPolicies when their referenced PrefixSet changes.
354+ func (r * RoutingPolicyReconciler ) prefixSetToRoutingPolicy (ctx context.Context , obj client.Object ) []ctrl.Request {
355+ prefixSet , ok := obj .(* v1alpha1.PrefixSet )
356+ if ! ok {
357+ panic (fmt .Sprintf ("Expected a PrefixSet but got a %T" , obj ))
358+ }
359+
360+ log := ctrl .LoggerFrom (ctx , "PrefixSet" , klog .KObj (prefixSet ))
361+
362+ routingPolicies := new (v1alpha1.RoutingPolicyList )
363+ if err := r .List (ctx , routingPolicies , client .InNamespace (prefixSet .Namespace ), client.MatchingFields {routingPolicyPrefixSetRefKey : prefixSet .Spec .Name }); err != nil {
364+ log .Error (err , "Failed to list RoutingPolicies" )
365+ return nil
366+ }
367+
368+ requests := []ctrl.Request {}
369+ for _ , rp := range routingPolicies .Items {
370+ for _ , stmt := range rp .Spec .Statements {
371+ if stmt .Conditions != nil && stmt .Conditions .MatchPrefixSet != nil && stmt .Conditions .MatchPrefixSet .PrefixSetRef .Name == prefixSet .Spec .Name {
372+ log .Info ("Enqueuing RoutingPolicy for reconciliation" , "RoutingPolicy" , klog .KObj (& rp ))
373+ requests = append (requests , ctrl.Request {
374+ NamespacedName : client.ObjectKey {
375+ Name : rp .Name ,
376+ Namespace : rp .Namespace ,
377+ },
378+ })
379+ break
380+ }
381+ }
382+ }
383+
384+ return requests
385+ }
0 commit comments