github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconciler/preflight.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package reconciler 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/cilium/hive/cell" 11 "github.com/sirupsen/logrus" 12 13 "github.com/cilium/cilium/pkg/bgpv1/agent" 14 "github.com/cilium/cilium/pkg/bgpv1/manager/instance" 15 "github.com/cilium/cilium/pkg/bgpv1/types" 16 ) 17 18 type PreflightReconcilerOut struct { 19 cell.Out 20 21 Reconciler ConfigReconciler `group:"bgp-config-reconciler"` 22 } 23 24 // PreflightReconciler is a preflight task before any other reconciliation should 25 // take place. 26 // 27 // this reconciler handles any changes in current and desired BgpState which leads 28 // to a recreation of an existing BgpServer. 29 // 30 // this must be done first so that the following reconciliation functions act 31 // upon the recreated BgpServer with the desired permanent configurations. 32 // 33 // permanent configurations for BgpServers (ones that cannot be changed after creation) 34 // are router ID and local listening port. 35 type PreflightReconciler struct{} 36 37 func NewPreflightReconciler() PreflightReconcilerOut { 38 return PreflightReconcilerOut{ 39 Reconciler: &PreflightReconciler{}, 40 } 41 } 42 43 func (r *PreflightReconciler) Name() string { 44 return "Preflight" 45 } 46 47 func (r *PreflightReconciler) Priority() int { 48 return 10 49 } 50 51 func (r *PreflightReconciler) Init(_ *instance.ServerWithConfig) error { 52 return nil 53 } 54 55 func (r *PreflightReconciler) Cleanup(_ *instance.ServerWithConfig) {} 56 57 func (r *PreflightReconciler) Reconcile(ctx context.Context, p ReconcileParams) error { 58 var ( 59 l = log.WithFields( 60 logrus.Fields{ 61 "component": "PreflightReconciler", 62 }, 63 ) 64 ) 65 66 // If we have no config attached, we don't need to perform a preflight for 67 // reconciliation. 68 // 69 // This is the first time this server is being registered and BGPRouterManager 70 // set any fields needing reconciliation in this function already. 71 if p.CurrentServer.Config == nil { 72 l.Debugf("Preflight for virtual router with ASN %v not necessary, first instantiation of this BgpServer.", p.DesiredConfig.LocalASN) 73 return nil 74 } 75 76 l.Debugf("Begin preflight reoncilation for virtual router with ASN %v", p.DesiredConfig.LocalASN) 77 bgpInfo, err := p.CurrentServer.Server.GetBGP(ctx) 78 if err != nil { 79 return fmt.Errorf("failed to retrieve BgpServer info for virtual router with ASN %v: %w", p.DesiredConfig.LocalASN, err) 80 } 81 82 // parse Node annotations into helper Annotation map 83 annoMap, err := agent.NewAnnotationMap(p.CiliumNode.Annotations) 84 if err != nil { 85 return fmt.Errorf("failed to parse CiliumNode annotations for virtual router with ASN %v: %w", p.DesiredConfig.LocalASN, err) 86 } 87 88 // resolve local port from kubernetes annotations 89 var localPort int32 90 localPort = -1 91 if attrs, ok := annoMap[p.DesiredConfig.LocalASN]; ok { 92 if attrs.LocalPort != 0 { 93 localPort = int32(attrs.LocalPort) 94 } 95 } 96 97 routerID, err := annoMap.ResolveRouterID(p.DesiredConfig.LocalASN) 98 if err != nil { 99 if nodeIP := p.CiliumNode.GetIP(false); nodeIP == nil { 100 return fmt.Errorf("failed to get ciliumnode IP %v: %w", nodeIP, err) 101 } else { 102 routerID = nodeIP.String() 103 } 104 } 105 106 var shouldRecreate bool 107 if localPort != bgpInfo.Global.ListenPort { 108 shouldRecreate = true 109 l.Infof("Virtual router with ASN %v local port has changed from %v to %v", p.DesiredConfig.LocalASN, bgpInfo.Global.ListenPort, localPort) 110 } 111 if routerID != bgpInfo.Global.RouterID { 112 shouldRecreate = true 113 l.Infof("Virtual router with ASN %v router ID has changed from %v to %v", p.DesiredConfig.LocalASN, bgpInfo.Global.RouterID, routerID) 114 } 115 if !shouldRecreate { 116 l.Debugf("No preflight reconciliation necessary for virtual router with local ASN %v", p.DesiredConfig.LocalASN) 117 return nil 118 } 119 120 l.Infof("Recreating virtual router with ASN %v for changes to take effect", p.DesiredConfig.LocalASN) 121 globalConfig := types.ServerParameters{ 122 Global: types.BGPGlobal{ 123 ASN: uint32(p.DesiredConfig.LocalASN), 124 RouterID: routerID, 125 ListenPort: localPort, 126 RouteSelectionOptions: &types.RouteSelectionOptions{ 127 AdvertiseInactiveRoutes: true, 128 }, 129 }, 130 } 131 132 // stop the old BgpServer 133 p.CurrentServer.Server.Stop() 134 135 // create a new one via ServerWithConfig constructor 136 s, err := instance.NewServerWithConfig(ctx, log, globalConfig) 137 if err != nil { 138 l.WithError(err).Errorf("Failed to start BGP server for virtual router with local ASN %v", p.DesiredConfig.LocalASN) 139 return fmt.Errorf("failed to start BGP server for virtual router with local ASN %v: %w", p.DesiredConfig.LocalASN, err) 140 } 141 142 // replace the old underlying server with our recreated one 143 p.CurrentServer.Server = s.Server 144 145 // dump the existing config so all subsequent reconcilers perform their 146 // actions as if this is a new BgpServer. 147 p.CurrentServer.Config = nil 148 149 // Clear the shadow state since any advertisements will be gone now that the server has been recreated. 150 p.CurrentServer.ReconcilerMetadata = make(map[string]any) 151 152 return nil 153 }