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 }