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  }