diff --git a/srv6egress/cmd/bgp-controller/main.go b/srv6egress/cmd/bgp-controller/main.go index b543d8a5..77b5526b 100644 --- a/srv6egress/cmd/bgp-controller/main.go +++ b/srv6egress/cmd/bgp-controller/main.go @@ -98,6 +98,16 @@ func main() { var encoder bgp.Encoder switch bgpEncoding { case "sr-policy": + // The headend keys its SR Policy install on the BSID, so under this + // encoding every color needs one. Fail fast at load instead of letting + // a bsid-less color go non-Ready yet un-deletable (BuildPath would fail + // on both Announce and Withdraw). + for color, cc := range cfg.Colors { + if cc.BSID == "" { + log.Error(fmt.Errorf("color %d: bsid is required with --bgp-encoding=sr-policy", color), "invalid config") + os.Exit(1) + } + } encoder = bgp.NewSRPolicyEncoder(bgp.SRPolicyOptions{}) case "color-route": encoder = bgp.NewColoredEncoder() diff --git a/srv6egress/internal/controller/egresspolicy_controller.go b/srv6egress/internal/controller/egresspolicy_controller.go index b3ea91ee..306206a7 100644 --- a/srv6egress/internal/controller/egresspolicy_controller.go +++ b/srv6egress/internal/controller/egresspolicy_controller.go @@ -226,7 +226,15 @@ func (r *EgressPolicyReconciler) reconcileDelete(ctx context.Context, ep *srv6eg EndpointAddr: sp.EndpointAddr, BSID: sp.BSID, } - if err := r.BGP.Withdraw(ctx, owner, r.Encoder.ClusterAdvert(key, sp.SegmentList)); err != nil { + adv := r.Encoder.ClusterAdvert(key, sp.SegmentList) + if _, err := adv.BuildPath(); err != nil { + // Deterministic encode failure (e.g. sr-policy with an empty + // BSID/endpoint): nothing valid could have been announced, so there + // is nothing to withdraw. Log and drop the finalizer rather than + // wedging the object in Terminating forever on every reconcile. + log.Error(err, "cannot rebuild withdraw advert; dropping finalizer without withdraw", "name", ep.Name) + } else if err := r.BGP.Withdraw(ctx, owner, adv); err != nil { + // Transient transport error: keep the finalizer and retry. return ctrl.Result{}, err } }