github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconcilerv2/peer_advertisements.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package reconcilerv2
     5  
     6  import (
     7  	"errors"
     8  	"sort"
     9  
    10  	"github.com/cilium/hive/cell"
    11  	"github.com/sirupsen/logrus"
    12  	"k8s.io/apimachinery/pkg/util/sets"
    13  
    14  	"github.com/cilium/cilium/pkg/bgpv1/manager/store"
    15  	"github.com/cilium/cilium/pkg/bgpv1/types"
    16  	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    17  	"github.com/cilium/cilium/pkg/k8s/resource"
    18  	"github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels"
    19  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    20  )
    21  
    22  type (
    23  	// PeerAdvertisements is a map of peer name to its family advertisements
    24  	// This is the top level map that is returned to the consumer with requested advertisements.
    25  	PeerAdvertisements       map[string]PeerFamilyAdvertisements
    26  	PeerFamilyAdvertisements map[v2alpha1.CiliumBGPFamily][]v2alpha1.BGPAdvertisement // key is the address family type
    27  )
    28  
    29  type PeerAdvertisementIn struct {
    30  	cell.In
    31  
    32  	Logger          logrus.FieldLogger
    33  	PeerConfigStore store.BGPCPResourceStore[*v2alpha1.CiliumBGPPeerConfig]
    34  	AdvertStore     store.BGPCPResourceStore[*v2alpha1.CiliumBGPAdvertisement]
    35  }
    36  
    37  type CiliumPeerAdvertisement struct {
    38  	logger     logrus.FieldLogger
    39  	peerConfig store.BGPCPResourceStore[*v2alpha1.CiliumBGPPeerConfig]
    40  	adverts    store.BGPCPResourceStore[*v2alpha1.CiliumBGPAdvertisement]
    41  }
    42  
    43  func NewCiliumPeerAdvertisement(p PeerAdvertisementIn) *CiliumPeerAdvertisement {
    44  	return &CiliumPeerAdvertisement{
    45  		logger:     p.Logger,
    46  		peerConfig: p.PeerConfigStore,
    47  		adverts:    p.AdvertStore,
    48  	}
    49  }
    50  
    51  // GetConfiguredAdvertisements can be called to get all configured advertisements of given BGPAdvertisementType for each peer.
    52  // Advertisements are selected based on below criteria:
    53  // Each peer is selected from the BGP node instance configuration. For each peer, the peer configuration is fetched
    54  // from local store.
    55  // Peer configuration contains the list of families and the advertisement selector.
    56  // We iterate over all advertisements ( available from local store ), select only those that match the advertisement
    57  // selector of the family.
    58  // Information of peer -> family -> advertisements is returned to the consumer.
    59  // Linear scan [ Peers ] - O(n) ( number of peers )
    60  // Linear scan [ Families ] - O(m) ( max 2 )
    61  // Linear scan [ Advertisements ] - O(k) ( number of advertisements - 3-4 types, which is again filtered)
    62  func (p *CiliumPeerAdvertisement) GetConfiguredAdvertisements(conf *v2alpha1.CiliumBGPNodeInstance, selectAdvertTypes ...v2alpha1.BGPAdvertisementType) (PeerAdvertisements, error) {
    63  	result := make(PeerAdvertisements)
    64  	l := p.logger.WithField(types.InstanceLogField, conf.Name)
    65  	for _, peer := range conf.Peers {
    66  		lp := l.WithField(types.PeerLogField, peer.Name)
    67  
    68  		if peer.PeerConfigRef == nil || peer.PeerConfigRef.Name == "" {
    69  			lp.Debug("Peer config not specified, skipping advertisement check")
    70  			continue
    71  		}
    72  		peerConfig, exist, err := p.peerConfig.GetByKey(resource.Key{Name: peer.PeerConfigRef.Name})
    73  		if err != nil {
    74  			if errors.Is(err, store.ErrStoreUninitialized) {
    75  				lp.Errorf("BUG: Peer config store is not initialized")
    76  			}
    77  			return nil, err
    78  		}
    79  		if !exist {
    80  			lp.Debug("Peer config not found, skipping advertisement check")
    81  			continue
    82  		}
    83  
    84  		peerAdverts, err := p.getPeerAdvertisements(peerConfig, selectAdvertTypes...)
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  		result[peer.Name] = peerAdverts
    89  	}
    90  	return result, nil
    91  }
    92  
    93  func (p *CiliumPeerAdvertisement) getPeerAdvertisements(peerConfig *v2alpha1.CiliumBGPPeerConfig, selectAdvertTypes ...v2alpha1.BGPAdvertisementType) (PeerFamilyAdvertisements, error) {
    94  	result := make(map[v2alpha1.CiliumBGPFamily][]v2alpha1.BGPAdvertisement)
    95  
    96  	for _, family := range peerConfig.Spec.Families {
    97  		advert, err := p.getFamilyAdvertisements(family, selectAdvertTypes...)
    98  		if err != nil {
    99  			return result, err
   100  		}
   101  		result[family.CiliumBGPFamily] = advert
   102  	}
   103  	return result, nil
   104  }
   105  
   106  func (p *CiliumPeerAdvertisement) getFamilyAdvertisements(family v2alpha1.CiliumBGPFamilyWithAdverts, selectAdvertTypes ...v2alpha1.BGPAdvertisementType) ([]v2alpha1.BGPAdvertisement, error) {
   107  	// get all advertisement CRD objects.
   108  	advertResources, err := p.adverts.List()
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	// select only label selected advertisements for the family
   114  	selectedAdvertResources, err := p.familySelectedAdvertisements(family, advertResources)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	// create selectTypeSet for easier lookup
   120  	selectTypesSet := sets.New[string]()
   121  	for _, selectType := range selectAdvertTypes {
   122  		selectTypesSet.Insert(string(selectType))
   123  	}
   124  
   125  	var selectedAdvertisements []v2alpha1.BGPAdvertisement
   126  	// select advertisements requested by the consumer
   127  	for _, advertResource := range selectedAdvertResources {
   128  		for _, advert := range advertResource.Spec.Advertisements {
   129  			// check if the advertisement type is in the selectType set
   130  			if selectTypesSet.Has(string(advert.AdvertisementType)) {
   131  				selectedAdvertisements = append(selectedAdvertisements, advert)
   132  			}
   133  		}
   134  	}
   135  
   136  	return selectedAdvertisements, nil
   137  }
   138  
   139  func (p *CiliumPeerAdvertisement) familySelectedAdvertisements(family v2alpha1.CiliumBGPFamilyWithAdverts, adverts []*v2alpha1.CiliumBGPAdvertisement) ([]*v2alpha1.CiliumBGPAdvertisement, error) {
   140  	var result []*v2alpha1.CiliumBGPAdvertisement
   141  	advertSelector, err := slim_metav1.LabelSelectorAsSelector(family.Advertisements)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	for _, advert := range adverts {
   147  		if advertSelector.Matches(labels.Set(advert.Labels)) {
   148  			result = append(result, advert)
   149  		}
   150  	}
   151  	return result, nil
   152  }
   153  
   154  func PeerAdvertisementsEqual(first, second PeerAdvertisements) bool {
   155  	if len(first) != len(second) {
   156  		return false
   157  	}
   158  
   159  	for peer, peerAdverts := range first {
   160  		if !FamilyAdvertisementsEqual(peerAdverts, second[peer]) {
   161  			return false
   162  		}
   163  	}
   164  	return true
   165  }
   166  
   167  func FamilyAdvertisementsEqual(first, second PeerFamilyAdvertisements) bool {
   168  	if len(first) != len(second) {
   169  		return false
   170  	}
   171  
   172  	for family, familyAdverts := range first {
   173  		otherFamilyAdverts, exist := second[family]
   174  		if !exist || len(familyAdverts) != len(otherFamilyAdverts) {
   175  			return false
   176  		}
   177  
   178  		sort.Slice(familyAdverts, func(i, j int) bool {
   179  			return familyAdverts[i].AdvertisementType < familyAdverts[j].AdvertisementType
   180  		})
   181  
   182  		sort.Slice(otherFamilyAdverts, func(i, j int) bool {
   183  			return otherFamilyAdverts[i].AdvertisementType < otherFamilyAdverts[j].AdvertisementType
   184  		})
   185  
   186  		for i, advert := range familyAdverts {
   187  			if !advert.DeepEqual(&otherFamilyAdverts[i]) {
   188  				return false
   189  			}
   190  		}
   191  	}
   192  	return true
   193  }