github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconcilerv2/paths.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package reconcilerv2 5 6 import ( 7 "context" 8 "maps" 9 10 "github.com/sirupsen/logrus" 11 12 "github.com/cilium/cilium/pkg/bgpv1/types" 13 "github.com/cilium/cilium/pkg/k8s/resource" 14 ) 15 16 // PathMap is a map of paths indexed by the NLRI string 17 type PathMap map[string]*types.Path 18 19 // AFPathsMap is a map of paths per address family, indexed by the family 20 type AFPathsMap map[types.Family]PathMap 21 22 // ResourceAFPathsMap holds the AF paths keyed by the resource name. 23 type ResourceAFPathsMap map[resource.Key]AFPathsMap 24 25 type ReconcileAFPathsParams struct { 26 Logger logrus.FieldLogger 27 Ctx context.Context 28 Router types.Router 29 DesiredPaths AFPathsMap 30 CurrentPaths AFPathsMap 31 } 32 33 type reconcilePathsParams struct { 34 Logger logrus.FieldLogger 35 Ctx context.Context 36 Router types.Router 37 CurrentAdvertisements PathMap 38 ToAdvertise PathMap 39 } 40 41 // ReconcileAFPaths reconciles BGP advertisements per address family. It will consume desired and current paths (AFPathsMap) 42 // and will return the outcome of the reconciliation. 43 func ReconcileAFPaths(rp *ReconcileAFPathsParams) (AFPathsMap, error) { 44 runningAFPaths := make(AFPathsMap) 45 maps.Copy(runningAFPaths, rp.CurrentPaths) 46 47 // to delete family advertisements that are not in desiredPaths 48 for family, runningPaths := range runningAFPaths { 49 if _, ok := rp.DesiredPaths[family]; !ok { 50 runningAdverts, err := reconcilePaths(&reconcilePathsParams{ 51 Logger: rp.Logger, 52 Ctx: rp.Ctx, 53 Router: rp.Router, 54 CurrentAdvertisements: runningPaths, 55 ToAdvertise: nil, 56 }) 57 if err != nil { 58 runningAFPaths[family] = runningAdverts 59 return runningAFPaths, err 60 } 61 delete(runningAFPaths, family) 62 } 63 } 64 65 // to update family advertisements that are in both runningState and desiredPaths 66 for family := range rp.DesiredPaths { 67 runningAdverts, err := reconcilePaths(&reconcilePathsParams{ 68 Logger: rp.Logger, 69 Ctx: rp.Ctx, 70 Router: rp.Router, 71 CurrentAdvertisements: runningAFPaths[family], 72 ToAdvertise: rp.DesiredPaths[family], 73 }) 74 75 // update runningState with the new advertisements 76 // even on error, we want to update the runningState with current advertisements. 77 runningAFPaths[family] = runningAdverts 78 if err != nil { 79 return runningAFPaths, err 80 } 81 } 82 83 return runningAFPaths, nil 84 } 85 86 // reconcilePaths reconciles the state of the BGP advertisements 87 // with the provided toAdvertise path map and returns a path map of the advertisements 88 // currently being announced. 89 // If there is an error from the BGP Router, the function will return the current advertisements in BGP router 90 // and the error. 91 func reconcilePaths(params *reconcilePathsParams) (PathMap, error) { 92 var ( 93 // logger for the reconciler 94 l = params.Logger 95 // holds advertisements which must be advertised 96 toAdvertise = make(PathMap) 97 // holds advertisements which must be removed 98 toWithdraw = make(PathMap) 99 ) 100 101 // running advertisements 102 runningAdverts := make(PathMap) 103 maps.Copy(runningAdverts, params.CurrentAdvertisements) 104 105 // if there are no advertisements to be made, we will withdraw all current advertisements 106 if len(params.ToAdvertise) == 0 { 107 for advrtKey, advrt := range runningAdverts { 108 if advrt == nil { 109 l.WithField(types.PathLogField, advrtKey).Error("BUG: nil path in running advertisements map") 110 continue 111 } 112 113 l.WithFields(logrus.Fields{ 114 types.PathLogField: advrt.NLRI.String(), 115 types.FamilyLogField: advrt.Family.String(), 116 }).Debug("Withdrawing path") 117 118 if err := params.Router.WithdrawPath(params.Ctx, types.PathRequest{Path: advrt}); err != nil { 119 return runningAdverts, err 120 } 121 delete(runningAdverts, advrtKey) 122 } 123 return nil, nil 124 } 125 126 for advrtKey, advrt := range params.ToAdvertise { 127 if advrt == nil { 128 l.WithField(types.PathLogField, advrtKey).Error("BUG: nil path in advertise advertisements map") 129 continue 130 } 131 132 if _, exists := runningAdverts[advrtKey]; !exists { 133 toAdvertise[advrtKey] = advrt 134 } 135 } 136 137 for advrtKey, advrt := range runningAdverts { 138 if advrt == nil { 139 l.WithField(types.PathLogField, advrtKey).Error("BUG: nil path in running advertisements map") 140 continue 141 } 142 143 if _, exists := params.ToAdvertise[advrtKey]; !exists { 144 toWithdraw[advrtKey] = advrt 145 } 146 } 147 148 if len(toAdvertise) == 0 && len(toWithdraw) == 0 { 149 l.Debug("no reconciliation necessary") 150 return params.CurrentAdvertisements, nil 151 } 152 153 // withdraw unneeded adverts 154 for advrtKey, advrt := range toWithdraw { 155 l.WithFields(logrus.Fields{ 156 types.PathLogField: advrt.NLRI.String(), 157 types.FamilyLogField: advrt.Family.String(), 158 }).Debug("Withdrawing path") 159 160 if err := params.Router.WithdrawPath(params.Ctx, types.PathRequest{Path: advrt}); err != nil { 161 return runningAdverts, err 162 } 163 delete(runningAdverts, advrtKey) 164 } 165 166 // create new adverts 167 for advrtKey, advrt := range toAdvertise { 168 l.WithFields(logrus.Fields{ 169 types.PathLogField: advrt.NLRI.String(), 170 types.FamilyLogField: advrt.Family.String(), 171 }).Debug("Advertising path") 172 173 advrtResp, err := params.Router.AdvertisePath(params.Ctx, types.PathRequest{Path: advrt}) 174 if err != nil { 175 return runningAdverts, err 176 } 177 runningAdverts[advrtKey] = advrtResp.Path 178 } 179 180 return runningAdverts, nil 181 } 182 183 func addPathToAFPathsMap(m AFPathsMap, fam types.Family, path *types.Path) { 184 pathsPerFamily, exists := m[fam] 185 if !exists { 186 pathsPerFamily = make(PathMap) 187 m[fam] = pathsPerFamily 188 } 189 pathsPerFamily[path.NLRI.String()] = path 190 }